def retrieve_data_blended( email, api_key, grid=None, solar_plant=None, interconnect_to_state_abvs=None, year="2016", rate_limit=0.5, cache_dir=None, ): """Retrieves irradiance data from NSRDB and calculate the power output using the System Adviser Model (SAM). Either a Grid object needs to be passed to ``grid``, or (a data frame needs to be passed to ``solar_plant`` and a dictionary needs to be passed to ``interconnect_to_state_abvs``). :param str email: email used to`sign up <https://developer.nrel.gov/signup/>`_. :param str api_key: API key. :param powersimdata.input.grid.Grid: grid instance. :param pandas.DataFrame solar_plant: plant data frame. :param dict/pandas.Series interconnect_to_state_abvs: mapping of interconnection name to state abbreviations, used to look up average parameters by interconnect when average parameters by state are not available. :param int/str year: year. :param int/float rate_limit: minimum seconds to wait between requests to NREL :param str cache_dir: directory to cache downloaded data. If None, don't cache. :return: (*pandas.DataFrame*) -- data frame with *'Pout'*, *'plant_id'*, *'ts'* and *'ts_id'* as columns. Values are power output for a 1MW generator. """ xor_err_msg = ( "Either grid xor (solar_plant and interconnect_to_state_abvs) must be defined" ) if grid is None: if solar_plant is None or interconnect_to_state_abvs is None: raise TypeError(xor_err_msg) if not {"state_abv", "interconnect"} <= set(solar_plant.columns): raise ValueError( "solar_plant needs 'state_abv' and 'interconnect' columns") # Create mappings from other inputs zone_id_to_state_abv = { i: group["state_abv"].unique()[0] for i, group in solar_plant.groupby("zone_id") } zone_id_to_interconnect = { i: group["interconnect"].unique()[0] for i, group in solar_plant.groupby("zone_id") } else: if solar_plant is not None or interconnect_to_state_abvs is not None: raise TypeError(xor_err_msg) solar_plant = grid.plant.query("type == 'solar'").copy() # Use existing mappings found in the Grid object interconnect_to_state_abvs = grid.model_immutables.zones[ "interconnect2abv"] zone_id_to_state_abv = grid.model_immutables.zones["id2abv"] zone_id_to_interconnect = { z: grid.model_immutables.zones["abv2interconnect"][ zone_id_to_state_abv[z]] for z in solar_plant["zone_id"].unique() } real_dates = pd.date_range(start=f"{year}-01-01-00", end=f"{year}-12-31-23", freq="H") sam_dates, leap_day = generate_timestamps_without_leap_day(year) # PV tracking ratios # By state and by interconnect when EIA data do not have any solar PV in the state pv_info = get_pv_tracking_data() zone_id = solar_plant.zone_id.unique() frac = {} for zone in zone_id: state = zone_id_to_state_abv[zone] frac[zone] = get_pv_tracking_ratio_state(pv_info, [state]) if frac[zone] is None: interconnect = zone_id_to_interconnect[zone] states_in_interconnect = list( interconnect_to_state_abvs[interconnect]) frac[zone] = get_pv_tracking_ratio_state(pv_info, states_in_interconnect) # Inverter Loading Ratio ilr = 1.25 api = NrelApi(email, api_key, rate_limit) # Identify unique location coord = get_plant_id_unique_location(solar_plant) data = {} for key, plants in tqdm(coord.items(), total=len(coord)): lat, lon = key[1], key[0] solar_data = api.get_psm3_at( lat, lon, attributes="dhi,dni,wind_speed,air_temperature", year=year, leap_day=False, dates=sam_dates, cache_dir=cache_dir, ).to_dict() for i, plant_id in enumerate(plants): if i == 0: # Calculate power for the first plant at each location first_plant_id = plant_id tracking_ratios = frac[solar_plant.loc[plant_id].zone_id] power = 0 for j, axis in enumerate([0, 2, 4]): plant_pv_dict = { "system_capacity": ilr, "dc_ac_ratio": ilr, "array_type": axis, } pv_dict = {**default_pv_parameters, **plant_pv_dict} power += tracking_ratios[j] * calculate_power( solar_data, pv_dict) if leap_day is not None: power = np.insert(power, leap_day, power[leap_day - 24:leap_day]) else: # For every other plant, look up power from first plant at the location power = data[first_plant_id] data[plant_id] = power return pd.DataFrame(data, index=real_dates).sort_index(axis="columns")
def retrieve_data(solar_plant, email, api_key, year="2016", rate_limit=0.5): """Retrieves irradiance data from NSRDB and calculate the power output using the System Adviser Model (SAM). :param pandas.DataFrame solar_plant: plant data frame. :param str email: email used to`sign up <https://developer.nrel.gov/signup/>`_. :param str api_key: API key. :param str year: year. :param int/float rate_limit: minimum seconds to wait between requests to NREL :return: (*pandas.DataFrame*) -- data frame with *'Pout'*, *'plant_id'*, *'ts'* and *'ts_id'* as columns. Values are power output for a 1MW generator. """ # SAM only takes 365 days. try: leap_day = (pd.Timestamp("%s-02-29-00" % year).dayofyear - 1) * 24 is_leap_year = True dates = pd.date_range(start="%s-01-01-00" % 2015, freq="H", periods=365 * 24) dates = dates.map(lambda t: t.replace(year=int(year))) except ValueError: leap_day = None is_leap_year = False dates = pd.date_range(start="%s-01-01-00" % year, freq="H", periods=365 * 24) # Identify unique location coord = get_plant_id_unique_location(solar_plant) data = pd.DataFrame({"Pout": [], "plant_id": [], "ts": [], "ts_id": []}) # PV tracking ratios # By state and by interconnect when EIA data do not have any solar PV in # the state pv_info = get_pv_tracking_data() zone_id = solar_plant.zone_id.unique() frac = {} for i in zone_id: state = id2abv[i] frac[i] = get_pv_tracking_ratio_state(pv_info, [state]) if frac[i] is None: frac[i] = get_pv_tracking_ratio_state( pv_info, list(interconnect2abv[abv2interconnect[state]])) # Inverter Loading Ratio ilr = 1.25 api = NrelApi(email, api_key, rate_limit) for key in tqdm(coord.keys(), total=len(coord)): lat, lon = key[1], key[0] solar_data = api.get_psm3_at( lat, lon, attributes="dhi,dni,wind_speed,air_temperature", year=year, leap_day=False, dates=dates, ).to_dict() for i in coord[key]: data_site = pd.DataFrame({ "ts": pd.date_range(start="%s-01-01-00" % year, end="%s-12-31-23" % year, freq="H") }) data_site["ts_id"] = range(1, len(data_site) + 1) data_site["plant_id"] = i power = 0 for j, axis in enumerate([0, 2, 4]): pv_dict = { "system_capacity": ilr, "dc_ac_ratio": ilr, "tilt": 30, "azimuth": 180, "inv_eff": 94, "losses": 14, "array_type": axis, "gcr": 0.4, "adjust:constant": 0, } pv_dat = pssc.dict_to_ssc_table(pv_dict, "pvwattsv7") pv = PVWatts.wrap(pv_dat) pv.SolarResource.assign({"solar_resource_data": solar_data}) pv.execute() ratio = frac[solar_plant.loc[i].zone_id][j] power += ratio * np.array(pv.Outputs.gen) if is_leap_year is True: data_site["Pout"] = np.insert(power, leap_day, power[leap_day - 24:leap_day]) else: data_site["Pout"] = power data = data.append(data_site, ignore_index=True, sort=False) data["plant_id"] = data["plant_id"].astype(np.int32) data["ts_id"] = data["ts_id"].astype(np.int32) data.sort_values(by=["ts_id", "plant_id"], inplace=True) data.reset_index(inplace=True, drop=True) return data
def test_ratio_state_with_multiple_plants_and_tracking_systems(): state = ["CA"] ratio = get_pv_tracking_ratio_state(pv_info, state) assert ratio == (1.0 / 10, 6.0 / 10, 3.0 / 10)
def test_state_type(): state = "AZ" with pytest.raises(TypeError, match="state must be a list"): get_pv_tracking_ratio_state(pv_info, state)
def test_ratio_state_with_single_plant_and_unique_tracking(): state = ["UT"] ratio = get_pv_tracking_ratio_state(pv_info, state) assert ratio[0] == 0 assert ratio[1] == 0 assert ratio[2] == 1
def test_ratio_state_with_single_plant_and_multiple_tracking(): state = ["WA"] ratio = get_pv_tracking_ratio_state(pv_info, state) assert ratio[0] == 0.5 assert ratio[1] == 0.5 assert ratio[2] == 0
def test_sum_ratio_state_with_multiple_plants_and_tracking_systems(): state = ["CA"] ratio = get_pv_tracking_ratio_state(pv_info, state) assert sum(ratio) == 1
def test_sum_ratio_state_with_single_plant_and_tracking_system(): state = ["UT"] ratio = get_pv_tracking_ratio_state(pv_info, state) assert sum(ratio) == 1
def test_state_with_solar_return_3ple(): state = ["UT"] ratio = get_pv_tracking_ratio_state(pv_info, state) assert type(ratio) is tuple assert len(ratio) == 3
def test_state_without_no_solar_return_none(): state = ["MT"] assert get_pv_tracking_ratio_state(pv_info, state) is None
def test_state_exists(): state = ["Tatooine"] with pytest.raises(ValueError, match="Invalid State"): get_pv_tracking_ratio_state(pv_info, state)