def test_capacity_credit(site): site = SiteInfo(data=flatirons_site, solar_resource_file=solar_resource_file, wind_resource_file=wind_resource_file, capacity_hours=capacity_credit_hours) wind_pv_battery = { key: technologies[key] for key in ('pv', 'wind', 'battery') } hybrid_plant = HybridSimulation(wind_pv_battery, site, interconnect_kw=interconnection_size_kw) hybrid_plant.battery.dispatch.lifecycle_cost_per_kWh_cycle = 0.01 hybrid_plant.ppa_price = (0.03, ) hybrid_plant.pv.dc_degradation = [0] * 25 # Backup values for resetting before tests gen_max_feasible_orig = hybrid_plant.battery.gen_max_feasible capacity_hours_orig = hybrid_plant.site.capacity_hours interconnect_kw_orig = hybrid_plant.interconnect_kw def reinstate_orig_values(): hybrid_plant.battery.gen_max_feasible = gen_max_feasible_orig hybrid_plant.site.capacity_hours = capacity_hours_orig hybrid_plant.interconnect_kw = interconnect_kw_orig # Test when 0 gen_max_feasible reinstate_orig_values() hybrid_plant.battery.gen_max_feasible = [0] * 8760 capacity_credit_battery = hybrid_plant.battery.calc_capacity_credit_percent( hybrid_plant.interconnect_kw) assert capacity_credit_battery == approx(0, rel=0.05) # Test when representative gen_max_feasible reinstate_orig_values() hybrid_plant.battery.gen_max_feasible = [2500] * 8760 capacity_credit_battery = hybrid_plant.battery.calc_capacity_credit_percent( hybrid_plant.interconnect_kw) assert capacity_credit_battery == approx(50, rel=0.05) # Test when no capacity hours reinstate_orig_values() hybrid_plant.battery.gen_max_feasible = [2500] * 8760 hybrid_plant.site.capacity_hours = [False] * 8760 capacity_credit_battery = hybrid_plant.battery.calc_capacity_credit_percent( hybrid_plant.interconnect_kw) assert capacity_credit_battery == approx(0, rel=0.05) # Test when no interconnect capacity reinstate_orig_values() hybrid_plant.battery.gen_max_feasible = [2500] * 8760 hybrid_plant.interconnect_kw = 0 capacity_credit_battery = hybrid_plant.battery.calc_capacity_credit_percent( hybrid_plant.interconnect_kw) assert capacity_credit_battery == approx(0, rel=0.05) # Test integration with system simulation reinstate_orig_values() cap_payment_mw = 100000 hybrid_plant.assign({"cp_capacity_payment_amount": [cap_payment_mw]}) hybrid_plant.simulate() total_gen_max_feasible = np.array(hybrid_plant.pv.gen_max_feasible) \ + np.array(hybrid_plant.wind.gen_max_feasible) \ + np.array(hybrid_plant.battery.gen_max_feasible) assert sum(hybrid_plant.grid.gen_max_feasible) == approx(sum(np.minimum(hybrid_plant.grid.interconnect_kw * hybrid_plant.site.interval / 60, \ total_gen_max_feasible)), rel=0.01) total_nominal_capacity = hybrid_plant.pv.calc_nominal_capacity(hybrid_plant.interconnect_kw) \ + hybrid_plant.wind.calc_nominal_capacity(hybrid_plant.interconnect_kw) \ + hybrid_plant.battery.calc_nominal_capacity(hybrid_plant.interconnect_kw) assert total_nominal_capacity == approx(18845.8, rel=0.01) assert total_nominal_capacity == approx( hybrid_plant.grid.hybrid_nominal_capacity, rel=0.01) capcred = hybrid_plant.capacity_credit_percent assert capcred['pv'] == approx(8.03, rel=0.05) assert capcred['wind'] == approx(33.25, rel=0.10) assert capcred['battery'] == approx(58.95, rel=0.05) assert capcred['hybrid'] == approx(43.88, rel=0.05) cp_pay = hybrid_plant.capacity_payments np_cap = hybrid_plant.system_nameplate_mw # This is not the same as nominal capacity... assert cp_pay['pv'][1] / (np_cap['pv']) / (capcred['pv'] / 100) == approx( cap_payment_mw, 0.05) assert cp_pay['wind'][1] / (np_cap['wind']) / (capcred['wind'] / 100) == approx( cap_payment_mw, 0.05) assert cp_pay['battery'][1] / (np_cap['battery']) / (capcred['battery'] / 100) == approx( cap_payment_mw, 0.05) assert cp_pay['hybrid'][1] / (np_cap['hybrid']) / (capcred['hybrid'] / 100) == approx( cap_payment_mw, 0.05) aeps = hybrid_plant.annual_energies assert aeps.pv == approx(9882421, rel=0.05) assert aeps.wind == approx(33637983, rel=0.05) assert aeps.battery == approx(-31287, rel=0.05) assert aeps.hybrid == approx(43489117, rel=0.05) npvs = hybrid_plant.net_present_values assert npvs.pv == approx(-565098, rel=5e-2) assert npvs.wind == approx(-1992106, rel=5e-2) assert npvs.battery == approx(-4773045, rel=5e-2) assert npvs.hybrid == approx(-5849767, rel=5e-2) taxes = hybrid_plant.federal_taxes assert taxes.pv[1] == approx(86826, rel=5e-2) assert taxes.wind[1] == approx(348124, rel=5e-2) assert taxes.battery[1] == approx(239607, rel=5e-2) assert taxes.hybrid[1] == approx(633523, rel=5e-2) apv = hybrid_plant.energy_purchases_values assert apv.pv[1] == approx(0, rel=5e-2) assert apv.wind[1] == approx(0, rel=5e-2) assert apv.battery[1] == approx(40158, rel=5e-2) assert apv.hybrid[1] == approx(2980, rel=5e-2) debt = hybrid_plant.debt_payment assert debt.pv[1] == approx(0, rel=5e-2) assert debt.wind[1] == approx(0, rel=5e-2) assert debt.battery[1] == approx(0, rel=5e-2) assert debt.hybrid[1] == approx(0, rel=5e-2) esv = hybrid_plant.energy_sales_values assert esv.pv[1] == approx(353105, rel=5e-2) assert esv.wind[1] == approx(956067, rel=5e-2) assert esv.battery[1] == approx(80449, rel=5e-2) assert esv.hybrid[1] == approx(1352445, rel=5e-2) depr = hybrid_plant.federal_depreciation_totals assert depr.pv[1] == approx(762811, rel=5e-2) assert depr.wind[1] == approx(2651114, rel=5e-2) assert depr.battery[1] == approx(1486921, rel=5e-2) assert depr.hybrid[1] == approx(4900847, rel=5e-2) insr = hybrid_plant.insurance_expenses assert insr.pv[0] == approx(0, rel=5e-2) assert insr.wind[0] == approx(0, rel=5e-2) assert insr.battery[0] == approx(0, rel=5e-2) assert insr.hybrid[0] == approx(0, rel=5e-2) om = hybrid_plant.om_total_expenses assert om.pv[1] == approx(74993, rel=5e-2) assert om.wind[1] == approx(420000, rel=5e-2) assert om.battery[1] == approx(75000, rel=5e-2) assert om.hybrid[1] == approx(569993, rel=5e-2) rev = hybrid_plant.total_revenues assert rev.pv[1] == approx(393226, rel=5e-2) assert rev.wind[1] == approx(1288603, rel=5e-2) assert rev.battery[1] == approx(375215, rel=5e-2) assert rev.hybrid[1] == approx(2229976, rel=5e-2) tc = hybrid_plant.tax_incentives assert tc.pv[1] == approx(1123104, rel=5e-2) assert tc.wind[1] == approx(504569, rel=5e-2) assert tc.battery[1] == approx(0, rel=5e-2) assert tc.hybrid[1] == approx(1646170, rel=5e-2)
def init_hybrid_plant(techs_in_sim: list, is_test: bool = False, ud_techs: dict = {}): """ Initialize hybrid simulation object using specific project inputs :param techs_in_sim: List of technologies to include in the simulation :param is_test: if True, runs dispatch for the first and last 5 days of the year and turns off tower and receiver optimization :param ud_techs: Dictionary containing technology initialization parameters required by HybridSimulation :return: HybridSimulation as defined for this problem """ schedule_scale = 100 # MWe grid_interconnect_mw = 100 # MWe example_root = get_example_path_root() # Set plant location site_data = { "lat": 34.8653, "lon": -116.7830, "elev": 561, "tz": 1, "no_wind": True } solar_file = example_root + "02_weather_data/daggett_ca_34.865371_-116.783023_psmv3_60_tmy.csv" prices_file = example_root + "03_cost_load_price_data/constant_norm_prices.csv" desired_schedule_file = example_root + "03_cost_load_price_data/desired_schedule_normalized.csv" # Reading in desired schedule with open(desired_schedule_file) as f: csvreader = csv.reader(f) desired_schedule = [] for row in csvreader: desired_schedule.append(float(row[0]) * schedule_scale) # If normalized pricing is used, then PPA price must be adjusted after HybridSimulation is initialized site = SiteInfo(site_data, solar_resource_file=solar_file, grid_resource_file=prices_file, desired_schedule=desired_schedule) # Load in system costs with open(example_root + "03_cost_load_price_data/system_costs_SAM.json") as f: cost_info = json.load(f) # Initializing technologies if ud_techs: technologies = ud_techs else: technologies = { 'tower': { 'cycle_capacity_kw': 200 * 1000, #100 'solar_multiple': 4.0, #2.0 'tes_hours': 20.0, #14 'optimize_field_before_sim': not is_test, 'scale_input_params': True, }, 'trough': { 'cycle_capacity_kw': 200 * 1000, 'solar_multiple': 6.0, 'tes_hours': 28.0 }, 'pv': { 'system_capacity_kw': 120 * 1000 }, 'battery': { 'system_capacity_kwh': 200 * 1000, 'system_capacity_kw': 100 * 1000 }, 'grid': grid_interconnect_mw * 1000 } # Create hybrid simulation class based on the technologies needed in the simulation sim_techs = {key: technologies[key] for key in techs_in_sim} sim_techs['grid'] = technologies['grid'] hybrid_plant = HybridSimulation(sim_techs, site, interconnect_kw=technologies['grid'], dispatch_options={ 'is_test_start_year': is_test, 'is_test_end_year': is_test, 'solver': 'cbc', 'grid_charging': False, 'pv_charging_only': True }, cost_info=cost_info['cost_info']) csp_dispatch_obj_costs = { 'cost_per_field_generation': 0.5, 'cost_per_field_start_rel': 0.0, 'cost_per_cycle_generation': 2.0, 'cost_per_cycle_start_rel': 0.0, 'cost_per_change_thermal_input': 0.5 } # Set CSP costs if hybrid_plant.tower: hybrid_plant.tower.ssc.set(cost_info['tower_costs']) hybrid_plant.tower.dispatch.objective_cost_terms = csp_dispatch_obj_costs if hybrid_plant.trough: hybrid_plant.trough.ssc.set(cost_info['trough_costs']) hybrid_plant.trough.dispatch.objective_cost_terms = csp_dispatch_obj_costs # Set O&M costs for all technologies for tech in ['tower', 'trough', 'pv', 'battery']: if not tech in techs_in_sim: cost_info["SystemCosts"].pop(tech) hybrid_plant.assign(cost_info["SystemCosts"]) # Set financial parameters for singleowner model with open(example_root + '03_cost_load_price_data/financial_parameters_SAM.json') as f: fin_info = json.load(f) hybrid_plant.assign(fin_info["FinancialParameters"]) hybrid_plant.assign(fin_info["TaxCreditIncentives"]) hybrid_plant.assign(fin_info["Revenue"]) hybrid_plant.assign(fin_info["Depreciation"]) hybrid_plant.assign(fin_info["PaymentIncentives"]) # Set specific technology assumptions here if hybrid_plant.pv: hybrid_plant.pv.dc_degradation = [0.5] * 25 hybrid_plant.pv.value('array_type', 2) # 1-axis tracking hybrid_plant.pv.value('tilt', 0) # Tilt for 1-axis # This is required if normalized prices are provided hybrid_plant.ppa_price = (0.10, ) # $/kWh return hybrid_plant