From 0ec50f11efea5a826400254aa5f9ed65128c9937 Mon Sep 17 00:00:00 2001 From: Brown Date: Fri, 13 Jan 2023 08:59:16 -0700 Subject: [PATCH 01/46] Add empty fresnel solver model to begin transitioning fresnel to current solver (same as physical trough) --- ssc/CMakeLists.txt | 1 + ssc/cmod_fresnel_physical.cpp | 307 ++++++++++++++++++ ssc/sscapi.cpp | 2 + tcs/CMakeLists.txt | 2 + tcs/csp_solver_fresnel_collector_receiver.cpp | 141 ++++++++ tcs/csp_solver_fresnel_collector_receiver.h | 105 ++++++ 6 files changed, 558 insertions(+) create mode 100644 ssc/cmod_fresnel_physical.cpp create mode 100644 tcs/csp_solver_fresnel_collector_receiver.cpp create mode 100644 tcs/csp_solver_fresnel_collector_receiver.h diff --git a/ssc/CMakeLists.txt b/ssc/CMakeLists.txt index c64ef74aa..b2f1ec3f4 100644 --- a/ssc/CMakeLists.txt +++ b/ssc/CMakeLists.txt @@ -39,6 +39,7 @@ set(SSC_SRC cmod_financial_eqns.cpp cmod_financial_eqns.h cmod_fossilgen.cpp + cmod_fresnel_physical.cpp cmod_fuelcell.cpp cmod_fuelcell.h cmod_generic_system-builder.cpp diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp new file mode 100644 index 000000000..2277a0a80 --- /dev/null +++ b/ssc/cmod_fresnel_physical.cpp @@ -0,0 +1,307 @@ +/* +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 "csp_solver_fresnel_collector_receiver.h" + +static var_info _cm_vtab_fresnel_physical[] = { + + // Solar Field (from cmod_tcsmslf.cpp) + { SSC_INPUT, SSC_NUMBER, "nMod", "Number of collector modules in a loop", "", "", "controller", "*", "INTEGER", ""}, + { SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variantions", "", "", "controller", "?=4", "INTEGER", ""}, + { SSC_INPUT, SSC_NUMBER, "nLoops", "Number of loops in the field", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "eta_pump", "HTF pump efficiency", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "HDR_rough", "Header pipe roughness", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "theta_stow", "stow angle", "deg", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "theta_dep", "deploy angle", "deg", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "FieldConfig", "Number of subfield headers", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_startup", "Power block startup temperature", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "pb_rated_cap", "Rated plant capacity", "MWe", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "m_dot_htfmax", "Maximum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "controller", "*", "INTEGER", ""}, + { SSC_INPUT, SSC_NUMBER, "T_field_ini", "Initial field temperature", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "fthrok", "Flag to allow partial defocusing of the collectors", "", "", "controller", "*", "INTEGER", ""}, + { SSC_INPUT, SSC_NUMBER, "fthrctrl", "Defocusing strategy", "", "", "controller", "*", "INTEGER", ""}, + { SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "solar_mult", "Solar multiple", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "mc_bal_hot", "The heat capacity of the balance of plant on the hot side", "kWht/K-MWt", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "mc_bal_cold", "The heat capacity of the balance of plant on the cold side", "kWht/K-MWt", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "controller", "*", "", ""}, + + { SSC_INPUT, SSC_NUMBER, "water_per_wash", "Water usage per wash", "L/m2_aper", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "washes_per_year", "Mirror washing frequency", "none", "", "solar_field", "*", "", "" }, + + { SSC_INPUT, SSC_NUMBER, "opt_model", "The optical model", "", "", "controller", "*", "INTEGER", ""}, + { SSC_INPUT, SSC_NUMBER, "A_aperture", "Reflective aperture area of the collector", "m2", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "reflectivity", "Solar-weighted mirror reflectivity value", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "TrackingError", "Tracking error derate", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "GeomEffects", "Geometry effects derate", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "Dirt_mirror", "User-defined dirt on mirror derate", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "Error", "User-defined general optical error derate", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "L_mod", "The length of the collector module", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "IAM_T_coefs", "Incidence angle modifier coefficients - transversal plane", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "IAM_L_coefs", "Incidence angle modifier coefficients - longitudinal plane", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_MATRIX, "OpticalTable", "Values of the optical efficiency table", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "rec_model", "Receiver model type (1=Polynomial ; 2=Evac tube)", "", "", "controller", "*", "INTEGER", ""}, + { SSC_INPUT, SSC_ARRAY, "HCE_FieldFrac", "The fraction of the field occupied by this HCE type", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "D_abs_in", "The inner absorber tube diameter", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "D_abs_out", "The outer absorber tube diameter", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "D_glass_in", "The inner glass envelope diameter", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "D_glass_out", "The outer glass envelope diameter", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "D_plug", "The diameter of the absorber flow plug (optional)", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "Flow_type", "The flow type through the absorber", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "Rough", "Roughness of the internal surface", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "alpha_env", "Envelope absorptance", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_MATRIX, "epsilon_abs_1", "Absorber emittance - HCE variation 1", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_MATRIX, "epsilon_abs_2", "Absorber emittance - HCE variation 2", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_MATRIX, "epsilon_abs_3", "Absorber emittance - HCE variation 3", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_MATRIX, "epsilon_abs_4", "Absorber emittance - HCE variation 4", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "alpha_abs", "Absorber absorptance", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "Tau_envelope", "Envelope transmittance", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "epsilon_glass", "Glass envelope emissivity", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "GlazingIntactIn", "The glazing intact flag", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "P_a", "Annulus gas pressure", "torr", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "AnnulusGas", "Annulus gas type (1=air; 26=Ar; 27=H2)", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "AbsorberMaterial", "Absorber material type", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "Shadowing", "Receiver bellows shadowing loss factor", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "dirt_env", "Loss due to dirt on the receiver envelope", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "Design_loss", "Receiver heat loss at design", "W/m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "L_mod_spacing", "Piping distance between sequential modules in a loop", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "L_crossover", "Length of crossover piping in a loop", "m", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "HL_T_coefs", "HTF temperature-dependent heat loss coefficients", "W/m-K", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "HL_w_coefs", "Wind-speed-dependent heat loss coefficients", "W/m-(m/s)", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "DP_nominal", "Pressure drop across a single collector assembly at design", "bar", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_ARRAY, "DP_coefs", "Pressure drop mass flow based part-load curve", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "rec_htf_vol", "Volume of HTF in a single collector unit per unit aperture area", "L/m2-ap", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "controller", "*", "", ""}, + + { SSC_INPUT, SSC_NUMBER, "I_b", "Direct normal incident solar irradiation", "kJ/m2-hr", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_db", "Dry bulb air temperature", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "V_wind", "Ambient windspeed", "m/s", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "P_amb", "Ambient pressure", "atm", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_dp", "The dewpoint temperature", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "T_cold_in", "HTF return temperature", "C", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "defocus", "Defocus control", "", "", "controller", "*", "", ""}, + + // All other inputs from (cmod_trough_physical.cpp) + + // Weather Reader + { SSC_INPUT, SSC_STRING, "file_name", "Local weather file with path", "none", "", "weather", "*", "LOCAL_FILE", "" }, + + // Power Cycle + { SSC_INPUT, SSC_NUMBER, "pc_config", "0: Steam Rankine (224), 1: user defined", "-", "", "powerblock", "?=0", "INTEGER", "" }, + { SSC_INPUT, SSC_NUMBER, "P_ref", "Rated plant capacity", "MWe", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "eta_ref", "Power cycle efficiency at design", "none", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "-", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "-", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby mode", "none", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "dT_cw_ref", "Reference condenser cooling water inlet/outlet T diff", "C", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "T_amb_des", "Reference ambient temperature at design point", "C", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "CT", "Flag for using dry cooling or wet cooling system", "none", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tech_type", "Turbine inlet pressure control flag (sliding=user, fixed=trough)", "1/2/3", "tower/trough/user", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "T_approach", "Cooling tower approach temperature", "C", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "T_ITD_des", "ITD at design for dry system", "C", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "P_cond_ratio", "Condenser pressure ratio", "none", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "pb_bd_frac", "Power block blowdown steam fraction ", "none", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "P_cond_min", "Minimum condenser pressure", "inHg", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "n_pl_inc", "Number of part-load increments for the heat rejection system", "none", "", "powerblock", "pc_config=0", "", "" }, + { 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", "", "" }, + + // UDPC parameters + { SSC_INPUT, SSC_NUMBER, "ud_f_W_dot_cool_des", "Percent of user-defined power cycle design gross output consumed by cooling", "%", "", "powerblock", "pc_config=1", "", "" }, + { 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", "", "powerblock", "pc_config=1", "", "" }, + { 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", "", "", "powerblock", "pc_config=1", "", "" }, + + // TES + { SSC_INPUT, SSC_NUMBER, "store_fluid", "Material number for storage fluid", "-", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "store_fl_props", "User defined storage fluid property data", "-", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tshours", "Equivalent full-load thermal storage hours", "hr", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "h_tank", "Total height of tank (height of HTF when tank is full", "m", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "u_tank", "Loss coefficient from the tank", "W/m2-K", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tank_pairs", "Number of equivalent tank pairs", "-", "", "TES", "*", "INTEGER", "" }, + { SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Minimum allowable hot tank HTF temp", "C", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "hot_tank_max_heat", "Rated heater capacity for hot tank heating", "MWe", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Minimum allowable cold tank HTF temp", "C", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "cold_tank_max_heat", "Rated heater capacity for cold tank heating", "MWe", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "dt_hot", "Hot side HX approach temp", "C", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "h_tank_min", "Minimum allowable HTF height in storage tank", "m", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "init_hot_htf_percent", "Initial fraction of avail. vol that is hot", "%", "", "TES", "*", "", "" }, + + // TOU + { SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 CSP operation Time-of-Use Weekday schedule", "-", "", "tou", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 CSP operation Time-of-Use Weekend schedule", "-", "", "tou", "*", "", "" }, + { 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", "", "" }, + { SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "" }, + { SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "" }, + { SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "tou", "is_dispatch=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "tou", "is_dispatch=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "tou", "is_dispatch=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "tou", "?=0.99", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "tou", "is_dispatch=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "" }, + { SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "is_wlim_series", "Use time-series net electricity generation limits", "", "", "tou", "?=0", "", "" }, + { SSC_INPUT, SSC_ARRAY, "wlim_series", "Time series net electicity generation limits", "kWe", "", "tou", "is_wlim_series=1", "", "" }, + { SSC_INPUT, SSC_ARRAY, "f_turb_tou_periods", "Dispatch logic for turbine load fraction", "-", "", "tou", "*", "", "" }, + + { SSC_INPUT, SSC_NUMBER, "csp_financial_model", "", "1-8", "", "Financial Model", "?=1", "INTEGER,MIN=0", "" }, + { SSC_INPUT, SSC_NUMBER, "ppa_multiplier_model", "PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts", "0/1", "0=diurnal,1=timestep", "tou", "?=0", /*need a default so this var works in required_if*/ "INTEGER,MIN=0", "" }, + { SSC_INPUT, SSC_ARRAY, "dispatch_factors_ts", "Dispatch payment factor array", "", "", "tou", "ppa_multiplier_model=1&csp_financial_model<5&is_dispatch=1","", "" }, + { 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","", "" }, + { SSC_INPUT, SSC_NUMBER, "en_electricity_rates", "Enable electricity rates for grid purchase", "0/1", "", "Electricity Rates", "?=0", "", "" }, + { SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekday", "12x24 PPA pricing Weekday schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekend", "12x24 PPA pricing Weekend schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor1", "Dispatch payment factor 1", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor2", "Dispatch payment factor 2", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor3", "Dispatch payment factor 3", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor4", "Dispatch payment factor 4", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor5", "Dispatch payment factor 5", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor6", "Dispatch payment factor 6", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor7", "Dispatch payment factor 7", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor8", "Dispatch payment factor 8", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "dispatch_factor9", "Dispatch payment factor 9", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "tou", "?=1", "", "" }, + { SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "tou", "", "", "" }, + { SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "tou", "?", "", "" }, + { SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA prices - yearly", "$/kWh", "", "Revenue", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + { 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", "", "" }, + + { SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Fraction of rated gross power constantly consumed", "MWe/MWcap", "", "system", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "bop_array", "Balance of plant parasitic power fraction, mult frac and const, linear and quad coeff", "", "", "system", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "aux_array", "Auxiliary heater, mult frac and const, linear and quad coeff", "", "", "system", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "water_usage_per_wash", "Water usage per wash", "L/m2_aper", "", "system", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "washing_frequency", "Mirror washing frequency", "-/year", "", "system", "*", "", "" }, + + // Newly added + { SSC_INPUT, SSC_NUMBER, "calc_design_pipe_vals", "Calculate temps and pressures at design conditions for runners and headers", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "V_hdr_cold_max", "Maximum HTF velocity in the cold headers at design", "m/s", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "V_hdr_cold_min", "Minimum HTF velocity in the cold headers at design", "m/s", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "V_hdr_hot_max", "Maximum HTF velocity in the hot headers at design", "m/s", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "V_hdr_hot_min", "Minimum HTF velocity in the hot headers at design", "m/s", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "N_max_hdr_diams", "Maximum number of diameters in each of the hot and cold headers", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "L_rnr_pb", "Length of runner pipe in power block", "m", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "L_rnr_per_xpan", "Threshold length of straight runner pipe without an expansion loop", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "L_xpan_hdr", "Compined perpendicular lengths of each header expansion loop", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "L_xpan_rnr", "Compined perpendicular lengths of each runner expansion loop", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "Min_rnr_xpans", "Minimum number of expansion loops per single-diameter runner section", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "northsouth_field_sep", "North/south separation between subfields. 0 = SCAs are touching", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "N_hdr_per_xpan", "Number of collector loops per expansion loop", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "offset_xpan_hdr", "Location of first header expansion loop. 1 = after first collector loop", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "K_cpnt", "Interconnect component minor loss coefficients, row=intc, col=cpnt", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "D_cpnt", "Interconnect component diameters, row=intc, col=cpnt", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "L_cpnt", "Interconnect component lengths, row=intc, col=cpnt", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "Type_cpnt", "Interconnect component type, row=intc, col=cpnt", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "custom_sf_pipe_sizes", "Use custom solar field pipe diams, wallthks, and lengths", "none", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "sf_rnr_diams", "Custom runner diameters", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "sf_rnr_wallthicks", "Custom runner wall thicknesses", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "sf_rnr_lengths", "Custom runner lengths", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "sf_hdr_diams", "Custom header diameters", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "sf_hdr_wallthicks", "Custom header wall thicknesses", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "sf_hdr_lengths", "Custom header lengths", "m", "", "solar_field", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "has_hot_tank_bypass", "Bypass valve connects field outlet to cold tank", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "T_tank_hot_inlet_min", "Minimum hot tank htf inlet temperature", "C", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tes_pump_coef", "Pumping power to move 1kg of HTF through tes loop", "kW/(kg/s)", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "custom_tes_p_loss", "TES pipe losses are based on custom lengths and coeffs", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "k_tes_loss_coeffs", "Minor loss coeffs for the coll, gen, and bypass loops", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "custom_tes_pipe_sizes", "Use custom TES pipe diams, wallthks, and lengths", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "tes_diams", "Custom TES diameters", "m", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "tes_wallthicks", "Custom TES wall thicknesses", "m", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "tes_lengths", "Custom TES lengths", "m", "", "controller", "", "", "" }, + { SSC_INPUT, SSC_NUMBER, "DP_SGS", "Pressure drop within the steam generator", "bar", "", "controller", "*", "", "" }, + + // Needed for auto-updating dependent inputs + { SSC_INPUT, SSC_NUMBER, "use_solar_mult_or_aperture_area", "Use solar multiple or total field aperture area", "-", "", "controller", "?=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "specified_solar_multiple", "specified_solar_multiple", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "specified_total_aperture", "specified_total_aperture", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "non_solar_field_land_area_multiplier", "non_solar_field_land_area_multiplier", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "trough_loop_control", "trough_loop_control", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "disp_wlim_maxspec", "disp_wlim_maxspec", "-", "", "controller", "*", "", "" }, + + + var_info_invalid }; + + + + +class cm_fresnel_physical : public compute_module +{ +public: + + cm_fresnel_physical() + { + add_var_info(_cm_vtab_fresnel_physical); + } + + void exec() + { + int x = 0; + + //C_csp_fresnel_collector_receiver* c = new C_csp_fresnel_collector_receiver(); + C_csp_fresnel_collector_receiver* c = new C_csp_fresnel_collector_receiver(); + int y = 0; + + } + +}; + +DEFINE_MODULE_ENTRY(fresnel_physical, "Physical trough applications", 1) diff --git a/ssc/sscapi.cpp b/ssc/sscapi.cpp index 55b8468fc..0aab686e3 100644 --- a/ssc/sscapi.cpp +++ b/ssc/sscapi.cpp @@ -119,6 +119,7 @@ extern module_entry_info cm_entry_tcsmolten_salt, cm_entry_mspt_sf_and_rec_isolated, cm_entry_ptes_design_point, + cm_entry_fresnel_physical, cm_entry_tcslinear_fresnel, cm_entry_linear_fresnel_dsg_iph, cm_entry_tcsmslf, @@ -218,6 +219,7 @@ static module_entry_info *module_table[] = { &cm_entry_tcsgeneric_solar, &cm_entry_tcsmolten_salt, &cm_entry_mspt_sf_and_rec_isolated, + &cm_entry_fresnel_physical, &cm_entry_ptes_design_point, &cm_entry_tcslinear_fresnel, &cm_entry_linear_fresnel_dsg_iph, diff --git a/tcs/CMakeLists.txt b/tcs/CMakeLists.txt index fc2b9e8d7..88d1b85e4 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 @@ -110,6 +111,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_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp new file mode 100644 index 000000000..fb7a66347 --- /dev/null +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -0,0 +1,141 @@ +#include "csp_solver_fresnel_collector_receiver.h" + +C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() +{ + int x = 0; +} + +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) +{ + + + return; +} + + +bool C_csp_fresnel_collector_receiver::init_fieldgeom() +{ + return true; +} + + +C_csp_collector_receiver::E_csp_cr_modes C_csp_fresnel_collector_receiver::get_operating_state() +{ + return E_csp_cr_modes::OFF; //[-] +} + + +double C_csp_fresnel_collector_receiver::get_startup_time() +{ + return 3600; +} +double C_csp_fresnel_collector_receiver::get_startup_energy() +{ + return 1.e-6; +} +double C_csp_fresnel_collector_receiver::get_pumping_parasitic_coef() +{ + return 1.e-6; + +} + +double C_csp_fresnel_collector_receiver::get_min_power_delivery() +{ + return 0; +} + +double C_csp_fresnel_collector_receiver::get_max_power_delivery(double T_cold_in /*C*/) +{ + return 0; +} + +double C_csp_fresnel_collector_receiver::get_tracking_power() +{ + return 0; +} + +double C_csp_fresnel_collector_receiver::get_col_startup_power() +{ + return 0; +} + + +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) +{ + + 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) +{ +} + + + +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) +{ + 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) +{ + 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) +{ + return; +} + + +void C_csp_fresnel_collector_receiver::converged() +{ + 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) +{ +} + +double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_csp_weatherreader::S_outputs& weather, const C_csp_solver_sim_info& sim) +{ + return 0; +} + +double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/) +{ + return 0; +} + +double C_csp_fresnel_collector_receiver::get_collector_area() +{ + return 0; +} + diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h new file mode 100644 index 000000000..5dc136da4 --- /dev/null +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -0,0 +1,105 @@ +/* +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" + +class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver +{ + +public: + C_csp_fresnel_collector_receiver(); + + ~C_csp_fresnel_collector_receiver(); + + + 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*/); + + virtual double get_collector_area(); + + + +}; + +#endif From 871d58091d4e2f1f2361879cb8a73377c8d4dcd4 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 2 Feb 2023 14:37:12 -0700 Subject: [PATCH 02/46] Continue physical fresnel model transition to CSP solver --- ssc/cmod_fresnel_physical.cpp | 838 ++- tcs/csp_solver_fresnel_collector_receiver.cpp | 4578 ++++++++++++++++- tcs/csp_solver_fresnel_collector_receiver.h | 577 +++ 3 files changed, 5902 insertions(+), 91 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 2277a0a80..fcaee4bdd 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -31,7 +31,16 @@ 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 +#include static var_info _cm_vtab_fresnel_physical[] = { @@ -123,6 +132,8 @@ static var_info _cm_vtab_fresnel_physical[] = { { SSC_INPUT, SSC_NUMBER, "T_cold_in", "HTF return temperature", "C", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "defocus", "Defocus control", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "tilt", "Tilt angle of surface/axis", "", "", "Weather", "*", "", "" }, + // All other inputs from (cmod_trough_physical.cpp) // Weather Reader @@ -280,9 +291,6 @@ static var_info _cm_vtab_fresnel_physical[] = { var_info_invalid }; - - - class cm_fresnel_physical : public compute_module { public: @@ -294,14 +302,828 @@ class cm_fresnel_physical : public compute_module void exec() { - int x = 0; - //C_csp_fresnel_collector_receiver* c = new C_csp_fresnel_collector_receiver(); - C_csp_fresnel_collector_receiver* c = new C_csp_fresnel_collector_receiver(); - int y = 0; + // General + bool is_dispatch = as_boolean("is_dispatch"); + + // 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, trough + C_csp_fresnel_collector_receiver c_fresnel; + { + c_fresnel.m_nMod = as_integer("nMod"); + c_fresnel.m_nRecVar = as_integer("nRecVar"); + c_fresnel.m_nLoops = as_integer("nLoops"); + 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_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_fthrok = as_integer("fthrok"); + c_fresnel.m_fthrctrl = as_integer("fthrctrl"); + c_fresnel.m_ColAz = as_number("ColAz"); + c_fresnel.m_ColTilt = as_number("tilt"); + + c_fresnel.m_solar_mult= as_number("solar_mult"); + + 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"); + + size_t size; + double* IAM_T_coefs = as_array("IAM_T_coefs", &size); + c_fresnel.m_IAM_T_coefs.assign(IAM_T_coefs, IAM_T_coefs + size); + + double* IAM_L_coefs = as_array("IAM_L_coefs", &size); + c_fresnel.m_IAM_L_coefs.assign(IAM_L_coefs, IAM_L_coefs + size); + + c_fresnel.m_OpticalTable = as_matrix("OpticalTable"); + + c_fresnel.m_rec_model = as_integer("rec_model"); + + double* HCE_FieldFrac = as_array("HCE_FieldFrac", &size); + c_fresnel.m_HCE_FieldFrac.assign(HCE_FieldFrac, HCE_FieldFrac + size); + + double* D_abs_in = as_array("D_abs_in", &size); + c_fresnel.m_D_abs_in.assign(D_abs_in, D_abs_in + size); + + double* D_abs_out = as_array("D_abs_out", &size); + c_fresnel.m_D_abs_out.assign(D_abs_out, D_abs_out + size); + + double* D_glass_in = as_array("D_glass_in", &size); + c_fresnel.m_D_glass_in.assign(D_glass_in, D_glass_in + size); + + double* D_glass_out = as_array("D_glass_out", &size); + c_fresnel.m_D_glass_out.assign(D_glass_out, D_glass_out + size); + + double* D_plug = as_array("D_plug", &size); + c_fresnel.m_D_plug.assign(D_plug, D_plug + size); + + double* Flow_type = as_array("Flow_type", &size); + c_fresnel.m_Flow_type.assign(Flow_type, Flow_type + size); + + double* Rough = as_array("Rough", &size); + c_fresnel.m_Rough.assign(Rough, Rough + size); + + double* alpha_env = as_array("alpha_env", &size); + c_fresnel.m_alpha_env.assign(alpha_env, alpha_env + size); + + 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"); + + double* alpha_abs = as_array("alpha_abs", &size); + c_fresnel.m_alpha_abs.assign(alpha_abs, alpha_abs + size); + + double* Tau_envelope = as_array("Tau_envelope", &size); + c_fresnel.m_Tau_envelope.assign(Tau_envelope, Tau_envelope + size); + + double* epsilon_glass = as_array("epsilon_glass", &size); + c_fresnel.m_epsilon_glass.assign(epsilon_glass, epsilon_glass + size); + + double* GlazingIntact = as_array("GlazingIntactIn", &size); + c_fresnel.m_GlazingIntact.assign(GlazingIntact, GlazingIntact + size); + + + + + + + double* P_a = as_array("P_a", &size); + c_fresnel.m_P_a.assign(P_a, P_a + size); + + double* AnnulusGas = as_array("AnnulusGas", &size); + c_fresnel.m_AnnulusGas.assign(AnnulusGas, AnnulusGas + size); + + double* AbsorberMaterial = as_array("AbsorberMaterial", &size); + c_fresnel.m_AbsorberMaterial.assign(AbsorberMaterial, AbsorberMaterial + size); + + double* Shadowing = as_array("Shadowing", &size); + c_fresnel.m_Shadowing.assign(Shadowing, Shadowing + size); + + double* dirt_env = as_array("dirt_env", &size); + c_fresnel.m_dirt_env.assign(dirt_env, dirt_env + size); + + double* Design_loss = as_array("Design_loss", &size); + c_fresnel.m_Design_loss.assign(Design_loss, Design_loss + size); + + c_fresnel.m_L_mod_spacing = as_number("L_mod_spacing"); + c_fresnel.m_L_crossover = as_number("L_crossover"); + + double* HL_T_coefs = as_array("HL_T_coefs", &size); + c_fresnel.m_HL_T_coefs.assign(HL_T_coefs, HL_T_coefs + size); + + double* HL_w_coefs = as_array("HL_w_coefs", &size); + c_fresnel.m_HL_w_coefs.assign(HL_w_coefs, HL_w_coefs + size); + + c_fresnel.m_DP_nominal = as_number("DP_nominal"); + + double* DP_coefs = as_array("DP_coefs", &size); + c_fresnel.m_DP_coefs.assign(DP_coefs, DP_coefs + size); + + c_fresnel.m_rec_htf_vol = as_number("rec_htf_vol"); + + + + //////////////////////// Questionable + c_fresnel.m_I_b = as_number("I_b"); + c_fresnel.m_V_wind_des = as_number("V_wind_des"); + c_fresnel.m_T_amb_sf_des = as_number("T_amb_sf_des"); + } + + // 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("trough_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 trough + if (pc->m_tech_type == 3) { pc->m_tech_type = 8; }; // changing sliding pressure for the tower to sliding pressure for the trough + 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; + { + util::matrix_t tes_lengths; + if (is_assigned("tes_lengths")) { + tes_lengths = as_matrix("tes_lengths"); //[m] + } + if (!is_assigned("tes_lengths") || tes_lengths.ncells() < 11) { + double vals1[11] = { 0., 90., 100., 120., 0., 30., 90., 80., 80., 120., 80. }; + tes_lengths.assign(vals1, 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"), + as_double("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"), + as_double("V_tes_des"), + as_boolean("calc_design_pipe_vals"), + as_double("tes_pump_coef"), + as_double("eta_pump"), + as_boolean("has_hot_tank_bypass"), + as_double("T_tank_hot_inlet_min"), + as_boolean("custom_tes_p_loss"), + as_boolean("custom_tes_pipe_sizes"), + as_matrix("k_tes_loss_coeffs"), + as_matrix("tes_diams"), + as_matrix("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; + double ppa_price_year1 = std::numeric_limits::quiet_NaN(); + { + tou_params->mc_csp_ops.mc_weekdays = as_matrix("weekday_schedule"); + tou_params->mc_csp_ops.mc_weekends = as_matrix("weekend_schedule"); + 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); + } + + 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"); + + + 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("trough_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("trough_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("trough_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") || is_dispatch) { + 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; + + bool are_all_assigned = is_assigned("dispatch_sched_weekday") || is_assigned("dispatch_sched_weekend") + || is_assigned("dispatch_factor1") || is_assigned("dispatch_factor2") || is_assigned("dispatch_factor3") + || is_assigned("dispatch_factor4") || is_assigned("dispatch_factor5") || is_assigned("dispatch_factor6") + || is_assigned("dispatch_factor7") || is_assigned("dispatch_factor8") || is_assigned("dispatch_factor9"); + + if (are_all_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"); + + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, 0.0); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][0] = as_double("dispatch_factor1"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][1] = as_double("dispatch_factor2"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][2] = as_double("dispatch_factor3"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][3] = as_double("dispatch_factor4"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][4] = as_double("dispatch_factor5"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][5] = as_double("dispatch_factor6"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][6] = as_double("dispatch_factor7"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][7] = as_double("dispatch_factor8"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][8] = as_double("dispatch_factor9"); + } + 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("trough_physical", "\nDispatch optimization current not enabled for the Commercial financial model\n"); + // need to add pricing lookup for Commercial financial model + } + 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) { + 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("trough_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] + } + } + 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("trough_physical", "csp_financial_model must be 1-8"); + } + } + + // 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("trough_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 (as_boolean("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 * as_double("solar_mult"); //[MWt] + + dispatch.solver_params.set_user_inputs(as_boolean("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]; + } + + 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("trough_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); + assign("solar_multiple_actual", as_double("solar_mult")); // calculated during verify() using cmod_csp_trough_eqns.cpp + + // Convert Units + return; + { + // 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("trough_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("trough_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("trough_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("trough_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); + + + } + + int x = 0; + } }; -DEFINE_MODULE_ENTRY(fresnel_physical, "Physical trough applications", 1) +DEFINE_MODULE_ENTRY(fresnel_physical, "Physical Fresnel applications", 1) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index fb7a66347..e998a2ec7 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -1,141 +1,4553 @@ #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_THETA_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_COSTH_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_IAM_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_ROWSHADOW_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_ENDLOSS_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_DNI_COSTH, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {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_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_trough_collector::off - freeze protection failed")); + } + + if (fp_code != C_monotonic_eq_solver::CONVERGED) + { + throw(C_csp_exception("C_csp_trough_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, DP_loop_tot, DP_toField, DP_fromField, DP_hdr_cold, DP_hdr_hot; + 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_outX) / 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; + } + DP_tube[i] += PressureDrop(m_dot_htf, m_TCS_T_htf_ave[i], 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++) { + DP_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 + DP_fromField += PressureDrop(m_dot_temp, T_loop_outX, 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 + 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 + DP_hdr_cold = 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 + //if(ErrorFound()) return 1 + DP_hdr_hot = DP_hdr_hot + PressureDrop(m_dot_header, T_loop_outX, 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 + //if(ErrorFound()) return 1 + //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; + + //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_THETA_AVE, m_Theta_ave * m_r2d); //[deg], convert from rad + mc_reported_outputs.value(E_COSTH_AVE, m_CosTh_ave); //[-] + mc_reported_outputs.value(E_IAM_AVE, m_IAM_ave); //[-] + mc_reported_outputs.value(E_ROWSHADOW_AVE, m_RowShadow_ave); //[-] + mc_reported_outputs.value(E_ENDLOSS_AVE, m_EndLoss_ave); //[-] + mc_reported_outputs.value(E_DNI_COSTH, m_dni_costh); //[W/m2] + mc_reported_outputs.value(E_EQUIV_OPT_ETA_TOT, m_EqOpteff); //[-] + 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_INC_SF_COSTH, m_dni_costh * m_Ap_tot / 1.E6); //[MWt] + + mc_reported_outputs.value(E_Q_DOT_REC_INC, m_q_dot_sca_abs_summed_fullts + m_q_dot_sca_loss_summed_fullts); //[MWt] + 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] + + 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; +} + +class E_loop_energy_balance_exit +{ + public: + enum + { + SOLVED, + NaN + }; +}; + + +// ------------------------------------------------------------------- PRIVATE SUPPLEMENTAL + +int 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) +{ + m_m_dot_htf_tot = m_dot_htf_loop * float(m_nLoops); + + // First calculate the cold header temperature, which will serve as the loop inlet temperature + double rho_hdr_cold = m_htfProps.dens(m_T_sys_c_t_end_last, 1.0); // [kg/m3] + double rho_hdr_hot = m_htfProps.dens(m_T_sys_h_t_end_last, 1.0); // [kg/m3] + 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 + + 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); + else + T_sky = T_db - 20.0; + + // Initialize + double q_dot_loss_HR_cold = 0.0; // [W] Cold Header Heat Loss + 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] + + // TEMPORARY by TAYLOR + + //m_q_i = m_I_b * m_A_aperture / m_L_mod; //[W/m] The incoming solar irradiation per aperture length + //for (int j = 0; j < m_nMod; j++) { + // m_q_SCA[j] = m_q_i; //[W/m] The flux on the collector + //} + + //if(m_accept_loc == E_piping_config::FIELD) + if (true) + { + // This values is the Bulk Temperature at the *end* of the timestep + 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) * sim_info.ms_ts.m_step) + T_htf_cold_in; + + // Try calculating a timestep-average Bulk Temperature (and assume it is the outlet) + 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; + + // Now calculate an energy balance using the timestep-average Bulk Temperature + // ** THIS IS JUST A TEST: can comment out if necessary ** + double E_bal_T_t_ave = -m_dot_htf_loop * float(m_nLoops) * c_hdr_cold_last * (m_T_sys_c_t_int - T_htf_cold_in) * sim_info.ms_ts.m_step - + (m_v_cold * rho_hdr_cold * c_hdr_cold_last + m_mc_bal_cold) * (m_T_sys_c_t_end - m_T_sys_c_t_end_last); //[J] + + //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; + double m_cp_sys_c_t_int = m_htfProps.Cp(m_T_sys_c_t_int) * 1000.0; //mjw 1.6.2011 Adding mc_bal to the cold header inertia + 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_rnr[i] - T_db); //[W] + m_Runner_hl_cold_tot += 2. * 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_Header_hl_cold + m_L_crossover * m_D_hdr[i] * pi * m_Pipe_hl_coef * (m_T_hdr[i] - T_db); //[W] + m_Header_hl_cold_tot += m_nfsec * m_Header_hl_cold; + } + 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] + 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); + + // 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] + } + + m_T_loop[0] = m_T_loop_in; + + // Set Inlet Temperature + double dt = sim_info.ms_ts.m_step; + m_T_htf_in_t_int[0] = (m_TCS_T_sys_c_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; + + // 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_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 q_dot_loss_xover; //[W] + std::vector E_xover, E_xover_htf, E_xover_abs, E_xover_bal; + std::vector m_EqOpteffs(m_nMod, 0.); + { + E_sca.resize(m_nMod); + E_sca_htf.resize(m_nMod); + E_sca_abs.resize(m_nMod); + E_sca_bal.resize(m_nMod); + + q_dot_loss_xover.resize(m_nMod - 1); + + 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); + } + double q_inc_total = 0.; + double q_abs_abs_total = 0.; + double q_abs_htf_total = 0.; + + // Loop Through SCAs + 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] + + //int HT = (int)m_SCAInfoArray(i, 0) - 1; //[-] HCE type + //int CT = (int)m_SCAInfoArray(i, 1) - 1; //[-] Collector type + + double c_htf_i = 0.0; //[J/kg-K] + double rho_htf_i = 0.0; //[kg/m^3] + + double T_node_ave; + double dT_loc; + double m_node; + double errhl; + + // Check Receiver Type + switch (m_rec_model) + { + // Evacuated Receiver Model + case 2: + { + // Loop Through Variations + 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; + EvacReceiver(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, m_ncall, sim_info.ms_ts.m_time / 3600.0, + //outputs + m_q_loss[j], m_q_abs[j], m_q_1abs[j], c_htf_j, rho_htf_j, mv_HCEguessargs); + + // 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]; + 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 + 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 + m_EqOpteffs[i] += m_ColOptEff.at(i) * m_Shadowing[j] * m_dirt_env[j] * m_alpha_abs[j] * m_Tau_envelope[j] * m_HCE_FieldFrac[j]; + m_EqOpteff += m_ColOptEff.at(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]; + + } + + break; + } + + // Polynomial Model + case 1: + { + //If the average temperature hasn't been calculated yet, use the initial value + if (m_ncall == 0) { + T_node_ave = m_T_htf_out_t_end_last[i]; + } + else { + T_node_ave = m_T_htf_out_t_end_last[i]; + } + //iterate to improve heat loss estimate + errhl = 999.; + + double V_wind = weather.m_wspd; + double dt = sim_info.ms_ts.m_step; + + + + 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 + //update estimate of average temperature + 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] + + 2.0 * (m_T_htf_out_t_end_last[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (2.0 * m_dot_htf_loop * c_htf_i)) * + exp(-2. * m_dot_htf_loop * c_htf_i * dt / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)); + //Recalculate the average temperature for the SCA + m_TCS_T_htf_ave[i] = (m_T_htf_in_t_int[i] + m_T_htf_out_t_end[i]) / 2.0; + + errhl = T_node_ave - m_TCS_T_htf_ave[i]; //iterate until the node temperature does not vary significantly + + T_node_ave = m_TCS_T_htf_ave[i]; + } + + m_q_1abs_tot[i] = m_q_loss_SCAtot[i] / m_L_mod; + + double eta_optical = m_EqOpteff; + m_EqOpteff = eta_optical; //Use the optical efficiency as it is for this option + + break; + } + } + + q_inc_total += m_q_SCA[i] * m_L_mod; // [W] + q_abs_abs_total += m_q_SCA[i] * m_L_mod * m_EqOpteffs[i]; // [W] absorbed by absorber + q_abs_htf_total += m_q_abs_SCAtot[i]; // [W] absorbed by HTF + + //Calculate the specific heat for the node + c_htf_i *= 1000.0; + + //Calculate the mass of HTF associated with this node + m_node = rho_htf_i * m_A_cs.at(0) * m_L_mod; + + // from old fresnel + 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)); + + // from trough + 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 actual amount of energy absorbed by the field that doesn't go into changing the SCA's average temperature + ////MJW 1.16.2011 Include the thermal inertia term + //if (!m_is_using_input_gen) + //{ + // if (m_q_abs_SCAtot[i] > 0.0) + // { + // //m_E_avail[i] = max(m_q_abs_SCAtot[i]*m_dt*3600. - m_A_cs(HT,1)*m_L_actSCA[CT]*m_rho_htf[i]*m_c_htf[i]*(m_T_htf_ave[i]- m_T_htf_ave0[i]),0.0) + // double x1 = (m_A_cs(HT, 1) * m_L_actSCA[CT] * rho_htf_i * c_htf_i + m_L_actSCA[CT] * m_mc_bal_sca); //mjw 4.29.11 removed m_c_htf[i] -> it doesn't make sense on the m_mc_bal_sca term + // m_E_accum[i] = x1 * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]); + // 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] + + // //Equation: m_m_dot_avail*m_c_htf[i]*(T_hft_out - m_T_htf_in) = m_E_avail/(m_dt*3600) + // //m_m_dot_avail = (m_E_avail[i]/(m_dt*3600.))/(m_c_htf[i]*(m_T_htf_out[i] - m_T_htf_in[i])) //[J/s]*[kg-K/J]*[K]: + // } + //} + //else + //{ + // //m_E_avail[i] = max(m_q_abs_SCAtot[i]*m_dt*3600. - m_A_cs(HT,1)*m_L_actSCA[CT]*m_rho_htf[i]*m_c_htf[i]*(m_T_htf_ave[i]- m_T_htf_ave0[i]),0.0) + // double x1 = (m_A_cs(HT, 1) * m_L_actSCA[CT] * rho_htf_i * c_htf_i + m_L_actSCA[CT] * m_mc_bal_sca); //mjw 4.29.11 removed m_c_htf[i] -> it doesn't make sense on the m_mc_bal_sca term + // m_E_accum[i] = x1 * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]); + // 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] * m_dt - m_E_accum[i], 0.0); //[J/s]*[hr]*[s/hr]: [J] + // m_E_avail[i] = (m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step - m_E_accum[i]); //[J/s]*[hr]*[s/hr]: [J] + + // //Equation: m_m_dot_avail*m_c_htf[i]*(T_hft_out - m_T_htf_in) = m_E_avail/(m_dt*3600) + // //m_m_dot_avail = (m_E_avail[i]/(m_dt*3600.))/(m_c_htf[i]*(m_T_htf_out[i] - m_T_htf_in[i])) //[J/s]*[kg-K/J]*[K]: + //} + } + + //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); + m_E_accum[i] = x1 * (m_TCS_T_htf_ave[i] - m_T_htf_out_t_end_last[i]); + m_E_int_loop[i] = x1 * (m_TCS_T_htf_ave[i] - 298.15); //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] + + } + + //Recalculate the average temperature for the SCA + m_TCS_T_htf_ave[i] = (m_T_htf_in_t_int[i] + m_T_htf_out_t_end_last[i]) / 2.0; + + //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] * pi * L_int * (m_T_htf_out_t_int[i] - T_db) / (m_dot_htf_loop * c_htf_i); + //Add the internal energy of the crossover piping + 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); + + //Calculate inlet temperature of the next SCA + 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] + } + + } + + double eta_optical = m_EqOpteff; + double eta_optical_est = calculate_optical_efficiency(weather, sim_info); + 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); + + //Set the loop outlet temperature + T_loop_outX = m_T_htf_out_t_end[m_nMod - 1]; + + + + //Calculation for heat losses from hot header and runner pipe + m_Runner_hl_hot = 0.0; //initialize + m_Header_hl_hot = 0.0; //initialize + for (int i = 0; i < m_nhdrsec; i++) { + m_Header_hl_hot = m_Header_hl_hot + m_L_crossover * m_D_hdr[i] * pi * m_Pipe_hl_coef * (T_loop_outX - T_db); + } + + //Add the runner length + for (int i = 0; i < m_nrunsec; i++) { + m_Runner_hl_hot = m_Runner_hl_hot + m_L_runner[i] * pi * m_D_runner[i] * m_Pipe_hl_coef * (T_loop_outX - T_db); //Wt + } + double Pipe_hl_hot = m_Header_hl_hot + m_Runner_hl_hot; + + //Fill in rest of T_loop using the SCA inlet and outlet temps + int loop_i = 2; int sca_i = 0; + while (loop_i < 2 * m_nMod + 2) { + m_T_loop[loop_i] = m_T_htf_in_t_int[sca_i]; + m_T_loop[loop_i + 1] = m_T_htf_out_t_int[sca_i]; + loop_i = loop_i + 2; sca_i++; + } + + //Adjust the loop outlet temperature to account for thermal losses incurred in the hot header and the runner pipe + m_TCS_T_sys_h = T_loop_outX - Pipe_hl_hot / (m_m_dot_htf_tot * m_c_hdr_hot); + + //Calculate the system temperature of the hot portion of the collector field. + //This will serve as the fluid outlet temperature + m_TCS_T_sys_h = (m_TCS_T_sys_h_last - m_TCS_T_sys_h) * exp(-m_m_dot_htf_tot / (m_v_hot * rho_hdr_hot + m_mc_bal_hot / m_c_hdr_hot) * sim_info.ms_ts.m_step) + m_TCS_T_sys_h; + + + + // From Trough Section + 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] + + if (true) + { + //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] + + for (int i = 0; i < m_nhdrsec; i++) + { + m_Header_hl_hot = m_Header_hl_hot + m_L_crossover * m_D_hdr[i] * pi * m_Pipe_hl_coef * (T_loop_outX - T_db); + m_Header_hl_hot_tot += m_nfsec * m_Header_hl_hot; + } + + /*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_Header_hl_hot + m_L_crossover * m_D_hdr[i] * pi * m_Pipe_hl_coef * (T_loop_outX - T_db); + m_Header_hl_hot_tot += m_nfsec * m_Header_hl_hot; + }*/ + + //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); + for (int i = 0; i < m_nrunsec; i++) + { + m_Runner_hl_hot = m_Runner_hl_hot + m_L_runner[i] * pi * m_D_runner[i] * m_Pipe_hl_coef * (T_loop_outX - T_db); //Wt + m_Runner_hl_hot_tot += m_nfsec * m_Runner_hl_hot; + } + //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[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (m_T_rnr[i] - T_db); //Wt + // m_Runner_hl_hot_tot += 2. * m_Runner_hl_hot; + //} + 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); + + q_dot_loss_HR_hot = m_Header_hl_hot_tot + m_Runner_hl_hot_tot; //[W] // aka m_Pipe_hl_hot + E_HR_hot_losses = q_dot_loss_HR_hot * sim_info.ms_ts.m_step / 1.E6; //[MJ] + + + // 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] + + // 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; //[C] + + // 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; + + 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_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_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_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, 5) * 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; + + 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 * 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 * 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 * pi))); + //Determine the mass flow corresponding to the minimum velocity at design + m_dot_min = rho * V_min * 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] * 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] * mtoinch, nd); + summary->append(tstr); + } + //110 format(2X,I4,3X,"|",4X,F6.4,4X,"|",4X,F6.3,5X,"|",1X,I3) + } + +} + + +// ------------------------------------------------------------------------------ PUBLIC C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() { - int x = 0; + 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_include_fixed_power_block_runner = true; + //m_L_power_block_piping = std::numeric_limits::quiet_NaN(); + 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_Row_Distance = 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_cold_max = std::numeric_limits::quiet_NaN(); + //m_V_hdr_cold_min = std::numeric_limits::quiet_NaN(); + //m_V_hdr_hot_max = std::numeric_limits::quiet_NaN(); + //m_V_hdr_hot_min = 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_fthrok = -1; + m_fthrctrl = -1; + m_ColTilt = std::numeric_limits::quiet_NaN(); + m_ColAz = std::numeric_limits::quiet_NaN(); + //m_wind_stow_speed = std::numeric_limits::quiet_NaN(); + + //m_accept_init = false; + //m_accept_loc = -1; + //m_is_using_input_gen = false; + + //m_custom_sf_pipe_sizes = false; + + 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_hot_per_MW = std::numeric_limits::quiet_NaN(); + //m_mc_bal_cold_per_MW = std::numeric_limits::quiet_NaN(); + m_mc_bal_sca = std::numeric_limits::quiet_NaN(); + + //m_defocus = std::numeric_limits::quiet_NaN(); + m_latitude = std::numeric_limits::quiet_NaN(); + m_longitude = std::numeric_limits::quiet_NaN(); + m_TCS_T_sys_h = std::numeric_limits::quiet_NaN(); + m_TCS_T_sys_c = std::numeric_limits::quiet_NaN(); + m_TCS_T_sys_h_converged = std::numeric_limits::quiet_NaN(); + m_TCS_T_sys_c_converged = 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_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_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_Theta_ave = std::numeric_limits::quiet_NaN(); + m_CosTh_ave = std::numeric_limits::quiet_NaN(); + m_IAM_ave = std::numeric_limits::quiet_NaN(); + m_RowShadow_ave = std::numeric_limits::quiet_NaN(); + m_EndLoss_ave = std::numeric_limits::quiet_NaN(); + m_dni_costh = std::numeric_limits::quiet_NaN(); + 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(); + + for (int i = 0; i < 5; i++) + m_T_save[i] = std::numeric_limits::quiet_NaN(); + + mv_reguess_args.resize(3); + std::fill(mv_reguess_args.begin(), mv_reguess_args.end(), 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) * d2r; + } + //get the yaxis data values + for (int j = 1; j < nrow_OpticalTable; j++) { + yax[j - 1] = m_OpticalTable.at(j, 0) * 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_ColTilt = m_ColTilt * m_d2r; //[rad] Collector tilt angle (0 is horizontal, 90deg is vertical), convert from [deg] + m_ColAz = m_ColAz * m_d2r; //[rad] Collector azimuth angle, convert from [deg] + + // Check m_IAM matrix against number of collectors: m_nColt + // **** Don't need to because only one type of collector + //m_n_r_iam_matrix = (int)m_IAM_matrix.nrows(); + //m_n_c_iam_matrix = (int)m_IAM_matrix.ncols(); + + // Check Inputs + { + /*if (m_n_c_iam_matrix < 3) + { + throw(C_csp_exception("There must be at least 3 incident angle modifier coefficients", "Trough collector solver")); + }*/ + + /*if (m_n_r_iam_matrix < m_nColt) + { + m_error_msg = util::format("The number of groups of m_IAM coefficients (%d) is less than the number of collector types in this simulation (%d)", m_n_r_iam_matrix, m_nColt); + throw(C_csp_exception(m_error_msg, "Trough collector solver")); + }*/ + + //// Check that for each collector, at least 3 coefficients are != 0.0 + //for (int i = 0; i < m_nColt; i++) + //{ + // for (int j = 0; j < 3; j++) + // { + // if (m_IAM_matrix(i, j) == 0.0) + // { + // m_error_msg = util::format("For %d collectors and groups of m_IAM coefficients, each group of m_IAM coefficients must begin with at least 3 non-zero values. There are only %d non-zero coefficients for collector %d", m_nColt, j, i + 1); + // throw(C_csp_exception(m_error_msg, "Trough collector solver")); + // //message(TCS_ERROR, "For %d collectors and groups of m_IAM coefficients, each group of m_IAM coefficients must begin with at least 3 non-zero values. There are only %d non-zero coefficients for collector %d", m_nColt, j, i + 1); + // //return -1; + // } + // } + //} + } + + // Organize the emittance tables + { + //m_epsilon_3.init(4, 4); + //m_epsilon_3.addTable(&m_epsilon_3_11); //HCE #1 + //m_epsilon_3.addTable(&m_epsilon_3_12); + //m_epsilon_3.addTable(&m_epsilon_3_13); + //m_epsilon_3.addTable(&m_epsilon_3_14); + //m_epsilon_3.addTable(&m_epsilon_3_21); //HCE #2 + //m_epsilon_3.addTable(&m_epsilon_3_22); + //m_epsilon_3.addTable(&m_epsilon_3_23); + //m_epsilon_3.addTable(&m_epsilon_3_24); + //m_epsilon_3.addTable(&m_epsilon_3_31); //HCE #3 + //m_epsilon_3.addTable(&m_epsilon_3_32); + //m_epsilon_3.addTable(&m_epsilon_3_33); + //m_epsilon_3.addTable(&m_epsilon_3_34); + //m_epsilon_3.addTable(&m_epsilon_3_41); //HCE #4 + //m_epsilon_3.addTable(&m_epsilon_3_42); + //m_epsilon_3.addTable(&m_epsilon_3_43); + //m_epsilon_3.addTable(&m_epsilon_3_44); + + 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_TCS_T_htf_in.resize(m_nMod); + m_TCS_T_htf_out.resize(m_nMod); + m_TCS_T_htf_ave.resize(m_nMod); + m_q_loss.resize(m_nRecVar); + m_q_abs.resize(m_nRecVar); + //c_htf.resize(nMod); + //rho_htf.resize(nMod); + m_DP_tube.resize(m_nMod); + //E_abs_field.resize(nMod); + m_E_int_loop.resize(m_nMod); + m_E_accum.resize(m_nMod); + m_E_avail.resize(m_nMod); + //E_abs_max.resize(nMod); + //v_1.resize(nMod); + m_q_loss_SCAtot.resize(m_nMod); + m_q_abs_SCAtot.resize(m_nMod); + m_q_SCA.resize(m_nMod); + //E_fp.resize_fill(nMod, 0.); + m_q_1abs_tot.resize(m_nMod); + m_q_1abs.resize(m_nRecVar); + m_ColOptEff.resize(m_nMod); + + // Trough + m_q_SCA_control_df.resize(m_nMod); + //m_q_i.resize(m_nColt); // Not a vector? + //m_IAM.resize(m_nColt); // Not a vector? + m_EndGain.resize(m_nMod); + m_EndLoss.resize(m_nMod); + // m_RowShadow.resize(m_nColt); // Not a vector? + // + //Allocate space for transient variables + m_TCS_T_htf_ave_last.resize(m_nMod); + m_TCS_T_htf_ave_converged.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 + { + // Trough Version + //m_AnnulusGasMat.resize(m_nRecVar, m_nHCEVar); + //m_AbsorberPropMat.resize(m_nRecVar, m_nHCEVar); + //for (int i = 0; i < m_nHCEt; i++) { + // for (int j = 0; j < m_nHCEVar; j++) { + // //Set up a matrix of annulus gas properties + // m_AnnulusGasMat.at(i, j) = new HTFProperties(); + // m_AnnulusGasMat.at(i, j)->SetFluid((int)m_AnnulusGas.at(i, j)); + // //Set up a matrix of absorber prop materials + // m_AbsorberPropMat(i, j) = new AbsorberProps(); + // m_AbsorberPropMat(i, j)->setMaterial((int)m_AbsorberMaterial.at(i, j)); + // } + //} + + // 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 + m_defocus_old = 0.; + m_ncall = -1; + + //Set the defocus order to always be last->first in the loop + m_SCADefocusArray.resize(m_nMod); + for (int i = 0; i < m_nMod; i++) { + m_SCADefocusArray[i] = m_nMod - i; + } + + // for test start + init_fieldgeom(); + // for test end + + // Calculate tracking parasitics for when trough is on sun + m_W_dot_sca_tracking_nom = m_SCA_drives_elec * (double)(m_nMod * m_nLoops) / 1.E6; //[MWe] + + // 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 + + + return; } -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) -{ +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(). + */ + + //Calculate the total field aperture area + A_loop = (float)m_nMod * m_A_aperture; + m_Ap_tot = (float)m_nLoops * A_loop; + + 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) = 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)); + + //Allocate space for the D_hdr array + //m_D_hdr.resize(m_nhdrsec, 0.); + + //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., 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., 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 + 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 + //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_D_runner.resize(m_nrunsec); + m_L_runner.resize(m_nrunsec); + m_D_hdr.resize(m_nhdrsec);*/ + 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(2 * m_nrunsec); + m_L_runner.resize(2 * m_nrunsec); + m_D_hdr.resize(2 * m_nhdrsec); + m_L_hdr.resize(2 * m_nhdrsec); + + 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); + + /* ----- 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] = 50.; //Assume 50 [m] of runner piping in and around the power block before it heads out to the field in the main runners + 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] * 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]; } + N_run_mult = 1.0 + (1.0 - 50.0 / lsum); + + //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 = 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. * 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 + /*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(), v_cold, v_hot, v_loop_tot / float(nLoops), v_loop_tot, (v_hot * 2. + v_loop_tot), v_sgs, (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.; + + is_fieldgeom_init = true; //The field geometry has been initialized. Make note. + + + + + // TCS Temperature Tracking + m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = T_field_ini; //[K] + m_TCS_T_sys_h_converged = m_TCS_T_sys_h_last = T_field_ini; //[K] + //cc--> Note that stored(3) -> Iter is no longer used in the TRNSYS code. It is omitted here. + for (int i = 0; i < m_nMod; i++) + { + m_TCS_T_htf_ave_converged[i] = m_TCS_T_htf_ave_last[i] = T_field_ini; //[K] + } + + m_TCS_T_sys_c_last = T_field_ini; + m_TCS_T_sys_h_last = T_field_ini; + ////cc--> Note that stored(3) -> Iter is no longer used in the TRNSYS code. It is omitted here. + //for (int i = 0; i < nMod; i++) { + // T_htf_in0[i] = T_field_ini; + // T_htf_out0[i] = T_field_ini; + // T_htf_ave0[i] = T_field_ini; + //} + + // ********************************************* + // 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] + } + // ********************************************* + + return true; +} + +C_csp_collector_receiver::E_csp_cr_modes C_csp_fresnel_collector_receiver::get_operating_state() +{ + return E_csp_cr_modes::OFF; //[-] +} + +double C_csp_fresnel_collector_receiver::get_startup_time() +{ + return 3600; +} + +double C_csp_fresnel_collector_receiver::get_startup_energy() +{ + return 1.e-6; +} + +double C_csp_fresnel_collector_receiver::get_pumping_parasitic_coef() +{ + return 1.e-6; +} + +double C_csp_fresnel_collector_receiver::get_min_power_delivery() +{ + return 0; +} + +double C_csp_fresnel_collector_receiver::get_max_power_delivery(double T_cold_in /*C*/) +{ + return 0; +} + +double C_csp_fresnel_collector_receiver::get_tracking_power() +{ + return 0; +} + +double C_csp_fresnel_collector_receiver::get_col_startup_power() +{ + return 0; +} + +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_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 + 10.0) + { + 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_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_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 trough 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_CosTh_ave * m_IAM_ave * m_RowShadow_ave * m_EndLoss_ave) / + (m_I_bn_des * m_opteff_des) * m_m_dot_loop_des; //[kg/s] + m_dot_htf_loop = min(m_m_dot_htfmax, max(m_m_dot_htfmin, 0.8 * m_dot_ss + 0.2 * m_m_dot_htfmin)); //[kg/s] + } + + // 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_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 + 10.0) + { + 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_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_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 trough 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_trough_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 + int 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_trough_collector::on(...) COMPONENT defocus failed.")); + on_success = false; + } + + if (defocus_code != C_monotonic_eq_solver::CONVERGED) + { + throw(C_csp_exception("C_csp_trough_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_trough_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_trough_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_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_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.; + + 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.; + + } while (ss_diff / 200. > tol); + + // 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] + bool custom_sf_pipe_sizes = true; + 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; + + double rho_ave = m_htfProps.dens((m_T_loop_out_des + m_T_loop_in_des) / 2.0, 0.0); //kg/m3 + + 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); + + // 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] Trough 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 + */ + + 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; + } + + // TCS Temperature Tracking + m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = m_TCS_T_sys_c; //[K] + m_TCS_T_sys_h_converged = m_TCS_T_sys_h_last = m_TCS_T_sys_h; //[K] + for (int i = 0; i < m_nMod; i++) + { + m_TCS_T_htf_ave_converged[i] = m_TCS_T_htf_ave_last[i] = m_TCS_T_htf_ave[i]; + } + + // 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] + } + + m_ncall = -1; //[-] + + 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", + "Trough 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_costh_ini(m_costh); + double m_q_i_ini(m_q_i); + double m_IAM_ini(m_IAM); + util::matrix_t m_ColOptEff_ini(m_ColOptEff); + double m_EqOpteff_ini(m_EqOpteff); + vector m_EndGain_ini(m_EndGain); // changed from matrix to vector + vector m_EndLoss_ini(m_EndLoss); // changed from matrix to vector + std::vector m_RowShadow_ini(m_RowShadow); + std::vector m_q_SCA_ini(m_q_SCA); + double m_Theta_ave_ini(m_Theta_ave); + double m_CosTh_ave_ini(m_CosTh_ave); + double m_IAM_ave_ini(m_IAM_ave); + double m_RowShadow_ave_ini(m_RowShadow_ave); + double m_EndLoss_ave_ini(m_EndLoss_ave); + double m_dni_costh_ini(m_dni_costh); + 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_EqOpteff * m_costh; + + // Restore member variable values + m_costh = m_costh_ini; + m_q_i = m_q_i_ini; + m_IAM = m_IAM_ini; + m_ColOptEff = m_ColOptEff_ini; + m_EqOpteff = m_EqOpteff_ini; + m_EndGain = m_EndGain_ini; + m_EndLoss = m_EndLoss_ini; + m_RowShadow = m_RowShadow_ini; + m_q_SCA = m_q_SCA_ini; + m_Theta_ave = m_Theta_ave_ini; + m_CosTh_ave = m_CosTh_ave_ini; + m_IAM_ave = m_IAM_ave_ini; + m_RowShadow_ave = m_RowShadow_ave_ini; + m_EndLoss_ave = m_EndLoss_ave_ini; + m_dni_costh = m_dni_costh_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*/) +{ + // 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.; + + // Incidence angle + int doy = DateTime::CalculateDayOfYear(weather.m_year, weather.m_month, weather.m_day); // day of year + double time_start = ((doy - 1) * 24 + weather.m_hour + weather.m_minute / 60.) * 3600.; + double step = 3600.; + double time = time_start + step; + double time_hr = time / 3600.; // [hr] + double dt_hr = step / 3600.; // [hr] + double hour = fmod(time_hr, 24.); // [hr] + int day_of_year = (int)ceil(time_hr / 24.); // Day of the year + double B = (day_of_year - 1) * 360.0 / 365.0 * CSP::pi / 180.0; // Duffie & Beckman 1.5.3b + 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)); // Eqn of time in minutes + double Dec = 23.45 * sin(360.0 * (284.0 + day_of_year) / 365.0 * CSP::pi / 180.0) * CSP::pi / 180.0; // Declination in radians (Duffie & Beckman 1.6.1) + double SolarNoon = 12. - ((m_shift) * 180.0 / CSP::pi) / 15.0 - EOT / 60.0; // Solar Noon and time in hours + double HrA = hour - dt_hr; + double StdTime = HrA + 0.5 * dt_hr; + double SolarTime = StdTime + ((m_shift) * 180.0 / CSP::pi) / 15.0 + EOT / 60.0; + double omega = (SolarTime - 12.0) * 15.0 * CSP::pi / 180.0; // m_hour angle (arc of sun) in radians + double SolarAlt = asin(sin(Dec) * sin(m_latitude) + cos(m_latitude) * cos(Dec) * cos(omega)); + double SolarAz = (weather.m_solazi - 180.) * m_d2r; // [rad] Solar azimuth angle + double CosTh = m_costh = sqrt(1.0 - pow(cos(SolarAlt - m_ColTilt) - cos(m_ColTilt) * cos(SolarAlt) * (1.0 - cos(SolarAz - m_ColAz)), 2)); + double Theta = acos(CosTh); // [rad] + + // Incidence angle modifier (IAM) from Burkholder & Kutscher 2008 + double IamF1 = 0.000884; + double IamF2 = -0.0000537; + double IAM; + if (CosTh == 0) { + IAM = 0; + } + else { + IAM = std::min(1., (CosTh + IamF1 * Theta + IamF2 * pow(Theta, 2.)) / CosTh); + } + + // Heat loss, where temperatures are in [C] per Burkholder & Kutscher 2008 + // model coefficients for 2008 Schott PTR70 (vacuum) receiver + double A0 = 4.05; + double A1 = 0.247; + double A2 = -0.00146; + double A3 = 5.65e-06; + double A4 = 7.62e-08; + double A5 = -1.7; + double A6 = 0.0125; + double PerfFac = 1; + double T_amb = weather.m_tdry; // [C] + double W_spd = std::abs(weather.m_wspd); + double DNI = weather.m_beam; + double T_out_des = m_T_loop_out_des - 273.15; // [C] (converted from [C] to [K] in init and now back to [C]) + double T_in_des = m_T_loop_in_des - 273.15; // [C] (converted from [C] in [K] in init and now back to [C]) + double HLTerm1 = (A0 + A5 * sqrt(W_spd)) * (T_out_des - T_in_des); + double HLTerm2 = (A1 + A6 * sqrt(W_spd)) * ((pow(T_out_des, 2) - pow(T_in_des, 2)) / 2. - T_amb * (T_out_des - T_in_des)); + double HLTerm3 = ((A2 + A4 * DNI * CosTh * IAM) / 3.) * (pow(T_out_des, 3) - pow(T_in_des, 3)); + double HLTerm4 = (A3 / 4.) * (pow(T_out_des, 4) - pow(T_in_des, 4)); + double HL = (HLTerm1 + HLTerm2 + HLTerm3 + HLTerm4) / (T_out_des - T_in_des); //[W/m] + double HL_hces = std::max(HL * m_L_tot * m_nLoops * PerfFac, 0.); // [W] convert from W/m to W for entire field + + // Piping heat loss, at average hot/cold design temperature + double T_avg_des = 0.5 * (T_out_des + T_in_des); // [C] + double HL_headers = m_nfsec * (2 * m_nhdrsec) * m_L_crossover * m_D_hdr[m_nhdrsec] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + double HL_runners = 0.; + for (int i = 0; i < 2 * m_nrunsec; i++) { + HL_runners += 2. * m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + } + + double HL_total = HL_hces + HL_headers + HL_runners; + return std::max(1. - HL_total * 1.e-6 / q_incident, 0.); +} + +double C_csp_fresnel_collector_receiver::get_collector_area() +{ + return m_Ap_tot; +} + + + + + + + + +// ------------------------------------------------------------------- PUBLIC SUPPLEMENTAL + +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(pi - m_theta_dep)) * acos((DepHr1 * DepHr2 + sqrt(DepHr1 * DepHr1 - DepHr2 * DepHr2 + 1.0)) / (DepHr1 * DepHr1 + 1.0)) * 180.0 / 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(pi - m_theta_stow)) * acos((StwHr1 * StwHr2 + sqrt(StwHr1 * StwHr1 - StwHr2 * StwHr2 + 1.0)) / (StwHr1 * StwHr1 + 1.0)) * 180.0 / 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; + double 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; + } + + 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)); + + + if (weather.m_solzen < pi / 2.) { + //Convert the solar angles to collector incidence angles + CSP::theta_trans(SolarAz, weather.m_solzen, m_ColAz, phi_t, theta_L); + + switch (m_opt_model) + { + case 1: //sun position + //user provides an optical table as a function of solar position + eta_optical = eta_opt_fixed * max(optical_table.interpolate(SolarAz, min(weather.m_solzen, pi / 2.)), 0.0); + break; + case 2: //incidence angle table + //user provides an optical table as a function of collector incidence angles + eta_optical = eta_opt_fixed * max(optical_table.interpolate(phi_t, max(theta_L, 0.0)), 0.0); + break; + case 3: //incidence angle modifier polys + //Otherwise, calculate the collector incidence angles for the IAM equations + eta_optical = eta_opt_fixed * + CSP::poly_eval(phi_t, &m_IAM_T_coefs[0], m_IAM_T_coefs.size()) * + CSP::poly_eval(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; + } + + eta_optical *= m_ftrack; + } + else { + eta_optical = 0.0; + phi_t = pi / 2.; + theta_L = 0.0; + } + + m_q_i = m_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) = eta_optical; + m_q_SCA[j] = m_q_i; //[W/m] The flux on the collector + } + + 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 + // 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 trough is not absorbing any sunlight (night or 100% defocus), then set member data as necessary + m_costh = 0.0; //[-] Cosine of the incident angle between the sun and trough aperture + + m_q_i = 0; //[W/m] DNI * A_aper / L_sca + //m_IAM.assign(m_IAM.size(), 0.0); //[-] Incidence angle modifiers + m_IAM = 0; //[-] Incidence angle modifiers NOT a vector (only one collector type) + m_ColOptEff.fill(0.0); //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack + m_EqOpteff = 0.; + //m_EndGain.fill(0.0); //[-] Light from different collector hitting receiver + //m_EndLoss.fill(0.0); //[-] Light missing receiver due to length + end gain + std::fill(m_EndGain.begin(), m_EndGain.end(), 0); //[-] Light from different collector hitting receiver NO longer a matrix + std::fill(m_EndLoss.begin(), m_EndLoss.end(), 0); //[-] Light missing receiver due to length + end gain NO longer a matrix + m_RowShadow.assign(m_RowShadow.size(), 0.0); //[-] Row-to-row m_Shadowing losses + 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_Theta_ave = 0.0; + m_CosTh_ave = 0.0; + m_IAM_ave = 0.0; + m_RowShadow_ave = 0.0; + m_EndLoss_ave = 0.0; + m_dni_costh = 0.0; + 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] + + return; +} + +void C_csp_fresnel_collector_receiver::loop_optical_wind_stow() +{ + // Want to completely defocus trough 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, m_costh, and input defocus to calculate m_q_SCA_control_df + + // Store control defocus + m_control_defocus = defocus; + + // Uses m_q_i, m_costh, and input defocus to calculate m_q_SCA_control_df + + // Store control defocus + m_control_defocus = defocus; + + if (m_fthrctrl == 0) + { + mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." + " The model will instead use Simultaneous Partial Defocusing"); + m_fthrctrl = 2; + } + if (m_fthrctrl == 1) + { + mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, partially defocusing SCAs is not available." + " The model will instead use Simultaneous Partial Defocusing"); + m_fthrctrl = 2; + } + if (m_fthrctrl == 2) + { + for (int i = 0; i < m_nMod; i++) + { + m_q_SCA_control_df[i] = defocus * m_q_i * m_costh; + } + } + +} + +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; + + if (m_fthrctrl == 0) + { + mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." + " The model will instead use Simultaneous Partial Defocusing"); + m_fthrctrl = 2; + } + if (m_fthrctrl == 1) + { + mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, partially defocusing SCAs is not available." + " The model will instead use Simultaneous Partial Defocusing"); + m_fthrctrl = 2; + } + if (m_fthrctrl == 2) + { + for (int i = 0; i < m_nMod; i++) + { + //int CT = (int)m_SCAInfoArray(i, 1) - 1; // Collector type + m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; + } + } +} + +//---------------------------------------------------------------------- PUBLIC SUPPLEMENTAL (from sam_mw_lf_type262_salt) + + + + +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 * pi))); + //Calculate the total volume + V[i] = pow(D[i], 2) / 4. * pi * L_line[i]; + psum += V[i]; + } + + delete[] V_dot; + delete[] D; + delete[] V; + + return psum; + +} + +/* + 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 + ---------------------------------------------------------------------------------------------------------------------- + */ +void C_csp_fresnel_collector_receiver::EvacReceiver(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, int ncall, double time, + //outputs + double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args) +{ + + //cc -- note that collector/hce geometry is part of the parent class. Only the indices specifying the + // number of the HCE and collector need to be passed here. + + //---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 = m_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] + } + 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] + + } + + 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(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(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(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(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(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(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(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(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; - - return; -} +}; +/* + ################################################################################################################# + ################################################################################################################# + ################################################################################################################# -bool C_csp_fresnel_collector_receiver::init_fieldgeom() -{ - return true; -} + "****************************************************************************************************************************** + 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. -C_csp_collector_receiver::E_csp_cr_modes C_csp_fresnel_collector_receiver::get_operating_state() -{ - return E_csp_cr_modes::OFF; //[-] -} + 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 C_csp_fresnel_collector_receiver::fT_2(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 + // Input units ( K , K , real, m/s, - , -) + + 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_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 C_csp_fresnel_collector_receiver::FQ_34CONV(double T_3, double T_4, double P_6, double v_6, double T_6, int hv, double& q_34conv, double& h_34) { + // UNITS ( K , K , Pa , m/s, K , -, -, W/m, W/m2-K) + + 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 C_csp_fresnel_collector_receiver::FQ_34RAD(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34) { + //units (K, K, K, -, -, -, W/m, W/m2-K) + 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); + } -double C_csp_fresnel_collector_receiver::get_startup_time() -{ - return 3600; -} -double C_csp_fresnel_collector_receiver::get_startup_energy() -{ - return 1.e-6; } -double C_csp_fresnel_collector_receiver::get_pumping_parasitic_coef() + +/****************************************************************************************************************************** + 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 C_csp_fresnel_collector_receiver::FQ_56CONV(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6) +// units ( K , K , torr, m/s, W/m , W/m2-K) { - return 1.e-6; + 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; + } + + 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] + } + } } -double C_csp_fresnel_collector_receiver::get_min_power_delivery() -{ - return 0; +/****************************************************************************************************************************** + 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 C_csp_fresnel_collector_receiver::FQ_COND_BRACKET(double T_3, double T_6, double P_6, double v_6) { + // units ( K , K , bar, m/s) + + 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] + } -double C_csp_fresnel_collector_receiver::get_max_power_delivery(double T_cold_in /*C*/) +/****************************************************************************************************************************** + FUNCTION fk_23: Absorber conductance +******************************************************************************************************************************" +{ Based on linear fit of data from "Alloy Digest, Sourcebook, Stainless Steels"; ASM International, 2000.} +*/ +double C_csp_fresnel_collector_receiver::FK_23(double T_2, double T_3, int hv) { - return 0; + 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); + } -double C_csp_fresnel_collector_receiver::get_tracking_power() -{ - return 0; +/* + *************************************************************************************************** + 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 / (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 * g); + DP_pipe = HL_pm * rho * g * 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 * g * Nels; + DP_elm = 0.75 * D / f * HL_pm * rho * g * Nelm; + DP_ell = 0.6 * D / f * HL_pm * rho * g * Nell; + DP_gav = 0.19 * D / f * HL_pm * rho * g * Ngav; + DP_glv = 10.0 * D / f * HL_pm * rho * g * Nglv; + DP_chv = 2.5 * D / f * HL_pm * rho * g * Nchv; + DP_lw = 1.8 * D / f * HL_pm * rho * g * Nlw; + DP_lcv = 10.0 * D / f * HL_pm * rho * g * Nlcv; + DP_bja = 8.69 * D / f * HL_pm * rho * g * 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; + } -double C_csp_fresnel_collector_receiver::get_col_startup_power() -{ +/************************************************************************************************** + 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; } -void C_csp_fresnel_collector_receiver::get_design_parameters(C_csp_collector_receiver::S_csp_cr_solved_params& solved_params) -{ - return; -} +// ******************************************************************** STATIC Methods -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) +// 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) { - - return; -} + int nrnrsec = (int)floor(float(nfieldsec) / 4.0) + 1; -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) -{ -} + 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; + } -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) -{ - return; + 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); } -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) +// 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) { - return; -} + 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"); } -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) -{ - return; -} + 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; + } -void C_csp_fresnel_collector_receiver::converged() -{ - return; + double m_dot_oneloop = m_dot_field / float(nLoopsField); + return m_dot_field / float(nfieldsec) - ihdr_onedir * 2 * m_dot_oneloop; } -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) -{ -} -double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_csp_weatherreader::S_outputs& weather, const C_csp_solver_sim_info& sim) +// ******************************************************************** 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_trough->apply_component_defocus(defocus); + + // Solve the loop energy balance at the input mass flow rate + int exit_code = mpc_trough->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_trough->m_T_htf_out_t_end[mpc_trough->m_nMod - 1]; + return 0; } -double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/) +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 + int exit_code = mpc_trough->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_trough->m_T_htf_out_t_end[mpc_trough->m_nMod - 1]; + return 0; } -double C_csp_fresnel_collector_receiver::get_collector_area() +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_trough->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_trough->m_m_dot_htf_tot * mpc_trough->m_c_htf_ave_ts_ave_temp * + (T_htf_cold_in - mpc_trough->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_trough->m_Q_field_losses_total_subts) / mpc_trough->m_Q_field_losses_total_subts; //[-] + return 0; } diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 5dc136da4..bf3a04f8f 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -34,26 +34,475 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define __csp_solver_fresnel_collector_receiver_ #include "csp_solver_core.h" +#include "htf_props.h" +#include "sam_csp_util.h" + class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver { public: + + enum + { + E_THETA_AVE, //[deg] + E_COSTH_AVE, //[-] + E_IAM_AVE, //[-] + E_ROWSHADOW_AVE, //[-] + E_ENDLOSS_AVE, //[-] + E_DNI_COSTH, //[W/m2] + 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_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] + }; + + + // Private Fields +private: + + // Fields in Trough + + C_csp_reported_outputs mc_reported_outputs; + + 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_L_hdr; //[m] Lengths of 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_DP_loop; //[bar] Pressure drop in loop sections + std::vector m_T_loop_dsn; //[C] Temperature entering loop sections at design + std::vector m_P_loop_dsn; //[bar] Gauge pessure in loop sections at design + std::vector m_T_loop; //[K] Temperature entering loop sections + std::vector m_P_loop; //[Pa] Gauge pessure in loop sections + + OpticalDataTable optical_table; + + HTFProperties m_htfProps, m_airProps; + + // 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 + + // 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() + + // Parameters calculated in init() + //int m_n_c_iam_matrix; //[-] Number of columns in the IAM matrix + //int m_n_r_iam_matrix; //[-] Number of rows in the IAM matrix + double m_v_hot; //[m^3] Hot piping volume + double m_v_cold; //[m^3] Cold piping volume + double m_Ap_tot; //[m^2] Total field aperture area + int m_nfsec; //[-] Number of field sections + int m_nhdrsec; //[-] Number of header sections + int m_nrunsec; //[-] Number of unique runner diameters + double m_L_tot; //[m] Total length of collectors in a loop + double m_opteff_des; //[-] Design-point optical efficieny (theta = 0) from the solar field + double m_m_dot_design; //[kg/s] Total solar field mass flow rate at design + double m_m_dot_loop_des;//[kg/s] LOOP design mass flow rate + double m_q_design; //[Wt] Design-point thermal power from the solar field + double m_W_dot_sca_tracking_nom; //[MWe] Tracking parasitics when trough is on sun + + 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 + + double m_defocus_old; //[-] Defocus during previous call (= 1 at first call) + int m_ncall; //[-] Track number of calls per timestep, reset = -1 in converged() call + + // Variables that are passed between methods, but not necessary to carry over timesteps + 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 + + 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?) + + 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_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 + double m_q_i; //[W/m] DNI * A_aper / L_sca NOT a vector because all collector lengths are equal + // This value (m_q_SCA) is passed to the Evacuated Tube model, where the other optical losses are applied + std::vector m_q_SCA; //[W/m] Total incident irradiation on the receiver (q"*A_aper/L_sca*cos(theta)*all_defocus) + std::vector m_q_SCA_control_df; //[W/m] Total incident irradiation less CONTROL defocus (m_q_sca * control_defocus) + + double m_IAM; //[-] Incidence angle modifiers NOT a vector becuase only one collector type ? + std::vector m_RowShadow; //[-] Row-to-row shadowing losses + + /*m_nColt, m_nSCA*/ + util::matrix_t m_ColOptEff; //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack + vector m_EndGain; //[-] Light from different collector hitting receiver // NOT a matrix because only one collector type + vector m_EndLoss; //[-] Light missing receiver due to length // NOT a matrix because only one collector type + + double m_Theta_ave; //[rad] Field average m_theta value (but... nothing in our model allows for this to different over SCAs) + + double m_CosTh_ave; //[-] Field average costheta value + double m_IAM_ave; //[-] Field average incidence angle modifier + double m_RowShadow_ave; //[-] Field average row shadowing loss + double m_EndLoss_ave; //[-] Field average end loss + + 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_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 + double m_EqOpteff; //[-] + 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...) + double m_q_dot_inc_sf_tot; //[MWt] Total incident radiation on solar field + + 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_cold; //[J/kg-K] Specific heat of fluid at m_T_sys_c + 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_P_field_in; + + // Classes that are defined as member data so are re-declared each time performance function is called + std::vector m_DP_tube; //[Pa] Pressure drops in each receiver + + // ********************************************* + // TCS Temperature Tracking + // Temperatures from the most recent converged() operation + double m_TCS_T_sys_c_converged; //[K] Temperature (bulk) of cold runners & headers in previous timestep + std::vector m_TCS_T_htf_ave_converged; //[K] Average HTF temperature in each SCA + double m_TCS_T_sys_h_converged; //[K] Temperature (bulk) of hot runners & headers in previous timestep + + // Temperatures from the most recent timestep (in the event that a method solves multiple, shorter timesteps) + double m_TCS_T_sys_c_last; //[K] Temperature (bulk) of cold runners & headers in previous timestep + std::vector m_TCS_T_htf_ave_last; //[K] Average HTF temperature in each SCA + double m_TCS_T_sys_h_last; //[K] Temperature (bulk) of hot runners & headers in previous timestep + + // Latest temperatures solved during present call to this class + double m_TCS_T_sys_c; //[K] Temperature (bulk) of cold runners & headers + std::vector m_TCS_T_htf_in; //[K] Inlet HTF temperature to each SCA + std::vector m_TCS_T_htf_ave; //[K] Average HTF temperature in each SCA + std::vector m_TCS_T_htf_out; //[K] Outlet HTF temperature to each SCA + double m_TCS_T_sys_h; //[K] Solar field HTF outlet temperature + // ********************************************* + // ********************************************* + + // ********************************************* + // 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_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_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 + + double m_dP_total; //[bar] FIELD pressure drop + double m_W_dot_pump; //[MWe] FIELD pumping power + + bool m_is_m_dot_recirc; //[-] True: trough is recirculationg HTF with interacting with other CSP components + + bool m_ss_init_complete; //[-] For TCS-based model in acceptance testing, has model achieved steady state at first timestep? + + // Member variables that are used to store information for the EvacReceiver method + double m_T_save[5]; //[K] Saved temperatures from previous call to EvacReceiver single SCA energy balance model + std::vector mv_reguess_args; //[-] Logic to determine whether to use previous guess values or start iteration fresh + + 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 + + // member string for exception messages + std::string m_error_msg; + + C_csp_collector_receiver::E_csp_cr_modes m_operating_mode_converged; + C_csp_collector_receiver::E_csp_cr_modes m_operating_mode; + + // *********************** + // ***** T E M P ****** + double m_step_recirc; + + // Fields NOT in Trough + double N_run_mult; + bool + no_fp, //Freeze protection flag + is_fieldgeom_init; //Flag to indicate whether the field geometry has been initialized + + double A_loop; + + const double Pi = acos(-1); + const double pi = Pi; + const double d2r = pi / 180.; + const double r2d = 180. / pi; + const double g = 9.81; //gravitation constant + const double mtoinch = 39.3700787; //[m] -> [in] + + double m_htf_prop_min; + + double eta_optical; //Collector total optical efficiency + double eta_opt_fixed; + double phi_t; //Solar incidence angle in the collector transversal plane + double theta_L; //Solar incidence angle in the collector longitudinal plane + + emit_table m_epsilon_abs; + + double T_loop_outX; + + std::vector mv_HCEguessargs; + + string m_piping_summary; + + // Private Methods +private: + + // Methods IN trough + + 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 + int 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); + + // Methods NOT in trough + + 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*); + + // Public Input Fields +public: + + int m_nMod; // Number of collector modules in a loop (m_nSCA) + int m_nRecVar; // Number of receiver variations (m_nHCEt) + int m_nLoops = -1; // [-] Number of loops in the field + 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_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 + double m_SCA_drives_elec; // [W/SCA] Tracking power, in Watts per SCA drive + 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 + + int m_fthrok; // [-] Flag to allow partial defocusing of the collectors + int m_fthrctrl; // [-] Defocusing strategy + double m_ColAz; // [deg] Collector azimuth angle + double m_ColTilt; // [deg] Collector tilt angle (0 is horizontal, 90deg is vertical) + + double m_solar_mult; // [-] Solar multiple + 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 + + std::vector m_SCADefocusArray; //[-] Order in which the SCA's should be defocused + + int m_opt_model; // The optical model (1=Solar position ; 2=Collector incidence table ; 3 = IAM polys) + 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_epsilon_glass; // Glass envelope emissivity + vector m_Tau_envelope; // [-] Envelope transmittance + + 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 + + // Fresnel Only Inputs + 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 + + // Removed + //double m_Row_Distance; //[m] Spacing between rows (centerline to centerline) + //double m_wind_stow_speed;//[m/s] Wind speed at and above which the collectors will be stowed + //bool m_accept_init; //[-] In acceptance testing mode - require steady-state startup + //int m_accept_loc; // Treat as = 1 //[-] In acceptance testing mode - temperature sensor location (1=hx,2=loop) + //bool m_calc_design_pipe_vals; // Treat as FALSE //[-] Should the HTF state be calculated at design conditions + //bool m_is_using_input_gen; // Treat as FALSE + + // Questionable + double m_I_b; //Direct normal incident solar irradiation + + // 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(); @@ -98,7 +547,135 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver virtual double get_collector_area(); + // ------------------------------------------ supplemental methods ----------------------------------------------------------- + + // Methods IN trough + + 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); + + void EvacReceiver(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, int ncall, double time, + //outputs + double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args); + + double fT_2(double q_12conv, double T_1, double T_2g, double v_1, int hv); + + void FQ_34CONV(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(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34); + + void FQ_56CONV(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6); + + double FQ_COND_BRACKET(double T_3, double T_6, double P_6, double v_6); + + double FK_23(double T_2, double T_3, int hv); + + + 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); + + // *********************************** BONUS Methods from Trough + + 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); + + + // 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_trough; + 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_trough, 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_trough = pc_trough; + 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_trough; + 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_trough, const C_csp_weatherreader::S_outputs& weather, + double T_htf_cold_in /*K*/, const C_csp_solver_sim_info& sim_info) + { + mpc_trough = pc_trough; + 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_trough; + 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_trough, const C_csp_weatherreader::S_outputs& weather, + double m_dot_loop /*kg/s*/, const C_csp_solver_sim_info& sim_info) + { + mpc_trough = pc_trough; + 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 /*-*/); + }; }; From 6d0aa0bbd4cb573ea3ff72ae2459512b704642c4 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Wed, 8 Feb 2023 08:22:42 -0700 Subject: [PATCH 03/46] Fix Solar Zenith angle (convert to radians). Continue transitioning fresnel to current CSP solver --- ssc/cmod_fresnel_physical.cpp | 3 + tcs/csp_solver_fresnel_collector_receiver.cpp | 548 ++++++++++++++++-- tcs/csp_solver_fresnel_collector_receiver.h | 7 + 3 files changed, 525 insertions(+), 33 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index fcaee4bdd..d63c95a0e 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -481,6 +481,9 @@ class cm_fresnel_physical : public compute_module c_fresnel.m_rec_htf_vol = as_number("rec_htf_vol"); + // Hard Coded (currently no UI) + c_fresnel.m_L_rnr_pb = 25; + //////////////////////// Questionable c_fresnel.m_I_b = as_number("I_b"); diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index e998a2ec7..7708c698b 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -295,6 +295,464 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we { 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 + + // This values is the Bulk Temperature at the *end* of the timestep (type 262 line 1551; trough line 1187) + //T_sys_c = (T_sys_c_last - T_cold_in_1) * exp(-(m_dot_htf * float(nLoops)) / (v_cold * rho_hdr_cold + mc_bal_cold / c_hdr_cold_last) * dt) + T_cold_in_1; + 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 fully 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_rnr[i] - T_db); //[W] + m_Runner_hl_cold_tot += 2. * 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_hdr[i] - T_db); //[W] + m_Header_hl_cold_tot += m_nfsec * m_Header_hl_cold; + } + + 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] + 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); + + // 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] + + //double Pipe_hl_cold = m_Runner_hl_cold_tot + m_Header_hl_cold_tot; + + 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); + 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_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] + E_sca.resize(m_nMod); + E_sca_htf.resize(m_nMod); + E_sca_abs.resize(m_nMod); + E_sca_bal.resize(m_nMod); + + std::vector q_dot_loss_xover; //[W] + q_dot_loss_xover.resize(m_nMod - 1); + + std::vector E_xover, E_xover_htf, E_xover_abs, E_xover_bal; + 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); + double q_inc_total = 0.; + double q_abs_abs_total = 0.; + double q_abs_htf_total = 0.; + std::vector m_EqOpteffs(m_nMod, 0.); + + // Loop through each Module + 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; + + if (weather.m_beam > 300) + int x = 0; + + double c_htf_j, rho_htf_j; + + EvacReceiver(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, m_ncall, sim_info.ms_ts.m_time / 3600.0, + //outputs + m_q_loss[j], m_q_abs[j], m_q_1abs[j], c_htf_j, rho_htf_j, mv_HCEguessargs); + + // 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 + 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 + m_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 * m_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 + double m_node = rho_htf_i * m_A_cs[0] * m_L_mod; + + if (weather.m_beam > 300) + int x = 0; + + double old_T_end = m_T_htf_out_t_end[i]; + + + // 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; + + + double new_T_end = m_T_htf_out_t_end[i]; + + double delta_T = new_T_end - old_T_end; + + + // Original Fresnel Eqn + { + //MJW 12.14.2010 The first term should represent the difference between the previous average temperature and the new + //average temperature. Thus, the heat addition in the first term should be divided by 2 rather than include the whole magnitude + //of the heat addition. + //mjw & tn 5.1.11: There was an error in the assumption about average and outlet temperature + double T_htf_out = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + + 2.0 * (m_TCS_T_htf_ave_converged[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (2.0 * m_dot_htf_loop * c_htf_i)) * + exp(-2. * m_dot_htf_loop * c_htf_i * dt / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)); + //Recalculate the average temperature for the SCA + double T_htf_ave = (m_T_htf_in_t_int[i] + T_htf_out) / 2.0; + } + + break; + } + + // Polynomial Model + case 1: + { + double V_wind = weather.m_wspd; + double dt = sim_info.ms_ts.m_step; + T_node_ave = m_TCS_T_htf_ave_converged[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; + + //Recalculate the average temperature for the SCA + m_TCS_T_htf_ave_converged[i] = (m_TCS_T_htf_ave_converged[i] + m_T_htf_out_t_end[i]) / 2.0; + + errhl = T_node_ave - m_TCS_T_htf_ave_converged[i]; //iterate until the node temperature does not vary significantly + + T_node_ave = m_TCS_T_htf_ave_converged[i]; + } + + m_q_1abs_tot[i] = m_q_loss_SCAtot[i] / m_L_mod; + m_EqOpteff = eta_optical; //Use the optical efficiency as it is for this option + + break; + + } + } + + //Calculate the mass of HTF associated with this node + m_node = rho_htf_i * m_A_cs.at(0) * m_L_mod; + + //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); + m_E_accum[i] = x1 * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]); + 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] + } + + + // Now calculate an energy balance using the timestep-average Bulk Temperature + // ** THIS IS JUST A TEST: can comment out if necessary ** + E_sca[i] = (m_A_cs[0] * m_L_mod * rho_htf_i * c_htf_i + m_L_mod * m_mc_bal_sca) * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]) * 1.E-6; //[MJ] SCA basis + E_sca_htf[i] = m_dot_htf_loop * c_htf_i * (m_T_htf_out_t_int[i] - m_T_htf_in_t_int[i]) * sim_info.ms_ts.m_step / 1.E6; + E_sca_abs[i] = m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step / 1.E6; + E_sca_bal[i] = E_sca_abs[i] - E_sca_htf[i] - E_sca[i]; + + //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] * pi * L_int * (m_T_htf_out_t_int[i] - T_db) + / (m_dot_htf_loop * c_htf_i); + //Add the internal energy of the crossover piping + 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); + + 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] + + } + + + } + + T_loop_outX = m_T_htf_out_t_int[m_nMod - 1]; + + 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 * (m_T_hdr[i] - T_db); + m_Header_hl_hot_tot += m_nfsec * 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); + 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[i] * CSP::pi * m_D_runner[D_index] * m_Pipe_hl_coef * (m_T_rnr[i] - T_db); //Wt + m_Runner_hl_hot_tot += 2. * m_Runner_hl_hot; + D_index++; + } + 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] + + // 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; + + 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_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_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_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, 5) * 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; +} + + +int C_csp_fresnel_collector_receiver::x(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) +{ + m_m_dot_htf_tot = m_dot_htf_loop * float(m_nLoops); + // First calculate the cold header temperature, which will serve as the loop inlet temperature double rho_hdr_cold = m_htfProps.dens(m_T_sys_c_t_end_last, 1.0); // [kg/m3] double rho_hdr_hot = m_htfProps.dens(m_T_sys_h_t_end_last, 1.0); // [kg/m3] @@ -318,13 +776,6 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we double E_HR_cold_losses = 0.0; // [MJ] double E_HR_cold_bal = 0.0; // [MJ] - // TEMPORARY by TAYLOR - - //m_q_i = m_I_b * m_A_aperture / m_L_mod; //[W/m] The incoming solar irradiation per aperture length - //for (int j = 0; j < m_nMod; j++) { - // m_q_SCA[j] = m_q_i; //[W/m] The flux on the collector - //} - //if(m_accept_loc == E_piping_config::FIELD) if (true) { @@ -538,11 +989,17 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we (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)); + //Adjust the loop outlet temperature to account for thermal losses incurred in the hot header and the runner pipe + //double T_sys_h = T_loop_outX - Pipe_hl_hot / (m_m_dot_htf_tot * m_c_hdr_hot); + + // From old fresnel type 262 line 1724 + //T_sys_h = (m_TCS_T_sys_h_last - T_sys_h) * exp(-m_m_dot_htf_tot / (m_v_hot * rho_hdr_hot + m_mc_bal_hot / m_c_hdr_hot) * dt) + T_sys_h; + // from trough - 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; + //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 actual amount of energy absorbed by the field that doesn't go into changing the SCA's average temperature @@ -835,8 +1292,8 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n //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); + //if ((int)D_hdr.size() != nhsec) D_hdr.resize(nhsec); + //if ((int)D_runner.size() != nrunsec) D_runner.resize(nrunsec); //---- int nst, nend, nd; @@ -854,11 +1311,13 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n //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 * pi))); + D_runner.at(2 * nrunsec - 1) = CSP::pipe_sched(sqrt(4. * m_dot_ts / (rho * V_max * 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 * pi))); + D_runner[2 * nrunsec - i - 1] = CSP::pipe_sched(sqrt(4. * m_dot_temp / (rho * V_max * pi))); m_dot_temp = max(m_dot_temp - m_dot_hdr * 2, 0.0); } } @@ -1601,7 +2060,8 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() else { x1 = 1.; //the first runners are short } - m_L_runner[0] = 50.; //Assume 50 [m] of runner piping in and around the power block before it heads out to the field in the main runners + m_L_runner[0] = m_L_rnr_pb; + m_L_runner[2 * m_nrunsec - 1] = m_L_rnr_pb; // assume symmetric runners (TB) 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.); @@ -1721,42 +2181,66 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() C_csp_collector_receiver::E_csp_cr_modes C_csp_fresnel_collector_receiver::get_operating_state() { - return E_csp_cr_modes::OFF; //[-] + return m_operating_mode_converged; } double C_csp_fresnel_collector_receiver::get_startup_time() { - return 3600; + throw("C_csp_fresnel_collector_receiver::get_startup_time() is not complete"); + + + return std::numeric_limits::quiet_NaN(); } double C_csp_fresnel_collector_receiver::get_startup_energy() { - return 1.e-6; + throw(C_csp_exception("C_csp_fresnel_collector_receiver::get_startup_energy() is not complete")); + + + return std::numeric_limits::quiet_NaN(); } double C_csp_fresnel_collector_receiver::get_pumping_parasitic_coef() { - return 1.e-6; + 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]; + 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() { - return 0; + 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*/) { - return 0; + 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 0; + return m_SCA_drives_elec * 1.e-6 * m_nMod * m_nLoops; //MWe } double C_csp_fresnel_collector_receiver::get_col_startup_power() { - return 0; + throw(C_csp_exception("C_csp_fresnel_collector_receiver::get_col_startup_power() is not complete")); + return std::numeric_limits::quiet_NaN(); //MWe-hr } void C_csp_fresnel_collector_receiver::get_design_parameters(C_csp_collector_receiver::S_csp_cr_solved_params& solved_params) @@ -2612,13 +3096,6 @@ double C_csp_fresnel_collector_receiver::get_collector_area() return m_Ap_tot; } - - - - - - - // ------------------------------------------------------------------- PUBLIC SUPPLEMENTAL void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreader::S_outputs& weather, @@ -2631,6 +3108,9 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade } else { + if (weather.m_beam > 300) + int x = 0; + // First, clear all the values calculated below loop_optical_eta_off(); @@ -2700,6 +3180,10 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade 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 @@ -2712,8 +3196,9 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade // 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 * d2r; // Convert from degree to radian - if (weather.m_solzen < pi / 2.) { + if (SolarZenRad < pi / 2.) { //Convert the solar angles to collector incidence angles CSP::theta_trans(SolarAz, weather.m_solzen, m_ColAz, phi_t, theta_L); @@ -2907,9 +3392,6 @@ void C_csp_fresnel_collector_receiver::apply_component_defocus(double defocus /* //---------------------------------------------------------------------- PUBLIC SUPPLEMENTAL (from sam_mw_lf_type262_salt) - - - double C_csp_fresnel_collector_receiver::Pump_SGS(double rho, double m_dotsf, double sm) { int nl = 8; diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index bf3a04f8f..0d00decf9 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -363,6 +363,11 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver void set_output_value(); + // TEMPORARY + int x(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); + // This method is designed to pass the timestep integrated HTF temperature to successive energy balance nodes int 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*/, @@ -466,6 +471,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 + // Removed //double m_Row_Distance; //[m] Spacing between rows (centerline to centerline) //double m_wind_stow_speed;//[m/s] Wind speed at and above which the collectors will be stowed From 8b110bc3b17567f002d12570ede66c7cdbd5fdce Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Mon, 13 Feb 2023 16:37:17 -0700 Subject: [PATCH 04/46] Continue transitioning linear fresnel to csp solver. Now is functional with comparable results. --- ssc/cmod_fresnel_physical.cpp | 1111 +++++++++++----- tcs/csp_solver_fresnel_collector_receiver.cpp | 1114 +++++------------ tcs/csp_solver_fresnel_collector_receiver.h | 83 +- 3 files changed, 1199 insertions(+), 1109 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index d63c95a0e..5bfc9fc85 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -44,6 +44,68 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. static var_info _cm_vtab_fresnel_physical[] = { + // Weather Reader + { SSC_INPUT, SSC_STRING, "file_name", "Local weather file with path", "none", "", "weather", "*", "LOCAL_FILE", "" }, + + // TOU + { SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 Time of Use Values for week days", "", "", "tou_translator", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 Time of Use Values for week end days", "", "", "tou_translator", "*", "", "" }, + //{ 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", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "" }, + //{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "" }, + //{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "tou", "?=0.99", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "is_wlim_series", "Use time-series net electricity generation limits", "", "", "tou", "?=0", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "wlim_series", "Time series net electicity generation limits", "kWe", "", "tou", "is_wlim_series=1", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "f_turb_tou_periods", "Dispatch logic for turbine load fraction", "-", "", "tou", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "csp_financial_model", "", "1-8", "", "Financial Model", "?=1", "INTEGER,MIN=0", "" }, + //{ SSC_INPUT, SSC_NUMBER, "ppa_multiplier_model", "PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts", "0/1", "0=diurnal,1=timestep", "tou", "?=0", /*need a default so this var works in required_if*/ "INTEGER,MIN=0", "" }, + //{ SSC_INPUT, SSC_ARRAY, "dispatch_factors_ts", "Dispatch payment factor array", "", "", "tou", "ppa_multiplier_model=1&csp_financial_model<5&is_dispatch=1","", "" }, + //{ 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","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "en_electricity_rates", "Enable electricity rates for grid purchase", "0/1", "", "Electricity Rates", "?=0", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekday", "12x24 PPA pricing Weekday schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekend", "12x24 PPA pricing Weekend schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor1", "Dispatch payment factor 1", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor2", "Dispatch payment factor 2", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor3", "Dispatch payment factor 3", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor4", "Dispatch payment factor 4", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor5", "Dispatch payment factor 5", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor6", "Dispatch payment factor 6", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor7", "Dispatch payment factor 7", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor8", "Dispatch payment factor 8", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor9", "Dispatch payment factor 9", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "tou", "?=1", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "tou", "", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "tou", "?", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA prices - yearly", "$/kWh", "", "Revenue", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, + //{ 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", "", "" }, + + { SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "bop_array", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "water_usage_per_wash", "Water usage per wash", "L/m2_aper", "", "system", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "washing_frequency", "Mirror washing frequency", "-/year", "", "system", "*", "", "" }, + // Solar Field (from cmod_tcsmslf.cpp) { SSC_INPUT, SSC_NUMBER, "nMod", "Number of collector modules in a loop", "", "", "controller", "*", "INTEGER", ""}, { SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variantions", "", "", "controller", "?=4", "INTEGER", ""}, @@ -124,7 +186,6 @@ static var_info _cm_vtab_fresnel_physical[] = { { SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "I_b", "Direct normal incident solar irradiation", "kJ/m2-hr", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "T_db", "Dry bulb air temperature", "C", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "V_wind", "Ambient windspeed", "m/s", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "P_amb", "Ambient pressure", "atm", "", "controller", "*", "", ""}, @@ -134,159 +195,330 @@ static var_info _cm_vtab_fresnel_physical[] = { { SSC_INPUT, SSC_NUMBER, "tilt", "Tilt angle of surface/axis", "", "", "Weather", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "calc_design_pipe_vals", "Calculate temps and pressures at design conditions for runners and headers", "-", "", "controller", "*", "", "" }, + + + //{ SSC_INPUT, SSC_NUMBER, "custom_sf_pipe_sizes", "Use custom solar field pipe diams, wallthks, and lengths", "none", "", "solar_field", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "sf_rnr_diams", "Custom runner diameters", "m", "", "solar_field", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "sf_rnr_wallthicks", "Custom runner wall thicknesses", "m", "", "solar_field", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "sf_rnr_lengths", "Custom runner lengths", "m", "", "solar_field", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "sf_hdr_diams", "Custom header diameters", "m", "", "solar_field", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "sf_hdr_wallthicks", "Custom header wall thicknesses", "m", "", "solar_field", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "sf_hdr_lengths", "Custom header lengths", "m", "", "solar_field", "*", "", "" }, + + // controller (type 251) inputs + //VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS + //{ SSC_INPUT, SSC_NUMBER, "field_fluid", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "is_hx", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hx_config", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "q_max_aux", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "T_set_aux", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "V_tank_hot_ini", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "T_tank_hot_ini", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "T_tank_cold_ini", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "vol_tank", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "T_field_in_des", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "T_field_out_des", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "q_pb_design", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "W_pb_design", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "solarm", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "tes_temp", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "fossil_mode", "Label", "", "", "controller", "*", "INTEGER", "" }, + //{ SSC_INPUT, SSC_NUMBER, "fthr_ok", "Label", "", "", "controller", "*", "INTEGER", "" }, + //{ SSC_INPUT, SSC_NUMBER, "fc_on", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "t_standby_reset", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "tes_type", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "tslogic_a", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "tslogic_b", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "tslogic_c", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "ffrac", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "tc_fill", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "tc_void", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "t_dis_out_min", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "t_ch_out_max", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "nodes", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "f_tc_cold", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "custom_sgs_pipe_sizes", "Use custom SGS pipe diams, wallthks, and lengths", "-", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "sgs_diams", "Custom SGS diameters", "m", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "sgs_wallthicks", "Custom SGS wall thicknesses", "m", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "sgs_lengths", "Custom SGS lengths", "m", "", "controller", "*", "", "" }, + + // Power Cycle Inputs + { SSC_INPUT, SSC_NUMBER, "pc_config", "0: Steam Rankine (224), 1: user defined", "-", "", "powerblock", "?=0", "INTEGER", ""}, + { SSC_INPUT, SSC_NUMBER, "P_ref", "Label", "-", "", "powerblock", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "powerblock", "*", "", ""}, + //{ SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "-", "", "powerblock", "*", "", ""}, + //{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "-", "", "powerblock", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "powerblock", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "powerblock", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby mode", "none", "", "powerblock", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "powerblock", "*", "", "" }, + // Steam Rankine Cycle + { SSC_INPUT, SSC_NUMBER, "dT_cw_ref", "Reference condenser cooling water inlet/outlet T diff", "C", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "T_amb_des", "Reference ambient temperature at design point", "C", "", "powerblock", "pc_config=0", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "P_boil", "Boiler operating pressure", "bar", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "CT", "Flag for using dry cooling or wet cooling system", "none", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "T_approach", "Cooling tower approach temperature", "C", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "T_ITD_des", "ITD at design for dry system", "C", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "P_cond_ratio", "Condenser pressure ratio", "none", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "pb_bd_frac", "Power block blowdown steam fraction ", "none", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "P_cond_min", "Minimum condenser pressure", "inHg", "", "powerblock", "pc_config=0", "", "" }, + { SSC_INPUT, SSC_NUMBER, "n_pl_inc", "Number of part-load increments for the heat rejection system", "none", "", "powerblock", "pc_config=0", "", "" }, + { 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", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tech_type", "Turbine inlet pressure control flag (sliding=user, fixed=trough)", "1/2/3", "tower/trough/user", "powerblock", "pc_config=0", "", "" }, + + { SSC_INPUT, SSC_NUMBER, "DP_SGS", "Pressure drop within the steam generator", "bar", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "controller", "*", "", "" }, + + + // User Defined 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", "", "" }, + { 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", "", "" }, + { 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 + { SSC_INPUT, SSC_NUMBER, "store_fluid", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_MATRIX, "store_fl_props", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tshours", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "h_tank", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "u_tank", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tank_pairs", "Label", "", "", "controller", "*", "INTEGER", "" }, + { SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tank_max_heat", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "hot_tank_max_heat", "Rated heater capacity for hot tank heating", "MWe", "", "TES", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "cold_tank_max_heat", "Rated heater capacity for cold tank heating", "MWe", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "dt_hot", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "h_tank_min", "Label", "", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "init_hot_htf_percent", "Initial fraction of avail. vol that is hot", "%", "", "TES", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "dt_cold", "Label", "", "", "controller", "*", "", "" }, + + { SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "has_hot_tank_bypass", "Bypass valve connects field outlet to cold tank", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "T_tank_hot_inlet_min", "Minimum hot tank htf inlet temperature", "C", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "tes_pump_coef", "Label", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "custom_tes_p_loss", "TES pipe losses are based on custom lengths and coeffs", "-", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_ARRAY, "k_tes_loss_coeffs", "Minor loss coeffs for the coll, gen, and bypass loops", "-", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "custom_tes_pipe_sizes", "Use custom TES pipe diams, wallthks, and lengths", "-", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "tes_diams", "Custom TES diameters", "m", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "tes_wallthicks", "Custom TES wall thicknesses", "m", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_MATRIX, "tes_lengths", "Custom TES lengths", "m", "", "controller", "", "", "" }, + + + + // All other inputs from (cmod_trough_physical.cpp) - // Weather Reader - { SSC_INPUT, SSC_STRING, "file_name", "Local weather file with path", "none", "", "weather", "*", "LOCAL_FILE", "" }, + // Needed for auto-updating dependent inputs + //{ SSC_INPUT, SSC_NUMBER, "use_solar_mult_or_aperture_area", "Use solar multiple or total field aperture area", "-", "", "controller", "?=0", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "specified_solar_multiple", "specified_solar_multiple", "-", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "specified_total_aperture", "specified_total_aperture", "-", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "non_solar_field_land_area_multiplier", "non_solar_field_land_area_multiplier", "-", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "trough_loop_control", "trough_loop_control", "-", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_wlim_maxspec", "disp_wlim_maxspec", "-", "", "controller", "*", "", "" }, - // Power Cycle - { SSC_INPUT, SSC_NUMBER, "pc_config", "0: Steam Rankine (224), 1: user defined", "-", "", "powerblock", "?=0", "INTEGER", "" }, - { SSC_INPUT, SSC_NUMBER, "P_ref", "Rated plant capacity", "MWe", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "eta_ref", "Power cycle efficiency at design", "none", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "-", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "-", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby mode", "none", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "dT_cw_ref", "Reference condenser cooling water inlet/outlet T diff", "C", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_amb_des", "Reference ambient temperature at design point", "C", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "CT", "Flag for using dry cooling or wet cooling system", "none", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tech_type", "Turbine inlet pressure control flag (sliding=user, fixed=trough)", "1/2/3", "tower/trough/user", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_approach", "Cooling tower approach temperature", "C", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_ITD_des", "ITD at design for dry system", "C", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "P_cond_ratio", "Condenser pressure ratio", "none", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "pb_bd_frac", "Power block blowdown steam fraction ", "none", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "P_cond_min", "Minimum condenser pressure", "inHg", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "n_pl_inc", "Number of part-load increments for the heat rejection system", "none", "", "powerblock", "pc_config=0", "", "" }, - { 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", "", "" }, - - // UDPC parameters - { SSC_INPUT, SSC_NUMBER, "ud_f_W_dot_cool_des", "Percent of user-defined power cycle design gross output consumed by cooling", "%", "", "powerblock", "pc_config=1", "", "" }, - { 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", "", "powerblock", "pc_config=1", "", "" }, - { 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", "", "", "powerblock", "pc_config=1", "", "" }, - // TES - { SSC_INPUT, SSC_NUMBER, "store_fluid", "Material number for storage fluid", "-", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "store_fl_props", "User defined storage fluid property data", "-", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tshours", "Equivalent full-load thermal storage hours", "hr", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "h_tank", "Total height of tank (height of HTF when tank is full", "m", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "u_tank", "Loss coefficient from the tank", "W/m2-K", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tank_pairs", "Number of equivalent tank pairs", "-", "", "TES", "*", "INTEGER", "" }, - { SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Minimum allowable hot tank HTF temp", "C", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "hot_tank_max_heat", "Rated heater capacity for hot tank heating", "MWe", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Minimum allowable cold tank HTF temp", "C", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "cold_tank_max_heat", "Rated heater capacity for cold tank heating", "MWe", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "dt_hot", "Hot side HX approach temp", "C", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "h_tank_min", "Minimum allowable HTF height in storage tank", "m", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "init_hot_htf_percent", "Initial fraction of avail. vol that is hot", "%", "", "TES", "*", "", "" }, - // TOU - { SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 CSP operation Time-of-Use Weekday schedule", "-", "", "tou", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 CSP operation Time-of-Use Weekend schedule", "-", "", "tou", "*", "", "" }, - { 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", "", "" }, - { SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "" }, - { SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "" }, - { SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "tou", "is_dispatch=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "tou", "is_dispatch=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "tou", "is_dispatch=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "tou", "?=0.99", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "tou", "is_dispatch=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "" }, - { SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "is_wlim_series", "Use time-series net electricity generation limits", "", "", "tou", "?=0", "", "" }, - { SSC_INPUT, SSC_ARRAY, "wlim_series", "Time series net electicity generation limits", "kWe", "", "tou", "is_wlim_series=1", "", "" }, - { SSC_INPUT, SSC_ARRAY, "f_turb_tou_periods", "Dispatch logic for turbine load fraction", "-", "", "tou", "*", "", "" }, - - { SSC_INPUT, SSC_NUMBER, "csp_financial_model", "", "1-8", "", "Financial Model", "?=1", "INTEGER,MIN=0", "" }, - { SSC_INPUT, SSC_NUMBER, "ppa_multiplier_model", "PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts", "0/1", "0=diurnal,1=timestep", "tou", "?=0", /*need a default so this var works in required_if*/ "INTEGER,MIN=0", "" }, - { SSC_INPUT, SSC_ARRAY, "dispatch_factors_ts", "Dispatch payment factor array", "", "", "tou", "ppa_multiplier_model=1&csp_financial_model<5&is_dispatch=1","", "" }, - { 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","", "" }, - { SSC_INPUT, SSC_NUMBER, "en_electricity_rates", "Enable electricity rates for grid purchase", "0/1", "", "Electricity Rates", "?=0", "", "" }, - { SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekday", "12x24 PPA pricing Weekday schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekend", "12x24 PPA pricing Weekend schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor1", "Dispatch payment factor 1", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor2", "Dispatch payment factor 2", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor3", "Dispatch payment factor 3", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor4", "Dispatch payment factor 4", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor5", "Dispatch payment factor 5", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor6", "Dispatch payment factor 6", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor7", "Dispatch payment factor 7", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor8", "Dispatch payment factor 8", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "dispatch_factor9", "Dispatch payment factor 9", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "tou", "?=1", "", "" }, - { SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "tou", "", "", "" }, - { SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "tou", "?", "", "" }, - { SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA prices - yearly", "$/kWh", "", "Revenue", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - { 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", "", "" }, - - { SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Fraction of rated gross power constantly consumed", "MWe/MWcap", "", "system", "*", "", "" }, - { SSC_INPUT, SSC_ARRAY, "bop_array", "Balance of plant parasitic power fraction, mult frac and const, linear and quad coeff", "", "", "system", "*", "", "" }, - { SSC_INPUT, SSC_ARRAY, "aux_array", "Auxiliary heater, mult frac and const, linear and quad coeff", "", "", "system", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "water_usage_per_wash", "Water usage per wash", "L/m2_aper", "", "system", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "washing_frequency", "Mirror washing frequency", "-/year", "", "system", "*", "", "" }, + // OUTPUTS - // Newly added - { SSC_INPUT, SSC_NUMBER, "calc_design_pipe_vals", "Calculate temps and pressures at design conditions for runners and headers", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "V_hdr_cold_max", "Maximum HTF velocity in the cold headers at design", "m/s", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "V_hdr_cold_min", "Minimum HTF velocity in the cold headers at design", "m/s", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "V_hdr_hot_max", "Maximum HTF velocity in the hot headers at design", "m/s", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "V_hdr_hot_min", "Minimum HTF velocity in the hot headers at design", "m/s", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "N_max_hdr_diams", "Maximum number of diameters in each of the hot and cold headers", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "L_rnr_pb", "Length of runner pipe in power block", "m", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "L_rnr_per_xpan", "Threshold length of straight runner pipe without an expansion loop", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "L_xpan_hdr", "Compined perpendicular lengths of each header expansion loop", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "L_xpan_rnr", "Compined perpendicular lengths of each runner expansion loop", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "Min_rnr_xpans", "Minimum number of expansion loops per single-diameter runner section", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "northsouth_field_sep", "North/south separation between subfields. 0 = SCAs are touching", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "N_hdr_per_xpan", "Number of collector loops per expansion loop", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "offset_xpan_hdr", "Location of first header expansion loop. 1 = after first collector loop", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "K_cpnt", "Interconnect component minor loss coefficients, row=intc, col=cpnt", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "D_cpnt", "Interconnect component diameters, row=intc, col=cpnt", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "L_cpnt", "Interconnect component lengths, row=intc, col=cpnt", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "Type_cpnt", "Interconnect component type, row=intc, col=cpnt", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "custom_sf_pipe_sizes", "Use custom solar field pipe diams, wallthks, and lengths", "none", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "sf_rnr_diams", "Custom runner diameters", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "sf_rnr_wallthicks", "Custom runner wall thicknesses", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "sf_rnr_lengths", "Custom runner lengths", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "sf_hdr_diams", "Custom header diameters", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "sf_hdr_wallthicks", "Custom header wall thicknesses", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "sf_hdr_lengths", "Custom header lengths", "m", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "has_hot_tank_bypass", "Bypass valve connects field outlet to cold tank", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_tank_hot_inlet_min", "Minimum hot tank htf inlet temperature", "C", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tes_pump_coef", "Pumping power to move 1kg of HTF through tes loop", "kW/(kg/s)", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "custom_tes_p_loss", "TES pipe losses are based on custom lengths and coeffs", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "k_tes_loss_coeffs", "Minor loss coeffs for the coll, gen, and bypass loops", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "custom_tes_pipe_sizes", "Use custom TES pipe diams, wallthks, and lengths", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "tes_diams", "Custom TES diameters", "m", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "tes_wallthicks", "Custom TES wall thicknesses", "m", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "tes_lengths", "Custom TES lengths", "m", "", "controller", "", "", "" }, - { SSC_INPUT, SSC_NUMBER, "DP_SGS", "Pressure drop within the steam generator", "bar", "", "controller", "*", "", "" }, + // Simulation Kernel + { SSC_OUTPUT, SSC_ARRAY, "time_hr", "Time at end of timestep", "hr", "", "solver", "*", "", "" }, - // Needed for auto-updating dependent inputs - { SSC_INPUT, SSC_NUMBER, "use_solar_mult_or_aperture_area", "Use solar multiple or total field aperture area", "-", "", "controller", "?=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "specified_solar_multiple", "specified_solar_multiple", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "specified_total_aperture", "specified_total_aperture", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "non_solar_field_land_area_multiplier", "non_solar_field_land_area_multiplier", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_ARRAY, "trough_loop_control", "trough_loop_control", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "disp_wlim_maxspec", "disp_wlim_maxspec", "-", "", "controller", "*", "", "" }, + + // Weather Reader + { SSC_OUTPUT, SSC_ARRAY, "month", "Resource Month", "", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "hour_day", "Resource Hour of Day", "", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "solazi", "Resource Solar Azimuth", "deg", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "solzen", "Resource Solar Zenith", "deg", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "beam", "Resource Beam normal irradiance", "W/m2", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "tdry", "Resource Dry bulb temperature", "C", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "twet", "Resource Wet bulb temperature", "C", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "rh", "Resource Relative Humidity", "%", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "wspd", "Resource Wind Speed", "m/s", "", "weather", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pres", "Resource Pressure", "mbar", "", "weather", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "defocus", "Field optical focus fraction", "", "", "weather", "*", "", "" }, + + + + // Solar Field (from Trough) + { SSC_OUTPUT, SSC_ARRAY, "Theta_ave", "Field collector solar incidence angle", "deg", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "CosTh_ave", "Field collector cosine efficiency", "", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "IAM_ave", "Field collector incidence angle modifier", "", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "RowShadow_ave", "Field collector row shadowing loss", "", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "EndLoss_ave", "Field collector optical end loss", "", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "dni_costh", "Field collector DNI-cosine product", "W/m2", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "EqOpteff", "Field optical efficiency before defocus", "", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "SCAs_def", "Field fraction of focused SCAs", "", "", "solar_field", "*", "", "" }, + + //{ SSC_OUTPUT, SSC_ARRAY, "q_inc_sf_tot", "Field thermal power incident", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "qinc_costh", "Field thermal power incident after cosine", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_inc", "Receiver thermal power incident", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_thermal_loss", "Receiver thermal losses", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_abs", "Receiver thermal power absorbed", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_piping_loss", "Field piping thermal losses", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "e_dot_field_int_energy", "Field change in material/htf internal energy", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_htf_sf_out", "Field thermal power leaving in HTF", "MWt", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_freeze_prot", "Field freeze protection required", "MWt", "", "solar_field", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "m_dot_loop", "Receiver mass flow rate", "kg/s", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_recirc", "Field total mass flow recirculated", "kg/s", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_delivered", "Field total mass flow delivered", "kg/s", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_field_cold_in", "Field timestep-averaged inlet temperature", "C", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_rec_cold_in", "Loop timestep-averaged inlet temperature", "C", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_rec_hot_out", "Loop timestep-averaged outlet temperature", "C", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_field_hot_out", "Field timestep-averaged outlet temperature", "C", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "deltaP_field", "Field pressure drop", "bar", "", "solar_field", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "W_dot_sca_track", "Field collector tracking power", "MWe", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "W_dot_field_pump", "Field htf pumping power", "MWe", "", "solar_field", "*", "", "" }, + + // Solar Field (from fresnel) + { SSC_OUTPUT, SSC_ARRAY, "theta_L", "Field collector incidence angle - longitudinal", "deg", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "phi_t", "Field collector incidence angle - transversal", "deg", "", "solar_field", " * ", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "eta_optical", "Field collector optical efficiency", "", "", "solar_field", "*", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "EqOptEff", "Field collector and receiver optical efficiency", "", "", "solar_field", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "sf_def", "Field collector focus fraction", "", "", "mslf", "*", "LENGTH=8760", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "q_inc_sf_tot", "Field thermal power incident", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_abs_tot", "Field thermal power absorbed", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dump", "Field thermal power dumped", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_loss_tot", "Field thermal power receiver loss", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "Pipe_hl", "Field thermal power header pipe losses", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_avail", "Field thermal power produced", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_loss_spec_tot", "Field thermal power avg. receiver loss", "W/m", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "eta_thermal", "Field thermal efficiency", "", "", "mslf", "*", "LENGTH=8760", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "E_bal_startup", "Field HTF energy inertial (consumed)", "MWht", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_avail", "Field HTF mass flow rate total", "kg/hr", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_htf2", "Field HTF mass flow rate loop", "kg/s", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "DP_tot", "Field HTF pressure drop total", "bar", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_sys_c", "Field HTF temperature cold header inlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_sys_h", "Field HTF temperature hot header outlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "t_loop_outlet", "Field HTF temperature loop outlet", "C", "", "mslf", "*", "LENGTH=8760", "" }, + + + // power block + { SSC_OUTPUT, SSC_ARRAY, "eta", "PC efficiency: gross", "", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_pb", "PC input energy", "MWt", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_pc", "PC HTF mass flow rate", "kg/s", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_startup", "PC startup thermal power", "MWt", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "P_cycle", "PC electrical power output: gross", "MWe", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_pc_in", "PC HTF inlet temperature", "C", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_pc_out", "PC HTF outlet temperature", "C", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_water_pc", "PC water consumption: makeup + cooling", "kg/s", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_pc_startup", "PC startup thermal energy", "MWht", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "cycle_htf_pump_power", "PC HTF pump power", "MWe", "", "powerblock", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "P_cooling_tower_tot", "Parasitic power condenser operation", "MWe", "", "powerblock", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "P_out_net", "Total electric power to grid", "MWe", "", "system", "*", "", "" }, + + // TES + { SSC_OUTPUT, SSC_ARRAY, "tank_losses", "TES thermal losses", "MWt", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_tes_heater", "TES freeze protection power", "MWe", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_tes_hot", "TES hot temperature", "C", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "T_tes_cold", "TES cold temperature", "C", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "mass_tes_cold", "TES cold tank mass (end)", "kg", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "mass_tes_hot", "TES hot tank mass (end)", "kg", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dc_tes", "TES discharge thermal power", "MWt", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_ch_tes", "TES charge thermal power", "MWt", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "e_ch_tes", "TES charge state", "MWht", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cr_to_tes_hot", "Mass flow: field to hot TES", "kg/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_hot_out", "Mass flow: TES hot out", "kg/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_pc_to_tes_cold", "Mass flow: cycle to cold TES", "kg/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_cold_out", "Mass flow: TES cold out", "kg/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_to_cycle", "Mass flow: field to cycle", "kg/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cycle_to_field", "Mass flow: cycle to field", "kg/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_cold_tank_to_hot_tank", "Mass flow: cold tank to hot tank", "kg/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "tes_htf_pump_power", "TES HTF pump power", "MWe", "", "TES", "*", "", "" }, + + // Controller + { SSC_OUTPUT, SSC_ARRAY, "op_mode_1", "1st operating mode", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "op_mode_2", "2nd op. mode, if applicable", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "op_mode_3", "3rd op. mode, if applicable", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "m_dot_balance", "Relative mass flow balance error", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_balance", "Relative energy balance error", "", "", "solver", "*", "", "" }, + + // Monthly Outputs + { SSC_OUTPUT, SSC_ARRAY, "monthly_energy", "Monthly Energy", "kWh", "", "Post-process", "*", "LENGTH=12", "" }, + + // Annual Outputs + { SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual Net Electrical Energy Production w/ avail derate", "kWe-hr", "", "Post-process", "*", "", "" }, + //{ 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", "*", "", "" }, + //{ 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", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_field_freeze_protection", "Annual thermal power for field freeze protection", "kWt-hr", "", "Post-process", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_tes_freeze_protection", "Annual thermal power for TES freeze protection", "kWt-hr", "", "Post-process", "*", "", "" }, + + // Newly added + { SSC_OUTPUT, SSC_ARRAY, "n_op_modes", "Operating modes in reporting timestep", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "tou_value", "CSP operating Time-of-use value", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pricing_mult", "PPA price multiplier", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_sb", "Thermal power for PC standby", "MWt", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_min", "Thermal power for PC min operation", "MWt", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_target", "Target thermal power to PC", "MWt", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_max", "Max thermal power to PC", "MWt", "", "solver", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "is_rec_su_allowed", "is receiver startup allowed", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "is_pc_su_allowed", "is power cycle startup allowed", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "is_pc_sb_allowed", "is power cycle standby allowed", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_su", "Estimate rec. startup thermal power", "MWt", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_on", "Estimate rec. thermal power TO HTF", "MWt", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_dc", "Estimate max TES discharge thermal power", "MWt", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_ch", "Estimate max TES charge thermal power", "MWt", "", "solver", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_a", "First 3 operating modes tried", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_b", "Next 3 operating modes tried", "", "", "solver", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "operating_modes_c", "Final 3 operating modes tried", "", "", "solver", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "disp_rel_mip_gap", "Dispatch relative MIP gap", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_state", "Dispatch solver state", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_subopt_flag", "Dispatch suboptimal solution flag", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_iter", "Dispatch iterations count", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_objective", "Dispatch objective function value", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_obj_relax", "Dispatch objective function - relaxed max", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsf_expected", "Dispatch expected solar field available energy", "MWt", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsfprod_expected", "Dispatch expected solar field generation", "MWt", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qsfsu_expected", "Dispatch expected solar field startup enegy", "MWt", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_tes_expected", "Dispatch expected TES charge level", "MWht", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_pceff_expected", "Dispatch expected power cycle efficiency adj.", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_thermeff_expected", "Dispatch expected SF thermal efficiency adj.", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_qpbsu_expected", "Dispatch expected power cycle startup energy", "MWht", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_wpb_expected", "Dispatch expected power generation", "MWe", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_rev_expected", "Dispatch expected revenue factor", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nconstr", "Dispatch number of constraints in problem", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nvar", "Dispatch number of variables in problem", "", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "disp_solve_time", "Dispatch solver time", "sec", "", "tou", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "avg_suboptimal_rel_mip_gap","Average suboptimal relative MIP gap", "%", "", "tou", "*", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "P_fixed", "Parasitic power fixed load", "MWe", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "P_plant_balance_tot", "Parasitic power generation-dependent load", "MWe", "", "system", "*", "", "" }, + + { 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", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "annual_W_cycle_gross", "Electrical source - Power cycle gross output", "kWhe", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "conversion_factor", "Gross to Net Conversion Factor", "%", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "sim_duration", "Computational time of timeseries simulation", "s", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "solar_multiple_actual", "Actual solar multiple of system", "-", "", "system", "*", "", "" }, + //{ 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", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_diams", "Pipe diameters in TES", "m", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_wallthk", "Pipe wall thickness in TES", "m", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_lengths", "Pipe lengths in TES", "m", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_mdot_dsn", "Mass flow TES pipes at design conditions", "kg/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_vel_dsn", "Velocity in TES pipes at design conditions", "m/s", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_T_dsn", "Temperature in TES pipes at design conditions", "C", "", "TES", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_P_dsn", "Pressure in TES pipes at design conditions", "bar", "", "TES", "*", "", "" }, + + //{ SSC_OUTPUT, SSC_ARRAY, "defocus", "Field optical focus fraction", "", "", "solver", "*", "", "" }, var_info_invalid }; @@ -302,9 +534,9 @@ class cm_fresnel_physical : public compute_module void exec() { - - // General - bool is_dispatch = as_boolean("is_dispatch"); + // NOT IN TROUGH + bool is_dispatch = false; + bool is_dispatch_series = false; // Weather reader C_csp_weatherreader weather_reader; @@ -340,155 +572,212 @@ class cm_fresnel_physical : public compute_module // Solar field, trough C_csp_fresnel_collector_receiver c_fresnel; { - c_fresnel.m_nMod = as_integer("nMod"); - c_fresnel.m_nRecVar = as_integer("nRecVar"); - c_fresnel.m_nLoops = as_integer("nLoops"); - 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_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_fthrok = as_integer("fthrok"); - c_fresnel.m_fthrctrl = as_integer("fthrctrl"); - c_fresnel.m_ColAz = as_number("ColAz"); - c_fresnel.m_ColTilt = as_number("tilt"); - - c_fresnel.m_solar_mult= as_number("solar_mult"); - - 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"); + // Inputs + { + c_fresnel.m_nMod = as_integer("nMod"); + c_fresnel.m_nRecVar = as_integer("nRecVar"); + c_fresnel.m_nLoops = as_integer("nLoops"); + 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"); - size_t size; - double* IAM_T_coefs = as_array("IAM_T_coefs", &size); - c_fresnel.m_IAM_T_coefs.assign(IAM_T_coefs, IAM_T_coefs + size); + 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"); - double* IAM_L_coefs = as_array("IAM_L_coefs", &size); - c_fresnel.m_IAM_L_coefs.assign(IAM_L_coefs, IAM_L_coefs + size); + 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_fthrok = as_integer("fthrok"); + c_fresnel.m_fthrctrl = as_integer("fthrctrl"); + c_fresnel.m_ColAz = as_number("ColAz"); + c_fresnel.m_ColTilt = as_number("tilt"); + + c_fresnel.m_solar_mult = as_number("solar_mult"); + + 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_OpticalTable = as_matrix("OpticalTable"); - - c_fresnel.m_rec_model = as_integer("rec_model"); - double* HCE_FieldFrac = as_array("HCE_FieldFrac", &size); - c_fresnel.m_HCE_FieldFrac.assign(HCE_FieldFrac, HCE_FieldFrac + size); + 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"); - double* D_abs_in = as_array("D_abs_in", &size); - c_fresnel.m_D_abs_in.assign(D_abs_in, D_abs_in + size); + size_t size; + double* IAM_T_coefs = as_array("IAM_T_coefs", &size); + c_fresnel.m_IAM_T_coefs.assign(IAM_T_coefs, IAM_T_coefs + size); - double* D_abs_out = as_array("D_abs_out", &size); - c_fresnel.m_D_abs_out.assign(D_abs_out, D_abs_out + size); + double* IAM_L_coefs = as_array("IAM_L_coefs", &size); + c_fresnel.m_IAM_L_coefs.assign(IAM_L_coefs, IAM_L_coefs + size); - double* D_glass_in = as_array("D_glass_in", &size); - c_fresnel.m_D_glass_in.assign(D_glass_in, D_glass_in + size); + c_fresnel.m_OpticalTable = as_matrix("OpticalTable"); - double* D_glass_out = as_array("D_glass_out", &size); - c_fresnel.m_D_glass_out.assign(D_glass_out, D_glass_out + size); + c_fresnel.m_rec_model = as_integer("rec_model"); - double* D_plug = as_array("D_plug", &size); - c_fresnel.m_D_plug.assign(D_plug, D_plug + size); + double* HCE_FieldFrac = as_array("HCE_FieldFrac", &size); + c_fresnel.m_HCE_FieldFrac.assign(HCE_FieldFrac, HCE_FieldFrac + size); - double* Flow_type = as_array("Flow_type", &size); - c_fresnel.m_Flow_type.assign(Flow_type, Flow_type + size); + double* D_abs_in = as_array("D_abs_in", &size); + c_fresnel.m_D_abs_in.assign(D_abs_in, D_abs_in + size); - double* Rough = as_array("Rough", &size); - c_fresnel.m_Rough.assign(Rough, Rough + size); + double* D_abs_out = as_array("D_abs_out", &size); + c_fresnel.m_D_abs_out.assign(D_abs_out, D_abs_out + size); - double* alpha_env = as_array("alpha_env", &size); - c_fresnel.m_alpha_env.assign(alpha_env, alpha_env + size); + double* D_glass_in = as_array("D_glass_in", &size); + c_fresnel.m_D_glass_in.assign(D_glass_in, D_glass_in + size); - 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"); + double* D_glass_out = as_array("D_glass_out", &size); + c_fresnel.m_D_glass_out.assign(D_glass_out, D_glass_out + size); - double* alpha_abs = as_array("alpha_abs", &size); - c_fresnel.m_alpha_abs.assign(alpha_abs, alpha_abs + size); + double* D_plug = as_array("D_plug", &size); + c_fresnel.m_D_plug.assign(D_plug, D_plug + size); - double* Tau_envelope = as_array("Tau_envelope", &size); - c_fresnel.m_Tau_envelope.assign(Tau_envelope, Tau_envelope + size); + double* Flow_type = as_array("Flow_type", &size); + c_fresnel.m_Flow_type.assign(Flow_type, Flow_type + size); - double* epsilon_glass = as_array("epsilon_glass", &size); - c_fresnel.m_epsilon_glass.assign(epsilon_glass, epsilon_glass + size); + double* Rough = as_array("Rough", &size); + c_fresnel.m_Rough.assign(Rough, Rough + size); - double* GlazingIntact = as_array("GlazingIntactIn", &size); - c_fresnel.m_GlazingIntact.assign(GlazingIntact, GlazingIntact + size); + double* alpha_env = as_array("alpha_env", &size); + c_fresnel.m_alpha_env.assign(alpha_env, alpha_env + size); + 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"); + double* alpha_abs = as_array("alpha_abs", &size); + c_fresnel.m_alpha_abs.assign(alpha_abs, alpha_abs + size); + double* Tau_envelope = as_array("Tau_envelope", &size); + c_fresnel.m_Tau_envelope.assign(Tau_envelope, Tau_envelope + size); + double* epsilon_glass = as_array("epsilon_glass", &size); + c_fresnel.m_epsilon_glass.assign(epsilon_glass, epsilon_glass + size); + double* GlazingIntact = as_array("GlazingIntactIn", &size); + c_fresnel.m_GlazingIntact.assign(GlazingIntact, GlazingIntact + size); - double* P_a = as_array("P_a", &size); - c_fresnel.m_P_a.assign(P_a, P_a + size); + double* P_a = as_array("P_a", &size); + c_fresnel.m_P_a.assign(P_a, P_a + size); - double* AnnulusGas = as_array("AnnulusGas", &size); - c_fresnel.m_AnnulusGas.assign(AnnulusGas, AnnulusGas + size); + double* AnnulusGas = as_array("AnnulusGas", &size); + c_fresnel.m_AnnulusGas.assign(AnnulusGas, AnnulusGas + size); - double* AbsorberMaterial = as_array("AbsorberMaterial", &size); - c_fresnel.m_AbsorberMaterial.assign(AbsorberMaterial, AbsorberMaterial + size); + double* AbsorberMaterial = as_array("AbsorberMaterial", &size); + c_fresnel.m_AbsorberMaterial.assign(AbsorberMaterial, AbsorberMaterial + size); - double* Shadowing = as_array("Shadowing", &size); - c_fresnel.m_Shadowing.assign(Shadowing, Shadowing + size); + double* Shadowing = as_array("Shadowing", &size); + c_fresnel.m_Shadowing.assign(Shadowing, Shadowing + size); - double* dirt_env = as_array("dirt_env", &size); - c_fresnel.m_dirt_env.assign(dirt_env, dirt_env + size); + double* dirt_env = as_array("dirt_env", &size); + c_fresnel.m_dirt_env.assign(dirt_env, dirt_env + size); - double* Design_loss = as_array("Design_loss", &size); - c_fresnel.m_Design_loss.assign(Design_loss, Design_loss + size); + double* Design_loss = as_array("Design_loss", &size); + c_fresnel.m_Design_loss.assign(Design_loss, Design_loss + size); - c_fresnel.m_L_mod_spacing = as_number("L_mod_spacing"); - c_fresnel.m_L_crossover = as_number("L_crossover"); + c_fresnel.m_L_mod_spacing = as_number("L_mod_spacing"); + c_fresnel.m_L_crossover = as_number("L_crossover"); - double* HL_T_coefs = as_array("HL_T_coefs", &size); - c_fresnel.m_HL_T_coefs.assign(HL_T_coefs, HL_T_coefs + size); + double* HL_T_coefs = as_array("HL_T_coefs", &size); + c_fresnel.m_HL_T_coefs.assign(HL_T_coefs, HL_T_coefs + size); - double* HL_w_coefs = as_array("HL_w_coefs", &size); - c_fresnel.m_HL_w_coefs.assign(HL_w_coefs, HL_w_coefs + size); + double* HL_w_coefs = as_array("HL_w_coefs", &size); + c_fresnel.m_HL_w_coefs.assign(HL_w_coefs, HL_w_coefs + size); - c_fresnel.m_DP_nominal = as_number("DP_nominal"); + c_fresnel.m_DP_nominal = as_number("DP_nominal"); - double* DP_coefs = as_array("DP_coefs", &size); - c_fresnel.m_DP_coefs.assign(DP_coefs, DP_coefs + size); + double* DP_coefs = as_array("DP_coefs", &size); + c_fresnel.m_DP_coefs.assign(DP_coefs, DP_coefs + size); - c_fresnel.m_rec_htf_vol = as_number("rec_htf_vol"); + c_fresnel.m_rec_htf_vol = as_number("rec_htf_vol"); - // Hard Coded (currently no UI) - c_fresnel.m_L_rnr_pb = 25; + // Hard Coded (currently no UI) + c_fresnel.m_L_rnr_pb = 25; - //////////////////////// Questionable - c_fresnel.m_I_b = as_number("I_b"); - c_fresnel.m_V_wind_des = as_number("V_wind_des"); - c_fresnel.m_T_amb_sf_des = as_number("T_amb_sf_des"); + //////////////////////// Questionable + c_fresnel.m_V_wind_des = as_number("V_wind_des"); + c_fresnel.m_T_amb_sf_des = as_number("T_amb_sf_des"); + } + + + // 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_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); + } + } // Power cycle @@ -584,6 +873,31 @@ class cm_fresnel_physical : public compute_module // TES C_csp_two_tank_tes storage; { + // No custom TES piping + bool custom_tes_pipe_sizes = false; + util::matrix_t tes_wallthicks(1, 1); + tes_wallthicks.at(0, 0) = -1; + util::matrix_t tes_diams(1, 1); + tes_diams.at(0, 0) = -1; + + // Convert array to matrix + util::matrix_t k_tes_loss_coeffs_mat; + { + size_t size; + ssc_number_t* k_tes_loss_coeffs_array = as_array("k_tes_loss_coeffs", &size); + vector k_tes_loss_coeffs_vec(k_tes_loss_coeffs_array, k_tes_loss_coeffs_array + size); + size_t sizeof1 = 1; + k_tes_loss_coeffs_mat = util::matrix_t(sizeof1, size, &k_tes_loss_coeffs_vec); + delete k_tes_loss_coeffs_array; + } + + // NOT in cmod inputs + double init_hot_htf_percent = 0.3; + double cold_tank_max_heat = 25; + double hot_tank_max_heat = 25; + + + util::matrix_t tes_lengths; if (is_assigned("tes_lengths")) { tes_lengths = as_matrix("tes_lengths"); //[m] @@ -592,6 +906,7 @@ class cm_fresnel_physical : public compute_module double vals1[11] = { 0., 90., 100., 120., 0., 30., 90., 80., 80., 120., 80. }; tes_lengths.assign(vals1, 11); } + storage = C_csp_two_tank_tes( as_integer("Fluid"), as_matrix("field_fl_props"), @@ -604,16 +919,16 @@ class cm_fresnel_physical : public compute_module as_double("u_tank"), as_integer("tank_pairs"), as_double("hot_tank_Thtr"), - as_double("hot_tank_max_heat"), + hot_tank_max_heat, as_double("cold_tank_Thtr"), - as_double("cold_tank_max_heat"), + 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"), + init_hot_htf_percent, as_double("pb_pump_coef"), as_boolean("tanks_in_parallel"), as_double("V_tes_des"), @@ -623,10 +938,10 @@ class cm_fresnel_physical : public compute_module as_boolean("has_hot_tank_bypass"), as_double("T_tank_hot_inlet_min"), as_boolean("custom_tes_p_loss"), - as_boolean("custom_tes_pipe_sizes"), - as_matrix("k_tes_loss_coeffs"), - as_matrix("tes_diams"), - as_matrix("tes_wallthicks"), + custom_tes_pipe_sizes, + k_tes_loss_coeffs_mat, + tes_diams, + tes_wallthicks, tes_lengths, as_double("HDR_rough"), as_double("DP_SGS") @@ -652,6 +967,13 @@ class cm_fresnel_physical : public compute_module C_csp_tou_block_schedules::S_params* tou_params = &tou.ms_params; double ppa_price_year1 = std::numeric_limits::quiet_NaN(); { + // NOT in CMOD inputs + bool is_tod_pc_target_also_pc_max = false; + bool is_dispatch = false; + vector f_turb_tou_periods{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + size_t n_f_turbine = f_turb_tou_periods.size(); + int csp_financial_model = 8; + tou_params->mc_csp_ops.mc_weekdays = as_matrix("weekday_schedule"); tou_params->mc_csp_ops.mc_weekends = as_matrix("weekend_schedule"); if (tou_params->mc_pricing.mc_weekdays.ncells() == 1) { @@ -663,16 +985,17 @@ class cm_fresnel_physical : public compute_module tou_params->mc_pricing.mc_weekends = util::matrix_t(12, 24, 1.0); } - 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_is_tod_pc_target_also_pc_max = 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); + //size_t n_f_turbine = 0; + //ssc_number_t* p_f_turbine = as_array("f_turb_tou_periods", &n_f_turbine); + ssc_number_t* p_f_turbine = &f_turb_tou_periods[0]; 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++) @@ -687,16 +1010,16 @@ class cm_fresnel_physical : public compute_module 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"); + //int csp_financial_model = as_integer("csp_financial_model"); 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) { + /*if (is_dispatch && !is_ppa_price_input_assigned) { throw exec_error("trough_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; @@ -708,19 +1031,19 @@ class cm_fresnel_physical : public compute_module } 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) { + /*if (ppa_soln_mode == 0 && is_dispatch) { throw exec_error("trough_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) { + /*if (en_electricity_rates == 1 && is_dispatch) { throw exec_error("trough_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"); @@ -728,7 +1051,7 @@ class cm_fresnel_physical : public compute_module { tou_params->mc_pricing.mv_is_diurnal = false; - if (is_assigned("dispatch_factors_ts") || is_dispatch) { + 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); @@ -748,7 +1071,7 @@ class cm_fresnel_physical : public compute_module || is_assigned("dispatch_factor4") || is_assigned("dispatch_factor5") || is_assigned("dispatch_factor6") || is_assigned("dispatch_factor7") || is_assigned("dispatch_factor8") || is_assigned("dispatch_factor9"); - if (are_all_assigned || is_dispatch) { + if (are_all_assigned) { tou_params->mc_pricing.mc_weekdays = as_matrix("dispatch_sched_weekday"); tou_params->mc_pricing.mc_weekends = as_matrix("dispatch_sched_weekend"); @@ -772,10 +1095,11 @@ class cm_fresnel_physical : public compute_module } } else if (csp_financial_model == 5) { // Commercial - if (is_dispatch) { - throw exec_error("trough_physical", "\nDispatch optimization current not enabled for the Commercial financial model\n"); - // need to add pricing lookup for Commercial financial model - } + //if (is_dispatch) { + // throw exec_error("trough_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; @@ -786,20 +1110,21 @@ class cm_fresnel_physical : public compute_module 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) { - 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("trough_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 (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) { + // 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("trough_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); } @@ -834,7 +1159,8 @@ class cm_fresnel_physical : public compute_module // System Dispatch csp_dispatch_opt dispatch; { - if (as_boolean("is_dispatch")) { + //if (as_boolean("is_dispatch")) { + if(is_dispatch){ // System Design Parameters double W_dot_cycle_des = as_double("P_ref"); //[MWe] @@ -981,7 +1307,8 @@ class cm_fresnel_physical : public compute_module } //if the pricing schedule is provided as hourly, overwrite the tou schedule - if (as_boolean("is_dispatch_series")) + //if (as_boolean("is_dispatch_series")) + if(is_dispatch_series) { size_t n_dispatch_series; ssc_number_t* dispatch_series = as_array("dispatch_series", &n_dispatch_series); @@ -1029,6 +1356,188 @@ class cm_fresnel_physical : public compute_module log(out_msg, out_type); } + + // DEBUG + if (true) + { + size_t size; + ssc_number_t* x; + + x = as_array("Theta_ave", &size); + vector Theta_ave_vec(x, x + size); + delete x; + + x = as_array("CosTh_ave", &size); + vector CosTh_ave_vec(x, x + size); + delete x; + + x = as_array("IAM_ave", &size); + vector IAM_ave_vec(x, x + size); + delete x; + + x = as_array("RowShadow_ave", &size); + vector RowShadow_ave_vec(x, x + size); + delete x; + + x = as_array("EndLoss_ave", &size); + vector EndLoss_ave_vec(x, x + size); + delete x; + + x = as_array("dni_costh", &size); + vector dni_costh_vec(x, x + size); + delete x; + + x = as_array("EqOpteff", &size); + vector EqOpteff_vec(x, x + size); + delete x; + + x = as_array("SCAs_def", &size); + vector SCAs_def_vec(x, x + size); + delete x; + + + /*x = as_array("q_inc_sf_tot", &size); + vector q_inc_sf_tot_vec(x, x + size); + delete x;*/ + + x = as_array("qinc_costh", &size); + vector qinc_costh_vec(x, x + size); + delete x; + x = as_array("q_dot_rec_inc", &size); + vector q_dot_rec_inc_vec(x, x + size); + delete x; + x = as_array("q_dot_rec_thermal_loss", &size); + vector q_dot_rec_thermal_loss_vec(x, x + size); + delete x; + x = as_array("q_dot_rec_abs", &size); + vector q_dot_rec_abs_vec(x, x + size); + delete x; + x = as_array("q_dot_piping_loss", &size); + vector q_dot_piping_loss_vec(x, x + size); + delete x; + x = as_array("e_dot_field_int_energy", &size); + vector e_dot_field_int_energy_vec(x, x + size); + delete x; + x = as_array("q_dot_htf_sf_out", &size); + vector q_dot_htf_sf_out_vec(x, x + size); + delete x; + x = as_array("q_dot_freeze_prot", &size); + vector q_dot_freeze_prot_vec(x, x + size); + delete x; + x = as_array("m_dot_loop", &size); + vector m_dot_loop_vec(x, x + size); + delete x; + x = as_array("recirculating", &size); + vector recirculating_vec(x, x + size); + delete x; + x = as_array("m_dot_field_recirc", &size); + vector m_dot_field_recirc_vec(x, x + size); + delete x; + x = as_array("m_dot_field_delivered", &size); + vector m_dot_field_delivered_vec(x, x + size); + delete x; + x = as_array("T_field_cold_in", &size); + vector T_field_cold_in_vec(x, x + size); + delete x; + x = as_array("T_rec_cold_in", &size); + vector T_rec_cold_in_vec(x, x + size); + delete x; + x = as_array("T_rec_hot_out", &size); + vector T_rec_hot_out_vec(x, x + size); + delete x; + x = as_array("T_field_hot_out", &size); + vector T_field_hot_out_vec(x, x + size); + delete x; + x = as_array("deltaP_field", &size); + vector deltaP_field_vec(x, x + size); + delete x; + x = as_array("W_dot_sca_track", &size); + vector W_dot_sca_track_vec(x, x + size); + delete x; + x = as_array("W_dot_field_pump", &size); + vector W_dot_field_pump_vec(x, x + size); + delete x; + + + // Fresnel + x = as_array("theta_L", &size); + vector theta_L_vec(x, x + size); + delete x; + + x = as_array("phi_t", &size); + vector phi_t_vec(x, x + size); + delete x; + + x = as_array("eta_optical", &size); + vector eta_optical_vec(x, x + size); + delete x; + + //x = as_array("EqOptEff", &size); + //vector EqOptEff_vec(x, x + size); + //delete x; + auto eq = EqOpteff_vec; + + x = as_array("sf_def", &size); + vector sf_def_vec(x, x + size); + delete x; + + x = as_array("q_inc_sf_tot", &size); + vector q_inc_sf_tot_vec(x, x + size); + delete x; + x = as_array("q_abs_tot", &size); + vector q_abs_tot_vec(x, x + size); + delete x; + x = as_array("q_dump", &size); + vector q_dump_vec(x, x + size); + delete x; + x = as_array("q_loss_tot", &size); + vector q_loss_tot_vec(x, x + size); + delete x; + x = as_array("Pipe_hl", &size); + vector Pipe_hl_vec(x, x + size); + delete x; + x = as_array("q_avail", &size); + vector q_avail_vec(x, x + size); + delete x; + x = as_array("q_loss_spec_tot", &size); + vector q_loss_spec_tot_vec(x, x + size); + delete x; + x = as_array("eta_thermal", &size); + vector eta_thermal_vec(x, x + size); + delete x; + x = as_array("E_bal_startup", &size); + vector E_bal_startup_vec(x, x + size); + delete x; + x = as_array("m_dot_avail", &size); + vector m_dot_avail_vec(x, x + size); + delete x; + x = as_array("m_dot_htf2", &size); + vector m_dot_htf2_vec(x, x + size); + delete x; + x = as_array("DP_tot", &size); + vector DP_tot_vec(x, x + size); + delete x; + x = as_array("T_sys_c", &size); + vector T_sys_c_vec(x, x + size); + delete x; + x = as_array("T_sys_h", &size); + vector T_sys_h_vec(x, x + size); + delete x; + x = as_array("t_loop_outlet", &size); + vector t_loop_outlet_vec(x, x + size); + delete x; + + // Actual Outputs + x = as_array("time_hr", &size); + vector time_hr_vec(x, x + size); + + x = as_array("P_out_net", &size); + vector P_out_net_vec(x, x + size); + + int y = 0; + } + + 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); diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 7708c698b..aacc555f3 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -37,6 +37,28 @@ static C_csp_reported_outputs::S_output_info S_output_info[] = {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}, + {C_csp_fresnel_collector_receiver::E_THETA_L, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_PHI_T, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_ETA_OPTICAL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + //{C_csp_fresnel_collector_receiver::E_EQ_OPT_EFF, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + + {C_csp_fresnel_collector_receiver::E_SF_DEF, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_INC_SF_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_ABS_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_DUMP, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_LOSS_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_PIPE_HL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_AVAIL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_LOSS_SPEC_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_ETA_THERMAL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_E_BAL_STARTUP, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_M_DOT_AVAIL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_M_DOT_HTF2, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_DP_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_T_SYS_C, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_T_SYS_H, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_T_LOOP_OUTLET, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + csp_info_invalid }; @@ -94,7 +116,12 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double { std::vector DP_tube(m_nMod, 0.); - double DP_IOCOP, DP_loop_tot, DP_toField, DP_fromField, DP_hdr_cold, DP_hdr_hot; + 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; @@ -102,6 +129,8 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double 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) { @@ -143,7 +172,9 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double x1 = 0.0; x2 = 1.0; } - DP_tube[i] += PressureDrop(m_dot_htf, m_TCS_T_htf_ave[i], 1.0, m_D_h.at(j), (m_Rough[j] * m_D_h.at(j)), + + 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 } @@ -215,10 +246,35 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double } - //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; - //m_dP_total *= 1.E-5; //[bar], convert from Pa ?????????????? + + // 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); } + //} + // + //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; } @@ -230,12 +286,12 @@ void C_csp_fresnel_collector_receiver::set_output_value() mc_reported_outputs.value(E_IAM_AVE, m_IAM_ave); //[-] mc_reported_outputs.value(E_ROWSHADOW_AVE, m_RowShadow_ave); //[-] mc_reported_outputs.value(E_ENDLOSS_AVE, m_EndLoss_ave); //[-] - mc_reported_outputs.value(E_DNI_COSTH, m_dni_costh); //[W/m2] + //mc_reported_outputs.value(E_DNI_COSTH, m_dni_costh); //[W/m2] mc_reported_outputs.value(E_EQUIV_OPT_ETA_TOT, m_EqOpteff); //[-] 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_INC_SF_COSTH, m_dni_costh * m_Ap_tot / 1.E6); //[MWt] + //mc_reported_outputs.value(E_Q_DOT_INC_SF_TOT, m_q_dot_inc_sf_tot); //[MWt] + //mc_reported_outputs.value(E_Q_DOT_INC_SF_COSTH, m_dni_costh * m_Ap_tot / 1.E6); //[MWt] mc_reported_outputs.value(E_Q_DOT_REC_INC, m_q_dot_sca_abs_summed_fullts + m_q_dot_sca_loss_summed_fullts); //[MWt] mc_reported_outputs.value(E_Q_DOT_REC_THERMAL_LOSS, m_q_dot_sca_loss_summed_fullts); //[MWt] @@ -273,23 +329,40 @@ void C_csp_fresnel_collector_receiver::set_output_value() 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; -} -class E_loop_energy_balance_exit -{ - public: - enum - { - SOLVED, - NaN - }; -}; + // Fresnel + mc_reported_outputs.value(E_THETA_L, m_theta_L * r2d); + mc_reported_outputs.value(E_PHI_T, m_phi_t * r2d); + mc_reported_outputs.value(E_ETA_OPTICAL, m_eta_optical); + //mc_reported_outputs.value(E_EQ_OPT_EFF, m_EqOpteff); (E_EQUIV_OPT_ETA_TOT) + + mc_reported_outputs.value(E_SF_DEF, m_control_defocus * m_component_defocus); // SCAs_def (duplicate) + mc_reported_outputs.value(E_Q_INC_SF_TOT, m_q_dot_inc_sf_tot); // (duplicate) + mc_reported_outputs.value(E_Q_ABS_TOT, m_q_dot_sca_abs_summed_fullts); // (duplicate) + mc_reported_outputs.value(E_Q_DUMP, 0); //????????? + mc_reported_outputs.value(E_Q_LOSS_TOT, m_q_dot_sca_loss_summed_fullts); // (duplicate) + mc_reported_outputs.value(E_PIPE_HL, m_q_dot_xover_loss_summed_fullts + + m_q_dot_HR_cold_loss_fullts + + m_q_dot_HR_hot_loss_fullts); // q_dot_piping_loss (duplicate) + mc_reported_outputs.value(E_Q_AVAIL, m_q_dot_htf_to_sink_fullts); // q_dot_htf_sf_out (duplicate) + mc_reported_outputs.value(E_Q_LOSS_SPEC_TOT, 0); // ?????????? likely E_Q_LOSS_TOT / num collectors + mc_reported_outputs.value(E_ETA_THERMAL, 0); // ???????????? m_eta_thermal is removed from trough + mc_reported_outputs.value(E_E_BAL_STARTUP, 0); // ????????????? + mc_reported_outputs.value(E_M_DOT_AVAIL, m_m_dot_htf_tot); // (duplicate) + mc_reported_outputs.value(E_M_DOT_HTF2, m_m_dot_htf_tot / (double)m_nLoops); // (duplicate) + mc_reported_outputs.value(E_DP_TOT, m_dP_total); // (duplicate) + + mc_reported_outputs.value(E_T_SYS_C, m_T_sys_c_t_int_fullts - 273.15); // (duplicate) + mc_reported_outputs.value(E_T_SYS_H, m_T_sys_h_t_int_fullts - 273.15); // (duplicate) + mc_reported_outputs.value(E_T_LOOP_OUTLET, m_T_htf_h_rec_out_t_int_fullts - 273.15); // (duplicate) + + return; +} // ------------------------------------------------------------------- PRIVATE SUPPLEMENTAL -int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_weatherreader::S_outputs& weather, +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) { @@ -303,10 +376,13 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we // 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; + { + 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] @@ -319,46 +395,53 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we 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 - // This values is the Bulk Temperature at the *end* of the timestep (type 262 line 1551; trough line 1187) - //T_sys_c = (T_sys_c_last - T_cold_in_1) * exp(-(m_dot_htf * float(nLoops)) / (v_cold * rho_hdr_cold + mc_bal_cold / c_hdr_cold_last) * dt) + T_cold_in_1; - 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; + // BULK Temperature calculations + { + // This values is the Bulk Temperature at the *end* of the timestep (type 262 line 1551; trough line 1187) + //T_sys_c = (T_sys_c_last - T_cold_in_1) * exp(-(m_dot_htf * float(nLoops)) / (v_cold * rho_hdr_cold + mc_bal_cold / c_hdr_cold_last) * dt) + T_cold_in_1; + 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 fully 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; + // Try calculating a timestep-average Bulk Temperature (and assume it is the outlet) + // This is fully 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++) + //Runner { - 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 = 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_rnr[i] - T_db); //[W] + m_Runner_hl_cold_tot += 2. * m_Runner_hl_cold; } - m_Runner_hl_cold = m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (m_T_rnr[i] - T_db); //[W] - m_Runner_hl_cold_tot += 2. * 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 = 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_hdr[i] - T_db); //[W] + m_Header_hl_cold_tot += m_nfsec * m_Header_hl_cold; } - m_Header_hl_cold = m_L_crossover * m_D_hdr[i] * CSP::pi * m_Pipe_hl_coef * (m_T_hdr[i] - T_db); //[W] - m_Header_hl_cold_tot += m_nfsec * m_Header_hl_cold; } - + 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] 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); @@ -368,8 +451,6 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we 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] - //double Pipe_hl_cold = m_Runner_hl_cold_tot + m_Header_hl_cold_tot; - 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); m_T_loop[0] = m_T_loop_in; m_T_htf_in_t_int[0] = m_T_loop_in; @@ -388,24 +469,29 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we // Vectors storing information for the energy balance std::vector E_sca, E_sca_htf, E_sca_abs, E_sca_bal; //[MJ] - E_sca.resize(m_nMod); - E_sca_htf.resize(m_nMod); - E_sca_abs.resize(m_nMod); - E_sca_bal.resize(m_nMod); + 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); - - std::vector E_xover, E_xover_htf, E_xover_abs, E_xover_bal; - 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); + double q_inc_total = 0.; double q_abs_abs_total = 0.; double q_abs_htf_total = 0.; std::vector m_EqOpteffs(m_nMod, 0.); + // MAIN SCA Temperature Solve // Loop through each Module for (int i = 0; i < m_nMod; i++) { @@ -466,11 +552,6 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we //Calculate the mass of HTF associated with this node double m_node = rho_htf_i * m_A_cs[0] * m_L_mod; - if (weather.m_beam > 300) - int x = 0; - - double old_T_end = m_T_htf_out_t_end[i]; - // 7.8.2016 twn: reformulate the energy balance calculations similar to the runner/headers: // the outlet HTF temperature is equal to the bulk temperature @@ -485,11 +566,6 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we (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; - double new_T_end = m_T_htf_out_t_end[i]; - - double delta_T = new_T_end - old_T_end; - - // Original Fresnel Eqn { //MJW 12.14.2010 The first term should represent the difference between the previous average temperature and the new @@ -544,7 +620,7 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we } m_q_1abs_tot[i] = m_q_loss_SCAtot[i] / m_L_mod; - m_EqOpteff = eta_optical; //Use the optical efficiency as it is for this option + m_EqOpteff = m_eta_optical; //Use the optical efficiency as it is for this option break; @@ -603,584 +679,104 @@ int C_csp_fresnel_collector_receiver::loop_energy_balance_T_t_int(const C_csp_we T_loop_outX = m_T_htf_out_t_int[m_nMod - 1]; - 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 * (m_T_hdr[i] - T_db); - m_Header_hl_hot_tot += m_nfsec * 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); - 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[i] * CSP::pi * m_D_runner[D_index] * m_Pipe_hl_coef * (m_T_rnr[i] - T_db); //Wt - m_Runner_hl_hot_tot += 2. * m_Runner_hl_hot; - D_index++; - } - 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] - - // 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; - - 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_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_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_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, 5) * 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; -} - - -int C_csp_fresnel_collector_receiver::x(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) -{ - m_m_dot_htf_tot = m_dot_htf_loop * float(m_nLoops); - - // First calculate the cold header temperature, which will serve as the loop inlet temperature - double rho_hdr_cold = m_htfProps.dens(m_T_sys_c_t_end_last, 1.0); // [kg/m3] - double rho_hdr_hot = m_htfProps.dens(m_T_sys_h_t_end_last, 1.0); // [kg/m3] - 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 - - 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); - else - T_sky = T_db - 20.0; - - // Initialize - double q_dot_loss_HR_cold = 0.0; // [W] Cold Header Heat Loss - 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] - - //if(m_accept_loc == E_piping_config::FIELD) + // DEBUGGING if (true) { - // This values is the Bulk Temperature at the *end* of the timestep - 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) * sim_info.ms_ts.m_step) + T_htf_cold_in; + // Variables to compare + vector ColOptEff_ = m_ColOptEff.to_vector(); + double q_abs_abs_total_ = q_abs_abs_total; // [W] absorbed by absorber - // Try calculating a timestep-average Bulk Temperature (and assume it is the outlet) - 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; + vector m_q_SCA_ = m_q_SCA; + vector m_q_abs_SCAtot_ = m_q_abs_SCAtot; + vector m_q_loss_SCAtot_ = m_q_loss_SCAtot; + vector m_q_1abs_tot_ = m_q_1abs_tot; - // Now calculate an energy balance using the timestep-average Bulk Temperature - // ** THIS IS JUST A TEST: can comment out if necessary ** - double E_bal_T_t_ave = -m_dot_htf_loop * float(m_nLoops) * c_hdr_cold_last * (m_T_sys_c_t_int - T_htf_cold_in) * sim_info.ms_ts.m_step - - (m_v_cold * rho_hdr_cold * c_hdr_cold_last + m_mc_bal_cold) * (m_T_sys_c_t_end - m_T_sys_c_t_end_last); //[J] + double T_htf_cold_in_ = T_htf_cold_in; + double m_dot_htf_loop_ = m_dot_htf_loop; - //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; - double m_cp_sys_c_t_int = m_htfProps.Cp(m_T_sys_c_t_int) * 1000.0; //mjw 1.6.2011 Adding mc_bal to the cold header inertia - 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_rnr[i] - T_db); //[W] - m_Runner_hl_cold_tot += 2. * 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_Header_hl_cold + m_L_crossover * m_D_hdr[i] * pi * m_Pipe_hl_coef * (m_T_hdr[i] - T_db); //[W] - m_Header_hl_cold_tot += m_nfsec * m_Header_hl_cold; - } - 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] - 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); - - // 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] - } - - m_T_loop[0] = m_T_loop_in; - - // Set Inlet Temperature - double dt = sim_info.ms_ts.m_step; - m_T_htf_in_t_int[0] = (m_TCS_T_sys_c_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; - - // 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_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 q_dot_loss_xover; //[W] - std::vector E_xover, E_xover_htf, E_xover_abs, E_xover_bal; - std::vector m_EqOpteffs(m_nMod, 0.); - { - E_sca.resize(m_nMod); - E_sca_htf.resize(m_nMod); - E_sca_abs.resize(m_nMod); - E_sca_bal.resize(m_nMod); + double current_time = sim_info.ms_ts.m_time; - q_dot_loss_xover.resize(m_nMod - 1); + int old_day = m_current_day; + int old_hr = m_current_hr; - 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); - } - double q_inc_total = 0.; - double q_abs_abs_total = 0.; - double q_abs_htf_total = 0.; + m_current_day = (int)(sim_info.ms_ts.m_time / 3600.0) / 24; + m_current_hr = (int)(sim_info.ms_ts.m_time / 3600.0) % 24; - // Loop Through SCAs - 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] - - //int HT = (int)m_SCAInfoArray(i, 0) - 1; //[-] HCE type - //int CT = (int)m_SCAInfoArray(i, 1) - 1; //[-] Collector type - - double c_htf_i = 0.0; //[J/kg-K] - double rho_htf_i = 0.0; //[kg/m^3] - - double T_node_ave; - double dT_loc; - double m_node; - double errhl; - - // Check Receiver Type - switch (m_rec_model) - { - // Evacuated Receiver Model - case 2: - { - // Loop Through Variations - 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; - EvacReceiver(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, m_ncall, sim_info.ms_ts.m_time / 3600.0, - //outputs - m_q_loss[j], m_q_abs[j], m_q_1abs[j], c_htf_j, rho_htf_j, mv_HCEguessargs); - - // 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]; - 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 - 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 - m_EqOpteffs[i] += m_ColOptEff.at(i) * m_Shadowing[j] * m_dirt_env[j] * m_alpha_abs[j] * m_Tau_envelope[j] * m_HCE_FieldFrac[j]; - m_EqOpteff += m_ColOptEff.at(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]; - - } - - break; - } - - // Polynomial Model - case 1: - { - //If the average temperature hasn't been calculated yet, use the initial value - if (m_ncall == 0) { - T_node_ave = m_T_htf_out_t_end_last[i]; - } - else { - T_node_ave = m_T_htf_out_t_end_last[i]; - } - //iterate to improve heat loss estimate - errhl = 999.; - - double V_wind = weather.m_wspd; - double dt = sim_info.ms_ts.m_step; - - - - 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 - //update estimate of average temperature - 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] + - 2.0 * (m_T_htf_out_t_end_last[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (2.0 * m_dot_htf_loop * c_htf_i)) * - exp(-2. * m_dot_htf_loop * c_htf_i * dt / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)); - //Recalculate the average temperature for the SCA - m_TCS_T_htf_ave[i] = (m_T_htf_in_t_int[i] + m_T_htf_out_t_end[i]) / 2.0; - - errhl = T_node_ave - m_TCS_T_htf_ave[i]; //iterate until the node temperature does not vary significantly - - T_node_ave = m_TCS_T_htf_ave[i]; - } - - m_q_1abs_tot[i] = m_q_loss_SCAtot[i] / m_L_mod; - - double eta_optical = m_EqOpteff; - m_EqOpteff = eta_optical; //Use the optical efficiency as it is for this option - - break; - } - } - - q_inc_total += m_q_SCA[i] * m_L_mod; // [W] - q_abs_abs_total += m_q_SCA[i] * m_L_mod * m_EqOpteffs[i]; // [W] absorbed by absorber - q_abs_htf_total += m_q_abs_SCAtot[i]; // [W] absorbed by HTF - - //Calculate the specific heat for the node - c_htf_i *= 1000.0; - - //Calculate the mass of HTF associated with this node - m_node = rho_htf_i * m_A_cs.at(0) * m_L_mod; - - // from old fresnel - 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)); - - //Adjust the loop outlet temperature to account for thermal losses incurred in the hot header and the runner pipe - //double T_sys_h = T_loop_outX - Pipe_hl_hot / (m_m_dot_htf_tot * m_c_hdr_hot); - - // From old fresnel type 262 line 1724 - //T_sys_h = (m_TCS_T_sys_h_last - T_sys_h) * exp(-m_m_dot_htf_tot / (m_v_hot * rho_hdr_hot + m_mc_bal_hot / m_c_hdr_hot) * dt) + T_sys_h; - - // from trough - //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 actual amount of energy absorbed by the field that doesn't go into changing the SCA's average temperature - ////MJW 1.16.2011 Include the thermal inertia term - //if (!m_is_using_input_gen) - //{ - // if (m_q_abs_SCAtot[i] > 0.0) - // { - // //m_E_avail[i] = max(m_q_abs_SCAtot[i]*m_dt*3600. - m_A_cs(HT,1)*m_L_actSCA[CT]*m_rho_htf[i]*m_c_htf[i]*(m_T_htf_ave[i]- m_T_htf_ave0[i]),0.0) - // double x1 = (m_A_cs(HT, 1) * m_L_actSCA[CT] * rho_htf_i * c_htf_i + m_L_actSCA[CT] * m_mc_bal_sca); //mjw 4.29.11 removed m_c_htf[i] -> it doesn't make sense on the m_mc_bal_sca term - // m_E_accum[i] = x1 * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]); - // 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] - - // //Equation: m_m_dot_avail*m_c_htf[i]*(T_hft_out - m_T_htf_in) = m_E_avail/(m_dt*3600) - // //m_m_dot_avail = (m_E_avail[i]/(m_dt*3600.))/(m_c_htf[i]*(m_T_htf_out[i] - m_T_htf_in[i])) //[J/s]*[kg-K/J]*[K]: - // } - //} - //else - //{ - // //m_E_avail[i] = max(m_q_abs_SCAtot[i]*m_dt*3600. - m_A_cs(HT,1)*m_L_actSCA[CT]*m_rho_htf[i]*m_c_htf[i]*(m_T_htf_ave[i]- m_T_htf_ave0[i]),0.0) - // double x1 = (m_A_cs(HT, 1) * m_L_actSCA[CT] * rho_htf_i * c_htf_i + m_L_actSCA[CT] * m_mc_bal_sca); //mjw 4.29.11 removed m_c_htf[i] -> it doesn't make sense on the m_mc_bal_sca term - // m_E_accum[i] = x1 * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]); - // 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] * m_dt - m_E_accum[i], 0.0); //[J/s]*[hr]*[s/hr]: [J] - // m_E_avail[i] = (m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step - m_E_accum[i]); //[J/s]*[hr]*[s/hr]: [J] - - // //Equation: m_m_dot_avail*m_c_htf[i]*(T_hft_out - m_T_htf_in) = m_E_avail/(m_dt*3600) - // //m_m_dot_avail = (m_E_avail[i]/(m_dt*3600.))/(m_c_htf[i]*(m_T_htf_out[i] - m_T_htf_in[i])) //[J/s]*[kg-K/J]*[K]: - //} - } - - //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); - m_E_accum[i] = x1 * (m_TCS_T_htf_ave[i] - m_T_htf_out_t_end_last[i]); - m_E_int_loop[i] = x1 * (m_TCS_T_htf_ave[i] - 298.15); //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] - - } - - //Recalculate the average temperature for the SCA - m_TCS_T_htf_ave[i] = (m_T_htf_in_t_int[i] + m_T_htf_out_t_end_last[i]) / 2.0; - - //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] * pi * L_int * (m_T_htf_out_t_int[i] - T_db) / (m_dot_htf_loop * c_htf_i); - //Add the internal energy of the crossover piping - 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); - - //Calculate inlet temperature of the next SCA - 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] - } - - } - - double eta_optical = m_EqOpteff; - double eta_optical_est = calculate_optical_efficiency(weather, sim_info); - 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); - - //Set the loop outlet temperature - T_loop_outX = m_T_htf_out_t_end[m_nMod - 1]; + if (m_current_day == 51 && m_current_hr == 15) + int x = 0; - + // Step by Hour + if (old_hr != m_current_hr) + int x = 0; - //Calculation for heat losses from hot header and runner pipe - m_Runner_hl_hot = 0.0; //initialize - m_Header_hl_hot = 0.0; //initialize - for (int i = 0; i < m_nhdrsec; i++) { - m_Header_hl_hot = m_Header_hl_hot + m_L_crossover * m_D_hdr[i] * pi * m_Pipe_hl_coef * (T_loop_outX - T_db); - } + // Step by Day + if (old_day != m_current_day) + int x = 0; - //Add the runner length - for (int i = 0; i < m_nrunsec; i++) { - m_Runner_hl_hot = m_Runner_hl_hot + m_L_runner[i] * pi * m_D_runner[i] * m_Pipe_hl_coef * (T_loop_outX - T_db); //Wt - } - double Pipe_hl_hot = m_Header_hl_hot + m_Runner_hl_hot; - - //Fill in rest of T_loop using the SCA inlet and outlet temps - int loop_i = 2; int sca_i = 0; - while (loop_i < 2 * m_nMod + 2) { - m_T_loop[loop_i] = m_T_htf_in_t_int[sca_i]; - m_T_loop[loop_i + 1] = m_T_htf_out_t_int[sca_i]; - loop_i = loop_i + 2; sca_i++; } - //Adjust the loop outlet temperature to account for thermal losses incurred in the hot header and the runner pipe - m_TCS_T_sys_h = T_loop_outX - Pipe_hl_hot / (m_m_dot_htf_tot * m_c_hdr_hot); - - //Calculate the system temperature of the hot portion of the collector field. - //This will serve as the fluid outlet temperature - m_TCS_T_sys_h = (m_TCS_T_sys_h_last - m_TCS_T_sys_h) * exp(-m_m_dot_htf_tot / (m_v_hot * rho_hdr_hot + m_mc_bal_hot / m_c_hdr_hot) * sim_info.ms_ts.m_step) + m_TCS_T_sys_h; - - - - // From Trough Section + // 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] - if (true) + //Calculation for heat losses from hot piping + //Header { - //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] - - for (int i = 0; i < m_nhdrsec; i++) - { - m_Header_hl_hot = m_Header_hl_hot + m_L_crossover * m_D_hdr[i] * pi * m_Pipe_hl_coef * (T_loop_outX - T_db); - m_Header_hl_hot_tot += m_nfsec * m_Header_hl_hot; - } - - /*for (int i = m_nhdrsec; i < 2 * m_nhdrsec; i++) + 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_Header_hl_hot + m_L_crossover * m_D_hdr[i] * pi * m_Pipe_hl_coef * (T_loop_outX - T_db); + + m_Header_hl_hot = m_L_crossover * m_D_hdr[D_index] * CSP::pi * m_Pipe_hl_coef * (m_T_hdr[i] - T_db); m_Header_hl_hot_tot += m_nfsec * m_Header_hl_hot; - }*/ - //Runner + 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); - for (int i = 0; i < m_nrunsec; i++) + int D_index = 0; + for (int i = m_nrunsec; i < 2 * m_nrunsec; i++) { - m_Runner_hl_hot = m_Runner_hl_hot + m_L_runner[i] * pi * m_D_runner[i] * m_Pipe_hl_coef * (T_loop_outX - T_db); //Wt - m_Runner_hl_hot_tot += m_nfsec * m_Runner_hl_hot; + 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[i] * CSP::pi * m_D_runner[D_index] * m_Pipe_hl_coef * (m_T_rnr[i] - T_db); //Wt + m_Runner_hl_hot_tot += 2. * m_Runner_hl_hot; + D_index++; } - //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[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (m_T_rnr[i] - T_db); //Wt - // m_Runner_hl_hot_tot += 2. * m_Runner_hl_hot; - //} 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] - q_dot_loss_HR_hot = m_Header_hl_hot_tot + m_Runner_hl_hot_tot; //[W] // aka m_Pipe_hl_hot - E_HR_hot_losses = q_dot_loss_HR_hot * sim_info.ms_ts.m_step / 1.E6; //[MJ] - - - // 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; //[C] + 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) + // 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; + } + // BONUS trough Calculations + { 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] @@ -1189,85 +785,86 @@ int C_csp_fresnel_collector_receiver::x(const C_csp_weatherreader::S_outputs& we 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_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] + // 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_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) + for (int i = 0; i < m_nMod; i++) { - 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 + 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_E_dot_sca_summed_subts += E_sca[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_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_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, 5) * 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] + 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_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, 5) * 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) + for (int i = 0; i < m_nMod; i++) { - 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 + 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_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 + 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] - 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_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] - 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] + } - m_Q_field_losses_total_subts = Q_loss_xover + Q_loss_HR_cold + Q_loss_HR_hot - Q_abs_scas_summed; return E_loop_energy_balance_exit::SOLVED; } @@ -1516,7 +1113,7 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() m_IAM_ave = std::numeric_limits::quiet_NaN(); m_RowShadow_ave = std::numeric_limits::quiet_NaN(); m_EndLoss_ave = std::numeric_limits::quiet_NaN(); - m_dni_costh = std::numeric_limits::quiet_NaN(); + //m_dni_costh = std::numeric_limits::quiet_NaN(); m_c_htf_ave = std::numeric_limits::quiet_NaN(); m_control_defocus = std::numeric_limits::quiet_NaN(); @@ -2029,6 +1626,10 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() m_L_runner.resize(2 * m_nrunsec); m_D_hdr.resize(2 * m_nhdrsec); m_L_hdr.resize(2 * m_nhdrsec); + //m_P_rnr.resize(2 * m_nrunsec); + //m_P_hdr.resize(2 * m_nhdrsec); + //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); @@ -2123,7 +1724,7 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() m_v_cold = m_v_hot; //Write the volume totals to the piping diameter file - /*piping_summary.append( + m_piping_summary.append( "\n----------------------------------------------\n" "Plant HTF volume information:\n" "----------------------------------------------\n"); @@ -2136,7 +1737,7 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() "Pump / SGS system volume: %10.4e m3\n" "---------------------------\n" "Total plant HTF volume: %10.4e m3\n"; - sprintf(tstr, fmt.c_str(), v_cold, v_hot, v_loop_tot / float(nLoops), v_loop_tot, (v_hot * 2. + v_loop_tot), v_sgs, (v_hot * 2. + v_loop_tot + v_sgs));*/ + 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 @@ -2290,6 +1891,9 @@ void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_outputs& m_E_dot_HR_cold_fullts = m_E_dot_HR_hot_fullts = m_q_dot_htf_to_sink_fullts = 0.0; + if ((int)(sim_info.ms_ts.m_time / 3600) == 5) + int x = 0; + // Simulate through time steps for (int i = 0; i < n_steps_recirc; i++) { @@ -2302,7 +1906,7 @@ void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_outputs& 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 + 10.0) + if (m_T_htf_out_t_end[m_nMod - 1] < m_T_fp + fp_offset) { if (m_Q_field_losses_total_subts > 0.0) { @@ -2356,6 +1960,10 @@ void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_outputs& m_q_dot_freeze_protection = Q_fp_sum / sim_info.ms_ts.m_step; //[MWt] + + if ((int)(sim_info.ms_ts.m_time / 3600) == 5) + int x = 0; + // 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); @@ -2453,7 +2061,7 @@ void C_csp_fresnel_collector_receiver::startup(const C_csp_weatherreader::S_outp 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 + 10.0) + if (m_T_htf_out_t_end[m_nMod - 1] < m_T_fp + fp_offset) { if (m_Q_field_losses_total_subts > 0.0) { @@ -2570,6 +2178,7 @@ void C_csp_fresnel_collector_receiver::on(const C_csp_weatherreader::S_outputs& //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(); @@ -2607,7 +2216,7 @@ void C_csp_fresnel_collector_receiver::on(const C_csp_weatherreader::S_outputs& // Get inlet condition from input argument double T_cold_in = htf_state_in.m_temp + 273.15; //[K] // Call energy balance with updated info - int balance_code = loop_energy_balance_T_t_int(weather, T_cold_in, m_dot_htf_loop, sim_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; @@ -2795,6 +2404,12 @@ void C_csp_fresnel_collector_receiver::on(const C_csp_weatherreader::S_outputs& set_output_value(); + + + + + + return; } @@ -2857,11 +2472,11 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S // 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_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_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 + //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; @@ -2972,7 +2587,7 @@ void C_csp_fresnel_collector_receiver::write_output_intervals(double report_time 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_costh_ini(m_costh); + //double m_costh_ini(m_costh); double m_q_i_ini(m_q_i); double m_IAM_ini(m_IAM); util::matrix_t m_ColOptEff_ini(m_ColOptEff); @@ -2986,17 +2601,18 @@ double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_cs double m_IAM_ave_ini(m_IAM_ave); double m_RowShadow_ave_ini(m_RowShadow_ave); double m_EndLoss_ave_ini(m_EndLoss_ave); - double m_dni_costh_ini(m_dni_costh); + //double m_dni_costh_ini(m_dni_costh); 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_EqOpteff * m_costh; + //double eta_optical = m_EqOpteff * m_costh; + double eta_optical = m_EqOpteff; // Restore member variable values - m_costh = m_costh_ini; + //m_costh = m_costh_ini; m_q_i = m_q_i_ini; m_IAM = m_IAM_ini; m_ColOptEff = m_ColOptEff_ini; @@ -3010,7 +2626,7 @@ double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_cs m_IAM_ave = m_IAM_ave_ini; m_RowShadow_ave = m_RowShadow_ave_ini; m_EndLoss_ave = m_EndLoss_ave_ini; - m_dni_costh = m_dni_costh_ini; + //m_dni_costh = m_dni_costh_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; @@ -3021,74 +2637,9 @@ double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_cs double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/) { - // 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.; - - // Incidence angle - int doy = DateTime::CalculateDayOfYear(weather.m_year, weather.m_month, weather.m_day); // day of year - double time_start = ((doy - 1) * 24 + weather.m_hour + weather.m_minute / 60.) * 3600.; - double step = 3600.; - double time = time_start + step; - double time_hr = time / 3600.; // [hr] - double dt_hr = step / 3600.; // [hr] - double hour = fmod(time_hr, 24.); // [hr] - int day_of_year = (int)ceil(time_hr / 24.); // Day of the year - double B = (day_of_year - 1) * 360.0 / 365.0 * CSP::pi / 180.0; // Duffie & Beckman 1.5.3b - 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)); // Eqn of time in minutes - double Dec = 23.45 * sin(360.0 * (284.0 + day_of_year) / 365.0 * CSP::pi / 180.0) * CSP::pi / 180.0; // Declination in radians (Duffie & Beckman 1.6.1) - double SolarNoon = 12. - ((m_shift) * 180.0 / CSP::pi) / 15.0 - EOT / 60.0; // Solar Noon and time in hours - double HrA = hour - dt_hr; - double StdTime = HrA + 0.5 * dt_hr; - double SolarTime = StdTime + ((m_shift) * 180.0 / CSP::pi) / 15.0 + EOT / 60.0; - double omega = (SolarTime - 12.0) * 15.0 * CSP::pi / 180.0; // m_hour angle (arc of sun) in radians - double SolarAlt = asin(sin(Dec) * sin(m_latitude) + cos(m_latitude) * cos(Dec) * cos(omega)); - double SolarAz = (weather.m_solazi - 180.) * m_d2r; // [rad] Solar azimuth angle - double CosTh = m_costh = sqrt(1.0 - pow(cos(SolarAlt - m_ColTilt) - cos(m_ColTilt) * cos(SolarAlt) * (1.0 - cos(SolarAz - m_ColAz)), 2)); - double Theta = acos(CosTh); // [rad] - - // Incidence angle modifier (IAM) from Burkholder & Kutscher 2008 - double IamF1 = 0.000884; - double IamF2 = -0.0000537; - double IAM; - if (CosTh == 0) { - IAM = 0; - } - else { - IAM = std::min(1., (CosTh + IamF1 * Theta + IamF2 * pow(Theta, 2.)) / CosTh); - } + throw(C_csp_exception("C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx")); - // Heat loss, where temperatures are in [C] per Burkholder & Kutscher 2008 - // model coefficients for 2008 Schott PTR70 (vacuum) receiver - double A0 = 4.05; - double A1 = 0.247; - double A2 = -0.00146; - double A3 = 5.65e-06; - double A4 = 7.62e-08; - double A5 = -1.7; - double A6 = 0.0125; - double PerfFac = 1; - double T_amb = weather.m_tdry; // [C] - double W_spd = std::abs(weather.m_wspd); - double DNI = weather.m_beam; - double T_out_des = m_T_loop_out_des - 273.15; // [C] (converted from [C] to [K] in init and now back to [C]) - double T_in_des = m_T_loop_in_des - 273.15; // [C] (converted from [C] in [K] in init and now back to [C]) - double HLTerm1 = (A0 + A5 * sqrt(W_spd)) * (T_out_des - T_in_des); - double HLTerm2 = (A1 + A6 * sqrt(W_spd)) * ((pow(T_out_des, 2) - pow(T_in_des, 2)) / 2. - T_amb * (T_out_des - T_in_des)); - double HLTerm3 = ((A2 + A4 * DNI * CosTh * IAM) / 3.) * (pow(T_out_des, 3) - pow(T_in_des, 3)); - double HLTerm4 = (A3 / 4.) * (pow(T_out_des, 4) - pow(T_in_des, 4)); - double HL = (HLTerm1 + HLTerm2 + HLTerm3 + HLTerm4) / (T_out_des - T_in_des); //[W/m] - double HL_hces = std::max(HL * m_L_tot * m_nLoops * PerfFac, 0.); // [W] convert from W/m to W for entire field - - // Piping heat loss, at average hot/cold design temperature - double T_avg_des = 0.5 * (T_out_des + T_in_des); // [C] - double HL_headers = m_nfsec * (2 * m_nhdrsec) * m_L_crossover * m_D_hdr[m_nhdrsec] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] - double HL_runners = 0.; - for (int i = 0; i < 2 * m_nrunsec; i++) { - HL_runners += 2. * m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] - } - - double HL_total = HL_hces + HL_headers + HL_runners; - return std::max(1. - HL_total * 1.e-6 / q_incident, 0.); + return std::numeric_limits::quiet_NaN(); } double C_csp_fresnel_collector_receiver::get_collector_area() @@ -3119,6 +2670,10 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade double dt_hr = sim_info.ms_ts.m_step / 3600.; //[hr] double hour = fmod(time_hr, 24.); //[hr] + // DEBUG + if (sim_info.ms_ts.m_time == 302400) + int x = 0; + //Time calculations int day_of_year = (int)ceil(time_hr / 24.); //Day of the year // Duffie & Beckman 1.5.3b @@ -3200,23 +2755,23 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade if (SolarZenRad < pi / 2.) { //Convert the solar angles to collector incidence angles - CSP::theta_trans(SolarAz, weather.m_solzen, m_ColAz, phi_t, theta_L); + 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 - eta_optical = eta_opt_fixed * max(optical_table.interpolate(SolarAz, min(weather.m_solzen, pi / 2.)), 0.0); + m_eta_optical = eta_opt_fixed * max(optical_table.interpolate(SolarAz, min(SolarZenRad, pi / 2.)), 0.0); break; case 2: //incidence angle table //user provides an optical table as a function of collector incidence angles - eta_optical = eta_opt_fixed * max(optical_table.interpolate(phi_t, max(theta_L, 0.0)), 0.0); + 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 - eta_optical = eta_opt_fixed * - CSP::poly_eval(phi_t, &m_IAM_T_coefs[0], m_IAM_T_coefs.size()) * - CSP::poly_eval(theta_L, &m_IAM_L_coefs[0], m_IAM_L_coefs.size()); + 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 @@ -3227,23 +2782,24 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade return; } - eta_optical *= m_ftrack; + m_eta_optical *= m_ftrack; } else { - eta_optical = 0.0; - phi_t = pi / 2.; - theta_L = 0.0; + m_eta_optical = 0.0; + m_phi_t = pi / 2.; + m_theta_L = 0.0; } - m_q_i = m_I_b * m_A_aperture / m_L_mod; //[W/m] The incoming solar irradiation per aperture length + 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) = eta_optical; + m_ColOptEff.at(j) = m_eta_optical; m_q_SCA[j] = m_q_i; //[W/m] The flux on the collector } - m_dni_costh = weather.m_beam * m_CosTh_ave; //[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 // This is because it takes power to move into and out of defocus, and we'd probably @@ -3253,13 +2809,17 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade m_control_defocus = m_component_defocus = 1.0; //[-] m_q_dot_inc_sf_tot = m_Ap_tot * weather.m_beam / 1.E6; //[MWt] + + // DEBUG + if (sim_info.ms_ts.m_time == 302400) + int x = 0; } } void C_csp_fresnel_collector_receiver::loop_optical_eta_off() { // If trough is not absorbing any sunlight (night or 100% defocus), then set member data as necessary - m_costh = 0.0; //[-] Cosine of the incident angle between the sun and trough aperture + //m_costh = 0.0; //[-] Cosine of the incident angle between the sun and trough aperture m_q_i = 0; //[W/m] DNI * A_aper / L_sca //m_IAM.assign(m_IAM.size(), 0.0); //[-] Incidence angle modifiers @@ -3278,7 +2838,7 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta_off() m_IAM_ave = 0.0; m_RowShadow_ave = 0.0; m_EndLoss_ave = 0.0; - m_dni_costh = 0.0; + //m_dni_costh = 0.0; m_W_dot_sca_tracking = 0.0; //[MWe] m_control_defocus = 1.0; @@ -3334,11 +2894,6 @@ void C_csp_fresnel_collector_receiver::apply_control_defocus(double defocus) // Store control defocus m_control_defocus = defocus; - // Uses m_q_i, m_costh, and input defocus to calculate m_q_SCA_control_df - - // Store control defocus - m_control_defocus = defocus; - if (m_fthrctrl == 0) { mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." @@ -3355,10 +2910,11 @@ void C_csp_fresnel_collector_receiver::apply_control_defocus(double defocus) { for (int i = 0; i < m_nMod; i++) { - m_q_SCA_control_df[i] = defocus * m_q_i * m_costh; + m_q_SCA_control_df[i] = defocus * m_q_i; } } + } void C_csp_fresnel_collector_receiver::apply_component_defocus(double defocus /*-*/) @@ -4983,7 +4539,7 @@ int C_csp_fresnel_collector_receiver::C_mono_eq_defocus::operator()(double defoc mpc_trough->apply_component_defocus(defocus); // Solve the loop energy balance at the input mass flow rate - int exit_code = mpc_trough->loop_energy_balance_T_t_int(ms_weather, m_T_cold_in, m_m_dot_loop, ms_sim_info); + E_loop_energy_balance_exit exit_code = mpc_trough->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) { @@ -5000,7 +4556,7 @@ int C_csp_fresnel_collector_receiver::C_mono_eq_defocus::operator()(double defoc 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 - int exit_code = mpc_trough->loop_energy_balance_T_t_int(ms_weather, m_T_cold_in, m_dot_htf_loop, ms_sim_info); + E_loop_energy_balance_exit exit_code = mpc_trough->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) { diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 0d00decf9..79dbc0eda 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -75,17 +75,46 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver E_PRESSURE_DROP, //[bar] E_W_DOT_SCA_TRACK, //[MWe] - E_W_DOT_PUMP //[MWe] + E_W_DOT_PUMP, //[MWe] + + + // From Fresnel + E_THETA_L, //[deg] + E_PHI_T, + E_ETA_OPTICAL, + //E_EQ_OPT_EFF, + + E_SF_DEF, + E_Q_INC_SF_TOT, + E_Q_ABS_TOT, + E_Q_DUMP, + E_Q_LOSS_TOT, + E_PIPE_HL, + E_Q_AVAIL, + E_Q_LOSS_SPEC_TOT, + E_ETA_THERMAL, + E_E_BAL_STARTUP, + E_M_DOT_AVAIL, + E_M_DOT_HTF2, + E_DP_TOT, + E_T_SYS_C, + E_T_SYS_H, + E_T_LOOP_OUTLET + + + }; + + enum struct E_loop_energy_balance_exit + { + SOLVED, + NaN }; - // Private Fields private: // Fields in Trough - C_csp_reported_outputs mc_reported_outputs; - 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 @@ -97,7 +126,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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_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 @@ -109,13 +138,13 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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_P_hdr; //[Pa] Gauge pessure in header sections std::vector m_DP_loop; //[bar] Pressure drop in loop sections std::vector m_T_loop_dsn; //[C] Temperature entering loop sections at design std::vector m_P_loop_dsn; //[bar] Gauge pessure in loop sections at design std::vector m_T_loop; //[K] Temperature entering loop sections - std::vector m_P_loop; //[Pa] Gauge pessure in loop sections + //std::vector m_P_loop; //[Pa] Gauge pessure in loop sections OpticalDataTable optical_table; @@ -192,8 +221,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double m_RowShadow_ave; //[-] Field average row shadowing loss double m_EndLoss_ave; //[-] Field average end loss - 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_costh; //[-] Cosine of the incidence angle between sun and trough aperture + //double m_dni_costh; //[W/m2] DNI x cos(theta) product 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 @@ -315,15 +344,23 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver C_csp_collector_receiver::E_csp_cr_modes m_operating_mode_converged; C_csp_collector_receiver::E_csp_cr_modes m_operating_mode; + const double fp_offset = 0; // freeze protection offset + // *********************** // ***** T E M P ****** double m_step_recirc; + double m_current_hr; + double m_current_day; + + + // Fields NOT in Trough double N_run_mult; bool no_fp, //Freeze protection flag is_fieldgeom_init; //Flag to indicate whether the field geometry has been initialized + double A_loop; @@ -336,10 +373,10 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double m_htf_prop_min; - double eta_optical; //Collector total optical efficiency + double m_eta_optical; //Collector total optical efficiency double eta_opt_fixed; - double phi_t; //Solar incidence angle in the collector transversal plane - double theta_L; //Solar incidence angle in the collector longitudinal plane + 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 emit_table m_epsilon_abs; @@ -349,6 +386,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver string m_piping_summary; + double m_sf_def; + // Private Methods private: @@ -363,13 +402,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver void set_output_value(); - // TEMPORARY - int x(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); - // This method is designed to pass the timestep integrated HTF temperature to successive energy balance nodes - int loop_energy_balance_T_t_int(const C_csp_weatherreader::S_outputs& weather, + 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); @@ -378,7 +412,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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*); - // Public Input Fields + // Public Fields public: int m_nMod; // Number of collector modules in a loop (m_nSCA) @@ -473,16 +507,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double m_L_rnr_pb; //[m] Length of hot or cold runner pipe around the power block - // Removed - //double m_Row_Distance; //[m] Spacing between rows (centerline to centerline) - //double m_wind_stow_speed;//[m/s] Wind speed at and above which the collectors will be stowed - //bool m_accept_init; //[-] In acceptance testing mode - require steady-state startup - //int m_accept_loc; // Treat as = 1 //[-] In acceptance testing mode - temperature sensor location (1=hx,2=loop) - //bool m_calc_design_pipe_vals; // Treat as FALSE //[-] Should the HTF state be calculated at design conditions - //bool m_is_using_input_gen; // Treat as FALSE - - // Questionable - double m_I_b; //Direct normal incident solar irradiation + C_csp_reported_outputs mc_reported_outputs; // Methods public: From 0408d0f925f5bb15ccfdbed37b49db027d32d63a Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 16 Feb 2023 08:26:18 -0700 Subject: [PATCH 05/46] Update new fresnel model to use same cmod inputs as original fresnel. Fix TES declaration --- ssc/cmod_fresnel_physical.cpp | 72 ++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 5bfc9fc85..8243d1dfe 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -122,7 +122,7 @@ static var_info _cm_vtab_fresnel_physical[] = { { SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "controller", "*", "INTEGER", ""}, - { SSC_INPUT, SSC_NUMBER, "T_field_ini", "Initial field temperature", "C", "", "controller", "*", "", ""}, + //{ SSC_INPUT, SSC_NUMBER, "T_field_ini", "Initial field temperature", "C", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "controller", "*", "", ""}, { SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "controller", "*", "", ""}, @@ -377,7 +377,7 @@ static var_info _cm_vtab_fresnel_physical[] = { // Solar Field (from fresnel) { SSC_OUTPUT, SSC_ARRAY, "theta_L", "Field collector incidence angle - longitudinal", "deg", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "phi_t", "Field collector incidence angle - transversal", "deg", "", "solar_field", " * ", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "phi_t", "Field collector incidence angle - transversal", "deg", "", "solar_field", "*", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "eta_optical", "Field collector optical efficiency", "", "", "solar_field", "*", "", "" }, //{ SSC_OUTPUT, SSC_ARRAY, "EqOptEff", "Field collector and receiver optical efficiency", "", "", "solar_field", "*", "", "" }, @@ -538,6 +538,26 @@ class cm_fresnel_physical : public compute_module bool is_dispatch = false; bool is_dispatch_series = false; + // TES + // No custom TES piping + //bool custom_tes_pipe_sizes = false; + //util::matrix_t tes_wallthicks(1, 1); + //tes_wallthicks.at(0, 0) = -1; + //util::matrix_t tes_diams(1, 1); + //tes_diams.at(0, 0) = -1; + double init_hot_htf_percent = 0.3; + double cold_tank_max_heat = 25; + double hot_tank_max_heat = 25; + + // TOU + // NOT in CMOD inputs + bool is_tod_pc_target_also_pc_max = false; + vector f_turb_tou_periods{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + size_t n_f_turbine = f_turb_tou_periods.size(); + int csp_financial_model = 8; + + + // Weather reader C_csp_weatherreader weather_reader; C_csp_solver::S_sim_setup sim_setup; @@ -873,12 +893,6 @@ class cm_fresnel_physical : public compute_module // TES C_csp_two_tank_tes storage; { - // No custom TES piping - bool custom_tes_pipe_sizes = false; - util::matrix_t tes_wallthicks(1, 1); - tes_wallthicks.at(0, 0) = -1; - util::matrix_t tes_diams(1, 1); - tes_diams.at(0, 0) = -1; // Convert array to matrix util::matrix_t k_tes_loss_coeffs_mat; @@ -891,13 +905,6 @@ class cm_fresnel_physical : public compute_module delete k_tes_loss_coeffs_array; } - // NOT in cmod inputs - double init_hot_htf_percent = 0.3; - double cold_tank_max_heat = 25; - double hot_tank_max_heat = 25; - - - util::matrix_t tes_lengths; if (is_assigned("tes_lengths")) { tes_lengths = as_matrix("tes_lengths"); //[m] @@ -932,20 +939,22 @@ class cm_fresnel_physical : public compute_module as_double("pb_pump_coef"), as_boolean("tanks_in_parallel"), as_double("V_tes_des"), - as_boolean("calc_design_pipe_vals"), - as_double("tes_pump_coef"), - 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") + false ); + //as_boolean("calc_design_pipe_vals"), + //as_double("tes_pump_coef"), + //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(); @@ -967,12 +976,7 @@ class cm_fresnel_physical : public compute_module C_csp_tou_block_schedules::S_params* tou_params = &tou.ms_params; double ppa_price_year1 = std::numeric_limits::quiet_NaN(); { - // NOT in CMOD inputs - bool is_tod_pc_target_also_pc_max = false; - bool is_dispatch = false; - vector f_turb_tou_periods{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - size_t n_f_turbine = f_turb_tou_periods.size(); - int csp_financial_model = 8; + tou_params->mc_csp_ops.mc_weekdays = as_matrix("weekday_schedule"); tou_params->mc_csp_ops.mc_weekends = as_matrix("weekend_schedule"); From 35ef604f437c6ec1a984266509841e3c85bdbdc4 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Tue, 28 Feb 2023 13:52:50 -0700 Subject: [PATCH 06/46] Begin synchronizing updated Molten Salt Linear Fresnel model with new UI --- ssc/cmod_fresnel_physical.cpp | 446 +++++++++--------- tcs/csp_solver_fresnel_collector_receiver.cpp | 111 +++-- tcs/csp_solver_fresnel_collector_receiver.h | 15 +- 3 files changed, 311 insertions(+), 261 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 8243d1dfe..7207d54e1 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -47,30 +47,52 @@ static var_info _cm_vtab_fresnel_physical[] = { // Weather Reader { SSC_INPUT, SSC_STRING, "file_name", "Local weather file with path", "none", "", "weather", "*", "LOCAL_FILE", "" }, - // TOU - { SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 Time of Use Values for week days", "", "", "tou_translator", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 Time of Use Values for week end days", "", "", "tou_translator", "*", "", "" }, + // System Design + /*System Design*///{ SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "nLoops", "Number of loops in the field", "", "", "controller", "*", "", ""}, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "controller", "*", "", ""}, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "controller", "*", "", ""}, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "controller", "*", "", ""}, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "P_ref", "Label", "-", "", "powerblock", "*", "", ""}, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "powerblock", "*", "", ""}, + /*System Design*///{ SSC_INPUT, SSC_NUMBER, "q_pb_design", "Label", "", "", "controller", "*", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "tshours", "Label", "", "", "controller", "*", "", "" }, + /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult", "Solar multiple", "", "", "controller", "*", "", ""}, + + + // System Control + /*Sys Control*/{ SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 Time of Use Values for week days", "", "", "tou_translator", "*", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 Time of Use Values for week end days", "", "", "tou_translator", "*", "", "" }, + + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Label", "", "", "controller", "*", "", ""}, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "bop_array", "Label", "", "", "controller", "*", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "controller", "*", "", "" }, + + + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "tou", "?=0.99", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, + //{ 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", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "" }, //{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "" }, //{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "tou", "is_dispatch=1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "tou", "is_dispatch=1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "tou", "is_dispatch=1", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "tou", "?=0.99", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "tou", "is_dispatch=1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "" }, @@ -99,103 +121,92 @@ static var_info _cm_vtab_fresnel_physical[] = { //{ SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA prices - yearly", "$/kWh", "", "Revenue", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, //{ 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", "", "" }, - { SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_ARRAY, "bop_array", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "water_usage_per_wash", "Water usage per wash", "L/m2_aper", "", "system", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "washing_frequency", "Mirror washing frequency", "-/year", "", "system", "*", "", "" }, // Solar Field (from cmod_tcsmslf.cpp) - { SSC_INPUT, SSC_NUMBER, "nMod", "Number of collector modules in a loop", "", "", "controller", "*", "INTEGER", ""}, - { SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variantions", "", "", "controller", "?=4", "INTEGER", ""}, - { SSC_INPUT, SSC_NUMBER, "nLoops", "Number of loops in the field", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "eta_pump", "HTF pump efficiency", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "HDR_rough", "Header pipe roughness", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "theta_stow", "stow angle", "deg", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "theta_dep", "deploy angle", "deg", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "FieldConfig", "Number of subfield headers", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "T_startup", "Power block startup temperature", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "pb_rated_cap", "Rated plant capacity", "MWe", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "m_dot_htfmax", "Maximum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "controller", "*", "INTEGER", ""}, - //{ SSC_INPUT, SSC_NUMBER, "T_field_ini", "Initial field temperature", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "fthrok", "Flag to allow partial defocusing of the collectors", "", "", "controller", "*", "INTEGER", ""}, - { SSC_INPUT, SSC_NUMBER, "fthrctrl", "Defocusing strategy", "", "", "controller", "*", "INTEGER", ""}, - { SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "solar_mult", "Solar multiple", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "mc_bal_hot", "The heat capacity of the balance of plant on the hot side", "kWht/K-MWt", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "mc_bal_cold", "The heat capacity of the balance of plant on the cold side", "kWht/K-MWt", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "controller", "*", "", ""}, - - { SSC_INPUT, SSC_NUMBER, "water_per_wash", "Water usage per wash", "L/m2_aper", "", "solar_field", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "washes_per_year", "Mirror washing frequency", "none", "", "solar_field", "*", "", "" }, - - { SSC_INPUT, SSC_NUMBER, "opt_model", "The optical model", "", "", "controller", "*", "INTEGER", ""}, - { SSC_INPUT, SSC_NUMBER, "A_aperture", "Reflective aperture area of the collector", "m2", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "reflectivity", "Solar-weighted mirror reflectivity value", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "TrackingError", "Tracking error derate", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "GeomEffects", "Geometry effects derate", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "Dirt_mirror", "User-defined dirt on mirror derate", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "Error", "User-defined general optical error derate", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "L_mod", "The length of the collector module", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "IAM_T_coefs", "Incidence angle modifier coefficients - transversal plane", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "IAM_L_coefs", "Incidence angle modifier coefficients - longitudinal plane", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_MATRIX, "OpticalTable", "Values of the optical efficiency table", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "rec_model", "Receiver model type (1=Polynomial ; 2=Evac tube)", "", "", "controller", "*", "INTEGER", ""}, - { SSC_INPUT, SSC_ARRAY, "HCE_FieldFrac", "The fraction of the field occupied by this HCE type", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "D_abs_in", "The inner absorber tube diameter", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "D_abs_out", "The outer absorber tube diameter", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "D_glass_in", "The inner glass envelope diameter", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "D_glass_out", "The outer glass envelope diameter", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "D_plug", "The diameter of the absorber flow plug (optional)", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "Flow_type", "The flow type through the absorber", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "Rough", "Roughness of the internal surface", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "alpha_env", "Envelope absorptance", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_MATRIX, "epsilon_abs_1", "Absorber emittance - HCE variation 1", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_MATRIX, "epsilon_abs_2", "Absorber emittance - HCE variation 2", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_MATRIX, "epsilon_abs_3", "Absorber emittance - HCE variation 3", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_MATRIX, "epsilon_abs_4", "Absorber emittance - HCE variation 4", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "alpha_abs", "Absorber absorptance", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "Tau_envelope", "Envelope transmittance", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "epsilon_glass", "Glass envelope emissivity", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "GlazingIntactIn", "The glazing intact flag", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "P_a", "Annulus gas pressure", "torr", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "AnnulusGas", "Annulus gas type (1=air; 26=Ar; 27=H2)", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "AbsorberMaterial", "Absorber material type", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "Shadowing", "Receiver bellows shadowing loss factor", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "dirt_env", "Loss due to dirt on the receiver envelope", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "Design_loss", "Receiver heat loss at design", "W/m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "L_mod_spacing", "Piping distance between sequential modules in a loop", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "L_crossover", "Length of crossover piping in a loop", "m", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "HL_T_coefs", "HTF temperature-dependent heat loss coefficients", "W/m-K", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "HL_w_coefs", "Wind-speed-dependent heat loss coefficients", "W/m-(m/s)", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "DP_nominal", "Pressure drop across a single collector assembly at design", "bar", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_ARRAY, "DP_coefs", "Pressure drop mass flow based part-load curve", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "rec_htf_vol", "Volume of HTF in a single collector unit per unit aperture area", "L/m2-ap", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "controller", "*", "", ""}, - - { SSC_INPUT, SSC_NUMBER, "T_db", "Dry bulb air temperature", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "V_wind", "Ambient windspeed", "m/s", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "P_amb", "Ambient pressure", "atm", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "T_dp", "The dewpoint temperature", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "T_cold_in", "HTF return temperature", "C", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "defocus", "Defocus control", "", "", "controller", "*", "", ""}, - - { SSC_INPUT, SSC_NUMBER, "tilt", "Tilt angle of surface/axis", "", "", "Weather", "*", "", "" }, - - { SSC_INPUT, SSC_NUMBER, "calc_design_pipe_vals", "Calculate temps and pressures at design conditions for runners and headers", "-", "", "controller", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "nMod", "Number of collector modules in a loop", "", "", "controller", "*", "INTEGER", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "eta_pump", "HTF pump efficiency", "", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "HDR_rough", "Header pipe roughness", "m", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_stow", "stow angle", "deg", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_dep", "deploy angle", "deg", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "FieldConfig", "Number of subfield headers", "", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmax", "Maximum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "controller", "*", "INTEGER", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "fthrok", "Flag to allow partial defocusing of the collectors", "", "", "controller", "*", "INTEGER", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "fthrctrl", "Defocusing strategy", "", "", "controller", "*", "INTEGER", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_hot", "The heat capacity of the balance of plant on the hot side", "kWht/K-MWt", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_cold", "The heat capacity of the balance of plant on the cold side", "kWht/K-MWt", "", "controller", "*", "", ""}, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "controller", "*", "", ""}, + /*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", "", "controller", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "controller", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "controller", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "controller", "*", "", "" }, + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "controller", "*", "", "" }, + + // In old fresnel but not Trough + //{ SSC_INPUT, SSC_NUMBER, "pb_rated_cap", "Rated plant capacity", "MWe", "", "controller", "*", "", ""}, + + // Collector and Receiver + + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "controller", "*", "", "" }, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "opt_model", "The optical model", "", "", "controller", "*", "INTEGER", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "A_aperture", "Reflective aperture area of the collector", "m2", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "reflectivity", "Solar-weighted mirror reflectivity value", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "TrackingError", "Tracking error derate", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "GeomEffects", "Geometry effects derate", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Dirt_mirror", "User-defined dirt on mirror derate", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Error", "User-defined general optical error derate", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod", "The length of the collector module", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_T_coefs", "Incidence angle modifier coefficients - transversal plane", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_L_coefs", "Incidence angle modifier coefficients - longitudinal plane", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "OpticalTable", "Values of the optical efficiency table", "", "", "controller", "*", "", ""}, + + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "rec_model", "Receiver model type (1=Polynomial ; 2=Evac tube)", "", "", "controller", "*", "INTEGER", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HCE_FieldFrac", "The fraction of the field occupied by this HCE type", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_in", "The inner absorber tube diameter", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_out", "The outer absorber tube diameter", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_in", "The inner glass envelope diameter", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_out", "The outer glass envelope diameter", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_plug", "The diameter of the absorber flow plug (optional)", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Flow_type", "The flow type through the absorber", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Rough", "Roughness of the internal surface", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_env", "Envelope absorptance", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_1", "Absorber emittance - HCE variation 1", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_2", "Absorber emittance - HCE variation 2", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_3", "Absorber emittance - HCE variation 3", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_4", "Absorber emittance - HCE variation 4", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_abs", "Absorber absorptance", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Tau_envelope", "Envelope transmittance", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "epsilon_glass", "Glass envelope emissivity", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "GlazingIntactIn", "The glazing intact flag", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "P_a", "Annulus gas pressure", "torr", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AnnulusGas", "Annulus gas type (1=air; 26=Ar; 27=H2)", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AbsorberMaterial", "Absorber material type", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Shadowing", "Receiver bellows shadowing loss factor", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "dirt_env", "Loss due to dirt on the receiver envelope", "", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Design_loss", "Receiver heat loss at design", "W/m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod_spacing", "Piping distance between sequential modules in a loop", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_crossover", "Length of crossover piping in a loop", "m", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_T_coefs", "HTF temperature-dependent heat loss coefficients", "W/m-K", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_w_coefs", "Wind-speed-dependent heat loss coefficients", "W/m-(m/s)", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "DP_nominal", "Pressure drop across a single collector assembly at design", "bar", "", "controller", "*", "", ""}, + /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "DP_coefs", "Pressure drop mass flow based part-load curve", "", "", "controller", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variations", "", "", "controller", "?=4", "INTEGER", "" }, + + + + + //{ SSC_INPUT, SSC_NUMBER, "tilt", "Tilt angle of surface/axis", "", "", "Weather", "*", "", "" }, + + //{ SSC_INPUT, SSC_NUMBER, "calc_design_pipe_vals", "Calculate temps and pressures at design conditions for runners and headers", "-", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "custom_sf_pipe_sizes", "Use custom solar field pipe diams, wallthks, and lengths", "none", "", "solar_field", "*", "", "" }, @@ -216,17 +227,13 @@ static var_info _cm_vtab_fresnel_physical[] = { //{ SSC_INPUT, SSC_NUMBER, "V_tank_hot_ini", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "T_tank_hot_ini", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "T_tank_cold_ini", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "vol_tank", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "T_field_in_des", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "T_field_out_des", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "q_pb_design", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "W_pb_design", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "solarm", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "tes_temp", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "fossil_mode", "Label", "", "", "controller", "*", "INTEGER", "" }, //{ SSC_INPUT, SSC_NUMBER, "fthr_ok", "Label", "", "", "controller", "*", "INTEGER", "" }, //{ SSC_INPUT, SSC_NUMBER, "fc_on", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "t_standby_reset", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "tes_type", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_ARRAY, "tslogic_a", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_ARRAY, "tslogic_b", "Label", "", "", "controller", "*", "", "" }, @@ -244,63 +251,69 @@ static var_info _cm_vtab_fresnel_physical[] = { //{ SSC_INPUT, SSC_ARRAY, "sgs_lengths", "Custom SGS lengths", "m", "", "controller", "*", "", "" }, // Power Cycle Inputs - { SSC_INPUT, SSC_NUMBER, "pc_config", "0: Steam Rankine (224), 1: user defined", "-", "", "powerblock", "?=0", "INTEGER", ""}, - { SSC_INPUT, SSC_NUMBER, "P_ref", "Label", "-", "", "powerblock", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "powerblock", "*", "", ""}, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "T_startup", "Power block startup temperature", "C", "", "controller", "*", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "powerblock", "*", "", ""}, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "powerblock", "*", "", ""}, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby mode", "none", "", "powerblock", "*", "", ""}, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "powerblock", "*", "", ""}, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "", "", "controller", "*", "", "" }, + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "controller", "*", "", "" }, + { SSC_INPUT, SSC_NUMBER, "pc_config", "0: Steam Rankine (224), 1: user defined", "-", "", "powerblock", "?=0", "INTEGER", "" }, + + /*Power Cycle*///{ SSC_INPUT, SSC_NUMBER, "t_standby_reset", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "-", "", "powerblock", "*", "", ""}, //{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "-", "", "powerblock", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "powerblock", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "powerblock", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby mode", "none", "", "powerblock", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "powerblock", "*", "", "" }, + + // Steam Rankine Cycle - { SSC_INPUT, SSC_NUMBER, "dT_cw_ref", "Reference condenser cooling water inlet/outlet T diff", "C", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_amb_des", "Reference ambient temperature at design point", "C", "", "powerblock", "pc_config=0", "", "" }, + /*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=trough)", "1/2/3", "tower/trough/user", "powerblock", "pc_config=0", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "P_boil", "Boiler operating pressure", "bar", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "CT", "Flag for using dry cooling or wet cooling system", "none", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_approach", "Cooling tower approach temperature", "C", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_ITD_des", "ITD at design for dry system", "C", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "P_cond_ratio", "Condenser pressure ratio", "none", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "pb_bd_frac", "Power block blowdown steam fraction ", "none", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "P_cond_min", "Minimum condenser pressure", "inHg", "", "powerblock", "pc_config=0", "", "" }, - { SSC_INPUT, SSC_NUMBER, "n_pl_inc", "Number of part-load increments for the heat rejection system", "none", "", "powerblock", "pc_config=0", "", "" }, - { 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", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tech_type", "Turbine inlet pressure control flag (sliding=user, fixed=trough)", "1/2/3", "tower/trough/user", "powerblock", "pc_config=0", "", "" }, - - { SSC_INPUT, SSC_NUMBER, "DP_SGS", "Pressure drop within the steam generator", "bar", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "controller", "*", "", "" }, - + + /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "DP_SGS", "Pressure drop within the steam generator", "bar", "", "controller", "*", "", "" }, + // User Defined 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", "", "" }, - { 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", "", "" }, - { 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", "", "" }, + /*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 - { SSC_INPUT, SSC_NUMBER, "store_fluid", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_MATRIX, "store_fl_props", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tshours", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "h_tank", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "u_tank", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tank_pairs", "Label", "", "", "controller", "*", "INTEGER", "" }, - { SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tank_max_heat", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "store_fluid", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_MATRIX, "store_fl_props", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "u_tank", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_pairs", "Label", "", "", "controller", "*", "INTEGER", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_max_heat", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_hot", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank_min", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_cold", "Label", "", "", "controller", "*", "", "" }, + /*Storage*///{ SSC_INPUT, SSC_NUMBER, "vol_tank", "Label", "", "", "controller", "*", "", "" }, + /*Storage*///{ SSC_INPUT, SSC_NUMBER, "tes_temp", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "hot_tank_max_heat", "Rated heater capacity for hot tank heating", "MWe", "", "TES", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "cold_tank_max_heat", "Rated heater capacity for cold tank heating", "MWe", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "dt_hot", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "h_tank_min", "Label", "", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "init_hot_htf_percent", "Initial fraction of avail. vol that is hot", "%", "", "TES", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "dt_cold", "Label", "", "", "controller", "*", "", "" }, - - { SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "has_hot_tank_bypass", "Bypass valve connects field outlet to cold tank", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "T_tank_hot_inlet_min", "Minimum hot tank htf inlet temperature", "C", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "tes_pump_coef", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "custom_tes_p_loss", "TES pipe losses are based on custom lengths and coeffs", "-", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_ARRAY, "k_tes_loss_coeffs", "Minor loss coeffs for the coll, gen, and bypass loops", "-", "", "controller", "*", "", "" }, + + + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "has_hot_tank_bypass", "Bypass valve connects field outlet to cold tank", "-", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "T_tank_hot_inlet_min", "Minimum hot tank htf inlet temperature", "C", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_NUMBER, "tes_pump_coef", "Label", "", "", "controller", "*", "", "" }, + /*Storage*/{ SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "controller", "*", "", "" }, + + //{ SSC_INPUT, SSC_NUMBER, "custom_tes_p_loss", "TES pipe losses are based on custom lengths and coeffs", "-", "", "controller", "*", "", "" }, + //{ SSC_INPUT, SSC_ARRAY, "k_tes_loss_coeffs", "Minor loss coeffs for the coll, gen, and bypass loops", "-", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "custom_tes_pipe_sizes", "Use custom TES pipe diams, wallthks, and lengths", "-", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_MATRIX, "tes_diams", "Custom TES diameters", "m", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_MATRIX, "tes_wallthicks", "Custom TES wall thicknesses", "m", "", "controller", "*", "", "" }, @@ -317,7 +330,7 @@ static var_info _cm_vtab_fresnel_physical[] = { //{ SSC_INPUT, SSC_NUMBER, "specified_total_aperture", "specified_total_aperture", "-", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_NUMBER, "non_solar_field_land_area_multiplier", "non_solar_field_land_area_multiplier", "-", "", "controller", "*", "", "" }, //{ SSC_INPUT, SSC_ARRAY, "trough_loop_control", "trough_loop_control", "-", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_wlim_maxspec", "disp_wlim_maxspec", "-", "", "controller", "*", "", "" }, + /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_wlim_maxspec", "disp_wlim_maxspec", "-", "", "controller", "*", "", "" }, @@ -400,6 +413,8 @@ static var_info _cm_vtab_fresnel_physical[] = { { SSC_OUTPUT, SSC_ARRAY, "T_sys_h", "Field HTF temperature hot header outlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, { SSC_OUTPUT, SSC_ARRAY, "t_loop_outlet", "Field HTF temperature loop outlet", "C", "", "mslf", "*", "LENGTH=8760", "" }, + { SSC_OUTPUT, SSC_ARRAY, "q_I", "Incident Radiation", "C", "", "mslf", "*", "LENGTH=8760", "" }, + // power block { SSC_OUTPUT, SSC_ARRAY, "eta", "PC efficiency: gross", "", "", "powerblock", "*", "", "" }, @@ -530,21 +545,17 @@ class cm_fresnel_physical : public compute_module cm_fresnel_physical() { add_var_info(_cm_vtab_fresnel_physical); + add_var_info(vtab_adjustment_factors); } void exec() { - // NOT IN TROUGH + bool is_dispatch = false; bool is_dispatch_series = false; // TES // No custom TES piping - //bool custom_tes_pipe_sizes = false; - //util::matrix_t tes_wallthicks(1, 1); - //tes_wallthicks.at(0, 0) = -1; - //util::matrix_t tes_diams(1, 1); - //tes_diams.at(0, 0) = -1; double init_hot_htf_percent = 0.3; double cold_tank_max_heat = 25; double hot_tank_max_heat = 25; @@ -621,7 +632,7 @@ class cm_fresnel_physical : public compute_module c_fresnel.m_fthrok = as_integer("fthrok"); c_fresnel.m_fthrctrl = as_integer("fthrctrl"); c_fresnel.m_ColAz = as_number("ColAz"); - c_fresnel.m_ColTilt = as_number("tilt"); + //c_fresnel.m_ColTilt = as_number("tilt"); c_fresnel.m_solar_mult = as_number("solar_mult"); @@ -731,7 +742,7 @@ class cm_fresnel_physical : public compute_module // Hard Coded (currently no UI) - c_fresnel.m_L_rnr_pb = 25; + c_fresnel.m_L_rnr_pb = 50; //////////////////////// Questionable @@ -796,6 +807,7 @@ class cm_fresnel_physical : public compute_module 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); } } @@ -895,7 +907,7 @@ class cm_fresnel_physical : public compute_module { // Convert array to matrix - util::matrix_t k_tes_loss_coeffs_mat; + /*util::matrix_t k_tes_loss_coeffs_mat; { size_t size; ssc_number_t* k_tes_loss_coeffs_array = as_array("k_tes_loss_coeffs", &size); @@ -903,7 +915,7 @@ class cm_fresnel_physical : public compute_module size_t sizeof1 = 1; k_tes_loss_coeffs_mat = util::matrix_t(sizeof1, size, &k_tes_loss_coeffs_vec); delete k_tes_loss_coeffs_array; - } + }*/ util::matrix_t tes_lengths; if (is_assigned("tes_lengths")) { @@ -1362,42 +1374,42 @@ class cm_fresnel_physical : public compute_module // DEBUG - if (true) + if (false) { size_t size; ssc_number_t* x; x = as_array("Theta_ave", &size); vector Theta_ave_vec(x, x + size); - delete x; + //delete x; x = as_array("CosTh_ave", &size); vector CosTh_ave_vec(x, x + size); - delete x; + //delete x; x = as_array("IAM_ave", &size); vector IAM_ave_vec(x, x + size); - delete x; + //delete x; x = as_array("RowShadow_ave", &size); vector RowShadow_ave_vec(x, x + size); - delete x; + //delete x; x = as_array("EndLoss_ave", &size); vector EndLoss_ave_vec(x, x + size); - delete x; + //delete x; x = as_array("dni_costh", &size); vector dni_costh_vec(x, x + size); - delete x; + //delete x; x = as_array("EqOpteff", &size); vector EqOpteff_vec(x, x + size); - delete x; + //delete x; x = as_array("SCAs_def", &size); vector SCAs_def_vec(x, x + size); - delete x; + //delete x; /*x = as_array("q_inc_sf_tot", &size); @@ -1406,75 +1418,75 @@ class cm_fresnel_physical : public compute_module x = as_array("qinc_costh", &size); vector qinc_costh_vec(x, x + size); - delete x; + //delete x; x = as_array("q_dot_rec_inc", &size); vector q_dot_rec_inc_vec(x, x + size); - delete x; + //delete x; x = as_array("q_dot_rec_thermal_loss", &size); vector q_dot_rec_thermal_loss_vec(x, x + size); - delete x; + //delete x; x = as_array("q_dot_rec_abs", &size); vector q_dot_rec_abs_vec(x, x + size); - delete x; + //delete x; x = as_array("q_dot_piping_loss", &size); vector q_dot_piping_loss_vec(x, x + size); - delete x; + //delete x; x = as_array("e_dot_field_int_energy", &size); vector e_dot_field_int_energy_vec(x, x + size); - delete x; + //delete x; x = as_array("q_dot_htf_sf_out", &size); vector q_dot_htf_sf_out_vec(x, x + size); - delete x; + //delete x; x = as_array("q_dot_freeze_prot", &size); vector q_dot_freeze_prot_vec(x, x + size); - delete x; + //delete x; x = as_array("m_dot_loop", &size); vector m_dot_loop_vec(x, x + size); - delete x; + //delete x; x = as_array("recirculating", &size); vector recirculating_vec(x, x + size); - delete x; + //delete x; x = as_array("m_dot_field_recirc", &size); vector m_dot_field_recirc_vec(x, x + size); - delete x; + //delete x; x = as_array("m_dot_field_delivered", &size); vector m_dot_field_delivered_vec(x, x + size); - delete x; + //delete x; x = as_array("T_field_cold_in", &size); vector T_field_cold_in_vec(x, x + size); - delete x; + //delete x; x = as_array("T_rec_cold_in", &size); vector T_rec_cold_in_vec(x, x + size); - delete x; + //delete x; x = as_array("T_rec_hot_out", &size); vector T_rec_hot_out_vec(x, x + size); - delete x; + //delete x; x = as_array("T_field_hot_out", &size); vector T_field_hot_out_vec(x, x + size); - delete x; + //delete x; x = as_array("deltaP_field", &size); vector deltaP_field_vec(x, x + size); - delete x; + //delete x; x = as_array("W_dot_sca_track", &size); vector W_dot_sca_track_vec(x, x + size); - delete x; + //delete x; x = as_array("W_dot_field_pump", &size); vector W_dot_field_pump_vec(x, x + size); - delete x; + //delete x; // Fresnel x = as_array("theta_L", &size); vector theta_L_vec(x, x + size); - delete x; + //delete x; x = as_array("phi_t", &size); vector phi_t_vec(x, x + size); - delete x; + //delete x; x = as_array("eta_optical", &size); vector eta_optical_vec(x, x + size); - delete x; + //delete x; //x = as_array("EqOptEff", &size); //vector EqOptEff_vec(x, x + size); @@ -1483,53 +1495,53 @@ class cm_fresnel_physical : public compute_module x = as_array("sf_def", &size); vector sf_def_vec(x, x + size); - delete x; + //delete x; x = as_array("q_inc_sf_tot", &size); vector q_inc_sf_tot_vec(x, x + size); - delete x; + //delete x; x = as_array("q_abs_tot", &size); vector q_abs_tot_vec(x, x + size); - delete x; + //delete x; x = as_array("q_dump", &size); vector q_dump_vec(x, x + size); - delete x; + //delete x; x = as_array("q_loss_tot", &size); vector q_loss_tot_vec(x, x + size); - delete x; + //delete x; x = as_array("Pipe_hl", &size); vector Pipe_hl_vec(x, x + size); - delete x; + //delete x; x = as_array("q_avail", &size); vector q_avail_vec(x, x + size); - delete x; + //delete x; x = as_array("q_loss_spec_tot", &size); vector q_loss_spec_tot_vec(x, x + size); - delete x; + //delete x; x = as_array("eta_thermal", &size); vector eta_thermal_vec(x, x + size); - delete x; + //delete x; x = as_array("E_bal_startup", &size); vector E_bal_startup_vec(x, x + size); - delete x; + //delete x; x = as_array("m_dot_avail", &size); vector m_dot_avail_vec(x, x + size); - delete x; + //delete x; x = as_array("m_dot_htf2", &size); vector m_dot_htf2_vec(x, x + size); - delete x; + //delete x; x = as_array("DP_tot", &size); vector DP_tot_vec(x, x + size); - delete x; + //delete x; x = as_array("T_sys_c", &size); vector T_sys_c_vec(x, x + size); - delete x; + //delete x; x = as_array("T_sys_h", &size); vector T_sys_h_vec(x, x + size); - delete x; + //delete x; x = as_array("t_loop_outlet", &size); vector t_loop_outlet_vec(x, x + size); - delete x; + //delete x; // Actual Outputs x = as_array("time_hr", &size); @@ -1538,6 +1550,9 @@ class cm_fresnel_physical : public compute_module x = as_array("P_out_net", &size); vector P_out_net_vec(x, x + size); + x = as_array("q_I", &size); + vector q_I_vec(x, x + size); + int y = 0; } @@ -1548,7 +1563,6 @@ class cm_fresnel_physical : public compute_module assign("solar_multiple_actual", as_double("solar_mult")); // calculated during verify() using cmod_csp_trough_eqns.cpp // Convert Units - return; { // Do unit post-processing here double* p_q_pc_startup = allocate("q_pc_startup", n_steps_fixed); @@ -1635,8 +1649,6 @@ class cm_fresnel_physical : public compute_module } - - int x = 0; } diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index aacc555f3..ff29e8092 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -58,6 +58,7 @@ static C_csp_reported_outputs::S_output_info S_output_info[] = {C_csp_fresnel_collector_receiver::E_T_SYS_C, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_T_SYS_H, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_T_LOOP_OUTLET, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_I, C_csp_reported_outputs::TS_WEIGHTED_AVE}, csp_info_invalid }; @@ -154,7 +155,7 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double { //------Inlet, Outlet, and COP - DP_IOCOP = PressureDrop(m_dot_htf, (T_loop_in + T_loop_outX) / 2.0, 1.0, m_D_h.at(0), + 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 @@ -217,7 +218,7 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double 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 - DP_fromField += PressureDrop(m_dot_temp, T_loop_outX, 1.0, m_D_runner[i], m_HDR_rough, m_L_runner[i], x3, 0.0, 0.0, 0.0, + DP_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 if (i > 1) m_dot_temp = max(m_dot_temp - 2. * m_m_dot_htf_tot / float(m_nfsec), 0.0); @@ -238,7 +239,7 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double DP_hdr_cold = 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 //if(ErrorFound()) return 1 - DP_hdr_hot = DP_hdr_hot + PressureDrop(m_dot_header, T_loop_outX, 1.0, m_D_hdr[i], m_HDR_rough, + DP_hdr_hot = 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 //if(ErrorFound()) return 1 //Siphon off header mass flow rate at each loop. Multiply by 2 because there are 2 loops per hdr section @@ -287,7 +288,7 @@ void C_csp_fresnel_collector_receiver::set_output_value() mc_reported_outputs.value(E_ROWSHADOW_AVE, m_RowShadow_ave); //[-] mc_reported_outputs.value(E_ENDLOSS_AVE, m_EndLoss_ave); //[-] //mc_reported_outputs.value(E_DNI_COSTH, m_dni_costh); //[W/m2] - mc_reported_outputs.value(E_EQUIV_OPT_ETA_TOT, m_EqOpteff); //[-] + 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] @@ -357,6 +358,8 @@ void C_csp_fresnel_collector_receiver::set_output_value() mc_reported_outputs.value(E_T_SYS_H, m_T_sys_h_t_int_fullts - 273.15); // (duplicate) mc_reported_outputs.value(E_T_LOOP_OUTLET, m_T_htf_h_rec_out_t_int_fullts - 273.15); // (duplicate) + mc_reported_outputs.value(E_Q_I, m_q_i); + return; } @@ -423,35 +426,37 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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_rnr[i] - T_db); //[W] - m_Runner_hl_cold_tot += 2. * m_Runner_hl_cold; + 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_hdr[i] - T_db); //[W] - m_Header_hl_cold_tot += m_nfsec * m_Header_hl_cold; + 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] - 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); // 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] - 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); + //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); + 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); m_T_loop[0] = m_T_loop_in; m_T_htf_in_t_int[0] = m_T_loop_in; @@ -489,7 +494,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle double q_inc_total = 0.; double q_abs_abs_total = 0.; double q_abs_htf_total = 0.; - std::vector m_EqOpteffs(m_nMod, 0.); + std::vector EqOpteffs(m_nMod, 0.); // MAIN SCA Temperature Solve // Loop through each Module @@ -537,13 +542,13 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle rho_htf_i += rho_htf_j * m_HCE_FieldFrac[j]; //keep track of the total equivalent optical efficiency - m_EqOpteffs[i] += m_ColOptEff[i] * m_Shadowing[j] * m_dirt_env[j] * m_alpha_abs[j] * m_Tau_envelope[j] * m_HCE_FieldFrac[j]; + 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 * m_EqOpteffs[i]; // [W] absorbed by absorber + 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 @@ -578,7 +583,25 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle //Recalculate the average temperature for the SCA double T_htf_ave = (m_T_htf_in_t_int[i] + T_htf_out) / 2.0; } + + + // DEBUG + + //double new_end = 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)); + + //double new_int = 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; + + /*double old = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + + 2.0 * (m_TCS_T_htf_ave_converged[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (2.0 * m_dot_htf_loop * c_htf_i)) * + exp(-2. * m_dot_htf_loop * c_htf_i * dt / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod));*/ + + break; } @@ -628,15 +651,18 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle } //Calculate the mass of HTF associated with this node - m_node = rho_htf_i * m_A_cs.at(0) * m_L_mod; + m_node = rho_htf_i * m_A_cs[0] * m_L_mod; //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); - m_E_accum[i] = x1 * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]); - m_E_int_loop[i] = x1 * (m_T_htf_out_t_end[i] - 298.15); //mjw 1.18.2011 energy relative to ambient + //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] } @@ -665,7 +691,12 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle m_T_htf_in_t_int[i + 1] = m_T_htf_out_t_int[i] - m_Pipe_hl_coef * m_D_abs_out[0] * pi * L_int * (m_T_htf_out_t_int[i] - T_db) / (m_dot_htf_loop * c_htf_i); //Add the internal energy of the crossover piping - 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); + //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. * 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] @@ -677,7 +708,8 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle } - T_loop_outX = m_T_htf_out_t_int[m_nMod - 1]; + //Set the loop outlet temperature + double T_loop_outX = m_T_htf_out_t_end[m_nMod - 1]; // DEBUGGING if (true) @@ -722,6 +754,8 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle double E_HR_hot_losses = 0.0; //[MJ] double E_HR_hot_bal = 0.0; //[MJ] + + //Calculation for heat losses from hot piping //Header { @@ -736,8 +770,8 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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 * (m_T_hdr[i] - T_db); - m_Header_hl_hot_tot += m_nfsec * m_Header_hl_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++; } @@ -753,13 +787,15 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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[i] * CSP::pi * m_D_runner[D_index] * m_Pipe_hl_coef * (m_T_rnr[i] - T_db); //Wt - m_Runner_hl_hot_tot += 2. * m_Runner_hl_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++; } - 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); + } - + + 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] @@ -888,9 +924,9 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n 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); + //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; @@ -908,13 +944,11 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n //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 * pi))); - D_runner.at(2 * nrunsec - 1) = CSP::pipe_sched(sqrt(4. * m_dot_ts / (rho * V_max * 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 * pi))); - D_runner[2 * nrunsec - i - 1] = CSP::pipe_sched(sqrt(4. * m_dot_temp / (rho * V_max * pi))); m_dot_temp = max(m_dot_temp - m_dot_hdr * 2, 0.0); } } @@ -1023,7 +1057,7 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() m_SCA_drives_elec = std::numeric_limits::quiet_NaN(); m_fthrok = -1; m_fthrctrl = -1; - m_ColTilt = std::numeric_limits::quiet_NaN(); + //m_ColTilt = std::numeric_limits::quiet_NaN(); m_ColAz = std::numeric_limits::quiet_NaN(); //m_wind_stow_speed = std::numeric_limits::quiet_NaN(); @@ -1622,10 +1656,10 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() 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(2 * m_nrunsec); - m_L_runner.resize(2 * m_nrunsec); - m_D_hdr.resize(2 * m_nhdrsec); - m_L_hdr.resize(2 * m_nhdrsec); + m_D_runner.resize(m_nrunsec); + m_L_runner.resize(m_nrunsec); + m_D_hdr.resize(m_nhdrsec); + //m_L_hdr.resize(2 * m_nhdrsec); //m_P_rnr.resize(2 * m_nrunsec); //m_P_hdr.resize(2 * m_nhdrsec); //m_P_loop.resize(2 * m_nMod + 3); @@ -1662,7 +1696,6 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() x1 = 1.; //the first runners are short } m_L_runner[0] = m_L_rnr_pb; - m_L_runner[2 * m_nrunsec - 1] = m_L_rnr_pb; // assume symmetric runners (TB) 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.); @@ -1998,6 +2031,10 @@ void C_csp_fresnel_collector_receiver::startup(const C_csp_weatherreader::S_outp C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, const C_csp_solver_sim_info& sim_info) { + if (sim_info.ms_ts.m_time / 3600 == 11) + int x = 0; + + // Always reset last temps reset_last_temps(); @@ -2671,7 +2708,7 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade double hour = fmod(time_hr, 24.); //[hr] // DEBUG - if (sim_info.ms_ts.m_time == 302400) + if (time_hr == 9) int x = 0; //Time calculations @@ -2704,7 +2741,7 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade double HrB = hour; double MidTrack; - double m_ftrack = std::numeric_limits::quiet_NaN(); + m_ftrack = std::numeric_limits::quiet_NaN(); // Solar field operates if ((HrB > DepTime) && (HrA < StwTime)) { diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 79dbc0eda..fc15c4f5c 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -99,9 +99,10 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver E_DP_TOT, E_T_SYS_C, E_T_SYS_H, - E_T_LOOP_OUTLET + E_T_LOOP_OUTLET, + E_Q_I }; enum struct E_loop_energy_balance_exit @@ -132,7 +133,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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_L_hdr; //[m] Lengths of header sections + //std::vector m_L_hdr; //[m] Lengths of 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 @@ -344,7 +345,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver C_csp_collector_receiver::E_csp_cr_modes m_operating_mode_converged; C_csp_collector_receiver::E_csp_cr_modes m_operating_mode; - const double fp_offset = 0; // freeze protection offset + const double fp_offset = 10; // freeze protection offset // *********************** // ***** T E M P ****** @@ -377,10 +378,10 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double eta_opt_fixed; 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; emit_table m_epsilon_abs; - double T_loop_outX; + std::vector mv_HCEguessargs; @@ -441,7 +442,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver int m_fthrok; // [-] Flag to allow partial defocusing of the collectors int m_fthrctrl; // [-] Defocusing strategy double m_ColAz; // [deg] Collector azimuth angle - double m_ColTilt; // [deg] Collector tilt angle (0 is horizontal, 90deg is vertical) + //double m_ColTilt; // [deg] Collector tilt angle (0 is horizontal, 90deg is vertical) double m_solar_mult; // [-] Solar multiple double m_mc_bal_hot; // [J/K] The heat capacity of the balance of plant on the hot side @@ -601,7 +602,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // From sam_mw_lf_type262_salt double Pump_SGS(double rho, double m_dotsf, double sm); - + void EvacReceiver(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, int ncall, double time, //outputs From e1b04fb00c4fd0a6dfa8893897afd3dba3c71443 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Wed, 1 Mar 2023 15:08:21 -0700 Subject: [PATCH 07/46] Reorganize Evacuated Receiver Code into dedicated class --- tcs/csp_solver_fresnel_collector_receiver.cpp | 2567 ++++++++++++----- tcs/csp_solver_fresnel_collector_receiver.h | 165 +- 2 files changed, 1928 insertions(+), 804 deletions(-) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index ff29e8092..acc3afd51 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -524,10 +524,21 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle double c_htf_j, rho_htf_j; - EvacReceiver(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, m_ncall, sim_info.ms_ts.m_time / 3600.0, + //EvacReceiver(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, m_ncall, sim_info.ms_ts.m_time / 3600.0, + // //outputs + // m_q_loss[j], m_q_abs[j], m_q_1abs[j], c_htf_j, rho_htf_j, mv_HCEguessargs); + + + 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, m_ncall, 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); + + + + + // Check for NaN if (m_q_abs[j] != m_q_abs[j]) { @@ -555,7 +566,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle c_htf_i *= 1000.0; //[J/kg-K] //Calculate the mass of HTF associated with this node - double m_node = rho_htf_i * m_A_cs[0] * m_L_mod; + 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: @@ -584,6 +595,8 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle double T_htf_ave = (m_T_htf_in_t_int[i] + T_htf_out) / 2.0; } + //Calculate the mass of HTF associated with this node + m_node = rho_htf_i * m_A_cs[0] * m_L_mod; // DEBUG @@ -645,13 +658,14 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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 mass of HTF associated with this node - m_node = rho_htf_i * m_A_cs[0] * m_L_mod; + //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 @@ -669,10 +683,10 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle // Now calculate an energy balance using the timestep-average Bulk Temperature // ** THIS IS JUST A TEST: can comment out if necessary ** - E_sca[i] = (m_A_cs[0] * m_L_mod * rho_htf_i * c_htf_i + m_L_mod * m_mc_bal_sca) * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]) * 1.E-6; //[MJ] SCA basis - E_sca_htf[i] = m_dot_htf_loop * c_htf_i * (m_T_htf_out_t_int[i] - m_T_htf_in_t_int[i]) * sim_info.ms_ts.m_step / 1.E6; - E_sca_abs[i] = m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step / 1.E6; - E_sca_bal[i] = E_sca_abs[i] - E_sca_htf[i] - E_sca[i]; + //E_sca[i] = (m_A_cs[0] * m_L_mod * rho_htf_i * c_htf_i + m_L_mod * m_mc_bal_sca) * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]) * 1.E-6; //[MJ] SCA basis + //E_sca_htf[i] = m_dot_htf_loop * c_htf_i * (m_T_htf_out_t_int[i] - m_T_htf_in_t_int[i]) * sim_info.ms_ts.m_step / 1.E6; + //E_sca_abs[i] = m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step / 1.E6; + //E_sca_bal[i] = E_sca_abs[i] - E_sca_htf[i] - E_sca[i]; //Set the inlet temperature of the next SCA equal to the outlet temperature of the current SCA //minus the heat losses in intermediate piping @@ -1485,6 +1499,14 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs // 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)); + } + return; } @@ -2883,6 +2905,8 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta_off() m_q_dot_inc_sf_tot = 0.0; //[MWt] + m_eta_optical = 0; + return; } @@ -3043,228 +3067,1749 @@ double C_csp_fresnel_collector_receiver::Pump_SGS(double rho, double m_dotsf, do } -/* - 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 - ---------------------------------------------------------------------------------------------------------------------- - */ -void C_csp_fresnel_collector_receiver::EvacReceiver(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, int ncall, double time, - //outputs - double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args) -{ +///* +// 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 +// ---------------------------------------------------------------------------------------------------------------------- +// */ +//void C_csp_fresnel_collector_receiver::EvacReceiver(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, int ncall, double time, +// //outputs +// double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args) +//{ +// +// //cc -- note that collector/hce geometry is part of the parent class. Only the indices specifying the +// // number of the HCE and collector need to be passed here. +// +// //---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 = m_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] +// } +// 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] +// +// } +// +// 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(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(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(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(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(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(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(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(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 C_csp_fresnel_collector_receiver::fT_2(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 +// // Input units ( K , K , real, m/s, - , -) +// +// 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 C_csp_fresnel_collector_receiver::FQ_34CONV(double T_3, double T_4, double P_6, double v_6, double T_6, int hv, double& q_34conv, double& h_34) +//{ +// // UNITS ( K , K , Pa , m/s, K , -, -, W/m, W/m2-K) +// +// 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 C_csp_fresnel_collector_receiver::FQ_34RAD(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34) { +// //units (K, K, K, -, -, -, W/m, W/m2-K) +// 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 C_csp_fresnel_collector_receiver::FQ_56CONV(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6) +//// units ( K , K , torr, m/s, W/m , W/m2-K) +//{ +// 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; +// } +// +// 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 C_csp_fresnel_collector_receiver::FQ_COND_BRACKET(double T_3, double T_6, double P_6, double v_6) { +// // units ( K , K , bar, m/s) +// +// 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 C_csp_fresnel_collector_receiver::FK_23(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); +// +//} - //cc -- note that collector/hce geometry is part of the parent class. Only the indices specifying the - // number of the HCE and collector need to be passed here. +/* + *************************************************************************************************** + Trough system piping loss model + *************************************************************************************************** - //---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; + This piping loss model is derived from the pressure drop calculations presented in the + following document: - bool UPFLAG, LOWFLAG, T3upflag, T3lowflag, is_e_table; - int qq, q5_iter, T1_iter, q_conv_iter; + Parabolic Trough Solar System Piping Model - double T_save_tot, colopteff_tot; - //cc--> note that xx and yy have size 'nea' + B. Kelly + Nexant, Inc. San Francisco, California - //---Re-guess criteria:--- - if (time <= 2) goto lab_reguess; + D. Kearney + Kearney & Associates + Vashon, Washington - if (((int)v_reguess_args[0] == 1) != m_GlazingIntact.at(hv)) goto lab_reguess; //m_GlazingIntact state has changed + Subcontract Report + NREL/SR-550-40165 + July 2006 - if (m_P_a[hv] != v_reguess_args[1]) goto lab_reguess; //Reguess for different annulus pressure + ---------------------------- + 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. - if (std::abs(v_reguess_args[2] - T_1_in) > 50.) goto lab_reguess; + ---------------------------- + 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) { - for (int i = 0; i < 5; i++) { if (T_save[i] < T_sky - 1.) goto lab_reguess; } + 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; - 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 + //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 / (pi * (D / 2.) * (D / 2.)); //Fluid mean velocity - reguess = false; - goto lab_keep_guess; -lab_reguess: - reguess = true; -lab_keep_guess: + //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 * g); + DP_pipe = HL_pm * rho * g * 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 * g * Nels; + DP_elm = 0.75 * D / f * HL_pm * rho * g * Nelm; + DP_ell = 0.6 * D / f * HL_pm * rho * g * Nell; + DP_gav = 0.19 * D / f * HL_pm * rho * g * Ngav; + DP_glv = 10.0 * D / f * HL_pm * rho * g * Nglv; + DP_chv = 2.5 * D / f * HL_pm * rho * g * Nchv; + DP_lw = 1.8 * D / f * HL_pm * rho * g * Nlw; + DP_lcv = 10.0 * D / f * HL_pm * rho * g * Nlcv; + DP_bja = 8.69 * D / f * HL_pm * rho * g * Nbja; - 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.; + 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; - 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; +/************************************************************************************************** + 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) { - 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 + double Test, TestOld, X, Xold, Slope; + double Acc = .01; //0.0001 + int NumTries; - } + if (Reynold < 2750.) { + return 64. / max(Reynold, 1.0); } - //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 + 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; - //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; + 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); + } - //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 + Slope = (Test - TestOld) / (X - Xold); + Xold = X; + TestOld = Test; + X = max((Slope * X - Test) / Slope, 1.e-5); } - Diff_T3 = 10.0 + T3_tol; //Set difference > tolerance + //call Messages(-1," Could not find friction factor solution",'Warning',0,250) + return 0; +} - //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 = m_ColOptEff.at(sca_num) * m_dirt_env[hv] * m_Shadowing[hv]; //The total optical efficiency +// ******************************************************************** STATIC Methods - 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] - } - 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] +// 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"); } - is_e_table = false; - if (m_epsilon_abs.getTableSize(hv) < 2) { - eps_3 = m_epsilon_abs.getSingleValue(hv); + 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 { - 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 + irnr_onedir = irnr; } - T3upflag = false; - T3lowflag = false; + m_dot_rnr_0 = m_dot_field / 2.; + m_dot_rnr_1 = m_dot_rnr_0 * (1. - float(nfieldsec % 4) / float(nfieldsec)); - double T3_adjust = 0.0; - double T3_prev_qq = 0.0; + 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; +} + + +// ******************************************************************** 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_trough->apply_component_defocus(defocus); + + // Solve the loop energy balance at the input mass flow rate + E_loop_energy_balance_exit exit_code = mpc_trough->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_trough->m_T_htf_out_t_end[mpc_trough->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_trough->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_trough->m_T_htf_out_t_end[mpc_trough->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_trough->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_trough->m_m_dot_htf_tot * mpc_trough->m_c_htf_ave_ts_ave_temp * + (T_htf_cold_in - mpc_trough->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_trough->m_Q_field_losses_total_subts) / mpc_trough->m_Q_field_losses_total_subts; //[-] + + return 0; +} + + + + +// ******************************************************************** Evacuated Receiver Model + + +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) +{ +} + +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, int ncall, 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) +{ + + //---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] + } + 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] + + } + + 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 @@ -3372,11 +4917,11 @@ void C_csp_fresnel_collector_receiver::EvacReceiver(double T_1_in, double m_dot, //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(T_3, T_4, P_6, v_6, T_6, hv, q_34conv, h_34conv); + 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(T_3, T_4, T_7, eps_3, hv, q_34rad, h_34rad); + 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] @@ -3395,7 +4940,7 @@ void C_csp_fresnel_collector_receiver::EvacReceiver(double T_1_in, double m_dot, //************************************************************************* //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(T_5, T_6, P_6, v_6, hv, q_56conv, h_56conv); //[W/m] + 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] @@ -3469,9 +5014,9 @@ void C_csp_fresnel_collector_receiver::EvacReceiver(double T_1_in, double m_dot, //----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(T_3, T_4, P_6, v_6, T_6, hv, q_34conv, h_34conv); + 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(T_3, T_4, T_7, eps_3, hv, q_34rad, h_34rad); + 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] @@ -3479,7 +5024,7 @@ void C_csp_fresnel_collector_receiver::EvacReceiver(double T_1_in, double m_dot, //Bracket Losses //Bracket conduction losses apply - q_cond_bracket = FQ_COND_BRACKET(T_3, T_6, P_6, v_6); //[W/m] + 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 @@ -3528,7 +5073,7 @@ void C_csp_fresnel_collector_receiver::EvacReceiver(double T_1_in, double m_dot, q_conv_iter++; //Increase iteration counter - T_2 = fT_2(q_12conv, T_1_ave, T_2g, v_1, hv); //Calculate T_2 (with previous T_2 as input) + 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 @@ -3567,7 +5112,7 @@ void C_csp_fresnel_collector_receiver::EvacReceiver(double T_1_in, double m_dot, q_23cond = q_12conv; //[W/m] //Calculate tube conductivity - k_23 = FK_23(T_2, T_3, hv); //[W/m-K] + 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)); @@ -3610,115 +5155,38 @@ void C_csp_fresnel_collector_receiver::EvacReceiver(double T_1_in, double m_dot, }; -/* - ################################################################################################################# - ################################################################################################################# - ################################################################################################################# - +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 - "****************************************************************************************************************************** - 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. + // 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; - Newton's Law of Cooling. + 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 - q' = h * D_i * PI * (T_m - T_s) + T_2g = max(T_2g, m_T_htf_prop_min); - h = Nu_Di * k / D_i + // 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] - Where + Pr_2 = (Cp_2 * mu_2) / k_2; + Pr_1 = (Cp_1 * mu_1) / k_1; - 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] + if (v_1 > 0.1) { - 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 C_csp_fresnel_collector_receiver::fT_2(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 - // Input units ( K , K , real, m/s, - , -) - - 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_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); + 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.)) { @@ -3763,148 +5231,18 @@ double C_csp_fresnel_collector_receiver::fT_2(double q_12conv, double T_1, doubl }; -/****************************************************************************************************************************** - 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 +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; - 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 C_csp_fresnel_collector_receiver::FQ_34CONV(double T_3, double T_4, double P_6, double v_6, double T_6, int hv, double& q_34conv, double& h_34) { - // UNITS ( K , K , Pa , m/s, K , -, -, W/m, W/m2-K) 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, @@ -4056,43 +5394,17 @@ void C_csp_fresnel_collector_receiver::FQ_34CONV(double T_3, double T_4, double } } -}; - -/****************************************************************************************************************************** - 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. +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; - q' = sigma * PI * D_1 * EPSILON_1 * (T_1^4 - T_2^4) -}*/ -void C_csp_fresnel_collector_receiver::FQ_34RAD(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34) { - //units (K, K, K, -, -, -, W/m, W/m2-K) double sigma = 5.67e-8, T_ave; T_ave = (T_3 + T_4) / 2.; if (!m_GlazingIntact.at(hv)) { @@ -4106,21 +5418,15 @@ void C_csp_fresnel_collector_receiver::FQ_34RAD(double T_3, double T_4, double T } -/****************************************************************************************************************************** - 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 C_csp_fresnel_collector_receiver::FQ_56CONV(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6) -// units ( K , K , torr, m/s, W/m , W/m2-K) +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; @@ -4218,16 +5524,11 @@ void C_csp_fresnel_collector_receiver::FQ_56CONV(double T_5, double T_6, double } } -/****************************************************************************************************************************** - 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 C_csp_fresnel_collector_receiver::FQ_COND_BRACKET(double T_3, double T_6, double P_6, double v_6) { - // units ( K , K , bar, m/s) +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, @@ -4346,12 +5647,7 @@ double C_csp_fresnel_collector_receiver::FQ_COND_BRACKET(double T_3, double T_6, } -/****************************************************************************************************************************** - FUNCTION fk_23: Absorber conductance -******************************************************************************************************************************" -{ Based on linear fit of data from "Alloy Digest, Sourcebook, Stainless Steels"; ASM International, 2000.} -*/ -double C_csp_fresnel_collector_receiver::FK_23(double T_2, double T_3, int hv) +double EvacReceiverModel::FK_23_v2(double T_2, double T_3, int hv) { double T_23; @@ -4366,263 +5662,12 @@ double C_csp_fresnel_collector_receiver::FK_23(double T_2, double T_3, int hv) } -/* - *************************************************************************************************** - 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 / (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 * g); - DP_pipe = HL_pm * rho * g * 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 * g * Nels; - DP_elm = 0.75 * D / f * HL_pm * rho * g * Nelm; - DP_ell = 0.6 * D / f * HL_pm * rho * g * Nell; - DP_gav = 0.19 * D / f * HL_pm * rho * g * Ngav; - DP_glv = 10.0 * D / f * HL_pm * rho * g * Nglv; - DP_chv = 2.5 * D / f * HL_pm * rho * g * Nchv; - DP_lw = 1.8 * D / f * HL_pm * rho * g * Nlw; - DP_lcv = 10.0 * D / f * HL_pm * rho * g * Nlcv; - DP_bja = 8.69 * D / f * HL_pm * rho * g * 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; -} - - -// ******************************************************************** 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; -} - - -// ******************************************************************** 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_trough->apply_component_defocus(defocus); - - // Solve the loop energy balance at the input mass flow rate - E_loop_energy_balance_exit exit_code = mpc_trough->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_trough->m_T_htf_out_t_end[mpc_trough->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_trough->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_trough->m_T_htf_out_t_end[mpc_trough->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_trough->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_trough->m_m_dot_htf_tot * mpc_trough->m_c_htf_ave_ts_ave_temp * - (T_htf_cold_in - mpc_trough->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_trough->m_Q_field_losses_total_subts) / mpc_trough->m_Q_field_losses_total_subts; //[-] - - return 0; -} diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index fc15c4f5c..7a9166c2a 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -37,6 +37,78 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #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 = 3.14159; + 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; + + HTFProperties m_htfProps, m_airProps; + 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, int ncall, 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); + +}; + class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver { @@ -116,36 +188,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // Fields in Trough - 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_L_hdr; //[m] Lengths of 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_DP_loop; //[bar] Pressure drop in loop sections - std::vector m_T_loop_dsn; //[C] Temperature entering loop sections at design - std::vector m_P_loop_dsn; //[bar] Gauge pessure in loop sections at design - std::vector m_T_loop; //[K] Temperature entering loop sections - //std::vector m_P_loop; //[Pa] Gauge pessure in loop sections + OpticalDataTable optical_table; @@ -372,8 +415,6 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver const double g = 9.81; //gravitation constant const double mtoinch = 39.3700787; //[m] -> [in] - double m_htf_prop_min; - double m_eta_optical; //Collector total optical efficiency double eta_opt_fixed; double m_phi_t = 0; //Solar incidence angle in the collector transversal plane @@ -389,6 +430,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double m_sf_def; + std::unique_ptr m_evac_receiver; + // Private Methods private: @@ -495,6 +538,37 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver vector m_dirt_env; // Loss due to dirt on the receiver envelope (m_Dirt_HCE) vector m_Design_loss; // [-] Receiver heat loss at design + 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_L_hdr; //[m] Lengths of 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_DP_loop; //[bar] Pressure drop in loop sections + std::vector m_T_loop_dsn; //[C] Temperature entering loop sections at design + std::vector m_P_loop_dsn; //[bar] Gauge pessure in loop sections at design + std::vector m_T_loop; //[K] Temperature entering loop sections + //std::vector m_P_loop; //[Pa] Gauge pessure in loop sections + // Fresnel Only Inputs double m_L_mod_spacing; // Piping distance between sequential modules in a loop double m_L_crossover; // Length of crossover piping in a loop @@ -510,6 +584,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver C_csp_reported_outputs mc_reported_outputs; + + // Methods public: @@ -603,23 +679,22 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double Pump_SGS(double rho, double m_dotsf, double sm); - void EvacReceiver(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, int ncall, double time, - //outputs - double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args); - - double fT_2(double q_12conv, double T_1, double T_2g, double v_1, int hv); + //void EvacReceiver(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, int ncall, double time, + // //outputs + // double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args); - void FQ_34CONV(double T_3, double T_4, double P_6, double v_6, double T_6, int hv, double& q_34conv, double& h_34); + //double fT_2(double q_12conv, double T_1, double T_2g, double v_1, int hv); - void FQ_34RAD(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34); + //void FQ_34CONV(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_56CONV(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6); + //void FQ_34RAD(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34); - double FQ_COND_BRACKET(double T_3, double T_6, double P_6, double v_6); + //void FQ_56CONV(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6); - double FK_23(double T_2, double T_3, int hv); + //double FQ_COND_BRACKET(double T_3, double T_6, double P_6, double v_6); + //double FK_23(double T_2, double T_3, int hv); 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, @@ -710,6 +785,10 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver virtual int operator()(double T_htf_cold_in /*K*/, double* E_loss_balance /*-*/); }; + + }; + + #endif From 7a8df792c920ba03ceab26fe29fb0c9b2731f181 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Wed, 1 Mar 2023 16:40:04 -0700 Subject: [PATCH 08/46] Remove defocus control options --- ssc/cmod_fresnel_physical.cpp | 316 ++- tcs/csp_solver_fresnel_collector_receiver.cpp | 1927 +++++------------ tcs/csp_solver_fresnel_collector_receiver.h | 8 +- 3 files changed, 707 insertions(+), 1544 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 7207d54e1..eb88df223 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -69,16 +69,16 @@ static var_info _cm_vtab_fresnel_physical[] = { /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "controller", "*", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "tou", "?=0.99", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "tou", "?=0.99", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, //{ 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", "", "" }, @@ -138,8 +138,8 @@ static var_info _cm_vtab_fresnel_physical[] = { /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "fthrok", "Flag to allow partial defocusing of the collectors", "", "", "controller", "*", "INTEGER", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "fthrctrl", "Defocusing strategy", "", "", "controller", "*", "INTEGER", ""}, + // /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "fthrok", "Flag to allow partial defocusing of the collectors", "", "", "controller", "*", "INTEGER", ""}, + // /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "fthrctrl", "Defocusing strategy", "", "", "controller", "*", "INTEGER", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_hot", "The heat capacity of the balance of plant on the hot side", "kWht/K-MWt", "", "controller", "*", "", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_cold", "The heat capacity of the balance of plant on the cold side", "kWht/K-MWt", "", "controller", "*", "", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "controller", "*", "", ""}, @@ -517,8 +517,8 @@ static var_info _cm_vtab_fresnel_physical[] = { { SSC_OUTPUT, SSC_ARRAY, "gen", "Total electric power to grid w/ avail. derate", "kWe", "", "system", "*", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "annual_W_cycle_gross", "Electrical source - Power cycle gross output", "kWhe", "", "system", "*", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "conversion_factor", "Gross to Net Conversion Factor", "%", "", "system", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "system", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "system", "*", "", "" }, + //{ SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "system", "*", "", "" }, + //{ SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "system", "*", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "sim_duration", "Computational time of timeseries simulation", "s", "", "system", "*", "", "" }, { SSC_OUTPUT, SSC_NUMBER, "solar_multiple_actual", "Actual solar multiple of system", "-", "", "system", "*", "", "" }, //{ SSC_OUTPUT, SSC_NUMBER, "W_dot_par_tot_haf", "Adjusted parasitic power", "kWe", "", "system", "*", "", "" }, @@ -629,8 +629,8 @@ class cm_fresnel_physical : public compute_module 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_fthrok = as_integer("fthrok"); - c_fresnel.m_fthrctrl = as_integer("fthrctrl"); + //c_fresnel.m_fthrok = as_integer("fthrok"); + //c_fresnel.m_fthrctrl = as_integer("fthrctrl"); c_fresnel.m_ColAz = as_number("ColAz"); //c_fresnel.m_ColTilt = as_number("tilt"); @@ -1562,93 +1562,217 @@ class cm_fresnel_physical : public compute_module assign("sim_duration", (ssc_number_t)sim_duration); assign("solar_multiple_actual", as_double("solar_mult")); // calculated during verify() using cmod_csp_trough_eqns.cpp - // Convert Units + // 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) { - // 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] - } + 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 - } + // 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("trough_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("trough_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("trough_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("trough_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); + 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("trough_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("trough_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("trough_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("trough_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 (as_boolean("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); } diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index acc3afd51..2fffd5e3d 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -1069,8 +1069,8 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() 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_fthrok = -1; - m_fthrctrl = -1; + //m_fthrok = -1; + //m_fthrctrl = -1; //m_ColTilt = std::numeric_limits::quiet_NaN(); m_ColAz = std::numeric_limits::quiet_NaN(); //m_wind_stow_speed = std::numeric_limits::quiet_NaN(); @@ -1503,7 +1503,7 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs 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_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)); } @@ -2955,1441 +2955,127 @@ void C_csp_fresnel_collector_receiver::apply_control_defocus(double defocus) // Store control defocus m_control_defocus = defocus; - if (m_fthrctrl == 0) - { - mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." - " The model will instead use Simultaneous Partial Defocusing"); - m_fthrctrl = 2; - } - if (m_fthrctrl == 1) - { - mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, partially defocusing SCAs is not available." - " The model will instead use Simultaneous Partial Defocusing"); - m_fthrctrl = 2; - } - if (m_fthrctrl == 2) - { - 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; - - if (m_fthrctrl == 0) - { - mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." - " The model will instead use Simultaneous Partial Defocusing"); - m_fthrctrl = 2; - } - if (m_fthrctrl == 1) - { - mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, partially defocusing SCAs is not available." - " The model will instead use Simultaneous Partial Defocusing"); - m_fthrctrl = 2; - } - if (m_fthrctrl == 2) - { - for (int i = 0; i < m_nMod; i++) - { - //int CT = (int)m_SCAInfoArray(i, 1) - 1; // Collector type - m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; - } - } -} - -//---------------------------------------------------------------------- PUBLIC SUPPLEMENTAL (from sam_mw_lf_type262_salt) - -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 * pi))); - //Calculate the total volume - V[i] = pow(D[i], 2) / 4. * pi * L_line[i]; - psum += V[i]; - } - - delete[] V_dot; - delete[] D; - delete[] V; - - return psum; - -} - -///* -// 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 -// ---------------------------------------------------------------------------------------------------------------------- -// */ -//void C_csp_fresnel_collector_receiver::EvacReceiver(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, int ncall, double time, -// //outputs -// double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args) -//{ -// -// //cc -- note that collector/hce geometry is part of the parent class. Only the indices specifying the -// // number of the HCE and collector need to be passed here. -// -// //---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 = m_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] -// } -// 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] -// -// } -// -// 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(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(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(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(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(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(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(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(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 C_csp_fresnel_collector_receiver::fT_2(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 -// // Input units ( K , K , real, m/s, - , -) -// -// 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 C_csp_fresnel_collector_receiver::FQ_34CONV(double T_3, double T_4, double P_6, double v_6, double T_6, int hv, double& q_34conv, double& h_34) -//{ -// // UNITS ( K , K , Pa , m/s, K , -, -, W/m, W/m2-K) -// -// 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 C_csp_fresnel_collector_receiver::FQ_34RAD(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34) { -// //units (K, K, K, -, -, -, W/m, W/m2-K) -// 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 C_csp_fresnel_collector_receiver::FQ_56CONV(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6) -//// units ( K , K , torr, m/s, W/m , W/m2-K) -//{ -// 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; -// } -// -// 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 C_csp_fresnel_collector_receiver::FQ_COND_BRACKET(double T_3, double T_6, double P_6, double v_6) { -// // units ( K , K , bar, m/s) -// -// 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 C_csp_fresnel_collector_receiver::FK_23(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); -// -//} + //if (m_fthrctrl == 0) + //{ + // mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." + // " The model will instead use Simultaneous Partial Defocusing"); + // m_fthrctrl = 2; + //} + //if (m_fthrctrl == 1) + //{ + // mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, partially defocusing SCAs is not available." + // " The model will instead use Simultaneous Partial Defocusing"); + // m_fthrctrl = 2; + //} + //if (m_fthrctrl == 2) + //{ + // for (int i = 0; i < m_nMod; i++) + // { + // m_q_SCA_control_df[i] = defocus * m_q_i; + // } + //} + + 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; + + //if (m_fthrctrl == 0) + //{ + // mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." + // " The model will instead use Simultaneous Partial Defocusing"); + // m_fthrctrl = 2; + //} + ////if (m_fthrctrl == 1) + //{ + // mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, partially defocusing SCAs is not available." + // " The model will instead use Simultaneous Partial Defocusing"); + // m_fthrctrl = 2; + //} + //if (m_fthrctrl == 2) + //{ + // for (int i = 0; i < m_nMod; i++) + // { + // //int CT = (int)m_SCAInfoArray(i, 1) - 1; // Collector type + // m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; + // } + //} + + for (int i = 0; i < m_nMod; i++) + { + //int CT = (int)m_SCAInfoArray(i, 1) - 1; // Collector type + m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; + } +} + +//---------------------------------------------------------------------- PUBLIC SUPPLEMENTAL (from sam_mw_lf_type262_salt) + +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 * pi))); + //Calculate the total volume + V[i] = pow(D[i], 2) / 4. * pi * L_line[i]; + psum += V[i]; + } + + delete[] V_dot; + delete[] D; + delete[] V; + + return psum; + +} /* *************************************************************************************************** @@ -4656,10 +3342,86 @@ int C_csp_fresnel_collector_receiver::C_mono_eq_freeze_prot_E_bal::operator()(do // ******************************************************************** 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 +// ---------------------------------------------------------------------------------------------------------------------- +// */ 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, + 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), @@ -4797,11 +3559,11 @@ void EvacReceiverModel::Calculate_Energy_Balance(double T_1_in, double m_dot, do } is_e_table = false; - if (m_epsilon_abs.getTableSize(hv) < 2) { - eps_3 = m_epsilon_abs.getSingleValue(hv); + 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. + 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 } @@ -4858,7 +3620,7 @@ void EvacReceiverModel::Calculate_Energy_Balance(double T_1_in, double m_dot, do 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) + 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 @@ -5155,6 +3917,90 @@ void EvacReceiverModel::Calculate_Energy_Balance(double T_1_in, double m_dot, do }; +// ################################################################################################################# +// ################################################################################################################# +// ################################################################################################################# +// +// +// "****************************************************************************************************************************** +// 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 @@ -5231,6 +4077,145 @@ double EvacReceiverModel::fT_2_v2(double q_12conv, double T_1, double T_2g, doub }; +// 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 @@ -5396,6 +4381,38 @@ void EvacReceiverModel::FQ_34CONV_v2(double T_3, double T_4, double P_6, double } +// 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 @@ -5418,6 +4435,17 @@ void EvacReceiverModel::FQ_34RAD_v2(double T_3, double T_4, double T_7, double e } +// 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 @@ -5524,6 +4552,13 @@ void EvacReceiverModel::FQ_56CONV_v2(double T_5, double T_6, double P_6, double } } +// 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 @@ -5647,6 +4682,10 @@ double EvacReceiverModel::FQ_COND_BRACKET_v2(double T_3, double T_6, double P_6, } +// 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; diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 7a9166c2a..1b1d049c0 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -68,7 +68,7 @@ class EvacReceiverModel const vector m_Tau_envelope; // [-] Envelope transmittance const vector m_alpha_env; // [-] Envelope absorptance - emit_table m_epsilon_abs; + emit_table* m_epsilon_abs; HTFProperties m_htfProps, m_airProps; const util::matrix_t m_AnnulusGasMat; // HTF Property class for each variant of each receiver type @@ -98,7 +98,7 @@ class 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, + 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); @@ -482,8 +482,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 - int m_fthrok; // [-] Flag to allow partial defocusing of the collectors - int m_fthrctrl; // [-] Defocusing strategy + //int m_fthrok; // [-] Flag to allow partial defocusing of the collectors + //int m_fthrctrl; // [-] Defocusing strategy double m_ColAz; // [deg] Collector azimuth angle //double m_ColTilt; // [deg] Collector tilt angle (0 is horizontal, 90deg is vertical) From 6fd6ab16d4899ec06a3635903e1df06a8e66e80b Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Fri, 3 Mar 2023 13:40:47 -0700 Subject: [PATCH 09/46] Add check for pump coef value for system with different field and storage materials --- tcs/csp_solver_two_tank_tes.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tcs/csp_solver_two_tank_tes.cpp b/tcs/csp_solver_two_tank_tes.cpp index fc36c5bd5..e67c19420 100644 --- a/tcs/csp_solver_two_tank_tes.cpp +++ b/tcs/csp_solver_two_tank_tes.cpp @@ -874,6 +874,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 ) { From 029ac69cb062fe9728acdb29ac9dd7d9415149cb Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Tue, 7 Mar 2023 13:12:00 -0700 Subject: [PATCH 10/46] Make new fresnel functional with the UI, including calculated variables. Remove unnecessary outputs --- ssc/cmod_fresnel_physical.cpp | 1238 +++++++++++------ tcs/csp_solver_fresnel_collector_receiver.cpp | 249 ++-- tcs/csp_solver_fresnel_collector_receiver.h | 61 +- tcs/csp_solver_two_tank_tes.cpp | 24 + tcs/csp_solver_two_tank_tes.h | 10 + 5 files changed, 1010 insertions(+), 572 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index eb88df223..530938e4d 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -44,12 +44,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. static var_info _cm_vtab_fresnel_physical[] = { + { SSC_INPUT, SSC_NUMBER, "sim_type", "1 (default): timeseries, 2: design only", "", "", "System Control", "?=1", "", "SIMULATION_PARAMETER"}, + + { SSC_INPUT, SSC_NUMBER, "solar_mult_in", "Solar multiple Input", "", "", "System Control", "", "", ""}, + { SSC_INPUT, SSC_NUMBER, "total_Ap_in", "Field aperture Input", "m3", "", "System Control", "", "", ""}, + { SSC_INPUT, SSC_NUMBER, "solar_mult_or_Ap", "Design using specified solar mult or field aperture", "m3", "", "System Control", "", "", ""}, + // 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, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, - /*System Design*/{ SSC_INPUT, SSC_NUMBER, "nLoops", "Number of loops in the field", "", "", "controller", "*", "", ""}, /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "controller", "*", "", ""}, /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "controller", "*", "", ""}, /*System Design*/{ SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "controller", "*", "", ""}, @@ -57,7 +61,7 @@ static var_info _cm_vtab_fresnel_physical[] = { /*System Design*/{ SSC_INPUT, SSC_NUMBER, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "powerblock", "*", "", ""}, /*System Design*///{ SSC_INPUT, SSC_NUMBER, "q_pb_design", "Label", "", "", "controller", "*", "", "" }, /*System Design*/{ SSC_INPUT, SSC_NUMBER, "tshours", "Label", "", "", "controller", "*", "", "" }, - /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult", "Solar multiple", "", "", "controller", "*", "", ""}, + // System Control @@ -67,7 +71,7 @@ static var_info _cm_vtab_fresnel_physical[] = { /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Label", "", "", "controller", "*", "", ""}, /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "bop_array", "Label", "", "", "controller", "*", "", "" }, /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "controller", "*", "", "" }, - + { SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, @@ -138,8 +142,6 @@ static var_info _cm_vtab_fresnel_physical[] = { /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "controller", "*", "", ""}, - // /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "fthrok", "Flag to allow partial defocusing of the collectors", "", "", "controller", "*", "INTEGER", ""}, - // /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "fthrctrl", "Defocusing strategy", "", "", "controller", "*", "INTEGER", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_hot", "The heat capacity of the balance of plant on the hot side", "kWht/K-MWt", "", "controller", "*", "", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_cold", "The heat capacity of the balance of plant on the cold side", "kWht/K-MWt", "", "controller", "*", "", ""}, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "controller", "*", "", ""}, @@ -151,9 +153,11 @@ static var_info _cm_vtab_fresnel_physical[] = { /*Solar Field*/{ SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "controller", "*", "", "" }, /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "controller", "*", "", "" }, - // In old fresnel but not Trough - //{ SSC_INPUT, SSC_NUMBER, "pb_rated_cap", "Rated plant capacity", "MWe", "", "controller", "*", "", ""}, - + /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "land_mult", "Non-solar field land area multiplier", "-", "", "controller", "*", "", "" }, + + + + // Collector and Receiver /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "controller", "*", "", "" }, @@ -335,203 +339,254 @@ static var_info _cm_vtab_fresnel_physical[] = { // OUTPUTS + // Design Point Outputs + + // System Design + { SSC_OUTPUT, SSC_NUMBER, "solar_mult", "Actual solar multiple", "MWe", "", "System Design Calc", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "total_Ap", "Actual field aperture", "MWe", "", "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", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_cycle_des", "Cycle design HTF mass flow rate", "kg/s", "", "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", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_therm_eff", "Loop thermal efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_eff", "Total loop conversion efficiency", "", "", "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", "*", "", "" }, + + + // 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", "*", "", "" }, + + // 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", "*", "", "" }, // Simulation Kernel - { SSC_OUTPUT, SSC_ARRAY, "time_hr", "Time at end of timestep", "hr", "", "solver", "*", "", "" }, + { 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", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "hour_day", "Resource Hour of Day", "", "", "weather", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "solazi", "Resource Solar Azimuth", "deg", "", "weather", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "solzen", "Resource Solar Zenith", "deg", "", "weather", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "beam", "Resource Beam normal irradiance", "W/m2", "", "weather", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "tdry", "Resource Dry bulb temperature", "C", "", "weather", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "twet", "Resource Wet bulb temperature", "C", "", "weather", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "rh", "Resource Relative Humidity", "%", "", "weather", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "wspd", "Resource Wind Speed", "m/s", "", "weather", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pres", "Resource Pressure", "mbar", "", "weather", "*", "", "" }, + { 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", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "defocus", "Field optical focus fraction", "", "", "weather", "sim_type=1", "", "" }, // Solar Field (from Trough) - { SSC_OUTPUT, SSC_ARRAY, "Theta_ave", "Field collector solar incidence angle", "deg", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "CosTh_ave", "Field collector cosine efficiency", "", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "IAM_ave", "Field collector incidence angle modifier", "", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "RowShadow_ave", "Field collector row shadowing loss", "", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "EndLoss_ave", "Field collector optical end loss", "", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "dni_costh", "Field collector DNI-cosine product", "W/m2", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "EqOpteff", "Field optical efficiency before defocus", "", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "SCAs_def", "Field fraction of focused SCAs", "", "", "solar_field", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "Theta_ave", "Field collector solar incidence angle", "deg", "", "solar_field", "sim_type=1", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "CosTh_ave", "Field collector cosine efficiency", "", "", "solar_field", "*", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "IAM_ave", "Field collector incidence angle modifier", "", "", "solar_field", "*", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "RowShadow_ave", "Field collector row shadowing loss", "", "", "solar_field", "*", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "EndLoss_ave", "Field collector optical end loss", "", "", "solar_field", "*", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "dni_costh", "Field collector DNI-cosine product", "W/m2", "", "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, "qinc_costh", "Field thermal power incident after cosine", "MWt", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_inc", "Receiver thermal power incident", "MWt", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_thermal_loss", "Receiver thermal losses", "MWt", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_rec_abs", "Receiver thermal power absorbed", "MWt", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_piping_loss", "Field piping thermal losses", "MWt", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "e_dot_field_int_energy", "Field change in material/htf internal energy", "MWt", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_htf_sf_out", "Field thermal power leaving in HTF", "MWt", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_freeze_prot", "Field freeze protection required", "MWt", "", "solar_field", "*", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "qinc_costh", "Field thermal power incident after cosine", "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, "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", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_recirc", "Field total mass flow recirculated", "kg/s", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_delivered", "Field total mass flow delivered", "kg/s", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_field_cold_in", "Field timestep-averaged inlet temperature", "C", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_rec_cold_in", "Loop timestep-averaged inlet temperature", "C", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_rec_hot_out", "Loop timestep-averaged outlet temperature", "C", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_field_hot_out", "Field timestep-averaged outlet temperature", "C", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "deltaP_field", "Field pressure drop", "bar", "", "solar_field", "*", "", "" }, + { 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", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "W_dot_field_pump", "Field htf pumping power", "MWe", "", "solar_field", "*", "", "" }, + { 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", "", "" }, - // Solar Field (from fresnel) - { SSC_OUTPUT, SSC_ARRAY, "theta_L", "Field collector incidence angle - longitudinal", "deg", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "phi_t", "Field collector incidence angle - transversal", "deg", "", "solar_field", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "eta_optical", "Field collector optical efficiency", "", "", "solar_field", "*", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "EqOptEff", "Field collector and receiver optical efficiency", "", "", "solar_field", "*", "", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "sf_def", "Field collector focus fraction", "", "", "mslf", "*", "LENGTH=8760", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "q_inc_sf_tot", "Field thermal power incident", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_abs_tot", "Field thermal power absorbed", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dump", "Field thermal power dumped", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_loss_tot", "Field thermal power receiver loss", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "Pipe_hl", "Field thermal power header pipe losses", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_avail", "Field thermal power produced", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_loss_spec_tot", "Field thermal power avg. receiver loss", "W/m", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "eta_thermal", "Field thermal efficiency", "", "", "mslf", "*", "LENGTH=8760", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "E_bal_startup", "Field HTF energy inertial (consumed)", "MWht", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_avail", "Field HTF mass flow rate total", "kg/hr", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_htf2", "Field HTF mass flow rate loop", "kg/s", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "DP_tot", "Field HTF pressure drop total", "bar", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_sys_c", "Field HTF temperature cold header inlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_sys_h", "Field HTF temperature hot header outlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, - { SSC_OUTPUT, SSC_ARRAY, "t_loop_outlet", "Field HTF temperature loop outlet", "C", "", "mslf", "*", "LENGTH=8760", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "q_I", "Incident Radiation", "C", "", "mslf", "*", "LENGTH=8760", "" }, + //// Solar Field (from fresnel) + //{ SSC_OUTPUT, SSC_ARRAY, "theta_L", "Field collector incidence angle - longitudinal", "deg", "", "solar_field", "*", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "phi_t", "Field collector incidence angle - transversal", "deg", "", "solar_field", "*", "", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "eta_optical", "Field collector optical efficiency", "", "", "solar_field", "*", "", "" }, + ////{ SSC_OUTPUT, SSC_ARRAY, "EqOptEff", "Field collector and receiver optical efficiency", "", "", "solar_field", "*", "", "" }, + + //{ SSC_OUTPUT, SSC_ARRAY, "sf_def", "Field collector focus fraction", "", "", "mslf", "*", "LENGTH=8760", "" }, + + //{ SSC_OUTPUT, SSC_ARRAY, "q_inc_sf_tot", "Field thermal power incident", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "q_abs_tot", "Field thermal power absorbed", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "q_dump", "Field thermal power dumped", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "q_loss_tot", "Field thermal power receiver loss", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "Pipe_hl", "Field thermal power header pipe losses", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "q_avail", "Field thermal power produced", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "q_loss_spec_tot", "Field thermal power avg. receiver loss", "W/m", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "eta_thermal", "Field thermal efficiency", "", "", "mslf", "*", "LENGTH=8760", "" }, + + //{ SSC_OUTPUT, SSC_ARRAY, "E_bal_startup", "Field HTF energy inertial (consumed)", "MWht", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "m_dot_avail", "Field HTF mass flow rate total", "kg/hr", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "m_dot_htf2", "Field HTF mass flow rate loop", "kg/s", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "DP_tot", "Field HTF pressure drop total", "bar", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "T_sys_c", "Field HTF temperature cold header inlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "T_sys_h", "Field HTF temperature hot header outlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, + //{ SSC_OUTPUT, SSC_ARRAY, "t_loop_outlet", "Field HTF temperature loop outlet", "C", "", "mslf", "*", "LENGTH=8760", "" }, + + //{ SSC_OUTPUT, SSC_ARRAY, "q_I", "Incident Radiation", "C", "", "mslf", "*", "LENGTH=8760", "" }, // power block - { SSC_OUTPUT, SSC_ARRAY, "eta", "PC efficiency: gross", "", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_pb", "PC input energy", "MWt", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_pc", "PC HTF mass flow rate", "kg/s", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_startup", "PC startup thermal power", "MWt", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "P_cycle", "PC electrical power output: gross", "MWe", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_pc_in", "PC HTF inlet temperature", "C", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_pc_out", "PC HTF outlet temperature", "C", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_water_pc", "PC water consumption: makeup + cooling", "kg/s", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_pc_startup", "PC startup thermal energy", "MWht", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "cycle_htf_pump_power", "PC HTF pump power", "MWe", "", "powerblock", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "P_cooling_tower_tot", "Parasitic power condenser operation", "MWe", "", "powerblock", "*", "", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "P_out_net", "Total electric power to grid", "MWe", "", "system", "*", "", "" }, + { 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", "", "" }, + + { SSC_OUTPUT, SSC_ARRAY, "P_out_net", "Total electric power to grid", "MWe", "", "system", "sim_type=1", "", "" }, // TES - { SSC_OUTPUT, SSC_ARRAY, "tank_losses", "TES thermal losses", "MWt", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_tes_heater", "TES freeze protection power", "MWe", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_tes_hot", "TES hot temperature", "C", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_tes_cold", "TES cold temperature", "C", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "mass_tes_cold", "TES cold tank mass (end)", "kg", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "mass_tes_hot", "TES hot tank mass (end)", "kg", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dc_tes", "TES discharge thermal power", "MWt", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_ch_tes", "TES charge thermal power", "MWt", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "e_ch_tes", "TES charge state", "MWht", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_cr_to_tes_hot", "Mass flow: field to hot TES", "kg/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_hot_out", "Mass flow: TES hot out", "kg/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_pc_to_tes_cold", "Mass flow: cycle to cold TES", "kg/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_tes_cold_out", "Mass flow: TES cold out", "kg/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_field_to_cycle", "Mass flow: field to cycle", "kg/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_cycle_to_field", "Mass flow: cycle to field", "kg/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_cold_tank_to_hot_tank", "Mass flow: cold tank to hot tank", "kg/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "tes_htf_pump_power", "TES HTF pump power", "MWe", "", "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", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "op_mode_2", "2nd op. mode, if applicable", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "op_mode_3", "3rd op. mode, if applicable", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_balance", "Relative mass flow balance error", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_balance", "Relative energy balance error", "", "", "solver", "*", "", "" }, + { 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", "*", "LENGTH=12", "" }, + { 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", "*", "", "" }, + { 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", "*", "", "" }, + { 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", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "annual_field_freeze_protection", "Annual thermal power for field freeze protection", "kWt-hr", "", "Post-process", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "annual_tes_freeze_protection", "Annual thermal power for TES freeze protection", "kWt-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", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "tou_value", "CSP operating Time-of-use value", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pricing_mult", "PPA price multiplier", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_sb", "Thermal power for PC standby", "MWt", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_min", "Thermal power for PC min operation", "MWt", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_target", "Target thermal power to PC", "MWt", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_max", "Max thermal power to PC", "MWt", "", "solver", "*", "", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "is_rec_su_allowed", "is receiver startup allowed", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "is_pc_su_allowed", "is power cycle startup allowed", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "is_pc_sb_allowed", "is power cycle standby allowed", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_su", "Estimate rec. startup thermal power", "MWt", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_cr_on", "Estimate rec. thermal power TO HTF", "MWt", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_dc", "Estimate max TES discharge thermal power", "MWt", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_est_tes_ch", "Estimate max TES charge thermal power", "MWt", "", "solver", "*", "", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "operating_modes_a", "First 3 operating modes tried", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "operating_modes_b", "Next 3 operating modes tried", "", "", "solver", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "operating_modes_c", "Final 3 operating modes tried", "", "", "solver", "*", "", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "disp_rel_mip_gap", "Dispatch relative MIP gap", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_solve_state", "Dispatch solver state", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_subopt_flag", "Dispatch suboptimal solution flag", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_solve_iter", "Dispatch iterations count", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_objective", "Dispatch objective function value", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_obj_relax", "Dispatch objective function - relaxed max", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_qsf_expected", "Dispatch expected solar field available energy", "MWt", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_qsfprod_expected", "Dispatch expected solar field generation", "MWt", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_qsfsu_expected", "Dispatch expected solar field startup enegy", "MWt", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_tes_expected", "Dispatch expected TES charge level", "MWht", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_pceff_expected", "Dispatch expected power cycle efficiency adj.", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_thermeff_expected", "Dispatch expected SF thermal efficiency adj.", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_qpbsu_expected", "Dispatch expected power cycle startup energy", "MWht", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_wpb_expected", "Dispatch expected power generation", "MWe", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_rev_expected", "Dispatch expected revenue factor", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nconstr", "Dispatch number of constraints in problem", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_presolve_nvar", "Dispatch number of variables in problem", "", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "disp_solve_time", "Dispatch solver time", "sec", "", "tou", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "avg_suboptimal_rel_mip_gap","Average suboptimal relative MIP gap", "%", "", "tou", "*", "", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "P_fixed", "Parasitic power fixed load", "MWe", "", "system", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "P_plant_balance_tot", "Parasitic power generation-dependent load", "MWe", "", "system", "*", "", "" }, - - { 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", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "annual_W_cycle_gross", "Electrical source - Power cycle gross output", "kWhe", "", "system", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "conversion_factor", "Gross to Net Conversion Factor", "%", "", "system", "*", "", "" }, - //{ SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "system", "*", "", "" }, - //{ SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "system", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "sim_duration", "Computational time of timeseries simulation", "s", "", "system", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "solar_multiple_actual", "Actual solar multiple of system", "-", "", "system", "*", "", "" }, + { 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, "solar_multiple_actual", "Actual solar multiple of system", "-", "", "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", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_diams", "Pipe diameters in TES", "m", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_wallthk", "Pipe wall thickness in TES", "m", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_lengths", "Pipe lengths in TES", "m", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_mdot_dsn", "Mass flow TES pipes at design conditions", "kg/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_vel_dsn", "Velocity in TES pipes at design conditions", "m/s", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_T_dsn", "Temperature in TES pipes at design conditions", "C", "", "TES", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "pipe_tes_P_dsn", "Pressure in TES pipes at design conditions", "bar", "", "TES", "*", "", "" }, + { 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", "*", "", "" }, @@ -550,24 +605,137 @@ class cm_fresnel_physical : public compute_module void exec() { - - bool is_dispatch = false; + // Missing Variables bool is_dispatch_series = false; - // TES - // No custom TES piping - double init_hot_htf_percent = 0.3; + // TES + // No custom TES piping + double init_hot_htf_percent = 30; double cold_tank_max_heat = 25; double hot_tank_max_heat = 25; - // TOU - // NOT in CMOD inputs + // TOU + // NOT in CMOD inputs bool is_tod_pc_target_also_pc_max = false; vector f_turb_tou_periods{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }; size_t n_f_turbine = f_turb_tou_periods.size(); int csp_financial_model = 8; + // Field + // Hard Coded (currently no UI) + double L_rnr_pb = 50; + + // Common Parameters + bool is_dispatch = as_boolean("is_dispatch"); + int sim_type = as_number("sim_type"); + + // Process Solar Multiple or Aperture Design + + double solar_mult; + double total_Ap; + int nLoops; + { + int nRecVar = as_integer("nRecVar"); + vector HCE_FieldFrac = as_vector_double("HCE_FieldFrac"); + vector Shadowing = as_vector_double("Shadowing"); + vector dirt_env = as_vector_double("dirt_env"); + + double opt_derate = 0; // + for (int i = 0; i < nRecVar; i++) + opt_derate += HCE_FieldFrac[i] * Shadowing[i] * dirt_env[i]; + + double TrackingError = as_double("TrackingError"); + double GeomEffects = as_double("GeomEffects"); + double reflectivity = as_double("reflectivity"); + double Dirt_mirror = as_double("Dirt_mirror"); + double Error = as_double("Error"); + + double opt_normal = TrackingError * GeomEffects * reflectivity * Dirt_mirror * Error; // + + double loop_opt_eff = opt_derate * opt_normal; // + + int nMod = as_integer("nMod"); + double A_aperture = as_double("A_aperture"); + double A_loop = (float)nMod * A_aperture; + + double T_loop_in_des = as_double("T_loop_in_des"); + double T_loop_out_des = as_double("T_loop_out"); + double T_amb_sf_des = as_double("T_amb_sf_des"); + vector Design_loss = as_vector_double("Design_loss"); + vector HL_T_coefs = as_vector_double("HL_T_coefs"); + + int rec_model = as_integer("rec_model"); + double hl_des = 0; + double dT_des = ((T_loop_in_des + T_loop_out_des) / 2.0) - (T_amb_sf_des + 273.15); + switch (rec_model) + { + // Polynomial + case (1): + { + hl_des = CSP::poly_eval(dT_des, &HL_T_coefs[0], HL_T_coefs.size()); + break; + } + // Evacuated Receiver + case (2): + { + for (int i = 0; i < nRecVar; i++) + hl_des += HCE_FieldFrac[i] * Design_loss[i]; + break; + } + default: + { + return; + } + + } + + double L_mod = as_double("L_mod"); + double I_bn_des = as_double("I_bn_des"); + + double loop_therm_eff = 1.0 - ((hl_des * L_mod * nMod) / (A_loop * I_bn_des * loop_opt_eff)); + + double loop_eff = loop_opt_eff * loop_therm_eff; + + double P_ref = as_double("P_ref") * 1e6; + double eta_ref = as_double("eta_ref"); + double q_design = P_ref / eta_ref; + + double Ap_sm1 = q_design / (I_bn_des * loop_eff); + + int use_solar_mult_or_total_Ap = as_integer("solar_mult_or_Ap"); + double solar_mult_in = as_double("solar_mult_in"); + double total_Ap_in = as_double("total_Ap_in"); + + switch (use_solar_mult_or_total_Ap) + { + // Use Solar Multiple + case 0: + { + solar_mult = solar_mult_in; + total_Ap = solar_mult * Ap_sm1; + nLoops = std::ceil(total_Ap / A_loop); + + break; + } + + // Use Total Aperture + case 1: + { + total_Ap = total_Ap_in; + nLoops = std::ceil(total_Ap / A_loop); + solar_mult = total_Ap / Ap_sm1; + break; + } + + // Error + default: + { + throw exec_error("fresnel_physical", "use_solar_mult_or_total_Ap integer should be 0 (solar mult) or 1 (field aperture)"); + } + } + + } // Weather reader C_csp_weatherreader weather_reader; @@ -600,14 +768,19 @@ class cm_fresnel_physical : public compute_module sim_setup.m_report_step = 3600.0 / (double)steps_per_hour; //[s] } - // Solar field, trough + // Solar field C_csp_fresnel_collector_receiver c_fresnel; { // Inputs { + c_fresnel.m_nLoops = nLoops; + c_fresnel.m_solar_mult = solar_mult; + + + c_fresnel.m_nMod = as_integer("nMod"); c_fresnel.m_nRecVar = as_integer("nRecVar"); - c_fresnel.m_nLoops = as_integer("nLoops"); + 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"); @@ -634,7 +807,7 @@ class cm_fresnel_physical : public compute_module c_fresnel.m_ColAz = as_number("ColAz"); //c_fresnel.m_ColTilt = as_number("tilt"); - c_fresnel.m_solar_mult = as_number("solar_mult"); + c_fresnel.m_mc_bal_hot = as_number("mc_bal_hot"); c_fresnel.m_mc_bal_cold = as_number("mc_bal_cold"); @@ -651,98 +824,82 @@ class cm_fresnel_physical : public compute_module c_fresnel.m_Error = as_number("Error"); c_fresnel.m_L_mod = as_number("L_mod"); - size_t size; - double* IAM_T_coefs = as_array("IAM_T_coefs", &size); - c_fresnel.m_IAM_T_coefs.assign(IAM_T_coefs, IAM_T_coefs + size); - - double* IAM_L_coefs = as_array("IAM_L_coefs", &size); - c_fresnel.m_IAM_L_coefs.assign(IAM_L_coefs, IAM_L_coefs + size); - + 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"); - double* HCE_FieldFrac = as_array("HCE_FieldFrac", &size); - c_fresnel.m_HCE_FieldFrac.assign(HCE_FieldFrac, HCE_FieldFrac + size); - - double* D_abs_in = as_array("D_abs_in", &size); - c_fresnel.m_D_abs_in.assign(D_abs_in, D_abs_in + size); + 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"); - double* D_abs_out = as_array("D_abs_out", &size); - c_fresnel.m_D_abs_out.assign(D_abs_out, D_abs_out + size); + c_fresnel.m_D_glass_in = as_vector_double("D_glass_in"); - double* D_glass_in = as_array("D_glass_in", &size); - c_fresnel.m_D_glass_in.assign(D_glass_in, D_glass_in + size); + c_fresnel.m_D_glass_out = as_vector_double("D_glass_out"); - double* D_glass_out = as_array("D_glass_out", &size); - c_fresnel.m_D_glass_out.assign(D_glass_out, D_glass_out + size); + c_fresnel.m_D_plug = as_vector_double("D_plug"); - double* D_plug = as_array("D_plug", &size); - c_fresnel.m_D_plug.assign(D_plug, D_plug + size); + c_fresnel.m_Flow_type = as_vector_double("Flow_type"); - double* Flow_type = as_array("Flow_type", &size); - c_fresnel.m_Flow_type.assign(Flow_type, Flow_type + size); - - double* Rough = as_array("Rough", &size); - c_fresnel.m_Rough.assign(Rough, Rough + size); + c_fresnel.m_Rough = as_vector_double("Rough"); + - double* alpha_env = as_array("alpha_env", &size); - c_fresnel.m_alpha_env.assign(alpha_env, alpha_env + size); + 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"); - double* alpha_abs = as_array("alpha_abs", &size); - c_fresnel.m_alpha_abs.assign(alpha_abs, alpha_abs + size); + c_fresnel.m_alpha_abs = as_vector_double("alpha_abs"); + - double* Tau_envelope = as_array("Tau_envelope", &size); - c_fresnel.m_Tau_envelope.assign(Tau_envelope, Tau_envelope + size); + c_fresnel.m_Tau_envelope = as_vector_double("Tau_envelope"); + - double* epsilon_glass = as_array("epsilon_glass", &size); - c_fresnel.m_epsilon_glass.assign(epsilon_glass, epsilon_glass + size); + c_fresnel.m_epsilon_glass = as_vector_double("epsilon_glass"); + - double* GlazingIntact = as_array("GlazingIntactIn", &size); - c_fresnel.m_GlazingIntact.assign(GlazingIntact, GlazingIntact + size); + c_fresnel.m_GlazingIntact = as_vector_bool("GlazingIntactIn"); + - double* P_a = as_array("P_a", &size); - c_fresnel.m_P_a.assign(P_a, P_a + size); + c_fresnel.m_P_a = as_vector_double("P_a"); + - double* AnnulusGas = as_array("AnnulusGas", &size); - c_fresnel.m_AnnulusGas.assign(AnnulusGas, AnnulusGas + size); + c_fresnel.m_AnnulusGas = as_vector_double("AnnulusGas"); + - double* AbsorberMaterial = as_array("AbsorberMaterial", &size); - c_fresnel.m_AbsorberMaterial.assign(AbsorberMaterial, AbsorberMaterial + size); + c_fresnel.m_AbsorberMaterial = as_vector_double("AbsorberMaterial"); + - double* Shadowing = as_array("Shadowing", &size); - c_fresnel.m_Shadowing.assign(Shadowing, Shadowing + size); + c_fresnel.m_Shadowing = as_vector_double("Shadowing"); + - double* dirt_env = as_array("dirt_env", &size); - c_fresnel.m_dirt_env.assign(dirt_env, dirt_env + size); + c_fresnel.m_dirt_env = as_vector_double("dirt_env"); + - double* Design_loss = as_array("Design_loss", &size); - c_fresnel.m_Design_loss.assign(Design_loss, Design_loss + size); + 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"); - double* HL_T_coefs = as_array("HL_T_coefs", &size); - c_fresnel.m_HL_T_coefs.assign(HL_T_coefs, HL_T_coefs + size); + c_fresnel.m_HL_T_coefs = as_vector_double("HL_T_coefs"); + - double* HL_w_coefs = as_array("HL_w_coefs", &size); - c_fresnel.m_HL_w_coefs.assign(HL_w_coefs, HL_w_coefs + size); + c_fresnel.m_HL_w_coefs = as_vector_double("HL_w_coefs"); + c_fresnel.m_DP_nominal = as_number("DP_nominal"); - double* DP_coefs = as_array("DP_coefs", &size); - c_fresnel.m_DP_coefs.assign(DP_coefs, DP_coefs + size); + c_fresnel.m_DP_coefs = as_vector_double("DP_coefs"); + c_fresnel.m_rec_htf_vol = as_number("rec_htf_vol"); - // Hard Coded (currently no UI) - c_fresnel.m_L_rnr_pb = 50; + c_fresnel.m_L_rnr_pb = L_rnr_pb; //////////////////////// Questionable @@ -758,7 +915,7 @@ class cm_fresnel_physical : public compute_module 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_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); @@ -785,29 +942,29 @@ class cm_fresnel_physical : public compute_module 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); + //// 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); } } @@ -917,14 +1074,16 @@ class cm_fresnel_physical : public compute_module delete k_tes_loss_coeffs_array; }*/ - util::matrix_t tes_lengths; - if (is_assigned("tes_lengths")) { - tes_lengths = as_matrix("tes_lengths"); //[m] - } - if (!is_assigned("tes_lengths") || tes_lengths.ncells() < 11) { - double vals1[11] = { 0., 90., 100., 120., 0., 30., 90., 80., 80., 120., 80. }; - tes_lengths.assign(vals1, 11); - } + //util::matrix_t tes_lengths; + //if (is_assigned("tes_lengths")) { + // tes_lengths = as_matrix("tes_lengths"); //[m] + //} + //if (!is_assigned("tes_lengths") || tes_lengths.ncells() < 11) { + // double vals1[11] = { 0., 90., 100., 120., 0., 30., 90., 80., 80., 120., 80. }; + // tes_lengths.assign(vals1, 11); + //} + + double tes_pump_coef = 0.15; storage = C_csp_two_tank_tes( as_integer("Fluid"), @@ -932,7 +1091,7 @@ class cm_fresnel_physical : public compute_module as_integer("store_fluid"), as_matrix("store_fl_props"), as_double("P_ref") / as_double("eta_ref"), - as_double("solar_mult"), + solar_mult, as_double("P_ref") / as_double("eta_ref") * as_double("tshours"), as_double("h_tank"), as_double("u_tank"), @@ -951,7 +1110,8 @@ class cm_fresnel_physical : public compute_module as_double("pb_pump_coef"), as_boolean("tanks_in_parallel"), as_double("V_tes_des"), - false + false, + tes_pump_coef ); //as_boolean("calc_design_pipe_vals"), //as_double("tes_pump_coef"), @@ -1028,134 +1188,143 @@ class cm_fresnel_physical : public compute_module //int csp_financial_model = as_integer("csp_financial_model"); - - 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("trough_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 - } + if (sim_type == 1) + { + if (csp_financial_model > 0 && csp_financial_model < 5) { // Single Owner financial models - 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("trough_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("trough_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; + // 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("trough_physical", "\n\nYou selected dispatch optimization which requires that the array input ppa_price_input is defined\n"); + } - 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]; + 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 { - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -1.0); + 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("trough_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("trough_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; + + bool are_all_assigned = is_assigned("dispatch_sched_weekday") || is_assigned("dispatch_sched_weekend") + || is_assigned("dispatch_factor1") || is_assigned("dispatch_factor2") || is_assigned("dispatch_factor3") + || is_assigned("dispatch_factor4") || is_assigned("dispatch_factor5") || is_assigned("dispatch_factor6") + || is_assigned("dispatch_factor7") || is_assigned("dispatch_factor8") || is_assigned("dispatch_factor9"); + + if (are_all_assigned) { + + tou_params->mc_pricing.mc_weekdays = as_matrix("dispatch_sched_weekday"); + tou_params->mc_pricing.mc_weekends = as_matrix("dispatch_sched_weekend"); + + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, 0.0); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][0] = as_double("dispatch_factor1"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][1] = as_double("dispatch_factor2"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][2] = as_double("dispatch_factor3"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][3] = as_double("dispatch_factor4"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][4] = as_double("dispatch_factor5"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][5] = as_double("dispatch_factor6"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][6] = as_double("dispatch_factor7"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][7] = as_double("dispatch_factor8"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][8] = as_double("dispatch_factor9"); + } + 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 (ppa_mult_model == 0) // standard diuranal input - { - tou_params->mc_pricing.mv_is_diurnal = true; - - bool are_all_assigned = is_assigned("dispatch_sched_weekday") || is_assigned("dispatch_sched_weekend") - || is_assigned("dispatch_factor1") || is_assigned("dispatch_factor2") || is_assigned("dispatch_factor3") - || is_assigned("dispatch_factor4") || is_assigned("dispatch_factor5") || is_assigned("dispatch_factor6") - || is_assigned("dispatch_factor7") || is_assigned("dispatch_factor8") || is_assigned("dispatch_factor9"); - - if (are_all_assigned) { - - tou_params->mc_pricing.mc_weekdays = as_matrix("dispatch_sched_weekday"); - tou_params->mc_pricing.mc_weekends = as_matrix("dispatch_sched_weekend"); - - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, 0.0); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][0] = as_double("dispatch_factor1"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][1] = as_double("dispatch_factor2"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][2] = as_double("dispatch_factor3"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][3] = as_double("dispatch_factor4"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][4] = as_double("dispatch_factor5"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][5] = as_double("dispatch_factor6"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][6] = as_double("dispatch_factor7"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][7] = as_double("dispatch_factor8"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][8] = as_double("dispatch_factor9"); + else if (csp_financial_model == 5) { // Commercial + if (is_dispatch) { + throw exec_error("trough_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.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); + 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 == 5) { // Commercial - //if (is_dispatch) { - // throw exec_error("trough_physical", "\nDispatch optimization current not enabled for the Commercial financial model\n"); - // // need to add pricing lookup for Commercial financial model - //} - if (false) {} - else { + else if (csp_financial_model == 6) { // use 'mp_energy_market_revenue' -> from Merchant Plant model + 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); + 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) { + 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("trough_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 == 6) { // use 'mp_energy_market_revenue' -> from Merchant Plant model + else if (csp_financial_model == 7 || csp_financial_model == 8) { // LCOE (7) and None (8) - tou_params->mc_pricing.mv_is_diurnal = false; + 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) { - // 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("trough_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); + // 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("trough_physical", "csp_financial_model must be 1-8"); } } - else if (csp_financial_model == 7 || csp_financial_model == 8) { // LCOE (7) and None (8) - + else if (sim_type == 2) + { 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("trough_physical", "csp_financial_model must be 1-8"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -1.0); } + } // System Parameters @@ -1175,7 +1344,6 @@ class cm_fresnel_physical : public compute_module // System Dispatch csp_dispatch_opt dispatch; { - //if (as_boolean("is_dispatch")) { if(is_dispatch){ // System Design Parameters @@ -1184,7 +1352,7 @@ class cm_fresnel_physical : public compute_module // System Design Calcs double q_dot_cycle_des = W_dot_cycle_des / eta_cycle; //[MWt] - double q_dot_rec_des = q_dot_cycle_des * as_double("solar_mult"); //[MWt] + double q_dot_rec_des = q_dot_cycle_des * solar_mult; //[MWt] dispatch.solver_params.set_user_inputs(as_boolean("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"), @@ -1345,8 +1513,157 @@ class cm_fresnel_physical : public compute_module tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][i] = dispatch_series[i]; } - update("Begin timeseries simulation...", 0.0); + // Design point is complete, assign design outputs + { + // 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 = solar_mult; + + double q_dot_pc_des = W_dot_cycle_des / eta_ref; //[MWt] + double Q_tes = q_dot_pc_des * tshours; //[MWt-hr] + + double m_dot_htf_cycle_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 = q_dot_pc_des / (c_fresnel.m_I_bn_des * loop_eff) * 1e6; // [m2] + double sm1_nLoops = std::ceil(sm1_aperture / c_fresnel.m_A_loop); + 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"); + double total_land_area = field_area * land_mult; // [acres] + + double field_htf_min_temp = c_fresnel.m_htfProps.min_temp(); + double field_htf_max_temp = c_fresnel.m_htfProps.max_temp(); + + // 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); + } + + // 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; //[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); + + // Assign + { + assign("q_dot_cycle_des", q_dot_pc_des); + } + + // 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"); + double nameplate_des = W_dot_cycle_des * gross_net_conversion_des; + + // Assign + { + assign("nameplate", nameplate_des); + assign("m_dot_htf_cycle_des", m_dot_htf_cycle_des); + assign("W_dot_bop_design", W_dot_bop_design); + assign("W_dot_fixed", W_dot_fixed_parasitic_design); + + assign("solar_mult", solar_mult); + assign("nLoops", nLoops); + assign("total_Ap", total_Ap); + } + + } + + // 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 @@ -1476,72 +1793,72 @@ class cm_fresnel_physical : public compute_module // Fresnel - x = as_array("theta_L", &size); - vector theta_L_vec(x, x + size); - //delete x; - - x = as_array("phi_t", &size); - vector phi_t_vec(x, x + size); - //delete x; - - x = as_array("eta_optical", &size); - vector eta_optical_vec(x, x + size); - //delete x; - - //x = as_array("EqOptEff", &size); - //vector EqOptEff_vec(x, x + size); - //delete x; - auto eq = EqOpteff_vec; - - x = as_array("sf_def", &size); - vector sf_def_vec(x, x + size); - //delete x; - - x = as_array("q_inc_sf_tot", &size); - vector q_inc_sf_tot_vec(x, x + size); - //delete x; - x = as_array("q_abs_tot", &size); - vector q_abs_tot_vec(x, x + size); - //delete x; - x = as_array("q_dump", &size); - vector q_dump_vec(x, x + size); - //delete x; - x = as_array("q_loss_tot", &size); - vector q_loss_tot_vec(x, x + size); - //delete x; - x = as_array("Pipe_hl", &size); - vector Pipe_hl_vec(x, x + size); - //delete x; - x = as_array("q_avail", &size); - vector q_avail_vec(x, x + size); - //delete x; - x = as_array("q_loss_spec_tot", &size); - vector q_loss_spec_tot_vec(x, x + size); - //delete x; - x = as_array("eta_thermal", &size); - vector eta_thermal_vec(x, x + size); - //delete x; - x = as_array("E_bal_startup", &size); - vector E_bal_startup_vec(x, x + size); - //delete x; - x = as_array("m_dot_avail", &size); - vector m_dot_avail_vec(x, x + size); - //delete x; - x = as_array("m_dot_htf2", &size); - vector m_dot_htf2_vec(x, x + size); - //delete x; - x = as_array("DP_tot", &size); - vector DP_tot_vec(x, x + size); - //delete x; - x = as_array("T_sys_c", &size); - vector T_sys_c_vec(x, x + size); - //delete x; - x = as_array("T_sys_h", &size); - vector T_sys_h_vec(x, x + size); - //delete x; - x = as_array("t_loop_outlet", &size); - vector t_loop_outlet_vec(x, x + size); - //delete x; + //x = as_array("theta_L", &size); + //vector theta_L_vec(x, x + size); + ////delete x; + + //x = as_array("phi_t", &size); + //vector phi_t_vec(x, x + size); + ////delete x; + + //x = as_array("eta_optical", &size); + //vector eta_optical_vec(x, x + size); + ////delete x; + + ////x = as_array("EqOptEff", &size); + ////vector EqOptEff_vec(x, x + size); + ////delete x; + //auto eq = EqOpteff_vec; + + //x = as_array("sf_def", &size); + //vector sf_def_vec(x, x + size); + ////delete x; + + //x = as_array("q_inc_sf_tot", &size); + //vector q_inc_sf_tot_vec(x, x + size); + ////delete x; + //x = as_array("q_abs_tot", &size); + //vector q_abs_tot_vec(x, x + size); + ////delete x; + //x = as_array("q_dump", &size); + //vector q_dump_vec(x, x + size); + ////delete x; + //x = as_array("q_loss_tot", &size); + //vector q_loss_tot_vec(x, x + size); + ////delete x; + //x = as_array("Pipe_hl", &size); + //vector Pipe_hl_vec(x, x + size); + ////delete x; + //x = as_array("q_avail", &size); + //vector q_avail_vec(x, x + size); + ////delete x; + //x = as_array("q_loss_spec_tot", &size); + //vector q_loss_spec_tot_vec(x, x + size); + ////delete x; + //x = as_array("eta_thermal", &size); + //vector eta_thermal_vec(x, x + size); + ////delete x; + //x = as_array("E_bal_startup", &size); + //vector E_bal_startup_vec(x, x + size); + ////delete x; + //x = as_array("m_dot_avail", &size); + //vector m_dot_avail_vec(x, x + size); + ////delete x; + //x = as_array("m_dot_htf2", &size); + //vector m_dot_htf2_vec(x, x + size); + ////delete x; + //x = as_array("DP_tot", &size); + //vector DP_tot_vec(x, x + size); + ////delete x; + //x = as_array("T_sys_c", &size); + //vector T_sys_c_vec(x, x + size); + ////delete x; + //x = as_array("T_sys_h", &size); + //vector T_sys_h_vec(x, x + size); + ////delete x; + //x = as_array("t_loop_outlet", &size); + //vector t_loop_outlet_vec(x, x + size); + ////delete x; // Actual Outputs x = as_array("time_hr", &size); @@ -1550,8 +1867,8 @@ class cm_fresnel_physical : public compute_module x = as_array("P_out_net", &size); vector P_out_net_vec(x, x + size); - x = as_array("q_I", &size); - vector q_I_vec(x, x + size); + /*x = as_array("q_I", &size); + vector q_I_vec(x, x + size);*/ int y = 0; } @@ -1560,7 +1877,7 @@ class cm_fresnel_physical : public compute_module 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); - assign("solar_multiple_actual", as_double("solar_mult")); // calculated during verify() using cmod_csp_trough_eqns.cpp + assign("solar_multiple_actual", solar_mult); // calculated during verify() using cmod_csp_trough_eqns.cpp // Do unit post-processing here double* p_q_pc_startup = allocate("q_pc_startup", n_steps_fixed); @@ -1722,6 +2039,7 @@ class cm_fresnel_physical : public compute_module 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] @@ -1743,7 +2061,7 @@ class cm_fresnel_physical : public compute_module } double avg_gap = 0; - if (as_boolean("is_dispatch")) { + 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); @@ -1766,13 +2084,13 @@ class cm_fresnel_physical : public compute_module 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; + 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); + 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); } diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 2fffd5e3d..71d2c8b94 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -283,10 +283,10 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double void C_csp_fresnel_collector_receiver::set_output_value() { mc_reported_outputs.value(E_THETA_AVE, m_Theta_ave * m_r2d); //[deg], convert from rad - mc_reported_outputs.value(E_COSTH_AVE, m_CosTh_ave); //[-] - mc_reported_outputs.value(E_IAM_AVE, m_IAM_ave); //[-] - mc_reported_outputs.value(E_ROWSHADOW_AVE, m_RowShadow_ave); //[-] - mc_reported_outputs.value(E_ENDLOSS_AVE, m_EndLoss_ave); //[-] + //mc_reported_outputs.value(E_COSTH_AVE, m_CosTh_ave); //[-] + //mc_reported_outputs.value(E_IAM_AVE, m_IAM_ave); //[-] + //mc_reported_outputs.value(E_ROWSHADOW_AVE, m_RowShadow_ave); //[-] + //mc_reported_outputs.value(E_ENDLOSS_AVE, m_EndLoss_ave); //[-] //mc_reported_outputs.value(E_DNI_COSTH, m_dni_costh); //[W/m2] 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); //[-] @@ -332,33 +332,33 @@ void C_csp_fresnel_collector_receiver::set_output_value() // Fresnel - mc_reported_outputs.value(E_THETA_L, m_theta_L * r2d); - mc_reported_outputs.value(E_PHI_T, m_phi_t * r2d); - mc_reported_outputs.value(E_ETA_OPTICAL, m_eta_optical); - //mc_reported_outputs.value(E_EQ_OPT_EFF, m_EqOpteff); (E_EQUIV_OPT_ETA_TOT) - - mc_reported_outputs.value(E_SF_DEF, m_control_defocus * m_component_defocus); // SCAs_def (duplicate) - mc_reported_outputs.value(E_Q_INC_SF_TOT, m_q_dot_inc_sf_tot); // (duplicate) - mc_reported_outputs.value(E_Q_ABS_TOT, m_q_dot_sca_abs_summed_fullts); // (duplicate) - mc_reported_outputs.value(E_Q_DUMP, 0); //????????? - mc_reported_outputs.value(E_Q_LOSS_TOT, m_q_dot_sca_loss_summed_fullts); // (duplicate) - mc_reported_outputs.value(E_PIPE_HL, m_q_dot_xover_loss_summed_fullts + - m_q_dot_HR_cold_loss_fullts + - m_q_dot_HR_hot_loss_fullts); // q_dot_piping_loss (duplicate) - mc_reported_outputs.value(E_Q_AVAIL, m_q_dot_htf_to_sink_fullts); // q_dot_htf_sf_out (duplicate) - - mc_reported_outputs.value(E_Q_LOSS_SPEC_TOT, 0); // ?????????? likely E_Q_LOSS_TOT / num collectors - mc_reported_outputs.value(E_ETA_THERMAL, 0); // ???????????? m_eta_thermal is removed from trough - mc_reported_outputs.value(E_E_BAL_STARTUP, 0); // ????????????? - mc_reported_outputs.value(E_M_DOT_AVAIL, m_m_dot_htf_tot); // (duplicate) - mc_reported_outputs.value(E_M_DOT_HTF2, m_m_dot_htf_tot / (double)m_nLoops); // (duplicate) - mc_reported_outputs.value(E_DP_TOT, m_dP_total); // (duplicate) - - mc_reported_outputs.value(E_T_SYS_C, m_T_sys_c_t_int_fullts - 273.15); // (duplicate) - mc_reported_outputs.value(E_T_SYS_H, m_T_sys_h_t_int_fullts - 273.15); // (duplicate) - mc_reported_outputs.value(E_T_LOOP_OUTLET, m_T_htf_h_rec_out_t_int_fullts - 273.15); // (duplicate) - - mc_reported_outputs.value(E_Q_I, m_q_i); + //mc_reported_outputs.value(E_THETA_L, m_theta_L * r2d); + //mc_reported_outputs.value(E_PHI_T, m_phi_t * r2d); + //mc_reported_outputs.value(E_ETA_OPTICAL, m_eta_optical); + ////mc_reported_outputs.value(E_EQ_OPT_EFF, m_EqOpteff); (E_EQUIV_OPT_ETA_TOT) + + //mc_reported_outputs.value(E_SF_DEF, m_control_defocus * m_component_defocus); // SCAs_def (duplicate) + //mc_reported_outputs.value(E_Q_INC_SF_TOT, m_q_dot_inc_sf_tot); // (duplicate) + //mc_reported_outputs.value(E_Q_ABS_TOT, m_q_dot_sca_abs_summed_fullts); // (duplicate) + //mc_reported_outputs.value(E_Q_DUMP, 0); //????????? + //mc_reported_outputs.value(E_Q_LOSS_TOT, m_q_dot_sca_loss_summed_fullts); // (duplicate) + //mc_reported_outputs.value(E_PIPE_HL, m_q_dot_xover_loss_summed_fullts + + // m_q_dot_HR_cold_loss_fullts + + // m_q_dot_HR_hot_loss_fullts); // q_dot_piping_loss (duplicate) + //mc_reported_outputs.value(E_Q_AVAIL, m_q_dot_htf_to_sink_fullts); // q_dot_htf_sf_out (duplicate) + + //mc_reported_outputs.value(E_Q_LOSS_SPEC_TOT, 0); // ?????????? likely E_Q_LOSS_TOT / num collectors + //mc_reported_outputs.value(E_ETA_THERMAL, 0); // ???????????? m_eta_thermal is removed from trough + //mc_reported_outputs.value(E_E_BAL_STARTUP, 0); // ????????????? + //mc_reported_outputs.value(E_M_DOT_AVAIL, m_m_dot_htf_tot); // (duplicate) + //mc_reported_outputs.value(E_M_DOT_HTF2, m_m_dot_htf_tot / (double)m_nLoops); // (duplicate) + //mc_reported_outputs.value(E_DP_TOT, m_dP_total); // (duplicate) + + //mc_reported_outputs.value(E_T_SYS_C, m_T_sys_c_t_int_fullts - 273.15); // (duplicate) + //mc_reported_outputs.value(E_T_SYS_H, m_T_sys_h_t_int_fullts - 273.15); // (duplicate) + //mc_reported_outputs.value(E_T_LOOP_OUTLET, m_T_htf_h_rec_out_t_int_fullts - 273.15); // (duplicate) + + //mc_reported_outputs.value(E_Q_I, m_q_i); return; } @@ -369,6 +369,9 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle double T_htf_cold_in /*K*/, double m_dot_htf_loop /*kg/s*/, const C_csp_solver_sim_info& sim_info) { + + counter++; // DEBUG + m_m_dot_htf_tot = m_dot_htf_loop * float(m_nLoops); // Helpful Variables @@ -1157,10 +1160,10 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() m_EqOpteff = std::numeric_limits::quiet_NaN(); m_m_dot_htf_tot = std::numeric_limits::quiet_NaN(); m_Theta_ave = std::numeric_limits::quiet_NaN(); - m_CosTh_ave = std::numeric_limits::quiet_NaN(); - m_IAM_ave = std::numeric_limits::quiet_NaN(); - m_RowShadow_ave = std::numeric_limits::quiet_NaN(); - m_EndLoss_ave = std::numeric_limits::quiet_NaN(); + //m_CosTh_ave = std::numeric_limits::quiet_NaN(); + //m_IAM_ave = std::numeric_limits::quiet_NaN(); + //m_RowShadow_ave = std::numeric_limits::quiet_NaN(); + //m_EndLoss_ave = std::numeric_limits::quiet_NaN(); //m_dni_costh = std::numeric_limits::quiet_NaN(); m_c_htf_ave = std::numeric_limits::quiet_NaN(); @@ -1169,11 +1172,11 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() m_q_dot_inc_sf_tot = std::numeric_limits::quiet_NaN(); - for (int i = 0; i < 5; i++) - m_T_save[i] = std::numeric_limits::quiet_NaN(); + /*for (int i = 0; i < 5; i++) + m_T_save[i] = std::numeric_limits::quiet_NaN();*/ - mv_reguess_args.resize(3); - std::fill(mv_reguess_args.begin(), mv_reguess_args.end(), std::numeric_limits::quiet_NaN()); + /*mv_reguess_args.resize(3); + std::fill(mv_reguess_args.begin(), mv_reguess_args.end(), std::numeric_limits::quiet_NaN());*/ m_AnnulusGasMat.fill(NULL); @@ -1217,7 +1220,6 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs 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 { @@ -1266,7 +1268,6 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs return; } } - // Set up the optical table object.. { @@ -1308,10 +1309,6 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs delete[] data; } - - - - // Adjust parameters //m_ColTilt = m_ColTilt * m_d2r; //[rad] Collector tilt angle (0 is horizontal, 90deg is vertical), convert from [deg] @@ -1391,7 +1388,6 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs m_mc_bal_sca *= 3.6e3; //[Wht/K-m] -> [J/K-m] } - /*--- Do any initialization calculations here ---- */ //Allocate space for the loop simulation objects { @@ -1423,8 +1419,8 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs m_q_SCA_control_df.resize(m_nMod); //m_q_i.resize(m_nColt); // Not a vector? //m_IAM.resize(m_nColt); // Not a vector? - m_EndGain.resize(m_nMod); - m_EndLoss.resize(m_nMod); + //m_EndGain.resize(m_nMod); + //m_EndLoss.resize(m_nMod); // m_RowShadow.resize(m_nColt); // Not a vector? // //Allocate space for transient variables @@ -1472,10 +1468,9 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs } - //Initialize values - m_defocus_old = 0.; + //m_defocus_old = 0.; m_ncall = -1; //Set the defocus order to always be last->first in the loop @@ -1488,8 +1483,7 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs init_fieldgeom(); // for test end - // Calculate tracking parasitics for when trough is on sun - m_W_dot_sca_tracking_nom = m_SCA_drives_elec * (double)(m_nMod * m_nLoops) / 1.E6; //[MWe] + // Set solved parameters solved_params.m_T_htf_cold_des = m_T_loop_in_des; //[K] @@ -1506,7 +1500,6 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs 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)); } - return; } @@ -1519,8 +1512,8 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() */ //Calculate the total field aperture area - A_loop = (float)m_nMod * m_A_aperture; - m_Ap_tot = (float)m_nLoops * A_loop; + m_A_loop = (float)m_nMod * m_A_aperture; + m_Ap_tot = (float)m_nLoops * m_A_loop; if (m_rec_model == 2) { @@ -1662,12 +1655,33 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() //the estimated mass flow rate at design 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] //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 @@ -1741,7 +1755,7 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() switch (m_rec_model) { case 1: //Polynomial model - v_loop_tot = A_loop * m_rec_htf_vol / 1000. * (float)m_nLoops; //[m3] + 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 @@ -1799,7 +1813,7 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() m_v_hot = m_v_hot + v_sgs / 2.; m_v_cold = m_v_cold + v_sgs / 2.; - is_fieldgeom_init = true; //The field geometry has been initialized. Make note. + //is_fieldgeom_init = true; //The field geometry has been initialized. Make note. @@ -1832,6 +1846,64 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() } // ********************************************* + // Calculate tracking parasitics for when trough is on sun + m_W_dot_sca_tracking_nom = m_SCA_drives_elec * (double)(m_nMod * m_nLoops) / 1.E6; //[MWe] + + // Extra Output Design Point Calculations + { + // Loop Optical Efficiency + 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]; + m_opt_normal = m_TrackingError * m_GeomEffects * m_reflectivity * m_Dirt_mirror * m_Error; + + m_loop_opt_eff = m_opt_derate * m_opt_normal; + + // Loop Heat Loss + 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); + 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: + { + //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; + } + + } + + // 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 total efficiency + m_loop_eff = m_loop_therm_eff * m_loop_opt_eff; + + + } + + + + + + + + return true; } @@ -2066,10 +2138,11 @@ void C_csp_fresnel_collector_receiver::startup(const C_csp_weatherreader::S_outp loop_optical_eta(weather, sim_info); // Set mass flow rate to what I imagine might be an appropriate value + // TEMPORARY FIX by TB 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_CosTh_ave * m_IAM_ave * m_RowShadow_ave * m_EndLoss_ave) / + double m_dot_ss = (weather.m_beam * m_opteff_des) / (m_I_bn_des * m_opteff_des) * m_m_dot_loop_des; //[kg/s] m_dot_htf_loop = min(m_m_dot_htfmax, max(m_m_dot_htfmin, 0.8 * m_dot_ss + 0.2 * m_m_dot_htfmin)); //[kg/s] } @@ -2463,12 +2536,6 @@ void C_csp_fresnel_collector_receiver::on(const C_csp_weatherreader::S_outputs& set_output_value(); - - - - - - return; } @@ -2648,18 +2715,18 @@ double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_cs // loop_optical_eta() has side-effects. Store affected member variable values for restore after call. //double m_costh_ini(m_costh); double m_q_i_ini(m_q_i); - double m_IAM_ini(m_IAM); + //double m_IAM_ini(m_IAM); util::matrix_t m_ColOptEff_ini(m_ColOptEff); double m_EqOpteff_ini(m_EqOpteff); - vector m_EndGain_ini(m_EndGain); // changed from matrix to vector - vector m_EndLoss_ini(m_EndLoss); // changed from matrix to vector - std::vector m_RowShadow_ini(m_RowShadow); + //vector m_EndGain_ini(m_EndGain); // changed from matrix to vector + //vector m_EndLoss_ini(m_EndLoss); // changed from matrix to vector + //std::vector m_RowShadow_ini(m_RowShadow); std::vector m_q_SCA_ini(m_q_SCA); double m_Theta_ave_ini(m_Theta_ave); - double m_CosTh_ave_ini(m_CosTh_ave); - double m_IAM_ave_ini(m_IAM_ave); - double m_RowShadow_ave_ini(m_RowShadow_ave); - double m_EndLoss_ave_ini(m_EndLoss_ave); + //double m_CosTh_ave_ini(m_CosTh_ave); + //double m_IAM_ave_ini(m_IAM_ave); + //double m_RowShadow_ave_ini(m_RowShadow_ave); + //double m_EndLoss_ave_ini(m_EndLoss_ave); //double m_dni_costh_ini(m_dni_costh); double m_W_dot_sca_tracking_ini(m_W_dot_sca_tracking); double m_control_defocus_ini(m_control_defocus); @@ -2673,18 +2740,18 @@ double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_cs // Restore member variable values //m_costh = m_costh_ini; m_q_i = m_q_i_ini; - m_IAM = m_IAM_ini; + //m_IAM = m_IAM_ini; m_ColOptEff = m_ColOptEff_ini; m_EqOpteff = m_EqOpteff_ini; - m_EndGain = m_EndGain_ini; - m_EndLoss = m_EndLoss_ini; - m_RowShadow = m_RowShadow_ini; + //m_EndGain = m_EndGain_ini; + //m_EndLoss = m_EndLoss_ini; + //m_RowShadow = m_RowShadow_ini; m_q_SCA = m_q_SCA_ini; m_Theta_ave = m_Theta_ave_ini; - m_CosTh_ave = m_CosTh_ave_ini; - m_IAM_ave = m_IAM_ave_ini; - m_RowShadow_ave = m_RowShadow_ave_ini; - m_EndLoss_ave = m_EndLoss_ave_ini; + //m_CosTh_ave = m_CosTh_ave_ini; + //m_IAM_ave = m_IAM_ave_ini; + //m_RowShadow_ave = m_RowShadow_ave_ini; + //m_EndLoss_ave = m_EndLoss_ave_ini; //m_dni_costh = m_dni_costh_ini; m_W_dot_sca_tracking = m_W_dot_sca_tracking_ini; m_control_defocus = m_control_defocus_ini; @@ -2853,9 +2920,13 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade 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 + //m_IAM_ave = 0; + //m_RowShadow_ave = 0; 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 + m_q_SCA[j] = m_q_i; //[W/m] The flux on the collector + + //m_RowShadow_ave += m_RowShadow[] } //m_dni_costh = weather.m_beam * m_CosTh_ave; //[W/m2] @@ -2882,21 +2953,21 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta_off() m_q_i = 0; //[W/m] DNI * A_aper / L_sca //m_IAM.assign(m_IAM.size(), 0.0); //[-] Incidence angle modifiers - m_IAM = 0; //[-] Incidence angle modifiers NOT a vector (only one collector type) + //m_IAM = 0; //[-] Incidence angle modifiers NOT a vector (only one collector type) m_ColOptEff.fill(0.0); //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack m_EqOpteff = 0.; //m_EndGain.fill(0.0); //[-] Light from different collector hitting receiver //m_EndLoss.fill(0.0); //[-] Light missing receiver due to length + end gain - std::fill(m_EndGain.begin(), m_EndGain.end(), 0); //[-] Light from different collector hitting receiver NO longer a matrix - std::fill(m_EndLoss.begin(), m_EndLoss.end(), 0); //[-] Light missing receiver due to length + end gain NO longer a matrix - m_RowShadow.assign(m_RowShadow.size(), 0.0); //[-] Row-to-row m_Shadowing losses + //std::fill(m_EndGain.begin(), m_EndGain.end(), 0); //[-] Light from different collector hitting receiver NO longer a matrix + //std::fill(m_EndLoss.begin(), m_EndLoss.end(), 0); //[-] Light missing receiver due to length + end gain NO longer a matrix + //m_RowShadow.assign(m_RowShadow.size(), 0.0); //[-] Row-to-row m_Shadowing losses 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_Theta_ave = 0.0; - m_CosTh_ave = 0.0; - m_IAM_ave = 0.0; - m_RowShadow_ave = 0.0; - m_EndLoss_ave = 0.0; + //m_CosTh_ave = 0.0; + //m_IAM_ave = 0.0; + //m_RowShadow_ave = 0.0; + //m_EndLoss_ave = 0.0; //m_dni_costh = 0.0; m_W_dot_sca_tracking = 0.0; //[MWe] diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 1b1d049c0..285fe58d4 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -71,6 +71,7 @@ class EvacReceiverModel emit_table* m_epsilon_abs; HTFProperties m_htfProps, m_airProps; + 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 @@ -192,7 +193,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver OpticalDataTable optical_table; - HTFProperties m_htfProps, m_airProps; + // Hardcoded constants const double m_d2r = CSP::pi / 180.0; @@ -210,22 +211,20 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver //int m_n_r_iam_matrix; //[-] Number of rows in the IAM matrix double m_v_hot; //[m^3] Hot piping volume double m_v_cold; //[m^3] Cold piping volume - double m_Ap_tot; //[m^2] Total field aperture area + int m_nfsec; //[-] Number of field sections int m_nhdrsec; //[-] Number of header sections int m_nrunsec; //[-] Number of unique runner diameters double m_L_tot; //[m] Total length of collectors in a loop double m_opteff_des; //[-] Design-point optical efficieny (theta = 0) from the solar field - double m_m_dot_design; //[kg/s] Total solar field mass flow rate at design - double m_m_dot_loop_des;//[kg/s] LOOP design mass flow rate - double m_q_design; //[Wt] Design-point thermal power from the solar field - double m_W_dot_sca_tracking_nom; //[MWe] Tracking parasitics when trough is on sun + + 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 - double m_defocus_old; //[-] Defocus during previous call (= 1 at first call) + //double m_defocus_old; //[-] Defocus during previous call (= 1 at first call) int m_ncall; //[-] Track number of calls per timestep, reset = -1 in converged() call // Variables that are passed between methods, but not necessary to carry over timesteps @@ -250,20 +249,20 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver std::vector m_q_SCA; //[W/m] Total incident irradiation on the receiver (q"*A_aper/L_sca*cos(theta)*all_defocus) std::vector m_q_SCA_control_df; //[W/m] Total incident irradiation less CONTROL defocus (m_q_sca * control_defocus) - double m_IAM; //[-] Incidence angle modifiers NOT a vector becuase only one collector type ? - std::vector m_RowShadow; //[-] Row-to-row shadowing losses + //double m_IAM; //[-] Incidence angle modifiers NOT a vector becuase only one collector type ? + //std::vector m_RowShadow; //[-] Row-to-row shadowing losses /*m_nColt, m_nSCA*/ util::matrix_t m_ColOptEff; //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack - vector m_EndGain; //[-] Light from different collector hitting receiver // NOT a matrix because only one collector type - vector m_EndLoss; //[-] Light missing receiver due to length // NOT a matrix because only one collector type + //vector m_EndGain; //[-] Light from different collector hitting receiver // NOT a matrix because only one collector type + //vector m_EndLoss; //[-] Light missing receiver due to length // NOT a matrix because only one collector type double m_Theta_ave; //[rad] Field average m_theta value (but... nothing in our model allows for this to different over SCAs) - double m_CosTh_ave; //[-] Field average costheta value - double m_IAM_ave; //[-] Field average incidence angle modifier - double m_RowShadow_ave; //[-] Field average row shadowing loss - double m_EndLoss_ave; //[-] Field average end loss + //double m_CosTh_ave; //[-] Field average costheta value + //double m_IAM_ave; //[-] Field average incidence angle modifier + //double m_RowShadow_ave; //[-] Field average row shadowing loss + //double m_EndLoss_ave; //[-] Field average end loss //double m_costh; //[-] Cosine of the incidence angle between sun and trough aperture //double m_dni_costh; //[W/m2] DNI x cos(theta) product @@ -376,8 +375,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver bool m_ss_init_complete; //[-] For TCS-based model in acceptance testing, has model achieved steady state at first timestep? // Member variables that are used to store information for the EvacReceiver method - double m_T_save[5]; //[K] Saved temperatures from previous call to EvacReceiver single SCA energy balance model - std::vector mv_reguess_args; //[-] Logic to determine whether to use previous guess values or start iteration fresh + //double m_T_save[5]; //[K] Saved temperatures from previous call to EvacReceiver single SCA energy balance model + //std::vector mv_reguess_args; //[-] Logic to determine whether to use previous guess values or start iteration fresh 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 @@ -397,16 +396,13 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double m_current_hr; double m_current_day; - + int counter = 0; // Fields NOT in Trough double N_run_mult; - bool - no_fp, //Freeze protection flag - is_fieldgeom_init; //Flag to indicate whether the field geometry has been initialized - double A_loop; + const double Pi = acos(-1); const double pi = Pi; @@ -428,7 +424,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver string m_piping_summary; - double m_sf_def; + //double m_sf_def; std::unique_ptr m_evac_receiver; @@ -469,6 +465,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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_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 @@ -569,6 +567,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver std::vector m_T_loop; //[K] Temperature entering loop sections //std::vector m_P_loop; //[Pa] Gauge pessure in loop sections + + // Fresnel Only Inputs double m_L_mod_spacing; // Piping distance between sequential modules in a loop double m_L_crossover; // Length of crossover piping in a loop @@ -584,7 +584,22 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver C_csp_reported_outputs mc_reported_outputs; + HTFProperties m_htfProps, m_airProps; + // Public Design Point Outputs + double m_q_design; //[Wt] Design-point thermal power from the solar field + 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 trough is on sun + double m_Ap_tot; //[m^2] Total field aperture area + 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 // Methods public: diff --git a/tcs/csp_solver_two_tank_tes.cpp b/tcs/csp_solver_two_tank_tes.cpp index e67c19420..9093aedbf 100644 --- a/tcs/csp_solver_two_tank_tes.cpp +++ b/tcs/csp_solver_two_tank_tes.cpp @@ -2077,6 +2077,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 439bf707b..bd16ab9ff 100644 --- a/tcs/csp_solver_two_tank_tes.h +++ b/tcs/csp_solver_two_tank_tes.h @@ -350,6 +350,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 From de3e6a938c183bde60cbeee26d0acd2e0fb8d57f Mon Sep 17 00:00:00 2001 From: taylorbrown75 Date: Wed, 8 Mar 2023 11:29:36 -0700 Subject: [PATCH 11/46] Move design point calculations to solar field receiver class --- ssc/cmod_fresnel_physical.cpp | 516 +++++------------- tcs/csp_solver_fresnel_collector_receiver.cpp | 155 ++++-- tcs/csp_solver_fresnel_collector_receiver.h | 30 +- 3 files changed, 250 insertions(+), 451 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 530938e4d..28f497fe9 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -46,22 +46,100 @@ static var_info _cm_vtab_fresnel_physical[] = { { SSC_INPUT, SSC_NUMBER, "sim_type", "1 (default): timeseries, 2: design only", "", "", "System Control", "?=1", "", "SIMULATION_PARAMETER"}, - { SSC_INPUT, SSC_NUMBER, "solar_mult_in", "Solar multiple Input", "", "", "System Control", "", "", ""}, - { SSC_INPUT, SSC_NUMBER, "total_Ap_in", "Field aperture Input", "m3", "", "System Control", "", "", ""}, - { SSC_INPUT, SSC_NUMBER, "solar_mult_or_Ap", "Design using specified solar mult or field aperture", "m3", "", "System Control", "", "", ""}, - + // 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, "T_loop_in_des", "Design loop inlet temperature", "C", "", "controller", "*", "", ""}, - /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "controller", "*", "", ""}, - /*System Design*/{ SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "controller", "*", "", ""}, - /*System Design*/{ SSC_INPUT, SSC_NUMBER, "P_ref", "Label", "-", "", "powerblock", "*", "", ""}, - /*System Design*/{ SSC_INPUT, SSC_NUMBER, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "powerblock", "*", "", ""}, - /*System Design*///{ SSC_INPUT, SSC_NUMBER, "q_pb_design", "Label", "", "", "controller", "*", "", "" }, - /*System Design*/{ SSC_INPUT, SSC_NUMBER, "tshours", "Label", "", "", "controller", "*", "", "" }, - + + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult_in", "Solar multiple Input", "", "", "System Control", "", "", ""}, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "total_Ap_in", "Field aperture Input", "m3", "", "System Control", "", "", ""}, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult_or_Ap", "Design using specified solar mult or field aperture", "m3", "", "System Control", "", "", ""}, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "controller", "*", "", ""}, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "controller", "*", "", ""}, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "controller", "*", "", ""}, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "P_ref", "Design Turbine Net Output", "MWe", "", "powerblock", "*", "", ""}, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "powerblock", "*", "", ""}, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "tshours", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, + + + // Solar Field + + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "nMod", "Number of collector modules in a loop", "", "", "controller", "*", "INTEGER", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "eta_pump", "HTF pump efficiency", "", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "HDR_rough", "Header pipe roughness", "m", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_stow", "stow angle", "deg", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_dep", "deploy angle", "deg", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "FieldConfig", "Number of subfield headers", "", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmax", "Maximum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "controller", "*", "INTEGER", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "controller", "*", "", ""}, + /*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", "", "controller", "*", "", ""}, + /*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", "", "controller", "*", "", ""}, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "controller", "*", "", ""}, + /*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", "", "controller", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "controller", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "controller", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "controller", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "controller", "*", "", "" }, + /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "land_mult", "Non-solar field land area multiplier", "-", "", "controller", "*", "", "" }, + + + // Collector and Receiver + + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "controller", "*", "", "" }, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "opt_model", "The optical model", "", "", "controller", "*", "INTEGER", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "A_aperture", "Reflective aperture area of the collector", "m2", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "reflectivity", "Solar-weighted mirror reflectivity value", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "TrackingError", "Tracking error derate", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "GeomEffects", "Geometry effects derate", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Dirt_mirror", "User-defined dirt on mirror derate", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Error", "User-defined general optical error derate", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod", "The length of the collector module", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_T_coefs", "Incidence angle modifier coefficients - transversal plane", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_L_coefs", "Incidence angle modifier coefficients - longitudinal plane", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "OpticalTable", "Values of the optical efficiency table", "", "", "controller", "*", "", ""}, + + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "rec_model", "Receiver model type (1=Polynomial ; 2=Evac tube)", "", "", "controller", "*", "INTEGER", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HCE_FieldFrac", "The fraction of the field occupied by this HCE type", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_in", "The inner absorber tube diameter", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_out", "The outer absorber tube diameter", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_in", "The inner glass envelope diameter", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_out", "The outer glass envelope diameter", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_plug", "The diameter of the absorber flow plug (optional)", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Flow_type", "The flow type through the absorber", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Rough", "Roughness of the internal surface", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_env", "Envelope absorptance", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_1", "Absorber emittance - HCE variation 1", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_2", "Absorber emittance - HCE variation 2", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_3", "Absorber emittance - HCE variation 3", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_4", "Absorber emittance - HCE variation 4", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_abs", "Absorber absorptance", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Tau_envelope", "Envelope transmittance", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "epsilon_glass", "Glass envelope emissivity", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "GlazingIntactIn", "The glazing intact flag", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "P_a", "Annulus gas pressure", "torr", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AnnulusGas", "Annulus gas type (1=air; 26=Ar; 27=H2)", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AbsorberMaterial", "Absorber material type", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Shadowing", "Receiver bellows shadowing loss factor", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "dirt_env", "Loss due to dirt on the receiver envelope", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Design_loss", "Receiver heat loss at design", "W/m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod_spacing", "Piping distance between sequential modules in a loop", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_crossover", "Length of crossover piping in a loop", "m", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_T_coefs", "HTF temperature-dependent heat loss coefficients", "W/m-K", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_w_coefs", "Wind-speed-dependent heat loss coefficients", "W/m-(m/s)", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "DP_nominal", "Pressure drop across a single collector assembly at design", "bar", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "DP_coefs", "Pressure drop mass flow based part-load curve", "", "", "controller", "*", "", ""}, + /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variations", "", "", "controller", "?=4", "INTEGER", "" }, // System Control @@ -71,8 +149,7 @@ static var_info _cm_vtab_fresnel_physical[] = { /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Label", "", "", "controller", "*", "", ""}, /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "bop_array", "Label", "", "", "controller", "*", "", "" }, /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, - + /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, @@ -84,190 +161,19 @@ static var_info _cm_vtab_fresnel_physical[] = { /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, - //{ 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", "", "" }, - - //{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "" }, - //{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "" }, - //{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "" }, - - //{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "is_wlim_series", "Use time-series net electricity generation limits", "", "", "tou", "?=0", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "wlim_series", "Time series net electicity generation limits", "kWe", "", "tou", "is_wlim_series=1", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "f_turb_tou_periods", "Dispatch logic for turbine load fraction", "-", "", "tou", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "csp_financial_model", "", "1-8", "", "Financial Model", "?=1", "INTEGER,MIN=0", "" }, - //{ SSC_INPUT, SSC_NUMBER, "ppa_multiplier_model", "PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts", "0/1", "0=diurnal,1=timestep", "tou", "?=0", /*need a default so this var works in required_if*/ "INTEGER,MIN=0", "" }, - //{ SSC_INPUT, SSC_ARRAY, "dispatch_factors_ts", "Dispatch payment factor array", "", "", "tou", "ppa_multiplier_model=1&csp_financial_model<5&is_dispatch=1","", "" }, - //{ 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","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "en_electricity_rates", "Enable electricity rates for grid purchase", "0/1", "", "Electricity Rates", "?=0", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekday", "12x24 PPA pricing Weekday schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekend", "12x24 PPA pricing Weekend schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor1", "Dispatch payment factor 1", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor2", "Dispatch payment factor 2", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor3", "Dispatch payment factor 3", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor4", "Dispatch payment factor 4", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor5", "Dispatch payment factor 5", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor6", "Dispatch payment factor 6", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor7", "Dispatch payment factor 7", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor8", "Dispatch payment factor 8", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "dispatch_factor9", "Dispatch payment factor 9", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "tou", "?=1", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "tou", "", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "tou", "?", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA prices - yearly", "$/kWh", "", "Revenue", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, - //{ 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", "", "" }, - - //{ SSC_INPUT, SSC_NUMBER, "water_usage_per_wash", "Water usage per wash", "L/m2_aper", "", "system", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "washing_frequency", "Mirror washing frequency", "-/year", "", "system", "*", "", "" }, - - // Solar Field (from cmod_tcsmslf.cpp) - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "nMod", "Number of collector modules in a loop", "", "", "controller", "*", "INTEGER", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "eta_pump", "HTF pump efficiency", "", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "HDR_rough", "Header pipe roughness", "m", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_stow", "stow angle", "deg", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_dep", "deploy angle", "deg", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "FieldConfig", "Number of subfield headers", "", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmax", "Maximum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "controller", "*", "INTEGER", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_hot", "The heat capacity of the balance of plant on the hot side", "kWht/K-MWt", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_cold", "The heat capacity of the balance of plant on the cold side", "kWht/K-MWt", "", "controller", "*", "", ""}, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "controller", "*", "", ""}, - /*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", "", "controller", "*", "", "" }, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "controller", "*", "", "" }, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "controller", "*", "", "" }, - /*Solar Field*/{ SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "controller", "*", "", "" }, - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "controller", "*", "", "" }, - - /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "land_mult", "Non-solar field land area multiplier", "-", "", "controller", "*", "", "" }, - - - - // Collector and Receiver - - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "controller", "*", "", "" }, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "opt_model", "The optical model", "", "", "controller", "*", "INTEGER", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "A_aperture", "Reflective aperture area of the collector", "m2", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "reflectivity", "Solar-weighted mirror reflectivity value", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "TrackingError", "Tracking error derate", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "GeomEffects", "Geometry effects derate", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Dirt_mirror", "User-defined dirt on mirror derate", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Error", "User-defined general optical error derate", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod", "The length of the collector module", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_T_coefs", "Incidence angle modifier coefficients - transversal plane", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_L_coefs", "Incidence angle modifier coefficients - longitudinal plane", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "OpticalTable", "Values of the optical efficiency table", "", "", "controller", "*", "", ""}, - - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "rec_model", "Receiver model type (1=Polynomial ; 2=Evac tube)", "", "", "controller", "*", "INTEGER", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HCE_FieldFrac", "The fraction of the field occupied by this HCE type", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_in", "The inner absorber tube diameter", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_out", "The outer absorber tube diameter", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_in", "The inner glass envelope diameter", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_out", "The outer glass envelope diameter", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_plug", "The diameter of the absorber flow plug (optional)", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Flow_type", "The flow type through the absorber", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Rough", "Roughness of the internal surface", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_env", "Envelope absorptance", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_1", "Absorber emittance - HCE variation 1", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_2", "Absorber emittance - HCE variation 2", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_3", "Absorber emittance - HCE variation 3", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_4", "Absorber emittance - HCE variation 4", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_abs", "Absorber absorptance", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Tau_envelope", "Envelope transmittance", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "epsilon_glass", "Glass envelope emissivity", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "GlazingIntactIn", "The glazing intact flag", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "P_a", "Annulus gas pressure", "torr", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AnnulusGas", "Annulus gas type (1=air; 26=Ar; 27=H2)", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AbsorberMaterial", "Absorber material type", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Shadowing", "Receiver bellows shadowing loss factor", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "dirt_env", "Loss due to dirt on the receiver envelope", "", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Design_loss", "Receiver heat loss at design", "W/m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod_spacing", "Piping distance between sequential modules in a loop", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_crossover", "Length of crossover piping in a loop", "m", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_T_coefs", "HTF temperature-dependent heat loss coefficients", "W/m-K", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_w_coefs", "Wind-speed-dependent heat loss coefficients", "W/m-(m/s)", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "DP_nominal", "Pressure drop across a single collector assembly at design", "bar", "", "controller", "*", "", ""}, - /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "DP_coefs", "Pressure drop mass flow based part-load curve", "", "", "controller", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variations", "", "", "controller", "?=4", "INTEGER", "" }, - - - //{ SSC_INPUT, SSC_NUMBER, "tilt", "Tilt angle of surface/axis", "", "", "Weather", "*", "", "" }, - - //{ SSC_INPUT, SSC_NUMBER, "calc_design_pipe_vals", "Calculate temps and pressures at design conditions for runners and headers", "-", "", "controller", "*", "", "" }, - - - //{ SSC_INPUT, SSC_NUMBER, "custom_sf_pipe_sizes", "Use custom solar field pipe diams, wallthks, and lengths", "none", "", "solar_field", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "sf_rnr_diams", "Custom runner diameters", "m", "", "solar_field", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "sf_rnr_wallthicks", "Custom runner wall thicknesses", "m", "", "solar_field", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "sf_rnr_lengths", "Custom runner lengths", "m", "", "solar_field", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "sf_hdr_diams", "Custom header diameters", "m", "", "solar_field", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "sf_hdr_wallthicks", "Custom header wall thicknesses", "m", "", "solar_field", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "sf_hdr_lengths", "Custom header lengths", "m", "", "solar_field", "*", "", "" }, - - // controller (type 251) inputs - //VARTYPE DATATYPE NAME LABEL UNITS META GROUP REQUIRED_IF CONSTRAINTS UI_HINTS - //{ SSC_INPUT, SSC_NUMBER, "field_fluid", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "is_hx", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "hx_config", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "q_max_aux", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "T_set_aux", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "V_tank_hot_ini", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "T_tank_hot_ini", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "T_tank_cold_ini", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "T_field_in_des", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "T_field_out_des", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "W_pb_design", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "solarm", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "fossil_mode", "Label", "", "", "controller", "*", "INTEGER", "" }, - //{ SSC_INPUT, SSC_NUMBER, "fthr_ok", "Label", "", "", "controller", "*", "INTEGER", "" }, - //{ SSC_INPUT, SSC_NUMBER, "fc_on", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "tes_type", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "tslogic_a", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "tslogic_b", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "tslogic_c", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "ffrac", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "tc_fill", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "tc_void", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "t_dis_out_min", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "t_ch_out_max", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "nodes", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "f_tc_cold", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "custom_sgs_pipe_sizes", "Use custom SGS pipe diams, wallthks, and lengths", "-", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "sgs_diams", "Custom SGS diameters", "m", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "sgs_wallthicks", "Custom SGS wall thicknesses", "m", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "sgs_lengths", "Custom SGS lengths", "m", "", "controller", "*", "", "" }, // Power Cycle Inputs /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "T_startup", "Power block startup temperature", "C", "", "controller", "*", "", "" }, - /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "powerblock", "*", "", ""}, - /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "powerblock", "*", "", ""}, - /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby mode", "none", "", "powerblock", "*", "", ""}, - /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "powerblock", "*", "", ""}, - /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "", "", "controller", "*", "", "" }, - /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "controller", "*", "", "" }, - { SSC_INPUT, SSC_NUMBER, "pc_config", "0: Steam Rankine (224), 1: user defined", "-", "", "powerblock", "?=0", "INTEGER", "" }, - - /*Power Cycle*///{ SSC_INPUT, SSC_NUMBER, "t_standby_reset", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "-", "", "powerblock", "*", "", ""}, - //{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "-", "", "powerblock", "*", "", "" }, - + /*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", "", "", "controller", "*", "", "" }, + /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "controller", "*", "", "" }, + /*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", "", "" }, @@ -281,8 +187,6 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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=trough)", "1/2/3", "tower/trough/user", "powerblock", "pc_config=0", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "P_boil", "Boiler operating pressure", "bar", "", "powerblock", "pc_config=0", "", "" }, - /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "DP_SGS", "Pressure drop within the steam generator", "bar", "", "controller", "*", "", "" }, @@ -292,50 +196,22 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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 - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "store_fluid", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_MATRIX, "store_fl_props", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "u_tank", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_pairs", "Label", "", "", "controller", "*", "INTEGER", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_max_heat", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_hot", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank_min", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_cold", "Label", "", "", "controller", "*", "", "" }, - /*Storage*///{ SSC_INPUT, SSC_NUMBER, "vol_tank", "Label", "", "", "controller", "*", "", "" }, - /*Storage*///{ SSC_INPUT, SSC_NUMBER, "tes_temp", "Label", "", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "hot_tank_max_heat", "Rated heater capacity for hot tank heating", "MWe", "", "TES", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "cold_tank_max_heat", "Rated heater capacity for cold tank heating", "MWe", "", "TES", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "init_hot_htf_percent", "Initial fraction of avail. vol that is hot", "%", "", "TES", "*", "", "" }, - - - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "has_hot_tank_bypass", "Bypass valve connects field outlet to cold tank", "-", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "T_tank_hot_inlet_min", "Minimum hot tank htf inlet temperature", "C", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "tes_pump_coef", "Label", "", "", "controller", "*", "", "" }, - /*Storage*/{ SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "controller", "*", "", "" }, - - //{ SSC_INPUT, SSC_NUMBER, "custom_tes_p_loss", "TES pipe losses are based on custom lengths and coeffs", "-", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "k_tes_loss_coeffs", "Minor loss coeffs for the coll, gen, and bypass loops", "-", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "custom_tes_pipe_sizes", "Use custom TES pipe diams, wallthks, and lengths", "-", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "tes_diams", "Custom TES diameters", "m", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "tes_wallthicks", "Custom TES wall thicknesses", "m", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_MATRIX, "tes_lengths", "Custom TES lengths", "m", "", "controller", "", "", "" }, - - - - - // All other inputs from (cmod_trough_physical.cpp) - - // Needed for auto-updating dependent inputs - //{ SSC_INPUT, SSC_NUMBER, "use_solar_mult_or_aperture_area", "Use solar multiple or total field aperture area", "-", "", "controller", "?=0", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "specified_solar_multiple", "specified_solar_multiple", "-", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "specified_total_aperture", "specified_total_aperture", "-", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_NUMBER, "non_solar_field_land_area_multiplier", "non_solar_field_land_area_multiplier", "-", "", "controller", "*", "", "" }, - //{ SSC_INPUT, SSC_ARRAY, "trough_loop_control", "trough_loop_control", "-", "", "controller", "*", "", "" }, - /*Sys Control*///{ SSC_INPUT, SSC_NUMBER, "disp_wlim_maxspec", "disp_wlim_maxspec", "-", "", "controller", "*", "", "" }, - + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "store_fluid", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_MATRIX, "store_fl_props", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "u_tank", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_pairs", "Label", "", "", "controller", "*", "INTEGER", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_max_heat", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_hot", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank_min", "Label", "", "", "controller", "*", "", "" }, + /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_cold", "Label", "", "", "controller", "*", "", "" }, + + /*???????Storage*/{ SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "controller", "*", "", "" }, + /*???????Storage*/{ SSC_INPUT, SSC_NUMBER, "has_hot_tank_bypass", "Bypass valve connects field outlet to cold tank", "-", "", "controller", "*", "", "" }, + /*???????Storage*/{ SSC_INPUT, SSC_NUMBER, "T_tank_hot_inlet_min", "Minimum hot tank htf inlet temperature", "C", "", "controller", "*", "", "" }, + /*???????Storage*/{ SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "controller", "*", "", "" }, // OUTPUTS @@ -575,7 +451,7 @@ static var_info _cm_vtab_fresnel_physical[] = { { 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, "solar_multiple_actual", "Actual solar multiple of system", "-", "", "system", "sim_type=1", "", "" }, + //{ SSC_OUTPUT, SSC_NUMBER, "solar_multiple_actual", "Actual solar multiple of system", "-", "", "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", "*", "", "" }, @@ -613,6 +489,7 @@ class cm_fresnel_physical : public compute_module double init_hot_htf_percent = 30; double cold_tank_max_heat = 25; double hot_tank_max_heat = 25; + double tes_pump_coef = 0.15; // TOU // NOT in CMOD inputs @@ -629,114 +506,7 @@ class cm_fresnel_physical : public compute_module bool is_dispatch = as_boolean("is_dispatch"); int sim_type = as_number("sim_type"); - // Process Solar Multiple or Aperture Design - double solar_mult; - double total_Ap; - int nLoops; - { - int nRecVar = as_integer("nRecVar"); - vector HCE_FieldFrac = as_vector_double("HCE_FieldFrac"); - vector Shadowing = as_vector_double("Shadowing"); - vector dirt_env = as_vector_double("dirt_env"); - - double opt_derate = 0; // - for (int i = 0; i < nRecVar; i++) - opt_derate += HCE_FieldFrac[i] * Shadowing[i] * dirt_env[i]; - - double TrackingError = as_double("TrackingError"); - double GeomEffects = as_double("GeomEffects"); - double reflectivity = as_double("reflectivity"); - double Dirt_mirror = as_double("Dirt_mirror"); - double Error = as_double("Error"); - - double opt_normal = TrackingError * GeomEffects * reflectivity * Dirt_mirror * Error; // - - double loop_opt_eff = opt_derate * opt_normal; // - - int nMod = as_integer("nMod"); - double A_aperture = as_double("A_aperture"); - double A_loop = (float)nMod * A_aperture; - - double T_loop_in_des = as_double("T_loop_in_des"); - double T_loop_out_des = as_double("T_loop_out"); - double T_amb_sf_des = as_double("T_amb_sf_des"); - vector Design_loss = as_vector_double("Design_loss"); - vector HL_T_coefs = as_vector_double("HL_T_coefs"); - - int rec_model = as_integer("rec_model"); - double hl_des = 0; - double dT_des = ((T_loop_in_des + T_loop_out_des) / 2.0) - (T_amb_sf_des + 273.15); - switch (rec_model) - { - // Polynomial - case (1): - { - hl_des = CSP::poly_eval(dT_des, &HL_T_coefs[0], HL_T_coefs.size()); - break; - } - // Evacuated Receiver - case (2): - { - for (int i = 0; i < nRecVar; i++) - hl_des += HCE_FieldFrac[i] * Design_loss[i]; - break; - } - default: - { - return; - } - - } - - double L_mod = as_double("L_mod"); - double I_bn_des = as_double("I_bn_des"); - - double loop_therm_eff = 1.0 - ((hl_des * L_mod * nMod) / (A_loop * I_bn_des * loop_opt_eff)); - - double loop_eff = loop_opt_eff * loop_therm_eff; - - double P_ref = as_double("P_ref") * 1e6; - double eta_ref = as_double("eta_ref"); - double q_design = P_ref / eta_ref; - - double Ap_sm1 = q_design / (I_bn_des * loop_eff); - - int use_solar_mult_or_total_Ap = as_integer("solar_mult_or_Ap"); - double solar_mult_in = as_double("solar_mult_in"); - double total_Ap_in = as_double("total_Ap_in"); - - switch (use_solar_mult_or_total_Ap) - { - // Use Solar Multiple - case 0: - { - solar_mult = solar_mult_in; - total_Ap = solar_mult * Ap_sm1; - nLoops = std::ceil(total_Ap / A_loop); - - break; - } - - // Use Total Aperture - case 1: - { - total_Ap = total_Ap_in; - nLoops = std::ceil(total_Ap / A_loop); - solar_mult = total_Ap / Ap_sm1; - - break; - } - - // Error - default: - { - throw exec_error("fresnel_physical", "use_solar_mult_or_total_Ap integer should be 0 (solar mult) or 1 (field aperture)"); - } - } - - } - // Weather reader C_csp_weatherreader weather_reader; C_csp_solver::S_sim_setup sim_setup; @@ -773,10 +543,9 @@ class cm_fresnel_physical : public compute_module { // Inputs { - c_fresnel.m_nLoops = nLoops; - c_fresnel.m_solar_mult = solar_mult; - - + 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"); @@ -787,6 +556,8 @@ class cm_fresnel_physical : public compute_module 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"); @@ -906,7 +677,8 @@ class cm_fresnel_physical : public compute_module c_fresnel.m_V_wind_des = as_number("V_wind_des"); c_fresnel.m_T_amb_sf_des = as_number("T_amb_sf_des"); } - + + c_fresnel.design_solar_mult(); // Allocate Outputs { @@ -1062,28 +834,7 @@ class cm_fresnel_physical : public compute_module // TES C_csp_two_tank_tes storage; { - - // Convert array to matrix - /*util::matrix_t k_tes_loss_coeffs_mat; - { - size_t size; - ssc_number_t* k_tes_loss_coeffs_array = as_array("k_tes_loss_coeffs", &size); - vector k_tes_loss_coeffs_vec(k_tes_loss_coeffs_array, k_tes_loss_coeffs_array + size); - size_t sizeof1 = 1; - k_tes_loss_coeffs_mat = util::matrix_t(sizeof1, size, &k_tes_loss_coeffs_vec); - delete k_tes_loss_coeffs_array; - }*/ - - //util::matrix_t tes_lengths; - //if (is_assigned("tes_lengths")) { - // tes_lengths = as_matrix("tes_lengths"); //[m] - //} - //if (!is_assigned("tes_lengths") || tes_lengths.ncells() < 11) { - // double vals1[11] = { 0., 90., 100., 120., 0., 30., 90., 80., 80., 120., 80. }; - // tes_lengths.assign(vals1, 11); - //} - - double tes_pump_coef = 0.15; + storage = C_csp_two_tank_tes( as_integer("Fluid"), @@ -1091,7 +842,7 @@ class cm_fresnel_physical : public compute_module as_integer("store_fluid"), as_matrix("store_fl_props"), as_double("P_ref") / as_double("eta_ref"), - solar_mult, + c_fresnel.m_solar_mult, as_double("P_ref") / as_double("eta_ref") * as_double("tshours"), as_double("h_tank"), as_double("u_tank"), @@ -1352,7 +1103,7 @@ class cm_fresnel_physical : public compute_module // System Design Calcs double q_dot_cycle_des = W_dot_cycle_des / eta_cycle; //[MWt] - double q_dot_rec_des = q_dot_cycle_des * solar_mult; //[MWt] + double q_dot_rec_des = q_dot_cycle_des * c_fresnel.m_solar_mult; //[MWt] dispatch.solver_params.set_user_inputs(as_boolean("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"), @@ -1491,7 +1242,6 @@ class cm_fresnel_physical : public compute_module } //if the pricing schedule is provided as hourly, overwrite the tou schedule - //if (as_boolean("is_dispatch_series")) if(is_dispatch_series) { size_t n_dispatch_series; @@ -1519,7 +1269,7 @@ class cm_fresnel_physical : public compute_module 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 = solar_mult; + double solar_mult_des = c_fresnel.m_solar_mult; double q_dot_pc_des = W_dot_cycle_des / eta_ref; //[MWt] double Q_tes = q_dot_pc_des * tshours; //[MWt-hr] @@ -1536,8 +1286,8 @@ class cm_fresnel_physical : public compute_module 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 = q_dot_pc_des / (c_fresnel.m_I_bn_des * loop_eff) * 1e6; // [m2] - double sm1_nLoops = std::ceil(sm1_aperture / c_fresnel.m_A_loop); + 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] @@ -1652,9 +1402,9 @@ class cm_fresnel_physical : public compute_module assign("W_dot_bop_design", W_dot_bop_design); assign("W_dot_fixed", W_dot_fixed_parasitic_design); - assign("solar_mult", solar_mult); - assign("nLoops", nLoops); - assign("total_Ap", total_Ap); + assign("solar_mult", c_fresnel.m_solar_mult); + assign("nLoops", c_fresnel.m_nLoops); + assign("total_Ap", c_fresnel.m_Ap_tot); } } @@ -1877,7 +1627,7 @@ class cm_fresnel_physical : public compute_module 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); - assign("solar_multiple_actual", solar_mult); // calculated during verify() using cmod_csp_trough_eqns.cpp + //assign("solar_multiple_actual", solar_mult); // calculated during verify() using cmod_csp_trough_eqns.cpp // Do unit post-processing here double* p_q_pc_startup = allocate("q_pc_startup", n_steps_fixed); diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 71d2c8b94..502077cb0 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -1511,9 +1511,9 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() is provided by the weatherreader class and not set until after init() and before the first call(). */ - //Calculate the total field aperture area - m_A_loop = (float)m_nMod * m_A_aperture; - m_Ap_tot = (float)m_nLoops * m_A_loop; + // If solar multiple is not yet calculated + if(m_is_solar_mult_designed == false) + this->design_solar_mult(); if (m_rec_model == 2) { @@ -1849,61 +1849,6 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() // Calculate tracking parasitics for when trough is on sun m_W_dot_sca_tracking_nom = m_SCA_drives_elec * (double)(m_nMod * m_nLoops) / 1.E6; //[MWe] - // Extra Output Design Point Calculations - { - // Loop Optical Efficiency - 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]; - m_opt_normal = m_TrackingError * m_GeomEffects * m_reflectivity * m_Dirt_mirror * m_Error; - - m_loop_opt_eff = m_opt_derate * m_opt_normal; - - // Loop Heat Loss - 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); - 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: - { - //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; - } - - } - - // 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 total efficiency - m_loop_eff = m_loop_therm_eff * m_loop_opt_eff; - - - } - - - - - - - - return true; } @@ -2775,6 +2720,100 @@ double C_csp_fresnel_collector_receiver::get_collector_area() // ------------------------------------------------------------------- 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 + 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 + 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); + break; + } + case 1: + { + m_Ap_tot = m_total_Ap_in; + m_nLoops = std::ceil(m_Ap_tot / 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; + } + } + + // Number of Loops necessary for solar mult = 1 + m_nLoops_sm1 = std::ceil(m_Ap_sm1 / m_A_loop); + + } + +} + void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreader::S_outputs& weather, const C_csp_solver_sim_info& sim_info) { diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 285fe58d4..7269c386e 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -211,7 +211,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver //int m_n_r_iam_matrix; //[-] Number of rows in the IAM matrix double m_v_hot; //[m^3] Hot piping volume double m_v_cold; //[m^3] Cold piping volume - + bool m_is_solar_mult_designed = false; + int m_nfsec; //[-] Number of field sections int m_nhdrsec; //[-] Number of header sections int m_nrunsec; //[-] Number of unique runner diameters @@ -455,17 +456,20 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // Public Fields public: + 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) - int m_nLoops = -1; // [-] Number of loops in the field 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 @@ -485,7 +489,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double m_ColAz; // [deg] Collector azimuth angle //double m_ColTilt; // [deg] Collector tilt angle (0 is horizontal, 90deg is vertical) - double m_solar_mult; // [-] Solar multiple + 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 @@ -587,19 +591,23 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver HTFProperties m_htfProps, m_airProps; // Public Design Point Outputs - double m_q_design; //[Wt] Design-point thermal power from the solar field + 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 trough is on sun - double m_Ap_tot; //[m^2] Total field aperture area + double m_W_dot_sca_tracking_nom; // [MWe] Tracking parasitics when trough 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 + 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 // Methods public: @@ -673,6 +681,8 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // ------------------------------------------ supplemental methods ----------------------------------------------------------- + bool design_solar_mult(); + // Methods IN trough void loop_optical_eta(const C_csp_weatherreader::S_outputs& weather, From 7246f37800c7008fde82d5c5561b04c4e9958cba Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Tue, 21 Mar 2023 18:32:53 -0600 Subject: [PATCH 12/46] Add capital costs to cmod calculations. --- ssc/cmod_fresnel_physical.cpp | 1440 +++--- tcs/csp_solver_fresnel_collector_receiver.cpp | 3977 ++++++++--------- tcs/csp_solver_fresnel_collector_receiver.h | 840 ++-- 3 files changed, 3029 insertions(+), 3228 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 28f497fe9..235311107 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -54,415 +54,491 @@ static var_info _cm_vtab_fresnel_physical[] = { // System Design - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult_in", "Solar multiple Input", "", "", "System Control", "", "", ""}, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "total_Ap_in", "Field aperture Input", "m3", "", "System Control", "", "", ""}, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "solar_mult_or_Ap", "Design using specified solar mult or field aperture", "m3", "", "System Control", "", "", ""}, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_in_des", "Design loop inlet temperature", "C", "", "controller", "*", "", ""}, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "T_loop_out", "Target loop outlet temperature", "C", "", "controller", "*", "", ""}, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "I_bn_des", "Solar irradiation at design", "W/m2", "", "controller", "*", "", ""}, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "P_ref", "Design Turbine Net Output", "MWe", "", "powerblock", "*", "", ""}, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "powerblock", "*", "", ""}, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "tshours", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*System Design*/{ SSC_INPUT, SSC_NUMBER, "gross_net_conversion_factor", "Estimated gross to net conversion factor", "", "", "system", "*", "", "" }, + /*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", "Label", "", "", "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", "", "", "controller", "*", "INTEGER", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "eta_pump", "HTF pump efficiency", "", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "HDR_rough", "Header pipe roughness", "m", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_stow", "stow angle", "deg", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "theta_dep", "deploy angle", "deg", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "FieldConfig", "Number of subfield headers", "", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmin", "Minimum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "m_dot_htfmax", "Maximum loop HTF flow rate", "kg/s", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Fluid", "Field HTF fluid number", "", "", "controller", "*", "INTEGER", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_fp", "Freeze protection temperature (heat trace activation temperature)", "C", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_max", "Maximum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_hdr_min", "Minimum HTF velocity in the header at design", "m/s", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "Pipe_hl_coef", "Loss coefficient from the header - runner pipe - and non-HCE piping", "W/m2-K", "", "controller", "*", "", ""}, - /*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", "", "controller", "*", "", ""}, - /*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", "", "controller", "*", "", ""}, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "mc_bal_sca", "Non-HTF heat capacity associated with each SCA - per meter basis", "Wht/K-m", "", "controller", "*", "", ""}, - /*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", "", "controller", "*", "", "" }, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "T_amb_sf_des", "Ambient design-point temperature for the solar field", "C", "", "controller", "*", "", "" }, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "V_wind_des", "Design-point wind velocity", "m/s", "", "controller", "*", "", "" }, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_MATRIX, "field_fl_props", "Fluid property data", "", "", "controller", "*", "", "" }, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "SCA_drives_elec", "Tracking power in Watts per SCA drive", "W/module", "", "controller", "*", "", "" }, - /*X*/ /*Solar Field*/{ SSC_INPUT, SSC_NUMBER, "land_mult", "Non-solar field land area multiplier", "-", "", "controller", "*", "", "" }, - + /*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", "*", "", "" }, // Collector and Receiver - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "ColAz", "Collector azimuth angle", "deg", "", "controller", "*", "", "" }, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "opt_model", "The optical model", "", "", "controller", "*", "INTEGER", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "A_aperture", "Reflective aperture area of the collector", "m2", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "reflectivity", "Solar-weighted mirror reflectivity value", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "TrackingError", "Tracking error derate", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "GeomEffects", "Geometry effects derate", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Dirt_mirror", "User-defined dirt on mirror derate", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "Error", "User-defined general optical error derate", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod", "The length of the collector module", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_T_coefs", "Incidence angle modifier coefficients - transversal plane", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "IAM_L_coefs", "Incidence angle modifier coefficients - longitudinal plane", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "OpticalTable", "Values of the optical efficiency table", "", "", "controller", "*", "", ""}, - - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "rec_model", "Receiver model type (1=Polynomial ; 2=Evac tube)", "", "", "controller", "*", "INTEGER", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HCE_FieldFrac", "The fraction of the field occupied by this HCE type", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_in", "The inner absorber tube diameter", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_abs_out", "The outer absorber tube diameter", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_in", "The inner glass envelope diameter", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_glass_out", "The outer glass envelope diameter", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "D_plug", "The diameter of the absorber flow plug (optional)", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Flow_type", "The flow type through the absorber", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Rough", "Roughness of the internal surface", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_env", "Envelope absorptance", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_1", "Absorber emittance - HCE variation 1", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_2", "Absorber emittance - HCE variation 2", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_3", "Absorber emittance - HCE variation 3", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_MATRIX, "epsilon_abs_4", "Absorber emittance - HCE variation 4", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "alpha_abs", "Absorber absorptance", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Tau_envelope", "Envelope transmittance", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "epsilon_glass", "Glass envelope emissivity", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "GlazingIntactIn", "The glazing intact flag", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "P_a", "Annulus gas pressure", "torr", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AnnulusGas", "Annulus gas type (1=air; 26=Ar; 27=H2)", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "AbsorberMaterial", "Absorber material type", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Shadowing", "Receiver bellows shadowing loss factor", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "dirt_env", "Loss due to dirt on the receiver envelope", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "Design_loss", "Receiver heat loss at design", "W/m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_mod_spacing", "Piping distance between sequential modules in a loop", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "L_crossover", "Length of crossover piping in a loop", "m", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_T_coefs", "HTF temperature-dependent heat loss coefficients", "W/m-K", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "HL_w_coefs", "Wind-speed-dependent heat loss coefficients", "W/m-(m/s)", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "DP_nominal", "Pressure drop across a single collector assembly at design", "bar", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_ARRAY, "DP_coefs", "Pressure drop mass flow based part-load curve", "", "", "controller", "*", "", ""}, - /*X*/ /*Col & Rec*/{ SSC_INPUT, SSC_NUMBER, "nRecVar", "Number of receiver variations", "", "", "controller", "?=4", "INTEGER", "" }, + /*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", "" }, - // System Control - /*Sys Control*/{ SSC_INPUT, SSC_MATRIX, "weekday_schedule", "12x24 Time of Use Values for week days", "", "", "tou_translator", "*", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_MATRIX, "weekend_schedule", "12x24 Time of Use Values for week end days", "", "", "tou_translator", "*", "", "" }, + // Power Cycle Inputs - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Label", "", "", "controller", "*", "", ""}, - /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "bop_array", "Label", "", "", "controller", "*", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "controller", "*", "", "" }, - - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "is_dispatch", "Allow dispatch optimization?", /*TRUE=1*/ "-", "", "tou", "?=0", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max. no. dispatch optimization iterations", "-", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max. dispatch optimization solve duration", "s", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "-", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "-", "", "tou", "?=0.99", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "tou", "is_dispatch=1", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "tou", "is_dispatch=1", "", "" }, + /*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", "Label", "", "", "Sys_Control", "*", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "bop_array", "Label", "", "", "Sys_Control", "*", "", "" }, + /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "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", "", "" }, - - // Power Cycle Inputs - /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "T_startup", "Power block startup temperature", "C", "", "controller", "*", "", "" }, - /*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", "", "", "controller", "*", "", "" }, - /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "controller", "*", "", "" }, - /*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=trough)", "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", "", "controller", "*", "", "" }, - + // Financials + /*Sys Design*/{SSC_INPUT, SSC_NUMBER, "csp_financial_model", "", "1-8", "", "Financial Model", "?=1", "INTEGER,MIN=0", ""}, - // 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", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_MATRIX, "store_fl_props", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "u_tank", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_pairs", "Label", "", "", "controller", "*", "INTEGER", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "hot_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "tank_max_heat", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "cold_tank_Thtr", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_hot", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "h_tank_min", "Label", "", "", "controller", "*", "", "" }, - /*X*/ /*Storage*/{ SSC_INPUT, SSC_NUMBER, "dt_cold", "Label", "", "", "controller", "*", "", "" }, - - /*???????Storage*/{ SSC_INPUT, SSC_NUMBER, "tanks_in_parallel", "Tanks are in parallel, not in series, with solar field", "-", "", "controller", "*", "", "" }, - /*???????Storage*/{ SSC_INPUT, SSC_NUMBER, "has_hot_tank_bypass", "Bypass valve connects field outlet to cold tank", "-", "", "controller", "*", "", "" }, - /*???????Storage*/{ SSC_INPUT, SSC_NUMBER, "T_tank_hot_inlet_min", "Minimum hot tank htf inlet temperature", "C", "", "controller", "*", "", "" }, - /*???????Storage*/{ SSC_INPUT, SSC_NUMBER, "V_tes_des", "Design-point velocity to size the TES pipe diameters", "m/s", "", "controller", "*", "", "" }, + /*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" }, + /*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","", "SIMULATION_PARAMETER" }, + /*Fin Sol Mode Sing Own*/{ SSC_INPUT, SSC_NUMBER, "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","", "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", "", "SIMULATION_PARAMETER" }, + + + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "SIMULATION_PARAMETER" }, + + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "SIMULATION_PARAMETER" }, + /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "SIMULATION_PARAMETER" }, + + /*??????????????*/{ SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "tou", "?", "", "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", "MWe", "", "System Design Calc", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "total_Ap", "Actual field aperture", "MWe", "", "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", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_cycle_des", "Cycle design HTF mass flow rate", "kg/s", "", "System Design Calc", "*", "", "" }, + { 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", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_cycle_des", "Cycle design HTF mass flow rate", "kg/s", "", "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", "", "", "Receiver", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "loop_therm_eff", "Loop thermal efficiency", "", "", "Receiver", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "loop_eff", "Total loop conversion efficiency", "", "", "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, "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", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_therm_eff", "Loop thermal efficiency", "", "", "Receiver", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "loop_eff", "Total loop conversion efficiency", "", "", "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", "*", "", "" }, // 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", "*", "", "" }, + { 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, "q_dot_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", "*", "", "" }, + { 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, "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", "*", "", "" }, + + // 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", "", "" }, + { 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, "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", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "defocus", "Field optical focus fraction", "", "", "weather", "sim_type=1", "", "" }, // Solar Field (from Trough) - { SSC_OUTPUT, SSC_ARRAY, "Theta_ave", "Field collector solar incidence angle", "deg", "", "solar_field", "sim_type=1", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "CosTh_ave", "Field collector cosine efficiency", "", "", "solar_field", "*", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "IAM_ave", "Field collector incidence angle modifier", "", "", "solar_field", "*", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "RowShadow_ave", "Field collector row shadowing loss", "", "", "solar_field", "*", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "EndLoss_ave", "Field collector optical end loss", "", "", "solar_field", "*", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "dni_costh", "Field collector DNI-cosine product", "W/m2", "", "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, "qinc_costh", "Field thermal power incident after cosine", "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, "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", "", "" }, - - //// Solar Field (from fresnel) - //{ SSC_OUTPUT, SSC_ARRAY, "theta_L", "Field collector incidence angle - longitudinal", "deg", "", "solar_field", "*", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "phi_t", "Field collector incidence angle - transversal", "deg", "", "solar_field", "*", "", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "eta_optical", "Field collector optical efficiency", "", "", "solar_field", "*", "", "" }, - ////{ SSC_OUTPUT, SSC_ARRAY, "EqOptEff", "Field collector and receiver optical efficiency", "", "", "solar_field", "*", "", "" }, - - //{ SSC_OUTPUT, SSC_ARRAY, "sf_def", "Field collector focus fraction", "", "", "mslf", "*", "LENGTH=8760", "" }, - - //{ SSC_OUTPUT, SSC_ARRAY, "q_inc_sf_tot", "Field thermal power incident", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "q_abs_tot", "Field thermal power absorbed", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "q_dump", "Field thermal power dumped", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "q_loss_tot", "Field thermal power receiver loss", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "Pipe_hl", "Field thermal power header pipe losses", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "q_avail", "Field thermal power produced", "MWt", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "q_loss_spec_tot", "Field thermal power avg. receiver loss", "W/m", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "eta_thermal", "Field thermal efficiency", "", "", "mslf", "*", "LENGTH=8760", "" }, - - //{ SSC_OUTPUT, SSC_ARRAY, "E_bal_startup", "Field HTF energy inertial (consumed)", "MWht", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "m_dot_avail", "Field HTF mass flow rate total", "kg/hr", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "m_dot_htf2", "Field HTF mass flow rate loop", "kg/s", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "DP_tot", "Field HTF pressure drop total", "bar", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "T_sys_c", "Field HTF temperature cold header inlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "T_sys_h", "Field HTF temperature hot header outlet", "C", "", "Type250", "*", "LENGTH=8760", "" }, - //{ SSC_OUTPUT, SSC_ARRAY, "t_loop_outlet", "Field HTF temperature loop outlet", "C", "", "mslf", "*", "LENGTH=8760", "" }, - - //{ SSC_OUTPUT, SSC_ARRAY, "q_I", "Incident Radiation", "C", "", "mslf", "*", "LENGTH=8760", "" }, - + //{ SSC_OUTPUT, SSC_ARRAY, "Theta_ave", "Field collector solar incidence angle", "deg", "", "solar_field", "sim_type=1", "", "" }, + { 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, "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", "", "" }, - - { SSC_OUTPUT, SSC_ARRAY, "P_out_net", "Total electric power to grid", "MWe", "", "system", "sim_type=1", "", "" }, + { 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", "", "" }, + { 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", "", "" }, + { 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", "" }, + { 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", "", "" }, + { 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, "solar_multiple_actual", "Actual solar multiple of system", "-", "", "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, "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", "*", "", "" }, @@ -482,21 +558,6 @@ class cm_fresnel_physical : public compute_module void exec() { // Missing Variables - bool is_dispatch_series = false; - - // TES - // No custom TES piping - double init_hot_htf_percent = 30; - double cold_tank_max_heat = 25; - double hot_tank_max_heat = 25; - double tes_pump_coef = 0.15; - - // TOU - // NOT in CMOD inputs - bool is_tod_pc_target_also_pc_max = false; - vector f_turb_tou_periods{ 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - size_t n_f_turbine = f_turb_tou_periods.size(); - int csp_financial_model = 8; // Field // Hard Coded (currently no UI) @@ -506,7 +567,6 @@ class cm_fresnel_physical : public compute_module 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; @@ -573,12 +633,7 @@ class cm_fresnel_physical : public compute_module 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_fthrok = as_integer("fthrok"); - //c_fresnel.m_fthrctrl = as_integer("fthrctrl"); c_fresnel.m_ColAz = as_number("ColAz"); - //c_fresnel.m_ColTilt = as_number("tilt"); - - c_fresnel.m_mc_bal_hot = as_number("mc_bal_hot"); c_fresnel.m_mc_bal_cold = as_number("mc_bal_cold"); @@ -586,7 +641,6 @@ class cm_fresnel_physical : public compute_module 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"); @@ -603,20 +657,12 @@ class cm_fresnel_physical : public compute_module 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"); @@ -624,74 +670,48 @@ class cm_fresnel_physical : public compute_module 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 = L_rnr_pb; - - //////////////////////// Questionable 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_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_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); @@ -748,7 +768,7 @@ class cm_fresnel_physical : public compute_module int pb_tech_type = as_integer("pc_config"); if (!(pb_tech_type == 0 || pb_tech_type == 1)) // 0 = Rankine, 1 = UDPC { - throw exec_error("trough_physical", "unsupported power cycle"); + throw exec_error("fresnel_physical", "unsupported power cycle"); } else { @@ -775,8 +795,8 @@ class cm_fresnel_physical : public compute_module 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 trough - if (pc->m_tech_type == 3) { pc->m_tech_type = 8; }; // changing sliding pressure for the tower to sliding pressure for the trough + 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." @@ -834,7 +854,7 @@ class cm_fresnel_physical : public compute_module // 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"), @@ -848,21 +868,21 @@ class cm_fresnel_physical : public compute_module as_double("u_tank"), as_integer("tank_pairs"), as_double("hot_tank_Thtr"), - hot_tank_max_heat, + as_double("hot_tank_max_heat"), as_double("cold_tank_Thtr"), - cold_tank_max_heat, + 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"), - init_hot_htf_percent, + as_double("init_hot_htf_percent"), as_double("pb_pump_coef"), as_boolean("tanks_in_parallel"), - as_double("V_tes_des"), + V_tes_des, false, - tes_pump_coef + as_double("tes_pump_coef") ); //as_boolean("calc_design_pipe_vals"), //as_double("tes_pump_coef"), @@ -898,9 +918,8 @@ class cm_fresnel_physical : public compute_module C_csp_tou_block_schedules tou; C_csp_tou_block_schedules::S_params* tou_params = &tou.ms_params; double ppa_price_year1 = std::numeric_limits::quiet_NaN(); + int csp_financial_model = as_integer("csp_financial_model"); { - - tou_params->mc_csp_ops.mc_weekdays = as_matrix("weekday_schedule"); tou_params->mc_csp_ops.mc_weekends = as_matrix("weekend_schedule"); if (tou_params->mc_pricing.mc_weekdays.ncells() == 1) { @@ -912,7 +931,7 @@ class cm_fresnel_physical : public compute_module tou_params->mc_pricing.mc_weekends = util::matrix_t(12, 24, 1.0); } - tou.mc_dispatch_params.m_is_tod_pc_target_also_pc_max = is_tod_pc_target_also_pc_max; + 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; @@ -921,7 +940,8 @@ class cm_fresnel_physical : public compute_module 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); + vector f_turb_tou_periods = as_vector_double("f_turb_tou_periods"); + size_t n_f_turbine = f_turb_tou_periods.size(); ssc_number_t* p_f_turbine = &f_turb_tou_periods[0]; 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); @@ -946,7 +966,7 @@ class cm_fresnel_physical : public compute_module // 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("trough_physical", "\n\nYou selected dispatch optimization which requires that the array input ppa_price_input is defined\n"); + 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) { @@ -960,7 +980,7 @@ class cm_fresnel_physical : public compute_module 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("trough_physical", "\n\nYou selected dispatch optimization and the Specify IRR Target financial solution mode, " + 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"); @@ -968,7 +988,7 @@ class cm_fresnel_physical : public compute_module 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("trough_physical", "\n\nYou selected dispatch optimization and the option to Use Retail Electricity Rates on the Electricity Purchases page, " + 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"); } @@ -1024,7 +1044,7 @@ class cm_fresnel_physical : public compute_module } else if (csp_financial_model == 5) { // Commercial if (is_dispatch) { - throw exec_error("trough_physical", "\nDispatch optimization current not enabled for the Commercial financial model\n"); + 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) {} @@ -1043,7 +1063,7 @@ class cm_fresnel_physical : public compute_module size_t n_rows = mp_energy_market_revenue.nrows(); if (n_rows < n_steps_fixed) { 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("trough_physical", ppa_msg); + throw exec_error("fresnel_physical", ppa_msg); } double conv_dolmwh_to_centkwh = 0.1; @@ -1066,7 +1086,7 @@ class cm_fresnel_physical : public compute_module tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, 1.0); } else { - throw exec_error("trough_physical", "csp_financial_model must be 1-8"); + throw exec_error("fresnel_physical", "csp_financial_model must be 1-8"); } } else if (sim_type == 2) @@ -1084,12 +1104,12 @@ class cm_fresnel_physical : public compute_module 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("trough_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"); + 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 @@ -1105,7 +1125,7 @@ class cm_fresnel_physical : public compute_module 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(as_boolean("is_dispatch"), as_integer("disp_steps_per_hour"), as_integer("disp_frequency"), as_integer("disp_horizon"), + 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")); @@ -1242,7 +1262,7 @@ class cm_fresnel_physical : public compute_module } //if the pricing schedule is provided as hourly, overwrite the tou schedule - if(is_dispatch_series) + if(as_boolean("is_dispatch_series")) { size_t n_dispatch_series; ssc_number_t* dispatch_series = as_array("dispatch_series", &n_dispatch_series); @@ -1263,7 +1283,10 @@ class cm_fresnel_physical : public compute_module tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][i] = dispatch_series[i]; } - // Design point is complete, assign design outputs + // 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"); //[-] @@ -1272,7 +1295,7 @@ class cm_fresnel_physical : public compute_module double solar_mult_des = c_fresnel.m_solar_mult; double q_dot_pc_des = W_dot_cycle_des / eta_ref; //[MWt] - double Q_tes = q_dot_pc_des * tshours; //[MWt-hr] + Q_tes = q_dot_pc_des * tshours; //[MWt-hr] double m_dot_htf_cycle_des = c_fresnel.m_m_dot_design; // [kg/s] @@ -1294,10 +1317,10 @@ class cm_fresnel_physical : public compute_module double field_area = A_field / 4046.85642; // [acres] (convert m2 to acre) double land_mult = as_double("land_mult"); - double total_land_area = field_area * land_mult; // [acres] + total_land_area = field_area * land_mult; // [acres] - double field_htf_min_temp = c_fresnel.m_htfProps.min_temp(); - double field_htf_max_temp = c_fresnel.m_htfProps.max_temp(); + 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] // Assign { @@ -1361,7 +1384,7 @@ class cm_fresnel_physical : public compute_module } // 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] @@ -1393,7 +1416,7 @@ class cm_fresnel_physical : public compute_module 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"); - double nameplate_des = W_dot_cycle_des * gross_net_conversion_des; + nameplate_des = W_dot_cycle_des * gross_net_conversion_des; // Assign { @@ -1406,8 +1429,81 @@ class cm_fresnel_physical : public compute_module assign("nLoops", c_fresnel.m_nLoops); assign("total_Ap", c_fresnel.m_Ap_tot); } - + + } + + // 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 + 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) @@ -1430,9 +1526,12 @@ class cm_fresnel_physical : public compute_module log(out_msg); } - throw exec_error("trough_physical", csp_exception.m_error_message); + throw exec_error("fresnel_physical", csp_exception.m_error_message); } + // DEBUG + vector call_per_step = c_fresnel.call_per_step; + // If no exception, then report messages while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) { @@ -1440,194 +1539,9 @@ class cm_fresnel_physical : public compute_module } - // DEBUG - if (false) - { - size_t size; - ssc_number_t* x; - - x = as_array("Theta_ave", &size); - vector Theta_ave_vec(x, x + size); - //delete x; - - x = as_array("CosTh_ave", &size); - vector CosTh_ave_vec(x, x + size); - //delete x; - - x = as_array("IAM_ave", &size); - vector IAM_ave_vec(x, x + size); - //delete x; - - x = as_array("RowShadow_ave", &size); - vector RowShadow_ave_vec(x, x + size); - //delete x; - - x = as_array("EndLoss_ave", &size); - vector EndLoss_ave_vec(x, x + size); - //delete x; - - x = as_array("dni_costh", &size); - vector dni_costh_vec(x, x + size); - //delete x; - - x = as_array("EqOpteff", &size); - vector EqOpteff_vec(x, x + size); - //delete x; - - x = as_array("SCAs_def", &size); - vector SCAs_def_vec(x, x + size); - //delete x; - - - /*x = as_array("q_inc_sf_tot", &size); - vector q_inc_sf_tot_vec(x, x + size); - delete x;*/ - - x = as_array("qinc_costh", &size); - vector qinc_costh_vec(x, x + size); - //delete x; - x = as_array("q_dot_rec_inc", &size); - vector q_dot_rec_inc_vec(x, x + size); - //delete x; - x = as_array("q_dot_rec_thermal_loss", &size); - vector q_dot_rec_thermal_loss_vec(x, x + size); - //delete x; - x = as_array("q_dot_rec_abs", &size); - vector q_dot_rec_abs_vec(x, x + size); - //delete x; - x = as_array("q_dot_piping_loss", &size); - vector q_dot_piping_loss_vec(x, x + size); - //delete x; - x = as_array("e_dot_field_int_energy", &size); - vector e_dot_field_int_energy_vec(x, x + size); - //delete x; - x = as_array("q_dot_htf_sf_out", &size); - vector q_dot_htf_sf_out_vec(x, x + size); - //delete x; - x = as_array("q_dot_freeze_prot", &size); - vector q_dot_freeze_prot_vec(x, x + size); - //delete x; - x = as_array("m_dot_loop", &size); - vector m_dot_loop_vec(x, x + size); - //delete x; - x = as_array("recirculating", &size); - vector recirculating_vec(x, x + size); - //delete x; - x = as_array("m_dot_field_recirc", &size); - vector m_dot_field_recirc_vec(x, x + size); - //delete x; - x = as_array("m_dot_field_delivered", &size); - vector m_dot_field_delivered_vec(x, x + size); - //delete x; - x = as_array("T_field_cold_in", &size); - vector T_field_cold_in_vec(x, x + size); - //delete x; - x = as_array("T_rec_cold_in", &size); - vector T_rec_cold_in_vec(x, x + size); - //delete x; - x = as_array("T_rec_hot_out", &size); - vector T_rec_hot_out_vec(x, x + size); - //delete x; - x = as_array("T_field_hot_out", &size); - vector T_field_hot_out_vec(x, x + size); - //delete x; - x = as_array("deltaP_field", &size); - vector deltaP_field_vec(x, x + size); - //delete x; - x = as_array("W_dot_sca_track", &size); - vector W_dot_sca_track_vec(x, x + size); - //delete x; - x = as_array("W_dot_field_pump", &size); - vector W_dot_field_pump_vec(x, x + size); - //delete x; - - - // Fresnel - //x = as_array("theta_L", &size); - //vector theta_L_vec(x, x + size); - ////delete x; - - //x = as_array("phi_t", &size); - //vector phi_t_vec(x, x + size); - ////delete x; - - //x = as_array("eta_optical", &size); - //vector eta_optical_vec(x, x + size); - ////delete x; - - ////x = as_array("EqOptEff", &size); - ////vector EqOptEff_vec(x, x + size); - ////delete x; - //auto eq = EqOpteff_vec; - - //x = as_array("sf_def", &size); - //vector sf_def_vec(x, x + size); - ////delete x; - - //x = as_array("q_inc_sf_tot", &size); - //vector q_inc_sf_tot_vec(x, x + size); - ////delete x; - //x = as_array("q_abs_tot", &size); - //vector q_abs_tot_vec(x, x + size); - ////delete x; - //x = as_array("q_dump", &size); - //vector q_dump_vec(x, x + size); - ////delete x; - //x = as_array("q_loss_tot", &size); - //vector q_loss_tot_vec(x, x + size); - ////delete x; - //x = as_array("Pipe_hl", &size); - //vector Pipe_hl_vec(x, x + size); - ////delete x; - //x = as_array("q_avail", &size); - //vector q_avail_vec(x, x + size); - ////delete x; - //x = as_array("q_loss_spec_tot", &size); - //vector q_loss_spec_tot_vec(x, x + size); - ////delete x; - //x = as_array("eta_thermal", &size); - //vector eta_thermal_vec(x, x + size); - ////delete x; - //x = as_array("E_bal_startup", &size); - //vector E_bal_startup_vec(x, x + size); - ////delete x; - //x = as_array("m_dot_avail", &size); - //vector m_dot_avail_vec(x, x + size); - ////delete x; - //x = as_array("m_dot_htf2", &size); - //vector m_dot_htf2_vec(x, x + size); - ////delete x; - //x = as_array("DP_tot", &size); - //vector DP_tot_vec(x, x + size); - ////delete x; - //x = as_array("T_sys_c", &size); - //vector T_sys_c_vec(x, x + size); - ////delete x; - //x = as_array("T_sys_h", &size); - //vector T_sys_h_vec(x, x + size); - ////delete x; - //x = as_array("t_loop_outlet", &size); - //vector t_loop_outlet_vec(x, x + size); - ////delete x; - - // Actual Outputs - x = as_array("time_hr", &size); - vector time_hr_vec(x, x + size); - - x = as_array("P_out_net", &size); - vector P_out_net_vec(x, x + size); - - /*x = as_array("q_I", &size); - vector q_I_vec(x, x + size);*/ - - int y = 0; - } - - 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); - //assign("solar_multiple_actual", solar_mult); // calculated during verify() using cmod_csp_trough_eqns.cpp // Do unit post-processing here double* p_q_pc_startup = allocate("q_pc_startup", n_steps_fixed); @@ -1670,12 +1584,12 @@ class cm_fresnel_physical : public compute_module 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("trough_physical", "The number of fixed steps does not match the length of output data arrays"); + 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("trough_physical", "failed to setup adjustment factors: " + haf.error()); + 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); @@ -1687,11 +1601,11 @@ class cm_fresnel_physical : public compute_module ssc_number_t* p_SCAs_def = as_array("SCAs_def", &count); if ((int)count != n_steps_fixed) - throw exec_error("trough_physical", "The number of fixed steps does not match the length of output data arrays2"); + 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("trough_physical", "The number of fixed steps does not match the length of output data arrays3"); + 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) @@ -1844,6 +1758,250 @@ class cm_fresnel_physical : public compute_module } + + + + static 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, // 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 + + + + ) + { + // Outputs + + // csp.mslf.cost.power_plant + double power_plant = calc_power_plant(power_plant_mwe, power_plant_cost_per_kwe); + + // csp.mslf.cost.ts + double ts = calc_ts(ts_mwht, ts_per_kwht); + + // csp.mslf.cost.site_improvements + double site_improvements = calc_site_improvements(site_improvement_area, site_improvement_cost_per_m2); + + // csp.mslf.cost.bop + double bop = calc_bop(bop_mwe, bop_per_kwe); + + // csp.mslf.cost.solar_field + double solar_field = calc_solar_field(sf_area, sf_cost_per_m2); + + // csp.mslf.cost.htf_system + double htf_system = calc_htf_system(htf_area, htf_cost_per_m2); + + // csp.mslf.cost.fossil_backup + double fossil_backup = calc_fossil_backup(fossil_mwe, fossil_cost_per_kwe); + + // csp.mslf.cost.contingency + double contingency = calc_contingency(contigency_percent, site_improvements, solar_field, + htf_system, fossil_backup, power_plant, bop, ts); + + // csp.mslf.cost.total_direct + double total_direct = calc_total_direct(contingency, site_improvements, solar_field, htf_system, + fossil_backup, power_plant, bop, ts); + + // csp.mslf.cost.epc.total + double epc_total = calc_epc_total(epc_per_acre, total_land_area, epc_percent, total_direct, + nameplate, epc_per_watt, epc_fixed); + + // csp.mslf.cost.plm.total + double plm_total = calc_plm_total(plm_per_acre, total_land_area, plm_percent, total_direct, + nameplate, plm_per_watt, plm_fixed); + + // csp.mslf.cost.total_indirect + double total_indirect = calc_total_indirect(epc_total, plm_total); + + // csp.mslf.cost.sales_tax.total + double sales_tax_total = calc_sales_tax_total(sales_tax_value, total_direct, sales_tax_percent); + + // csp.mslf.cost.total_installed + double total_installed = calc_total_installed(total_direct, total_indirect, sales_tax_total); + + // csp.mslf.cost.installed_per_capacity + double installed_per_cap = calc_installed_per_cap(total_installed, nameplate); + + // 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; + } + } + + // csp.mslf.cost.power_plant + static double calc_power_plant(double power_plant_mwe, double power_plant_cost_per_kwe) + { + return power_plant_mwe * power_plant_cost_per_kwe * 1000.0; + } + + // csp.mslf.cost.ts + static double calc_ts(double ts_mwht, double ts_per_kwht) + { + return ts_mwht * ts_per_kwht * 1000.0; + } + + // csp.mslf.cost.site_improvements + static double calc_site_improvements(double site_improvement_area, double site_improvement_cost_per_m2) + { + return site_improvement_area * site_improvement_cost_per_m2; + } + + // csp.mslf.cost.bop + static double calc_bop(double bop_mwe, double bop_per_kwe) + { + return bop_mwe * bop_per_kwe * 1000.0; + } + + // csp.mslf.cost.solar_field + static double calc_solar_field(double sf_area, double sf_cost_per_m2) + { + return sf_area * sf_cost_per_m2; + } + + // csp.mslf.cost.htf_system + static double calc_htf_system(double htf_area, double htf_cost_per_m2) + { + return htf_area * htf_cost_per_m2; + } + + // csp.mslf.cost.fossil_backup + static double calc_fossil_backup(double fossil_mwe, double fossil_cost_per_kwe) + { + return fossil_mwe * fossil_cost_per_kwe * 1000.0; + } + + // csp.mslf.cost.contingency + static double calc_contingency(double contingency_percent, double site_improvements, double solar_field, + double htf_system, double fossil_backup, double power_plant, + double bop, double ts) + { + double sum = site_improvements + solar_field + htf_system + fossil_backup + power_plant + bop + ts; + + return (contingency_percent / 100.0) * sum; + } + + // csp.mslf.cost.total_direct + static double calc_total_direct(double contingency, double site_improvements, double solar_field, + double htf_system, double fossil_backup, double power_plant, + double bop, double ts) + { + return contingency + site_improvements + solar_field + htf_system + fossil_backup + power_plant + bop + ts; + } + + // csp.mslf.cost.epc.total + static double calc_epc_total(double epc_per_acre, double total_land_area, double epc_percent, + double total_direct, double nameplate, double epc_per_watt, + double epc_fixed) + { + double epc = (epc_per_acre * total_land_area) + + ((epc_percent / 100.0) * total_direct) + + (nameplate * 1e6 * epc_per_watt) + + epc_fixed; + + return epc; + } + + // csp.mslf.cost.plm.total + static double calc_plm_total(double plm_per_acre, double total_land_area, double plm_percent, double total_direct, + double nameplate, double plm_per_watt, double plm_fixed) + { + double plm = (plm_per_acre * total_land_area) + + ((plm_percent / 100.0) * total_direct) + + (nameplate * 1e6 * plm_per_watt) + + plm_fixed; + + return plm; + } + + // csp.mslf.cost.total_indirect + static double calc_total_indirect(double epc_total, double plm_total) + { + return epc_total + plm_total; + } + + // csp.mslf.cost.sales_tax.total + static double calc_sales_tax_total(double sales_tax_value, double total_direct_cost, double sales_tax_percent) + { + return (sales_tax_value / 100.0) * total_direct_cost * (sales_tax_percent / 100.0); + } + + // csp.mslf.cost.total_installed + static double calc_total_installed(double total_direct, double total_indirect, double sales_tax_total) + { + return total_direct + total_indirect + sales_tax_total; + } + + // csp.mslf.cost.installed_per_capacity + static double calc_installed_per_cap(double total_installed, double nameplate) + { + return 0.001 * total_installed * nameplate; + } + + + + + + }; DEFINE_MODULE_ENTRY(fresnel_physical, "Physical Fresnel applications", 1) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 502077cb0..ab310cbcf 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -5,12 +5,11 @@ using namespace std; static C_csp_reported_outputs::S_output_info S_output_info[] = { - {C_csp_fresnel_collector_receiver::E_THETA_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_COSTH_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + /*{C_csp_fresnel_collector_receiver::E_COSTH_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_IAM_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_ROWSHADOW_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_ENDLOSS_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_DNI_COSTH, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_DNI_COSTH, C_csp_reported_outputs::TS_WEIGHTED_AVE},*/ {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}, @@ -37,10 +36,9 @@ static C_csp_reported_outputs::S_output_info S_output_info[] = {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}, - {C_csp_fresnel_collector_receiver::E_THETA_L, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + /*{C_csp_fresnel_collector_receiver::E_THETA_L, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_PHI_T, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_ETA_OPTICAL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - //{C_csp_fresnel_collector_receiver::E_EQ_OPT_EFF, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_SF_DEF, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_Q_INC_SF_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, @@ -58,7 +56,7 @@ static C_csp_reported_outputs::S_output_info S_output_info[] = {C_csp_fresnel_collector_receiver::E_T_SYS_C, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_T_SYS_H, C_csp_reported_outputs::TS_WEIGHTED_AVE}, {C_csp_fresnel_collector_receiver::E_T_LOOP_OUTLET, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_Q_I, C_csp_reported_outputs::TS_WEIGHTED_AVE}, + {C_csp_fresnel_collector_receiver::E_Q_I, C_csp_reported_outputs::TS_WEIGHTED_AVE},*/ csp_info_invalid }; @@ -98,12 +96,12 @@ int C_csp_fresnel_collector_receiver::freeze_protection(const C_csp_weatherreade } catch (C_csp_exception) { - throw(C_csp_exception("C_csp_trough_collector::off - freeze protection failed")); + 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_trough_collector::off - freeze protection failed to converge")); + throw(C_csp_exception("C_csp_fresnel_collector::off - freeze protection failed to converge")); } T_cold_in = T_cold_in_solved; //[K] @@ -130,8 +128,6 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double 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) { @@ -282,7 +278,7 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double void C_csp_fresnel_collector_receiver::set_output_value() { - mc_reported_outputs.value(E_THETA_AVE, m_Theta_ave * m_r2d); //[deg], convert from rad + //mc_reported_outputs.value(E_THETA_AVE, m_Theta_ave * m_r2d); //[deg], convert from rad //mc_reported_outputs.value(E_COSTH_AVE, m_CosTh_ave); //[-] //mc_reported_outputs.value(E_IAM_AVE, m_IAM_ave); //[-] //mc_reported_outputs.value(E_ROWSHADOW_AVE, m_RowShadow_ave); //[-] @@ -291,7 +287,7 @@ 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_INC_SF_TOT, m_q_dot_inc_sf_tot); //[MWt] //mc_reported_outputs.value(E_Q_DOT_INC_SF_COSTH, m_dni_costh * m_Ap_tot / 1.E6); //[MWt] mc_reported_outputs.value(E_Q_DOT_REC_INC, m_q_dot_sca_abs_summed_fullts + m_q_dot_sca_loss_summed_fullts); //[MWt] @@ -369,9 +365,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle double T_htf_cold_in /*K*/, double m_dot_htf_loop /*kg/s*/, const C_csp_solver_sim_info& sim_info) { - - counter++; // DEBUG - + // Calculate total field mass flow rate m_m_dot_htf_tot = m_dot_htf_loop * float(m_nLoops); // Helpful Variables @@ -397,19 +391,18 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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 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 values is the Bulk Temperature at the *end* of the timestep (type 262 line 1551; trough line 1187) - //T_sys_c = (T_sys_c_last - T_cold_in_1) * exp(-(m_dot_htf * float(nLoops)) / (v_cold * rho_hdr_cold + mc_bal_cold / c_hdr_cold_last) * dt) + T_cold_in_1; 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 fully from trough (line 1189) + // 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; @@ -489,7 +482,6 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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); @@ -500,15 +492,14 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle std::vector EqOpteffs(m_nMod, 0.); // MAIN SCA Temperature Solve - // Loop through each Module 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 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) @@ -522,26 +513,13 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle //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; - if (weather.m_beam > 300) - int x = 0; - double c_htf_j, rho_htf_j; - //EvacReceiver(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, m_ncall, sim_info.ms_ts.m_time / 3600.0, - // //outputs - // m_q_loss[j], m_q_abs[j], m_q_1abs[j], c_htf_j, rho_htf_j, mv_HCEguessargs); - - 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, m_ncall, sim_info.ms_ts.m_time / 3600.0, m_ColOptEff, + 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); - - - - - // Check for NaN if (m_q_abs[j] != m_q_abs[j]) { @@ -561,8 +539,8 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle } - 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_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 @@ -587,37 +565,20 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle // Original Fresnel Eqn { - //MJW 12.14.2010 The first term should represent the difference between the previous average temperature and the new - //average temperature. Thus, the heat addition in the first term should be divided by 2 rather than include the whole magnitude - //of the heat addition. - //mjw & tn 5.1.11: There was an error in the assumption about average and outlet temperature - double T_htf_out = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + - 2.0 * (m_TCS_T_htf_ave_converged[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (2.0 * m_dot_htf_loop * c_htf_i)) * - exp(-2. * m_dot_htf_loop * c_htf_i * dt / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)); - //Recalculate the average temperature for the SCA - double T_htf_ave = (m_T_htf_in_t_int[i] + T_htf_out) / 2.0; + ////MJW 12.14.2010 The first term should represent the difference between the previous average temperature and the new + ////average temperature. Thus, the heat addition in the first term should be divided by 2 rather than include the whole magnitude + ////of the heat addition. + ////mjw & tn 5.1.11: There was an error in the assumption about average and outlet temperature + //double T_htf_out = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + + // 2.0 * (m_TCS_T_htf_ave_converged[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (2.0 * m_dot_htf_loop * c_htf_i)) * + // exp(-2. * m_dot_htf_loop * c_htf_i * dt / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)); + ////Recalculate the average temperature for the SCA + //double T_htf_ave = (m_T_htf_in_t_int[i] + T_htf_out) / 2.0; } //Calculate the mass of HTF associated with this node m_node = rho_htf_i * m_A_cs[0] * m_L_mod; - // DEBUG - - //double new_end = 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)); - - //double new_int = 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; - - /*double old = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + - 2.0 * (m_TCS_T_htf_ave_converged[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (2.0 * m_dot_htf_loop * c_htf_i)) * - exp(-2. * m_dot_htf_loop * c_htf_i * dt / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod));*/ - - - break; } @@ -626,7 +587,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle { double V_wind = weather.m_wspd; double dt = sim_info.ms_ts.m_step; - T_node_ave = m_TCS_T_htf_ave_converged[i]; + T_node_ave = m_T_htf_out_t_int[i]; //iterate to improve heat loss estimate double errhl = 999.; @@ -641,6 +602,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle // 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)); @@ -650,26 +612,18 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle (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; - //Recalculate the average temperature for the SCA - m_TCS_T_htf_ave_converged[i] = (m_TCS_T_htf_ave_converged[i] + m_T_htf_out_t_end[i]) / 2.0; - - errhl = T_node_ave - m_TCS_T_htf_ave_converged[i]; //iterate until the node temperature does not vary significantly + errhl = T_node_ave - m_T_htf_out_t_int[i]; //iterate until the node temperature does not vary significantly - T_node_ave = m_TCS_T_htf_ave_converged[i]; + 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) @@ -691,8 +645,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle //E_sca_abs[i] = m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step / 1.E6; //E_sca_bal[i] = E_sca_abs[i] - E_sca_htf[i] - E_sca[i]; - //Set the inlet temperature of the next SCA equal to the outlet temperature of the current SCA - //minus the heat losses in intermediate piping + //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. @@ -705,12 +658,12 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle } //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] * pi * L_int * (m_T_htf_out_t_int[i] - T_db) + 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); //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. * pi; + 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); @@ -722,48 +675,11 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle } - } //Set the loop outlet temperature double T_loop_outX = m_T_htf_out_t_end[m_nMod - 1]; - // DEBUGGING - if (true) - { - // Variables to compare - vector ColOptEff_ = m_ColOptEff.to_vector(); - double q_abs_abs_total_ = q_abs_abs_total; // [W] absorbed by absorber - - vector m_q_SCA_ = m_q_SCA; - vector m_q_abs_SCAtot_ = m_q_abs_SCAtot; - vector m_q_loss_SCAtot_ = m_q_loss_SCAtot; - vector m_q_1abs_tot_ = m_q_1abs_tot; - - double T_htf_cold_in_ = T_htf_cold_in; - double m_dot_htf_loop_ = m_dot_htf_loop; - - double current_time = sim_info.ms_ts.m_time; - - int old_day = m_current_day; - int old_hr = m_current_hr; - - m_current_day = (int)(sim_info.ms_ts.m_time / 3600.0) / 24; - m_current_hr = (int)(sim_info.ms_ts.m_time / 3600.0) % 24; - - if (m_current_day == 51 && m_current_hr == 15) - int x = 0; - - // Step by Hour - if (old_hr != m_current_hr) - int x = 0; - - // Step by Day - if (old_day != m_current_day) - int x = 0; - - } - // Initialize double q_dot_loss_HR_hot = 0.0; //[W] double E_HR_hot = 0.0; //[MJ] @@ -771,8 +687,6 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle double E_HR_hot_losses = 0.0; //[MJ] double E_HR_hot_bal = 0.0; //[MJ] - - //Calculation for heat losses from hot piping //Header { @@ -808,7 +722,6 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle m_Runner_hl_hot_tot += m_Runner_hl_hot; D_index++; } - } 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); @@ -828,7 +741,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle / sim_info.ms_ts.m_step; } - // BONUS trough Calculations + // 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] @@ -866,9 +779,9 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle // 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] + 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, 5) * 1000.0; //[J/kg-K] @@ -880,13 +793,13 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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 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] + double E_scas_htf_summed = 0.0; //[MJ] + double E_xovers_htf_summed = 0.0; //[MJ] for (int i = 0; i < m_nMod; i++) { @@ -896,24 +809,24 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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_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 + 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] + 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 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] + 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] } @@ -960,12 +873,12 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n //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 * pi))); + 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 * pi))); + 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); } } @@ -980,9 +893,9 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n 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 * pi))); + 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 * pi * D_hdr[i] * D_hdr[i] / 4.; + 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 @@ -1007,7 +920,7 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n 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] * mtoinch); + 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 @@ -1019,7 +932,7 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n 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] * mtoinch, nd); + 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) @@ -1027,2377 +940,2131 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n } +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(); -// ------------------------------------------------------------------------------ PUBLIC + //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] -C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() -{ - mc_reported_outputs.construct(S_output_info); + //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; - // 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] + // 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_W_dot_sca_tracking_nom = std::numeric_limits::quiet_NaN(); + 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; - // set initial values for all parameters to prevent possible misuse - m_nMod = -1; - m_nRecVar = -1; - m_nLoops = -1; - m_FieldConfig = -1; - //m_include_fixed_power_block_runner = true; - //m_L_power_block_piping = std::numeric_limits::quiet_NaN(); - 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_Row_Distance = 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_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; - m_m_dot_design = std::numeric_limits::quiet_NaN(); - m_m_dot_loop_des = std::numeric_limits::quiet_NaN(); + 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; - m_T_fp = std::numeric_limits::quiet_NaN(); - m_I_bn_des = std::numeric_limits::quiet_NaN(); - //m_V_hdr_cold_max = std::numeric_limits::quiet_NaN(); - //m_V_hdr_cold_min = std::numeric_limits::quiet_NaN(); - //m_V_hdr_hot_max = std::numeric_limits::quiet_NaN(); - //m_V_hdr_hot_min = 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_fthrok = -1; - //m_fthrctrl = -1; - //m_ColTilt = std::numeric_limits::quiet_NaN(); - m_ColAz = std::numeric_limits::quiet_NaN(); - //m_wind_stow_speed = std::numeric_limits::quiet_NaN(); + // 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; + } - //m_accept_init = false; - //m_accept_loc = -1; - //m_is_using_input_gen = false; + //// Maximum wind speed value NO max wind speed + //if (V_wind >= m_V_wind_max) + // m_ftrack = 0.0; - //m_custom_sf_pipe_sizes = false; + 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; - 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_hot_per_MW = std::numeric_limits::quiet_NaN(); - //m_mc_bal_cold_per_MW = std::numeric_limits::quiet_NaN(); - m_mc_bal_sca = std::numeric_limits::quiet_NaN(); + // 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)); - //m_defocus = std::numeric_limits::quiet_NaN(); - m_latitude = std::numeric_limits::quiet_NaN(); - m_longitude = std::numeric_limits::quiet_NaN(); - m_TCS_T_sys_h = std::numeric_limits::quiet_NaN(); - m_TCS_T_sys_c = std::numeric_limits::quiet_NaN(); - m_TCS_T_sys_h_converged = std::numeric_limits::quiet_NaN(); - m_TCS_T_sys_c_converged = std::numeric_limits::quiet_NaN(); + 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; + } - // ************************************************************************ - // 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] + m_eta_optical *= m_ftrack; + } + else { + m_eta_optical = 0.0; + m_phi_t = CSP::pi / 2.; + m_theta_L = 0.0; + } - // 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] + 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 - // 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] + //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 + } - 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] + // 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_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_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_control_defocus = m_component_defocus = 1.0; //[-] - 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_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_q_dot_inc_sf_tot = m_Ap_tot * weather.m_beam / 1.E6; //[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; +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_W_dot_sca_tracking = std::numeric_limits::quiet_NaN(); //[MWe] + 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_EqOpteff = std::numeric_limits::quiet_NaN(); - m_m_dot_htf_tot = std::numeric_limits::quiet_NaN(); - m_Theta_ave = std::numeric_limits::quiet_NaN(); - //m_CosTh_ave = std::numeric_limits::quiet_NaN(); - //m_IAM_ave = std::numeric_limits::quiet_NaN(); - //m_RowShadow_ave = std::numeric_limits::quiet_NaN(); - //m_EndLoss_ave = std::numeric_limits::quiet_NaN(); - //m_dni_costh = std::numeric_limits::quiet_NaN(); - m_c_htf_ave = std::numeric_limits::quiet_NaN(); + m_W_dot_sca_tracking = 0.0; //[MWe] - m_control_defocus = std::numeric_limits::quiet_NaN(); - m_component_defocus = std::numeric_limits::quiet_NaN(); + m_control_defocus = 1.0; + m_component_defocus = 1.0; - m_q_dot_inc_sf_tot = std::numeric_limits::quiet_NaN(); + m_q_dot_inc_sf_tot = 0.0; //[MWt] - /*for (int i = 0; i < 5; i++) - m_T_save[i] = std::numeric_limits::quiet_NaN();*/ + m_eta_optical = 0; - /*mv_reguess_args.resize(3); - std::fill(mv_reguess_args.begin(), mv_reguess_args.end(), std::numeric_limits::quiet_NaN());*/ + 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 - m_AnnulusGasMat.fill(NULL); - m_AbsorberPropMat.fill(NULL); + loop_optical_eta_off(); - mv_HCEguessargs.resize(3); - std::fill(mv_HCEguessargs.begin(), mv_HCEguessargs.end(), std::numeric_limits::quiet_NaN()); + m_W_dot_sca_tracking = m_W_dot_sca_tracking_nom; //[MWe] + + m_component_defocus = 0.0; } -C_csp_fresnel_collector_receiver::~C_csp_fresnel_collector_receiver() +void C_csp_fresnel_collector_receiver::update_last_temps() { - 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); - } + // 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::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-- +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] + } +} - Do any setup required here. - Get the values of the inputs and parameters - */ +void C_csp_fresnel_collector_receiver::apply_control_defocus(double defocus) +{ + // Uses m_q_i, m_costh, and input defocus to calculate m_q_SCA_control_df - //Initialize air properties -- used in reeiver calcs - m_airProps.SetFluid(HTFProperties::Air); + // Store control defocus + m_control_defocus = defocus; - // Save init_inputs to member data + for (int i = 0; i < m_nMod; i++) { - 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] + m_q_SCA_control_df[i] = defocus * m_q_i; } - // 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.. +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++) { - /* - 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(); + //int CT = (int)m_SCAInfoArray(i, 1) - 1; // Collector type + m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; + } +} - double* xax = new double[ncol_OpticalTable - 1]; - double* yax = new double[nrow_OpticalTable - 1]; - double* data = new double[(ncol_OpticalTable - 1) * (nrow_OpticalTable - 1)]; +double C_csp_fresnel_collector_receiver::Pump_SGS(double rho, double m_dotsf, double sm) { - //get the xaxis data values - for (int i = 1; i < ncol_OpticalTable; i++) { - xax[i - 1] = m_OpticalTable.at(0, i) * d2r; - } - //get the yaxis data values - for (int j = 1; j < nrow_OpticalTable; j++) { - yax[j - 1] = m_OpticalTable.at(j, 0) * 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); - } - } + 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]; - optical_table.AddXAxis(xax, ncol_OpticalTable - 1); - optical_table.AddYAxis(yax, nrow_OpticalTable - 1); - optical_table.AddData(data); - delete[] xax; - delete[] yax; - delete[] data; + //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]; } - // Adjust parameters - //m_ColTilt = m_ColTilt * m_d2r; //[rad] Collector tilt angle (0 is horizontal, 90deg is vertical), convert from [deg] - m_ColAz = m_ColAz * m_d2r; //[rad] Collector azimuth angle, convert from [deg] + delete[] V_dot; + delete[] D; + delete[] V; - // Check m_IAM matrix against number of collectors: m_nColt - // **** Don't need to because only one type of collector - //m_n_r_iam_matrix = (int)m_IAM_matrix.nrows(); - //m_n_c_iam_matrix = (int)m_IAM_matrix.ncols(); + return psum; - // Check Inputs - { - /*if (m_n_c_iam_matrix < 3) - { - throw(C_csp_exception("There must be at least 3 incident angle modifier coefficients", "Trough collector solver")); - }*/ +} - /*if (m_n_r_iam_matrix < m_nColt) - { - m_error_msg = util::format("The number of groups of m_IAM coefficients (%d) is less than the number of collector types in this simulation (%d)", m_n_r_iam_matrix, m_nColt); - throw(C_csp_exception(m_error_msg, "Trough collector solver")); - }*/ - - //// Check that for each collector, at least 3 coefficients are != 0.0 - //for (int i = 0; i < m_nColt; i++) - //{ - // for (int j = 0; j < 3; j++) - // { - // if (m_IAM_matrix(i, j) == 0.0) - // { - // m_error_msg = util::format("For %d collectors and groups of m_IAM coefficients, each group of m_IAM coefficients must begin with at least 3 non-zero values. There are only %d non-zero coefficients for collector %d", m_nColt, j, i + 1); - // throw(C_csp_exception(m_error_msg, "Trough collector solver")); - // //message(TCS_ERROR, "For %d collectors and groups of m_IAM coefficients, each group of m_IAM coefficients must begin with at least 3 non-zero values. There are only %d non-zero coefficients for collector %d", m_nColt, j, i + 1); - // //return -1; - // } - // } - //} - } +/* + *************************************************************************************************** + Trough system piping loss model + *************************************************************************************************** - // Organize the emittance tables - { - //m_epsilon_3.init(4, 4); - //m_epsilon_3.addTable(&m_epsilon_3_11); //HCE #1 - //m_epsilon_3.addTable(&m_epsilon_3_12); - //m_epsilon_3.addTable(&m_epsilon_3_13); - //m_epsilon_3.addTable(&m_epsilon_3_14); - //m_epsilon_3.addTable(&m_epsilon_3_21); //HCE #2 - //m_epsilon_3.addTable(&m_epsilon_3_22); - //m_epsilon_3.addTable(&m_epsilon_3_23); - //m_epsilon_3.addTable(&m_epsilon_3_24); - //m_epsilon_3.addTable(&m_epsilon_3_31); //HCE #3 - //m_epsilon_3.addTable(&m_epsilon_3_32); - //m_epsilon_3.addTable(&m_epsilon_3_33); - //m_epsilon_3.addTable(&m_epsilon_3_34); - //m_epsilon_3.addTable(&m_epsilon_3_41); //HCE #4 - //m_epsilon_3.addTable(&m_epsilon_3_42); - //m_epsilon_3.addTable(&m_epsilon_3_43); - //m_epsilon_3.addTable(&m_epsilon_3_44); + This piping loss model is derived from the pressure drop calculations presented in the + following document: - 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); - } + Parabolic Trough Solar System Piping Model - // 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 - { + B. Kelly + Nexant, Inc. San Francisco, California - // Old Fresnel - m_TCS_T_htf_in.resize(m_nMod); - m_TCS_T_htf_out.resize(m_nMod); - m_TCS_T_htf_ave.resize(m_nMod); - m_q_loss.resize(m_nRecVar); - m_q_abs.resize(m_nRecVar); - //c_htf.resize(nMod); - //rho_htf.resize(nMod); - m_DP_tube.resize(m_nMod); - //E_abs_field.resize(nMod); - m_E_int_loop.resize(m_nMod); - m_E_accum.resize(m_nMod); - m_E_avail.resize(m_nMod); - //E_abs_max.resize(nMod); - //v_1.resize(nMod); - m_q_loss_SCAtot.resize(m_nMod); - m_q_abs_SCAtot.resize(m_nMod); - m_q_SCA.resize(m_nMod); - //E_fp.resize_fill(nMod, 0.); - m_q_1abs_tot.resize(m_nMod); - m_q_1abs.resize(m_nRecVar); - m_ColOptEff.resize(m_nMod); + D. Kearney + Kearney & Associates + Vashon, Washington - // Trough - m_q_SCA_control_df.resize(m_nMod); - //m_q_i.resize(m_nColt); // Not a vector? - //m_IAM.resize(m_nColt); // Not a vector? - //m_EndGain.resize(m_nMod); - //m_EndLoss.resize(m_nMod); - // m_RowShadow.resize(m_nColt); // Not a vector? - // - //Allocate space for transient variables - m_TCS_T_htf_ave_last.resize(m_nMod); - m_TCS_T_htf_ave_converged.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 - { - // Trough Version - //m_AnnulusGasMat.resize(m_nRecVar, m_nHCEVar); - //m_AbsorberPropMat.resize(m_nRecVar, m_nHCEVar); - //for (int i = 0; i < m_nHCEt; i++) { - // for (int j = 0; j < m_nHCEVar; j++) { - // //Set up a matrix of annulus gas properties - // m_AnnulusGasMat.at(i, j) = new HTFProperties(); - // m_AnnulusGasMat.at(i, j)->SetFluid((int)m_AnnulusGas.at(i, j)); - // //Set up a matrix of absorber prop materials - // m_AbsorberPropMat(i, j) = new AbsorberProps(); - // m_AbsorberPropMat(i, j)->setMaterial((int)m_AbsorberMaterial.at(i, j)); - // } - //} + Subcontract Report + NREL/SR-550-40165 + July 2006 - // 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]); - } + ---------------------------- + 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; - //Initialize values - //m_defocus_old = 0.; - m_ncall = -1; + //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 - //Set the defocus order to always be last->first in the loop - m_SCADefocusArray.resize(m_nMod); - for (int i = 0; i < m_nMod; i++) { - m_SCADefocusArray[i] = m_nMod - i; - } + //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(); + //} - // for test start - init_fieldgeom(); - // for test end + //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; - // 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] + 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; - // 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)); +/************************************************************************************************** + 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); } - return; + 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; } -bool C_csp_fresnel_collector_receiver::init_fieldgeom() +// ******************************************************************** 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) { - /* - 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(). - */ + int nrnrsec = (int)floor(float(nfieldsec) / 4.0) + 1; - // If solar multiple is not yet calculated - if(m_is_solar_mult_designed == false) - this->design_solar_mult(); + if (irnr < 0 || irnr > 2 * nrnrsec - 1) { throw std::invalid_argument("Invalid runner index"); } - 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++) { + int irnr_onedir; + double m_dot_rnr; + double m_dot_rnr_0; + double m_dot_rnr_1; - 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) = 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 - } + // convert index to a mass flow equivalent cold runner index + if (irnr > nrnrsec - 1) { + irnr_onedir = 2 * nrnrsec - irnr - 1; + } + else { + irnr_onedir = irnr; } - //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; + 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; } - /* - 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)); + return max(m_dot_rnr, 0.0); +} - //Allocate space for the D_hdr array - //m_D_hdr.resize(m_nhdrsec, 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 - //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 + if (ihdr < 0 || ihdr > 2 * nhdrsec - 1) { throw std::invalid_argument("Invalid header index"); } - //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; + int ihdr_onedir; - //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., 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., 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 - 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] - //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_D_runner.resize(m_nrunsec); - m_L_runner.resize(m_nrunsec); - m_D_hdr.resize(m_nhdrsec);*/ - 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_L_hdr.resize(2 * m_nhdrsec); - //m_P_rnr.resize(2 * m_nrunsec); - //m_P_hdr.resize(2 * m_nhdrsec); - //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); - - /* ----- 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 + // convert index to a mass flow equivalent cold header index + if (ihdr > nhdrsec - 1) { + ihdr_onedir = 2 * nhdrsec - ihdr - 1; } 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] * pi * pow(m_D_runner[i], 2) / 4.; //This is the volume of the runner in 1 direction. + ihdr_onedir = ihdr; } - //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]; } - N_run_mult = 1.0 + (1.0 - 50.0 / lsum); - - //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; - } + double m_dot_oneloop = m_dot_field / float(nLoopsField); + return m_dot_field / float(nfieldsec) - ihdr_onedir * 2 * m_dot_oneloop; +} - //-------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. * 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); +// ------------------------------------------------------------------------------ PUBLIC - //Calculate the HTF volume associated with pumps and the SGS - double v_sgs = Pump_SGS(rho_ave, m_m_dot_design, m_solar_mult); +C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() +{ + mc_reported_outputs.construct(S_output_info); - //Calculate the hot and cold balance-of-plant volumes - m_v_hot = v_header + v_tofrom_sgs; - m_v_cold = m_v_hot; + // 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] - //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); + m_W_dot_sca_tracking_nom = std::numeric_limits::quiet_NaN(); - //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.; + // 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; - //is_fieldgeom_init = true; //The field geometry has been initialized. Make note. + 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(); - // TCS Temperature Tracking - m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = T_field_ini; //[K] - m_TCS_T_sys_h_converged = m_TCS_T_sys_h_last = T_field_ini; //[K] - //cc--> Note that stored(3) -> Iter is no longer used in the TRNSYS code. It is omitted here. - for (int i = 0; i < m_nMod; i++) - { - m_TCS_T_htf_ave_converged[i] = m_TCS_T_htf_ave_last[i] = T_field_ini; //[K] - } + m_latitude = std::numeric_limits::quiet_NaN(); + m_longitude = std::numeric_limits::quiet_NaN(); - m_TCS_T_sys_c_last = T_field_ini; - m_TCS_T_sys_h_last = T_field_ini; - ////cc--> Note that stored(3) -> Iter is no longer used in the TRNSYS code. It is omitted here. - //for (int i = 0; i < nMod; i++) { - // T_htf_in0[i] = T_field_ini; - // T_htf_out0[i] = T_field_ini; - // T_htf_ave0[i] = T_field_ini; - //} + // ************************************************************************ + // 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] - // ********************************************* - // 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] - } - // ********************************************* + // 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] - // Calculate tracking parasitics for when trough is on sun - m_W_dot_sca_tracking_nom = m_SCA_drives_elec * (double)(m_nMod * m_nLoops) / 1.E6; //[MWe] + // 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] - return true; -} + 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] -C_csp_collector_receiver::E_csp_cr_modes C_csp_fresnel_collector_receiver::get_operating_state() -{ - return m_operating_mode_converged; -} + 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_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] -double C_csp_fresnel_collector_receiver::get_startup_time() -{ - throw("C_csp_fresnel_collector_receiver::get_startup_time() is not complete"); + // ************************************************************************ + // ************************************************************************ + // 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_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] - return std::numeric_limits::quiet_NaN(); -} + m_dP_total = std::numeric_limits::quiet_NaN(); //[bar] + m_W_dot_pump = std::numeric_limits::quiet_NaN(); //[MWe] -double C_csp_fresnel_collector_receiver::get_startup_energy() -{ - throw(C_csp_exception("C_csp_fresnel_collector_receiver::get_startup_energy() is not complete")); + m_is_m_dot_recirc = false; + m_W_dot_sca_tracking = std::numeric_limits::quiet_NaN(); //[MWe] - return std::numeric_limits::quiet_NaN(); -} + m_EqOpteff = std::numeric_limits::quiet_NaN(); + m_m_dot_htf_tot = std::numeric_limits::quiet_NaN(); + //m_Theta_ave = std::numeric_limits::quiet_NaN(); + //m_CosTh_ave = std::numeric_limits::quiet_NaN(); + //m_IAM_ave = std::numeric_limits::quiet_NaN(); + //m_RowShadow_ave = std::numeric_limits::quiet_NaN(); + //m_EndLoss_ave = std::numeric_limits::quiet_NaN(); + //m_dni_costh = std::numeric_limits::quiet_NaN(); + m_c_htf_ave = std::numeric_limits::quiet_NaN(); -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]; - double dT_avg_SCA = (m_T_loop_out_des - m_T_loop_in_des) / m_nMod; - std::vector T_in_SCA, T_out_SCA; + m_control_defocus = std::numeric_limits::quiet_NaN(); + m_component_defocus = std::numeric_limits::quiet_NaN(); - 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)); - } + m_q_dot_inc_sf_tot = std::numeric_limits::quiet_NaN(); - double dP_field = field_pressure_drop(T_amb_des, m_m_dot_design, P_field_in, T_in_SCA, T_out_SCA); + /*for (int i = 0; i < 5; i++) + m_T_save[i] = std::numeric_limits::quiet_NaN();*/ - return m_W_dot_pump / (m_q_design * 1.e-6); -} + /*mv_reguess_args.resize(3); + std::fill(mv_reguess_args.begin(), mv_reguess_args.end(), std::numeric_limits::quiet_NaN());*/ -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] -} + m_AnnulusGasMat.fill(NULL); + m_AbsorberPropMat.fill(NULL); -double C_csp_fresnel_collector_receiver::get_tracking_power() -{ - return m_SCA_drives_elec * 1.e-6 * m_nMod * m_nLoops; //MWe + mv_HCEguessargs.resize(3); + std::fill(mv_HCEguessargs.begin(), mv_HCEguessargs.end(), std::numeric_limits::quiet_NaN()); } -double C_csp_fresnel_collector_receiver::get_col_startup_power() +C_csp_fresnel_collector_receiver::~C_csp_fresnel_collector_receiver() { - throw(C_csp_exception("C_csp_fresnel_collector_receiver::get_col_startup_power() is not complete")); - return std::numeric_limits::quiet_NaN(); //MWe-hr + 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::get_design_parameters(C_csp_collector_receiver::S_csp_cr_solved_params& solved_params) -{ - return; -} +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-- -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(); + Do any setup required here. + Get the values of the inputs and parameters + */ - m_is_m_dot_recirc = true; + //Initialize air properties -- used in reeiver calcs + m_airProps.SetFluid(HTFProperties::Air); - // Get optical properties - // Should reflect that the collector is not tracking and probably (but not necessarily) DNI = 0 - loop_optical_eta_off(); + // 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] - // Set mass flow rate to minimum allowable - double m_dot_htf_loop = m_m_dot_htfmin; //[kg/s] + m_P_field_in = 17 / 1.e-5; //Assumed inlet htf pressure for property lookups (DP_tot_max = 16 bar + 1 atm) [Pa] + } - // Set duration for recirculation timestep - if (m_step_recirc != m_step_recirc) - m_step_recirc = 10.0 * 60.0; //[s] + // 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) + { - // 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 + 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(); - // 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* xax = new double[ncol_OpticalTable - 1]; + double* yax = new double[nrow_OpticalTable - 1]; + double* data = new double[(ncol_OpticalTable - 1) * (nrow_OpticalTable - 1)]; - double Q_fp_sum = 0.0; //[MJ] + //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); + } + } - // 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] + optical_table.AddXAxis(xax, ncol_OpticalTable - 1); + optical_table.AddYAxis(yax, nrow_OpticalTable - 1); + optical_table.AddData(data); + delete[] xax; + delete[] yax; + delete[] data; - m_q_dot_sca_loss_summed_fullts = 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 = 0.0; + } - if ((int)(sim_info.ms_ts.m_time / 3600) == 5) - int x = 0; + // Adjust parameters + m_ColAz = m_ColAz * m_d2r; //[rad] Collector azimuth angle, convert from [deg] - // Simulate through time steps - for (int i = 0; i < n_steps_recirc; i++) + // Organize the emittance tables { - sim_info_temp.ms_ts.m_time = time_start + step_local * (i + 1); //[s] + 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); + } - // Set inlet temperature to previous timestep outlet temperature - double T_cold_in = m_T_sys_h_t_end_last; //[K] + // 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 + { - // Call energy balance with updated info - loop_energy_balance_T_t_int(weather, T_cold_in, m_dot_htf_loop, sim_info_temp); + // Old Fresnel + /*m_TCS_T_htf_in.resize(m_nMod); + m_TCS_T_htf_out.resize(m_nMod); + m_TCS_T_htf_ave.resize(m_nMod);*/ + 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_ColOptEff.resize(m_nMod); - // 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); + // Trough + m_q_SCA_control_df.resize(m_nMod); - T_cold_in = T_cold_in_i; //[K] - Q_fp_sum += Q_fp_i; //[MJ] - } + //Allocate space for transient variables + /*m_TCS_T_htf_ave_last.resize(m_nMod); + m_TCS_T_htf_ave_converged.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]); } + } - // 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_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] + //Initialize values - update_last_temps(); - } + // for test start + init_fieldgeom(); + // for test end - // 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] + // 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] - 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_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] + // Set previous operating mode + m_operating_mode_converged = C_csp_collector_receiver::OFF; //[-] 0 = requires startup, 1 = starting up, 2 = running - m_q_dot_freeze_protection = Q_fp_sum / sim_info.ms_ts.m_step; //[MWt] + // 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)); + } + return; +} - if ((int)(sim_info.ms_ts.m_time / 3600) == 5) - int x = 0; +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(). + */ - // 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); + // If solar multiple is not yet calculated + if(m_is_solar_mult_designed == false) + this->design_solar_mult(); - // 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 + 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++) { - // 5.8.17, twn: Don't report a component *delivered* mass flow rate if trough 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 + 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 + } + } - 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; + //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; + } - 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] + /* + 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)); - m_operating_mode = C_csp_collector_receiver::OFF; + //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 - set_output_value(); + //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; - return; -} + //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; + } -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) -{ - if (sim_info.ms_ts.m_time / 3600 == 11) - int x = 0; + //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); + } - // Always reset last temps - reset_last_temps(); + //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; - m_is_m_dot_recirc = true; + break; + } - // Get optical performance - loop_optical_eta(weather, sim_info); + //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; - // Set mass flow rate to what I imagine might be an appropriate value - // TEMPORARY FIX by TB - 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_opteff_des) / - (m_I_bn_des * m_opteff_des) * m_m_dot_loop_des; //[kg/s] - m_dot_htf_loop = min(m_m_dot_htfmax, max(m_m_dot_htfmin, 0.8 * m_dot_ss + 0.2 * m_m_dot_htfmin)); //[kg/s] + 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; } - // Set duration for recirculation timestep - if (m_step_recirc != m_step_recirc) - m_step_recirc = 10.0 * 60.0; //[s] + //the estimated mass flow rate at design + 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; + } - // 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; + m_m_dot_loop_des = m_m_dot_design / (double)m_nLoops; // [kg/s] + //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] - 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] + //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); - // 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] + 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); - // Zero full timestep outputs - m_q_dot_sca_loss_summed_fullts = 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 = 0.0; + /* ----- Set initial storage values ------ */ + double T_field_ini = 0.5 * (m_T_fp + m_T_loop_in_des); //[K] - sim_info_temp.ms_ts.m_time = time_start; + /* + 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. + } - // 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] + //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]; } - // Set inlet temperature to previous timestep outlet temperature - double T_cold_in = m_T_sys_h_t_end_last; //[K] + //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; + } - // Call energy balance with updated info - loop_energy_balance_T_t_int(weather, T_cold_in, m_dot_htf_loop, sim_info_temp); + //-------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); - // 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); + //Calculate the HTF volume associated with pumps and the SGS + double v_sgs = Pump_SGS(rho_ave, m_m_dot_design, m_solar_mult); - T_cold_in = T_cold_in_i; //[K] - Q_fp_sum += Q_fp_i; //[MJ] - } - } + //Calculate the hot and cold balance-of-plant volumes + m_v_hot = v_header + v_tofrom_sgs; + m_v_cold = m_v_hot; - // 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] + //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); - // 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_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; + //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.; - // 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(); + // TCS Temperature Tracking + //m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = T_field_ini; //[K] + //m_TCS_T_sys_h_converged = m_TCS_T_sys_h_last = T_field_ini; //[K] + ////cc--> Note that stored(3) -> Iter is no longer used in the TRNSYS code. It is omitted here. + //for (int i = 0; i < m_nMod; i++) + //{ + // m_TCS_T_htf_ave_converged[i] = m_TCS_T_htf_ave_last[i] = T_field_ini; //[K] + //} - } + /*m_TCS_T_sys_c_last = T_field_ini; + m_TCS_T_sys_h_last = T_field_ini;*/ + ////cc--> Note that stored(3) -> Iter is no longer used in the TRNSYS code. It is omitted here. + //for (int i = 0; i < nMod; i++) { + // T_htf_in0[i] = T_field_ini; + // T_htf_out0[i] = T_field_ini; + // T_htf_ave0[i] = T_field_ini; + //} - // Check if startup is achieved in current controller/kernel timestep - if (!is_T_startup_achieved) + // ********************************************* + // 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++) { - time_required_su = sim_info.ms_ts.m_step; //[s] - m_operating_mode = C_csp_collector_receiver::STARTUP; //[-] + m_T_htf_out_t_end_converged[i] = m_T_htf_out_t_end_last[i] = T_field_ini; //[K] } + // ********************************************* - // 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] + // 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() +{ + throw("C_csp_fresnel_collector_receiver::get_startup_time() is not complete"); - 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_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] - } + return std::numeric_limits::quiet_NaN(); +} + +double C_csp_fresnel_collector_receiver::get_startup_energy() +{ + throw(C_csp_exception("C_csp_fresnel_collector_receiver::get_startup_energy() is not complete")); + - 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] + return std::numeric_limits::quiet_NaN(); +} - // 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); +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]; + double dT_avg_SCA = (m_T_loop_out_des - m_T_loop_in_des) / m_nMod; + std::vector T_in_SCA, T_out_SCA; - // 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) + 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)); + } - // 5.8.17, twn: Don't report a component *delivered* mass flow rate if trough 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] + double dP_field = field_pressure_drop(T_amb_des, m_m_dot_design, P_field_in, T_in_SCA, T_out_SCA); - // 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] + return m_W_dot_pump / (m_q_design * 1.e-6); +} - cr_out_solver.m_component_defocus = 1.0; //[-] - cr_out_solver.m_is_recirculating = m_is_m_dot_recirc; +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] +} - 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] +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] +} - set_output_value(); +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() +{ + throw(C_csp_exception("C_csp_fresnel_collector_receiver::get_col_startup_power() is not complete")); + return std::numeric_limits::quiet_NaN(); //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::on(const C_csp_weatherreader::S_outputs& weather, +void C_csp_fresnel_collector_receiver::off(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); + m_is_m_dot_recirc = true; - // 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_trough_collector::on(...) received a CONTROL defocus > 1.0, " - "and that is not ok!")); - } + // Get optical properties + // Should reflect that the collector is not tracking and probably (but not necessarily) DNI = 0 + loop_optical_eta_off(); - // Solve the loop energy balance at the minimum mass flow rate - // Set mass flow rate to minimum allowable + // 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] + // Set duration for recirculation timestep + if (m_step_recirc != m_step_recirc) + m_step_recirc = 10.0 * 60.0; //[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); + // 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 - // 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); + // 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] - // Set upper and lower bounds - double defocus_upper = 1.0; //[-] - double defocus_lower = 0.0; //[-] + double Q_fp_sum = 0.0; //[MJ] - // 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; //[-] + // 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] - // Set solver settings - relative error on T_htf_out - c_defocus_solver.settings(0.001, 30, defocus_lower, defocus_upper, true); + m_q_dot_sca_loss_summed_fullts = 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 = 0.0; - int iter_solved = -1; - double tol_solved = std::numeric_limits::quiet_NaN(); + // 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] - 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_trough_collector::on(...) COMPONENT defocus failed.")); - on_success = false; - } + // Set inlet temperature to previous timestep outlet temperature + double T_cold_in = m_T_sys_h_t_end_last; //[K] - if (defocus_code != C_monotonic_eq_solver::CONVERGED) - { - throw(C_csp_exception("C_csp_trough_collector::on(...) COMPONENT defocus failed.")); - on_success = false; - } + // Call energy balance with updated info + loop_energy_balance_T_t_int(weather, T_cold_in, m_dot_htf_loop, sim_info_temp); - } - else + // Check freeze protection + if (m_T_htf_out_t_end[m_nMod - 1] < m_T_fp + fp_offset) { - // 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) + if (m_Q_field_losses_total_subts > 0.0) { - throw(C_csp_exception("C_csp_trough_collector::on(...) HTF mass flow rate iteration failed.")); - on_success = false; - } + 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); - if (m_dot_htf_code != C_monotonic_eq_solver::CONVERGED) - { - throw(C_csp_exception("C_csp_trough_collector::on(...) HTF mass flow rate iteration failed.")); - on_success = false; + T_cold_in = T_cold_in_i; //[K] + Q_fp_sum += Q_fp_i; //[MJ] } } - } - - 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] + // 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] - 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_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] + // 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_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] - 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] + update_last_temps(); + } - // 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); + // 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] - // 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] + 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_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] - // 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] + m_q_dot_freeze_protection = Q_fp_sum / sim_info.ms_ts.m_step; //[MWt] - cr_out_solver.m_component_defocus = m_component_defocus; //[-] - cr_out_solver.m_is_recirculating = m_is_m_dot_recirc; - // *********************************************************** - // *********************************************************** + // 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); - // 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 + // 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 - 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] + // 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 - m_q_dot_sca_loss_summed_fullts = 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 = m_q_dot_freeze_protection = 0.0; + 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_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_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] - 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::steady_state(const C_csp_weatherreader::S_outputs& weather, +void C_csp_fresnel_collector_receiver::startup(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.; + // Always reset last temps + reset_last_temps(); - do - { - this->on(weather, htf_state_in, W_dot_elec_to_CR_heat, field_control, cr_out_solver, sim_info); + m_is_m_dot_recirc = true; - // 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]); - } + // Get optical performance + loop_optical_eta(weather, sim_info); - // 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; + // Set mass flow rate to what I imagine might be an appropriate value + // TEMPORARY FIX by TB + 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] + } - // 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; + // Set duration for recirculation timestep + if (m_step_recirc != m_step_recirc) + m_step_recirc = 10.0 * 60.0; //[s] - minutes2SS += sim_info.ms_ts.m_step / 60.; + // 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 - } while (ss_diff / 200. > tol); + // 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; - // 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] - bool custom_sf_pipe_sizes = true; - 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; + bool is_T_startup_achieved = false; - double rho_ave = m_htfProps.dens((m_T_loop_out_des + m_T_loop_in_des) / 2.0, 0.0); //kg/m3 + // 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] - 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); + double Q_fp_sum = 0.0; //[MJ] - // 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 + // 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] - // 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; + // Zero full timestep outputs + m_q_dot_sca_loss_summed_fullts = 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 = 0.0; - return; -} + sim_info_temp.ms_ts.m_time = time_start; -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) + // Loop through time steps + while (sim_info_temp.ms_ts.m_time < time_end) { - C_csp_collector_receiver::S_csp_cr_out_solver cr_out_solver; + 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] - on(weather, htf_state_in, std::numeric_limits::quiet_NaN(), 1.0, cr_out_solver, sim_info); + // Set inlet temperature to previous timestep outlet temperature + double T_cold_in = m_T_sys_h_t_end_last; //[K] - 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] Trough is recirculating, so going into startup isn't significantly different than OFF - } - else + // 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) { - est_out.m_q_startup_avail = 0.0; + 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] + } } - 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; -} + // 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] -void C_csp_fresnel_collector_receiver::converged() -{ - /* - -- Post-convergence call -- + // 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_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; - Update values that should be transferred to the next time 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; + } - m_ss_init_complete = true; + update_last_temps(); - // 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; } - // TCS Temperature Tracking - m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = m_TCS_T_sys_c; //[K] - m_TCS_T_sys_h_converged = m_TCS_T_sys_h_last = m_TCS_T_sys_h; //[K] - for (int i = 0; i < m_nMod; i++) + // Check if startup is achieved in current controller/kernel timestep + if (!is_T_startup_achieved) { - m_TCS_T_htf_ave_converged[i] = m_TCS_T_htf_ave_last[i] = m_TCS_T_htf_ave[i]; + time_required_su = sim_info.ms_ts.m_step; //[s] + m_operating_mode = C_csp_collector_receiver::STARTUP; //[-] } - // 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++) + // Account for time required { - m_T_htf_out_t_end_converged[i] = m_T_htf_out_t_end_last[i] = m_T_htf_out_t_end[i]; //[K] - } + 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_ncall = -1; //[-] + 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_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] - 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", - "Trough converged method")); + 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] - 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(); + // 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); - mc_reported_outputs.set_timestep_outputs(); + // 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) - return; -} + // 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] -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); -} + // 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] -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_costh_ini(m_costh); - double m_q_i_ini(m_q_i); - //double m_IAM_ini(m_IAM); - util::matrix_t m_ColOptEff_ini(m_ColOptEff); - double m_EqOpteff_ini(m_EqOpteff); - //vector m_EndGain_ini(m_EndGain); // changed from matrix to vector - //vector m_EndLoss_ini(m_EndLoss); // changed from matrix to vector - //std::vector m_RowShadow_ini(m_RowShadow); - std::vector m_q_SCA_ini(m_q_SCA); - double m_Theta_ave_ini(m_Theta_ave); - //double m_CosTh_ave_ini(m_CosTh_ave); - //double m_IAM_ave_ini(m_IAM_ave); - //double m_RowShadow_ave_ini(m_RowShadow_ave); - //double m_EndLoss_ave_ini(m_EndLoss_ave); - //double m_dni_costh_ini(m_dni_costh); - 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); + cr_out_solver.m_component_defocus = 1.0; //[-] + cr_out_solver.m_is_recirculating = m_is_m_dot_recirc; - loop_optical_eta(weather, sim); - //double eta_optical = m_EqOpteff * m_costh; - double eta_optical = m_EqOpteff; + 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] - // Restore member variable values - //m_costh = m_costh_ini; - m_q_i = m_q_i_ini; - //m_IAM = m_IAM_ini; - m_ColOptEff = m_ColOptEff_ini; - m_EqOpteff = m_EqOpteff_ini; - //m_EndGain = m_EndGain_ini; - //m_EndLoss = m_EndLoss_ini; - //m_RowShadow = m_RowShadow_ini; - m_q_SCA = m_q_SCA_ini; - m_Theta_ave = m_Theta_ave_ini; - //m_CosTh_ave = m_CosTh_ave_ini; - //m_IAM_ave = m_IAM_ave_ini; - //m_RowShadow_ave = m_RowShadow_ave_ini; - //m_EndLoss_ave = m_EndLoss_ave_ini; - //m_dni_costh = m_dni_costh_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; + set_output_value(); - return eta_optical; + return; } -double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/) +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) { - throw(C_csp_exception("C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx")); - - return std::numeric_limits::quiet_NaN(); -} -double C_csp_fresnel_collector_receiver::get_collector_area() -{ - return m_Ap_tot; -} + // Always reset last temps + reset_last_temps(); -// ------------------------------------------------------------------- PUBLIC SUPPLEMENTAL + m_is_m_dot_recirc = false; -bool C_csp_fresnel_collector_receiver::design_solar_mult() -{ - if (m_is_solar_mult_designed == true) - return false; + // Get optical performance (no defocus applied in this method) + // This returns m_q_SCA with NO defocus + loop_optical_eta(weather, sim_info); - // Calculate nLoops, depending on designing for solar mult or total field aperture + // If Control Defocus: field_control < 1, then apply it here + if (field_control < 1.0) { - // Optical Derate - 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 - 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); - break; - } - case 1: - { - m_Ap_tot = m_total_Ap_in; - m_nLoops = std::ceil(m_Ap_tot / 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; - } - } - - // Number of Loops necessary for solar mult = 1 - m_nLoops_sm1 = std::ceil(m_Ap_sm1 / m_A_loop); + // 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); -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) + bool on_success = true; + + if (balance_code != E_loop_energy_balance_exit::SOLVED) { - loop_optical_wind_stow(); + on_success = false; } - else - { - if (weather.m_beam > 300) - int x = 0; - // First, clear all the values calculated below - loop_optical_eta_off(); + // 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] - //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] + // 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); - // DEBUG - if (time_hr == 9) - int x = 0; + // 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); - //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; + // Set upper and lower bounds + double defocus_upper = 1.0; //[-] + double defocus_lower = 0.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(pi - m_theta_dep)) * acos((DepHr1 * DepHr2 + sqrt(DepHr1 * DepHr1 - DepHr2 * DepHr2 + 1.0)) / (DepHr1 * DepHr1 + 1.0)) * 180.0 / pi / 15.0; - double DepTime = SolarNoon + DepHr3; + // 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; //[-] - 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(pi - m_theta_stow)) * acos((StwHr1 * StwHr2 + sqrt(StwHr1 * StwHr1 - StwHr2 * StwHr2 + 1.0)) / (StwHr1 * StwHr1 + 1.0)) * 180.0 / pi / 15.0; - double StwTime = SolarNoon + StwHr3; + // Set solver settings - relative error on T_htf_out + c_defocus_solver.settings(0.001, 30, defocus_lower, defocus_upper, true); - // 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; + int iter_solved = -1; + double tol_solved = std::numeric_limits::quiet_NaN(); - 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) + int defocus_code = 0; + double defocus_solved = 1.0; + try { - m_ftrack = (HrB - DepTime) / dt_hr; - MidTrack = HrB - m_ftrack * 0.5 * dt_hr; - - // Solar field stows during time period + defocus_code = c_defocus_solver.solve(defocus_guess_lower, defocus_guess_upper, m_T_loop_out_des, + defocus_solved, tol_solved, iter_solved); } - else if (HrB > StwTime) + catch (C_csp_exception) { - m_ftrack = (StwTime - HrA) / dt_hr; - MidTrack = HrA + m_ftrack * 0.5 * dt_hr; - // solar field operates during entire period + throw(C_csp_exception("C_csp_fresnel_collector::on(...) COMPONENT defocus failed.")); + on_success = false; } - else + + if (defocus_code != C_monotonic_eq_solver::CONVERGED) { - m_ftrack = 1.0; - MidTrack = HrA + 0.5 * dt_hr; + throw(C_csp_exception("C_csp_fresnel_collector::on(...) COMPONENT defocus failed.")); + on_success = false; } - // solar field doesn't operate + } else { - m_ftrack = 0.0; - MidTrack = HrA + 0.5 * dt_hr; - } + // 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); - //// Maximum wind speed value NO max wind speed - //if (V_wind >= m_V_wind_max) - // m_ftrack = 0.0; + // 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] - 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; + // 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] - // 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 * d2r; // Convert from degree to radian + // Set solver settings + // Relative error + c_htf_m_dot_solver.settings(0.001, 30, m_dot_lower, m_dot_upper, true); - if (SolarZenRad < pi / 2.) { - //Convert the solar angles to collector incidence angles - CSP::theta_trans(SolarAz, SolarZenRad, m_ColAz, m_phi_t, m_theta_L); + int iter_solved = -1; + double tol_solved = std::numeric_limits::quiet_NaN(); - switch (m_opt_model) + int m_dot_htf_code = 0; + try { - 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, 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_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; } - m_eta_optical *= m_ftrack; - } - else { - m_eta_optical = 0.0; - m_phi_t = 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 - //m_IAM_ave = 0; - //m_RowShadow_ave = 0; - 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 - - //m_RowShadow_ave += m_RowShadow[] + 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; + } } - //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 - // 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] - - // DEBUG - if (sim_info.ms_ts.m_time == 302400) - int x = 0; } -} - -void C_csp_fresnel_collector_receiver::loop_optical_eta_off() -{ - // If trough is not absorbing any sunlight (night or 100% defocus), then set member data as necessary - //m_costh = 0.0; //[-] Cosine of the incident angle between the sun and trough aperture - - m_q_i = 0; //[W/m] DNI * A_aper / L_sca - //m_IAM.assign(m_IAM.size(), 0.0); //[-] Incidence angle modifiers - //m_IAM = 0; //[-] Incidence angle modifiers NOT a vector (only one collector type) - m_ColOptEff.fill(0.0); //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack - m_EqOpteff = 0.; - //m_EndGain.fill(0.0); //[-] Light from different collector hitting receiver - //m_EndLoss.fill(0.0); //[-] Light missing receiver due to length + end gain - //std::fill(m_EndGain.begin(), m_EndGain.end(), 0); //[-] Light from different collector hitting receiver NO longer a matrix - //std::fill(m_EndLoss.begin(), m_EndLoss.end(), 0); //[-] Light missing receiver due to length + end gain NO longer a matrix - //m_RowShadow.assign(m_RowShadow.size(), 0.0); //[-] Row-to-row m_Shadowing losses - 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_Theta_ave = 0.0; - //m_CosTh_ave = 0.0; - //m_IAM_ave = 0.0; - //m_RowShadow_ave = 0.0; - //m_EndLoss_ave = 0.0; - //m_dni_costh = 0.0; - 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; -} + 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] -void C_csp_fresnel_collector_receiver::loop_optical_wind_stow() -{ - // Want to completely defocus trough because wind speed is faster than stow speed - // Can use 'loop_optical_eta_off' but then need to reset: - // * tracking power - // * defocus values + 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_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] - loop_optical_eta_off(); + 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] - m_W_dot_sca_tracking = m_W_dot_sca_tracking_nom; //[MWe] + // 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); - m_component_defocus = 0.0; -} + // 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] -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] - } + // 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] - return; -} + cr_out_solver.m_component_defocus = m_component_defocus; //[-] + cr_out_solver.m_is_recirculating = m_is_m_dot_recirc; + // *********************************************************** + // *********************************************************** -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] + // 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 -void C_csp_fresnel_collector_receiver::apply_control_defocus(double defocus) -{ - // Uses m_q_i, m_costh, and input defocus to calculate m_q_SCA_control_df + 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] - // Store control defocus - m_control_defocus = defocus; + m_q_dot_sca_loss_summed_fullts = 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 = m_q_dot_freeze_protection = 0.0; - //if (m_fthrctrl == 0) - //{ - // mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." - // " The model will instead use Simultaneous Partial Defocusing"); - // m_fthrctrl = 2; - //} - //if (m_fthrctrl == 1) - //{ - // mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, partially defocusing SCAs is not available." - // " The model will instead use Simultaneous Partial Defocusing"); - // m_fthrctrl = 2; - //} - //if (m_fthrctrl == 2) - //{ - // for (int i = 0; i < m_nMod; i++) - // { - // m_q_SCA_control_df[i] = defocus * m_q_i; - // } - //} + 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] - for (int i = 0; i < m_nMod; i++) - { - m_q_SCA_control_df[i] = defocus * m_q_i; + cr_out_solver.m_q_dot_heater = m_q_dot_freeze_protection; //[MWt] } + set_output_value(); + + return; } -void C_csp_fresnel_collector_receiver::apply_component_defocus(double defocus /*-*/) +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) { - // Uses m_q_SCA_control_df and input defocus to calculate m_q_SCA + // 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; - // Store component defocus - m_component_defocus = defocus; + 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); - //if (m_fthrctrl == 0) - //{ - // mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, fully defocusing SCAs is not available." - // " The model will instead use Simultaneous Partial Defocusing"); - // m_fthrctrl = 2; - //} - ////if (m_fthrctrl == 1) - //{ - // mc_csp_messages.add_message(C_csp_messages::WARNING, "The selected defocusing method of sequentially, partially defocusing SCAs is not available." - // " The model will instead use Simultaneous Partial Defocusing"); - // m_fthrctrl = 2; - //} - //if (m_fthrctrl == 2) - //{ - // for (int i = 0; i < m_nMod; i++) - // { - // //int CT = (int)m_SCAInfoArray(i, 1) - 1; // Collector type - // m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; - // } - //} + // 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.; - for (int i = 0; i < m_nMod; i++) + do { - //int CT = (int)m_SCAInfoArray(i, 1) - 1; // Collector type - m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; - } -} + this->on(weather, htf_state_in, W_dot_elec_to_CR_heat, field_control, cr_out_solver, sim_info); -//---------------------------------------------------------------------- PUBLIC SUPPLEMENTAL (from sam_mw_lf_type262_salt) + // 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]); + } -double C_csp_fresnel_collector_receiver::Pump_SGS(double rho, double m_dotsf, double sm) { + // 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; - 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]; + // 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; - //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 }; + minutes2SS += sim_info.ms_ts.m_step / 60.; - //Assume a maximum HTF velocity of 1.85 m/s (based on average from Kelly & Kearney model - vel_max = 1.85; + } while (ss_diff / 200. > tol); - //design-point vol. flow rate m3/s - m_dotpb = m_dotsf / sm; - v_dotpb = m_dotpb / rho; - v_dotsf = m_dotsf / rho; + // 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] + bool custom_sf_pipe_sizes = true; + 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 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]; + double rho_ave = m_htfProps.dens((m_T_loop_out_des + m_T_loop_in_des) / 2.0, 0.0); //kg/m3 - //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 * pi))); - //Calculate the total volume - V[i] = pow(D[i], 2) / 4. * pi * L_line[i]; - psum += V[i]; - } + 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); - delete[] V_dot; - delete[] D; - delete[] V; + // 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 - return psum; + // 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; } -/* - *************************************************************************************************** - 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 +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; - ---------------------------- - 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. + on(weather, htf_state_in, std::numeric_limits::quiet_NaN(), 1.0, cr_out_solver, sim_info); - ---------------------------- - 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) { + 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; + } - 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; + return; +} - //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 / (pi * (D / 2.) * (D / 2.)); //Fluid mean velocity +void C_csp_fresnel_collector_receiver::converged() +{ + /* + -- Post-convergence call -- - //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(); + 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; + } + + // TCS Temperature Tracking + //m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = m_TCS_T_sys_c; //[K] + //m_TCS_T_sys_h_converged = m_TCS_T_sys_h_last = m_TCS_T_sys_h; //[K] + //for (int i = 0; i < m_nMod; i++) + //{ + // m_TCS_T_htf_ave_converged[i] = m_TCS_T_htf_ave_last[i] = m_TCS_T_htf_ave[i]; //} - //Calculation of pressure loss from pipe length - HL_pm = f * u_fluid * u_fluid / (2. * D * g); - DP_pipe = HL_pm * rho * g * L_pipe; + // 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] + } - //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 * g * Nels; - DP_elm = 0.75 * D / f * HL_pm * rho * g * Nelm; - DP_ell = 0.6 * D / f * HL_pm * rho * g * Nell; - DP_gav = 0.19 * D / f * HL_pm * rho * g * Ngav; - DP_glv = 10.0 * D / f * HL_pm * rho * g * Nglv; - DP_chv = 2.5 * D / f * HL_pm * rho * g * Nchv; - DP_lw = 1.8 * D / f * HL_pm * rho * g * Nlw; - DP_lcv = 10.0 * D / f * HL_pm * rho * g * Nlcv; - DP_bja = 8.69 * D / f * HL_pm * rho * g * Nbja; + 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")); + } - 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; + 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; //[-] -/************************************************************************************************** - 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) { + m_W_dot_sca_tracking = 0.0; //[MWe] - double Test, TestOld, X, Xold, Slope; - double Acc = .01; //0.0001 - int NumTries; + // Reset the optical efficiency member data + loop_optical_eta_off(); - if (Reynold < 2750.) { - return 64. / max(Reynold, 1.0); - } + mc_reported_outputs.set_timestep_outputs(); - 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; + return; +} - 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); - } +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); +} - Slope = (Test - TestOld) / (X - Xold); - Xold = X; - TestOld = Test; - X = max((Slope * X - Test) / Slope, 1.e-5); - } +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); - //call Messages(-1," Could not find friction factor solution",'Warning',0,250) - return 0; + loop_optical_eta(weather, sim); + double 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*/) +{ + throw(C_csp_exception("C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx")); + + return std::numeric_limits::quiet_NaN(); +} -// ******************************************************************** STATIC Methods +double C_csp_fresnel_collector_receiver::get_collector_area() +{ + return m_Ap_tot; +} +// ------------------------------------------------------------------- PUBLIC SUPPLEMENTAL -// 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) +bool C_csp_fresnel_collector_receiver::design_solar_mult() { - int nrnrsec = (int)floor(float(nfieldsec) / 4.0) + 1; + if (m_is_solar_mult_designed == true) + return false; - if (irnr < 0 || irnr > 2 * nrnrsec - 1) { throw std::invalid_argument("Invalid runner index"); } + // Calculate nLoops, depending on designing for solar mult or total field aperture + { + // Optical Derate + 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]; - int irnr_onedir; - double m_dot_rnr; - double m_dot_rnr_0; - double m_dot_rnr_1; + // Optical Normal + m_opt_normal = 0; + m_opt_normal = m_TrackingError * m_GeomEffects * m_reflectivity * m_Dirt_mirror * m_Error; - // convert index to a mass flow equivalent cold runner index - if (irnr > nrnrsec - 1) { - irnr_onedir = 2 * nrnrsec - irnr - 1; - } - else { - irnr_onedir = irnr; - } + // Loop Optical Efficiency + m_loop_opt_eff = m_opt_derate * m_opt_normal; - m_dot_rnr_0 = m_dot_field / 2.; - m_dot_rnr_1 = m_dot_rnr_0 * (1. - float(nfieldsec % 4) / float(nfieldsec)); + // Loop Aperture + m_A_loop = (float)m_nMod * m_A_aperture; - 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; - } + // 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; + } + } - return max(m_dot_rnr, 0.0); -} + // 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)); -// 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 + // Loop Efficiency + m_loop_eff = m_loop_opt_eff * m_loop_therm_eff; - if (ihdr < 0 || ihdr > 2 * nhdrsec - 1) { throw std::invalid_argument("Invalid header index"); } + // Thermal Power at Design + m_q_design = m_P_ref / m_eta_ref; - int ihdr_onedir; + // Required Aperture for solar multiple = 1 + m_Ap_sm1 = m_q_design / (m_I_bn_des * m_loop_eff); - // convert index to a mass flow equivalent cold header index - if (ihdr > nhdrsec - 1) { - ihdr_onedir = 2 * nhdrsec - ihdr - 1; - } - else { - ihdr_onedir = ihdr; - } + // 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); + break; + } + case 1: + { + m_Ap_tot = m_total_Ap_in; + m_nLoops = std::ceil(m_Ap_tot / 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; + } + } - double m_dot_oneloop = m_dot_field / float(nLoopsField); - return m_dot_field / float(nfieldsec) - ihdr_onedir * 2 * m_dot_oneloop; -} + // 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_trough->apply_component_defocus(defocus); + 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_trough->loop_energy_balance_T_t_int(ms_weather, m_T_cold_in, m_m_dot_loop, ms_sim_info); + 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) { @@ -3406,7 +3073,7 @@ int C_csp_fresnel_collector_receiver::C_mono_eq_defocus::operator()(double defoc } // Set the outlet temperature at end of timestep - *T_htf_loop_out = mpc_trough->m_T_htf_out_t_end[mpc_trough->m_nMod - 1]; + *T_htf_loop_out = mpc_fresnel->m_T_htf_out_t_end[mpc_fresnel->m_nMod - 1]; return 0; } @@ -3414,7 +3081,7 @@ int C_csp_fresnel_collector_receiver::C_mono_eq_defocus::operator()(double defoc 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_trough->loop_energy_balance_T_t_int(ms_weather, m_T_cold_in, m_dot_htf_loop, ms_sim_info); + 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) { @@ -3423,7 +3090,7 @@ int C_csp_fresnel_collector_receiver::C_mono_eq_T_htf_loop_out::operator()(doubl } // Set the outlet temperature at end of timestep - *T_htf_loop_out = mpc_trough->m_T_htf_out_t_end[mpc_trough->m_nMod - 1]; + *T_htf_loop_out = mpc_fresnel->m_T_htf_out_t_end[mpc_fresnel->m_nMod - 1]; return 0; } @@ -3431,18 +3098,18 @@ int C_csp_fresnel_collector_receiver::C_mono_eq_T_htf_loop_out::operator()(doubl 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_trough->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) + 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_trough->m_m_dot_htf_tot * mpc_trough->m_c_htf_ave_ts_ave_temp * - (T_htf_cold_in - mpc_trough->m_T_sys_h_t_end_last) / 1.E6 * (ms_sim_info.ms_ts.m_step); //[MJ] + 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_trough->m_Q_field_losses_total_subts) / mpc_trough->m_Q_field_losses_total_subts; //[-] + *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; } @@ -3529,6 +3196,36 @@ int C_csp_fresnel_collector_receiver::C_mono_eq_freeze_prot_E_bal::operator()(do // 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, @@ -3541,8 +3238,30 @@ EvacReceiverModel::EvacReceiverModel(vector D_abs_in, vector D_a { } +/// +/// 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 +/// 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, int ncall, double time, util::matrix_t ColOptEff, + 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) { @@ -3638,13 +3357,13 @@ void EvacReceiverModel::Calculate_Energy_Balance(double T_1_in, double m_dot, do 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 - } + ////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 diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 7269c386e..f2f7da1ac 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -44,41 +44,41 @@ class EvacReceiverModel // Constants const double m_T_htf_prop_min = 275; - const double pi = 3.14159; + 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 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_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_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; + const vector m_alpha_env; // [-] Envelope absorptance + emit_table* m_epsilon_abs; // [-] Absorber emittance - HTFProperties m_htfProps, m_airProps; + 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_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?) + const vector m_D_h; //[m^2] Hydraulic diameters for HTF flow for each receiver and variant (why variant?) // Private Methods private: @@ -104,7 +104,7 @@ class EvacReceiverModel 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, int ncall, double time, util::matrix_t ColOptEff, + 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); @@ -118,12 +118,12 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver enum { - E_THETA_AVE, //[deg] - E_COSTH_AVE, //[-] - E_IAM_AVE, //[-] - E_ROWSHADOW_AVE, //[-] - E_ENDLOSS_AVE, //[-] - E_DNI_COSTH, //[W/m2] + //E_THETA_AVE, //[deg] + //E_COSTH_AVE, //[-] + //E_IAM_AVE, //[-] + //E_ROWSHADOW_AVE, //[-] + //E_ENDLOSS_AVE, //[-] + //E_DNI_COSTH, //[W/m2] E_EQUIV_OPT_ETA_TOT, //[-] E_DEFOCUS, //[-] @@ -152,30 +152,28 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // From Fresnel - E_THETA_L, //[deg] - E_PHI_T, - E_ETA_OPTICAL, - //E_EQ_OPT_EFF, - - E_SF_DEF, - E_Q_INC_SF_TOT, - E_Q_ABS_TOT, - E_Q_DUMP, - E_Q_LOSS_TOT, - E_PIPE_HL, - E_Q_AVAIL, - E_Q_LOSS_SPEC_TOT, - E_ETA_THERMAL, - E_E_BAL_STARTUP, - E_M_DOT_AVAIL, - E_M_DOT_HTF2, - E_DP_TOT, - E_T_SYS_C, - E_T_SYS_H, - E_T_LOOP_OUTLET, - - - E_Q_I + //E_THETA_L, //[deg] + //E_PHI_T, + //E_ETA_OPTICAL, + + //E_SF_DEF, + //E_Q_INC_SF_TOT, + //E_Q_ABS_TOT, + //E_Q_DUMP, + //E_Q_LOSS_TOT, + //E_PIPE_HL, + //E_Q_AVAIL, + //E_Q_LOSS_SPEC_TOT, + //E_ETA_THERMAL, + //E_E_BAL_STARTUP, + //E_M_DOT_AVAIL, + //E_M_DOT_HTF2, + //E_DP_TOT, + //E_T_SYS_C, + //E_T_SYS_H, + //E_T_LOOP_OUTLET, + + //E_Q_I }; enum struct E_loop_energy_balance_exit @@ -187,133 +185,108 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // Private Fields private: - // Fields in Trough - - - - OpticalDataTable optical_table; - - - // 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 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()) - // 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() - - // Parameters calculated in init() - //int m_n_c_iam_matrix; //[-] Number of columns in the IAM matrix - //int m_n_r_iam_matrix; //[-] Number of rows in the IAM matrix - double m_v_hot; //[m^3] Hot piping volume - double m_v_cold; //[m^3] Cold piping volume - bool m_is_solar_mult_designed = false; - - int m_nfsec; //[-] Number of field sections - int m_nhdrsec; //[-] Number of header sections - int m_nrunsec; //[-] Number of unique runner diameters - double m_L_tot; //[m] Total length of collectors in a loop - double m_opteff_des; //[-] Design-point optical efficieny (theta = 0) from the solar field - - + // Evac Receiver Model Class and Parameters + std::unique_ptr m_evac_receiver; + std::vector mv_HCEguessargs; - emit_table m_epsilon_3; // Table of emissivity vs temperature for each variant of each receiver type + // 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 efficieny (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; - //double m_defocus_old; //[-] Defocus during previous call (= 1 at first call) - int m_ncall; //[-] Track number of calls per timestep, reset = -1 in converged() call - - // Variables that are passed between methods, but not necessary to carry over timesteps - 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 - - 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?) - - 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_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 - double m_q_i; //[W/m] DNI * A_aper / L_sca NOT a vector because all collector lengths are equal - // This value (m_q_SCA) is passed to the Evacuated Tube model, where the other optical losses are applied - std::vector m_q_SCA; //[W/m] Total incident irradiation on the receiver (q"*A_aper/L_sca*cos(theta)*all_defocus) - std::vector m_q_SCA_control_df; //[W/m] Total incident irradiation less CONTROL defocus (m_q_sca * control_defocus) - - //double m_IAM; //[-] Incidence angle modifiers NOT a vector becuase only one collector type ? - //std::vector m_RowShadow; //[-] Row-to-row shadowing losses - - /*m_nColt, m_nSCA*/ - util::matrix_t m_ColOptEff; //[-] tracking * geom * rho * dirt * error * IAM * row shadow * end loss * ftrack - //vector m_EndGain; //[-] Light from different collector hitting receiver // NOT a matrix because only one collector type - //vector m_EndLoss; //[-] Light missing receiver due to length // NOT a matrix because only one collector type - - double m_Theta_ave; //[rad] Field average m_theta value (but... nothing in our model allows for this to different over SCAs) - - //double m_CosTh_ave; //[-] Field average costheta value - //double m_IAM_ave; //[-] Field average incidence angle modifier - //double m_RowShadow_ave; //[-] Field average row shadowing loss - //double m_EndLoss_ave; //[-] Field average end loss - - //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_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 - double m_EqOpteff; //[-] - 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...) - double m_q_dot_inc_sf_tot; //[MWt] Total incident radiation on solar field - - 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_cold; //[J/kg-K] Specific heat of fluid at m_T_sys_c - 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_P_field_in; - - // Classes that are defined as member data so are re-declared each time performance function is called - std::vector m_DP_tube; //[Pa] Pressure drops in each receiver + // 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_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 + + 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 - // ********************************************* - // TCS Temperature Tracking - // Temperatures from the most recent converged() operation - double m_TCS_T_sys_c_converged; //[K] Temperature (bulk) of cold runners & headers in previous timestep - std::vector m_TCS_T_htf_ave_converged; //[K] Average HTF temperature in each SCA - double m_TCS_T_sys_h_converged; //[K] Temperature (bulk) of hot runners & headers in previous timestep - - // Temperatures from the most recent timestep (in the event that a method solves multiple, shorter timesteps) - double m_TCS_T_sys_c_last; //[K] Temperature (bulk) of cold runners & headers in previous timestep - std::vector m_TCS_T_htf_ave_last; //[K] Average HTF temperature in each SCA - double m_TCS_T_sys_h_last; //[K] Temperature (bulk) of hot runners & headers in previous timestep - - // Latest temperatures solved during present call to this class - double m_TCS_T_sys_c; //[K] Temperature (bulk) of cold runners & headers - std::vector m_TCS_T_htf_in; //[K] Inlet HTF temperature to each SCA - std::vector m_TCS_T_htf_ave; //[K] Average HTF temperature in each SCA - std::vector m_TCS_T_htf_out; //[K] Outlet HTF temperature to each SCA - double m_TCS_T_sys_h; //[K] Solar field HTF outlet temperature - // ********************************************* - // ********************************************* // ********************************************* // CSP Solver Temperature Tracking @@ -324,290 +297,291 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // ** 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 + 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_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_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_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_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 - 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_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 + - double m_dP_total; //[bar] FIELD pressure drop - double m_W_dot_pump; //[MWe] FIELD pumping power + - bool m_is_m_dot_recirc; //[-] True: trough is recirculationg HTF with interacting with other CSP components + - bool m_ss_init_complete; //[-] For TCS-based model in acceptance testing, has model achieved steady state at first timestep? + - // Member variables that are used to store information for the EvacReceiver method - //double m_T_save[5]; //[K] Saved temperatures from previous call to EvacReceiver single SCA energy balance model - //std::vector mv_reguess_args; //[-] Logic to determine whether to use previous guess values or start iteration fresh + // // TCS Temperature Tracking + // // Temperatures from the most recent converged() operation + //double m_TCS_T_sys_c_converged; //[K] Temperature (bulk) of cold runners & headers in previous timestep + //std::vector m_TCS_T_htf_ave_converged; //[K] Average HTF temperature in each SCA + //double m_TCS_T_sys_h_converged; //[K] Temperature (bulk) of hot runners & headers in previous timestep + //// Temperatures from the most recent timestep (in the event that a method solves multiple, shorter timesteps) + //double m_TCS_T_sys_c_last; //[K] Temperature (bulk) of cold runners & headers in previous timestep + //std::vector m_TCS_T_htf_ave_last; //[K] Average HTF temperature in each SCA + //double m_TCS_T_sys_h_last; //[K] Temperature (bulk) of hot runners & headers in previous timestep + //// Latest temperatures solved during present call to this class + //double m_TCS_T_sys_c; //[K] Temperature (bulk) of cold runners & headers + //std::vector m_TCS_T_htf_in; //[K] Inlet HTF temperature to each SCA + //std::vector m_TCS_T_htf_ave; //[K] Average HTF temperature in each SCA + //std::vector m_TCS_T_htf_out; //[K] Outlet HTF temperature to each SCA + //double m_TCS_T_sys_h; //[K] Solar field HTF outlet temperature + //// ********************************************* + //// ********************************************* - 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 + // Private Methods +private: - // member string for exception messages - std::string m_error_msg; + 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*/); - C_csp_collector_receiver::E_csp_cr_modes m_operating_mode_converged; - C_csp_collector_receiver::E_csp_cr_modes m_operating_mode; + 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); - const double fp_offset = 10; // freeze protection offset + void set_output_value(); - // *********************** - // ***** T E M P ****** - double m_step_recirc; + // 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); - double m_current_hr; - double m_current_day; + 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*); - int counter = 0; - // Fields NOT in Trough - double N_run_mult; - + void loop_optical_eta(const C_csp_weatherreader::S_outputs& weather, + const C_csp_solver_sim_info& sim_info); - + void loop_optical_eta_off(); - const double Pi = acos(-1); - const double pi = Pi; - const double d2r = pi / 180.; - const double r2d = 180. / pi; - const double g = 9.81; //gravitation constant - const double mtoinch = 39.3700787; //[m] -> [in] + void loop_optical_wind_stow(); - double m_eta_optical; //Collector total optical efficiency - double eta_opt_fixed; - 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; - emit_table m_epsilon_abs; + void update_last_temps(); + void reset_last_temps(); + void apply_control_defocus(double defocus /*-*/); - std::vector mv_HCEguessargs; + void apply_component_defocus(double defocus /*-*/); - string m_piping_summary; + // From sam_mw_lf_type262_salt - //double m_sf_def; + double Pump_SGS(double rho, double m_dotsf, double sm); - std::unique_ptr m_evac_receiver; + 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); - // Private Methods -private: + double FricFactor(double Rough, double Reynold); - // Methods IN trough + static double m_dot_runner(double m_dot_field, int nfieldsec, int irnr); - 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*/); + static double m_dot_header(double m_dot_field, int nfieldsec, int nLoopsField, int ihdr); - 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(); + // Public Fields +public: - // 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); + // INPUTS - // Methods NOT in trough + 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 - 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*); + 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) + + 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 - // Public Fields -public: - 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 - double m_SCA_drives_elec; // [W/SCA] Tracking power, in Watts per SCA drive - 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 - - //int m_fthrok; // [-] Flag to allow partial defocusing of the collectors - //int m_fthrctrl; // [-] Defocusing strategy - double m_ColAz; // [deg] Collector azimuth angle - //double m_ColTilt; // [deg] Collector tilt angle (0 is horizontal, 90deg is vertical) - - 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 - - std::vector m_SCADefocusArray; //[-] Order in which the SCA's should be defocused - - int m_opt_model; // The optical model (1=Solar position ; 2=Collector incidence table ; 3 = IAM polys) - 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_epsilon_glass; // Glass envelope emissivity - vector m_Tau_envelope; // [-] Envelope transmittance - - 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 - - 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_L_hdr; //[m] Lengths of 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_DP_loop; //[bar] Pressure drop in loop sections - std::vector m_T_loop_dsn; //[C] Temperature entering loop sections at design - std::vector m_P_loop_dsn; //[bar] Gauge pessure in loop sections at design - std::vector m_T_loop; //[K] Temperature entering loop sections - //std::vector m_P_loop; //[Pa] Gauge pessure in loop sections - // Fresnel Only Inputs - 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 + // 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_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_DP_loop; // [bar] Pressure drop in loop sections + std::vector m_T_loop_dsn; // [C] Temperature entering loop sections at design + std::vector m_P_loop_dsn; // [bar] Gauge pessure in loop sections at design + std::vector m_T_loop; // [K] Temperature entering loop sections - double m_L_rnr_pb; //[m] Length of hot or cold runner pipe around the power block 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 trough 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 + 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 + + + // DEBUG + vector call_per_step; // Methods public: @@ -683,56 +657,6 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver bool design_solar_mult(); - // Methods IN trough - - 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); - - //void EvacReceiver(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, int ncall, double time, - // //outputs - // double& q_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args); - - //double fT_2(double q_12conv, double T_1, double T_2g, double v_1, int hv); - - //void FQ_34CONV(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(double T_3, double T_4, double T_7, double epsilon_abs_v, int hv, double& q_34rad, double& h_34); - - //void FQ_56CONV(double T_5, double T_6, double P_6, double v_6, int hv, double& q_56conv, double& h_6); - - //double FQ_COND_BRACKET(double T_3, double T_6, double P_6, double v_6); - - //double FK_23(double T_2, double T_3, int hv); - - 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); - - // *********************************** BONUS Methods from Trough - - 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); - // Classes public: @@ -741,17 +665,17 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // 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_trough; + 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_trough, const C_csp_weatherreader::S_outputs& weather, + 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_trough = pc_trough; + 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] @@ -764,16 +688,16 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver class C_mono_eq_T_htf_loop_out : public C_monotonic_equation { private: - C_csp_fresnel_collector_receiver* mpc_trough; + 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_trough, const C_csp_weatherreader::S_outputs& weather, + 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_trough = pc_trough; + mpc_fresnel = pc_fresnel; ms_weather = weather; m_T_cold_in = T_htf_cold_in; //[K] ms_sim_info = sim_info; @@ -787,7 +711,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // 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_trough; + 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; @@ -796,10 +720,10 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver double m_Q_htf_fp; //[MJ] - C_mono_eq_freeze_prot_E_bal(C_csp_fresnel_collector_receiver* pc_trough, const C_csp_weatherreader::S_outputs& weather, + 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_trough = pc_trough; + mpc_fresnel = pc_fresnel; ms_weather = weather; m_m_dot_loop = m_dot_loop; //[kg/s] ms_sim_info = sim_info; From ffa22b59e629195bf7ea195b95e97165b5862dcb Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Fri, 24 Mar 2023 14:37:57 -0600 Subject: [PATCH 13/46] Fix actual total aperture and solar multiple calculations. Add design mass flow rate for field and power cycle --- ssc/cmod_fresnel_physical.cpp | 39 ++++++++++++------- tcs/csp_solver_fresnel_collector_receiver.cpp | 28 +++++++++++-- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 235311107..89a75c8e7 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -299,8 +299,7 @@ static var_info _cm_vtab_fresnel_physical[] = { { 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", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_cycle_des", "Cycle design HTF mass flow rate", "kg/s", "", "System Design Calc", "*", "", "" }, - + // Solar Field { SSC_OUTPUT, SSC_NUMBER, "q_dot_rec_des", "Receiver thermal output at design", "MWt", "", "Receiver", "*", "", "" }, @@ -315,8 +314,9 @@ static var_info _cm_vtab_fresnel_physical[] = { { 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, "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", "*", "", "" }, // Collector and Receiver @@ -328,6 +328,7 @@ static var_info _cm_vtab_fresnel_physical[] = { // 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", "*", "", "" }, @@ -343,6 +344,7 @@ static var_info _cm_vtab_fresnel_physical[] = { // 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 @@ -1155,7 +1157,6 @@ class cm_fresnel_physical : public compute_module ssc_cmod_update, (void*)(this)); - // Set solver reporting outputs { // Simulation Kernel @@ -1297,7 +1298,7 @@ class cm_fresnel_physical : public compute_module double q_dot_pc_des = W_dot_cycle_des / eta_ref; //[MWt] Q_tes = q_dot_pc_des * tshours; //[MWt-hr] - double m_dot_htf_cycle_des = c_fresnel.m_m_dot_design; // [kg/s] + 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; @@ -1338,6 +1339,7 @@ class cm_fresnel_physical : public compute_module 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); } // Collector and Receiver @@ -1385,7 +1387,7 @@ class cm_fresnel_physical : public compute_module // Power Cycle - double m_dot_htf_pc_des; //[kg/s] + 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] @@ -1399,7 +1401,7 @@ class cm_fresnel_physical : public compute_module 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, + 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*/, @@ -1409,6 +1411,7 @@ class cm_fresnel_physical : public compute_module // 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 @@ -1421,7 +1424,6 @@ class cm_fresnel_physical : public compute_module // Assign { assign("nameplate", nameplate_des); - assign("m_dot_htf_cycle_des", m_dot_htf_cycle_des); assign("W_dot_bop_design", W_dot_bop_design); assign("W_dot_fixed", W_dot_fixed_parasitic_design); @@ -1430,6 +1432,15 @@ class cm_fresnel_physical : public compute_module 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 @@ -1781,7 +1792,7 @@ class cm_fresnel_physical : public compute_module double contigency_percent, // csp.mslf.cost.contingency_percent double total_land_area, // csp.mslf.cost.total_land_area - double nameplate, // csp.mslf.cost.nameplate + 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 @@ -1850,11 +1861,11 @@ class cm_fresnel_physical : public compute_module // csp.mslf.cost.epc.total double epc_total = calc_epc_total(epc_per_acre, total_land_area, epc_percent, total_direct, - nameplate, epc_per_watt, epc_fixed); + nameplate_MWe, epc_per_watt, epc_fixed); // csp.mslf.cost.plm.total double plm_total = calc_plm_total(plm_per_acre, total_land_area, plm_percent, total_direct, - nameplate, plm_per_watt, plm_fixed); + nameplate_MWe, plm_per_watt, plm_fixed); // csp.mslf.cost.total_indirect double total_indirect = calc_total_indirect(epc_total, plm_total); @@ -1866,7 +1877,7 @@ class cm_fresnel_physical : public compute_module double total_installed = calc_total_installed(total_direct, total_indirect, sales_tax_total); // csp.mslf.cost.installed_per_capacity - double installed_per_cap = calc_installed_per_cap(total_installed, nameplate); + double installed_per_cap = calc_installed_per_cap(total_installed, nameplate_MWe); // Set Outputs { @@ -1994,7 +2005,7 @@ class cm_fresnel_physical : public compute_module // csp.mslf.cost.installed_per_capacity static double calc_installed_per_cap(double total_installed, double nameplate) { - return 0.001 * total_installed * nameplate; + return 0.001 * total_installed / nameplate; } diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index ab310cbcf..e77192e86 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -950,6 +950,10 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade } else { + int hr = (sim_info.ms_ts.m_time) / 3600; + if (hr == 6360) + int x = 0; + // First, clear all the values calculated below loop_optical_eta_off(); @@ -1946,7 +1950,7 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() return false; } - //the estimated mass flow rate at design + //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; @@ -1967,8 +1971,11 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() m_m_dot_loop_des = m_m_dot_design / (double)m_nLoops; // [kg/s] - //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] + + // 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] @@ -2206,6 +2213,12 @@ void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_outputs& C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, const C_csp_solver_sim_info& sim_info) { + // DEBUG + int hr = sim_info.ms_ts.m_time / 3600; + if (hr == 50) + int x = 0; + + // Always reset last temps reset_last_temps(); @@ -3032,12 +3045,18 @@ bool C_csp_fresnel_collector_receiver::design_solar_mult() 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; } @@ -3049,6 +3068,9 @@ bool C_csp_fresnel_collector_receiver::design_solar_mult() } } + // 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); From 0c93c4de98e8ed024e1c9d2a8f32c41191b416aa Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Fri, 24 Mar 2023 16:22:25 -0600 Subject: [PATCH 14/46] Move MSLF installation cost calculations to csp_system_costs --- ssc/cmod_fresnel_physical.cpp | 248 +--------------------------------- tcs/csp_system_costs.cpp | 111 +++++++++++++++ tcs/csp_system_costs.h | 56 ++++++++ 3 files changed, 169 insertions(+), 246 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 89a75c8e7..61da850e7 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -38,6 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #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 @@ -1486,7 +1487,7 @@ class cm_fresnel_physical : public compute_module 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 - 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, + 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, @@ -1768,251 +1769,6 @@ class cm_fresnel_physical : public compute_module assign("kwh_per_kw", (ssc_number_t)kWh_per_kW); } - - - - - static 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 - - - - ) - { - // Outputs - - // csp.mslf.cost.power_plant - double power_plant = calc_power_plant(power_plant_mwe, power_plant_cost_per_kwe); - - // csp.mslf.cost.ts - double ts = calc_ts(ts_mwht, ts_per_kwht); - - // csp.mslf.cost.site_improvements - double site_improvements = calc_site_improvements(site_improvement_area, site_improvement_cost_per_m2); - - // csp.mslf.cost.bop - double bop = calc_bop(bop_mwe, bop_per_kwe); - - // csp.mslf.cost.solar_field - double solar_field = calc_solar_field(sf_area, sf_cost_per_m2); - - // csp.mslf.cost.htf_system - double htf_system = calc_htf_system(htf_area, htf_cost_per_m2); - - // csp.mslf.cost.fossil_backup - double fossil_backup = calc_fossil_backup(fossil_mwe, fossil_cost_per_kwe); - - // csp.mslf.cost.contingency - double contingency = calc_contingency(contigency_percent, site_improvements, solar_field, - htf_system, fossil_backup, power_plant, bop, ts); - - // csp.mslf.cost.total_direct - double total_direct = calc_total_direct(contingency, site_improvements, solar_field, htf_system, - fossil_backup, power_plant, bop, ts); - - // csp.mslf.cost.epc.total - double epc_total = calc_epc_total(epc_per_acre, total_land_area, epc_percent, total_direct, - nameplate_MWe, epc_per_watt, epc_fixed); - - // csp.mslf.cost.plm.total - double plm_total = calc_plm_total(plm_per_acre, total_land_area, plm_percent, total_direct, - nameplate_MWe, plm_per_watt, plm_fixed); - - // csp.mslf.cost.total_indirect - double total_indirect = calc_total_indirect(epc_total, plm_total); - - // csp.mslf.cost.sales_tax.total - double sales_tax_total = calc_sales_tax_total(sales_tax_value, total_direct, sales_tax_percent); - - // csp.mslf.cost.total_installed - double total_installed = calc_total_installed(total_direct, total_indirect, sales_tax_total); - - // csp.mslf.cost.installed_per_capacity - double installed_per_cap = calc_installed_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; - } - } - - // csp.mslf.cost.power_plant - static double calc_power_plant(double power_plant_mwe, double power_plant_cost_per_kwe) - { - return power_plant_mwe * power_plant_cost_per_kwe * 1000.0; - } - - // csp.mslf.cost.ts - static double calc_ts(double ts_mwht, double ts_per_kwht) - { - return ts_mwht * ts_per_kwht * 1000.0; - } - - // csp.mslf.cost.site_improvements - static double calc_site_improvements(double site_improvement_area, double site_improvement_cost_per_m2) - { - return site_improvement_area * site_improvement_cost_per_m2; - } - - // csp.mslf.cost.bop - static double calc_bop(double bop_mwe, double bop_per_kwe) - { - return bop_mwe * bop_per_kwe * 1000.0; - } - - // csp.mslf.cost.solar_field - static double calc_solar_field(double sf_area, double sf_cost_per_m2) - { - return sf_area * sf_cost_per_m2; - } - - // csp.mslf.cost.htf_system - static double calc_htf_system(double htf_area, double htf_cost_per_m2) - { - return htf_area * htf_cost_per_m2; - } - - // csp.mslf.cost.fossil_backup - static double calc_fossil_backup(double fossil_mwe, double fossil_cost_per_kwe) - { - return fossil_mwe * fossil_cost_per_kwe * 1000.0; - } - - // csp.mslf.cost.contingency - static double calc_contingency(double contingency_percent, double site_improvements, double solar_field, - double htf_system, double fossil_backup, double power_plant, - double bop, double ts) - { - double sum = site_improvements + solar_field + htf_system + fossil_backup + power_plant + bop + ts; - - return (contingency_percent / 100.0) * sum; - } - - // csp.mslf.cost.total_direct - static double calc_total_direct(double contingency, double site_improvements, double solar_field, - double htf_system, double fossil_backup, double power_plant, - double bop, double ts) - { - return contingency + site_improvements + solar_field + htf_system + fossil_backup + power_plant + bop + ts; - } - - // csp.mslf.cost.epc.total - static double calc_epc_total(double epc_per_acre, double total_land_area, double epc_percent, - double total_direct, double nameplate, double epc_per_watt, - double epc_fixed) - { - double epc = (epc_per_acre * total_land_area) - + ((epc_percent / 100.0) * total_direct) - + (nameplate * 1e6 * epc_per_watt) - + epc_fixed; - - return epc; - } - - // csp.mslf.cost.plm.total - static double calc_plm_total(double plm_per_acre, double total_land_area, double plm_percent, double total_direct, - double nameplate, double plm_per_watt, double plm_fixed) - { - double plm = (plm_per_acre * total_land_area) - + ((plm_percent / 100.0) * total_direct) - + (nameplate * 1e6 * plm_per_watt) - + plm_fixed; - - return plm; - } - - // csp.mslf.cost.total_indirect - static double calc_total_indirect(double epc_total, double plm_total) - { - return epc_total + plm_total; - } - - // csp.mslf.cost.sales_tax.total - static double calc_sales_tax_total(double sales_tax_value, double total_direct_cost, double sales_tax_percent) - { - return (sales_tax_value / 100.0) * total_direct_cost * (sales_tax_percent / 100.0); - } - - // csp.mslf.cost.total_installed - static double calc_total_installed(double total_direct, double total_indirect, double sales_tax_total) - { - return total_direct + total_indirect + sales_tax_total; - } - - // csp.mslf.cost.installed_per_capacity - static double calc_installed_per_cap(double total_installed, double nameplate) - { - return 0.001 * total_installed / nameplate; - } - - - - - - }; DEFINE_MODULE_ENTRY(fresnel_physical, "Physical Fresnel applications", 1) 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 /*$*/ ); From 6db9dfdb4d1a3739f34e947b2dfc9236f649f0a9 Mon Sep 17 00:00:00 2001 From: taylorbrown75 Date: Thu, 30 Mar 2023 14:29:05 -0600 Subject: [PATCH 15/46] Clean code and reorganize cmod vartable --- ssc/cmod_fresnel_physical.cpp | 73 +++++++++++++------ tcs/csp_solver_fresnel_collector_receiver.cpp | 13 +--- 2 files changed, 53 insertions(+), 33 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 61da850e7..10ada507b 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -212,6 +212,20 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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" }, @@ -246,24 +260,6 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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", "", "SIMULATION_PARAMETER" }, - - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "-", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "-", "", "tou", "?=0", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "-", "", "tou", "?=''", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "-", "", "tou", "?='ampl sdk_solution.run'", "", "SIMULATION_PARAMETER" }, - - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "-", "", "tou", "?=1", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "-", "", "tou", "?=-1", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "q_rec_standby", "Receiver standby energy consumption", "kWt", "", "tou", "?=9e99", "", "SIMULATION_PARAMETER" }, - /*??????????????*/{ SSC_INPUT, SSC_NUMBER, "q_rec_heattrace", "Receiver heat trace energy consumption during startup", "kWe-hr", "", "tou", "?=0.0", "", "SIMULATION_PARAMETER" }, - - /*??????????????*/{ SSC_INPUT, SSC_ARRAY, "timestep_load_fractions", "Turbine load fraction for each timestep, alternative to block dispatch", "", "", "tou", "?", "", "SIMULATION_PARAMETER" }, - // Capital Costs // Direct Capital Costs @@ -858,6 +854,30 @@ class cm_fresnel_physical : public compute_module 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"), @@ -885,10 +905,21 @@ class cm_fresnel_physical : public compute_module as_boolean("tanks_in_parallel"), V_tes_des, false, - as_double("tes_pump_coef") + 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_boolean("calc_design_pipe_vals"), - //as_double("tes_pump_coef"), + + //as_double("eta_pump"), //as_boolean("has_hot_tank_bypass"), //as_double("T_tank_hot_inlet_min"), diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index e77192e86..9ec02aa53 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -1160,7 +1160,7 @@ void C_csp_fresnel_collector_receiver::reset_last_temps() void C_csp_fresnel_collector_receiver::apply_control_defocus(double defocus) { - // Uses m_q_i, m_costh, and input defocus to calculate m_q_SCA_control_df + // Uses m_q_i, and input defocus to calculate m_q_SCA_control_df // Store control defocus m_control_defocus = defocus; @@ -1181,7 +1181,6 @@ void C_csp_fresnel_collector_receiver::apply_component_defocus(double defocus /* for (int i = 0; i < m_nMod; i++) { - //int CT = (int)m_SCAInfoArray(i, 1) - 1; // Collector type m_q_SCA[i] = defocus * m_q_SCA_control_df[i]; } } @@ -4551,13 +4550,3 @@ double EvacReceiverModel::FK_23_v2(double T_2, double T_3, int hv) return m_AbsorberPropMat.at(hv)->cond(T_23); } - - - - - - - - - - From ee080a372ca733bb1534e80eb64a68451854f260 Mon Sep 17 00:00:00 2001 From: taylorbrown75 Date: Fri, 31 Mar 2023 09:46:07 -0600 Subject: [PATCH 16/46] Add ctime to fix clock_t bug --- ssc/cmod_fresnel_physical.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 10ada507b..a142f53b4 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -40,6 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "csp_dispatch.h" #include "csp_system_costs.h" +#include #include #include From 3f0d5405ddde592f717587671d653a6b1a80967d Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 10 Apr 2023 11:07:29 -0500 Subject: [PATCH 17/46] merge develop --- tcs/csp_solver_fresnel_collector_receiver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 9ec02aa53..ed62c8c0e 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -784,7 +784,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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, 5) * 1000.0; //[J/kg-K] + 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 - From 30f52dd1ddc39bdc266bd2a6ac8d7402270e0498 Mon Sep 17 00:00:00 2001 From: tyneises Date: Tue, 18 Apr 2023 15:32:38 -0500 Subject: [PATCH 18/46] sort dispatch and sim_type 2 in fresnel cmod --- ssc/cmod_fresnel_physical.cpp | 289 +++++++++++++++++----------------- 1 file changed, 143 insertions(+), 146 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index a142f53b4..1bf1ca751 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -256,8 +256,8 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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","", "SIMULATION_PARAMETER" }, - /*Fin Sol Mode Sing Own*/{ SSC_INPUT, SSC_NUMBER, "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","", "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_NUMBER, "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", "", "SIMULATION_PARAMETER" }, @@ -952,11 +952,39 @@ class cm_fresnel_physical : public compute_module // 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"); - { - tou_params->mc_csp_ops.mc_weekdays = as_matrix("weekday_schedule"); - tou_params->mc_csp_ops.mc_weekends = as_matrix("weekend_schedule"); + + 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); @@ -966,172 +994,141 @@ class cm_fresnel_physical : public compute_module tou_params->mc_pricing.mc_weekends = util::matrix_t(12, 24, 1.0); } - 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; - vector f_turb_tou_periods = as_vector_double("f_turb_tou_periods"); - size_t n_f_turbine = f_turb_tou_periods.size(); - ssc_number_t* p_f_turbine = &f_turb_tou_periods[0]; - 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"); + if (csp_financial_model > 0 && csp_financial_model < 5) { // Single Owner financial models - if (sim_type == 1) - { - 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"); + } - // 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 + } - 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 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"); + } - 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; - // 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); - } + 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 if (ppa_mult_model == 0) // standard diuranal input - { - tou_params->mc_pricing.mv_is_diurnal = true; - - bool are_all_assigned = is_assigned("dispatch_sched_weekday") || is_assigned("dispatch_sched_weekend") - || is_assigned("dispatch_factor1") || is_assigned("dispatch_factor2") || is_assigned("dispatch_factor3") - || is_assigned("dispatch_factor4") || is_assigned("dispatch_factor5") || is_assigned("dispatch_factor6") - || is_assigned("dispatch_factor7") || is_assigned("dispatch_factor8") || is_assigned("dispatch_factor9"); - - if (are_all_assigned) { - - tou_params->mc_pricing.mc_weekdays = as_matrix("dispatch_sched_weekday"); - tou_params->mc_pricing.mc_weekends = as_matrix("dispatch_sched_weekend"); - - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, 0.0); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][0] = as_double("dispatch_factor1"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][1] = as_double("dispatch_factor2"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][2] = as_double("dispatch_factor3"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][3] = as_double("dispatch_factor4"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][4] = as_double("dispatch_factor5"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][5] = as_double("dispatch_factor6"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][6] = as_double("dispatch_factor7"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][7] = as_double("dispatch_factor8"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][8] = as_double("dispatch_factor9"); - } - 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 { + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(n_steps_fixed, -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 + else if (ppa_mult_model == 0) // standard diuranal input + { + tou_params->mc_pricing.mv_is_diurnal = true; + + bool are_all_assigned = is_assigned("dispatch_sched_weekday") || is_assigned("dispatch_sched_weekend") + || is_assigned("dispatch_factor1") || is_assigned("dispatch_factor2") || is_assigned("dispatch_factor3") + || is_assigned("dispatch_factor4") || is_assigned("dispatch_factor5") || is_assigned("dispatch_factor6") + || is_assigned("dispatch_factor7") || is_assigned("dispatch_factor8") || is_assigned("dispatch_factor9"); + + if (are_all_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"); + + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE].resize(9, 0.0); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][0] = as_double("dispatch_factor1"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][1] = as_double("dispatch_factor2"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][2] = as_double("dispatch_factor3"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][3] = as_double("dispatch_factor4"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][4] = as_double("dispatch_factor5"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][5] = as_double("dispatch_factor6"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][6] = as_double("dispatch_factor7"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][7] = as_double("dispatch_factor8"); + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][8] = as_double("dispatch_factor9"); } - 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); + 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 == 6) { // use 'mp_energy_market_revenue' -> from Merchant Plant model - + } + 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; - 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) { - 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); - } + 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) + } + else if (csp_financial_model == 6) { // use 'mp_energy_market_revenue' -> from Merchant Plant model - tou_params->mc_pricing.mv_is_diurnal = false; + 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); + 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) { + 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] + } } - else { - throw exec_error("fresnel_physical", "csp_financial_model must be 1-8"); + 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 (sim_type == 2) - { + else if (csp_financial_model == 7 || csp_financial_model == 8) { // LCOE (7) and None (8) + 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); + // 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; From 9748bf22c1c806dfc990c3cc2053fe3b37d2712a Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 20 Apr 2023 16:21:41 -0600 Subject: [PATCH 19/46] Add runner length to cmod, rather than hard code value --- ssc/cmod_fresnel_physical.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 1bf1ca751..a9ced580c 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -153,7 +153,9 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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", "" }, - + /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "L_rnr_pb", "Length of runner pipe in power block", "m", "", "Powerblock", "*", "", "" }, + + // 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", "", "" }, @@ -557,12 +559,6 @@ class cm_fresnel_physical : public compute_module void exec() { - // Missing Variables - - // Field - // Hard Coded (currently no UI) - double L_rnr_pb = 50; - // Common Parameters bool is_dispatch = as_boolean("is_dispatch"); int sim_type = as_number("sim_type"); @@ -691,7 +687,7 @@ class cm_fresnel_physical : public compute_module 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 = L_rnr_pb; + c_fresnel.m_L_rnr_pb = as_number("L_rnr_pb"); c_fresnel.m_V_wind_des = as_number("V_wind_des"); c_fresnel.m_T_amb_sf_des = as_number("T_amb_sf_des"); From c67d4c7ddde1a076972d6822692871dd817ae7f9 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Mon, 24 Apr 2023 17:03:17 -0600 Subject: [PATCH 20/46] Fix ppa_price_input variable type bug. Define fresnel functions necessary for merchant plant. --- ssc/cmod_fresnel_physical.cpp | 19 ++- tcs/csp_solver_fresnel_collector_receiver.cpp | 158 +++++++++++++++--- tcs/csp_solver_fresnel_collector_receiver.h | 8 +- 3 files changed, 155 insertions(+), 30 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index a9ced580c..07a99fe90 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -95,6 +95,10 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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", "*", "", "" }, + // Collector and Receiver @@ -245,7 +249,7 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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_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" }, /*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" }, @@ -258,10 +262,10 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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_NUMBER, "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 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", "", "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 @@ -328,7 +332,7 @@ static var_info _cm_vtab_fresnel_physical[] = { // 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", "*", "", "" }, + { 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", "*", "", "" }, @@ -688,6 +692,9 @@ class cm_fresnel_physical : public compute_module 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"); @@ -1091,6 +1098,8 @@ class cm_fresnel_physical : public compute_module 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); } diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index ed62c8c0e..c0801da98 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -210,16 +210,25 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double DP_toField = 0.0; DP_fromField = 0.0; for (int i = 0; i < m_nrunsec; i++) { - DP_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, + 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 - DP_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, + 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; @@ -245,16 +254,17 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double + // 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_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]; @@ -1800,6 +1810,44 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs m_htfProps, m_airProps, m_AnnulusGasMat, m_AbsorberPropMat, m_Flow_type, m_A_cs, m_D_h)); } + // Calculate other design parameters + { + 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; + + 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 troughInfo; + troughInfo.ms_ts.m_time_start = 14817600.; + troughInfo.ms_ts.m_step = 5. * 60.; // 5-minute timesteps + troughInfo.ms_ts.m_time = troughInfo.ms_ts.m_time_start + troughInfo.ms_ts.m_step; + troughInfo.m_tou = 1.; + C_csp_collector_receiver::S_csp_cr_out_solver troughOutputs; + + steady_state(weatherValues, htfInletState, std::numeric_limits::quiet_NaN(), defocus, troughOutputs, troughInfo); + solved_params.m_T_htf_hot_des = m_T_field_out; + solved_params.m_dP_sf = troughOutputs.m_dP_sf; + } + return; } @@ -1991,6 +2039,12 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() 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_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_T_loop_dsn = m_T_loop; 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); @@ -2145,18 +2199,14 @@ C_csp_collector_receiver::E_csp_cr_modes C_csp_fresnel_collector_receiver::get_o double C_csp_fresnel_collector_receiver::get_startup_time() { - throw("C_csp_fresnel_collector_receiver::get_startup_time() is not complete"); - - - return std::numeric_limits::quiet_NaN(); + // 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() { - throw(C_csp_exception("C_csp_fresnel_collector_receiver::get_startup_energy() is not complete")); - - - return std::numeric_limits::quiet_NaN(); + // 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() @@ -2198,8 +2248,7 @@ double C_csp_fresnel_collector_receiver::get_tracking_power() double C_csp_fresnel_collector_receiver::get_col_startup_power() { - throw(C_csp_exception("C_csp_fresnel_collector_receiver::get_col_startup_power() is not complete")); - return std::numeric_limits::quiet_NaN(); //MWe-hr + 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) @@ -2824,7 +2873,7 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S // 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_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 @@ -2962,9 +3011,76 @@ double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_cs double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/) { - throw(C_csp_exception("C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx")); + // 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.; + + // Incidence angle + int doy = DateTime::CalculateDayOfYear(weather.m_year, weather.m_month, weather.m_day); // day of year + double time_start = ((doy - 1) * 24 + weather.m_hour + weather.m_minute / 60.) * 3600.; + double step = 3600.; + double time = time_start + step; + double time_hr = time / 3600.; // [hr] + double dt_hr = step / 3600.; // [hr] + double hour = fmod(time_hr, 24.); // [hr] + int day_of_year = (int)ceil(time_hr / 24.); // Day of the year + double B = (day_of_year - 1) * 360.0 / 365.0 * CSP::pi / 180.0; // Duffie & Beckman 1.5.3b + 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)); // Eqn of time in minutes + double Dec = 23.45 * sin(360.0 * (284.0 + day_of_year) / 365.0 * CSP::pi / 180.0) * CSP::pi / 180.0; // Declination in radians (Duffie & Beckman 1.6.1) + double SolarNoon = 12. - ((m_shift) * 180.0 / CSP::pi) / 15.0 - EOT / 60.0; // Solar Noon and time in hours + double HrA = hour - dt_hr; + double StdTime = HrA + 0.5 * dt_hr; + double SolarTime = StdTime + ((m_shift) * 180.0 / CSP::pi) / 15.0 + EOT / 60.0; + double omega = (SolarTime - 12.0) * 15.0 * CSP::pi / 180.0; // m_hour angle (arc of sun) in radians + double SolarAlt = asin(sin(Dec) * sin(m_latitude) + cos(m_latitude) * cos(Dec) * cos(omega)); + double SolarAz = (weather.m_solazi - 180.) * m_d2r; // [rad] Solar azimuth angle + double CosTh = sqrt(1.0 - pow(cos(SolarAlt - 0) - cos(0) * cos(SolarAlt) * (1.0 - cos(SolarAz - m_ColAz)), 2)); + double Theta = acos(CosTh); // [rad] + + // Incidence angle modifier (IAM) from Burkholder & Kutscher 2008 + double IamF1 = 0.000884; + double IamF2 = -0.0000537; + double IAM; + if (CosTh == 0) { + IAM = 0; + } + else { + IAM = std::min(1., (CosTh + IamF1 * Theta + IamF2 * pow(Theta, 2.)) / CosTh); + } + + // Heat loss, where temperatures are in [C] per Burkholder & Kutscher 2008 + // model coefficients for 2008 Schott PTR70 (vacuum) receiver + double A0 = 4.05; + double A1 = 0.247; + double A2 = -0.00146; + double A3 = 5.65e-06; + double A4 = 7.62e-08; + double A5 = -1.7; + double A6 = 0.0125; + double PerfFac = 1; + double T_amb = weather.m_tdry; // [C] + double W_spd = std::abs(weather.m_wspd); + double DNI = weather.m_beam; + double T_out_des = m_T_loop_out_des - 273.15; // [C] (converted from [C] to [K] in init and now back to [C]) + double T_in_des = m_T_loop_in_des - 273.15; // [C] (converted from [C] in [K] in init and now back to [C]) + double HLTerm1 = (A0 + A5 * sqrt(W_spd)) * (T_out_des - T_in_des); + double HLTerm2 = (A1 + A6 * sqrt(W_spd)) * ((pow(T_out_des, 2) - pow(T_in_des, 2)) / 2. - T_amb * (T_out_des - T_in_des)); + double HLTerm3 = ((A2 + A4 * DNI * CosTh * IAM) / 3.) * (pow(T_out_des, 3) - pow(T_in_des, 3)); + double HLTerm4 = (A3 / 4.) * (pow(T_out_des, 4) - pow(T_in_des, 4)); + double HL = (HLTerm1 + HLTerm2 + HLTerm3 + HLTerm4) / (T_out_des - T_in_des); //[W/m] + double HL_hces = std::max(HL * m_L_tot * m_nLoops * PerfFac, 0.); // [W] convert from W/m to W for entire field + + // Piping heat loss, at average hot/cold design temperature + double T_avg_des = 0.5 * (T_out_des + T_in_des); // [C] + + double row_distance = m_L_crossover; // use crossover as row distance + double HL_headers = m_nfsec * (2 * m_nhdrsec) * row_distance * m_D_hdr[m_nhdrsec] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + double HL_runners = 0.; + for (int i = 0; i < 2 * m_nrunsec; i++) { + HL_runners += 2. * m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + } - return std::numeric_limits::quiet_NaN(); + double HL_total = HL_hces + HL_headers + HL_runners; + return std::max(1. - HL_total * 1.e-6 / q_incident, 0.); } double C_csp_fresnel_collector_receiver::get_collector_area() diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index f2f7da1ac..719cc03f7 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -517,9 +517,9 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 @@ -536,7 +536,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 From 4dba44fd39c3401333de080c9cc87672dcb6adfb Mon Sep 17 00:00:00 2001 From: tyneises Date: Tue, 25 Apr 2023 11:15:45 -0500 Subject: [PATCH 21/46] update fresnel dispatch methods --- tcs/csp_solver_fresnel_collector_receiver.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index c0801da98..226ddc10a 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -2994,7 +2994,7 @@ double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_cs double m_q_dot_inc_sf_tot_ini(m_q_dot_inc_sf_tot); loop_optical_eta(weather, sim); - double eta_optical = m_EqOpteff; + double eta_optical = m_eta_optical; // m_EqOpteff; // Restore member variable values m_q_i = m_q_i_ini; @@ -3069,6 +3069,7 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con double HL = (HLTerm1 + HLTerm2 + HLTerm3 + HLTerm4) / (T_out_des - T_in_des); //[W/m] double HL_hces = std::max(HL * m_L_tot * m_nLoops * PerfFac, 0.); // [W] convert from W/m to W for entire field + /* // Piping heat loss, at average hot/cold design temperature double T_avg_des = 0.5 * (T_out_des + T_in_des); // [C] @@ -3078,9 +3079,11 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con for (int i = 0; i < 2 * m_nrunsec; i++) { HL_runners += 2. * m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] } + */ - double HL_total = HL_hces + HL_headers + HL_runners; - return std::max(1. - HL_total * 1.e-6 / q_incident, 0.); + double HL_total = HL_hces; // +HL_headers + HL_runners; + double eta_therm_approx = std::max(1. - HL_total * 1.e-6 / q_incident, 0.); + return eta_therm_approx; } double C_csp_fresnel_collector_receiver::get_collector_area() From 9ff382b1ff109b30e883740beb12664f6088af16 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 27 Apr 2023 16:36:04 -0600 Subject: [PATCH 22/46] Remove unnecessary hardcoded storage parameters --- ssc/cmod_fresnel_physical.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 07a99fe90..a7513aaec 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -858,7 +858,7 @@ class cm_fresnel_physical : public compute_module C_csp_two_tank_tes storage; { double V_tes_des = as_double("V_tes_des"); - double eta_pump = 0.85; + /*double eta_pump = 0.85; bool has_hot_tank_bypass = false; double T_tank_hot_inlet_min = 200; bool custom_tes_p_loss = false; @@ -881,7 +881,7 @@ class cm_fresnel_physical : public compute_module 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); + tes_lengths.assign(tes_length_vals, 11);*/ storage = C_csp_two_tank_tes( as_integer("Fluid"), @@ -909,8 +909,9 @@ class cm_fresnel_physical : public compute_module as_boolean("tanks_in_parallel"), V_tes_des, false, - as_double("tes_pump_coef"), - eta_pump, + as_double("tes_pump_coef") + ); + /*eta_pump, has_hot_tank_bypass, T_tank_hot_inlet_min, custom_tes_p_loss, @@ -921,7 +922,7 @@ class cm_fresnel_physical : public compute_module tes_lengths, pipe_rough, dP_discharge - ); + );*/ //as_double("eta_pump"), From 63d9aadb6b8f1cd98c3b74846e7a2cd8d143c87f Mon Sep 17 00:00:00 2001 From: taylorbrown75 Date: Mon, 1 May 2023 09:24:49 -0600 Subject: [PATCH 23/46] Remove temporary debugging code --- ssc/cmod_fresnel_physical.cpp | 3 -- tcs/csp_solver_fresnel_collector_receiver.cpp | 42 ++++++++++--------- tcs/csp_solver_fresnel_collector_receiver.h | 6 +-- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index a7513aaec..a91da84c2 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -1576,9 +1576,6 @@ class cm_fresnel_physical : public compute_module throw exec_error("fresnel_physical", csp_exception.m_error_message); } - // DEBUG - vector call_per_step = c_fresnel.call_per_step; - // If no exception, then report messages while (csp_solver.mc_csp_messages.get_message(&out_type, &out_msg)) { diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 226ddc10a..e137f354d 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -960,10 +960,6 @@ void C_csp_fresnel_collector_receiver::loop_optical_eta(const C_csp_weatherreade } else { - int hr = (sim_info.ms_ts.m_time) / 3600; - if (hr == 6360) - int x = 0; - // First, clear all the values calculated below loop_optical_eta_off(); @@ -1838,7 +1834,7 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs double defocus = 1; C_csp_solver_sim_info troughInfo; troughInfo.ms_ts.m_time_start = 14817600.; - troughInfo.ms_ts.m_step = 5. * 60.; // 5-minute timesteps + troughInfo.ms_ts.m_step = 15. * 60.; // 5-minute timesteps troughInfo.ms_ts.m_time = troughInfo.ms_ts.m_time_start + troughInfo.ms_ts.m_step; troughInfo.m_tou = 1.; C_csp_collector_receiver::S_csp_cr_out_solver troughOutputs; @@ -2213,7 +2209,7 @@ 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]; + 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; @@ -2261,12 +2257,6 @@ void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_outputs& C_csp_collector_receiver::S_csp_cr_out_solver& cr_out_solver, const C_csp_solver_sim_info& sim_info) { - // DEBUG - int hr = sim_info.ms_ts.m_time / 3600; - if (hr == 50) - int x = 0; - - // Always reset last temps reset_last_temps(); @@ -2862,14 +2852,13 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S // 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] - bool custom_sf_pipe_sizes = true; 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; double rho_ave = m_htfProps.dens((m_T_loop_out_des + m_T_loop_in_des) / 2.0, 0.0); //kg/m3 - 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); + //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); // 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 @@ -2887,6 +2876,7 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S 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, @@ -3069,19 +3059,33 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con double HL = (HLTerm1 + HLTerm2 + HLTerm3 + HLTerm4) / (T_out_des - T_in_des); //[W/m] double HL_hces = std::max(HL * m_L_tot * m_nLoops * PerfFac, 0.); // [W] convert from W/m to W for entire field - /* + // Piping heat loss, at average hot/cold design temperature double T_avg_des = 0.5 * (T_out_des + T_in_des); // [C] double row_distance = m_L_crossover; // use crossover as row distance - double HL_headers = m_nfsec * (2 * m_nhdrsec) * row_distance * m_D_hdr[m_nhdrsec] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + //double HL_headers_old = m_nfsec * (2 * m_nhdrsec) * row_distance * m_D_hdr[0] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + + + double HL_headers = 0; + // Sum hot headers heat loss + for (int i = 0; i < m_nhdrsec; i++) + { + HL_headers += m_L_crossover * m_D_hdr[i] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); + } + HL_headers *= 2.0; // multiply to account for cold headers + HL_headers *= m_nfsec; // account for field sections + + + double HL_runners = 0.; - for (int i = 0; i < 2 * m_nrunsec; i++) { + for (int i = 0; i < m_nrunsec; i++) { HL_runners += 2. * m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] } - */ + HL_runners *= 2.0; // account for cold headers + - double HL_total = HL_hces; // +HL_headers + HL_runners; + double HL_total = HL_hces + HL_headers + HL_runners; double eta_therm_approx = std::max(1. - HL_total * 1.e-6 / q_incident, 0.); return eta_therm_approx; } diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 719cc03f7..4007c83ea 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -461,6 +461,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 @@ -579,10 +580,6 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 - - // DEBUG - vector call_per_step; - // Methods public: @@ -637,6 +634,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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, From 219f988badb8f8b84d5be88bf21bac7830843cef Mon Sep 17 00:00:00 2001 From: taylorbrown75 Date: Wed, 31 May 2023 15:31:19 -0600 Subject: [PATCH 24/46] Fix steady state optical efficiency bug. Add steady state design point outputs to cmod --- ssc/cmod_fresnel_physical.cpp | 43 ++++++++- tcs/csp_solver_fresnel_collector_receiver.cpp | 93 ++++++++++++++++--- tcs/csp_solver_fresnel_collector_receiver.h | 14 ++- 3 files changed, 131 insertions(+), 19 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index a91da84c2..f2a606783 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -308,20 +308,30 @@ static var_info _cm_vtab_fresnel_physical[] = { // 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", "", "", "Receiver", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "loop_therm_eff", "Loop thermal efficiency", "", "", "Receiver", "*", "", "" }, - { SSC_OUTPUT, SSC_NUMBER, "loop_eff", "Total loop conversion efficiency", "", "", "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, "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", "MW", "", "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", "*", "", "" }, // Collector and Receiver { SSC_OUTPUT, SSC_NUMBER, "DP_pressure_loss", "Total loop pressure loss at design", "bar", "", "Receiver", "*", "", "" }, @@ -1346,7 +1356,7 @@ class cm_fresnel_physical : public compute_module 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_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] @@ -1359,6 +1369,18 @@ class cm_fresnel_physical : public compute_module 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; + // Assign { assign("q_dot_rec_des", q_dot_rec_des); @@ -1376,6 +1398,17 @@ class cm_fresnel_physical : public compute_module 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); } // Collector and Receiver diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index e137f354d..9cc476114 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -407,7 +407,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle // BULK Temperature calculations { - // This values is the Bulk Temperature at the *end* of the timestep (type 262 line 1551; trough line 1187) + // 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; @@ -461,8 +461,9 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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] - //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); - 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); + // 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; @@ -896,6 +897,7 @@ void C_csp_fresnel_collector_receiver::header_design(int nhsec, int nfsec, int n //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... @@ -1806,7 +1808,7 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs m_htfProps, m_airProps, m_AnnulusGasMat, m_AbsorberPropMat, m_Flow_type, m_A_cs, m_D_h)); } - // Calculate other design parameters + // Run steady state { C_csp_weatherreader::S_outputs weatherValues; weatherValues.m_lat = init_inputs.m_latitude; @@ -1825,6 +1827,7 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs 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; @@ -1832,16 +1835,18 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs //htfInletState.m_qual = 0; htfInletState.m_temp = m_T_loop_in_des - 273.15; double defocus = 1; - C_csp_solver_sim_info troughInfo; - troughInfo.ms_ts.m_time_start = 14817600.; - troughInfo.ms_ts.m_step = 15. * 60.; // 5-minute timesteps - troughInfo.ms_ts.m_time = troughInfo.ms_ts.m_time_start + troughInfo.ms_ts.m_step; - troughInfo.m_tou = 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 troughOutputs; - steady_state(weatherValues, htfInletState, std::numeric_limits::quiet_NaN(), defocus, troughOutputs, troughInfo); + steady_state(weatherValues, htfInletState, std::numeric_limits::quiet_NaN(), defocus, troughOutputs, fresnelInfo); solved_params.m_T_htf_hot_des = m_T_field_out; solved_params.m_dP_sf = troughOutputs.m_dP_sf; + + this->m_dP_des_SS = troughOutputs.m_dP_sf; } return; @@ -2850,13 +2855,75 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S } while (ss_diff / 200. > tol); + + // Calculate Steady State Results + double temp_initial = m_T_loop_in_des; // K + double temp_final = cr_out_solver.m_T_salt_hot + 273.15; // convert from C to K + double mdot = cr_out_solver.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 = cr_out_solver.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; + + double Q_available = m_Ap_tot * weather.m_beam * m_eta_optical_des_SS; + 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; + // 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; - double rho_ave = m_htfProps.dens((m_T_loop_out_des + m_T_loop_in_des) / 2.0, 0.0); //kg/m3 + //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); @@ -3104,12 +3171,12 @@ bool C_csp_fresnel_collector_receiver::design_solar_mult() // Calculate nLoops, depending on designing for solar mult or total field aperture { - // Optical Derate + // 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 + // Optical Normal (Mirror/Collector) m_opt_normal = 0; m_opt_normal = m_TrackingError * m_GeomEffects * m_reflectivity * m_Dirt_mirror * m_Error; diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 4007c83ea..675b20f03 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -207,7 +207,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver // 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 efficieny (theta = 0) from the solar field + 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; @@ -580,6 +580,18 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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)) + // Methods public: From 6f076499ed188cf21a794d0c8b501cc7272713a5 Mon Sep 17 00:00:00 2001 From: taylorbrown75 Date: Thu, 1 Jun 2023 11:34:28 -0600 Subject: [PATCH 25/46] Change dispatch factors to single array, to work with latest develop code --- ssc/cmod_fresnel_physical.cpp | 49 +++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index f2a606783..60a8f2477 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -252,15 +252,20 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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" }, - /*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" }, + { 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" }, @@ -1061,26 +1066,24 @@ class cm_fresnel_physical : public compute_module { tou_params->mc_pricing.mv_is_diurnal = true; - bool are_all_assigned = is_assigned("dispatch_sched_weekday") || is_assigned("dispatch_sched_weekend") - || is_assigned("dispatch_factor1") || is_assigned("dispatch_factor2") || is_assigned("dispatch_factor3") - || is_assigned("dispatch_factor4") || is_assigned("dispatch_factor5") || is_assigned("dispatch_factor6") - || is_assigned("dispatch_factor7") || is_assigned("dispatch_factor8") || is_assigned("dispatch_factor9"); + // 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 (are_all_assigned || is_dispatch) { + 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); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][0] = as_double("dispatch_factor1"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][1] = as_double("dispatch_factor2"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][2] = as_double("dispatch_factor3"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][3] = as_double("dispatch_factor4"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][4] = as_double("dispatch_factor5"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][5] = as_double("dispatch_factor6"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][6] = as_double("dispatch_factor7"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][7] = as_double("dispatch_factor8"); - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][8] = as_double("dispatch_factor9"); + 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.); From dc7e84f2a62bbe9a31b3cb5b60e0f983aee2b30c Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Tue, 6 Jun 2023 09:53:23 -0600 Subject: [PATCH 26/46] Add steady state loop calculations, pumping power, and receiver and header heat losses to cmod. Add piping summary to notices --- ssc/cmod_fresnel_physical.cpp | 32 ++++- tcs/csp_solver_fresnel_collector_receiver.cpp | 129 +++++++++++------- tcs/csp_solver_fresnel_collector_receiver.h | 7 + 3 files changed, 118 insertions(+), 50 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 60a8f2477..e58ebc910 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -328,7 +328,7 @@ static var_info _cm_vtab_fresnel_physical[] = { { 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", "MW", "", "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", "*", "", "" }, @@ -337,6 +337,17 @@ static var_info _cm_vtab_fresnel_physical[] = { { 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", "*", "", "" }, @@ -1373,7 +1384,7 @@ class cm_fresnel_physical : public compute_module 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 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] @@ -1383,6 +1394,14 @@ class cm_fresnel_physical : public compute_module 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 { @@ -1412,6 +1431,15 @@ class cm_fresnel_physical : public compute_module 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 diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 9cc476114..c68af3248 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -735,6 +735,8 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle } } + 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 @@ -2048,6 +2050,7 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() m_T_loop_dsn = m_T_loop; 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] @@ -2857,70 +2860,100 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S // Calculate Steady State Results - double temp_initial = m_T_loop_in_des; // K - double temp_final = cr_out_solver.m_T_salt_hot + 273.15; // convert from C to K - double mdot = cr_out_solver.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 = cr_out_solver.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++) + // Field Results + double temp_initial = htf_state_in.m_temp + 273.15; // K + double temp_final = m_T_field_out; // K + double mdot = cr_out_solver.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 = cr_out_solver.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 mdot_hdr_section = this->m_dot_header(mdot, m_nfsec, this->m_nLoops, i); - double D_hdr_section = m_D_hdr[i]; + 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)); + 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; + 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); } - 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)); } - m_V_hdr_min_des_SS = V_min_calc; - m_V_hdr_max_des_SS = V_max_calc; + // 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 * weather.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 * weather.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; - 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)); + // Pumping Power + m_W_dot_pump_des_SS = m_W_dot_pump; + + // 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 - 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; - double Q_available = m_Ap_tot * weather.m_beam * m_eta_optical_des_SS; - 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; + + + + + + // 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] + 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; diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 675b20f03..009f7748e 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -591,6 +591,13 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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: From c0c0177273f395dc40b4d7d8b42377c989d2a239 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 8 Jun 2023 11:27:55 -0600 Subject: [PATCH 27/46] Fix dispatch_tod_factors bug --- ssc/cmod_trough_physical.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssc/cmod_trough_physical.cpp b/ssc/cmod_trough_physical.cpp index 467273a95..a2ae25c9f 100644 --- a/ssc/cmod_trough_physical.cpp +++ b/ssc/cmod_trough_physical.cpp @@ -240,7 +240,7 @@ static var_info _cm_vtab_trough_physical[] = { { SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekday", "12x24 PPA pricing Weekday schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, { SSC_INPUT, SSC_MATRIX, "dispatch_sched_weekend", "12x24 PPA pricing Weekend schedule", "", "", "tou", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1","", "" }, { 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" }, + "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","", "SIMULATION_PARAMETER" }, { SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "tou", "?=1", "", "" }, { SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "tou", "", "", "" }, From 6d0dbdf09bb06e5fa029312e11cdc546847c947e Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 8 Jun 2023 11:34:31 -0600 Subject: [PATCH 28/46] Add sim info to thermal eff approx call. Use steady state to calculate thermal efficiency approx in fresnel. Add receiver theraml efficiency to cmod --- ssc/cmod_fresnel_physical.cpp | 4 +- tcs/csp_dispatch.cpp | 4 +- tcs/csp_solver_core.h | 2 +- tcs/csp_solver_cr_electric_resistance.cpp | 2 +- tcs/csp_solver_cr_electric_resistance.h | 2 +- tcs/csp_solver_cr_heat_pump.cpp | 2 +- tcs/csp_solver_cr_heat_pump.h | 2 +- tcs/csp_solver_fresnel_collector_receiver.cpp | 267 +++++++++++------- tcs/csp_solver_fresnel_collector_receiver.h | 27 +- tcs/csp_solver_gen_collector_receiver.cpp | 2 +- tcs/csp_solver_gen_collector_receiver.h | 2 +- tcs/csp_solver_lf_dsg_collector_receiver.cpp | 2 +- tcs/csp_solver_lf_dsg_collector_receiver.h | 2 +- tcs/csp_solver_mspt_collector_receiver.cpp | 2 +- tcs/csp_solver_mspt_collector_receiver.h | 2 +- tcs/csp_solver_trough_collector_receiver.cpp | 4 +- tcs/csp_solver_trough_collector_receiver.h | 2 +- 17 files changed, 191 insertions(+), 139 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index e58ebc910..f46637fb8 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -432,7 +432,6 @@ static var_info _cm_vtab_fresnel_physical[] = { // Solar Field (from Trough) - //{ SSC_OUTPUT, SSC_ARRAY, "Theta_ave", "Field collector solar incidence angle", "deg", "", "solar_field", "sim_type=1", "", "" }, { 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", "", "" }, @@ -440,6 +439,8 @@ static var_info _cm_vtab_fresnel_physical[] = { { 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", "", "" }, @@ -745,6 +746,7 @@ class cm_fresnel_physical : public compute_module 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); 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 index c68af3248..1518b91be 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -17,6 +17,7 @@ static C_csp_reported_outputs::S_output_info S_output_info[] = {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}, @@ -304,6 +305,11 @@ void C_csp_fresnel_collector_receiver::set_output_value() 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] @@ -1842,13 +1848,102 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs 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 troughOutputs; + C_csp_collector_receiver::S_csp_cr_out_solver fresnelOutputs; - steady_state(weatherValues, htfInletState, std::numeric_limits::quiet_NaN(), defocus, troughOutputs, fresnelInfo); + 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 = troughOutputs.m_dP_sf; + 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; - this->m_dP_des_SS = troughOutputs.m_dP_sf; + // 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; @@ -2765,6 +2860,7 @@ void C_csp_fresnel_collector_receiver::on(const C_csp_weatherreader::S_outputs& // 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] @@ -2833,6 +2929,8 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S 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 { @@ -2856,99 +2954,14 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S minutes2SS += sim_info.ms_ts.m_step / 60.; - } while (ss_diff / 200. > tol); + + count++; + } while (ss_diff / 200. > tol && count < max_iterations); - - // Calculate Steady State Results + if (count == max_iterations) { - // Field Results - double temp_initial = htf_state_in.m_temp + 273.15; // K - double temp_final = m_T_field_out; // K - double mdot = cr_out_solver.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 = cr_out_solver.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 * weather.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 * weather.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; - - // 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 - + 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] @@ -2956,8 +2969,6 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S double rho_hot = m_htfProps.dens(T_htf_out_t_int_last[m_nMod - 1], 10.e5); // [kg/m3] std::string summary; - - //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); // Set steady-state outputs @@ -3099,7 +3110,7 @@ double C_csp_fresnel_collector_receiver::calculate_optical_efficiency(const C_cs return eta_optical; } -double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(const C_csp_weatherreader::S_outputs& weather, double q_incident /*MW*/) +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.; @@ -3187,9 +3198,69 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con double HL_total = HL_hces + HL_headers + HL_runners; double eta_therm_approx = std::max(1. - HL_total * 1.e-6 / q_incident, 0.); - return eta_therm_approx; + + + // DEBUG + double q_eff = 0; + if (true) + { + 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; + + + double eff_old = eta_therm_approx; + double eff_new = q_eff; + + + 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; diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 009f7748e..91ec2075a 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -131,6 +131,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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] @@ -150,30 +151,6 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver E_W_DOT_SCA_TRACK, //[MWe] E_W_DOT_PUMP, //[MWe] - - // From Fresnel - //E_THETA_L, //[deg] - //E_PHI_T, - //E_ETA_OPTICAL, - - //E_SF_DEF, - //E_Q_INC_SF_TOT, - //E_Q_ABS_TOT, - //E_Q_DUMP, - //E_Q_LOSS_TOT, - //E_PIPE_HL, - //E_Q_AVAIL, - //E_Q_LOSS_SPEC_TOT, - //E_ETA_THERMAL, - //E_E_BAL_STARTUP, - //E_M_DOT_AVAIL, - //E_M_DOT_HTF2, - //E_DP_TOT, - //E_T_SYS_C, - //E_T_SYS_H, - //E_T_LOOP_OUTLET, - - //E_Q_I }; enum struct E_loop_energy_balance_exit @@ -666,7 +643,7 @@ class C_csp_fresnel_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_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 179c50374..02e81add2 100644 --- a/tcs/csp_solver_trough_collector_receiver.cpp +++ b/tcs/csp_solver_trough_collector_receiver.cpp @@ -1399,7 +1399,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; @@ -4103,7 +4103,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 6da3285e2..4a1d341d0 100644 --- a/tcs/csp_solver_trough_collector_receiver.h +++ b/tcs/csp_solver_trough_collector_receiver.h @@ -541,7 +541,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(); From 722bf802a37c563755f73b5d390c0ef5c8186a08 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Wed, 14 Jun 2023 13:26:19 -0600 Subject: [PATCH 29/46] Clean pressure calculations and add variables to store pressure data. --- tcs/csp_solver_fresnel_collector_receiver.cpp | 41 ++++++++++++++----- tcs/csp_solver_fresnel_collector_receiver.h | 11 +++-- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 1518b91be..d71b7a42e 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -197,6 +197,7 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double 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(); @@ -242,12 +243,19 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double } //Calculate pressure drop in cold header and hot header sections.. both use similar information - DP_hdr_cold = DP_hdr_cold + PressureDrop(m_dot_header, T_loop_in, 1.0, m_D_hdr[i], m_HDR_rough, + + // 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 - //if(ErrorFound()) return 1 - DP_hdr_hot = DP_hdr_hot + PressureDrop(m_dot_header, T_loop_out, 1.0, m_D_hdr[i], m_HDR_rough, + 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 - //if(ErrorFound()) return 1 + 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); @@ -266,12 +274,13 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double 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); } - //} - // + 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]; @@ -342,7 +351,6 @@ void C_csp_fresnel_collector_receiver::set_output_value() 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] - // Fresnel //mc_reported_outputs.value(E_THETA_L, m_theta_L * r2d); //mc_reported_outputs.value(E_PHI_T, m_phi_t * r2d); @@ -657,6 +665,8 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle // Now calculate an energy balance using the timestep-average Bulk Temperature // ** THIS IS JUST A TEST: can comment out if necessary ** + // + // REMOVED because polynomial heat loss model does not have necessary info //E_sca[i] = (m_A_cs[0] * m_L_mod * rho_htf_i * c_htf_i + m_L_mod * m_mc_bal_sca) * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]) * 1.E-6; //[MJ] SCA basis //E_sca_htf[i] = m_dot_htf_loop * c_htf_i * (m_T_htf_out_t_int[i] - m_T_htf_in_t_int[i]) * sim_info.ms_ts.m_step / 1.E6; //E_sca_abs[i] = m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step / 1.E6; @@ -677,6 +687,9 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle //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); @@ -2138,12 +2151,18 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() 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); diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 91ec2075a..3475d9e43 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -317,9 +317,6 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 - - - @@ -526,13 +523,15 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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_DP_loop; // [bar] Pressure drop in loop sections std::vector m_T_loop_dsn; // [C] Temperature entering loop sections at design - std::vector m_P_loop_dsn; // [bar] Gauge pessure in 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; From efa4f892b856b4e041935924e0e4e4675973411e Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Wed, 5 Jul 2023 12:28:45 -0600 Subject: [PATCH 30/46] Add nan initialization for variables in Evacuated Receiver Model --- tcs/csp_solver_fresnel_collector_receiver.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index d71b7a42e..5552ea8b5 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -4685,6 +4685,10 @@ void EvacReceiverModel::FQ_56CONV_v2(double T_5, double T_6, double P_6, double 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; From 2d599d57f785ec8ad35d7c46aee4064afb769aec Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 6 Jul 2023 12:36:16 -0600 Subject: [PATCH 31/46] Remove old method of calculating thermal efficiency approximate. --- tcs/csp_solver_fresnel_collector_receiver.cpp | 162 +++++++++--------- 1 file changed, 82 insertions(+), 80 deletions(-) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 5552ea8b5..7d5af14a0 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -3134,94 +3134,96 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con // 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.; - // Incidence angle - int doy = DateTime::CalculateDayOfYear(weather.m_year, weather.m_month, weather.m_day); // day of year - double time_start = ((doy - 1) * 24 + weather.m_hour + weather.m_minute / 60.) * 3600.; - double step = 3600.; - double time = time_start + step; - double time_hr = time / 3600.; // [hr] - double dt_hr = step / 3600.; // [hr] - double hour = fmod(time_hr, 24.); // [hr] - int day_of_year = (int)ceil(time_hr / 24.); // Day of the year - double B = (day_of_year - 1) * 360.0 / 365.0 * CSP::pi / 180.0; // Duffie & Beckman 1.5.3b - 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)); // Eqn of time in minutes - double Dec = 23.45 * sin(360.0 * (284.0 + day_of_year) / 365.0 * CSP::pi / 180.0) * CSP::pi / 180.0; // Declination in radians (Duffie & Beckman 1.6.1) - double SolarNoon = 12. - ((m_shift) * 180.0 / CSP::pi) / 15.0 - EOT / 60.0; // Solar Noon and time in hours - double HrA = hour - dt_hr; - double StdTime = HrA + 0.5 * dt_hr; - double SolarTime = StdTime + ((m_shift) * 180.0 / CSP::pi) / 15.0 + EOT / 60.0; - double omega = (SolarTime - 12.0) * 15.0 * CSP::pi / 180.0; // m_hour angle (arc of sun) in radians - double SolarAlt = asin(sin(Dec) * sin(m_latitude) + cos(m_latitude) * cos(Dec) * cos(omega)); - double SolarAz = (weather.m_solazi - 180.) * m_d2r; // [rad] Solar azimuth angle - double CosTh = sqrt(1.0 - pow(cos(SolarAlt - 0) - cos(0) * cos(SolarAlt) * (1.0 - cos(SolarAz - m_ColAz)), 2)); - double Theta = acos(CosTh); // [rad] - - // Incidence angle modifier (IAM) from Burkholder & Kutscher 2008 - double IamF1 = 0.000884; - double IamF2 = -0.0000537; - double IAM; - if (CosTh == 0) { - IAM = 0; - } - else { - IAM = std::min(1., (CosTh + IamF1 * Theta + IamF2 * pow(Theta, 2.)) / CosTh); - } - - // Heat loss, where temperatures are in [C] per Burkholder & Kutscher 2008 - // model coefficients for 2008 Schott PTR70 (vacuum) receiver - double A0 = 4.05; - double A1 = 0.247; - double A2 = -0.00146; - double A3 = 5.65e-06; - double A4 = 7.62e-08; - double A5 = -1.7; - double A6 = 0.0125; - double PerfFac = 1; - double T_amb = weather.m_tdry; // [C] - double W_spd = std::abs(weather.m_wspd); - double DNI = weather.m_beam; - double T_out_des = m_T_loop_out_des - 273.15; // [C] (converted from [C] to [K] in init and now back to [C]) - double T_in_des = m_T_loop_in_des - 273.15; // [C] (converted from [C] in [K] in init and now back to [C]) - double HLTerm1 = (A0 + A5 * sqrt(W_spd)) * (T_out_des - T_in_des); - double HLTerm2 = (A1 + A6 * sqrt(W_spd)) * ((pow(T_out_des, 2) - pow(T_in_des, 2)) / 2. - T_amb * (T_out_des - T_in_des)); - double HLTerm3 = ((A2 + A4 * DNI * CosTh * IAM) / 3.) * (pow(T_out_des, 3) - pow(T_in_des, 3)); - double HLTerm4 = (A3 / 4.) * (pow(T_out_des, 4) - pow(T_in_des, 4)); - double HL = (HLTerm1 + HLTerm2 + HLTerm3 + HLTerm4) / (T_out_des - T_in_des); //[W/m] - double HL_hces = std::max(HL * m_L_tot * m_nLoops * PerfFac, 0.); // [W] convert from W/m to W for entire field - - - // Piping heat loss, at average hot/cold design temperature - double T_avg_des = 0.5 * (T_out_des + T_in_des); // [C] + // DEBUG (old estimation) + if (false) + { + // Incidence angle + int doy = DateTime::CalculateDayOfYear(weather.m_year, weather.m_month, weather.m_day); // day of year + double time_start = ((doy - 1) * 24 + weather.m_hour + weather.m_minute / 60.) * 3600.; + double step = 3600.; + double time = time_start + step; + double time_hr = time / 3600.; // [hr] + double dt_hr = step / 3600.; // [hr] + double hour = fmod(time_hr, 24.); // [hr] + int day_of_year = (int)ceil(time_hr / 24.); // Day of the year + double B = (day_of_year - 1) * 360.0 / 365.0 * CSP::pi / 180.0; // Duffie & Beckman 1.5.3b + 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)); // Eqn of time in minutes + double Dec = 23.45 * sin(360.0 * (284.0 + day_of_year) / 365.0 * CSP::pi / 180.0) * CSP::pi / 180.0; // Declination in radians (Duffie & Beckman 1.6.1) + double SolarNoon = 12. - ((m_shift) * 180.0 / CSP::pi) / 15.0 - EOT / 60.0; // Solar Noon and time in hours + double HrA = hour - dt_hr; + double StdTime = HrA + 0.5 * dt_hr; + double SolarTime = StdTime + ((m_shift) * 180.0 / CSP::pi) / 15.0 + EOT / 60.0; + double omega = (SolarTime - 12.0) * 15.0 * CSP::pi / 180.0; // m_hour angle (arc of sun) in radians + double SolarAlt = asin(sin(Dec) * sin(m_latitude) + cos(m_latitude) * cos(Dec) * cos(omega)); + double SolarAz = (weather.m_solazi - 180.) * m_d2r; // [rad] Solar azimuth angle + double CosTh = sqrt(1.0 - pow(cos(SolarAlt - 0) - cos(0) * cos(SolarAlt) * (1.0 - cos(SolarAz - m_ColAz)), 2)); + double Theta = acos(CosTh); // [rad] + + // Incidence angle modifier (IAM) from Burkholder & Kutscher 2008 + double IamF1 = 0.000884; + double IamF2 = -0.0000537; + double IAM; + if (CosTh == 0) { + IAM = 0; + } + else { + IAM = std::min(1., (CosTh + IamF1 * Theta + IamF2 * pow(Theta, 2.)) / CosTh); + } - double row_distance = m_L_crossover; // use crossover as row distance - //double HL_headers_old = m_nfsec * (2 * m_nhdrsec) * row_distance * m_D_hdr[0] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + // Heat loss, where temperatures are in [C] per Burkholder & Kutscher 2008 + // model coefficients for 2008 Schott PTR70 (vacuum) receiver + double A0 = 4.05; + double A1 = 0.247; + double A2 = -0.00146; + double A3 = 5.65e-06; + double A4 = 7.62e-08; + double A5 = -1.7; + double A6 = 0.0125; + double PerfFac = 1; + double T_amb = weather.m_tdry; // [C] + double W_spd = std::abs(weather.m_wspd); + double DNI = weather.m_beam; + double T_out_des = m_T_loop_out_des - 273.15; // [C] (converted from [C] to [K] in init and now back to [C]) + double T_in_des = m_T_loop_in_des - 273.15; // [C] (converted from [C] in [K] in init and now back to [C]) + double HLTerm1 = (A0 + A5 * sqrt(W_spd)) * (T_out_des - T_in_des); + double HLTerm2 = (A1 + A6 * sqrt(W_spd)) * ((pow(T_out_des, 2) - pow(T_in_des, 2)) / 2. - T_amb * (T_out_des - T_in_des)); + double HLTerm3 = ((A2 + A4 * DNI * CosTh * IAM) / 3.) * (pow(T_out_des, 3) - pow(T_in_des, 3)); + double HLTerm4 = (A3 / 4.) * (pow(T_out_des, 4) - pow(T_in_des, 4)); + double HL = (HLTerm1 + HLTerm2 + HLTerm3 + HLTerm4) / (T_out_des - T_in_des); //[W/m] + double HL_hces = std::max(HL * m_L_tot * m_nLoops * PerfFac, 0.); // [W] convert from W/m to W for entire field + + + // Piping heat loss, at average hot/cold design temperature + double T_avg_des = 0.5 * (T_out_des + T_in_des); // [C] + + double row_distance = m_L_crossover; // use crossover as row distance + //double HL_headers_old = m_nfsec * (2 * m_nhdrsec) * row_distance * m_D_hdr[0] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + + + double HL_headers = 0; + // Sum hot headers heat loss + for (int i = 0; i < m_nhdrsec; i++) + { + HL_headers += m_L_crossover * m_D_hdr[i] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); + } + HL_headers *= 2.0; // multiply to account for cold headers + HL_headers *= m_nfsec; // account for field sections - double HL_headers = 0; - // Sum hot headers heat loss - for (int i = 0; i < m_nhdrsec; i++) - { - HL_headers += m_L_crossover * m_D_hdr[i] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); - } - HL_headers *= 2.0; // multiply to account for cold headers - HL_headers *= m_nfsec; // account for field sections + double HL_runners = 0.; + for (int i = 0; i < m_nrunsec; i++) { + HL_runners += 2. * m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + } + HL_runners *= 2.0; // account for cold headers - double HL_runners = 0.; - for (int i = 0; i < m_nrunsec; i++) { - HL_runners += 2. * m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] + double HL_total = HL_hces + HL_headers + HL_runners; + double eta_therm_approx = std::max(1. - HL_total * 1.e-6 / q_incident, 0.); } - HL_runners *= 2.0; // account for cold headers - - - double HL_total = HL_hces + HL_headers + HL_runners; - double eta_therm_approx = std::max(1. - HL_total * 1.e-6 / q_incident, 0.); - - // DEBUG + // New Estimate (using steady state) double q_eff = 0; - if (true) { C_csp_solver_htf_1state htfInletState; htfInletState.m_temp = m_T_loop_in_des - 273.15; @@ -3253,7 +3255,7 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con q_eff = q_thermal / Q_available; - double eff_old = eta_therm_approx; + //double eff_old = eta_therm_approx; double eff_new = q_eff; From c0a02f2fd2d20d1c45d967a70f64a6df47c06ab3 Mon Sep 17 00:00:00 2001 From: tyneises Date: Fri, 21 Jul 2023 15:54:22 -0500 Subject: [PATCH 32/46] add mspt iph cmod --- ssc/CMakeLists.txt | 1 + ssc/cmod_mspt_iph.cpp | 2894 +++++++++++++++++++++++++++++++++++++++++ ssc/sscapi.cpp | 2 + 3 files changed, 2897 insertions(+) create mode 100644 ssc/cmod_mspt_iph.cpp diff --git a/ssc/CMakeLists.txt b/ssc/CMakeLists.txt index b2f1ec3f4..2721261c2 100644 --- a/ssc/CMakeLists.txt +++ b/ssc/CMakeLists.txt @@ -69,6 +69,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_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp new file mode 100644 index 000000000..7442313b5 --- /dev/null +++ b/ssc/cmod_mspt_iph.cpp @@ -0,0 +1,2894 @@ +/* +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_Rankine_indirect_224.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, "P_ref", "Reference output electric power at design condition", "MW", "", "System Design", "*", "", ""}, + { SSC_INPUT, SSC_NUMBER, "design_eff", "Power cycle efficiency at design", "none", "", "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", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "heater_efficiency", "Heater electric to thermal efficiency", "%", "", "Parallel Heater", "is_parallel_htr=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "f_q_dot_des_allowable_su", "Fraction of design power allowed during startup", "-", "", "Parallel Heater", "is_parallel_htr=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "hrs_startup_at_max_rate", "Duration of startup at max startup power", "hr", "", "Parallel Heater", "is_parallel_htr=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "f_q_dot_heater_min", "Minimum allowable heater output as fraction of design", "", "", "Parallel Heater", "is_parallel_htr=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "disp_hsu_cost_rel", "Heater startup cost", "$/MWt/start", "", "System Control", "is_dispatch=1&is_parallel_htr=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "heater_spec_cost", "Heater specific cost", "$/kWht", "", "System Costs", "is_parallel_htr=1", "", "" }, +{ 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", "*", "", "" }, + + +//RADIATIVE COOLING WITH COLD STORAGE +// Includes radiative cooling cost parameters +{ SSC_INPUT, SSC_NUMBER, "h_ctes_tank_min", "Minimum allowable water height in storage tank", "m", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "ctes_tshours", "Equivalent full load storage hours", "hr", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "ctes_field_fl", "Fluid in radiator field. 3=liquid water. Other = Glycol.", "-", "", "RADCOOL", "?=3", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "h_ctes_tank", "Total height of cold storage tank when full", "m", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "u_ctes_tank", "Loss coefficient from cold storage tank", "W/m2-K", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "ctes_tankpairs", "Number of equivalent tank pairs", "-", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "T_ctes_cold_design", "Design value of cooled water to power block", "C", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "T_ctes_warm_design", "Design value of warm water returning from power block", "C", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "T_ctes_warm_ini", "Initial value of warm tank", "C", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "T_ctes_cold_ini", "Initial value of cold tank", "C", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "f_ctes_warm_ini", "Initial fraction of avail. volume that is warm", "-", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "rad_multiplier", "Ratio of radiator field area to solar aperature area", "-", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "m_dot_radpanel", "Mass flow rate through single radiator panel", "kg/sec", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "n_rad_tubes", "Number of parallel tubes in single radiator panel", "-", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "W_rad_tubes", "Center-to-center distance between tubes in radiator panel", "m", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "L_rad", "Length of radiator panel row", "m", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "th_rad_panel", "Thickness of radiator panel", "m", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "D_rad_tubes", "Inner diameter of tubes in radiator panel", "m", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "k_panel", "Thermal conductivity of radiator panel material", "W/m-K", "", "RADCOOL", "?=235", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "epsilon_radtop", "Emmissivity of top of radiator panel", "-", "", "RADCOOL", "?=.95", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "epsilon_radbot", "Emmissivity of top of radiator panel bottom (facing ground)", "-", "", "RADCOOL", "?=.07", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "epsilon_radgrnd", "Emmissivity of ground underneath radiator panel", "-", "", "RADCOOL", "?=.90", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "L_rad_sections", "Length of individual radiator panel", "m", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "epsilon_radHX", "Effectiveness of HX between radiative field and cold storage", "-", "", "RADCOOL", "?=.8", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "ctes_type", "Type of cold storage (2=two tank, 3= three node)", "-", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "radiator_unitcost", "Cost of radiative panels", "$/m^2", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "radiator_installcost", "Installation cost of radiative panels", "$/m^2", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "radiator_fluidcost", "Cost of circulating fluid in radiative panels", "$/L", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "radfluid_vol_ratio", "Ratio of fluid in distribution to fluid in panels", "-", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "ctes_cost", "Cost of cold storage construction", "$/L", "", "RADCOOL", "?=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "rad_pressuredrop", "Average pressure drop through a radiative panel & distribution", "kPa", "", "RADCOOL", "?=0", "", ""}, + +// Power Cycle Inputs +{ SSC_INPUT, SSC_NUMBER, "pc_config", "PC configuration 0=Steam Rankine (224), 1=user defined", "", "", "Power Cycle", "?=0", "INTEGER", ""}, +{ SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "Power Cycle", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "Power Cycle", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "Power Cycle", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "", "", "Power Cycle", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "Power Cycle", "*", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby", "", "", "Power Cycle", "*", "", ""}, + +// Steam Rankine cycle +{ SSC_INPUT, SSC_NUMBER, "dT_cw_ref", "Reference condenser cooling water inlet/outlet temperature difference", "C", "", "Rankine Cycle", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "T_amb_des", "Reference ambient temperature at design point", "C", "", "Rankine Cycle", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "CT", "Condensor type: 1=evaporative, 2=air, 3=hybrid", "", "", "Rankine Cycle", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "T_approach", "Cooling tower approach temperature", "C", "", "Rankine Cycle", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "T_ITD_des", "ITD at design for dry system", "C", "", "Rankine Cycle", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "P_cond_ratio", "Condenser pressure ratio", "", "", "Rankine Cycle", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "pb_bd_frac", "Power block blowdown steam fraction", "", "", "Rankine Cycle", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "P_cond_min", "Minimum condenser pressure", "inHg", "", "Rankine Cycle", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "n_pl_inc", "Number of part-load increments for the heat rejection system", "none", "", "Rankine Cycle", "pc_config=0", "INTEGER", ""}, +{ SSC_INPUT, SSC_ARRAY, "F_wc", "TOU array of fractions indicating wet cooling share for hybrid cooling", "", "", "System Control", "pc_config=0", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "tech_type", "Turbine inlet pressure control 1=Fixed, 3=Sliding", "", "", "Rankine Cycle", "pc_config=0", "", ""}, + +// User Defined 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 Power Cycle", "pc_config=1", "", ""}, +{ 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 Power Cycle", "pc_config=1", "", ""}, +{ 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 Power Cycle", "pc_config=1", "", ""}, + +// 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", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "System Control", "is_dispatch=1", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "System Control", "is_dispatch=1", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "", "", "System Control", "?=1", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max number of dispatch optimization iterations", "", "", "System Control", "is_dispatch=1", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max dispatch optimization solve duration", "s", "", "System Control", "is_dispatch=1", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "", "", "System Control", "is_dispatch=1", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "", "", "System Control", "?=0.99", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "", "", "System Control", "?=''", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "", "", "System Control", "?='ampl sdk_solution.run'", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "System Control", "", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "System Control", "", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "System Control", "", "", ""}, +{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "SIMULATION_PARAMETER"}, +{ 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"}, +{ SSC_INPUT, SSC_NUMBER, "is_wlim_design", "Use fixed design-point net electricity generation limits (dispatch opt only)", "", "", "System Control", "?=0", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "disp_wlim_maxspec", "Fixed design-point max net power to the grid (dispatch opt only)", "", "", "System Control", "is_wlim_design=1", "", "" }, +{ SSC_INPUT, SSC_NUMBER, "is_wlim_series", "Use time-series net electricity generation limits (dispatch opt only)", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_ARRAY, "wlim_series", "Time series net electicity generation limits (dispatch opt only)", "kWe", "", "System Control", "is_wlim_series=1", "", "SIMULATION_PARAMETER"}, + +// Pricing schedules and multipliers + // Ideally this would work with sim_type = 2, but UI inputs availability depends on financial mode +{ SSC_INPUT, SSC_NUMBER, "ppa_multiplier_model", "PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts", "0/1", "0=diurnal,1=timestep", "Time of Delivery Factors", "?=0", /*need a default so this var works in required_if*/ "INTEGER,MIN=0", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "ppa_soln_mode", "PPA solution mode (0=Specify IRR target, 1=Specify PPA price)", "", "", "Financial Solution Mode", "sim_type=1&ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, +{ SSC_INPUT, SSC_ARRAY, "dispatch_factors_ts", "Dispatch payment factor array", "", "", "Time of Delivery Factors", "ppa_multiplier_model=1&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_NUMBER, "en_electricity_rates", "Enable electricity rates for grid purchase", "0/1", "", "Electricity Rates", "?=0", "", "SIMULATION_PARAMETER"}, +{ 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"}, +{ 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" }, + +{ SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "System Control", "", "", "SIMULATION_PARAMETER"}, +{ SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA prices - yearly", "$/kWh", "", "Revenue", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER"}, +{ 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"}, + +// 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", "kWe", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "cp_system_nameplate", "System capacity for capacity payments", "MWe", "", "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", "*", "", "" }, + +// 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", "*", "", "" }, +// UDPC +{ SSC_OUTPUT, SSC_NUMBER, "n_T_htf_pars_calc", "UDPC number of HTF parametric values", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "n_T_amb_pars_calc", "UDPC number of ambient temp parametric values", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "n_m_dot_pars_calc", "UDPC number of mass flow parametric values", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "T_htf_ref_calc", "UDPC reference HTF temperature", "C", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "T_htf_low_calc", "UDPC low level HTF temperature", "C", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "T_htf_high_calc", "UDPC high level HTF temperature", "C", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "T_amb_ref_calc", "UDPC reference ambient temperature", "C", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "T_amb_low_calc", "UDPC low level ambient temperature", "C", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "T_amb_high_calc", "UDPC high level ambient temperature", "C", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_ND_ref_calc", "UDPC reference normalized mass flow rate", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_ND_low_calc", "UDPC low level normalized mass flow rate", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_ND_high_calc", "UDPC high level normalized mass flow rate", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "W_dot_gross_ND_des_calc", "UDPC calculated normalized gross power at design", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "Q_dot_HTF_ND_des_calc", "UDPC calculated normalized heat input at design", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "W_dot_cooling_ND_des_calc", "UPPC calculated normalized cooling power at design", "", "", "UDPC Design Calc", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "m_dot_water_ND_des_calc", "UDPC calculated water use at design", "", "", "UDPC Design Calc", "*", "", "" }, + +// 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", "MWe", "", "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.rad_field", "Radiative field cost" "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.rad_fluid", "Radiative fluid cost" "$", "", "System Costs", "*", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.rad_storage", "Cold storage 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", "", "" }, + +// Power cycle outputs +{ SSC_OUTPUT, SSC_ARRAY, "eta", "PC efficiency, gross", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_pb", "PC input energy", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_pc", "PC HTF mass flow rate", "kg/s", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_pc_startup", "PC startup thermal energy", "MWht", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_startup", "PC startup thermal power", "MWt", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "P_cycle", "PC electrical power output, gross", "MWe", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_pc_in", "PC HTF inlet temperature", "C", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_pc_out", "PC HTF outlet temperature", "C", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "m_dot_water_pc", "PC water consumption, makeup + cooling", "kg/s", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_cond_out", "PC condenser water outlet temperature", "C", "", "PC", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_cold", "Cold storage cold temperature", "C", "", "PC", "?", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "m_cold", "Cold storage cold tank mass", "kg", "", "PC", "?", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "m_warm", "Cold storage warm tank mass", "kg", "", "PC", "?", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_warm", "Cold storage warm tank temperature", "C", "", "PC", "?", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "T_rad_out", "Radiator outlet temperature", "C", "", "PC", "?", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "A_radfield", "Radiator field surface area", "m^2", "", "PC", "?", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "P_cond", "PC condensing presssure", "Pa", "", "PC", "?", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "P_cond_iter_err", "PC condenser presure iteration error", "", "", "PC", "?", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "radcool_control", "Radiative cooling status code", "-", "", "PC", "?", "", ""}, +{ SSC_OUTPUT, SSC_ARRAY, "cycle_htf_pump_power", "Cycle HTF pump power", "MWe", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_ARRAY, "P_cooling_tower_tot", "Parasitic power condenser operation", "MWe", "", "", "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_heater", "TES freeze protection power", "MWe", "", "", "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, "P_out_net", "Total electric power to grid", "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 electric power to grid with available derate", "kWe", "", "", "sim_type=1", "", ""}, + +// Annual single-value outputs +{ SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual total electric power to grid", "kWhe", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "annual_W_cycle_gross", "Electrical source - power cycle gross output", "kWhe", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "annual_W_cooling_tower", "Total of condenser operation parasitics", "kWhe", "", "PC", "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_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, "conversion_factor", "Gross to net conversion factor", "%", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "sales_energy_capacity_factor", "Capacity factor considering only positive net generation periods", "%", "", "", "sim_type=1", "", "" }, +{ SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "annual_total_water_use", "Total annual water usage, cycle + mirror washing", "m3", "", "", "sim_type=1", "", ""}, + +{ SSC_OUTPUT, SSC_NUMBER, "disp_objective_ann", "Annual sum of dispatch objective function value", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "disp_iter_ann", "Annual sum of dispatch solver iterations", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "disp_presolve_nconstr_ann", "Annual sum of dispatch problem constraint count", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "disp_presolve_nvar_ann", "Annual sum of dispatch problem variable count", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "disp_solve_time_ann", "Annual sum of dispatch solver time", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "disp_solve_state_ann", "Annual sum of dispatch solve state", "", "", "", "sim_type=1", "", ""}, +{ SSC_OUTPUT, SSC_NUMBER, "avg_suboptimal_rel_mip_gap", "Average suboptimal relative MIP gap", "%", "", "", "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"); + + // ***************************************************** + // Check deprecated variables + // Piping loss coefficient + bool is_piping_loss_assigned = is_assigned("piping_loss"); + bool is_piping_loss_coefficient_assigned = is_assigned("piping_loss_coefficient"); + + if (is_piping_loss_assigned && is_piping_loss_coefficient_assigned) { + log("We replaced the functionality of input variable piping_loss with new input variable piping_loss_coefficient," + " so the model does not use your piping_loss input."); + } + else if (is_piping_loss_assigned) { + throw exec_error("tcsmolten_salt", "We replaced the functionality of input variable piping_loss [Wt/m] with new input variable piping_loss_coefficient [Wt/m2-K]." + " The new input scales piping thermal losses as a function of receiver thermal power and design-point temperatures." + " Please define piping_loss_coefficient in your script."); + } + + bool is_csp_pt_tes_init_hot_htf_percent_assigned = is_assigned("csp.pt.tes.init_hot_htf_percent"); + bool is_tes_hot_htf_percent_assigned = is_assigned("tes_hot_htf_percent_assigned"); + if (is_csp_pt_tes_init_hot_htf_percent_assigned && is_tes_hot_htf_percent_assigned) { + log("We renamed input variable csp.pt.tes.init_hot_htf_percent to tes_init_hot_htf_percent," + " you provided both inputs, so the model does not use your csp.pt.tes.init_hot_htf_percent input."); + } + else if (is_csp_pt_tes_init_hot_htf_percent_assigned) { + throw exec_error("tcsmolten_salt", "We renamed input variable csp.pt.tes.init_hot_htf_percent to tes_init_hot_htf_percent," + " please define tes_init_hot_htf_percent in your script."); + } + + if (is_assigned("P_boil")) { + log("We removed boiler pressure (P_boil) as a user input to the Rankine Cycle model. Because the cycle efficiency" + " is provided by the user, the boiler pressure input does not modify the efficiency as one might expect. Instead the model" + " uses boiler pressure in second order calculations to 1) define a boiling temperature to normalize off-design HTF temperature and" + " 2) estimate steam mass flow for cycle make-up water calculations. Because boiler pressure only has influences" + " results in these minor non-intuitive ways, we decided to hardcode the valu to 100 bar."); + } + + if (is_dispatch) { + // Cycle startup cost disp_csu_cost + bool is_disp_csu_cost_assigned = is_assigned("disp_csu_cost"); + bool is_disp_csu_cost_rel_assigned = is_assigned("disp_csu_cost_rel"); + + if (is_disp_csu_cost_assigned && is_disp_csu_cost_rel_assigned) { + log("We replaced the functionality of input variable disp_csu_cost with new input variable disp_csu_cost_rel," + " so the model does not use your disp_csu_cost input."); + } + else if (is_disp_csu_cost_assigned) { + throw exec_error("tcsmolten_salt", "We replaced the functionality of input variable disp_csu_cost [$/start] with new input variable disp_csu_cost_rel [$/MWe-cycle/start]." + " The new input represents cycle startup costs normalized by the cycle design capacity." + " Please define disp_csu_cost_rel in your script."); + } + + // Receiver startup cost disp_rsu_cost + bool is_disp_rsu_cost_assigned = is_assigned("disp_rsu_cost"); + bool is_disp_rsu_cost_rel_assigned = is_assigned("disp_rsu_cost_rel"); + + if (is_disp_rsu_cost_assigned && is_disp_rsu_cost_rel_assigned) { + log("We replaced the funcationality of input variable disp_rsu_cost with new input variable disp_rsu_cost_rel," + " so the model does not use your disp_rsu_cost input."); + } + else if (is_disp_rsu_cost_assigned) { + throw exec_error("tcsmolten_salt", "We replaced the functionality of input variable disp_rsu_cost [$/start] with new input variable disp_rsu_cost_rel [$/MWe-cycle/start]." + " The new input represents receiver startup costs normalized by the receiver design thermal power." + " Please define disp_rsu_cost_rel in your script."); + } + + // Cycle ramping + bool is_disp_pen_delta_w_assigned = is_assigned("disp_pen_delta_w"); + bool is_disp_pen_ramping_assigned = is_assigned("disp_pen_ramping"); + if (is_disp_pen_delta_w_assigned && is_disp_pen_ramping_assigned) { + log("We replaced the functionality of input variable disp_pen_delta_w with new input variable disp_pen_ramping," + " so the model does not use your disp_pen_delta_w input"); + } + else if (is_disp_pen_delta_w_assigned) { + throw exec_error("tcsmolten_salt", "We replaced the functionality of input variable disp_pen_delta_w [$/kWe-change] with new input variable disp_pen_ramping [$/MWe-change]." + " The new input represents the receiver startup costs in an optimization model that uses absolute grid prices." + " Please define disp_pen_ramping in your script. SAM's default value in the molten salt power tower model is 1.0"); + } + } + + // ***************************************************** + // ***************************************************** + + + // ***************************************************** + // 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 W_dot_cycle_des = as_double("P_ref"); //[MWe] + double eta_cycle = as_double("design_eff"); //[-] + double tshours = as_double("tshours"); //[-] + + // System Design Calcs + double q_dot_pc_des = W_dot_cycle_des / eta_cycle; //[MWt] + double Q_tes = q_dot_pc_des * tshours; //[MWt-hr] + double q_dot_rec_des = q_dot_pc_des * as_number("solarm"); //[MWt] + //double system_capacity = W_dot_cycle_des * as_double("gross_net_conversion_factor") * 1.E3; //[kWe] + + // 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] + + // *********************************************** + // *********************************************** + // Power cycle + // *********************************************** + // *********************************************** + C_csp_power_cycle* p_csp_power_cycle; + // Steam Rankine and User Defined power cycle classes + C_pc_Rankine_indirect_224 rankine_pc; + + // Check power block type + int pb_tech_type = as_integer("pc_config"); + if (pb_tech_type == 0 || pb_tech_type == 1) // Rankine or User Defined + { + 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("design_eff"); + pc->m_T_htf_hot_ref = as_double("T_htf_hot_des"); + pc->m_T_htf_cold_ref = as_double("T_htf_cold_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("rec_htf"); // power cycle HTF is same as receiver HTF + pc->m_pc_fl_props = as_matrix("field_fl_props"); + + if (pb_tech_type == 0) + { + pc->m_P_boil_des = 100; //[bar] + 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_CT = as_integer("CT"); // cooling tech type: 1=evaporative, 2=air, 3=hybrid , 5= custom for rad cool, 6= custom for rad cool + pc->m_tech_type = as_integer("tech_type"); // 1: Fixed, 3: Sliding + if (pc->m_tech_type == 2) { pc->m_tech_type = 1; }; // changing fixed pressure for the trough to fixed pressure for the tower + //if (pc->m_tech_type == 8) { pc->m_tech_type = 3; }; // changing sliding pressure for the trough to sliding pressure for the tower -> don't, this disallows the use of the old tower sliding curves + + if (!(pc->m_tech_type == 1 || pc->m_tech_type == 3 || pc->m_tech_type == 5 || pc->m_tech_type == 6 || pc->m_tech_type == 7 || pc->m_tech_type == 8)) + { + std::string tech_msg = util::format("tech_type must be either 1 (fixed pressure) or 3 (sliding). Input was %d." + " Simulation proceeded with fixed pressure", pc->m_tech_type); + pc->m_tech_type = 1; + } + 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"); + + //parameters for radiative cooling with cold storage + C_csp_cold_tes* two_tank = &rankine_pc.mc_two_tank_ctes; //pointer for two tank + C_csp_stratified_tes* stratified = &rankine_pc.mc_stratified_ctes; //pointer for stratified + + two_tank->ms_params.m_ctes_type = as_integer("ctes_type"); + stratified->ms_params.m_ctes_type = as_integer("ctes_type"); + + if (rankine_pc.ms_params.m_CT == 4) + { + if (two_tank->ms_params.m_ctes_type == 2) + { + two_tank->ms_params.m_h_tank_min = as_double("h_ctes_tank_min"); + two_tank->ms_params.m_ts_hours = as_double("ctes_tshours"); + two_tank->ms_params.m_h_tank = as_double("h_ctes_tank"); + two_tank->ms_params.m_u_tank = as_double("u_ctes_tank"); + two_tank->ms_params.m_tank_pairs = as_integer("ctes_tankpairs"); + two_tank->ms_params.m_T_cold_des = as_double("T_ctes_cold_design"); + two_tank->ms_params.m_T_hot_des = as_double("T_ctes_warm_design"); + two_tank->ms_params.m_T_tank_hot_ini = as_double("T_ctes_warm_ini"); + two_tank->ms_params.m_T_tank_cold_ini = as_double("T_ctes_cold_ini"); + two_tank->ms_params.m_f_V_hot_ini = as_double("f_ctes_warm_ini"); + two_tank->ms_params.m_lat = weather_reader.ms_solved_params.m_lat; + } + if (two_tank->ms_params.m_ctes_type > 2) + { + stratified->ms_params.m_h_tank_min = 0; //hardcode zero minimum height for stratified tanks. + stratified->ms_params.m_ts_hours = as_double("ctes_tshours"); + stratified->ms_params.m_h_tank = as_double("h_ctes_tank"); + stratified->ms_params.m_u_tank = as_double("u_ctes_tank"); + stratified->ms_params.m_tank_pairs = as_integer("ctes_tankpairs"); + stratified->ms_params.m_T_cold_des = as_double("T_ctes_cold_design"); + stratified->ms_params.m_T_hot_des = as_double("T_ctes_warm_design"); + stratified->ms_params.m_T_tank_hot_ini = as_double("T_ctes_warm_ini"); + stratified->ms_params.m_T_tank_cold_ini = as_double("T_ctes_cold_ini"); + stratified->ms_params.m_f_V_hot_ini = as_double("f_ctes_warm_ini"); + stratified->ms_params.m_lat = weather_reader.ms_solved_params.m_lat; + + } + rankine_pc.mc_radiator.ms_params.m_field_fl = as_integer("ctes_field_fl"); + rankine_pc.mc_radiator.ms_params.RM = as_double("rad_multiplier"); + rankine_pc.mc_radiator.ms_params.Asolar_refl = A_sf; //[m2] + rankine_pc.mc_radiator.ms_params.m_dot_panel = as_double("m_dot_radpanel"); + rankine_pc.mc_radiator.ms_params.n = as_integer("n_rad_tubes"); + rankine_pc.mc_radiator.ms_params.W = as_double("W_rad_tubes"); + rankine_pc.mc_radiator.ms_params.L = as_double("L_rad"); + rankine_pc.mc_radiator.ms_params.th = as_double("th_rad_panel"); + rankine_pc.mc_radiator.ms_params.D = as_double("D_rad_tubes"); + rankine_pc.mc_radiator.ms_params.k_panel = as_double("k_panel"); + rankine_pc.mc_radiator.ms_params.epsilon = as_double("epsilon_radtop"); + rankine_pc.mc_radiator.ms_params.epsilonb = as_double("epsilon_radbot"); + rankine_pc.mc_radiator.ms_params.epsilong = as_double("epsilon_radgrnd"); + rankine_pc.mc_radiator.ms_params.Lsec = as_double("L_rad_sections"); + rankine_pc.mc_radiator.ms_params.epsilon_HX = as_double("epsilon_radHX"); + rankine_pc.mc_radiator.ms_params.radfield_dp = as_double("rad_pressuredrop"); + } + + 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]; + + // Set User Defined cycle parameters to appropriate values + 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; + } + else + { + std::string err_msg = util::format("The specified power cycle configuration, %d, does not exist. See SSC Input Table for help.\n", pb_tech_type); + log(err_msg, SSC_WARNING); + return; + } + + // 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_T_COND_OUT, allocate("T_cond_out", 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); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_P_COND, allocate("P_cond", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_P_COND_ITER_ERR, allocate("P_cond_iter_err", n_steps_fixed), n_steps_fixed); + + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_ETA_THERMAL, allocate("eta", n_steps_fixed), n_steps_fixed); + + if (pb_tech_type == 0) { + if (rankine_pc.ms_params.m_CT == 4) { + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_T_COLD, allocate("T_cold", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_M_COLD, allocate("m_cold", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_M_WARM, allocate("m_warm", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_T_WARM, allocate("T_warm", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_T_RADOUT, allocate("T_rad_out", n_steps_fixed), n_steps_fixed); + p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_RADCOOL_CNTRL, allocate("radcool_control", 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"); + + 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") + )); // 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"), + 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"), + as_double("P_ref") / as_double("design_eff"), //[MWt] + as_double("solarm"), //[-] + as_double("P_ref") / as_double("design_eff") * 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"), + 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_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"); + + if (is_dispatch) + { + tou.mc_dispatch_params.m_w_lim_full.resize(n_steps_full); + std::fill(tou.mc_dispatch_params.m_w_lim_full.begin(), tou.mc_dispatch_params.m_w_lim_full.end(), 9.e99); + if (as_boolean("is_wlim_series")) + { + size_t n_wlim_series = 0; + ssc_number_t* wlim_series = as_array("wlim_series", &n_wlim_series); + if (n_wlim_series != n_steps_full) + throw exec_error("tcsmolten_salt", "Invalid net electricity generation limit series dimension. Matrix must have " + util::to_string((int)n_steps_full) + " rows."); + for (size_t i = 0; i < n_steps_full; i++) + tou.mc_dispatch_params.m_w_lim_full.at(i) = (double)wlim_series[i]; + } + else if (as_boolean("is_wlim_design")) { + double wlim_design = as_double("disp_wlim_maxspec"); + for (size_t i = 0; i < n_steps_full; i++) + tou.mc_dispatch_params.m_w_lim_full.at(i) = wlim_design; + } + } + + 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 > 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("tcsmolten_salt", "\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("tcsmolten_salt", "\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("tcsmolten_salt", "\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") || is_dispatch) { + 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 { // 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 (ppa_mult_model == 0) // standard diurnal 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"); + if (tou_params->mc_pricing.mc_weekdays.ncells() == 1) { tou_params->mc_pricing.mc_weekdays.resize_fill(12, 24, 1.); }; + tou_params->mc_pricing.mc_weekends = as_matrix("dispatch_sched_weekend"); + if (tou_params->mc_pricing.mc_weekends.ncells() == 1) { tou_params->mc_pricing.mc_weekends.resize_fill(12, 24, 1.); }; + + auto dispatch_tod_factors = as_vector_double("dispatch_tod_factors"); + if (dispatch_tod_factors.size() != 9) + throw exec_error("tcsmolten_salt", 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 { + // 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 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) { + 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("tcsmolten_salt", 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] + } + } + 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 == 8) { // No Financial Model + 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("tcsmolten_salt", "csp_financial_model must be 1, 2, 3, 4, or 6"); + } + } + 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; + + if (as_boolean("is_dispatch")) { + + + double heater_startup_cost = 0.0; + if (is_parallel_heater) { + double heater_mult = as_double("heater_mult"); //[-] + double q_dot_heater_des = q_dot_pc_des * heater_mult; //[MWt] + heater_startup_cost = as_double("disp_hsu_cost_rel") * q_dot_heater_des; //[$/start] + } + + dispatch.solver_params.set_user_inputs(as_boolean("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, heater_startup_cost, 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, + collector_receiver, + *p_csp_power_cycle, + 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("P_out_net", 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); + } + + + //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("tcsmolten_salt", "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 (size_t 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 (size_t i = 0; i < n_steps_fixed; i++) + tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][i] = dispatch_series[i]; + } + + + // ***************************************************** + // 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(); + receiver->get_design_geometry(L_tower_piping); + assign("L_tower_piping_calc", L_tower_piping); //[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 + + 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); //[kWe] + assign("nameplate", system_capacity * 1.E-3); //[MWe] + assign("cp_system_nameplate", system_capacity * 1.E-3); //[MWe] + 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 Q_storage = as_double("P_ref") / as_double("design_eff") * as_double("tshours"); + 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 = as_double("plant_spec_cost"); + + // Set heater thermal power and cost above, because they're dependent on is_heater boolean + + 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; + + if (rankine_pc.ms_params.m_CT == 4) { + radfield_area = rankine_pc.mc_radiator.ms_params.Afield; + radfield_vol = rankine_pc.mc_radiator.ms_params.D * rankine_pc.mc_radiator.ms_params.D / 4 * PI * rankine_pc.mc_radiator.ms_params.n * rankine_pc.mc_radiator.ms_params.Np * rankine_pc.mc_radiator.ms_params.L; //Calculate volume in radiator panel tubes = pi/4*d^2*L*n*Np + if (rankine_pc.mc_two_tank_ctes.ms_params.m_ctes_type == 2) //If two tank + { + coldstorage_vol = rankine_pc.mc_two_tank_ctes.get_physical_volume(); + } + if (rankine_pc.mc_two_tank_ctes.ms_params.m_ctes_type > 2) //If stratified + { + coldstorage_vol = rankine_pc.mc_stratified_ctes.get_physical_volume(); + + } + + rad_unitcost = as_double("radiator_unitcost"); + rad_installcost = as_double("radiator_installcost"); + rad_fluidcost = as_double("radiator_fluidcost"); + rad_volmulti = as_double("radfluid_vol_ratio"); + coldstorage_unitcost = as_double("ctes_cost"); + } + + double bop_spec_cost = as_double("bop_spec_cost"); + + double fossil_backup_spec_cost = as_double("fossil_spec_cost"); + + double contingency_rate = as_double("contingency_rate"); + + //land area + double total_land_area = total_land_area_before_rad_cooling + radfield_area / 4046.86 /*acres/m^2*/; + 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(); + + 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_storage, + tes_spec_cost, + + Q_CT_tes, + CT_tes_spec_cost, + + W_dot_design, + 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); + + if (pb_tech_type == 0) { + if (rankine_pc.ms_params.m_CT == 4) { + assign("csp.pt.cost.rad_field", (ssc_number_t)rad_field_totcost); + assign("csp.pt.cost.rad_fluid", (ssc_number_t)rad_fluid_totcost); + assign("csp.pt.cost.rad_storage", (ssc_number_t)rad_storage_totcost); + } + } + 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); + } + + + + // 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 (count_pc_su != n_steps_fixed) + { + log("q_dot_pc_startup array is a different length than 'n_steps_fixed'.", SSC_WARNING); + return; + } + for (size_t 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_rec, count_m_dot_water_pc; + count_m_dot_pc = count_m_dot_rec = count_m_dot_water_pc = 0; + ssc_number_t* p_m_dot_rec = as_array("m_dot_rec", &count_m_dot_rec); + 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); + if (count_m_dot_rec != n_steps_fixed || count_m_dot_pc != n_steps_fixed || count_m_dot_water_pc != 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 + 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 + } + + // 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); + } + } + + 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); + + // '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_gensales_after_avail = allocate("gensales_after_avail", 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_W_dot_net[i] * 1.E3 * haf(hour)); //[kWe] + p_gensales_after_avail[i] = max(0.0, p_gen[i]); //[kWe] + } + 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); + accumulate_annual_for_year("gensales_after_avail", "annual_sales_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("P_cooling_tower_tot", "annual_W_cooling_tower", 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("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); + + 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"))); + + accumulate_annual_for_year("disp_objective", "disp_objective_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_iter", "disp_iter_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_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.0 / as_double("disp_frequency"), steps_per_hour, 1, n_steps_fixed / steps_per_hour); + accumulate_annual_for_year("disp_solve_state", "disp_solve_state_ann", sim_setup.m_report_step / 3600.0 / as_double("disp_frequency"), steps_per_hour, 1, n_steps_fixed / steps_per_hour); + + // 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 (as_boolean("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); + + // Calculated Outputs + // 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 + // Then, add water usage from mirror cleaning + ssc_number_t V_water_cycle = as_number("annual_total_water_use"); + 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_cycle + V_water_mirrors)); + + ssc_number_t ae = as_number("annual_energy"); //[kWe-hr] + ssc_number_t pg = as_number("annual_W_cycle_gross"); //[kWe-hr] + ssc_number_t annual_sales_energy = as_number("annual_sales_energy"); //[kWe-hr] + 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 kWh_sales_energy_per_kW_nameplate = 0.0; + double nameplate = system_capacity; //[kWe] + if (nameplate > 0.0) { + kWh_per_kW = ae / nameplate; + kWh_sales_energy_per_kW_nameplate = annual_sales_energy / nameplate; + } + + assign("capacity_factor", (ssc_number_t)(kWh_per_kW / ((double)n_steps_fixed / (double)steps_per_hour) * 100.)); + assign("sales_energy_capacity_factor", (ssc_number_t)(kWh_sales_energy_per_kW_nameplate / ((double)n_steps_fixed / (double)steps_per_hour) * 100.)); + assign("kwh_per_kw", (ssc_number_t)kWh_per_kW); + + ssc_number_t* p_pricing_mult = as_array("pricing_mult", &count); + + std::vector> ppa_pairs; + ppa_pairs.resize(count); + for (size_t i = 0; i < count; i++) { + ppa_pairs[i].first = i; + ppa_pairs[i].second = p_pricing_mult[i]; + } + + 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 9fcc0d661..0ad5748c5 100644 --- a/ssc/sscapi.cpp +++ b/ssc/sscapi.cpp @@ -117,6 +117,7 @@ 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, @@ -218,6 +219,7 @@ 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_ptes_design_point, From e334c3748ecb4021429444a2abc442bbedf7694a Mon Sep 17 00:00:00 2001 From: tyneises Date: Sat, 22 Jul 2023 14:41:26 -0500 Subject: [PATCH 33/46] develop mspt iph cmod --- ssc/cmod_mspt_iph.cpp | 533 +++++++++++------------------------------- 1 file changed, 137 insertions(+), 396 deletions(-) diff --git a/ssc/cmod_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp index 7442313b5..38777dc85 100644 --- a/ssc/cmod_mspt_iph.cpp +++ b/ssc/cmod_mspt_iph.cpp @@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "csp_solver_mspt_receiver_222.h" #include "csp_solver_mspt_receiver.h" #include "csp_solver_mspt_collector_receiver.h" -#include "csp_solver_pc_Rankine_indirect_224.h" +#include "csp_solver_pc_heat_sink.h" #include "csp_solver_two_tank_tes.h" #include "csp_solver_tou_block_schedules.h" @@ -80,8 +80,7 @@ static var_info _cm_vtab_mspt_iph[] = { { 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, "P_ref", "Reference output electric power at design condition", "MW", "", "System Design", "*", "", ""}, - { SSC_INPUT, SSC_NUMBER, "design_eff", "Power cycle efficiency at design", "none", "", "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", "*", "", ""}, @@ -211,7 +210,6 @@ static var_info _cm_vtab_mspt_iph[] = { { SSC_INPUT, SSC_NUMBER, "heater_spec_cost", "Heater specific cost", "$/kWht", "", "System Costs", "is_parallel_htr=1", "", "" }, { 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", "*", "", ""}, @@ -225,67 +223,8 @@ static var_info _cm_vtab_mspt_iph[] = { { 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", "*", "", "" }, - -//RADIATIVE COOLING WITH COLD STORAGE -// Includes radiative cooling cost parameters -{ SSC_INPUT, SSC_NUMBER, "h_ctes_tank_min", "Minimum allowable water height in storage tank", "m", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "ctes_tshours", "Equivalent full load storage hours", "hr", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "ctes_field_fl", "Fluid in radiator field. 3=liquid water. Other = Glycol.", "-", "", "RADCOOL", "?=3", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "h_ctes_tank", "Total height of cold storage tank when full", "m", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "u_ctes_tank", "Loss coefficient from cold storage tank", "W/m2-K", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "ctes_tankpairs", "Number of equivalent tank pairs", "-", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "T_ctes_cold_design", "Design value of cooled water to power block", "C", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "T_ctes_warm_design", "Design value of warm water returning from power block", "C", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "T_ctes_warm_ini", "Initial value of warm tank", "C", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "T_ctes_cold_ini", "Initial value of cold tank", "C", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "f_ctes_warm_ini", "Initial fraction of avail. volume that is warm", "-", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "rad_multiplier", "Ratio of radiator field area to solar aperature area", "-", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "m_dot_radpanel", "Mass flow rate through single radiator panel", "kg/sec", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "n_rad_tubes", "Number of parallel tubes in single radiator panel", "-", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "W_rad_tubes", "Center-to-center distance between tubes in radiator panel", "m", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "L_rad", "Length of radiator panel row", "m", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "th_rad_panel", "Thickness of radiator panel", "m", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "D_rad_tubes", "Inner diameter of tubes in radiator panel", "m", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "k_panel", "Thermal conductivity of radiator panel material", "W/m-K", "", "RADCOOL", "?=235", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "epsilon_radtop", "Emmissivity of top of radiator panel", "-", "", "RADCOOL", "?=.95", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "epsilon_radbot", "Emmissivity of top of radiator panel bottom (facing ground)", "-", "", "RADCOOL", "?=.07", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "epsilon_radgrnd", "Emmissivity of ground underneath radiator panel", "-", "", "RADCOOL", "?=.90", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "L_rad_sections", "Length of individual radiator panel", "m", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "epsilon_radHX", "Effectiveness of HX between radiative field and cold storage", "-", "", "RADCOOL", "?=.8", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "ctes_type", "Type of cold storage (2=two tank, 3= three node)", "-", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "radiator_unitcost", "Cost of radiative panels", "$/m^2", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "radiator_installcost", "Installation cost of radiative panels", "$/m^2", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "radiator_fluidcost", "Cost of circulating fluid in radiative panels", "$/L", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "radfluid_vol_ratio", "Ratio of fluid in distribution to fluid in panels", "-", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "ctes_cost", "Cost of cold storage construction", "$/L", "", "RADCOOL", "?=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "rad_pressuredrop", "Average pressure drop through a radiative panel & distribution", "kPa", "", "RADCOOL", "?=0", "", ""}, - -// Power Cycle Inputs -{ SSC_INPUT, SSC_NUMBER, "pc_config", "PC configuration 0=Steam Rankine (224), 1=user defined", "", "", "Power Cycle", "?=0", "INTEGER", ""}, -{ SSC_INPUT, SSC_NUMBER, "pb_pump_coef", "Pumping power to move 1kg of HTF through PB loop", "kW/kg", "", "Power Cycle", "*", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "startup_time", "Time needed for power block startup", "hr", "", "Power Cycle", "*", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "startup_frac", "Fraction of design thermal power needed for startup", "none", "", "Power Cycle", "*", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "cycle_max_frac", "Maximum turbine over design operation fraction", "", "", "Power Cycle", "*", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "cycle_cutoff_frac", "Minimum turbine operation fraction before shutdown", "", "", "Power Cycle", "*", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "q_sby_frac", "Fraction of thermal power required for standby", "", "", "Power Cycle", "*", "", ""}, - -// Steam Rankine cycle -{ SSC_INPUT, SSC_NUMBER, "dT_cw_ref", "Reference condenser cooling water inlet/outlet temperature difference", "C", "", "Rankine Cycle", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "T_amb_des", "Reference ambient temperature at design point", "C", "", "Rankine Cycle", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "CT", "Condensor type: 1=evaporative, 2=air, 3=hybrid", "", "", "Rankine Cycle", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "T_approach", "Cooling tower approach temperature", "C", "", "Rankine Cycle", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "T_ITD_des", "ITD at design for dry system", "C", "", "Rankine Cycle", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "P_cond_ratio", "Condenser pressure ratio", "", "", "Rankine Cycle", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "pb_bd_frac", "Power block blowdown steam fraction", "", "", "Rankine Cycle", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "P_cond_min", "Minimum condenser pressure", "inHg", "", "Rankine Cycle", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "n_pl_inc", "Number of part-load increments for the heat rejection system", "none", "", "Rankine Cycle", "pc_config=0", "INTEGER", ""}, -{ SSC_INPUT, SSC_ARRAY, "F_wc", "TOU array of fractions indicating wet cooling share for hybrid cooling", "", "", "System Control", "pc_config=0", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "tech_type", "Turbine inlet pressure control 1=Fixed, 3=Sliding", "", "", "Rankine Cycle", "pc_config=0", "", ""}, - -// User Defined 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 Power Cycle", "pc_config=1", "", ""}, -{ 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 Power Cycle", "pc_config=1", "", ""}, -{ 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 Power Cycle", "pc_config=1", "", ""}, +// 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", "*", "", "" }, @@ -415,8 +354,8 @@ static var_info _cm_vtab_mspt_iph[] = { // 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", "kWe", "", "System Costs", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "cp_system_nameplate", "System capacity for capacity payments", "MWe", "", "System Costs", "*", "", "" }, +{ 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 @@ -463,27 +402,10 @@ static var_info _cm_vtab_mspt_iph[] = { { 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", "*", "", "" }, -// UDPC -{ SSC_OUTPUT, SSC_NUMBER, "n_T_htf_pars_calc", "UDPC number of HTF parametric values", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "n_T_amb_pars_calc", "UDPC number of ambient temp parametric values", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "n_m_dot_pars_calc", "UDPC number of mass flow parametric values", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "T_htf_ref_calc", "UDPC reference HTF temperature", "C", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "T_htf_low_calc", "UDPC low level HTF temperature", "C", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "T_htf_high_calc", "UDPC high level HTF temperature", "C", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "T_amb_ref_calc", "UDPC reference ambient temperature", "C", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "T_amb_low_calc", "UDPC low level ambient temperature", "C", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "T_amb_high_calc", "UDPC high level ambient temperature", "C", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_ND_ref_calc", "UDPC reference normalized mass flow rate", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_ND_low_calc", "UDPC low level normalized mass flow rate", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "m_dot_htf_ND_high_calc", "UDPC high level normalized mass flow rate", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "W_dot_gross_ND_des_calc", "UDPC calculated normalized gross power at design", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "Q_dot_HTF_ND_des_calc", "UDPC calculated normalized heat input at design", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "W_dot_cooling_ND_des_calc", "UPPC calculated normalized cooling power at design", "", "", "UDPC Design Calc", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "m_dot_water_ND_des_calc", "UDPC calculated water use at design", "", "", "UDPC Design Calc", "*", "", "" }, +//{ 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", "*", "", "" }, @@ -496,7 +418,7 @@ static var_info _cm_vtab_mspt_iph[] = { { 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", "MWe", "", "System Design Calc", "*", "", "" }, +{ 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", "*", "", "" }, @@ -509,9 +431,6 @@ static var_info _cm_vtab_mspt_iph[] = { { 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.rad_field", "Radiative field cost" "$", "", "System Costs", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.rad_fluid", "Radiative fluid cost" "$", "", "System Costs", "*", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "csp.pt.cost.rad_storage", "Cold storage 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", "*", "", "" }, @@ -604,29 +523,12 @@ static var_info _cm_vtab_mspt_iph[] = { { 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", "", "" }, -// Power cycle outputs -{ SSC_OUTPUT, SSC_ARRAY, "eta", "PC efficiency, gross", "", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "q_pb", "PC input energy", "MWt", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "m_dot_pc", "PC HTF mass flow rate", "kg/s", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "q_pc_startup", "PC startup thermal energy", "MWht", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_startup", "PC startup thermal power", "MWt", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "P_cycle", "PC electrical power output, gross", "MWe", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "T_pc_in", "PC HTF inlet temperature", "C", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "T_pc_out", "PC HTF outlet temperature", "C", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "m_dot_water_pc", "PC water consumption, makeup + cooling", "kg/s", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "T_cond_out", "PC condenser water outlet temperature", "C", "", "PC", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "T_cold", "Cold storage cold temperature", "C", "", "PC", "?", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "m_cold", "Cold storage cold tank mass", "kg", "", "PC", "?", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "m_warm", "Cold storage warm tank mass", "kg", "", "PC", "?", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "T_warm", "Cold storage warm tank temperature", "C", "", "PC", "?", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "T_rad_out", "Radiator outlet temperature", "C", "", "PC", "?", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "A_radfield", "Radiator field surface area", "m^2", "", "PC", "?", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "P_cond", "PC condensing presssure", "Pa", "", "PC", "?", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "P_cond_iter_err", "PC condenser presure iteration error", "", "", "PC", "?", "", "" }, -{ SSC_OUTPUT, SSC_ARRAY, "radcool_control", "Radiative cooling status code", "-", "", "PC", "?", "", ""}, -{ SSC_OUTPUT, SSC_ARRAY, "cycle_htf_pump_power", "Cycle HTF pump power", "MWe", "", "", "sim_type=1", "", "" }, -{ SSC_OUTPUT, SSC_ARRAY, "P_cooling_tower_tot", "Parasitic power condenser operation", "MWe", "", "", "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", "", "" }, // Thermal energy storage outputs { SSC_OUTPUT, SSC_ARRAY, "tank_losses", "TES thermal losses", "MWt", "", "", "sim_type=1", "", "" }, @@ -707,30 +609,30 @@ static var_info _cm_vtab_mspt_iph[] = { { 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 electric power to grid with available derate", "kWe", "", "", "sim_type=1", "", ""}, - -// Annual single-value outputs -{ SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual total electric power to grid", "kWhe", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "annual_W_cycle_gross", "Electrical source - power cycle gross output", "kWhe", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "annual_W_cooling_tower", "Total of condenser operation parasitics", "kWhe", "", "PC", "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_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, "conversion_factor", "Gross to net conversion factor", "%", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "sales_energy_capacity_factor", "Capacity factor considering only positive net generation periods", "%", "", "", "sim_type=1", "", "" }, -{ SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "annual_total_water_use", "Total annual water usage, cycle + mirror washing", "m3", "", "", "sim_type=1", "", ""}, - -{ SSC_OUTPUT, SSC_NUMBER, "disp_objective_ann", "Annual sum of dispatch objective function value", "", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "disp_iter_ann", "Annual sum of dispatch solver iterations", "", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "disp_presolve_nconstr_ann", "Annual sum of dispatch problem constraint count", "", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "disp_presolve_nvar_ann", "Annual sum of dispatch problem variable count", "", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "disp_solve_time_ann", "Annual sum of dispatch solver time", "", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "disp_solve_state_ann", "Annual sum of dispatch solve state", "", "", "", "sim_type=1", "", ""}, -{ SSC_OUTPUT, SSC_NUMBER, "avg_suboptimal_rel_mip_gap", "Average suboptimal relative MIP gap", "%", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_ARRAY, "gen", "Total electric power to grid with available derate", "kWe", "", "", "sim_type=1", "", ""}, +// +//// Annual single-value outputs +//{ SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual total electric power to grid", "kWhe", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "annual_W_cycle_gross", "Electrical source - power cycle gross output", "kWhe", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "annual_W_cooling_tower", "Total of condenser operation parasitics", "kWhe", "", "PC", "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_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, "conversion_factor", "Gross to net conversion factor", "%", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "sales_energy_capacity_factor", "Capacity factor considering only positive net generation periods", "%", "", "", "sim_type=1", "", "" }, +//{ SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "annual_total_water_use", "Total annual water usage, cycle + mirror washing", "m3", "", "", "sim_type=1", "", ""}, +// +//{ SSC_OUTPUT, SSC_NUMBER, "disp_objective_ann", "Annual sum of dispatch objective function value", "", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "disp_iter_ann", "Annual sum of dispatch solver iterations", "", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "disp_presolve_nconstr_ann", "Annual sum of dispatch problem constraint count", "", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "disp_presolve_nvar_ann", "Annual sum of dispatch problem variable count", "", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "disp_solve_time_ann", "Annual sum of dispatch solver time", "", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "disp_solve_state_ann", "Annual sum of dispatch solve state", "", "", "", "sim_type=1", "", ""}, +//{ SSC_OUTPUT, SSC_NUMBER, "avg_suboptimal_rel_mip_gap", "Average suboptimal relative MIP gap", "%", "", "", "sim_type=1", "", ""}, { SSC_OUTPUT, SSC_NUMBER, "sim_cpu_run_time", "Simulation duration clock time", "s", "", "", "sim_type=1", "", ""}, @@ -745,7 +647,7 @@ class cm_mspt_iph : public compute_module 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); + //add_var_info(vtab_technology_outputs); } bool relay_message(string& msg, double percent) @@ -848,15 +750,12 @@ class cm_mspt_iph : public compute_module // 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 W_dot_cycle_des = as_double("P_ref"); //[MWe] - double eta_cycle = as_double("design_eff"); //[-] double tshours = as_double("tshours"); //[-] // System Design Calcs - double q_dot_pc_des = W_dot_cycle_des / eta_cycle; //[MWt] + 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] - //double system_capacity = W_dot_cycle_des * as_double("gross_net_conversion_factor") * 1.E3; //[kWe] // Weather reader C_csp_weatherreader weather_reader; @@ -1372,169 +1271,35 @@ class cm_mspt_iph : public compute_module // *********************************************** // *********************************************** - // Power cycle + // Heat Sink // *********************************************** // *********************************************** - C_csp_power_cycle* p_csp_power_cycle; - // Steam Rankine and User Defined power cycle classes - C_pc_Rankine_indirect_224 rankine_pc; - - // Check power block type - int pb_tech_type = as_integer("pc_config"); - if (pb_tech_type == 0 || pb_tech_type == 1) // Rankine or User Defined - { - 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("design_eff"); - pc->m_T_htf_hot_ref = as_double("T_htf_hot_des"); - pc->m_T_htf_cold_ref = as_double("T_htf_cold_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("rec_htf"); // power cycle HTF is same as receiver HTF - pc->m_pc_fl_props = as_matrix("field_fl_props"); - - if (pb_tech_type == 0) - { - pc->m_P_boil_des = 100; //[bar] - 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_CT = as_integer("CT"); // cooling tech type: 1=evaporative, 2=air, 3=hybrid , 5= custom for rad cool, 6= custom for rad cool - pc->m_tech_type = as_integer("tech_type"); // 1: Fixed, 3: Sliding - if (pc->m_tech_type == 2) { pc->m_tech_type = 1; }; // changing fixed pressure for the trough to fixed pressure for the tower - //if (pc->m_tech_type == 8) { pc->m_tech_type = 3; }; // changing sliding pressure for the trough to sliding pressure for the tower -> don't, this disallows the use of the old tower sliding curves - - if (!(pc->m_tech_type == 1 || pc->m_tech_type == 3 || pc->m_tech_type == 5 || pc->m_tech_type == 6 || pc->m_tech_type == 7 || pc->m_tech_type == 8)) - { - std::string tech_msg = util::format("tech_type must be either 1 (fixed pressure) or 3 (sliding). Input was %d." - " Simulation proceeded with fixed pressure", pc->m_tech_type); - pc->m_tech_type = 1; - } - 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"); - //parameters for radiative cooling with cold storage - C_csp_cold_tes* two_tank = &rankine_pc.mc_two_tank_ctes; //pointer for two tank - C_csp_stratified_tes* stratified = &rankine_pc.mc_stratified_ctes; //pointer for stratified - - two_tank->ms_params.m_ctes_type = as_integer("ctes_type"); - stratified->ms_params.m_ctes_type = as_integer("ctes_type"); - - if (rankine_pc.ms_params.m_CT == 4) - { - if (two_tank->ms_params.m_ctes_type == 2) - { - two_tank->ms_params.m_h_tank_min = as_double("h_ctes_tank_min"); - two_tank->ms_params.m_ts_hours = as_double("ctes_tshours"); - two_tank->ms_params.m_h_tank = as_double("h_ctes_tank"); - two_tank->ms_params.m_u_tank = as_double("u_ctes_tank"); - two_tank->ms_params.m_tank_pairs = as_integer("ctes_tankpairs"); - two_tank->ms_params.m_T_cold_des = as_double("T_ctes_cold_design"); - two_tank->ms_params.m_T_hot_des = as_double("T_ctes_warm_design"); - two_tank->ms_params.m_T_tank_hot_ini = as_double("T_ctes_warm_ini"); - two_tank->ms_params.m_T_tank_cold_ini = as_double("T_ctes_cold_ini"); - two_tank->ms_params.m_f_V_hot_ini = as_double("f_ctes_warm_ini"); - two_tank->ms_params.m_lat = weather_reader.ms_solved_params.m_lat; - } - if (two_tank->ms_params.m_ctes_type > 2) - { - stratified->ms_params.m_h_tank_min = 0; //hardcode zero minimum height for stratified tanks. - stratified->ms_params.m_ts_hours = as_double("ctes_tshours"); - stratified->ms_params.m_h_tank = as_double("h_ctes_tank"); - stratified->ms_params.m_u_tank = as_double("u_ctes_tank"); - stratified->ms_params.m_tank_pairs = as_integer("ctes_tankpairs"); - stratified->ms_params.m_T_cold_des = as_double("T_ctes_cold_design"); - stratified->ms_params.m_T_hot_des = as_double("T_ctes_warm_design"); - stratified->ms_params.m_T_tank_hot_ini = as_double("T_ctes_warm_ini"); - stratified->ms_params.m_T_tank_cold_ini = as_double("T_ctes_cold_ini"); - stratified->ms_params.m_f_V_hot_ini = as_double("f_ctes_warm_ini"); - stratified->ms_params.m_lat = weather_reader.ms_solved_params.m_lat; - - } - rankine_pc.mc_radiator.ms_params.m_field_fl = as_integer("ctes_field_fl"); - rankine_pc.mc_radiator.ms_params.RM = as_double("rad_multiplier"); - rankine_pc.mc_radiator.ms_params.Asolar_refl = A_sf; //[m2] - rankine_pc.mc_radiator.ms_params.m_dot_panel = as_double("m_dot_radpanel"); - rankine_pc.mc_radiator.ms_params.n = as_integer("n_rad_tubes"); - rankine_pc.mc_radiator.ms_params.W = as_double("W_rad_tubes"); - rankine_pc.mc_radiator.ms_params.L = as_double("L_rad"); - rankine_pc.mc_radiator.ms_params.th = as_double("th_rad_panel"); - rankine_pc.mc_radiator.ms_params.D = as_double("D_rad_tubes"); - rankine_pc.mc_radiator.ms_params.k_panel = as_double("k_panel"); - rankine_pc.mc_radiator.ms_params.epsilon = as_double("epsilon_radtop"); - rankine_pc.mc_radiator.ms_params.epsilonb = as_double("epsilon_radbot"); - rankine_pc.mc_radiator.ms_params.epsilong = as_double("epsilon_radgrnd"); - rankine_pc.mc_radiator.ms_params.Lsec = as_double("L_rad_sections"); - rankine_pc.mc_radiator.ms_params.epsilon_HX = as_double("epsilon_radHX"); - rankine_pc.mc_radiator.ms_params.radfield_dp = as_double("rad_pressuredrop"); - } + 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]); + } - 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]; + 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; - // Set User Defined cycle parameters to appropriate values - 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; + 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"); - // 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; - } - else - { - std::string err_msg = util::format("The specified power cycle configuration, %d, does not exist. See SSC Input Table for help.\n", pb_tech_type); - log(err_msg, SSC_WARNING); - return; - } - - // 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_T_COND_OUT, allocate("T_cond_out", 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); - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_P_COND, allocate("P_cond", n_steps_fixed), n_steps_fixed); - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_P_COND_ITER_ERR, allocate("P_cond_iter_err", n_steps_fixed), n_steps_fixed); - - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_ETA_THERMAL, allocate("eta", n_steps_fixed), n_steps_fixed); - - if (pb_tech_type == 0) { - if (rankine_pc.ms_params.m_CT == 4) { - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_T_COLD, allocate("T_cold", n_steps_fixed), n_steps_fixed); - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_M_COLD, allocate("m_cold", n_steps_fixed), n_steps_fixed); - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_M_WARM, allocate("m_warm", n_steps_fixed), n_steps_fixed); - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_T_WARM, allocate("T_warm", n_steps_fixed), n_steps_fixed); - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_T_RADOUT, allocate("T_rad_out", n_steps_fixed), n_steps_fixed); - p_csp_power_cycle->assign(C_pc_Rankine_indirect_224::E_RADCOOL_CNTRL, allocate("radcool_control", n_steps_fixed), n_steps_fixed); - } - } + // 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); //// ********************************************************* //// ********************************************************* @@ -1856,9 +1621,9 @@ class cm_mspt_iph : public compute_module as_matrix("field_fl_props"), as_integer("rec_htf"), as_matrix("field_fl_props"), - as_double("P_ref") / as_double("design_eff"), //[MWt] + q_dot_pc_des, //[MWt] as_double("solarm"), //[-] - as_double("P_ref") / as_double("design_eff") * as_double("tshours"), + Q_tes, as_double("h_tank"), as_double("u_tank"), as_integer("tank_pairs"), @@ -2087,7 +1852,6 @@ class cm_mspt_iph : public compute_module if (as_boolean("is_dispatch")) { - double heater_startup_cost = 0.0; if (is_parallel_heater) { double heater_mult = as_double("heater_mult"); //[-] @@ -2100,7 +1864,7 @@ class cm_mspt_iph : public compute_module 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_csu_cost_calc = as_double("disp_csu_cost_rel") * q_dot_pc_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, heater_startup_cost, disp_csu_cost_calc, as_double("disp_pen_ramping"), @@ -2113,7 +1877,7 @@ class cm_mspt_iph : public compute_module // Instantiate Solver C_csp_solver csp_solver(weather_reader, collector_receiver, - *p_csp_power_cycle, + c_heat_sink, storage, tou, dispatch, @@ -2359,47 +2123,47 @@ class cm_mspt_iph : public compute_module // ************************* // 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); + //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 @@ -2407,19 +2171,20 @@ class cm_mspt_iph : public compute_module 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_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 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 = 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); //[kWe] - assign("nameplate", system_capacity * 1.E-3); //[MWe] - assign("cp_system_nameplate", system_capacity * 1.E-3); //[MWe] + 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 ************ @@ -2442,55 +2207,25 @@ class cm_mspt_iph : public compute_module double A_rec_ref = as_double("rec_ref_area"); double rec_cost_scaling_exp = as_double("rec_cost_exp"); - double Q_storage = as_double("P_ref") / as_double("design_eff") * as_double("tshours"); 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 W_dot_design = as_double("P_ref"); double power_cycle_spec_cost = as_double("plant_spec_cost"); // Set heater thermal power and cost above, because they're dependent on is_heater boolean - 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; - - if (rankine_pc.ms_params.m_CT == 4) { - radfield_area = rankine_pc.mc_radiator.ms_params.Afield; - radfield_vol = rankine_pc.mc_radiator.ms_params.D * rankine_pc.mc_radiator.ms_params.D / 4 * PI * rankine_pc.mc_radiator.ms_params.n * rankine_pc.mc_radiator.ms_params.Np * rankine_pc.mc_radiator.ms_params.L; //Calculate volume in radiator panel tubes = pi/4*d^2*L*n*Np - if (rankine_pc.mc_two_tank_ctes.ms_params.m_ctes_type == 2) //If two tank - { - coldstorage_vol = rankine_pc.mc_two_tank_ctes.get_physical_volume(); - } - if (rankine_pc.mc_two_tank_ctes.ms_params.m_ctes_type > 2) //If stratified - { - coldstorage_vol = rankine_pc.mc_stratified_ctes.get_physical_volume(); - - } - - rad_unitcost = as_double("radiator_unitcost"); - rad_installcost = as_double("radiator_installcost"); - rad_fluidcost = as_double("radiator_fluidcost"); - rad_volmulti = as_double("radfluid_vol_ratio"); - coldstorage_unitcost = as_double("ctes_cost"); - } - double bop_spec_cost = as_double("bop_spec_cost"); double fossil_backup_spec_cost = as_double("fossil_spec_cost"); double contingency_rate = as_double("contingency_rate"); - //land area - double total_land_area = total_land_area_before_rad_cooling + radfield_area / 4046.86 /*acres/m^2*/; + // 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 @@ -2515,6 +2250,16 @@ class cm_mspt_iph : public compute_module 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, @@ -2532,13 +2277,13 @@ class cm_mspt_iph : public compute_module A_rec_ref, rec_cost_scaling_exp, - Q_storage, + Q_tes, tes_spec_cost, Q_CT_tes, CT_tes_spec_cost, - W_dot_design, + q_dot_pc_des, power_cycle_spec_cost, q_dot_heater_des, //[MWt] @@ -2608,13 +2353,6 @@ class cm_mspt_iph : public compute_module assign("csp.pt.cost.power_block", (ssc_number_t)power_cycle_cost); assign("heater_cost", (ssc_number_t)heater_cost); - if (pb_tech_type == 0) { - if (rankine_pc.ms_params.m_CT == 4) { - assign("csp.pt.cost.rad_field", (ssc_number_t)rad_field_totcost); - assign("csp.pt.cost.rad_fluid", (ssc_number_t)rad_fluid_totcost); - assign("csp.pt.cost.rad_storage", (ssc_number_t)rad_storage_totcost); - } - } 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); @@ -2725,7 +2463,7 @@ class cm_mspt_iph : public compute_module } - + /* // Do unit post-processing here double* p_q_pc_startup = allocate("q_pc_startup", n_steps_fixed); size_t count_pc_su = 0; @@ -2757,6 +2495,7 @@ class cm_mspt_iph : public compute_module 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 } + */ // Set output data from heliostat class size_t n_rows_eta_map = heliostatfield.ms_params.m_eta_map.nrows(); @@ -2789,6 +2528,7 @@ class cm_mspt_iph : public compute_module } } + /* 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); @@ -2883,6 +2623,7 @@ class cm_mspt_iph : public compute_module ppa_pairs[i].first = i; ppa_pairs[i].second = p_pricing_mult[i]; } + */ std::clock_t clock_end = std::clock(); double sim_cpu_run_time = (clock_end - clock_start) / (double)CLOCKS_PER_SEC; //[s] From acb90cb280d697bc89e089c3d2cc9f9cea130fea Mon Sep 17 00:00:00 2001 From: tyneises Date: Sun, 23 Jul 2023 22:16:56 -0500 Subject: [PATCH 34/46] update mspt iph cmod --- ssc/cmod_mspt_iph.cpp | 330 +----------------------------------------- 1 file changed, 5 insertions(+), 325 deletions(-) diff --git a/ssc/cmod_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp index 38777dc85..fdcd7a94e 100644 --- a/ssc/cmod_mspt_iph.cpp +++ b/ssc/cmod_mspt_iph.cpp @@ -245,48 +245,10 @@ static var_info _cm_vtab_mspt_iph[] = { { 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", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "can_cycle_use_standby", "Can the cycle use standby operation?", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "disp_horizon", "Time horizon for dispatch optimization", "hour", "", "System Control", "is_dispatch=1", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "disp_frequency", "Frequency for dispatch optimization calculations", "hour", "", "System Control", "is_dispatch=1", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "disp_steps_per_hour", "Time steps per hour for dispatch optimization calculations", "", "", "System Control", "?=1", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "disp_max_iter", "Max number of dispatch optimization iterations", "", "", "System Control", "is_dispatch=1", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "disp_timeout", "Max dispatch optimization solve duration", "s", "", "System Control", "is_dispatch=1", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "disp_mip_gap", "Dispatch optimization solution tolerance", "", "", "System Control", "is_dispatch=1", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "disp_spec_bb", "Dispatch optimization B&B heuristic", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "disp_reporting", "Dispatch optimization reporting level", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "disp_spec_presolve", "Dispatch optimization presolve heuristic", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "disp_spec_scaling", "Dispatch optimization scaling heuristic", "", "", "System Control", "?=-1", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "disp_time_weighting", "Dispatch optimization future time discounting factor", "", "", "System Control", "?=0.99", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "is_write_ampl_dat", "Write AMPL data files for dispatch run", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_STRING, "ampl_data_dir", "AMPL data file directory", "", "", "System Control", "?=''", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "is_ampl_engine", "Run dispatch optimization with external AMPL engine", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_STRING, "ampl_exec_call", "System command to run AMPL code", "", "", "System Control", "?='ampl sdk_solution.run'", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "disp_rsu_cost_rel", "Receiver startup cost", "$/MWt/start", "", "System Control", "", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "disp_csu_cost_rel", "Cycle startup cost", "$/MWe-cycle/start", "", "System Control", "", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "disp_pen_ramping", "Dispatch cycle production change penalty", "$/MWe-change", "", "System Control", "", "", ""}, -{ SSC_INPUT, SSC_NUMBER, "disp_inventory_incentive", "Dispatch storage terminal inventory incentive multiplier", "", "", "System Control", "?=0.0", "", "SIMULATION_PARAMETER"}, + +// 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"}, -{ SSC_INPUT, SSC_NUMBER, "is_wlim_design", "Use fixed design-point net electricity generation limits (dispatch opt only)", "", "", "System Control", "?=0", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "disp_wlim_maxspec", "Fixed design-point max net power to the grid (dispatch opt only)", "", "", "System Control", "is_wlim_design=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "is_wlim_series", "Use time-series net electricity generation limits (dispatch opt only)", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_ARRAY, "wlim_series", "Time series net electicity generation limits (dispatch opt only)", "kWe", "", "System Control", "is_wlim_series=1", "", "SIMULATION_PARAMETER"}, - -// Pricing schedules and multipliers - // Ideally this would work with sim_type = 2, but UI inputs availability depends on financial mode -{ SSC_INPUT, SSC_NUMBER, "ppa_multiplier_model", "PPA multiplier model 0: dispatch factors dispatch_factorX, 1: hourly multipliers dispatch_factors_ts", "0/1", "0=diurnal,1=timestep", "Time of Delivery Factors", "?=0", /*need a default so this var works in required_if*/ "INTEGER,MIN=0", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "ppa_soln_mode", "PPA solution mode (0=Specify IRR target, 1=Specify PPA price)", "", "", "Financial Solution Mode", "sim_type=1&ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER" }, -{ SSC_INPUT, SSC_ARRAY, "dispatch_factors_ts", "Dispatch payment factor array", "", "", "Time of Delivery Factors", "ppa_multiplier_model=1&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_NUMBER, "en_electricity_rates", "Enable electricity rates for grid purchase", "0/1", "", "Electricity Rates", "?=0", "", "SIMULATION_PARAMETER"}, -{ 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"}, -{ 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" }, - -{ SSC_INPUT, SSC_NUMBER, "is_dispatch_series", "Use time-series dispatch factors", "", "", "System Control", "?=0", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_ARRAY, "dispatch_series", "Time series dispatch factors", "", "", "System Control", "", "", "SIMULATION_PARAMETER"}, -{ SSC_INPUT, SSC_ARRAY, "ppa_price_input", "PPA prices - yearly", "$/kWh", "", "Revenue", "ppa_multiplier_model=0&csp_financial_model<5&is_dispatch=1&sim_type=1", "", "SIMULATION_PARAMETER"}, -{ 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"}, // Costs { SSC_INPUT, SSC_NUMBER, "tower_fixed_cost", "Tower fixed cost", "$", "", "System Costs", "*", "", "" }, @@ -664,84 +626,6 @@ class cm_mspt_iph : public compute_module bool is_dispatch = as_boolean("is_dispatch"); - // ***************************************************** - // Check deprecated variables - // Piping loss coefficient - bool is_piping_loss_assigned = is_assigned("piping_loss"); - bool is_piping_loss_coefficient_assigned = is_assigned("piping_loss_coefficient"); - - if (is_piping_loss_assigned && is_piping_loss_coefficient_assigned) { - log("We replaced the functionality of input variable piping_loss with new input variable piping_loss_coefficient," - " so the model does not use your piping_loss input."); - } - else if (is_piping_loss_assigned) { - throw exec_error("tcsmolten_salt", "We replaced the functionality of input variable piping_loss [Wt/m] with new input variable piping_loss_coefficient [Wt/m2-K]." - " The new input scales piping thermal losses as a function of receiver thermal power and design-point temperatures." - " Please define piping_loss_coefficient in your script."); - } - - bool is_csp_pt_tes_init_hot_htf_percent_assigned = is_assigned("csp.pt.tes.init_hot_htf_percent"); - bool is_tes_hot_htf_percent_assigned = is_assigned("tes_hot_htf_percent_assigned"); - if (is_csp_pt_tes_init_hot_htf_percent_assigned && is_tes_hot_htf_percent_assigned) { - log("We renamed input variable csp.pt.tes.init_hot_htf_percent to tes_init_hot_htf_percent," - " you provided both inputs, so the model does not use your csp.pt.tes.init_hot_htf_percent input."); - } - else if (is_csp_pt_tes_init_hot_htf_percent_assigned) { - throw exec_error("tcsmolten_salt", "We renamed input variable csp.pt.tes.init_hot_htf_percent to tes_init_hot_htf_percent," - " please define tes_init_hot_htf_percent in your script."); - } - - if (is_assigned("P_boil")) { - log("We removed boiler pressure (P_boil) as a user input to the Rankine Cycle model. Because the cycle efficiency" - " is provided by the user, the boiler pressure input does not modify the efficiency as one might expect. Instead the model" - " uses boiler pressure in second order calculations to 1) define a boiling temperature to normalize off-design HTF temperature and" - " 2) estimate steam mass flow for cycle make-up water calculations. Because boiler pressure only has influences" - " results in these minor non-intuitive ways, we decided to hardcode the valu to 100 bar."); - } - - if (is_dispatch) { - // Cycle startup cost disp_csu_cost - bool is_disp_csu_cost_assigned = is_assigned("disp_csu_cost"); - bool is_disp_csu_cost_rel_assigned = is_assigned("disp_csu_cost_rel"); - - if (is_disp_csu_cost_assigned && is_disp_csu_cost_rel_assigned) { - log("We replaced the functionality of input variable disp_csu_cost with new input variable disp_csu_cost_rel," - " so the model does not use your disp_csu_cost input."); - } - else if (is_disp_csu_cost_assigned) { - throw exec_error("tcsmolten_salt", "We replaced the functionality of input variable disp_csu_cost [$/start] with new input variable disp_csu_cost_rel [$/MWe-cycle/start]." - " The new input represents cycle startup costs normalized by the cycle design capacity." - " Please define disp_csu_cost_rel in your script."); - } - - // Receiver startup cost disp_rsu_cost - bool is_disp_rsu_cost_assigned = is_assigned("disp_rsu_cost"); - bool is_disp_rsu_cost_rel_assigned = is_assigned("disp_rsu_cost_rel"); - - if (is_disp_rsu_cost_assigned && is_disp_rsu_cost_rel_assigned) { - log("We replaced the funcationality of input variable disp_rsu_cost with new input variable disp_rsu_cost_rel," - " so the model does not use your disp_rsu_cost input."); - } - else if (is_disp_rsu_cost_assigned) { - throw exec_error("tcsmolten_salt", "We replaced the functionality of input variable disp_rsu_cost [$/start] with new input variable disp_rsu_cost_rel [$/MWe-cycle/start]." - " The new input represents receiver startup costs normalized by the receiver design thermal power." - " Please define disp_rsu_cost_rel in your script."); - } - - // Cycle ramping - bool is_disp_pen_delta_w_assigned = is_assigned("disp_pen_delta_w"); - bool is_disp_pen_ramping_assigned = is_assigned("disp_pen_ramping"); - if (is_disp_pen_delta_w_assigned && is_disp_pen_ramping_assigned) { - log("We replaced the functionality of input variable disp_pen_delta_w with new input variable disp_pen_ramping," - " so the model does not use your disp_pen_delta_w input"); - } - else if (is_disp_pen_delta_w_assigned) { - throw exec_error("tcsmolten_salt", "We replaced the functionality of input variable disp_pen_delta_w [$/kWe-change] with new input variable disp_pen_ramping [$/MWe-change]." - " The new input represents the receiver startup costs in an optimization model that uses absolute grid prices." - " Please define disp_pen_ramping in your script. SAM's default value in the molten salt power tower model is 1.0"); - } - } - // ***************************************************** // ***************************************************** @@ -1653,34 +1537,12 @@ class cm_mspt_iph : public compute_module 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"); - if (is_dispatch) - { - tou.mc_dispatch_params.m_w_lim_full.resize(n_steps_full); - std::fill(tou.mc_dispatch_params.m_w_lim_full.begin(), tou.mc_dispatch_params.m_w_lim_full.end(), 9.e99); - if (as_boolean("is_wlim_series")) - { - size_t n_wlim_series = 0; - ssc_number_t* wlim_series = as_array("wlim_series", &n_wlim_series); - if (n_wlim_series != n_steps_full) - throw exec_error("tcsmolten_salt", "Invalid net electricity generation limit series dimension. Matrix must have " + util::to_string((int)n_steps_full) + " rows."); - for (size_t i = 0; i < n_steps_full; i++) - tou.mc_dispatch_params.m_w_lim_full.at(i) = (double)wlim_series[i]; - } - else if (as_boolean("is_wlim_design")) { - double wlim_design = as_double("disp_wlim_maxspec"); - for (size_t i = 0; i < n_steps_full; i++) - tou.mc_dispatch_params.m_w_lim_full.at(i) = wlim_design; - } - } - 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; @@ -1709,112 +1571,7 @@ class cm_mspt_iph : public compute_module double ppa_price_year1 = std::numeric_limits::quiet_NaN(); if (sim_type == 1) { - 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("tcsmolten_salt", "\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("tcsmolten_salt", "\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("tcsmolten_salt", "\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") || is_dispatch) { - 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 { // 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 (ppa_mult_model == 0) // standard diurnal 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"); - if (tou_params->mc_pricing.mc_weekdays.ncells() == 1) { tou_params->mc_pricing.mc_weekdays.resize_fill(12, 24, 1.); }; - tou_params->mc_pricing.mc_weekends = as_matrix("dispatch_sched_weekend"); - if (tou_params->mc_pricing.mc_weekends.ncells() == 1) { tou_params->mc_pricing.mc_weekends.resize_fill(12, 24, 1.); }; - - auto dispatch_tod_factors = as_vector_double("dispatch_tod_factors"); - if (dispatch_tod_factors.size() != 9) - throw exec_error("tcsmolten_salt", 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 { - // 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 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) { - 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("tcsmolten_salt", 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] - } - } - 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 == 8) { // No Financial Model + if (csp_financial_model == 8) { // No Financial Model if (is_dispatch) { throw exec_error("tcsmolten_salt", "Can't select dispatch optimization if No Financial model"); } @@ -1826,7 +1583,7 @@ class cm_mspt_iph : public compute_module } } else { - throw exec_error("tcsmolten_salt", "csp_financial_model must be 1, 2, 3, 4, or 6"); + throw exec_error("mspt_iph", "csp_financial_model must 8"); } } else if (sim_type == 2) { @@ -1849,30 +1606,7 @@ class cm_mspt_iph : public compute_module // ***************************************************** // System dispatch csp_dispatch_opt dispatch; - - if (as_boolean("is_dispatch")) { - - double heater_startup_cost = 0.0; - if (is_parallel_heater) { - double heater_mult = as_double("heater_mult"); //[-] - double q_dot_heater_des = q_dot_pc_des * heater_mult; //[MWt] - heater_startup_cost = as_double("disp_hsu_cost_rel") * q_dot_heater_des; //[$/start] - } - - dispatch.solver_params.set_user_inputs(as_boolean("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") * q_dot_pc_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, heater_startup_cost, 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; - } + dispatch.solver_params.dispatch_optimize = false; // Instantiate Solver C_csp_solver csp_solver(weather_reader, @@ -1992,30 +1726,6 @@ class cm_mspt_iph : public compute_module 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("tcsmolten_salt", "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 (size_t 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 (size_t i = 0; i < n_steps_fixed; i++) - tou_params->mc_pricing.mvv_tou_arrays[C_block_schedule_pricing::MULT_PRICE][i] = dispatch_series[i]; - } - - // ***************************************************** // System design is complete, get design parameters from component models as necessary @@ -2559,36 +2269,6 @@ class cm_mspt_iph : public compute_module 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"))); - accumulate_annual_for_year("disp_objective", "disp_objective_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_iter", "disp_iter_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_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.0 / as_double("disp_frequency"), steps_per_hour, 1, n_steps_fixed / steps_per_hour); - accumulate_annual_for_year("disp_solve_state", "disp_solve_state_ann", sim_setup.m_report_step / 3600.0 / as_double("disp_frequency"), steps_per_hour, 1, n_steps_fixed / steps_per_hour); - - // 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 (as_boolean("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); - // Calculated Outputs // 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 From ad886cd54307713a194011e1805fc900d086bb6d Mon Sep 17 00:00:00 2001 From: tyneises Date: Mon, 24 Jul 2023 13:09:33 -0500 Subject: [PATCH 35/46] update mspt iph --- ssc/cmod_mspt_iph.cpp | 195 ++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 122 deletions(-) diff --git a/ssc/cmod_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp index fdcd7a94e..fe37a85fe 100644 --- a/ssc/cmod_mspt_iph.cpp +++ b/ssc/cmod_mspt_iph.cpp @@ -201,13 +201,13 @@ static var_info _cm_vtab_mspt_iph[] = { { 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", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "heater_efficiency", "Heater electric to thermal efficiency", "%", "", "Parallel Heater", "is_parallel_htr=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "f_q_dot_des_allowable_su", "Fraction of design power allowed during startup", "-", "", "Parallel Heater", "is_parallel_htr=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "hrs_startup_at_max_rate", "Duration of startup at max startup power", "hr", "", "Parallel Heater", "is_parallel_htr=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "f_q_dot_heater_min", "Minimum allowable heater output as fraction of design", "", "", "Parallel Heater", "is_parallel_htr=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "disp_hsu_cost_rel", "Heater startup cost", "$/MWt/start", "", "System Control", "is_dispatch=1&is_parallel_htr=1", "", "" }, -{ SSC_INPUT, SSC_NUMBER, "heater_spec_cost", "Heater specific cost", "$/kWht", "", "System Costs", "is_parallel_htr=1", "", "" }, +{ 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 @@ -258,7 +258,7 @@ static var_info _cm_vtab_mspt_iph[] = { { 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, "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", "*", "", "" }, @@ -266,7 +266,7 @@ static var_info _cm_vtab_mspt_iph[] = { { 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, "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", "*", "", "" }, @@ -494,7 +494,7 @@ static var_info _cm_vtab_mspt_iph[] = { // Thermal energy storage outputs { SSC_OUTPUT, SSC_ARRAY, "tank_losses", "TES thermal losses", "MWt", "", "", "sim_type=1", "", "" }, -{ SSC_OUTPUT, SSC_ARRAY, "q_heater", "TES freeze protection power", "MWe", "", "", "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", "", "" }, @@ -517,7 +517,7 @@ static var_info _cm_vtab_mspt_iph[] = { { 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, "P_out_net", "Total electric power to grid", "MWe", "", "", "sim_type=1", "", ""}, +{ 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", "", ""}, @@ -571,30 +571,22 @@ static var_info _cm_vtab_mspt_iph[] = { { 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 electric power to grid with available derate", "kWe", "", "", "sim_type=1", "", ""}, -// -//// Annual single-value outputs -//{ SSC_OUTPUT, SSC_NUMBER, "annual_energy", "Annual total electric power to grid", "kWhe", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "annual_W_cycle_gross", "Electrical source - power cycle gross output", "kWhe", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "annual_W_cooling_tower", "Total of condenser operation parasitics", "kWhe", "", "PC", "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_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, "conversion_factor", "Gross to net conversion factor", "%", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "capacity_factor", "Capacity factor", "%", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "sales_energy_capacity_factor", "Capacity factor considering only positive net generation periods", "%", "", "", "sim_type=1", "", "" }, -//{ SSC_OUTPUT, SSC_NUMBER, "kwh_per_kw", "First year kWh/kW", "kWh/kW", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "annual_total_water_use", "Total annual water usage, cycle + mirror washing", "m3", "", "", "sim_type=1", "", ""}, -// -//{ SSC_OUTPUT, SSC_NUMBER, "disp_objective_ann", "Annual sum of dispatch objective function value", "", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "disp_iter_ann", "Annual sum of dispatch solver iterations", "", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "disp_presolve_nconstr_ann", "Annual sum of dispatch problem constraint count", "", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "disp_presolve_nvar_ann", "Annual sum of dispatch problem variable count", "", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "disp_solve_time_ann", "Annual sum of dispatch solver time", "", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "disp_solve_state_ann", "Annual sum of dispatch solve state", "", "", "", "sim_type=1", "", ""}, -//{ SSC_OUTPUT, SSC_NUMBER, "avg_suboptimal_rel_mip_gap", "Average suboptimal relative MIP gap", "%", "", "", "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_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_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, "sim_cpu_run_time", "Simulation duration clock time", "s", "", "", "sim_type=1", "", ""}, @@ -609,7 +601,7 @@ class cm_mspt_iph : public compute_module 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); + add_var_info(vtab_technology_outputs); } bool relay_message(string& msg, double percent) @@ -1530,7 +1522,7 @@ class cm_mspt_iph : public compute_module // 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_heater", 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); @@ -1696,8 +1688,7 @@ class cm_mspt_iph : public compute_module 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); - + 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); @@ -1924,13 +1915,13 @@ class cm_mspt_iph : public compute_module double CT_tes_spec_cost = 0.0; //double W_dot_design = as_double("P_ref"); - double power_cycle_spec_cost = as_double("plant_spec_cost"); + 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 = as_double("fossil_spec_cost"); + double fossil_backup_spec_cost = 0.0; // as_double("fossil_spec_cost"); double contingency_rate = as_double("contingency_rate"); @@ -2172,29 +2163,53 @@ class cm_mspt_iph : public compute_module log(out_msg, out_type); } + size_t count; + ssc_number_t* p_q_dot_heat_sink = as_array("q_dot_to_heat_sink", &count); - /* - // 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 (count_pc_su != n_steps_fixed) - { - log("q_dot_pc_startup array is a different length than 'n_steps_fixed'.", SSC_WARNING); - return; - } - for (size_t i = 0; i < n_steps_fixed; i++) + // '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++) { - p_q_pc_startup[i] = (float)(p_q_dot_pc_startup[i] * (sim_setup.m_report_step / 3600.0)); //[MWh] + 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_pc, count_m_dot_rec, count_m_dot_water_pc; - count_m_dot_pc = count_m_dot_rec = count_m_dot_water_pc = 0; + size_t count_m_dot_rec = 0; ssc_number_t* p_m_dot_rec = as_array("m_dot_rec", &count_m_dot_rec); - 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); - if (count_m_dot_rec != n_steps_fixed || count_m_dot_pc != n_steps_fixed || count_m_dot_water_pc != n_steps_fixed) + 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; @@ -2202,10 +2217,7 @@ class cm_mspt_iph : public compute_module 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 - 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 } - */ // Set output data from heliostat class size_t n_rows_eta_map = heliostatfield.ms_params.m_eta_map.nrows(); @@ -2238,73 +2250,12 @@ class cm_mspt_iph : public compute_module } } - /* - 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); - - // '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_gensales_after_avail = allocate("gensales_after_avail", 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_W_dot_net[i] * 1.E3 * haf(hour)); //[kWe] - p_gensales_after_avail[i] = max(0.0, p_gen[i]); //[kWe] - } - 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); - accumulate_annual_for_year("gensales_after_avail", "annual_sales_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("P_cooling_tower_tot", "annual_W_cooling_tower", 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("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); 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"))); - // Calculated Outputs - // 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 - // Then, add water usage from mirror cleaning - ssc_number_t V_water_cycle = as_number("annual_total_water_use"); - 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_cycle + V_water_mirrors)); - - ssc_number_t ae = as_number("annual_energy"); //[kWe-hr] - ssc_number_t pg = as_number("annual_W_cycle_gross"); //[kWe-hr] - ssc_number_t annual_sales_energy = as_number("annual_sales_energy"); //[kWe-hr] - 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 kWh_sales_energy_per_kW_nameplate = 0.0; - double nameplate = system_capacity; //[kWe] - if (nameplate > 0.0) { - kWh_per_kW = ae / nameplate; - kWh_sales_energy_per_kW_nameplate = annual_sales_energy / nameplate; - } - - assign("capacity_factor", (ssc_number_t)(kWh_per_kW / ((double)n_steps_fixed / (double)steps_per_hour) * 100.)); - assign("sales_energy_capacity_factor", (ssc_number_t)(kWh_sales_energy_per_kW_nameplate / ((double)n_steps_fixed / (double)steps_per_hour) * 100.)); - assign("kwh_per_kw", (ssc_number_t)kWh_per_kW); - - ssc_number_t* p_pricing_mult = as_array("pricing_mult", &count); - - std::vector> ppa_pairs; - ppa_pairs.resize(count); - for (size_t i = 0; i < count; i++) { - ppa_pairs[i].first = i; - ppa_pairs[i].second = p_pricing_mult[i]; - } - */ - 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] From d5f9df2f923220a4f4df84677ffcbe94bfee3054 Mon Sep 17 00:00:00 2001 From: tyneises Date: Tue, 25 Jul 2023 12:09:05 -0500 Subject: [PATCH 36/46] add lcoh option to mspt iph cmod --- ssc/cmod_mspt_iph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssc/cmod_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp index fe37a85fe..1758a79d0 100644 --- a/ssc/cmod_mspt_iph.cpp +++ b/ssc/cmod_mspt_iph.cpp @@ -1563,7 +1563,7 @@ class cm_mspt_iph : public compute_module double ppa_price_year1 = std::numeric_limits::quiet_NaN(); if (sim_type == 1) { - if (csp_financial_model == 8) { // No Financial Model + 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"); } From 0a39dd237c37e41650525b10fe23533b660c9645 Mon Sep 17 00:00:00 2001 From: tyneises Date: Wed, 26 Jul 2023 12:27:22 -0500 Subject: [PATCH 37/46] add lcoe fcr cmod that does not required input fixed charge rate --- ssc/cmod_lcoefcr.cpp | 172 +++++++++++++++++++++++++++++++++++++++++++ ssc/sscapi.cpp | 2 + 2 files changed, 174 insertions(+) diff --git a/ssc/cmod_lcoefcr.cpp b/ssc/cmod_lcoefcr.cpp index 3db0eee64..aed7ab91e 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,173 @@ 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, "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 = as_double("fixed_operating_cost"); // $ + double voc = as_double("variable_operating_cost"); // $/kWh + double icc = as_double("total_installed_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/sscapi.cpp b/ssc/sscapi.cpp index 0ad5748c5..e067072b2 100644 --- a/ssc/sscapi.cpp +++ b/ssc/sscapi.cpp @@ -153,6 +153,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, @@ -255,6 +256,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, From ffd93578be81e0a798d4917c68cc1f82264cdac7 Mon Sep 17 00:00:00 2001 From: tyneises Date: Tue, 1 Aug 2023 18:01:54 -0500 Subject: [PATCH 38/46] calculate electricity cost --- ssc/cmod_lcoefcr.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ssc/cmod_lcoefcr.cpp b/ssc/cmod_lcoefcr.cpp index aed7ab91e..6e253b8cf 100644 --- a/ssc/cmod_lcoefcr.cpp +++ b/ssc/cmod_lcoefcr.cpp @@ -105,8 +105,12 @@ static var_info vtab_lcoefcr_design[] = // 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, "fixed_operating_cost", "Annual fixed operating cost", "$", "", "Simple LCOE", "sim_type=1", "", "SIMULATION_PARAMETER" }, + { 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" }, @@ -196,10 +200,16 @@ class cm_lcoefcr_design : public compute_module // ***************************************************** double aep = as_double("annual_energy"); // kWh annual output, get from performance model - double foc = as_double("fixed_operating_cost"); // $ + 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)); From 677f65628bd02d6ee85c45d17bf8c2c904295bf7 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Mon, 7 Aug 2023 14:08:34 -0600 Subject: [PATCH 39/46] Add fresnel iph cmod. Fix label bugs in mslf --- ssc/cmod_fresnel_physical.cpp | 8 +- ssc/cmod_fresnel_physical_iph.cpp | 1491 +++++++++++++++++++++++++++++ 2 files changed, 1495 insertions(+), 4 deletions(-) create mode 100644 ssc/cmod_fresnel_physical_iph.cpp diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index f46637fb8..272fcb9b1 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -64,7 +64,7 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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", "Label", "", "", "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", "*", "", "" }, @@ -202,9 +202,9 @@ static var_info _cm_vtab_fresnel_physical[] = { // System Control - /*Sys Control*/{ SSC_INPUT, SSC_NUMBER, "pb_fixed_par", "Label", "", "", "Sys_Control", "*", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "bop_array", "Label", "", "", "Sys_Control", "*", "", "" }, - /*Sys Control*/{ SSC_INPUT, SSC_ARRAY, "aux_array", "Label", "", "", "Sys_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", "", "" }, diff --git a/ssc/cmod_fresnel_physical_iph.cpp b/ssc/cmod_fresnel_physical_iph.cpp new file mode 100644 index 000000000..c0df67333 --- /dev/null +++ b/ssc/cmod_fresnel_physical_iph.cpp @@ -0,0 +1,1491 @@ +/* +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, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "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", "*", "", "" }, + + + // 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, "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", "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", "", "" }, + + // 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, "W_dot_parasitic_tot", "System total electrical parasitic", "MWe", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_ARRAY, "gen", "Total thermal power to heat sink with available 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_iph : public compute_module +{ +public: + + cm_fresnel_physical_iph() + { + add_var_info(_cm_vtab_fresnel_physical_iph); + add_var_info(vtab_adjustment_factors); + } + + 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_htf_cold_des"); //[C] + double T_htf_hot_des = as_double("T_htf_hot_des"); //[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"); + 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_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("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); + } + + // 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_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); + + 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 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 fossil_backup_mwe = 0.0; // MWe + double power_plant_mwe = 0.0; // MWe + double bop_mwe = 0.0; // 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("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); + + + + // 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); + + + + + } +}; + +DEFINE_MODULE_ENTRY(fresnel_physical_iph, "Physical Fresnel IPH applications", 1) \ No newline at end of file From 0e19e5c944c132ec1a610427be3dfc91edef86e1 Mon Sep 17 00:00:00 2001 From: taylorbrown75 Date: Wed, 9 Aug 2023 16:11:14 -0600 Subject: [PATCH 40/46] Add mslf iph cmod. --- ssc/CMakeLists.txt | 1 + ssc/cmod_fresnel_physical_iph.cpp | 387 +++++++++++++++--------------- ssc/sscapi.cpp | 2 + 3 files changed, 193 insertions(+), 197 deletions(-) diff --git a/ssc/CMakeLists.txt b/ssc/CMakeLists.txt index 2721261c2..b6b1405fd 100644 --- a/ssc/CMakeLists.txt +++ b/ssc/CMakeLists.txt @@ -40,6 +40,7 @@ set(SSC_SRC 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 diff --git a/ssc/cmod_fresnel_physical_iph.cpp b/ssc/cmod_fresnel_physical_iph.cpp index c0df67333..77083df37 100644 --- a/ssc/cmod_fresnel_physical_iph.cpp +++ b/ssc/cmod_fresnel_physical_iph.cpp @@ -63,7 +63,6 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { /*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, "eta_ref", "Cycle thermal efficiency at design point", "-", "", "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", "*", "", "" }, @@ -241,7 +240,7 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { /*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, "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", "", "" }, @@ -318,8 +317,8 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { 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", "*", "", "" }, + //{ 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", "*", "", "" }, @@ -345,7 +344,7 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { 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, "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", "", "", "" }, @@ -392,7 +391,7 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { - // Solar Field (from Trough) + // 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", "", "" }, @@ -418,80 +417,74 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { SSC_OUTPUT, SSC_ARRAY, "W_dot_sca_track", "Field collector tracking power", "MWe", "", "solar_field", "sim_type=1", "", "" }, { SSC_OUTPUT, SSC_ARRAY, "W_dot_field_pump", "Field htf pumping power", "MWe", "", "solar_field", "sim_type=1", "", "" }, - + { SSC_OUTPUT, SSC_ARRAY, "recirculating", "Field recirculating (bypass valve open)", "-", "", "solar_field", "sim_type=1", "", "" }, // power block - { SSC_OUTPUT, SSC_ARRAY, "eta", "PC efficiency: gross", "", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_pb", "PC input energy", "MWt", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_pc", "PC HTF mass flow rate", "kg/s", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_dot_pc_startup", "PC startup thermal power", "MWt", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "P_cycle", "PC electrical power output: gross", "MWe", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_pc_in", "PC HTF inlet temperature", "C", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "T_pc_out", "PC HTF outlet temperature", "C", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "m_dot_water_pc", "PC water consumption: makeup + cooling", "kg/s", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "q_pc_startup", "PC startup thermal energy", "MWht", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "cycle_htf_pump_power", "PC HTF pump power", "MWe", "", "powerblock", "sim_type=1", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "P_cooling_tower_tot", "Parasitic power condenser operation", "MWe", "", "powerblock", "sim_type=1", "", "" }, - - // 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", "", "" }, - - // Controller + //{ 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", "", "" }, - - // 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", "", "" }, - + + // 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", "", "" }, @@ -511,32 +504,45 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { 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, "W_dot_parasitic_tot", "System total electrical parasitic", "MWe", "", "system", "*", "", "" }, - { SSC_OUTPUT, SSC_ARRAY, "gen", "Total thermal power to heat sink with available 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", "", "" }, + + + // 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", "*", "", "" }, - //{ SSC_OUTPUT, SSC_NUMBER, "q_dot_defocus_est", "Thermal energy intentionally lost by defocusing", "MWt", "", "system", "*", "", "" }, + { SSC_OUTPUT, SSC_NUMBER, "W_dot_par_tot_haf", "Adjusted parasitic power", "kWe", "", "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_iph : public compute_module @@ -547,6 +553,7 @@ class cm_fresnel_physical_iph : public compute_module { add_var_info(_cm_vtab_fresnel_physical_iph); add_var_info(vtab_adjustment_factors); + add_var_info(vtab_technology_outputs); } void exec() @@ -554,8 +561,8 @@ class cm_fresnel_physical_iph : public compute_module // Common Parameters bool is_dispatch = as_boolean("is_dispatch"); int sim_type = as_number("sim_type"); - 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 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] @@ -609,8 +616,10 @@ class cm_fresnel_physical_iph : public compute_module 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"); + + // 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"); @@ -684,7 +693,7 @@ class cm_fresnel_physical_iph : public compute_module 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_L_rnr_pb = 0; // 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"); @@ -745,7 +754,7 @@ class cm_fresnel_physical_iph : public compute_module 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 = as_integer("Fluid"); c_heat_sink.ms_params.m_pc_fl_props = as_matrix("field_fl_props"); @@ -1008,12 +1017,13 @@ class cm_fresnel_physical_iph : public compute_module double nameplate_des; { // System Design Calcs - double eta_ref = as_double("eta_ref"); //[-] - double W_dot_cycle_des = as_double("P_ref"); //[MWe] + //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 = W_dot_cycle_des / eta_ref; //[MWt] + 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] @@ -1192,6 +1202,7 @@ class cm_fresnel_physical_iph : public compute_module // 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 @@ -1201,7 +1212,7 @@ class cm_fresnel_physical_iph : public compute_module } // Calculate Costs and assign outputs - if (csp_financial_model != 8) + if (true) { // Collect dedicated cost inputs @@ -1210,7 +1221,7 @@ class cm_fresnel_physical_iph : public compute_module 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 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"); @@ -1230,23 +1241,24 @@ class cm_fresnel_physical_iph : public compute_module double solar_field_area = c_fresnel.m_Ap_tot; double htf_system_area = c_fresnel.m_Ap_tot; // Q_tes - double fossil_backup_mwe = 0.0; // MWe - double power_plant_mwe = 0.0; // MWe - double bop_mwe = 0.0; // MWe + double heat_sink_mwt = as_double("q_pb_design"); + double fossil_backup_mwt = heat_sink_mwt; // MWe + 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 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, + double heat_sink_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, + 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_mwt, + fossil_spec_cost, 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, - 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, + heat_sink_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 @@ -1256,7 +1268,7 @@ class cm_fresnel_physical_iph : public compute_module 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("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); @@ -1307,45 +1319,12 @@ class cm_fresnel_physical_iph : public compute_module 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 - } + // Outputs size_t count; - ssc_number_t* p_W_dot_net = as_array("P_out_net", &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"); @@ -1356,39 +1335,82 @@ class cm_fresnel_physical_iph : public compute_module 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_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"); - - //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_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_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 + 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 @@ -1451,36 +1473,7 @@ class cm_fresnel_physical_iph : public compute_module 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("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); - - - - // 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); + @@ -1488,4 +1481,4 @@ class cm_fresnel_physical_iph : public compute_module } }; -DEFINE_MODULE_ENTRY(fresnel_physical_iph, "Physical Fresnel IPH applications", 1) \ No newline at end of file +DEFINE_MODULE_ENTRY(fresnel_physical_iph, "Physical Fresnel IPH applications", 1) diff --git a/ssc/sscapi.cpp b/ssc/sscapi.cpp index e067072b2..220475774 100644 --- a/ssc/sscapi.cpp +++ b/ssc/sscapi.cpp @@ -121,6 +121,7 @@ extern module_entry_info 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, @@ -223,6 +224,7 @@ static module_entry_info *module_table[] = { &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, From 24996fa78ce1d39848c93dc0fae7c5021f23bb42 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 10 Aug 2023 13:00:10 -0600 Subject: [PATCH 41/46] Move powerblock runner length to solar field --- ssc/cmod_fresnel_physical.cpp | 4 ++-- ssc/cmod_fresnel_physical_iph.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ssc/cmod_fresnel_physical.cpp b/ssc/cmod_fresnel_physical.cpp index 272fcb9b1..6e722c57b 100644 --- a/ssc/cmod_fresnel_physical.cpp +++ b/ssc/cmod_fresnel_physical.cpp @@ -98,7 +98,7 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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 @@ -157,7 +157,7 @@ static var_info _cm_vtab_fresnel_physical[] = { /*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", "" }, - /*X*/ /*Power Cycle*/{ SSC_INPUT, SSC_NUMBER, "L_rnr_pb", "Length of runner pipe in power block", "m", "", "Powerblock", "*", "", "" }, + // Steam Rankine Cycle diff --git a/ssc/cmod_fresnel_physical_iph.cpp b/ssc/cmod_fresnel_physical_iph.cpp index 77083df37..9bf7af0e1 100644 --- a/ssc/cmod_fresnel_physical_iph.cpp +++ b/ssc/cmod_fresnel_physical_iph.cpp @@ -97,7 +97,7 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { /*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 @@ -693,7 +693,7 @@ class cm_fresnel_physical_iph : public compute_module 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 = 0; // No power block line length + 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"); From 81eec24bed1947d9112a283d7d7af59b638cc807 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Thu, 10 Aug 2023 13:27:11 -0600 Subject: [PATCH 42/46] Remove fossil backup from MSLF IPH capital costs. --- ssc/cmod_fresnel_physical_iph.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/ssc/cmod_fresnel_physical_iph.cpp b/ssc/cmod_fresnel_physical_iph.cpp index 9bf7af0e1..adffc0d39 100644 --- a/ssc/cmod_fresnel_physical_iph.cpp +++ b/ssc/cmod_fresnel_physical_iph.cpp @@ -239,7 +239,6 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { /*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, "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", "", "" }, @@ -343,7 +342,6 @@ static var_info _cm_vtab_fresnel_physical_iph[] = { { 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, "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", "", "", "" }, @@ -1220,7 +1218,6 @@ class cm_fresnel_physical_iph : public compute_module 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 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"); @@ -1242,7 +1239,6 @@ class cm_fresnel_physical_iph : public compute_module double htf_system_area = c_fresnel.m_Ap_tot; // Q_tes double heat_sink_mwt = as_double("q_pb_design"); - double fossil_backup_mwt = heat_sink_mwt; // MWe double power_plant_mwt = heat_sink_mwt; // MWe double bop_mwt = heat_sink_mwt; // MWe // total_land_area // m2 @@ -1250,15 +1246,16 @@ class cm_fresnel_physical_iph : public compute_module 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, fossil_backup_cost_out, contingency_cost_out, + 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, fossil_backup_mwt, - fossil_spec_cost, 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, + 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, fossil_backup_cost_out, contingency_cost_out, + 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 @@ -1267,7 +1264,6 @@ class cm_fresnel_physical_iph : public compute_module 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("heat_sink_cost", heat_sink_cost_out); assign("bop_cost", bop_cost_out); assign("contingency_cost", contingency_cost_out); From 1cda1903870d44a2da4f0aa400cb0b4d6c53a55a Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Sun, 27 Aug 2023 17:13:17 -0600 Subject: [PATCH 43/46] On converged, resort to STARTUP rather than OFF, if dni is high but field should not be on. --- tcs/csp_solver_trough_collector_receiver.cpp | 15 ++++++++++----- tcs/csp_solver_trough_collector_receiver.h | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tcs/csp_solver_trough_collector_receiver.cpp b/tcs/csp_solver_trough_collector_receiver.cpp index 02e81add2..387c4fec0 100644 --- a/tcs/csp_solver_trough_collector_receiver.cpp +++ b/tcs/csp_solver_trough_collector_receiver.cpp @@ -1811,6 +1811,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 @@ -2095,6 +2096,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] @@ -3999,11 +4001,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] diff --git a/tcs/csp_solver_trough_collector_receiver.h b/tcs/csp_solver_trough_collector_receiver.h index 4a1d341d0..66b7259c6 100644 --- a/tcs/csp_solver_trough_collector_receiver.h +++ b/tcs/csp_solver_trough_collector_receiver.h @@ -172,6 +172,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 From c6ff796d7145da7037ddcf6cd9a6ed1030797f66 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Tue, 5 Sep 2023 11:58:44 -0600 Subject: [PATCH 44/46] Update MSPT IPH to correspond with updated receiver model. Add annual single value outputs. --- ssc/cmod_mspt_iph.cpp | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/ssc/cmod_mspt_iph.cpp b/ssc/cmod_mspt_iph.cpp index 1758a79d0..02500fec1 100644 --- a/ssc/cmod_mspt_iph.cpp +++ b/ssc/cmod_mspt_iph.cpp @@ -346,6 +346,7 @@ static var_info _cm_vtab_mspt_iph[] = { { 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", "*", "", "" }, @@ -575,7 +576,9 @@ static var_info _cm_vtab_mspt_iph[] = { // 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", "", ""}, @@ -585,8 +588,12 @@ static var_info _cm_vtab_mspt_iph[] = { { 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", "", ""}, @@ -1270,6 +1277,8 @@ class cm_mspt_iph : public compute_module 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'"); @@ -1292,7 +1301,8 @@ class cm_mspt_iph : public compute_module 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") + 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); @@ -1327,6 +1337,7 @@ class cm_mspt_iph : public compute_module 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"), @@ -1762,8 +1773,10 @@ class cm_mspt_iph : public compute_module assign("A_rec", A_rec); //[m2] double L_tower_piping = std::numeric_limits::quiet_NaN(); - receiver->get_design_geometry(L_tower_piping); + 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] @@ -2250,12 +2263,30 @@ class cm_mspt_iph : public compute_module } } + 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] From 6e804e0e1b50f46a5f893d2026ee6fb07fd4f7fb Mon Sep 17 00:00:00 2001 From: taylorbrown75 Date: Fri, 8 Sep 2023 11:02:14 -0600 Subject: [PATCH 45/46] Add receiver reflectivity losses to receiver thermal incident power. --- tcs/csp_solver_fresnel_collector_receiver.cpp | 42 +++++++++++++++---- tcs/csp_solver_fresnel_collector_receiver.h | 10 +++-- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index 7d5af14a0..b7de73383 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -310,7 +310,8 @@ void C_csp_fresnel_collector_receiver::set_output_value() mc_reported_outputs.value(E_Q_DOT_INC_SF_TOT, m_q_dot_inc_sf_tot); //[MWt] //mc_reported_outputs.value(E_Q_DOT_INC_SF_COSTH, m_dni_costh * m_Ap_tot / 1.E6); //[MWt] - mc_reported_outputs.value(E_Q_DOT_REC_INC, m_q_dot_sca_abs_summed_fullts + m_q_dot_sca_loss_summed_fullts); //[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] @@ -486,6 +487,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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); @@ -543,7 +545,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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_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]) @@ -555,6 +557,8 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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]; @@ -788,6 +792,7 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle // 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] @@ -801,12 +806,14 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle } 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 @@ -1539,6 +1546,7 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() 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] @@ -1558,6 +1566,7 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() 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] @@ -1772,6 +1781,8 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs 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 @@ -2410,7 +2421,8 @@ void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_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_xover_loss_summed_fullts = + 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 = @@ -2450,6 +2462,7 @@ void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_outputs& // 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] @@ -2471,6 +2484,7 @@ void C_csp_fresnel_collector_receiver::off(const C_csp_weatherreader::S_outputs& 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] @@ -2558,7 +2572,8 @@ void C_csp_fresnel_collector_receiver::startup(const C_csp_weatherreader::S_outp 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_xover_loss_summed_fullts = + 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 = @@ -2602,6 +2617,7 @@ void C_csp_fresnel_collector_receiver::startup(const C_csp_weatherreader::S_outp // 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] @@ -2640,11 +2656,12 @@ void C_csp_fresnel_collector_receiver::startup(const C_csp_weatherreader::S_outp 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_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_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] @@ -2848,6 +2865,7 @@ void C_csp_fresnel_collector_receiver::on(const C_csp_weatherreader::S_outputs& 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] @@ -2901,7 +2919,8 @@ void C_csp_fresnel_collector_receiver::on(const C_csp_weatherreader::S_outputs& 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_xover_loss_summed_fullts = + 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 = @@ -3596,10 +3615,11 @@ EvacReceiverModel::EvacReceiverModel(vector D_abs_in, vector D_a /// 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_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args, double& q_3reflect) { //---Variable declarations------ @@ -3713,7 +3733,9 @@ void EvacReceiverModel::Calculate_Energy_Balance(double T_1_in, double m_dot, do 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_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 @@ -3721,6 +3743,8 @@ void EvacReceiverModel::Calculate_Energy_Balance(double T_1_in, double m_dot, do //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; diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 3475d9e43..2ea605161 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -106,7 +106,7 @@ class EvacReceiverModel 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_heatloss, double& q_12conv, double& q_34tot, double& c_1ave, double& rho_1ave, std::vector& v_reguess_args, double& q_3reflect); }; @@ -256,11 +256,13 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_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 @@ -290,6 +292,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 @@ -308,6 +311,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 From 71d7e9293557cecfffe622117333b9131356b2f0 Mon Sep 17 00:00:00 2001 From: Taylor Brown Date: Mon, 11 Sep 2023 09:49:33 -0600 Subject: [PATCH 46/46] Clean code and remove unnecessary comments --- tcs/csp_solver_fresnel_collector_receiver.cpp | 238 ------------------ tcs/csp_solver_fresnel_collector_receiver.h | 28 --- 2 files changed, 266 deletions(-) diff --git a/tcs/csp_solver_fresnel_collector_receiver.cpp b/tcs/csp_solver_fresnel_collector_receiver.cpp index b7de73383..969f6ff13 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.cpp +++ b/tcs/csp_solver_fresnel_collector_receiver.cpp @@ -5,11 +5,6 @@ using namespace std; static C_csp_reported_outputs::S_output_info S_output_info[] = { - /*{C_csp_fresnel_collector_receiver::E_COSTH_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_IAM_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_ROWSHADOW_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_ENDLOSS_AVE, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_DNI_COSTH, C_csp_reported_outputs::TS_WEIGHTED_AVE},*/ {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}, @@ -37,28 +32,6 @@ static C_csp_reported_outputs::S_output_info S_output_info[] = {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}, - /*{C_csp_fresnel_collector_receiver::E_THETA_L, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_PHI_T, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_ETA_OPTICAL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - - {C_csp_fresnel_collector_receiver::E_SF_DEF, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_Q_INC_SF_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_Q_ABS_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_Q_DUMP, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_Q_LOSS_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_PIPE_HL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_Q_AVAIL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_Q_LOSS_SPEC_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_ETA_THERMAL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_E_BAL_STARTUP, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_M_DOT_AVAIL, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_M_DOT_HTF2, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_DP_TOT, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_T_SYS_C, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_T_SYS_H, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_T_LOOP_OUTLET, C_csp_reported_outputs::TS_WEIGHTED_AVE}, - {C_csp_fresnel_collector_receiver::E_Q_I, C_csp_reported_outputs::TS_WEIGHTED_AVE},*/ - csp_info_invalid }; @@ -298,17 +271,10 @@ double C_csp_fresnel_collector_receiver::field_pressure_drop(double T_db, double void C_csp_fresnel_collector_receiver::set_output_value() { - //mc_reported_outputs.value(E_THETA_AVE, m_Theta_ave * m_r2d); //[deg], convert from rad - //mc_reported_outputs.value(E_COSTH_AVE, m_CosTh_ave); //[-] - //mc_reported_outputs.value(E_IAM_AVE, m_IAM_ave); //[-] - //mc_reported_outputs.value(E_ROWSHADOW_AVE, m_RowShadow_ave); //[-] - //mc_reported_outputs.value(E_ENDLOSS_AVE, m_EndLoss_ave); //[-] - //mc_reported_outputs.value(E_DNI_COSTH, m_dni_costh); //[W/m2] 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_INC_SF_COSTH, m_dni_costh * m_Ap_tot / 1.E6); //[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 @@ -352,35 +318,6 @@ void C_csp_fresnel_collector_receiver::set_output_value() 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] - // Fresnel - //mc_reported_outputs.value(E_THETA_L, m_theta_L * r2d); - //mc_reported_outputs.value(E_PHI_T, m_phi_t * r2d); - //mc_reported_outputs.value(E_ETA_OPTICAL, m_eta_optical); - ////mc_reported_outputs.value(E_EQ_OPT_EFF, m_EqOpteff); (E_EQUIV_OPT_ETA_TOT) - - //mc_reported_outputs.value(E_SF_DEF, m_control_defocus * m_component_defocus); // SCAs_def (duplicate) - //mc_reported_outputs.value(E_Q_INC_SF_TOT, m_q_dot_inc_sf_tot); // (duplicate) - //mc_reported_outputs.value(E_Q_ABS_TOT, m_q_dot_sca_abs_summed_fullts); // (duplicate) - //mc_reported_outputs.value(E_Q_DUMP, 0); //????????? - //mc_reported_outputs.value(E_Q_LOSS_TOT, m_q_dot_sca_loss_summed_fullts); // (duplicate) - //mc_reported_outputs.value(E_PIPE_HL, m_q_dot_xover_loss_summed_fullts + - // m_q_dot_HR_cold_loss_fullts + - // m_q_dot_HR_hot_loss_fullts); // q_dot_piping_loss (duplicate) - //mc_reported_outputs.value(E_Q_AVAIL, m_q_dot_htf_to_sink_fullts); // q_dot_htf_sf_out (duplicate) - - //mc_reported_outputs.value(E_Q_LOSS_SPEC_TOT, 0); // ?????????? likely E_Q_LOSS_TOT / num collectors - //mc_reported_outputs.value(E_ETA_THERMAL, 0); // ???????????? m_eta_thermal is removed from trough - //mc_reported_outputs.value(E_E_BAL_STARTUP, 0); // ????????????? - //mc_reported_outputs.value(E_M_DOT_AVAIL, m_m_dot_htf_tot); // (duplicate) - //mc_reported_outputs.value(E_M_DOT_HTF2, m_m_dot_htf_tot / (double)m_nLoops); // (duplicate) - //mc_reported_outputs.value(E_DP_TOT, m_dP_total); // (duplicate) - - //mc_reported_outputs.value(E_T_SYS_C, m_T_sys_c_t_int_fullts - 273.15); // (duplicate) - //mc_reported_outputs.value(E_T_SYS_H, m_T_sys_h_t_int_fullts - 273.15); // (duplicate) - //mc_reported_outputs.value(E_T_LOOP_OUTLET, m_T_htf_h_rec_out_t_int_fullts - 273.15); // (duplicate) - - //mc_reported_outputs.value(E_Q_I, m_q_i); - return; } @@ -591,20 +528,6 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle (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; - - // Original Fresnel Eqn - { - ////MJW 12.14.2010 The first term should represent the difference between the previous average temperature and the new - ////average temperature. Thus, the heat addition in the first term should be divided by 2 rather than include the whole magnitude - ////of the heat addition. - ////mjw & tn 5.1.11: There was an error in the assumption about average and outlet temperature - //double T_htf_out = m_q_abs_SCAtot[i] / (m_dot_htf_loop * c_htf_i) + m_T_htf_in_t_int[i] + - // 2.0 * (m_TCS_T_htf_ave_converged[i] - m_T_htf_in_t_int[i] - m_q_abs_SCAtot[i] / (2.0 * m_dot_htf_loop * c_htf_i)) * - // exp(-2. * m_dot_htf_loop * c_htf_i * dt / (m_node * c_htf_i + m_mc_bal_sca * m_L_mod)); - ////Recalculate the average temperature for the SCA - //double T_htf_ave = (m_T_htf_in_t_int[i] + T_htf_out) / 2.0; - } - //Calculate the mass of HTF associated with this node m_node = rho_htf_i * m_A_cs[0] * m_L_mod; @@ -666,16 +589,6 @@ C_csp_fresnel_collector_receiver::E_loop_energy_balance_exit C_csp_fresnel_colle 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] } - - // Now calculate an energy balance using the timestep-average Bulk Temperature - // ** THIS IS JUST A TEST: can comment out if necessary ** - // - // REMOVED because polynomial heat loss model does not have necessary info - //E_sca[i] = (m_A_cs[0] * m_L_mod * rho_htf_i * c_htf_i + m_L_mod * m_mc_bal_sca) * (m_T_htf_out_t_end[i] - m_T_htf_out_t_end_last[i]) * 1.E-6; //[MJ] SCA basis - //E_sca_htf[i] = m_dot_htf_loop * c_htf_i * (m_T_htf_out_t_int[i] - m_T_htf_in_t_int[i]) * sim_info.ms_ts.m_step / 1.E6; - //E_sca_abs[i] = m_q_abs_SCAtot[i] * sim_info.ms_ts.m_step / 1.E6; - //E_sca_bal[i] = E_sca_abs[i] - E_sca_htf[i] - E_sca[i]; - //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) { @@ -1586,12 +1499,6 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() m_EqOpteff = std::numeric_limits::quiet_NaN(); m_m_dot_htf_tot = std::numeric_limits::quiet_NaN(); - //m_Theta_ave = std::numeric_limits::quiet_NaN(); - //m_CosTh_ave = std::numeric_limits::quiet_NaN(); - //m_IAM_ave = std::numeric_limits::quiet_NaN(); - //m_RowShadow_ave = std::numeric_limits::quiet_NaN(); - //m_EndLoss_ave = std::numeric_limits::quiet_NaN(); - //m_dni_costh = std::numeric_limits::quiet_NaN(); m_c_htf_ave = std::numeric_limits::quiet_NaN(); m_control_defocus = std::numeric_limits::quiet_NaN(); @@ -1599,13 +1506,6 @@ C_csp_fresnel_collector_receiver::C_csp_fresnel_collector_receiver() m_q_dot_inc_sf_tot = std::numeric_limits::quiet_NaN(); - /*for (int i = 0; i < 5; i++) - m_T_save[i] = std::numeric_limits::quiet_NaN();*/ - - /*mv_reguess_args.resize(3); - std::fill(mv_reguess_args.begin(), mv_reguess_args.end(), std::numeric_limits::quiet_NaN());*/ - - m_AnnulusGasMat.fill(NULL); m_AbsorberPropMat.fill(NULL); @@ -1767,9 +1667,6 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs { // Old Fresnel - /*m_TCS_T_htf_in.resize(m_nMod); - m_TCS_T_htf_out.resize(m_nMod); - m_TCS_T_htf_ave.resize(m_nMod);*/ m_q_loss.resize(m_nRecVar); m_q_abs.resize(m_nRecVar); m_DP_tube.resize(m_nMod); @@ -1787,10 +1684,6 @@ void C_csp_fresnel_collector_receiver::init(const C_csp_collector_receiver::S_cs // Trough m_q_SCA_control_df.resize(m_nMod); - - //Allocate space for transient variables - /*m_TCS_T_htf_ave_last.resize(m_nMod); - m_TCS_T_htf_ave_converged.resize(m_nMod);*/ } // Resize CSP Solver Temp Tracking Vectors @@ -2286,25 +2179,6 @@ bool C_csp_fresnel_collector_receiver::init_fieldgeom() m_v_hot = m_v_hot + v_sgs / 2.; m_v_cold = m_v_cold + v_sgs / 2.; - - // TCS Temperature Tracking - //m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = T_field_ini; //[K] - //m_TCS_T_sys_h_converged = m_TCS_T_sys_h_last = T_field_ini; //[K] - ////cc--> Note that stored(3) -> Iter is no longer used in the TRNSYS code. It is omitted here. - //for (int i = 0; i < m_nMod; i++) - //{ - // m_TCS_T_htf_ave_converged[i] = m_TCS_T_htf_ave_last[i] = T_field_ini; //[K] - //} - - /*m_TCS_T_sys_c_last = T_field_ini; - m_TCS_T_sys_h_last = T_field_ini;*/ - ////cc--> Note that stored(3) -> Iter is no longer used in the TRNSYS code. It is omitted here. - //for (int i = 0; i < nMod; i++) { - // T_htf_in0[i] = T_field_ini; - // T_htf_out0[i] = T_field_ini; - // T_htf_ave0[i] = T_field_ini; - //} - // ********************************************* // CSP Solver Temperature Tracking m_T_sys_c_t_end_converged = m_T_sys_c_t_end_last = T_field_ini; //[K] @@ -2540,7 +2414,6 @@ void C_csp_fresnel_collector_receiver::startup(const C_csp_weatherreader::S_outp loop_optical_eta(weather, sim_info); // Set mass flow rate to what I imagine might be an appropriate value - // TEMPORARY FIX by TB 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)) { @@ -3007,8 +2880,6 @@ void C_csp_fresnel_collector_receiver::steady_state(const C_csp_weatherreader::S double rho_hot = m_htfProps.dens(T_htf_out_t_int_last[m_nMod - 1], 10.e5); // [kg/m3] std::string summary; - //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); - // 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 @@ -3074,14 +2945,6 @@ void C_csp_fresnel_collector_receiver::converged() m_operating_mode = OFF; } - // TCS Temperature Tracking - //m_TCS_T_sys_c_converged = m_TCS_T_sys_c_last = m_TCS_T_sys_c; //[K] - //m_TCS_T_sys_h_converged = m_TCS_T_sys_h_last = m_TCS_T_sys_h; //[K] - //for (int i = 0; i < m_nMod; i++) - //{ - // m_TCS_T_htf_ave_converged[i] = m_TCS_T_htf_ave_last[i] = m_TCS_T_htf_ave[i]; - //} - // 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] @@ -3153,94 +3016,6 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con // 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.; - // DEBUG (old estimation) - if (false) - { - // Incidence angle - int doy = DateTime::CalculateDayOfYear(weather.m_year, weather.m_month, weather.m_day); // day of year - double time_start = ((doy - 1) * 24 + weather.m_hour + weather.m_minute / 60.) * 3600.; - double step = 3600.; - double time = time_start + step; - double time_hr = time / 3600.; // [hr] - double dt_hr = step / 3600.; // [hr] - double hour = fmod(time_hr, 24.); // [hr] - int day_of_year = (int)ceil(time_hr / 24.); // Day of the year - double B = (day_of_year - 1) * 360.0 / 365.0 * CSP::pi / 180.0; // Duffie & Beckman 1.5.3b - 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)); // Eqn of time in minutes - double Dec = 23.45 * sin(360.0 * (284.0 + day_of_year) / 365.0 * CSP::pi / 180.0) * CSP::pi / 180.0; // Declination in radians (Duffie & Beckman 1.6.1) - double SolarNoon = 12. - ((m_shift) * 180.0 / CSP::pi) / 15.0 - EOT / 60.0; // Solar Noon and time in hours - double HrA = hour - dt_hr; - double StdTime = HrA + 0.5 * dt_hr; - double SolarTime = StdTime + ((m_shift) * 180.0 / CSP::pi) / 15.0 + EOT / 60.0; - double omega = (SolarTime - 12.0) * 15.0 * CSP::pi / 180.0; // m_hour angle (arc of sun) in radians - double SolarAlt = asin(sin(Dec) * sin(m_latitude) + cos(m_latitude) * cos(Dec) * cos(omega)); - double SolarAz = (weather.m_solazi - 180.) * m_d2r; // [rad] Solar azimuth angle - double CosTh = sqrt(1.0 - pow(cos(SolarAlt - 0) - cos(0) * cos(SolarAlt) * (1.0 - cos(SolarAz - m_ColAz)), 2)); - double Theta = acos(CosTh); // [rad] - - // Incidence angle modifier (IAM) from Burkholder & Kutscher 2008 - double IamF1 = 0.000884; - double IamF2 = -0.0000537; - double IAM; - if (CosTh == 0) { - IAM = 0; - } - else { - IAM = std::min(1., (CosTh + IamF1 * Theta + IamF2 * pow(Theta, 2.)) / CosTh); - } - - // Heat loss, where temperatures are in [C] per Burkholder & Kutscher 2008 - // model coefficients for 2008 Schott PTR70 (vacuum) receiver - double A0 = 4.05; - double A1 = 0.247; - double A2 = -0.00146; - double A3 = 5.65e-06; - double A4 = 7.62e-08; - double A5 = -1.7; - double A6 = 0.0125; - double PerfFac = 1; - double T_amb = weather.m_tdry; // [C] - double W_spd = std::abs(weather.m_wspd); - double DNI = weather.m_beam; - double T_out_des = m_T_loop_out_des - 273.15; // [C] (converted from [C] to [K] in init and now back to [C]) - double T_in_des = m_T_loop_in_des - 273.15; // [C] (converted from [C] in [K] in init and now back to [C]) - double HLTerm1 = (A0 + A5 * sqrt(W_spd)) * (T_out_des - T_in_des); - double HLTerm2 = (A1 + A6 * sqrt(W_spd)) * ((pow(T_out_des, 2) - pow(T_in_des, 2)) / 2. - T_amb * (T_out_des - T_in_des)); - double HLTerm3 = ((A2 + A4 * DNI * CosTh * IAM) / 3.) * (pow(T_out_des, 3) - pow(T_in_des, 3)); - double HLTerm4 = (A3 / 4.) * (pow(T_out_des, 4) - pow(T_in_des, 4)); - double HL = (HLTerm1 + HLTerm2 + HLTerm3 + HLTerm4) / (T_out_des - T_in_des); //[W/m] - double HL_hces = std::max(HL * m_L_tot * m_nLoops * PerfFac, 0.); // [W] convert from W/m to W for entire field - - - // Piping heat loss, at average hot/cold design temperature - double T_avg_des = 0.5 * (T_out_des + T_in_des); // [C] - - double row_distance = m_L_crossover; // use crossover as row distance - //double HL_headers_old = m_nfsec * (2 * m_nhdrsec) * row_distance * m_D_hdr[0] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] - - - double HL_headers = 0; - // Sum hot headers heat loss - for (int i = 0; i < m_nhdrsec; i++) - { - HL_headers += m_L_crossover * m_D_hdr[i] * CSP::pi * m_Pipe_hl_coef * (T_avg_des - T_amb); - } - HL_headers *= 2.0; // multiply to account for cold headers - HL_headers *= m_nfsec; // account for field sections - - - - double HL_runners = 0.; - for (int i = 0; i < m_nrunsec; i++) { - HL_runners += 2. * m_L_runner[i] * CSP::pi * m_D_runner[i] * m_Pipe_hl_coef * (T_avg_des - T_amb); // [W] - } - HL_runners *= 2.0; // account for cold headers - - - double HL_total = HL_hces + HL_headers + HL_runners; - double eta_therm_approx = std::max(1. - HL_total * 1.e-6 / q_incident, 0.); - } - // New Estimate (using steady state) double q_eff = 0; { @@ -3273,11 +3048,6 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con double Q_available = m_Ap_tot * weather.m_beam * optical_eff; // W q_eff = q_thermal / Q_available; - - //double eff_old = eta_therm_approx; - double eff_new = q_eff; - - if (q_incident == 0) q_eff = 0; @@ -3290,17 +3060,9 @@ double C_csp_fresnel_collector_receiver::calculate_thermal_efficiency_approx(con } - - - return q_eff; } - - - - - double C_csp_fresnel_collector_receiver::get_collector_area() { return m_Ap_tot; diff --git a/tcs/csp_solver_fresnel_collector_receiver.h b/tcs/csp_solver_fresnel_collector_receiver.h index 2ea605161..3a9e3a6ba 100644 --- a/tcs/csp_solver_fresnel_collector_receiver.h +++ b/tcs/csp_solver_fresnel_collector_receiver.h @@ -118,12 +118,6 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver enum { - //E_THETA_AVE, //[deg] - //E_COSTH_AVE, //[-] - //E_IAM_AVE, //[-] - //E_ROWSHADOW_AVE, //[-] - //E_ENDLOSS_AVE, //[-] - //E_DNI_COSTH, //[W/m2] E_EQUIV_OPT_ETA_TOT, //[-] E_DEFOCUS, //[-] @@ -321,29 +315,7 @@ class C_csp_fresnel_collector_receiver : public C_csp_collector_receiver 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 - - - - - - // // TCS Temperature Tracking - // // Temperatures from the most recent converged() operation - //double m_TCS_T_sys_c_converged; //[K] Temperature (bulk) of cold runners & headers in previous timestep - //std::vector m_TCS_T_htf_ave_converged; //[K] Average HTF temperature in each SCA - //double m_TCS_T_sys_h_converged; //[K] Temperature (bulk) of hot runners & headers in previous timestep - //// Temperatures from the most recent timestep (in the event that a method solves multiple, shorter timesteps) - //double m_TCS_T_sys_c_last; //[K] Temperature (bulk) of cold runners & headers in previous timestep - //std::vector m_TCS_T_htf_ave_last; //[K] Average HTF temperature in each SCA - //double m_TCS_T_sys_h_last; //[K] Temperature (bulk) of hot runners & headers in previous timestep - //// Latest temperatures solved during present call to this class - //double m_TCS_T_sys_c; //[K] Temperature (bulk) of cold runners & headers - //std::vector m_TCS_T_htf_in; //[K] Inlet HTF temperature to each SCA - //std::vector m_TCS_T_htf_ave; //[K] Average HTF temperature in each SCA - //std::vector m_TCS_T_htf_out; //[K] Outlet HTF temperature to each SCA - //double m_TCS_T_sys_h; //[K] Solar field HTF outlet temperature - //// ********************************************* - //// ********************************************* // Private Methods private: