def recalc_financial_objective(df, scenarios, obj_col): f_cols = [col for col in df.columns if col.endswith('_financial_model')] data = dict() for idx, row in df.iterrows(): for i, scenario in enumerate(scenarios): for j, col in enumerate(f_cols): new_col = col.split('_')[0] + '_' + scenario['name'] if new_col not in data: data[new_col] = [] d = expand_financial_model(row[col]) model = Singleowner.new() #.default(defaults[tech_prefix[j]]) model.assign(d) for key, val in scenario['values'].items(): if key == 'cp_capacity_credit_percent': model.value(key, calc_capacity_credit_perc(d, val)) else: model.value(key, val) model.execute() data[new_col].append(model.value(obj_col)) for key, val in data.items(): df[key] = val return df
def __init__(self, site: SiteInfo, trough_config: dict): """ Parabolic trough concentrating solar power class based on SSC’s Parabolic trough (physical model) :param site: Power source site information :param trough_config: CSP configuration with the following keys: #. ``cycle_capacity_kw``: float, Power cycle design turbine gross output [kWe] #. ``solar_multiple``: float, Solar multiple [-] #. ``tes_hours``: float, Full load hours of thermal energy storage [hrs] """ financial_model = Singleowner.default('PhysicalTroughSingleOwner') # set-up param file paths # TODO: Site should have dispatch factors consistent across all models self.param_files = {'tech_model_params_path': 'tech_model_defaults.json', 'cf_params_path': 'construction_financing_defaults.json', 'wlim_series_path': 'wlim_series.csv'} rel_path_to_param_files = os.path.join('pySSC_daotk', 'trough_data') self.param_file_paths(rel_path_to_param_files) super().__init__("TroughPlant", 'trough_physical', site, financial_model, trough_config) self._dispatch: TroughDispatch = None
def __init__(self, site: SiteInfo, interconnect_kw): """ Class that houses the hybrid system performance and financials. Enforces interconnection and curtailment limits based on PySAM's Grid module :param site: Power source site information (SiteInfo object) :param interconnect_kw: Interconnection limit [kW] """ system_model = GridModel.default("GenericSystemSingleOwner") financial_model: Singleowner.Singleowner = Singleowner.from_existing(system_model, "GenericSystemSingleOwner") super().__init__("Grid", site, system_model, financial_model) self._system_model.GridLimits.enable_interconnection_limit = 1 self._system_model.GridLimits.grid_interconnection_limit_kwac = interconnect_kw # financial calculations set up self._financial_model.value("add_om_num_types", 1) self._dispatch: GridDispatch = None # TODO: figure out if this is the best place for these self.missed_load = [0.] self.missed_load_percentage = 0.0 self.schedule_curtailed = [0.] self.schedule_curtailed_percentage = 0.0
def default(): """Get the default PySAM object""" pv = DefaultPvwattsv5.default() obj = PySamSingleOwner.default('PVWattsSingleOwner') obj.SystemOutput.gen = pv.Outputs.ac obj.execute() return obj
def test_ReOPT(): lat = 39.7555 lon = -105.2211 # get resource and create model site = SiteInfo(flatirons_site) load = [1000 * (sin(x) + pi) for x in range(0, 8760)] urdb_label = "5ca4d1175457a39b23b3d45e" # https://openei.org/apps/IURDB/rate/view/5ca3d45ab718b30e03405898 solar_model = PVPlant(site, {'system_capacity_kw': 20000}) wind_model = WindPlant(site, { 'num_turbines': 10, "turbine_rating_kw": 2000 }) wind_model._system_model.Resource.wind_resource_filename = os.path.join( "data", "39.7555_-105.2211_windtoolkit_2012_60min_60m.srw") fin_model = so.default("GenericSystemSingleOwner") fileout = os.path.join(filepath, "REoptResultsNoExportAboveLoad.json") reopt = REopt(lat=lat, lon=lon, load_profile=load, urdb_label=urdb_label, solar_model=solar_model, wind_model=wind_model, fin_model=fin_model, interconnection_limit_kw=20000, fileout=fileout) reopt.set_rate_path(os.path.join(filepath, 'data')) reopt_site = reopt.post['Scenario']['Site'] pv = reopt_site['PV'] assert (pv['dc_ac_ratio'] == pytest.approx(1.3, 0.01)) wind = reopt_site['Wind'] assert (wind['pbi_us_dollars_per_kwh'] == pytest.approx(0.015)) results = reopt.get_reopt_results() assert (isinstance(results, dict)) print(results["outputs"]["Scenario"]["Site"]["Wind"] ['year_one_to_grid_series_kw']) if 'error' in results['outputs']['Scenario']["status"]: if 'error' in results["messages"].keys(): if 'Optimization exceeded timeout' in results["messages"]['error']: assert True else: print(results["messages"]['error']) elif 'warning' in results["messages"].keys(): print(results["messages"]['warnings']) assert True else: assert (results["outputs"]["Scenario"]["Site"]["Wind"]["size_kw"] >= 0) os.remove(fileout)
def __init__(self, site: SiteInfo, battery_config: dict, chemistry: str = 'lfpgraphite', system_voltage_volts: float = 500): """ Battery Storage class based on PySAM's BatteryStateful Model :param site: Power source site information (SiteInfo object) :param battery_config: Battery configuration with the following keys: #. ``system_capacity_kwh``: float, Battery energy capacity [kWh] #. ``system_capacity_kw``: float, Battery rated power capacity [kW] :param chemistry: Battery storage chemistry, options include: #. ``LFPGraphite``: Lithium Iron Phosphate (Lithium Ion) #. ``LMOLTO``: LMO/Lithium Titanate (Lithium Ion) #. ``LeadAcid``: Lead Acid #. ``NMCGraphite``: Nickel Manganese Cobalt Oxide (Lithium Ion) :param system_voltage_volts: Battery system voltage [VDC] """ for key in ('system_capacity_kwh', 'system_capacity_kw'): if key not in battery_config.keys(): raise ValueError system_model = BatteryModel.default(chemistry) financial_model = Singleowner.from_existing(system_model, "StandaloneBatterySingleOwner") super().__init__("Battery", site, system_model, financial_model) self.Outputs = BatteryOutputs(n_timesteps=site.n_timesteps) self.system_capacity_kw: float = battery_config['system_capacity_kw'] self.chemistry = chemistry BatteryTools.battery_model_sizing(self._system_model, battery_config['system_capacity_kw'], battery_config['system_capacity_kwh'], system_voltage_volts, module_specs=Battery.module_specs) self._system_model.ParamsPack.h = 20 self._system_model.ParamsPack.Cp = 900 self._system_model.ParamsCell.resistance = 0.001 self._system_model.ParamsCell.C_rate = battery_config['system_capacity_kw'] / battery_config['system_capacity_kwh'] # Minimum set of parameters to set to get statefulBattery to work self._system_model.value("control_mode", 0.0) self._system_model.value("input_current", 0.0) self._system_model.value("dt_hr", 1.0) self._system_model.value("minimum_SOC", 10.0) self._system_model.value("maximum_SOC", 90.0) self._system_model.value("initial_SOC", 10.0) self._dispatch = None logger.info("Initialized battery with parameters and state {}".format(self._system_model.export()))
def __init__(self, site: SiteInfo, pv_config: dict, detailed_not_simple: bool = False): """ :param pv_config: dict, with keys ('system_capacity_kw', 'layout_params') where 'layout_params' is of the SolarGridParameters type :param detailed_not_simple: Detailed model uses Pvsamv1, simple uses PVWatts """ if 'system_capacity_kw' not in pv_config.keys(): raise ValueError self._detailed_not_simple: bool = detailed_not_simple if not detailed_not_simple: system_model = Pvwatts.default("PVWattsSingleOwner") financial_model = Singleowner.from_existing(system_model, "PVWattsSingleOwner") else: system_model = Pvsam.default("FlatPlatePVSingleOwner") financial_model = Singleowner.from_existing(system_model, "FlatPlatePVSingleOwner") super().__init__("SolarPlant", site, system_model, financial_model) self._system_model.SolarResource.solar_resource_data = self.site.solar_resource.data self.dc_degradation = [0] params: Optional[PVGridParameters] = None if 'layout_params' in pv_config.keys(): params: PVGridParameters = pv_config['layout_params'] self._layout = PVLayout(site, system_model, params) self._dispatch: PvDispatch = None self.system_capacity_kw: float = pv_config['system_capacity_kw']
def __init__(self, site: SiteInfo, tower_config: dict): """ Tower concentrating solar power class based on SSC’s MSPT (molten salt power tower) model :param site: Power source site information :param tower_config: CSP configuration with the following keys: #. ``cycle_capacity_kw``: float, Power cycle design turbine gross output [kWe] #. ``solar_multiple``: float, Solar multiple [-] #. ``tes_hours``: float, Full load hours of thermal energy storage [hrs] #. ``optimize_field_before_sim``: (optional, default = True) bool, If True, SolarPilot's field and tower height optimization will before system simulation, o.w., SolarPilot will just generate field based on inputs. #. ``scale_input_params``: (optional, default = True) bool, If True, HOPP will run :py:func:`hybrid.tower_source.scale_params` before system simulation. """ financial_model = Singleowner.default('MSPTSingleOwner') # set-up param file paths self.param_files = { 'tech_model_params_path': 'tech_model_defaults.json', 'cf_params_path': 'construction_financing_defaults.json', 'wlim_series_path': 'wlim_series.csv', 'helio_positions_path': 'helio_positions.csv' } rel_path_to_param_files = os.path.join('pySSC_daotk', 'tower_data') self.param_file_paths(rel_path_to_param_files) super().__init__("TowerPlant", 'tcsmolten_salt', site, financial_model, tower_config) self.optimize_field_before_sim = True if 'optimize_field_before_sim' in tower_config: self.optimize_field_before_sim = tower_config[ 'optimize_field_before_sim'] # (optionally) adjust ssc input parameters based on tower capacity self.is_scale_params = False if 'scale_input_params' in tower_config and tower_config[ 'scale_input_params']: self.is_scale_params = True # Parameters to be scaled before receiver size optimization self.scale_params(params_names=[ 'helio_size', 'helio_parasitics', 'tank_heaters', 'tank_height' ]) self._dispatch: TowerDispatch = None
def recalc_financials(result, assumptions): cols = [col for col in result.keys() if col.endswith('_financial_model')] models = [] for col in cols: model_dict = expand_financial_model(result[col]) model = Singleowner.new() #.default(defaults[tech_prefix[j]]) model.assign(model_dict) for key, val in assumptions.items(): if key == 'cp_capacity_credit_percent': val = calc_capacity_credit_percent(model_dict, N=val) model.value(key, val) model.execute() models.append(model) return models
def test_ReOPT(): lat = 39.7555 lon = -105.2211 # get resource and create model site = SiteInfo(flatirons_site) load = [1000*(sin(x) + pi)for x in range(0, 8760)] urdb_label = "5ca4d1175457a39b23b3d45e" # https://openei.org/apps/IURDB/rate/view/5ca3d45ab718b30e03405898 solar_model = SolarPlant(site, 20000) wind_model = WindPlant(site, 20000) wind_model.system_model.Resource.wind_resource_filename = os.path.join( "data", "39.7555_-105.2211_windtoolkit_2012_60min_60m.srw") fin_model = so.default("GenericSystemSingleOwner") reopt = REopt(lat=lat, lon=lon, load_profile=load, urdb_label=urdb_label, solar_model=solar_model, wind_model=wind_model, fin_model=fin_model, interconnection_limit_kw=20000, fileout=os.path.join(filepath, "data", "REoptResultsNoExportAboveLoad.json")) reopt.set_rate_path(os.path.join(filepath, 'data')) reopt_site = reopt.post['Scenario']['Site'] pv = reopt_site['PV'] assert(pv['dc_ac_ratio'] == pytest.approx(1.2, 0.01)) wind = reopt_site['Wind'] assert(wind['pbi_us_dollars_per_kwh'] == pytest.approx(0.022)) results = reopt.get_reopt_results(force_download=True) assert(isinstance(results, dict)) print(results["outputs"]["Scenario"]["Site"]["Wind"]['year_one_to_grid_series_kw']) assert (results["outputs"]["Scenario"]["Site"]["Wind"]["size_kw"] == pytest.approx(20000, 1)) assert(results["outputs"]["Scenario"]["Site"]["Financial"]["lcc_us_dollars"] == pytest.approx(17008573.0, 1)) assert(results["outputs"]["Scenario"]["Site"]["Financial"]["lcc_bau_us_dollars"] == pytest.approx(15511546.0, 1)) assert(results["outputs"]["Scenario"]["Site"]["ElectricTariff"]["year_one_export_benefit_us_dollars"] == pytest.approx(-15158711.0, 1))
def gcr_func(x, y): # set up base a = Pvsamv1.default("FlatPlatePVSingleowner") a.SolarResource.solar_resource_file = "../sam/deploy/solar_resource/tucson_az_32.116521_-110.933042_psmv3_60_tmy.csv" b = Singleowner.default("FlatPlatePVSingleowner") # set up shading a.Shading.subarray1_shade_mode = 1 a.Layout.subarray1_nmodx = 12 a.Layout.subarray1_nmody = 2 a.SystemDesign.subarray1_gcr = float(x) land_area = a.CECPerformanceModelWithModuleDatabase.cec_area * ( a.SystemDesign.subarray1_nstrings * a.SystemDesign.subarray1_modules_per_string) / x * 0.0002471 a.execute() # total_installed_cost = total_direct_cost + permitting_total + engr_total + grid_total + landprep_total + sales_tax_total + land_total b.SystemCosts.total_installed_cost += y * land_area * 1000 b.SystemOutput.gen = a.Outputs.gen b.execute() return b.Outputs.analysis_period_irr
Most recently tested against PySAM 2.2.3 @author: frohro """ import json import PySAM.GenericSystem as GenericSystem import PySAM.Grid as Grid import PySAM.Singleowner as Singleowner import PySAM.PySSC as pssc ssc = pssc.PySSC() with open("Examples/100mW_Generic.json") as f: dic = json.load(f) gs_dat = pssc.dict_to_ssc_table(dic, "generic_system") grid_dat = pssc.dict_to_ssc_table(dic, "grid") so_dat = pssc.dict_to_ssc_table(dic, "singleowner") gs = GenericSystem.wrap(gs_dat) grid = Grid.from_existing(gs) grid.assign(Grid.wrap(grid_dat).export()) # to create GenericSystem and Singleowner combined simulation, sharing the same data so = Singleowner.from_existing(gs) so.assign(Singleowner.wrap(so_dat).export()) gs.execute() grid.execute() so.execute() print('Made it past execute.') print(gs.Outputs.export()) # as dictionary
import PySAM.ResourceTools as tools import PySAM.Windpower as wp import PySAM.Singleowner as so # --- Initialize Wind Fetcher --- wtkfetcher = tools.FetchResourceFiles( tech='wind', workers=1, #thread workers if fetching multiple files nrel_api_key=<NREL_API_KEY>, nrel_api_email=<NREL_API_EMAIL>) # --- Pass a list of (lon, lat) tuples or Shapely points to fetch the nearest resource data --- lon_lats = [(-105.1800775, 39.7383155)] # golden CO wtkfetcher.fetch(lon_lats) # --- Get resource data file path --- wtk_path_dict = wtkfetcher.resource_file_paths_dict wtk_fp = wtk_path_dict[lon_lats[0]] # --- Initialize Generator --- generator = wp.default('WindPowerSingleOwner') generator.Resource.assign({'wind_resource_model_choice': 0}) generator.Resource.assign({'wind_resource_filename': wtk_fp}) #pass path to resource file # --- Initialize Financial Model --- financial = so.from_existing(generator, "WindPowerSingleOwner") # --- Execute Models --- generator.execute() financial.execute()
def __init__( self, site: SiteInfo, farm_config: dict, rating_range_kw: tuple = (1000, 3000), ): """ Set up a WindPlant :param farm_config: dict, with keys ('num_turbines', 'turbine_rating_kw', 'rotor_diameter', 'hub_height', 'layout_mode', 'layout_params') where layout_mode can be selected from the following: - 'boundarygrid': regular grid with boundary turbines, requires WindBoundaryGridParameters as 'params' - 'grid': regular grid with dx, dy distance, 0 angle; does not require 'params' :param rating_range_kw: allowable kw range of turbines, default is 1000 - 3000 kW """ self._rating_range_kw = rating_range_kw if 'model_name' in farm_config.keys(): if farm_config['model_name'] == 'floris': print('FLORIS is the system model...') system_model = Floris(farm_config, site, timestep=farm_config['timestep']) financial_model = Singleowner.default("WindPowerSingleOwner") else: raise NotImplementedError else: system_model = Windpower.default("WindPowerSingleOwner") financial_model = Singleowner.from_existing( system_model, "WindPowerSingleOwner") super().__init__("WindPlant", site, system_model, financial_model) self._system_model.value("wind_resource_data", self.site.wind_resource.data) if 'layout_mode' not in farm_config.keys(): layout_mode = 'grid' else: layout_mode = farm_config['layout_mode'] params: Optional[WindBoundaryGridParameters] = None if layout_mode == 'boundarygrid': if 'layout_params' not in farm_config.keys(): raise ValueError( "Parameters of WindBoundaryGridParameters required for boundarygrid layout mode" ) else: params: WindBoundaryGridParameters = farm_config[ 'layout_params'] self._layout = WindLayout(site, system_model, layout_mode, params) self._dispatch: WindDispatch = None if 'turbine_rating_kw' not in farm_config.keys(): raise ValueError("Turbine rating required for WindPlant") if 'num_turbines' not in farm_config.keys(): raise ValueError("Num Turbines required for WindPlant") self.turb_rating = farm_config['turbine_rating_kw'] self.num_turbines = farm_config['num_turbines'] if 'hub_height' in farm_config.keys(): self._system_model.Turbine.wind_turbine_hub_ht = farm_config[ 'hub_height'] if 'rotor_diameter' in farm_config.keys(): self.rotor_diameter = farm_config['rotor_diameter']
def run_reopt(site, scenario, load, interconnection_limit_kw, critical_load_factor, useful_life, battery_can_grid_charge, storage_used, run_reopt_flag): # kw_continuous = forced_system_size # 5 MW continuous load - equivalent to 909kg H2 per hr at 55 kWh/kg electrical intensity urdb_label = "5ca4d1175457a39b23b3d45e" # https://openei.org/apps/IURDB/rate/view/5ca3d45ab718b30e03405898 pv_config = {'system_capacity_kw': 20000} solar_model = PVPlant(site, pv_config) # ('num_turbines', 'turbine_rating_kw', 'rotor_diameter', 'hub_height', 'layout_mode', 'layout_params') wind_config = {'num_turbines': np.floor(scenario['Wind Size MW'] / scenario['Turbine Rating']), 'rotor_dimeter': scenario['Rotor Diameter'], 'hub_height': scenario['Tower Height'], 'turbine_rating_kw': scenario['Turbine Rating']} wind_model = WindPlant(site, wind_config) fin_model = so.default("GenericSystemSingleOwner") filepath = os.path.dirname(os.path.abspath(__file__)) fileout = 'reopt_result_test_intergration.json' # site = SiteInfo(sample_site, hub_height=tower_height) count = 1 reopt = REopt(lat=scenario['Lat'], lon=scenario['Long'], load_profile=load, urdb_label=urdb_label, solar_model=solar_model, wind_model=wind_model, fin_model=fin_model, interconnection_limit_kw=interconnection_limit_kw, off_grid=True, fileout=fileout) reopt.set_rate_path(os.path.join(filepath, '../data')) reopt.post['Scenario']['Site']['Wind']['installed_cost_us_dollars_per_kw'] = scenario['Wind Cost KW'] # ATB reopt.post['Scenario']['Site']['PV']['installed_cost_us_dollars_per_kw'] = scenario['Solar Cost KW'] reopt.post['Scenario']['Site']['Storage'] = {'min_kw': 0.0, 'max_kw': 0.99e9, 'min_kwh': 0.0, 'max_kwh': 0.99e9, 'internal_efficiency_pct': 0.975, 'inverter_efficiency_pct': 0.96, 'rectifier_efficiency_pct': 0.96, 'soc_min_pct': 0.2, 'soc_init_pct': 0.5, 'canGridCharge': battery_can_grid_charge, 'installed_cost_us_dollars_per_kw': scenario['Storage Cost KW'], 'installed_cost_us_dollars_per_kwh': scenario['Storage Cost KWh'], 'replace_cost_us_dollars_per_kw': scenario['Storage Cost KW'], 'replace_cost_us_dollars_per_kwh': scenario['Storage Cost KWh'], 'inverter_replacement_year': 10, 'battery_replacement_year': 10, 'macrs_option_years': 7, 'macrs_bonus_pct': 1.0, 'macrs_itc_reduction': 0.5, 'total_itc_pct': 0.0, 'total_rebate_us_dollars_per_kw': 0, 'total_rebate_us_dollars_per_kwh': 0} reopt.post['Scenario']['Site']['Financial']['analysis_years'] = useful_life if not storage_used: reopt.post['Scenario']['Site']['Storage']['max_kw'] = 0 if scenario['PTC Available']: reopt.post['Scenario']['Site']['Wind']['pbi_us_dollars_per_kwh'] = 0.022 else: reopt.post['Scenario']['Site']['Wind']['pbi_us_dollars_per_kwh'] = 0.0 if scenario['ITC Available']: reopt.post['Scenario']['Site']['PV']['federal_itc_pct'] = 0.26 else: reopt.post['Scenario']['Site']['PV']['federal_itc_pct'] = 0.0 # reopt.post['Scenario']['Site']['LoadProfile']['doe_reference_name'] = "FlatLoad" # reopt.post['Scenario']['Site']['LoadProfile']['annual_kwh'] = load #8760 * kw_continuous reopt.post['Scenario']['Site']['LoadProfile']['loads_kw'] = load reopt.post['Scenario']['Site']['LoadProfile']['critical_load_pct'] = critical_load_factor off_grid = False reopt.post['Scenario']['optimality_tolerance_techs'] = 0.05 if off_grid == True: # reopt.post['Scenario']['Site'].pop('Wind') # reopt.post['Scenario']['Site']['Wind']['min_kw'] = 10000 dictforstuff = {"off_grid_flag": True} reopt.post['Scenario'].update(dictforstuff) reopt.post['Scenario']['optimality_tolerance_techs'] = 0.05 reopt.post['Scenario']["timeout_seconds"] = 3600 # reopt.post['Scenario']['Site']['LoadProfile'].pop('annual kwh') reopt.post['Scenario']['Site'].pop('ElectricTariff') reopt.post['Scenario']['Site']['LoadProfile']['critical_load_pct'] = 1.0 f = open('massproducer_offgrid (1).json') data_for_post = json.load(f) reopt.post['Scenario']['Site']['Financial'] = data_for_post['Scenario']['Site']['Financial'] else: reopt.post['Scenario']['Site']['ElectricTariff']['wholesale_rate_us_dollars_per_kwh'] = 0.01 reopt.post['Scenario']['Site']['ElectricTariff']['wholesale_rate_above_site_load_us_dollars_per_kwh'] = 0.01 reopt.post['Scenario']['Site']['LoadProfile']['outage_start_hour'] = 10 reopt.post['Scenario']['Site']['LoadProfile']['outage_end_hour'] = 11 from pathlib import Path post_path = 'results/reopt_precomputes/reopt_post' post_path_abs = Path(__file__).parent / post_path if not os.path.exists(post_path_abs.parent): os.mkdir(post_path_abs.parent) with open(post_path_abs, 'w') as outfile: json.dump(reopt.post, outfile) # mass_producer_dict = { # "mass_units": "kg", # "time_units": "hr", # "min_mass_per_time": 10.0, # "max_mass_per_time": 10.0, # "electric_consumed_to_mass_produced_ratio_kwh_per_mass": 71.7, # "thermal_consumed_to_mass_produced_ratio_kwh_per_mass": 0.0, # "feedstock_consumed_to_mass_produced_ratio": 0.0, # "installed_cost_us_dollars_per_mass_per_time": 10.0, # "om_cost_us_dollars_per_mass_per_time": 1.5, # "om_cost_us_dollars_per_mass": 0.0, # "mass_value_us_dollars_per_mass": 5.0, # "feedstock_cost_us_dollars_per_mass": 0.0, # "macrs_option_years": 0, # "macrs_bonus_pct": 0 # } # reopt.post['Scenario']['Site']['MassProducer'] = mass_producer_dict if run_reopt_flag: #NEW METHOD load_dotenv() result = reopt.get_reopt_results() #BASIC INITIAL TEST FOR NEW METHOD # result = post_and_poll.get_api_results(data_for_post, NREL_API_KEY, 'https://offgrid-electrolyzer-reopt-dev-api.its.nrel.gov/v1', # 'reopt_result_test_intergration.json') # f = open('massproducer_offgrid (1).json') # data_for_post = json.load(f) #OLD METHOD # result = reopt.get_reopt_results(force_download=True) pickle.dump(result, open("results/reopt_precomputes/results_{}_{}_{}.p".format( scenario['Site Name'], scenario['Scenario Name'], critical_load_factor), "wb")) else: print("Not running reopt. Loading Dummy data") precompute_path = 'results/reopt_precomputes/' precompute_path_abs = Path(__file__).parent / precompute_path result = pickle.load( open(os.path.join(precompute_path_abs, "results_ATB_moderate_2020_IOWA_0.9.p"), "rb")) if result['outputs']['Scenario']['Site']['PV']['size_kw']: solar_size_mw = result['outputs']['Scenario']['Site']['PV']['size_kw'] / 1000 if result['outputs']['Scenario']['Site']['Wind']['size_kw']: wind_size_mw = result['outputs']['Scenario']['Site']['Wind']['size_kw'] / 1000 if result['outputs']['Scenario']['Site']['Storage']['size_kw']: storage_size_mw = result['outputs']['Scenario']['Site']['Storage']['size_kw'] / 1000 storage_size_mwh = result['outputs']['Scenario']['Site']['Storage']['size_kwh'] / 1000 storage_hours = storage_size_mwh / storage_size_mw reopt_site_result = result['outputs']['Scenario']['Site'] generated_date = pd.date_range(start='1/1/2018 00:00:00', end='12/31/2018 23:00:00', periods=8760) if reopt_site_result['Wind']['size_kw'] == 0: reopt_site_result['Wind']['year_one_power_production_series_kw'] = np.zeros(8760) reopt_site_result['Wind']['year_one_to_grid_series_kw'] = np.zeros(8760) reopt_site_result['Wind']['year_one_to_load_series_kw'] = np.zeros(8760) reopt_site_result['Wind']['year_one_to_battery_series_kw'] = np.zeros(8760) reopt_site_result['Wind']['year_one_curtailed_production_series_kw'] = np.zeros(8760) wind_size_mw = 0 if reopt_site_result['PV']['size_kw'] == 0: reopt_site_result['PV']['year_one_power_production_series_kw'] = np.zeros(8760) reopt_site_result['PV']['year_one_to_grid_series_kw'] = np.zeros(8760) reopt_site_result['PV']['year_one_to_load_series_kw'] = np.zeros(8760) reopt_site_result['PV']['year_one_to_battery_series_kw'] = np.zeros(8760) reopt_site_result['PV']['year_one_curtailed_production_series_kw'] = np.zeros(8760) solar_size_mw = 0 if reopt_site_result['Storage']['size_kw'] == 0: reopt_site_result['Storage']['year_one_soc_series_pct'] = np.zeros(8760) reopt_site_result['Storage']['year_one_to_massproducer_series_kw'] = np.zeros(8760) storage_size_mw = 0 storage_size_mwh = 0 storage_hours = 0 combined_pv_wind_power_production = [x + y for x, y in zip(reopt_site_result['PV']['year_one_power_production_series_kw'] , reopt_site_result['Wind']['year_one_power_production_series_kw'])] combined_pv_wind_storage_power_production = [x + y for x, y in zip(combined_pv_wind_power_production, reopt_site_result['Storage'][ 'year_one_to_load_series_kw'])] energy_shortfall = [y - x for x, y in zip(combined_pv_wind_storage_power_production, load)] energy_shortfall = [x if x > 0 else 0 for x in energy_shortfall] combined_pv_wind_curtailment = [x + y for x, y in zip(reopt_site_result['PV']['year_one_curtailed_production_series_kw'] , reopt_site_result['Wind']['year_one_curtailed_production_series_kw'])] reopt_result_dict = {'Date': generated_date, 'pv_power_production': reopt_site_result['PV'] ['year_one_power_production_series_kw'], 'pv_power_to_grid': reopt_site_result['PV'] ['year_one_to_grid_series_kw'], 'pv_power_to_load': reopt_site_result['PV']['year_one_to_load_series_kw'], 'pv_power_to_battery': reopt_site_result['PV']['year_one_to_battery_series_kw'], 'pv_power_curtailed': reopt_site_result['PV']['year_one_curtailed_production_series_kw'], 'wind_power_production': reopt_site_result['Wind'] ['year_one_power_production_series_kw'], 'wind_power_to_grid': reopt_site_result['Wind'] ['year_one_to_grid_series_kw'], 'wind_power_to_load': reopt_site_result['Wind']['year_one_to_load_series_kw'], 'wind_power_to_battery': reopt_site_result['Wind']['year_one_to_battery_series_kw'], 'wind_power_curtailed': reopt_site_result['Wind']['year_one_curtailed_production_series_kw'], 'combined_pv_wind_power_production': combined_pv_wind_power_production, 'combined_pv_wind_storage_power_production': combined_pv_wind_storage_power_production, 'storage_power_to_load': reopt_site_result['Storage']['year_one_to_load_series_kw'], 'storage_power_to_grid': reopt_site_result['Storage']['year_one_to_grid_series_kw'], 'battery_soc_pct': reopt_site_result['Storage']['year_one_soc_series_pct'], 'energy_shortfall': energy_shortfall, 'combined_pv_wind_curtailment': combined_pv_wind_curtailment } REoptResultsDF = pd.DataFrame(reopt_result_dict) return wind_size_mw, solar_size_mw, storage_size_mw, storage_size_mwh, storage_hours, result, REoptResultsDF
# --- List of (lon, lat) tuples or Shapely points --- lon_lats = [(lon, lat)] wtkfetcher.fetch(lon_lats) # --- Get resource data file path --- wtk_path_dict = wtkfetcher.resource_file_paths_dict wtk_fp = wtk_path_dict[lon_lats[0]] # --- Initialize generator --- if wtk_fp is not None: generator = wp.default('WindPowerSingleOwner') generator.Resource.assign({'wind_resource_model_choice': 0}) generator.Resource.assign({'wind_resource_filename': wtk_fp}) # --- Initialize financial model --- financial = so.from_existing(generator, 'WindPowerSingleOwner') print('Wind Power - Single Owner Results') generator.execute() print('capacity factor = {:.3f}'.format(generator.Outputs.capacity_factor)) financial.execute() print('npv = ${:,.2f}'.format( financial.Outputs.project_return_aftertax_npv)) else: print('Wind resource file does not exist. Skipping wind model simulation.') # --- Solar Example --- # --- Initialize Solar Resource Fetcher with minimum parameters --- # See function documentation for full parameter list