def site(): return Site( name='WRMC BSRN NASA Langley Research Center', latitude=37.1048, longitude=-76.3872, elevation=3.0, timezone='Etc/GMT+5', site_id='', provider='', extra_parameters= '{"network_api_id": "LRC", "attribution": "Driemel, A., Augustine, J., Behrens, K., Colle, S., Cox, C., Cuevas-Agull\\u00f3, E., Denn, F. M., Duprat, T., Fukuda, M., Grobe, H., Haeffelin, M., Hodges, G., Hyett, N., Ijima, O., Kallis, A., Knap, W., Kustov, V., Long, C. N., Longenecker, D., Lupi, A., Maturilli, M., Mimouni, M., Ntsangwane, L., Ogihara, H., Olano, X., Olefs, M., Omori, M., Passamani, L., Pereira, E. B., Schmith\\u00fcsen, H., Schumacher, S., Sieger, R., Tamlyn, J., Vogt, R., Vuilleumier, L., Xia, X., Ohmura, A., and K\\u00f6nig-Langlo, G.: Baseline Surface Radiation Network (BSRN): structure and data description (1992\\u20132017), Earth Syst. Sci. Data, 10, 1491-1501, doi:10.5194/essd-10-1491-2018, 2018.", "network": "WRMC BSRN", "network_api_abbreviation": "", "observation_interval_length": 1}', # noqa: E501 )
def restrict_forecast_upload_window(extra_parameters, get_forecast, first_time): """ Check that the first_time falls within the window before the next initialization time of the forecast from the current time. Accounts for forecast lead_time_to_start and interval_label. Requires 'read' permission on the forecast in question. Parameters ---------- extra_parameters : str The extra_parameters string for the forecast. If '"restrict_upload": true' is not found in the string, no restriction occurs and this function returns immediately. get_forecast : func Function to get the forecast from the database. first_time : datetime-like First timestamp in the posted forecast timeseries. Raises ------ NotFoundException When the user does not have 'read' permission for the forecast or it doesn't exist. BadAPIRequest If the first_time of the timeseries is not consistent for the next initaliziation time of the forecast. """ if not _restrict_in_extra(extra_parameters): return try: fx_dict = get_forecast().copy() except (StorageAuthError, NotFoundException): raise NotFoundException( errors={'404': 'Cannot read forecast or forecast does not exist'}) # we don't care about the axis or constant values for probabilistic fx_dict['site'] = Site('name', 0, 0, 0, 'UTC') fx = Forecast.from_dict(fx_dict) next_issue_time = fx_utils.get_next_issue_time(fx, _current_utc_timestamp()) expected_start = next_issue_time + fx.lead_time_to_start if fx.interval_label == 'ending': expected_start += fx.interval_length if first_time != expected_start: raise BadAPIRequest( errors={ 'issue_time': (f'Currently only accepting forecasts issued for {next_issue_time}.' f' Expecting forecast series to start at {expected_start}.') })
def create_site(api, site): """Post a new site to the API and create it's applicable reference observations. Parameters ---------- api : io.APISession An APISession with a valid JWT for accessing the Reference Data user. site : dict Dictionary describing the site to post. This will be instantiated as a datamodel.Site object and the value of 'extra_parameters' will be serialized to json. Returns ------- datamodel.Site The created site object. """ # get a reference to network before we serialize extra_parameters network = site['extra_parameters']['network'] network_handler = NETWORKHANDLER_MAP.get(network) if network_handler is None: logger.warning(f'Unrecognized network, {network} on Site ' f'{site["name"]} Observations cannot be ' 'automatically generated.') return site.update({'extra_parameters': json.dumps(site['extra_parameters'])}) site_name = f"{network} {common.clean_name(site['name'])}" existing = common.existing_sites(api) if site_name in existing: logger.info('Site, %s, already exists', site_name) created = existing[site_name] else: site['name'] = site_name site_to_create = Site.from_dict(site) try: created = api.create_site(site_to_create) except HTTPError as e: logger.error(f"Failed to create Site {site['name']}.") logger.debug(f'HTTP Error: {e.response.text}') return False else: logger.info(f'Created Site {created.name} successfully.') network_handler.initialize_site_observations(api, created) try: network_handler.initialize_site_forecasts(api, created) except ValueError as e: logger.error('Cannot create forecasts for %s: %s', site_name, e) return created
def test_initialize_site_forecasts(mocker, test_site): mock_create_fx = mocker.patch( 'solarforecastarbiter.io.reference_observations.srml.common.' 'create_forecasts') mock_api = mocker.MagicMock() srml.initialize_site_forecasts(mock_api, test_site) assert 'ac_power' in mock_create_fx.call_args[0][2] assert 'dc_power' in mock_create_fx.call_args[0][2] regular_site_dict = test_site_dict.copy() regular_site_dict.pop('modeling_parameters') reg_site = Site.from_dict(regular_site_dict) srml.initialize_site_forecasts(mock_api, reg_site) assert 'ac_power' not in mock_create_fx.call_args[0][2] assert 'dc_power' not in mock_create_fx.call_args[0][2]
def template_fx(mock_api, mocker): mock_api.create_forecast = mocker.MagicMock(side_effect=lambda x: x) mock_api.create_probabilistic_forecast = mocker.MagicMock( side_effect=lambda x: x) site = site_objects[1].replace(latitude=32, longitude=-110) template = Forecast(name='Test Template', issue_time_of_day=dt.time(0), lead_time_to_start=pd.Timedelta('1d'), interval_length=pd.Timedelta('1h'), run_length=pd.Timedelta('24h'), interval_label='ending', interval_value_type='interval_mean', variable='ghi', site=Site('dummy', 0, 0, 0, 'MST'), extra_parameters=json.dumps({ 'is_reference_forecast': True, 'model': 'gfs_quarter_deg_hourly_to_hourly_mean' })) return mock_api, template, site
def test_decode_extra_parameters_error(site): with pytest.raises(ValueError): common.decode_extra_parameters(Site.from_dict(site))
def test_decode_extra_parameters(): metadata = Site.from_dict(site_string_dicts[0]) params = common.decode_extra_parameters(metadata) assert params['network'] == 'DOE ARM' assert params['observation_interval_length'] == 1
def test_check_network(networks, site, expected): metadata = Site.from_dict(site) assert common.check_network(networks, metadata) == expected
def test_create_observation_exists(mock_api, site_objects_param, observation_objects_param): variable = 'ghi' site = site_objects_param[0] common.create_observation(mock_api, site, variable) mock_api.create_observation.assert_not_called() long_site_name = Site.from_dict({ 'name': 'ARM site with just abouts sixty four characters in its name oops', 'latitude': 1, 'longitude': 1, 'elevation': 5, 'timezone': 'Etc/GMT+8', 'extra_parameters': ('{"network": "DOE ARM", "network_api_abbreviation": ' '"site_abbrev", "network_api_id": "thing", ' '"observation_interval_length": 1}'), }) observation_long_site_name = Observation.from_dict({ 'name': 'site_abbrev air_temperature', 'variable': 'air_temperature', 'interval_label': 'ending', 'interval_value_type':
"network_api_id": 'BMS', "network_api_abbreviation": 'abbrv'}, } ]] def expected_site(site): new_site = site.copy() network = site['extra_parameters'].get('network', '') new_site['name'] = f"{network} {site['name']}" new_site.update({'extra_parameters': json.dumps(site['extra_parameters'])}) return new_site site_string_dicts = [expected_site(site) for site in site_dicts()] site_objects = [Site.from_dict(site) for site in site_string_dicts] @pytest.fixture def site_dicts_param(): return site_string_dicts @pytest.fixture def site_objects_param(): return site_objects def site_to_obs(site): ep = json.loads(site.extra_parameters) interval_length = ep['observation_interval_length']
from solarforecastarbiter.datamodel import (Forecast, Site, ProbabilisticForecast) from solarforecastarbiter.io.fetch.nwp import DOMAIN def is_in_nwp_domain(site): """ Checks the location of the site and returns True if it is within the domain of the NWP forecasts. """ return ((DOMAIN['leftlon'] <= site.longitude <= DOMAIN['rightlon']) and (DOMAIN['bottomlat'] <= site.latitude <= DOMAIN['toplat'])) _DUMMY_SITE = Site('dummy', 0, 0, 0, 'UTC') # TODO: add poa globa, maybe relative humidity CURRENT_NWP_VARIABLES = { 'ac_power', 'ghi', 'dni', 'dhi', 'air_temperature', 'wind_speed' } # issue time of day is in local standard time and will be # adjusted to the appropriate UTC hour TEMPLATE_DETERMINISTIC_NWP_FORECASTS = [ Forecast(name='Day Ahead GFS', issue_time_of_day=dt.time(0), lead_time_to_start=pd.Timedelta('1d'), interval_length=pd.Timedelta('1h'), run_length=pd.Timedelta('24h'), interval_label='ending',
"ac_capacity": 0.02, "ac_loss_factor": 0.0, "dc_capacity": 0.02, "dc_loss_factor": 0.0, "surface_azimuth": 180.0, "surface_tilt": 15.0, "temperature_coefficient": 0.3, "tracking_type": "fixed" }, "name": "Ashland OR PV", "timezone": "Etc/GMT+8", "provider": "", "site_id": "", } test_site_object = Site.from_dict(test_site_dict) @pytest.fixture() def test_site(): return test_site_object srml_df = pd.DataFrame({ 'ghi_1': { Timestamp('2020-03-01 00:00:00-0800', tz='Etc/GMT+8'): 0.0 }, 'ghi_1_flag': { Timestamp('2020-03-01 00:00:00-0800', tz='Etc/GMT+8'): 11 }, 'dni_1': {