From d7deb80cdc5d1b63de5b2865a0c5cf24d4655fc1 Mon Sep 17 00:00:00 2001 From: AdamRJensen Date: Mon, 22 Feb 2021 22:14:15 +0100 Subject: [PATCH 01/31] Add cams.get_cams_radiation function --- docs/sphinx/source/api.rst | 1 + docs/sphinx/source/whatsnew/v0.9.0.rst | 3 + pvlib/iotools/__init__.py | 1 + pvlib/iotools/cams.py | 207 +++++++++++++++++++++++++ 4 files changed, 212 insertions(+) create mode 100644 pvlib/iotools/cams.py diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 8805d199a4..31204b6f0d 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -484,6 +484,7 @@ relevant to solar energy modeling. iotools.get_pvgis_tmy iotools.read_pvgis_tmy iotools.read_bsrn + iotools.get_cams_mcclear A :py:class:`~pvlib.location.Location` object may be created from metadata in some files. diff --git a/docs/sphinx/source/whatsnew/v0.9.0.rst b/docs/sphinx/source/whatsnew/v0.9.0.rst index 81e7a0c60b..a4e2688bb0 100644 --- a/docs/sphinx/source/whatsnew/v0.9.0.rst +++ b/docs/sphinx/source/whatsnew/v0.9.0.rst @@ -64,6 +64,9 @@ Enhancements ~~~~~~~~~~~~ * Add :func:`~pvlib.iotools.read_bsrn` for reading BSRN solar radiation data files. (:pull:`1145`, :issue:`1015`) +* Add :func:`~pvlib.iotools.get_cams_radiation` for retrieving CAMS McClear + clear-sky radiation time series. + files. (:pull:`1145`, :issue:`1015`) * In :py:class:`~pvlib.modelchain.ModelChain`, attributes which contain output of models are now collected into ``ModelChain.results``. (:pull:`1076`, :issue:`1067`) diff --git a/pvlib/iotools/__init__.py b/pvlib/iotools/__init__.py index ba5d5e8807..737ee66d4d 100644 --- a/pvlib/iotools/__init__.py +++ b/pvlib/iotools/__init__.py @@ -14,3 +14,4 @@ from pvlib.iotools.psm3 import parse_psm3 # noqa: F401 from pvlib.iotools.pvgis import get_pvgis_tmy, read_pvgis_tmy # noqa: F401 from pvlib.iotools.bsrn import read_bsrn # noqa: F401 +from pvlib.iotools.cams import get_cams_radiation # noqa: F401 diff --git a/pvlib/iotools/cams.py b/pvlib/iotools/cams.py new file mode 100644 index 0000000000..c802420623 --- /dev/null +++ b/pvlib/iotools/cams.py @@ -0,0 +1,207 @@ +"""Functions to access data from Copernicus Atmosphere Monitoring Service + (CAMS) radiation service. +.. codeauthor:: Adam R. Jensen +""" + +import pandas as pd +import requests +import io + + +MCCLEAR_COLUMNS = ['Observation period', 'TOA', 'Clear sky GHI', + 'Clear sky BHI', 'Clear sky DHI', 'Clear sky BNI'] + +MCCLEAR_VERBOSE_COLUMNS = ['sza', 'summer/winter split', 'tco3', 'tcwv', + 'AOD BC', 'AOD DU', 'AOD SS', 'AOD OR', 'AOD SU', + 'AOD NI', 'AOD AM', 'alpha', 'Aerosol type', + 'fiso', 'fvol', 'fgeo', 'albedo'] + +# Dictionary mapping CAMS MCCLEAR variables to pvlib names +MCCLEAR_VARIABLE_MAP = { + 'TOA': 'ghi_extra', + 'Clear sky GHI': 'ghi_clear', + 'Clear sky BHI': 'bhi_clear', + 'Clear sky DHI': 'dhi_clear', + 'Clear sky BNI': 'dni_clear', + 'sza': 'solar_zenith', +} + + +# Dictionary mapping Python time steps to CAMS time step format +TIME_STEPS = {'1min': 'PT01M', '15min': 'PT15M', '1h': 'PT01H', '1d': 'P01D', + '1M': 'P01M'} + +TIME_STEPS_HOURS = {'1min': 1/60, '15min': 15/60, '1h': 1, '1d': 24} + + +def get_cams_mcclear(start_date, end_date, latitude, longitude, email, + altitude=None, time_step='1h', time_ref='UT', + integrated=False, label=None, verbose=False, + map_variables=True, server='www.soda-is.com'): + """ + Retrieve time-series of clear-sky global, beam, and diffuse radiation + anywhere in the world from CAMS McClear [1]_ using the WGET service [2]_. + + + Geographical coverage: wordwide + Time coverage: 2004-01-01 to two days ago + Access: free, but requires registration, see [1]_ + Requests: max. 100 per day + + + Parameters + ---------- + start_date: datetime like + First day of the requested period + end_date: datetime like + Last day of the requested period + latitude: float + in decimal degrees, between -90 and 90, north is positive (ISO 19115) + longitude : float + in decimal degrees, between -180 and 180, east is positive (ISO 19115) + altitude: float, default: None + Altitude in meters. If None, then the altitude is determined from the + NASA SRTM database + email: str + Email address linked to a SoDa account + time_step: str, {'1min', '15min', '1h', '1d', '1M'}, default: '1h' + Time step of the time series, either 1 minute, 15 minute, hourly, + daily, or monthly. + time_reference: str, {'UT', 'TST'}, default: 'UT' + 'UT' (universal time) or 'TST' (True Solar Time) + integrated: boolean, default False + Whether to return integrated irradiation values (Wh/m^2) from CAMS or + average irradiance values (W/m^2) as is more commonly used + label: {‘right’, ‘left’}, default: None + Which bin edge label to label bucket with. The default is ‘left’ for + all frequency offsets except for ‘M’ which has a default of ‘right’. + verbose: boolean, default: False + Verbose mode outputs additional parameters (aerosols). Only avaiable + for 1 minute and universal time. See [1] for parameter description. + map_variables: bool, default: True + When true, renames columns of the Dataframe to pvlib variable names + where applicable. See variable MCCLEAR_VARIABLE_MAP. + server: str, default: 'www.soda-is.com' + Main server (www.soda-is.com) or backup mirror server (pro.soda-is.com) + + + Notes + ---------- + The returned data Dataframe includes the following fields: + + ======================= ====== ========================================== + Key, mapped key Format Description + ======================= ====== ========================================== + **Mapped field names are returned when the map_variables argument is True** + -------------------------------------------------------------------------- + Observation period str Beginning/end of time period + TOA, ghi_extra float Horizontal radiation at top of atmosphere + Clear sky GHI, ghi_clear float Clear sky global radiation on horizontal + Clear sky BHI, bhi_clear float Clear sky beam radiation on horizontal + Clear sky DHI, dhi_clear float Clear sky diffuse radiation on horizontal + Clear sky BNI, dni_clear float Clear sky beam radiation normal to sun + ======================= ====== ========================================== + + For the returned units see the integrated argument. For description of + additional output parameters in verbose mode, see [1]. + + Note that it is recommended to specify the latitude and longitude to at + least the fourth decimal place. + + Variables corresponding to standard pvlib variables are renamed, + e.g. `sza` becomes `solar_zenith`. See the + `pvlib.iotools.cams.MCCLEAR_VARIABLE_MAP` dict for the complete mapping. + + + References + ---------- + .. [1] `CAMS McClear Service Info + `_ + .. [2] `CAMS McClear Automatic Access + `_ + """ + + if time_step in TIME_STEPS.keys(): + time_step_str = TIME_STEPS[time_step] + else: + print('WARNING: time step not recognized, 1 hour time step used!') + time_step_str = 'PT01H' + + names = MCCLEAR_COLUMNS + if verbose: + if (time_step == '1min') & (time_ref == 'UT'): + names += MCCLEAR_VERBOSE_COLUMNS + else: + verbose = False + print("Verbose mode only supports 1 min. UT time series!") + + if altitude is None: # Let SoDa get elevation from the NASA SRTM database + altitude = -999 + + # Start and end date should be in the format: yyyy-mm-dd + start_date = start_date.strftime('%Y-%m-%d') + end_date = end_date.strftime('%Y-%m-%d') + + email = email.replace('@', '%2540') # Format email address + + # Format verbose variable to the required format: {'true', 'false'} + verbose = str(verbose).lower() + + # Manual format the request url, due to uncommon usage of & and ; in url + url = ("http://{}/service/wps?Service=WPS&Request=Execute&" + "Identifier=get_mcclear&version=1.0.0&RawDataOutput=irradiation&" + "DataInputs=latitude={};longitude={};altitude={};" + "date_begin={};date_end={};time_ref={};summarization={};" + "username={};verbose={}" + ).format(server, latitude, longitude, altitude, start_date, + end_date, time_ref, time_step_str, email, verbose) + + res = requests.get(url) + + # Invalid requests returns helpful XML error message + if res.headers['Content-Type'] == 'application/xml': + print('REQUEST ERROR MESSAGE:') + print(res.text.split('ows:ExceptionText')[1][1:-2]) + + # Check if returned file is a csv data file + elif res.headers['Content-Type'] == 'application/csv': + data = pd.read_csv(io.StringIO(res.content.decode('utf-8')), sep=';', + comment='#', header=None, names=names) + + obs_period = data['Observation period'].str.split('/') + + # Set index as the start observation time (left) and localize to UTC + if (label == 'left') | ((label is None) & (time_step != '1M')): + data.index = pd.to_datetime(obs_period.str[0], utc=True) + # Set index as the stop observation time (right) and localize to UTC + elif (label == 'right') | ((label is None) & (time_step == '1M')): + data.index = pd.to_datetime(obs_period.str[1], utc=True) + + data.index.name = None # Set index name to None + + # Change index for '1d' and '1M' to be date and not datetime + if time_step == '1d': + data.index = data.index.date + elif (time_step == '1M') & (label is not None): + data.index = data.index.date + # For monthly data with 'right' label, the index should be the last + # date of the month and not the first date of the following month + elif (time_step == '1M') & (time_step != 'left'): + data.index = data.index.date - pd.Timestamp(days=1) + + if not integrated: # Convert from Wh/m2 to W/m2 + integrated_cols = MCCLEAR_COLUMNS[1:6] + + if time_step == '1M': + time_delta = (pd.to_datetime(obs_period.str[1]) + - pd.to_datetime(obs_period.str[0])) + hours = time_delta.dt.total_seconds()/60/60 + data[integrated_cols] = data[integrated_cols] / hours + else: + data[integrated_cols] = (data[integrated_cols] / + TIME_STEPS_HOURS[time_step]) + + if map_variables: + data = data.rename(columns=MCCLEAR_VARIABLE_MAP) + + return data From 510f08ef8b2d0ee543c197a1433c6294ce410cde Mon Sep 17 00:00:00 2001 From: AdamRJensen Date: Mon, 22 Feb 2021 22:14:29 +0100 Subject: [PATCH 02/31] Revert "Add cams.get_cams_radiation function" This reverts commit d7deb80cdc5d1b63de5b2865a0c5cf24d4655fc1. --- docs/sphinx/source/api.rst | 1 - docs/sphinx/source/whatsnew/v0.9.0.rst | 3 - pvlib/iotools/__init__.py | 1 - pvlib/iotools/cams.py | 207 ------------------------- 4 files changed, 212 deletions(-) delete mode 100644 pvlib/iotools/cams.py diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 31204b6f0d..8805d199a4 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -484,7 +484,6 @@ relevant to solar energy modeling. iotools.get_pvgis_tmy iotools.read_pvgis_tmy iotools.read_bsrn - iotools.get_cams_mcclear A :py:class:`~pvlib.location.Location` object may be created from metadata in some files. diff --git a/docs/sphinx/source/whatsnew/v0.9.0.rst b/docs/sphinx/source/whatsnew/v0.9.0.rst index a4e2688bb0..81e7a0c60b 100644 --- a/docs/sphinx/source/whatsnew/v0.9.0.rst +++ b/docs/sphinx/source/whatsnew/v0.9.0.rst @@ -64,9 +64,6 @@ Enhancements ~~~~~~~~~~~~ * Add :func:`~pvlib.iotools.read_bsrn` for reading BSRN solar radiation data files. (:pull:`1145`, :issue:`1015`) -* Add :func:`~pvlib.iotools.get_cams_radiation` for retrieving CAMS McClear - clear-sky radiation time series. - files. (:pull:`1145`, :issue:`1015`) * In :py:class:`~pvlib.modelchain.ModelChain`, attributes which contain output of models are now collected into ``ModelChain.results``. (:pull:`1076`, :issue:`1067`) diff --git a/pvlib/iotools/__init__.py b/pvlib/iotools/__init__.py index 737ee66d4d..ba5d5e8807 100644 --- a/pvlib/iotools/__init__.py +++ b/pvlib/iotools/__init__.py @@ -14,4 +14,3 @@ from pvlib.iotools.psm3 import parse_psm3 # noqa: F401 from pvlib.iotools.pvgis import get_pvgis_tmy, read_pvgis_tmy # noqa: F401 from pvlib.iotools.bsrn import read_bsrn # noqa: F401 -from pvlib.iotools.cams import get_cams_radiation # noqa: F401 diff --git a/pvlib/iotools/cams.py b/pvlib/iotools/cams.py deleted file mode 100644 index c802420623..0000000000 --- a/pvlib/iotools/cams.py +++ /dev/null @@ -1,207 +0,0 @@ -"""Functions to access data from Copernicus Atmosphere Monitoring Service - (CAMS) radiation service. -.. codeauthor:: Adam R. Jensen -""" - -import pandas as pd -import requests -import io - - -MCCLEAR_COLUMNS = ['Observation period', 'TOA', 'Clear sky GHI', - 'Clear sky BHI', 'Clear sky DHI', 'Clear sky BNI'] - -MCCLEAR_VERBOSE_COLUMNS = ['sza', 'summer/winter split', 'tco3', 'tcwv', - 'AOD BC', 'AOD DU', 'AOD SS', 'AOD OR', 'AOD SU', - 'AOD NI', 'AOD AM', 'alpha', 'Aerosol type', - 'fiso', 'fvol', 'fgeo', 'albedo'] - -# Dictionary mapping CAMS MCCLEAR variables to pvlib names -MCCLEAR_VARIABLE_MAP = { - 'TOA': 'ghi_extra', - 'Clear sky GHI': 'ghi_clear', - 'Clear sky BHI': 'bhi_clear', - 'Clear sky DHI': 'dhi_clear', - 'Clear sky BNI': 'dni_clear', - 'sza': 'solar_zenith', -} - - -# Dictionary mapping Python time steps to CAMS time step format -TIME_STEPS = {'1min': 'PT01M', '15min': 'PT15M', '1h': 'PT01H', '1d': 'P01D', - '1M': 'P01M'} - -TIME_STEPS_HOURS = {'1min': 1/60, '15min': 15/60, '1h': 1, '1d': 24} - - -def get_cams_mcclear(start_date, end_date, latitude, longitude, email, - altitude=None, time_step='1h', time_ref='UT', - integrated=False, label=None, verbose=False, - map_variables=True, server='www.soda-is.com'): - """ - Retrieve time-series of clear-sky global, beam, and diffuse radiation - anywhere in the world from CAMS McClear [1]_ using the WGET service [2]_. - - - Geographical coverage: wordwide - Time coverage: 2004-01-01 to two days ago - Access: free, but requires registration, see [1]_ - Requests: max. 100 per day - - - Parameters - ---------- - start_date: datetime like - First day of the requested period - end_date: datetime like - Last day of the requested period - latitude: float - in decimal degrees, between -90 and 90, north is positive (ISO 19115) - longitude : float - in decimal degrees, between -180 and 180, east is positive (ISO 19115) - altitude: float, default: None - Altitude in meters. If None, then the altitude is determined from the - NASA SRTM database - email: str - Email address linked to a SoDa account - time_step: str, {'1min', '15min', '1h', '1d', '1M'}, default: '1h' - Time step of the time series, either 1 minute, 15 minute, hourly, - daily, or monthly. - time_reference: str, {'UT', 'TST'}, default: 'UT' - 'UT' (universal time) or 'TST' (True Solar Time) - integrated: boolean, default False - Whether to return integrated irradiation values (Wh/m^2) from CAMS or - average irradiance values (W/m^2) as is more commonly used - label: {‘right’, ‘left’}, default: None - Which bin edge label to label bucket with. The default is ‘left’ for - all frequency offsets except for ‘M’ which has a default of ‘right’. - verbose: boolean, default: False - Verbose mode outputs additional parameters (aerosols). Only avaiable - for 1 minute and universal time. See [1] for parameter description. - map_variables: bool, default: True - When true, renames columns of the Dataframe to pvlib variable names - where applicable. See variable MCCLEAR_VARIABLE_MAP. - server: str, default: 'www.soda-is.com' - Main server (www.soda-is.com) or backup mirror server (pro.soda-is.com) - - - Notes - ---------- - The returned data Dataframe includes the following fields: - - ======================= ====== ========================================== - Key, mapped key Format Description - ======================= ====== ========================================== - **Mapped field names are returned when the map_variables argument is True** - -------------------------------------------------------------------------- - Observation period str Beginning/end of time period - TOA, ghi_extra float Horizontal radiation at top of atmosphere - Clear sky GHI, ghi_clear float Clear sky global radiation on horizontal - Clear sky BHI, bhi_clear float Clear sky beam radiation on horizontal - Clear sky DHI, dhi_clear float Clear sky diffuse radiation on horizontal - Clear sky BNI, dni_clear float Clear sky beam radiation normal to sun - ======================= ====== ========================================== - - For the returned units see the integrated argument. For description of - additional output parameters in verbose mode, see [1]. - - Note that it is recommended to specify the latitude and longitude to at - least the fourth decimal place. - - Variables corresponding to standard pvlib variables are renamed, - e.g. `sza` becomes `solar_zenith`. See the - `pvlib.iotools.cams.MCCLEAR_VARIABLE_MAP` dict for the complete mapping. - - - References - ---------- - .. [1] `CAMS McClear Service Info - `_ - .. [2] `CAMS McClear Automatic Access - `_ - """ - - if time_step in TIME_STEPS.keys(): - time_step_str = TIME_STEPS[time_step] - else: - print('WARNING: time step not recognized, 1 hour time step used!') - time_step_str = 'PT01H' - - names = MCCLEAR_COLUMNS - if verbose: - if (time_step == '1min') & (time_ref == 'UT'): - names += MCCLEAR_VERBOSE_COLUMNS - else: - verbose = False - print("Verbose mode only supports 1 min. UT time series!") - - if altitude is None: # Let SoDa get elevation from the NASA SRTM database - altitude = -999 - - # Start and end date should be in the format: yyyy-mm-dd - start_date = start_date.strftime('%Y-%m-%d') - end_date = end_date.strftime('%Y-%m-%d') - - email = email.replace('@', '%2540') # Format email address - - # Format verbose variable to the required format: {'true', 'false'} - verbose = str(verbose).lower() - - # Manual format the request url, due to uncommon usage of & and ; in url - url = ("http://{}/service/wps?Service=WPS&Request=Execute&" - "Identifier=get_mcclear&version=1.0.0&RawDataOutput=irradiation&" - "DataInputs=latitude={};longitude={};altitude={};" - "date_begin={};date_end={};time_ref={};summarization={};" - "username={};verbose={}" - ).format(server, latitude, longitude, altitude, start_date, - end_date, time_ref, time_step_str, email, verbose) - - res = requests.get(url) - - # Invalid requests returns helpful XML error message - if res.headers['Content-Type'] == 'application/xml': - print('REQUEST ERROR MESSAGE:') - print(res.text.split('ows:ExceptionText')[1][1:-2]) - - # Check if returned file is a csv data file - elif res.headers['Content-Type'] == 'application/csv': - data = pd.read_csv(io.StringIO(res.content.decode('utf-8')), sep=';', - comment='#', header=None, names=names) - - obs_period = data['Observation period'].str.split('/') - - # Set index as the start observation time (left) and localize to UTC - if (label == 'left') | ((label is None) & (time_step != '1M')): - data.index = pd.to_datetime(obs_period.str[0], utc=True) - # Set index as the stop observation time (right) and localize to UTC - elif (label == 'right') | ((label is None) & (time_step == '1M')): - data.index = pd.to_datetime(obs_period.str[1], utc=True) - - data.index.name = None # Set index name to None - - # Change index for '1d' and '1M' to be date and not datetime - if time_step == '1d': - data.index = data.index.date - elif (time_step == '1M') & (label is not None): - data.index = data.index.date - # For monthly data with 'right' label, the index should be the last - # date of the month and not the first date of the following month - elif (time_step == '1M') & (time_step != 'left'): - data.index = data.index.date - pd.Timestamp(days=1) - - if not integrated: # Convert from Wh/m2 to W/m2 - integrated_cols = MCCLEAR_COLUMNS[1:6] - - if time_step == '1M': - time_delta = (pd.to_datetime(obs_period.str[1]) - - pd.to_datetime(obs_period.str[0])) - hours = time_delta.dt.total_seconds()/60/60 - data[integrated_cols] = data[integrated_cols] / hours - else: - data[integrated_cols] = (data[integrated_cols] / - TIME_STEPS_HOURS[time_step]) - - if map_variables: - data = data.rename(columns=MCCLEAR_VARIABLE_MAP) - - return data From 8743519559bdd991224cda7be04daa493b94bf57 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Fri, 22 Jul 2022 05:10:20 +0200 Subject: [PATCH 03/31] Add get, read, parse solaranywhere functions --- docs/sphinx/source/reference/iotools.rst | 3 + pvlib/data/variables_style_rules.csv | 1 + pvlib/iotools/__init__.py | 3 + pvlib/iotools/solaranywhere.py | 300 +++++++++++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 pvlib/iotools/solaranywhere.py diff --git a/docs/sphinx/source/reference/iotools.rst b/docs/sphinx/source/reference/iotools.rst index 514aeac2f5..3862923d3f 100644 --- a/docs/sphinx/source/reference/iotools.rst +++ b/docs/sphinx/source/reference/iotools.rst @@ -37,6 +37,9 @@ of sources and file formats relevant to solar energy modeling. iotools.get_cams iotools.read_cams iotools.parse_cams + iotools.get_solaranywhere + iotools.read_solaranywhere + iotools.parse_solaranywhere A :py:class:`~pvlib.location.Location` object may be created from metadata in some files. diff --git a/pvlib/data/variables_style_rules.csv b/pvlib/data/variables_style_rules.csv index a56dddd161..2a98e32531 100644 --- a/pvlib/data/variables_style_rules.csv +++ b/pvlib/data/variables_style_rules.csv @@ -4,6 +4,7 @@ latitude;latitude longitude;longitude dni;direct normal irradiance dni_extra;direct normal irradiance at top of atmosphere (extraterrestrial) +dni_clear;clear sky direct normal irradiance dhi;diffuse horizontal irradiance bhi;beam/direct horizontal irradiance ghi;global horizontal irradiance diff --git a/pvlib/iotools/__init__.py b/pvlib/iotools/__init__.py index b02ce243ae..4b471d90b4 100644 --- a/pvlib/iotools/__init__.py +++ b/pvlib/iotools/__init__.py @@ -21,3 +21,6 @@ from pvlib.iotools.sodapro import get_cams # noqa: F401 from pvlib.iotools.sodapro import read_cams # noqa: F401 from pvlib.iotools.sodapro import parse_cams # noqa: F401 +from pvlib.iotools.solaranywhere import get_solaranywhere # noqa: F401 +from pvlib.iotools.solaranywhere import read_solaranywhere # noqa: F401 +from pvlib.iotools.solaranywhere import parse_solaranywhere # noqa: F401 diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py new file mode 100644 index 0000000000..74de6cde41 --- /dev/null +++ b/pvlib/iotools/solaranywhere.py @@ -0,0 +1,300 @@ +"""Functions to read and retrieve SolarAnywhere data.""" + +import requests +import pandas as pd +import time +import json + +URL = 'https://service.solaranywhere.com/api/v2' + +# Dictionary mapping SolarAnywhere names to standard pvlib names +# Names with spaces are used in SolarAnywhere files, and names without spaces +# are used by the SolarAnywhere API +VARIABLE_MAP = { + 'Global Horizontal Irradiance (GHI) W/m2': 'ghi', + 'GlobalHorizontalIrradiance_WattsPerMeterSquared': 'ghi', + 'DirectNormalIrradiance_WattsPerMeterSquared': 'dni', + 'Direct Normal Irradiance (DNI) W/m2': 'dni', + 'Diffuse Horizontal Irradiance (DIF) W/m2': 'dhi', + 'DiffuseHorizontalIrradiance_WattsPerMeterSquared': 'dhi', + 'AmbientTemperature (deg C)': 'temp_air', + 'AmbientTemperature_DegreesC': 'temp_air', + 'WindSpeed (m/s)': 'wind_speed', + 'WindSpeed_MetersPerSecond': 'wind_speed', + 'Relative Humidity (%)': 'relative_humidity', + 'Clear Sky GHI': 'ghi_clear', + 'Clear Sky DNI': 'dni_clear', + 'Clear Sky DHI': 'dhi_clear', + 'Albedo': 'albedo', + 'Albedo_Unitless': 'albedo', +} + +DEFAULT_VARIABLES = [ + 'StartTime', 'ObservationTime', 'EndTime', + 'GlobalHorizontalIrradiance_WattsPerMeterSquared', + 'DirectNormalIrradiance_WattsPerMeterSquared', + 'DiffuseHorizontalIrradiance_WattsPerMeterSquared', + 'AmbientTemperature_DegreesC', 'WindSpeed_MetersPerSecond', + 'Albedo_Unitless', 'DataVersion' +] + + +def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, + time_resolution=60, spatial_resolution=0.1, + true_dynamics=False, source='SolarAnywhereLatest', + missing_data='Omit', url=URL, map_variables=True, + max_response_time=300): + """Retrieve historical time series irradiance data from SolarAnywhere. + + The SolarAnywhere API is described in [1]_ and [2]_. + + Parameters + ---------- + latitude: float + In decimal degrees, north is positive (ISO 19115). + longitude: float + In decimal degrees, east is positive (ISO 19115). + api_key: str + SolarAnywhere API key. + start: datetime like, optional + First timestamp of the requested period. If a timezone is not + specified, UTC is assumed. Not applicable for TMY data. + end: datetime like, optional + Last timtestamp of the requested period. If a timezone is not + specified, UTC is assumed. Not applicable for TMY data. + time_resolution: {60, 30, 15, 5}, default: 60 + Time resolution in minutes. For TMY data, time resolution has to be 60 + min. (hourly). + spatial_resolution: {0.1, 0.01, 0.005}, default: 0.1 + Spatial resolution in degrees. + true_dynamics: bool, default: False + Whether to apply SolarAnywhere TrueDynamics statistical processing. + Only available for the 5-min time resolution. + source: str, default: 'SolarAnywhereLatest' + Data source. Options include: 'SolarAnywhereLatest' (historical data), + 'SolarAnywhereTGYLatest' (TMY for GHI), or 'SolarAnywhereTDYLatest' + (TMY for DNI). + variables: list-like, default: :const:`DEFAULT_VARIABLES` + Variables to retrieve. A description can be found in [3]_. Available + variables depend on whether historical or TMY data is requested. + missing_data: {'Omit', 'FillAverage'}, default: 'Omit' + Method for treating missing data. + url: str, default: :const:`pvlib.iotools.solaranywhere.URL` + Base url of SolarAnywhere API. + map_variables: bool, default: True + When true, renames columns of the Dataframe to pvlib variable names + where applicable. See variable :const:`VARIABLE_MAP`. + max_response_time: int, default: 300 + Time in seconds to wait for requested data to become available. + + Returns + ------- + data: pandas.DataFrame + Timeseries data from SolarAnywhere. The index is the observation time + (middle of period) in UTC. + metadata: dict + Metadata available (includes site latitude, longitude, and altitude). + + See Also + -------- + pvlib.iotools.read_solaranywhere + + Note + ---- + SolarAnywhere data requests are asynchronous, and it might take several + minutes for the data to become available. + + Examples + -------- + >>> # Retrieve one month of SolarAnywhere data for Atlanta, GA + >>> data, meta = pvlib.iotools.get_solaranywhere( + ... latitude=33.765, longitude=-84.395, api_key='redacted', + ... start=pd.Timestamp(2020,1,1), end=pd.Timestamp(2020,2,1)) # doctest: +SKIP + + References + ---------- + .. [1] `SolarAnywhere API + `_ + .. [2] `SolarAnywhere irradiance and weather API requests + `_ + .. [3] `SolarAnywhere variable definitions + `_ + """ # noqa: E501 + headers = {'content-type': "application/json; charset=utf-8", + 'X-Api-Key': api_key, + 'Accept': "application/json"} + + payload = { + "Sites": [{ + "Latitude": latitude, + "Longitude": longitude + }], + "Options": { + "OutputFields": DEFAULT_VARIABLES, + "SummaryOutputFields": [], # Do not request summary/monthly data + "SpatialResolution_Degrees": spatial_resolution, + "TimeResolution_Minutes": time_resolution, + "WeatherDataSource": source, + "MissingDataHandling": missing_data, + } + } + + if true_dynamics: + payload['Options']['ApplyTrueDynamics'] = True + + # Add start/end time if requesting non-TMY data (SolarAnywhereLatest) + if source == 'SolarAnywhereLatest': + if (start is None) or (end is None): + ValueError('When requesting non-TMY data, specifying `start` and' + '`end` is required.') + # start/end are required to have an associated time zone + if start.tz is None: + start = start.tz_localize('UTC') + if end.tz is None: + end = end.tz_localize('UTC') + payload['Options']["StartTime"] = start.isoformat() + payload['Options']["EndTime"] = end.isoformat() + + # Convert the payload dictionary to a JSON string (uses double quotes) + payload = json.dumps(payload) + # Make data request + request = requests.post(url+'/WeatherData', data=payload, headers=headers) + # Raise error if request is not OK + if request.ok is False: + raise ValueError(request.json()['Message']) + # Retrieve weather request ID + weather_request_id = request.json()["WeatherRequestId"] + + # The SolarAnywhere API is asynchronous, hence a second request is + # necessary to retrieve the data (WeatherDataResult). + start_time = time.time() # Current time in seconds since the Epoch + # Attempt to retrieve results until the max response time has been exceeded + while True: + time.sleep(5) # Sleep for 5 seconds before each data retrieval attempt + results = requests.get(url+'/WeatherDataResult/'+weather_request_id, headers=headers) # noqa: E501 + results_json = results.json() + if results_json.get('Status') == 'Done': + if results_json['WeatherDataResults'][0]['Status'] == 'Failure': + raise ValueError(results_json['WeatherDataResults'][0]['ErrorMessages']) # noqa: E501 + break + elif results_json.get('StatusCode') == 'BadRequest': + raise ValueError(f"Bad request: {results_json['Message']}") + elif (time.time()-start_time) > max_response_time: + raise TimeoutError('Time exceeded the `max_response_time`.') + + # Extract time series data + data = pd.DataFrame(results_json['WeatherDataResults'][0]['WeatherDataPeriods']['WeatherDataPeriods']) # noqa: E501 + # Set index and convert to UTC time + data.index = pd.to_datetime(data['ObservationTime']) + data.index = data.index.tz_convert('UTC') + if map_variables: + data = data.rename(columns=VARIABLE_MAP) + + # Parse metadata + meta = results_json['WeatherDataResults'][0]['WeatherSourceInformation'] + meta['time_resolution'] = results_json['WeatherDataResults'][0]['WeatherDataPeriods']['TimeResolution_Minutes'] # noqa: E501 + # Rename and convert applicable metadata parameters to floats + meta['latitude'] = float(meta.pop('Latitude')) + meta['longitude'] = float(meta.pop('Longitude')) + meta['altitude'] = float(meta.pop('Elevation_Meters')) + return data, meta + + +def read_solaranywhere(filename, map_variables=True): + """ + Read a SolarAnywhere formatted file into a pandas DataFrame. + + The SolarAnywhere file format and the variables are described in [1]_. The + SolarAnywhere file format resembles the TMY3 file format but contains + additional variables and meatadata. + + Parameters + ---------- + fbuf: file-like object + File-like object containing data to read. + map_variables: bool, default: True + When true, renames columns of the Dataframe to pvlib variable names + where applicable. See variable :const:`VARIABLE_MAP`. + + Returns + ------- + data: pandas.DataFrame + Timeseries data from SolarAnywhere. Index is localized to UTC. + metadata: dict + Metadata available in the file. + + See Also + -------- + pvlib.iotools.get_solaranywhere, pvlib.iotools.parse_solaranywhere + + References + ---------- + .. [1] `SolarAnywhere historical data file formats + `_ + """ + with open(str(filename), 'r') as fbuf: + content = parse_solaranywhere(fbuf, map_variables=map_variables) + return content + + +def parse_solaranywhere(fbuf, map_variables=True): + """ + Parse a file-like buffer with data in the format of a SolarAnywhere file. + + The SolarAnywhere file format and the variables are described in [1]_. The + SolarAnywhere file format resembles the TMY3 file format but contains + additional variables and meatadata. + + Parameters + ---------- + fbuf: file-like object + File-like object containing data to read. + map_variables: bool, default: True + When true, renames columns of the Dataframe to pvlib variable names + where applicable. See variable :const:`VARIABLE_MAP`. + + Returns + ------- + data: pandas.DataFrame + Timeseries data from SolarAnywhere. Index is localized to UTC. + metadata: dict + Metadata available in the file. + + See Also + -------- + pvlib.iotools.read_solaranywhere, pvlib.iotools.get_solaranywhere + + References + ---------- + .. [1] `SolarAnywhere historical data file formats + `_ + """ + # Parse metadata contained within the first line + firstline = fbuf.readline().strip().split(',') + meta = {} + meta['USAF'] = int(firstline.pop(0)) + meta['name'] = firstline.pop(0) + meta['state'] = firstline.pop(0) + meta['TZ'] = float(firstline.pop(0)) + meta['latitude'] = float(firstline.pop(0)) + meta['longitude'] = float(firstline.pop(0)) + meta['altitude'] = float(firstline.pop(0)) + + # SolarAnywhere files contain additional metadata than the TMY3 format. + # The additional metadata is specified as key-value pairs, where each entry + # is separated by a slash, and the key-value pairs are separated by a + # colon. E.g., 'Data Version: 3.4 / Type: Typical Year / ...' + for i in ','.join(firstline).split('/'): + if ':' in i: + k, v = i.split(':') + meta[k.strip()] = v.strip() + + # Read remaining part of file which contains the time series data + data = pd.read_csv(fbuf) + # Set index to UTC + data.index = pd.to_datetime(data['ObservationTime(GMT)'], + format='%m/%d/%Y %H:%M', utc=True) + if map_variables: + data = data.rename(columns=VARIABLE_MAP) + + return data, meta From b5b2508ea325a73bff29e66cbf1e0a395cda1df9 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Fri, 22 Jul 2022 05:25:15 +0200 Subject: [PATCH 04/31] Add whatsnew --- docs/sphinx/source/whatsnew/v0.9.2.rst | 3 +++ pvlib/iotools/solaranywhere.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.9.2.rst b/docs/sphinx/source/whatsnew/v0.9.2.rst index 028b905003..5069d0486a 100644 --- a/docs/sphinx/source/whatsnew/v0.9.2.rst +++ b/docs/sphinx/source/whatsnew/v0.9.2.rst @@ -11,6 +11,9 @@ Enhancements * Add :py:func:`pvlib.tracking.calc_surface_orientation` for calculating single-axis tracker ``surface_tilt`` and ``surface_azimuth`` from rotation angles. (:issue:`1471`, :pull:`1480`) +* Add :py:func:`pvlib.iotools.read_solaranywhere` and + :py:func:`pvlib.iotools.get_solaranywhere` for reading and retrieving + SolarAnywhere solar irradiance data. (:pull:`1497`, :discussion:`1310`) Bug fixes ~~~~~~~~~ diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 74de6cde41..0dc7df233c 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -128,7 +128,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, "Sites": [{ "Latitude": latitude, "Longitude": longitude - }], + }], "Options": { "OutputFields": DEFAULT_VARIABLES, "SummaryOutputFields": [], # Do not request summary/monthly data @@ -136,8 +136,8 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, "TimeResolution_Minutes": time_resolution, "WeatherDataSource": source, "MissingDataHandling": missing_data, - } } + } if true_dynamics: payload['Options']['ApplyTrueDynamics'] = True From 0158719e98afc2b508141532963bd1fac66d05e0 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Fri, 22 Jul 2022 07:00:58 +0200 Subject: [PATCH 05/31] Updates to get_solaranywhere --- pvlib/iotools/solaranywhere.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 0dc7df233c..92c0a764e3 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -22,9 +22,13 @@ 'WindSpeed (m/s)': 'wind_speed', 'WindSpeed_MetersPerSecond': 'wind_speed', 'Relative Humidity (%)': 'relative_humidity', + 'RelativeHumidity_Percent': 'relative_humidity', 'Clear Sky GHI': 'ghi_clear', + 'ClearSkyGHI_WattsPerMeterSquared': 'ghi_clear', 'Clear Sky DNI': 'dni_clear', + 'ClearSkyDNI_WattsPerMeterSquared': 'dni_clear', 'Clear Sky DHI': 'dhi_clear', + 'ClearSkyDHI_WattsPerMeterSquared': 'dhi_clear', 'Albedo': 'albedo', 'Albedo_Unitless': 'albedo', } @@ -42,11 +46,12 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, time_resolution=60, spatial_resolution=0.1, true_dynamics=False, source='SolarAnywhereLatest', - missing_data='Omit', url=URL, map_variables=True, - max_response_time=300): + variables=DEFAULT_VARIABLES, missing_data='Omit', + url=URL, map_variables=True, max_response_time=300): """Retrieve historical time series irradiance data from SolarAnywhere. - The SolarAnywhere API is described in [1]_ and [2]_. + The SolarAnywhere API is described in [1]_ and [2]_. A detailed list of + available options for the input parameters can be found in [3]_. Parameters ---------- @@ -73,10 +78,11 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, source: str, default: 'SolarAnywhereLatest' Data source. Options include: 'SolarAnywhereLatest' (historical data), 'SolarAnywhereTGYLatest' (TMY for GHI), or 'SolarAnywhereTDYLatest' - (TMY for DNI). + (TMY for DNI). Specific dataset versions can also be specified, e.g., + 'SolarAnywhere3_2' (see [3]_ for a full list of options). variables: list-like, default: :const:`DEFAULT_VARIABLES` - Variables to retrieve. A description can be found in [3]_. Available - variables depend on whether historical or TMY data is requested. + Variables to retrieve (see [4]_). Available variables depend on + whether historical or TMY data is requested. missing_data: {'Omit', 'FillAverage'}, default: 'Omit' Method for treating missing data. url: str, default: :const:`pvlib.iotools.solaranywhere.URL` @@ -117,7 +123,9 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, `_ .. [2] `SolarAnywhere irradiance and weather API requests `_ - .. [3] `SolarAnywhere variable definitions + .. [3] `SolarAnywhere API options + `_ + .. [4] `SolarAnywhere variable definitions `_ """ # noqa: E501 headers = {'content-type': "application/json; charset=utf-8", @@ -130,7 +138,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, "Longitude": longitude }], "Options": { - "OutputFields": DEFAULT_VARIABLES, + "OutputFields": variables, "SummaryOutputFields": [], # Do not request summary/monthly data "SpatialResolution_Degrees": spatial_resolution, "TimeResolution_Minutes": time_resolution, From 2aeafb1b331ea9c60163b600dcee3c51b4baf64c Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Fri, 22 Jul 2022 21:39:52 +0200 Subject: [PATCH 06/31] Minor doc updates --- pvlib/iotools/solaranywhere.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 92c0a764e3..5dd1895fb3 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -48,7 +48,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, true_dynamics=False, source='SolarAnywhereLatest', variables=DEFAULT_VARIABLES, missing_data='Omit', url=URL, map_variables=True, max_response_time=300): - """Retrieve historical time series irradiance data from SolarAnywhere. + """Retrieve historical irradiance time series data from SolarAnywhere. The SolarAnywhere API is described in [1]_ and [2]_. A detailed list of available options for the input parameters can be found in [3]_. @@ -81,23 +81,23 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, (TMY for DNI). Specific dataset versions can also be specified, e.g., 'SolarAnywhere3_2' (see [3]_ for a full list of options). variables: list-like, default: :const:`DEFAULT_VARIABLES` - Variables to retrieve (see [4]_). Available variables depend on - whether historical or TMY data is requested. + Variables to retrieve (described in [4]_). Available variables depend + on whether historical or TMY data is requested. missing_data: {'Omit', 'FillAverage'}, default: 'Omit' Method for treating missing data. url: str, default: :const:`pvlib.iotools.solaranywhere.URL` Base url of SolarAnywhere API. map_variables: bool, default: True - When true, renames columns of the Dataframe to pvlib variable names + When true, renames columns of the DataFrame to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. - max_response_time: int, default: 300 + max_response_time: float, default: 300 Time in seconds to wait for requested data to become available. Returns ------- data: pandas.DataFrame Timeseries data from SolarAnywhere. The index is the observation time - (middle of period) in UTC. + (middle of period) localized to UTC. metadata: dict Metadata available (includes site latitude, longitude, and altitude). @@ -108,7 +108,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, Note ---- SolarAnywhere data requests are asynchronous, and it might take several - minutes for the data to become available. + minutes for the requested data to become available. Examples -------- @@ -150,8 +150,8 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, if true_dynamics: payload['Options']['ApplyTrueDynamics'] = True - # Add start/end time if requesting non-TMY data (SolarAnywhereLatest) - if source == 'SolarAnywhereLatest': + # Add start/end time if requesting non-TMY data + if ('TGY' not in source) & ('TDY' not in source) & ('TMY' not in source): if (start is None) or (end is None): ValueError('When requesting non-TMY data, specifying `start` and' '`end` is required.') @@ -183,10 +183,10 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, results_json = results.json() if results_json.get('Status') == 'Done': if results_json['WeatherDataResults'][0]['Status'] == 'Failure': - raise ValueError(results_json['WeatherDataResults'][0]['ErrorMessages']) # noqa: E501 + raise RuntimeError(results_json['WeatherDataResults'][0]['ErrorMessages']) # noqa: E501 break elif results_json.get('StatusCode') == 'BadRequest': - raise ValueError(f"Bad request: {results_json['Message']}") + raise RuntimeError(f"Bad request: {results_json['Message']}") elif (time.time()-start_time) > max_response_time: raise TimeoutError('Time exceeded the `max_response_time`.') @@ -212,8 +212,8 @@ def read_solaranywhere(filename, map_variables=True): """ Read a SolarAnywhere formatted file into a pandas DataFrame. - The SolarAnywhere file format and the variables are described in [1]_. The - SolarAnywhere file format resembles the TMY3 file format but contains + The SolarAnywhere file format and variables are described in [1]_. Note, + the SolarAnywhere file format resembles the TMY3 file format but contains additional variables and meatadata. Parameters @@ -221,7 +221,7 @@ def read_solaranywhere(filename, map_variables=True): fbuf: file-like object File-like object containing data to read. map_variables: bool, default: True - When true, renames columns of the Dataframe to pvlib variable names + When true, renames columns of the DataFrame to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. Returns @@ -249,8 +249,8 @@ def parse_solaranywhere(fbuf, map_variables=True): """ Parse a file-like buffer with data in the format of a SolarAnywhere file. - The SolarAnywhere file format and the variables are described in [1]_. The - SolarAnywhere file format resembles the TMY3 file format but contains + The SolarAnywhere file format and variables are described in [1]_. Note, + the SolarAnywhere file format resembles the TMY3 file format but contains additional variables and meatadata. Parameters @@ -258,7 +258,7 @@ def parse_solaranywhere(fbuf, map_variables=True): fbuf: file-like object File-like object containing data to read. map_variables: bool, default: True - When true, renames columns of the Dataframe to pvlib variable names + When true, renames columns of the DataFrame to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. Returns From 43eca9000104927b14b122d1fb33ef2b50ddf4df Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Sat, 23 Jul 2022 09:19:03 +0200 Subject: [PATCH 07/31] Updated default values & add POE --- pvlib/iotools/solaranywhere.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 5dd1895fb3..897a22f769 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -44,9 +44,10 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, - time_resolution=60, spatial_resolution=0.1, - true_dynamics=False, source='SolarAnywhereLatest', - variables=DEFAULT_VARIABLES, missing_data='Omit', + source='SolarAnywhereLatest', time_resolution=60, + spatial_resolution=0.01, true_dynamics=False, + probability_of_exceedance=None, + variables=DEFAULT_VARIABLES, missing_data='FillAverage', url=URL, map_variables=True, max_response_time=300): """Retrieve historical irradiance time series data from SolarAnywhere. @@ -67,23 +68,27 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, end: datetime like, optional Last timtestamp of the requested period. If a timezone is not specified, UTC is assumed. Not applicable for TMY data. + source: str, default: 'SolarAnywhereLatest' + Data source. Options include: 'SolarAnywhereLatest' (historical data), + 'SolarAnywhereTGYLatest' (TMY for GHI), 'SolarAnywhereTDYLatest' (TMY + for DNI), or 'SolarAnywherePOELatest' for probability of exceedance. + Specific dataset versions can also be specified, e.g., + 'SolarAnywhere3_2' (see [3]_ for a full list of options). time_resolution: {60, 30, 15, 5}, default: 60 Time resolution in minutes. For TMY data, time resolution has to be 60 min. (hourly). - spatial_resolution: {0.1, 0.01, 0.005}, default: 0.1 + spatial_resolution: {0.1, 0.01, 0.005}, default: 0.01 Spatial resolution in degrees. true_dynamics: bool, default: False Whether to apply SolarAnywhere TrueDynamics statistical processing. Only available for the 5-min time resolution. - source: str, default: 'SolarAnywhereLatest' - Data source. Options include: 'SolarAnywhereLatest' (historical data), - 'SolarAnywhereTGYLatest' (TMY for GHI), or 'SolarAnywhereTDYLatest' - (TMY for DNI). Specific dataset versions can also be specified, e.g., - 'SolarAnywhere3_2' (see [3]_ for a full list of options). + probability_of_exceedance: int, optional + Probability of exceedance in the range of 1 to 99. Only relevant when + requesting probability of exceedance (POE) time series. variables: list-like, default: :const:`DEFAULT_VARIABLES` Variables to retrieve (described in [4]_). Available variables depend on whether historical or TMY data is requested. - missing_data: {'Omit', 'FillAverage'}, default: 'Omit' + missing_data: {'Omit', 'FillAverage'}, default: 'FillAverage' Method for treating missing data. url: str, default: :const:`pvlib.iotools.solaranywhere.URL` Base url of SolarAnywhere API. @@ -150,8 +155,15 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, if true_dynamics: payload['Options']['ApplyTrueDynamics'] = True + if probability_of_exceedance is not None: + if type(probability_of_exceedance) != int: + raise ValueError('`probability_of_exceedance` must be an integer') + payload['Options']['ProbabilityOfExceedance'] = \ + probability_of_exceedance + # Add start/end time if requesting non-TMY data - if ('TGY' not in source) & ('TDY' not in source) & ('TMY' not in source): + if (('TGY' not in source) & ('TDY' not in source) & ('TMY' not in source) & + ('POE' not in source)): if (start is None) or (end is None): ValueError('When requesting non-TMY data, specifying `start` and' '`end` is required.') From 8456aeed0701ac232408b89d40c1f93e14e149e1 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Wed, 27 Jul 2022 01:36:24 +0200 Subject: [PATCH 08/31] Properly raise start/end ValueError --- pvlib/iotools/solaranywhere.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 897a22f769..0f199022c0 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -165,8 +165,8 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, if (('TGY' not in source) & ('TDY' not in source) & ('TMY' not in source) & ('POE' not in source)): if (start is None) or (end is None): - ValueError('When requesting non-TMY data, specifying `start` and' - '`end` is required.') + raise ValueError('When requesting non-TMY data, specifying `start`' + ' and `end` is required.') # start/end are required to have an associated time zone if start.tz is None: start = start.tz_localize('UTC') @@ -304,7 +304,7 @@ def parse_solaranywhere(fbuf, map_variables=True): # The additional metadata is specified as key-value pairs, where each entry # is separated by a slash, and the key-value pairs are separated by a # colon. E.g., 'Data Version: 3.4 / Type: Typical Year / ...' - for i in ','.join(firstline).split('/'): + for i in ','.join(firstline).replace('"', '').split('/'): if ':' in i: k, v = i.split(':') meta[k.strip()] = v.strip() From 78a3edb4422325ac3cb3803644016d59941ececb Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:11:59 +0200 Subject: [PATCH 09/31] Add api_key to pytest-remote-data.yml --- .github/workflows/pytest-remote-data.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pytest-remote-data.yml b/.github/workflows/pytest-remote-data.yml index ad57d2c175..347871658b 100644 --- a/.github/workflows/pytest-remote-data.yml +++ b/.github/workflows/pytest-remote-data.yml @@ -97,6 +97,7 @@ jobs: env: # copy GitHub Secrets into environment variables for the tests to access NREL_API_KEY: ${{ secrets.NRELAPIKEY }} + SOLARANYWHERE_API_KEY: ${{ secrets.SOLARANYWHERE_API_KEY }} BSRN_FTP_USERNAME: ${{ secrets.BSRN_FTP_USERNAME }} BSRN_FTP_PASSWORD: ${{ secrets.BSRN_FTP_PASSWORD }} run: pytest pvlib/tests/iotools pvlib/tests/test_forecast.py --cov=./ --cov-report=xml --remote-data From 2d5271949d5fe15125c1d1f0a065109d9bbffe7b Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:14:45 +0200 Subject: [PATCH 10/31] Add test coverage --- ...103 Lat_44_4675 Lon_-73_2075 SA format.csv | 578 ++++++++++++++++++ ... Year Lat_44_465 Lon_-73_205 SA format.csv | 74 +++ pvlib/iotools/solaranywhere.py | 7 +- pvlib/tests/conftest.py | 13 + pvlib/tests/iotools/test_solaranywhere.py | 225 +++++++ 5 files changed, 896 insertions(+), 1 deletion(-) create mode 100644 pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv create mode 100644 pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv create mode 100644 pvlib/tests/iotools/test_solaranywhere.py diff --git a/pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv b/pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv new file mode 100644 index 0000000000..bcf41e46fd --- /dev/null +++ b/pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv @@ -0,0 +1,578 @@ +0,Burlington United States,NA,-5,44.4675,-73.2075,41,"Data Version: 3.6 / Type: Timeseries / LatLon Resolution: 0.005 / Time Resolution: 5 minutes / Averaging Method: End of Period / TD: Yes / Copyright 2010-2022 Clean Power Research®, L.L.C. DownloadID=adb3c86a-b11f-4263-a2db-8d2626da9390" +ObservationTime(LST),Global Horizontal Irradiance (GHI) W/m2,Direct Normal Irradiance (DNI) W/m2,AmbientTemperature (deg C),WindSpeed (m/s),Relative Humidity (%),Liquid Precipitation (kg/m2),Solid Precipitation (kg/m2),Snow Depth (m),Clear Sky GHI,Clear Sky DNI,Clear Sky DHI,IrradianceObservationType,LeadTime,DataVersion,ObservationTime(GMT),Diffuse Horizontal Irradiance (DIF) W/m2,AmbientTemperatureObservationType,WindSpeedObservationType,Albedo,Particulate Matter 10 (µg/m3),Particulate Matter 2.5 (µg/m3) +01/01/2021 00:05,0,0,-2,2,73,0.0007,0,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:05,0,O,O,0.6,4.5,5.8 +01/01/2021 00:10,0,0,-2,2,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:10,0,O,O,0.6,4.5,5.8 +01/01/2021 00:15,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:15,0,O,O,0.6,4.5,5.8 +01/01/2021 00:20,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:20,0,O,O,0.6,4.5,5.8 +01/01/2021 00:25,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:25,0,O,O,0.6,4.5,5.8 +01/01/2021 00:30,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:30,0,O,O,0.6,4.5,5.8 +01/01/2021 00:35,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:35,0,O,O,0.6,4.5,5.8 +01/01/2021 00:40,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:40,0,O,O,0.6,4.5,5.8 +01/01/2021 00:45,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:45,0,O,O,0.6,4.5,5.8 +01/01/2021 00:50,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:50,0,O,O,0.6,4.5,5.8 +01/01/2021 00:55,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 05:55,0,O,O,0.6,4.5,5.8 +01/01/2021 01:00,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:00,0,O,O,0.6,4.5,5.8 +01/01/2021 01:05,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:05,0,O,O,0.6,5.1,6.4 +01/01/2021 01:10,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:10,0,O,O,0.6,5.1,6.4 +01/01/2021 01:15,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:15,0,O,O,0.6,5.1,6.4 +01/01/2021 01:20,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:20,0,O,O,0.6,5.1,6.4 +01/01/2021 01:25,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:25,0,O,O,0.6,5.1,6.4 +01/01/2021 01:30,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:30,0,O,O,0.6,5.1,6.4 +01/01/2021 01:35,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:35,0,O,O,0.6,5.1,6.4 +01/01/2021 01:40,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:40,0,O,O,0.6,5.1,6.4 +01/01/2021 01:45,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:45,0,O,O,0.6,5.1,6.4 +01/01/2021 01:50,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:50,0,O,O,0.6,5.1,6.4 +01/01/2021 01:55,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 06:55,0,O,O,0.6,5.1,6.4 +01/01/2021 02:00,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:00,0,O,O,0.6,5.1,6.4 +01/01/2021 02:05,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:05,0,O,O,0.6,5.1,7 +01/01/2021 02:10,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:10,0,O,O,0.6,5.1,7 +01/01/2021 02:15,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:15,0,O,O,0.6,5.1,7 +01/01/2021 02:20,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:20,0,O,O,0.6,5.1,7 +01/01/2021 02:25,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:25,0,O,O,0.6,5.1,7 +01/01/2021 02:30,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:30,0,O,O,0.6,5.1,7 +01/01/2021 02:35,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:35,0,O,O,0.6,5.1,7 +01/01/2021 02:40,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:40,0,O,O,0.6,5.1,7 +01/01/2021 02:45,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:45,0,O,O,0.6,5.1,7 +01/01/2021 02:50,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:50,0,O,O,0.6,5.1,7 +01/01/2021 02:55,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 07:55,0,O,O,0.6,5.1,7 +01/01/2021 03:00,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:00,0,O,O,0.6,5.1,7 +01/01/2021 03:05,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:05,0,O,O,0.6,5.1,7.6 +01/01/2021 03:10,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:10,0,O,O,0.6,5.1,7.6 +01/01/2021 03:15,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:15,0,O,O,0.6,5.1,7.6 +01/01/2021 03:20,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:20,0,O,O,0.6,5.1,7.6 +01/01/2021 03:25,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:25,0,O,O,0.6,5.1,7.6 +01/01/2021 03:30,0,0,-2,2,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:30,0,O,O,0.6,5.1,7.6 +01/01/2021 03:35,0,0,-2,2,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:35,0,O,O,0.6,5.1,7.6 +01/01/2021 03:40,0,0,-2,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:40,0,O,O,0.6,5.1,7.6 +01/01/2021 03:45,0,0,-2,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:45,0,O,O,0.6,5.1,7.6 +01/01/2021 03:50,0,0,-2,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:50,0,O,O,0.6,5.1,7.6 +01/01/2021 03:55,0,0,-2,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 08:55,0,O,O,0.6,5.1,7.6 +01/01/2021 04:00,0,0,-2,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:00,0,O,O,0.6,5.1,7.6 +01/01/2021 04:05,0,0,-2,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:05,0,O,O,0.6,6.2,8 +01/01/2021 04:10,0,0,-2,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:10,0,O,O,0.6,6.2,8 +01/01/2021 04:15,0,0,-2,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:15,0,O,O,0.6,6.2,8 +01/01/2021 04:20,0,0,-2,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:20,0,O,O,0.6,6.2,8 +01/01/2021 04:25,0,0,-2,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:25,0,O,O,0.6,6.2,8 +01/01/2021 04:30,0,0,-2,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:30,0,O,O,0.6,6.2,8 +01/01/2021 04:35,0,0,-2,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:35,0,O,O,0.6,6.2,8 +01/01/2021 04:40,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:40,0,O,O,0.6,6.2,8 +01/01/2021 04:45,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:45,0,O,O,0.6,6.2,8 +01/01/2021 04:50,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:50,0,O,O,0.6,6.2,8 +01/01/2021 04:55,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 09:55,0,O,O,0.6,6.2,8 +01/01/2021 05:00,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:00,0,O,O,0.6,6.2,8 +01/01/2021 05:05,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:05,0,O,O,0.6,6.2,8.5 +01/01/2021 05:10,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:10,0,O,O,0.6,6.2,8.5 +01/01/2021 05:15,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:15,0,O,O,0.6,6.2,8.5 +01/01/2021 05:20,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:20,0,O,O,0.6,6.2,8.5 +01/01/2021 05:25,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:25,0,O,O,0.6,6.2,8.5 +01/01/2021 05:30,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:30,0,O,O,0.6,6.2,8.5 +01/01/2021 05:35,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:35,0,O,O,0.6,6.2,8.5 +01/01/2021 05:40,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:40,0,O,O,0.6,6.2,8.5 +01/01/2021 05:45,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:45,0,O,O,0.6,6.2,8.5 +01/01/2021 05:50,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:50,0,O,O,0.6,6.2,8.5 +01/01/2021 05:55,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 10:55,0,O,O,0.6,6.2,8.5 +01/01/2021 06:00,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:00,0,O,O,0.6,6.2,8.5 +01/01/2021 06:05,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:05,0,O,O,0.6,6.2,8.8 +01/01/2021 06:10,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:10,0,O,O,0.6,6.2,8.8 +01/01/2021 06:15,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:15,0,O,O,0.6,6.2,8.8 +01/01/2021 06:20,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:20,0,O,O,0.6,6.2,8.8 +01/01/2021 06:25,0,0,-3,1,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:25,0,O,O,0.6,6.2,8.8 +01/01/2021 06:30,0,0,-3,1,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:30,0,O,O,0.6,6.2,8.8 +01/01/2021 06:35,0,0,-3,0,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:35,0,O,O,0.6,6.2,8.8 +01/01/2021 06:40,0,0,-3,0,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:40,0,O,O,0.6,6.2,8.8 +01/01/2021 06:45,0,0,-3,0,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:45,0,O,O,0.6,6.2,8.8 +01/01/2021 06:50,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:50,0,O,O,0.6,6.2,8.8 +01/01/2021 06:55,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 11:55,0,O,O,0.6,6.2,8.8 +01/01/2021 07:00,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 12:00,0,O,O,0.6,6.2,8.8 +01/01/2021 07:05,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 12:05,0,O,O,0.6,6.9,8.9 +01/01/2021 07:10,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 12:10,0,O,O,0.6,6.9,8.9 +01/01/2021 07:15,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 12:15,0,O,O,0.6,6.9,8.9 +01/01/2021 07:20,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 12:20,0,O,O,0.6,6.9,8.9 +01/01/2021 07:25,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 12:25,0,O,O,0.6,6.9,8.9 +01/01/2021 07:30,0,0,-3,0,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 12:30,0,O,O,0.6,6.9,8.9 +01/01/2021 07:35,0,0,-3,0,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 12:35,0,O,O,0.6,6.9,8.9 +01/01/2021 07:40,0,0,-3,1,74,0,0.0017,0,0,112,0,AN,,SolarAnywhere3_6,01/01/2021 12:40,0,O,O,0.6,6.9,8.9 +01/01/2021 07:45,1,0,-3,1,74,0,0.0017,0,5,170,2,AD,,SolarAnywhere3_6,01/01/2021 12:45,1,O,O,0.6,6.9,8.9 +01/01/2021 07:50,5,0,-3,1,74,0,0.0017,0,14,225,6,AD,,SolarAnywhere3_6,01/01/2021 12:50,5,O,O,0.6,6.9,8.9 +01/01/2021 07:55,9,0,-3,1,74,0,0.0017,0,24,274,11,AD,,SolarAnywhere3_6,01/01/2021 12:55,9,O,O,0.6,6.9,8.9 +01/01/2021 08:00,17,0,-3,1,74,0,0.0017,0,36,316,18,AD,,SolarAnywhere3_6,01/01/2021 13:00,17,O,O,0.6,6.9,8.9 +01/01/2021 08:05,15,0,-3,1,74,0,0.0017,0,47,336,23,AD,,SolarAnywhere3_6,01/01/2021 13:05,15,O,O,0.6,6.9,8.8 +01/01/2021 08:10,20,0,-3,1,73,0,0.0017,0,59,372,28,AD,,SolarAnywhere3_6,01/01/2021 13:10,20,O,O,0.6,6.9,8.8 +01/01/2021 08:15,23,0,-2,1,72,0,0.0017,0,71,407,32,AD,,SolarAnywhere3_6,01/01/2021 13:15,23,O,O,0.6,6.9,8.8 +01/01/2021 08:20,26,0,-2,1,71,0,0.0017,0,84,440,37,AD,,SolarAnywhere3_6,01/01/2021 13:20,26,O,O,0.6,6.9,8.8 +01/01/2021 08:25,28,0,-2,1,70,0,0.0017,0,96,471,40,AD,,SolarAnywhere3_6,01/01/2021 13:25,28,O,O,0.6,6.9,8.8 +01/01/2021 08:30,42,0,-2,1,69,0,0.0017,0,109,500,44,AD,,SolarAnywhere3_6,01/01/2021 13:30,42,O,O,0.6,6.9,8.8 +01/01/2021 08:35,31,0,-2,1,68,0,0.0017,0,121,526,47,AD,,SolarAnywhere3_6,01/01/2021 13:35,31,O,O,0.6,6.9,8.8 +01/01/2021 08:40,35,0,-1,1,67,0,0.0017,0,134,551,50,AD,,SolarAnywhere3_6,01/01/2021 13:40,35,O,O,0.6,6.9,8.8 +01/01/2021 08:45,37,0,-1,1,67,0,0.0017,0,146,574,53,AD,,SolarAnywhere3_6,01/01/2021 13:45,37,O,O,0.6,6.9,8.8 +01/01/2021 08:50,37,0,-1,1,66,0,0.0017,0,159,595,56,AD,,SolarAnywhere3_6,01/01/2021 13:50,37,O,O,0.6,6.9,8.8 +01/01/2021 08:55,40,0,0,1,65,0,0.0017,0,171,614,58,AD,,SolarAnywhere3_6,01/01/2021 13:55,40,O,O,0.6,6.9,8.8 +01/01/2021 09:00,39,0,0,1,64,0,0.0017,0,183,632,61,AD,,SolarAnywhere3_6,01/01/2021 14:00,39,O,O,0.6,6.9,8.8 +01/01/2021 09:05,41,0,0,1,63,0,0.0017,0,194,638,64,AD,,SolarAnywhere3_6,01/01/2021 14:05,41,O,O,0.6,6.9,5.6 +01/01/2021 09:10,63,0,0,1,63,0,0.0017,0,206,654,66,AD,,SolarAnywhere3_6,01/01/2021 14:10,63,O,O,0.6,6.9,5.6 +01/01/2021 09:15,64,0,0,1,63,0,0.0017,0,217,668,68,AD,,SolarAnywhere3_6,01/01/2021 14:15,64,O,O,0.6,6.9,5.6 +01/01/2021 09:20,51,0,0,1,63,0,0.0017,0,228,682,70,AD,,SolarAnywhere3_6,01/01/2021 14:20,51,O,O,0.6,6.9,5.6 +01/01/2021 09:25,51,0,0,1,63,0,0.0017,0,240,695,72,AD,,SolarAnywhere3_6,01/01/2021 14:25,51,O,O,0.6,6.9,5.6 +01/01/2021 09:30,76,0,0,1,63,0,0.0017,0,250,706,74,AD,,SolarAnywhere3_6,01/01/2021 14:30,76,O,O,0.6,6.9,5.6 +01/01/2021 09:35,80,0,0,1,63,0,0.0017,0,261,717,76,AD,,SolarAnywhere3_6,01/01/2021 14:35,80,O,O,0.6,6.9,5.6 +01/01/2021 09:40,82,0,0,1,63,0,0.0017,0,271,728,77,AD,,SolarAnywhere3_6,01/01/2021 14:40,82,O,O,0.6,6.9,5.6 +01/01/2021 09:45,65,0,0,1,63,0,0.0017,0,281,737,79,AD,,SolarAnywhere3_6,01/01/2021 14:45,65,O,O,0.6,6.9,5.6 +01/01/2021 09:50,63,0,0,1,63,0,0.0017,0,291,746,80,AD,,SolarAnywhere3_6,01/01/2021 14:50,63,O,O,0.6,6.9,5.6 +01/01/2021 09:55,73,0,0,1,63,0,0.0017,0,300,755,81,AD,,SolarAnywhere3_6,01/01/2021 14:55,73,O,O,0.6,6.9,5.6 +01/01/2021 10:00,95,0,0,1,63,0,0.0017,0,310,762,84,AD,,SolarAnywhere3_6,01/01/2021 15:00,95,O,O,0.6,6.9,5.6 +01/01/2021 10:05,94,0,0,1,63,0,0.0017,0,318,761,87,AD,,SolarAnywhere3_6,01/01/2021 15:05,94,O,O,0.6,9.2,4.6 +01/01/2021 10:10,84,0,0,1,63,0,0.0017,0,326,768,87,AD,,SolarAnywhere3_6,01/01/2021 15:10,84,O,O,0.6,9.2,4.6 +01/01/2021 10:15,127,9,0,1,63,0,0.0017,0,334,774,89,AD,,SolarAnywhere3_6,01/01/2021 15:15,124,O,O,0.6,9.2,4.6 +01/01/2021 10:20,172,69,0,1,63,0,0.0017,0,342,780,90,AD,,SolarAnywhere3_6,01/01/2021 15:20,150,O,O,0.6,9.2,4.6 +01/01/2021 10:25,218,252,0,1,63,0,0.0017,0,350,786,91,AD,,SolarAnywhere3_6,01/01/2021 15:25,135,O,O,0.6,9.2,4.6 +01/01/2021 10:30,237,313,0,1,63,0,0.0017,0,357,791,92,AD,,SolarAnywhere3_6,01/01/2021 15:30,132,O,O,0.6,9.2,4.6 +01/01/2021 10:35,251,356,0,1,63,0,0.0017,0,364,796,94,AD,,SolarAnywhere3_6,01/01/2021 15:35,130,O,O,0.6,9.2,4.6 +01/01/2021 10:40,274,455,0,1,62,0,0.0017,0,370,801,94,AD,,SolarAnywhere3_6,01/01/2021 15:40,117,O,O,0.6,9.2,4.6 +01/01/2021 10:45,290,541,0,1,62,0,0.0017,0,377,805,96,AD,,SolarAnywhere3_6,01/01/2021 15:45,101,O,O,0.6,9.2,4.6 +01/01/2021 10:50,276,424,0,1,62,0,0.0017,0,382,809,96,AD,,SolarAnywhere3_6,01/01/2021 15:50,126,O,O,0.6,9.2,4.6 +01/01/2021 10:55,274,397,0,1,62,0,0.0017,0,388,812,97,AD,,SolarAnywhere3_6,01/01/2021 15:55,132,O,O,0.6,9.2,4.6 +01/01/2021 11:00,220,119,0,1,62,0,0.0017,0,393,816,98,AD,,SolarAnywhere3_6,01/01/2021 16:00,177,O,O,0.6,9.2,4.6 +01/01/2021 11:05,199,75,0,1,62,0,0.0017,0,397,817,99,AD,,SolarAnywhere3_6,01/01/2021 16:05,172,O,O,0.6,9.2,4.3 +01/01/2021 11:10,179,44,0,1,62,0,0.0017,0,402,820,100,AD,,SolarAnywhere3_6,01/01/2021 16:10,163,O,O,0.6,9.2,4.3 +01/01/2021 11:15,163,22,0,1,61,0,0.0017,0,405,822,100,AD,,SolarAnywhere3_6,01/01/2021 16:15,155,O,O,0.6,9.2,4.3 +01/01/2021 11:20,159,18,0,1,61,0,0.0017,0,409,824,101,AD,,SolarAnywhere3_6,01/01/2021 16:20,152,O,O,0.6,9.2,4.3 +01/01/2021 11:25,189,51,0,1,60,0,0.0017,0,412,826,102,AD,,SolarAnywhere3_6,01/01/2021 16:25,170,O,O,0.6,9.2,4.3 +01/01/2021 11:30,154,13,0,1,60,0,0.0017,0,415,828,102,AD,,SolarAnywhere3_6,01/01/2021 16:30,149,O,O,0.6,9.2,4.3 +01/01/2021 11:35,161,17,0,0,59,0,0.0017,0,417,829,102,AD,,SolarAnywhere3_6,01/01/2021 16:35,154,O,O,0.6,9.2,4.3 +01/01/2021 11:40,179,32,0,0,59,0,0.0017,0,419,830,103,AD,,SolarAnywhere3_6,01/01/2021 16:40,167,O,O,0.6,9.2,4.3 +01/01/2021 11:45,151,10,0,0,59,0,0.0017,0,421,831,104,AD,,SolarAnywhere3_6,01/01/2021 16:45,147,O,O,0.6,9.2,4.3 +01/01/2021 11:50,198,57,0,0,58,0,0.0017,0,422,832,104,AD,,SolarAnywhere3_6,01/01/2021 16:50,176,O,O,0.6,9.2,4.3 +01/01/2021 11:55,163,17,0,0,58,0,0.0017,0,422,832,104,AD,,SolarAnywhere3_6,01/01/2021 16:55,156,O,O,0.6,9.2,4.3 +01/01/2021 12:00,176,28,0,0,57,0,0.0017,0,423,832,104,AD,,SolarAnywhere3_6,01/01/2021 17:00,165,O,O,0.6,9.2,4.3 +01/01/2021 12:05,146,7,0,0,57,0,0.0017,0,423,831,105,AD,,SolarAnywhere3_6,01/01/2021 17:05,143,O,O,0.6,9.2,4.2 +01/01/2021 12:10,165,19,0,0,57,0,0.0017,0,422,830,105,AD,,SolarAnywhere3_6,01/01/2021 17:10,158,O,O,0.6,9.2,4.2 +01/01/2021 12:15,222,93,0,0,57,0,0.0017,0,421,830,105,AD,,SolarAnywhere3_6,01/01/2021 17:15,187,O,O,0.6,9.2,4.2 +01/01/2021 12:20,207,70,0,0,57,0,0.0017,0,420,829,105,AD,,SolarAnywhere3_6,01/01/2021 17:20,180,O,O,0.6,9.2,4.2 +01/01/2021 12:25,259,255,0,0,57,0,0.0017,0,418,828,105,AD,,SolarAnywhere3_6,01/01/2021 17:25,162,O,O,0.6,9.2,4.2 +01/01/2021 12:30,276,323,0,0,57,0,0.0017,0,416,827,104,AD,,SolarAnywhere3_6,01/01/2021 17:30,154,O,O,0.6,9.2,4.2 +01/01/2021 12:35,271,309,0,0,57,0,0.0017,0,414,825,105,AD,,SolarAnywhere3_6,01/01/2021 17:35,155,O,O,0.6,9.2,4.2 +01/01/2021 12:40,225,109,1,1,57,0,0.0017,0,411,824,104,AD,,SolarAnywhere3_6,01/01/2021 17:40,184,O,O,0.6,9.2,4.2 +01/01/2021 12:45,229,120,1,1,57,0,0.0017,0,408,822,104,AD,,SolarAnywhere3_6,01/01/2021 17:45,185,O,O,0.6,9.2,4.2 +01/01/2021 12:50,219,104,1,1,57,0,0.0017,0,404,819,104,AD,,SolarAnywhere3_6,01/01/2021 17:50,181,O,O,0.6,9.2,4.2 +01/01/2021 12:55,176,41,1,1,57,0,0.0017,0,400,817,103,AD,,SolarAnywhere3_6,01/01/2021 17:55,161,O,O,0.6,9.2,4.2 +01/01/2021 13:00,170,33,1,1,57,0,0.0017,0,396,814,104,AD,,SolarAnywhere3_6,01/01/2021 18:00,158,O,O,0.6,9.2,4.2 +01/01/2021 13:05,159,24,1,1,57,0,0.0017,0,391,809,104,AD,,SolarAnywhere3_6,01/01/2021 18:05,150,O,O,0.6,7.2,4.1 +01/01/2021 13:10,181,55,1,1,56,0,0.0017,0,385,806,102,AD,,SolarAnywhere3_6,01/01/2021 18:10,162,O,O,0.6,7.2,4.1 +01/01/2021 13:15,168,41,1,1,56,0,0.0017,0,380,802,102,AD,,SolarAnywhere3_6,01/01/2021 18:15,154,O,O,0.6,7.2,4.1 +01/01/2021 13:20,174,53,1,1,56,0,0.0017,0,374,798,101,AD,,SolarAnywhere3_6,01/01/2021 18:20,156,O,O,0.6,7.2,4.1 +01/01/2021 13:25,179,60,1,1,56,0,0.0017,0,368,794,101,AD,,SolarAnywhere3_6,01/01/2021 18:25,159,O,O,0.6,7.2,4.1 +01/01/2021 13:30,159,36,1,1,56,0,0.0017,0,361,789,100,AD,,SolarAnywhere3_6,01/01/2021 18:30,147,O,O,0.6,7.2,4.1 +01/01/2021 13:35,159,40,1,1,56,0,0.0017,0,354,784,99,AD,,SolarAnywhere3_6,01/01/2021 18:35,146,O,O,0.6,7.2,4.1 +01/01/2021 13:40,139,14,1,1,56,0,0.0017,0,347,779,98,AD,,SolarAnywhere3_6,01/01/2021 18:40,134,O,O,0.6,7.2,4.1 +01/01/2021 13:45,138,15,1,1,56,0,0.0017,0,339,773,97,AD,,SolarAnywhere3_6,01/01/2021 18:45,133,O,O,0.6,7.2,4.1 +01/01/2021 13:50,152,44,1,1,56,0,0.0017,0,331,767,96,AD,,SolarAnywhere3_6,01/01/2021 18:50,138,O,O,0.6,7.2,4.1 +01/01/2021 13:55,136,19,1,1,56,0,0.0017,0,323,760,95,AD,,SolarAnywhere3_6,01/01/2021 18:55,130,O,O,0.6,7.2,4.1 +01/01/2021 14:00,135,21,1,1,56,0,0.0017,0,314,753,94,AD,,SolarAnywhere3_6,01/01/2021 19:00,129,O,O,0.6,7.2,4.1 +01/01/2021 14:05,129,18,1,1,56,0,0.0017,0,303,726,96,AD,,SolarAnywhere3_6,01/01/2021 19:05,124,O,O,0.6,7.2,4.1 +01/01/2021 14:10,134,38,1,1,56,0,0.0017,0,294,718,95,AD,,SolarAnywhere3_6,01/01/2021 19:10,123,O,O,0.6,7.2,4.1 +01/01/2021 14:15,121,17,1,1,57,0,0.0017,0,285,709,94,AD,,SolarAnywhere3_6,01/01/2021 19:15,116,O,O,0.6,7.2,4.1 +01/01/2021 14:20,116,16,1,1,57,0,0.0017,0,275,699,92,AD,,SolarAnywhere3_6,01/01/2021 19:20,112,O,O,0.6,7.2,4.1 +01/01/2021 14:25,109,13,1,1,57,0,0.0017,0,265,689,91,AD,,SolarAnywhere3_6,01/01/2021 19:25,106,O,O,0.6,7.2,4.1 +01/01/2021 14:30,93,3,1,1,57,0,0.0017,0,254,678,88,AD,,SolarAnywhere3_6,01/01/2021 19:30,92,O,O,0.6,7.2,4.1 +01/01/2021 14:35,88,1,0,1,57,0,0.0017,0,244,666,87,AD,,SolarAnywhere3_6,01/01/2021 19:35,88,O,O,0.6,7.2,4.1 +01/01/2021 14:40,87,3,0,1,57,0,0.0017,0,233,653,85,AD,,SolarAnywhere3_6,01/01/2021 19:40,86,O,O,0.6,7.2,4.1 +01/01/2021 14:45,75,0,0,1,57,0,0.0017,0,222,639,83,AD,,SolarAnywhere3_6,01/01/2021 19:45,75,O,O,0.6,7.2,4.1 +01/01/2021 14:50,57,0,0,1,58,0,0.0017,0,211,625,81,AD,,SolarAnywhere3_6,01/01/2021 19:50,57,O,O,0.6,7.2,4.1 +01/01/2021 14:55,59,0,0,1,58,0,0.0017,0,199,609,79,AD,,SolarAnywhere3_6,01/01/2021 19:55,59,O,O,0.6,7.2,4.1 +01/01/2021 15:00,50,0,0,1,58,0,0.0017,0,187,592,76,AD,,SolarAnywhere3_6,01/01/2021 20:00,50,O,O,0.6,7.2,4.1 +01/01/2021 15:05,54,0,0,1,58,0,0.0017,0,174,551,76,AD,,SolarAnywhere3_6,01/01/2021 20:05,54,O,O,0.6,7.2,4.3 +01/01/2021 15:10,53,0,0,1,59,0,0.0017,0,162,531,74,AD,,SolarAnywhere3_6,01/01/2021 20:10,53,O,O,0.6,7.2,4.3 +01/01/2021 15:15,40,0,0,1,59,0,0.0017,0,150,509,71,AD,,SolarAnywhere3_6,01/01/2021 20:15,40,O,O,0.6,7.2,4.3 +01/01/2021 15:20,22,0,0,1,59,0,0.0017,0,138,486,68,AD,,SolarAnywhere3_6,01/01/2021 20:20,22,O,O,0.6,7.2,4.3 +01/01/2021 15:25,30,0,0,1,60,0,0.0017,0,126,461,64,AD,,SolarAnywhere3_6,01/01/2021 20:25,30,O,O,0.6,7.2,4.3 +01/01/2021 15:30,19,0,0,1,60,0,0.0017,0,113,434,60,AD,,SolarAnywhere3_6,01/01/2021 20:30,19,O,O,0.6,7.2,4.3 +01/01/2021 15:35,24,0,0,2,61,0,0.0017,0,101,405,56,AD,,SolarAnywhere3_6,01/01/2021 20:35,24,O,O,0.6,7.2,4.3 +01/01/2021 15:40,19,0,0,2,61,0,0.0017,0,89,374,52,AD,,SolarAnywhere3_6,01/01/2021 20:40,19,O,O,0.6,7.2,4.3 +01/01/2021 15:45,19,0,0,2,61,0,0.0017,0,77,341,47,AD,,SolarAnywhere3_6,01/01/2021 20:45,19,O,O,0.6,7.2,4.3 +01/01/2021 15:50,13,0,0,2,62,0,0.0017,0,64,306,41,AD,,SolarAnywhere3_6,01/01/2021 20:50,13,O,O,0.6,7.2,4.3 +01/01/2021 15:55,10,0,0,2,62,0,0.0017,0,53,270,36,AD,,SolarAnywhere3_6,01/01/2021 20:55,10,O,O,0.6,7.2,4.3 +01/01/2021 16:00,8,0,0,2,63,0,0.0017,0,41,232,29,AD,,SolarAnywhere3_6,01/01/2021 21:00,8,O,O,0.6,7.2,4.3 +01/01/2021 16:05,7,0,0,2,63,0,0.0017,0,29,171,22,AD,,SolarAnywhere3_6,01/01/2021 21:05,7,O,O,0.6,7.3,4.5 +01/01/2021 16:10,6,0,0,2,64,0,0.0017,0,19,130,16,AD,,SolarAnywhere3_6,01/01/2021 21:10,6,O,O,0.6,7.3,4.5 +01/01/2021 16:15,2,0,0,2,64,0,0.0017,0,10,89,9,AD,,SolarAnywhere3_6,01/01/2021 21:15,2,O,O,0.6,7.3,4.5 +01/01/2021 16:20,2,0,0,2,65,0,0.0017,0,2,50,2,AD,,SolarAnywhere3_6,01/01/2021 21:20,2,O,O,0.6,7.3,4.5 +01/01/2021 16:25,0,0,0,2,65,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 21:25,0,O,O,0.6,7.3,4.5 +01/01/2021 16:30,0,0,0,2,66,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 21:30,0,O,O,0.6,7.3,4.5 +01/01/2021 16:35,0,0,0,2,66,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 21:35,0,O,O,0.6,7.3,4.5 +01/01/2021 16:40,0,0,-1,2,67,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 21:40,0,O,O,0.6,7.3,4.5 +01/01/2021 16:45,0,0,-1,2,67,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 21:45,0,O,O,0.6,7.3,4.5 +01/01/2021 16:50,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 21:50,0,O,O,0.6,7.3,4.5 +01/01/2021 16:55,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 21:55,0,O,O,0.6,7.3,4.5 +01/01/2021 17:00,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:00,0,O,O,0.6,7.3,4.5 +01/01/2021 17:05,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:05,0,O,O,0.6,7.3,4.7 +01/01/2021 17:10,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:10,0,O,O,0.6,7.3,4.7 +01/01/2021 17:15,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:15,0,O,O,0.6,7.3,4.7 +01/01/2021 17:20,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:20,0,O,O,0.6,7.3,4.7 +01/01/2021 17:25,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:25,0,O,O,0.6,7.3,4.7 +01/01/2021 17:30,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:30,0,O,O,0.6,7.3,4.7 +01/01/2021 17:35,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:35,0,O,O,0.6,7.3,4.7 +01/01/2021 17:40,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:40,0,O,O,0.6,7.3,4.7 +01/01/2021 17:45,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:45,0,O,O,0.6,7.3,4.7 +01/01/2021 17:50,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:50,0,O,O,0.6,7.3,4.7 +01/01/2021 17:55,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 22:55,0,O,O,0.6,7.3,4.7 +01/01/2021 18:00,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:00,0,O,O,0.6,7.3,4.7 +01/01/2021 18:05,0,0,-1,2,71,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:05,0,O,O,0.6,7.3,5 +01/01/2021 18:10,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:10,0,O,O,0.6,7.3,5 +01/01/2021 18:15,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:15,0,O,O,0.6,7.3,5 +01/01/2021 18:20,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:20,0,O,O,0.6,7.3,5 +01/01/2021 18:25,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:25,0,O,O,0.6,7.3,5 +01/01/2021 18:30,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:30,0,O,O,0.6,7.3,5 +01/01/2021 18:35,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:35,0,O,O,0.6,7.3,5 +01/01/2021 18:40,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:40,0,O,O,0.6,7.3,5 +01/01/2021 18:45,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:45,0,O,O,0.6,7.3,5 +01/01/2021 18:50,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:50,0,O,O,0.6,7.3,5 +01/01/2021 18:55,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/01/2021 23:55,0,O,O,0.6,7.3,5 +01/01/2021 19:00,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:00,0,O,O,0.6,7.3,5 +01/01/2021 19:05,0,0,-1,2,70,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:05,0,O,O,0.6,8.9,5.3 +01/01/2021 19:10,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:10,0,O,O,0.6,8.9,5.3 +01/01/2021 19:15,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:15,0,O,O,0.6,8.9,5.3 +01/01/2021 19:20,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:20,0,O,O,0.6,8.9,5.3 +01/01/2021 19:25,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:25,0,O,O,0.6,8.9,5.3 +01/01/2021 19:30,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:30,0,O,O,0.6,8.9,5.3 +01/01/2021 19:35,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:35,0,O,O,0.6,8.9,5.3 +01/01/2021 19:40,0,0,-1,2,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:40,0,O,O,0.6,8.9,5.3 +01/01/2021 19:45,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:45,0,O,O,0.6,8.9,5.3 +01/01/2021 19:50,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:50,0,O,O,0.6,8.9,5.3 +01/01/2021 19:55,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 00:55,0,O,O,0.6,8.9,5.3 +01/01/2021 20:00,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:00,0,O,O,0.6,8.9,5.3 +01/01/2021 20:05,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:05,0,O,O,0.6,8.9,5.7 +01/01/2021 20:10,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:10,0,O,O,0.6,8.9,5.7 +01/01/2021 20:15,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:15,0,O,O,0.6,8.9,5.7 +01/01/2021 20:20,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:20,0,O,O,0.6,8.9,5.7 +01/01/2021 20:25,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:25,0,O,O,0.6,8.9,5.7 +01/01/2021 20:30,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:30,0,O,O,0.6,8.9,5.7 +01/01/2021 20:35,0,0,-1,2,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:35,0,O,O,0.6,8.9,5.7 +01/01/2021 20:40,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:40,0,O,O,0.6,8.9,5.7 +01/01/2021 20:45,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:45,0,O,O,0.6,8.9,5.7 +01/01/2021 20:50,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:50,0,O,O,0.6,8.9,5.7 +01/01/2021 20:55,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 01:55,0,O,O,0.6,8.9,5.7 +01/01/2021 21:00,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:00,0,O,O,0.6,8.9,5.7 +01/01/2021 21:05,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:05,0,O,O,0.6,8.9,6.2 +01/01/2021 21:10,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:10,0,O,O,0.6,8.9,6.2 +01/01/2021 21:15,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:15,0,O,O,0.6,8.9,6.2 +01/01/2021 21:20,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:20,0,O,O,0.6,8.9,6.2 +01/01/2021 21:25,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:25,0,O,O,0.6,8.9,6.2 +01/01/2021 21:30,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:30,0,O,O,0.6,8.9,6.2 +01/01/2021 21:35,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:35,0,O,O,0.6,8.9,6.2 +01/01/2021 21:40,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:40,0,O,O,0.6,8.9,6.2 +01/01/2021 21:45,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:45,0,O,O,0.6,8.9,6.2 +01/01/2021 21:50,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:50,0,O,O,0.6,8.9,6.2 +01/01/2021 21:55,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 02:55,0,O,O,0.6,8.9,6.2 +01/01/2021 22:00,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:00,0,O,O,0.6,8.9,6.2 +01/01/2021 22:05,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:05,0,O,O,0.6,12,6.6 +01/01/2021 22:10,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:10,0,O,O,0.6,12,6.6 +01/01/2021 22:15,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:15,0,O,O,0.6,12,6.6 +01/01/2021 22:20,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:20,0,O,O,0.6,12,6.6 +01/01/2021 22:25,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:25,0,O,O,0.6,12,6.6 +01/01/2021 22:30,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:30,0,O,O,0.6,12,6.6 +01/01/2021 22:35,0,0,-1,3,68,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:35,0,O,O,0.6,12,6.6 +01/01/2021 22:40,0,0,-1,3,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:40,0,O,O,0.6,12,6.6 +01/01/2021 22:45,0,0,-1,3,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:45,0,O,O,0.6,12,6.6 +01/01/2021 22:50,0,0,-1,3,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:50,0,O,O,0.6,12,6.6 +01/01/2021 22:55,0,0,-1,3,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 03:55,0,O,O,0.6,12,6.6 +01/01/2021 23:00,0,0,-1,3,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:00,0,O,O,0.6,12,6.6 +01/01/2021 23:05,0,0,-1,3,69,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:05,0,O,O,0.6,12,7 +01/01/2021 23:10,0,0,-1,3,71,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:10,0,O,O,0.6,12,7 +01/01/2021 23:15,0,0,-1,3,72,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:15,0,O,O,0.6,12,7 +01/01/2021 23:20,0,0,-1,3,73,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:20,0,O,O,0.6,12,7 +01/01/2021 23:25,0,0,-1,3,74,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:25,0,O,O,0.6,12,7 +01/01/2021 23:30,0,0,-1,3,75,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:30,0,O,O,0.6,12,7 +01/01/2021 23:35,0,0,-1,3,77,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:35,0,O,O,0.6,12,7 +01/01/2021 23:40,0,0,-1,3,78,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:40,0,O,O,0.6,12,7 +01/01/2021 23:45,0,0,-1,3,79,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:45,0,O,O,0.6,12,7 +01/01/2021 23:50,0,0,-1,3,80,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:50,0,O,O,0.6,12,7 +01/01/2021 23:55,0,0,-1,3,82,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 04:55,0,O,O,0.6,12,7 +01/02/2021 00:00,0,0,-1,3,83,0,0.0017,0,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:00,0,O,O,0.6,12,7 +01/02/2021 00:05,0,0,-1,3,84,0,0.0017,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:05,0,O,O,0.6,12,7.4 +01/02/2021 00:10,0,0,-1,3,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:10,0,O,O,0.6,12,7.4 +01/02/2021 00:15,0,0,-1,3,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:15,0,O,O,0.6,12,7.4 +01/02/2021 00:20,0,0,-1,3,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:20,0,O,O,0.6,12,7.4 +01/02/2021 00:25,0,0,-1,3,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:25,0,O,O,0.6,12,7.4 +01/02/2021 00:30,0,0,-1,3,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:30,0,O,O,0.6,12,7.4 +01/02/2021 00:35,0,0,-2,3,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:35,0,O,O,0.6,12,7.4 +01/02/2021 00:40,0,0,-2,3,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:40,0,O,O,0.6,12,7.4 +01/02/2021 00:45,0,0,-2,3,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:45,0,O,O,0.6,12,7.4 +01/02/2021 00:50,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:50,0,O,O,0.6,12,7.4 +01/02/2021 00:55,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 05:55,0,O,O,0.6,12,7.4 +01/02/2021 01:00,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:00,0,O,O,0.6,12,7.4 +01/02/2021 01:05,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:05,0,O,O,0.6,13.7,7.7 +01/02/2021 01:10,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:10,0,O,O,0.6,13.7,7.7 +01/02/2021 01:15,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:15,0,O,O,0.6,13.7,7.7 +01/02/2021 01:20,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:20,0,O,O,0.6,13.7,7.7 +01/02/2021 01:25,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:25,0,O,O,0.6,13.7,7.7 +01/02/2021 01:30,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:30,0,O,O,0.6,13.7,7.7 +01/02/2021 01:35,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:35,0,O,O,0.6,13.7,7.7 +01/02/2021 01:40,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:40,0,O,O,0.6,13.7,7.7 +01/02/2021 01:45,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:45,0,O,O,0.6,13.7,7.7 +01/02/2021 01:50,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:50,0,O,O,0.6,13.7,7.7 +01/02/2021 01:55,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 06:55,0,O,O,0.6,13.7,7.7 +01/02/2021 02:00,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:00,0,O,O,0.6,13.7,7.7 +01/02/2021 02:05,0,0,-2,3,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:05,0,O,O,0.6,13.7,7.6 +01/02/2021 02:10,0,0,-2,3,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:10,0,O,O,0.6,13.7,7.6 +01/02/2021 02:15,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:15,0,O,O,0.6,13.7,7.6 +01/02/2021 02:20,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:20,0,O,O,0.6,13.7,7.6 +01/02/2021 02:25,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:25,0,O,O,0.6,13.7,7.6 +01/02/2021 02:30,0,0,-2,3,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:30,0,O,O,0.6,13.7,7.6 +01/02/2021 02:35,0,0,-2,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:35,0,O,O,0.6,13.7,7.6 +01/02/2021 02:40,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:40,0,O,O,0.6,13.7,7.6 +01/02/2021 02:45,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:45,0,O,O,0.6,13.7,7.6 +01/02/2021 02:50,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:50,0,O,O,0.6,13.7,7.6 +01/02/2021 02:55,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 07:55,0,O,O,0.6,13.7,7.6 +01/02/2021 03:00,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:00,0,O,O,0.6,13.7,7.6 +01/02/2021 03:05,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:05,0,O,O,0.6,13.7,7.3 +01/02/2021 03:10,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:10,0,O,O,0.6,13.7,7.3 +01/02/2021 03:15,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:15,0,O,O,0.6,13.7,7.3 +01/02/2021 03:20,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:20,0,O,O,0.6,13.7,7.3 +01/02/2021 03:25,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:25,0,O,O,0.6,13.7,7.3 +01/02/2021 03:30,0,0,-1,4,87,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:30,0,O,O,0.6,13.7,7.3 +01/02/2021 03:35,0,0,-2,4,88,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:35,0,O,O,0.6,13.7,7.3 +01/02/2021 03:40,0,0,-2,4,88,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:40,0,O,O,0.6,13.7,7.3 +01/02/2021 03:45,0,0,-2,4,88,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:45,0,O,O,0.6,13.7,7.3 +01/02/2021 03:50,0,0,-2,4,88,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:50,0,O,O,0.6,13.7,7.3 +01/02/2021 03:55,0,0,-2,4,88,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 08:55,0,O,O,0.6,13.7,7.3 +01/02/2021 04:00,0,0,-2,4,88,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:00,0,O,O,0.6,13.7,7.3 +01/02/2021 04:05,0,0,-2,4,88,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:05,0,O,O,0.6,9.9,7 +01/02/2021 04:10,0,0,-2,4,88,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:10,0,O,O,0.6,9.9,7 +01/02/2021 04:15,0,0,-2,4,89,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:15,0,O,O,0.6,9.9,7 +01/02/2021 04:20,0,0,-2,4,89,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:20,0,O,O,0.6,9.9,7 +01/02/2021 04:25,0,0,-2,4,89,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:25,0,O,O,0.6,9.9,7 +01/02/2021 04:30,0,0,-2,4,90,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:30,0,O,O,0.6,9.9,7 +01/02/2021 04:35,0,0,-2,4,90,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:35,0,O,O,0.6,9.9,7 +01/02/2021 04:40,0,0,-1,3,91,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:40,0,O,O,0.6,9.9,7 +01/02/2021 04:45,0,0,-1,3,91,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:45,0,O,O,0.6,9.9,7 +01/02/2021 04:50,0,0,-1,3,91,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:50,0,O,O,0.6,9.9,7 +01/02/2021 04:55,0,0,-1,3,92,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 09:55,0,O,O,0.6,9.9,7 +01/02/2021 05:00,0,0,-1,3,92,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:00,0,O,O,0.6,9.9,7 +01/02/2021 05:05,0,0,-1,3,92,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:05,0,O,O,0.6,9.9,6.2 +01/02/2021 05:10,0,0,-1,3,93,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:10,0,O,O,0.6,9.9,6.2 +01/02/2021 05:15,0,0,-1,3,93,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:15,0,O,O,0.6,9.9,6.2 +01/02/2021 05:20,0,0,-1,3,93,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:20,0,O,O,0.6,9.9,6.2 +01/02/2021 05:25,0,0,-1,3,93,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:25,0,O,O,0.6,9.9,6.2 +01/02/2021 05:30,0,0,-1,3,93,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:30,0,O,O,0.6,9.9,6.2 +01/02/2021 05:35,0,0,-1,2,93,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:35,0,O,O,0.6,9.9,6.2 +01/02/2021 05:40,0,0,-1,2,93,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:40,0,O,O,0.6,9.9,6.2 +01/02/2021 05:45,0,0,-1,2,94,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:45,0,O,O,0.6,9.9,6.2 +01/02/2021 05:50,0,0,-1,2,94,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:50,0,O,O,0.6,9.9,6.2 +01/02/2021 05:55,0,0,-1,2,94,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 10:55,0,O,O,0.6,9.9,6.2 +01/02/2021 06:00,0,0,-1,2,94,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:00,0,O,O,0.6,9.9,6.2 +01/02/2021 06:05,0,0,-1,2,94,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:05,0,O,O,0.6,9.9,5.5 +01/02/2021 06:10,0,0,-1,2,94,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:10,0,O,O,0.6,9.9,5.5 +01/02/2021 06:15,0,0,-1,2,94,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:15,0,O,O,0.6,9.9,5.5 +01/02/2021 06:20,0,0,-1,2,94,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:20,0,O,O,0.6,9.9,5.5 +01/02/2021 06:25,0,0,-1,2,95,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:25,0,O,O,0.6,9.9,5.5 +01/02/2021 06:30,0,0,-1,2,95,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:30,0,O,O,0.6,9.9,5.5 +01/02/2021 06:35,0,0,-1,2,95,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:35,0,O,O,0.6,9.9,5.5 +01/02/2021 06:40,0,0,-1,1,95,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:40,0,O,O,0.6,9.9,5.5 +01/02/2021 06:45,0,0,-1,1,95,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:45,0,O,O,0.6,9.9,5.5 +01/02/2021 06:50,0,0,-1,1,95,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:50,0,O,O,0.6,9.9,5.5 +01/02/2021 06:55,0,0,-1,1,95,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 11:55,0,O,O,0.6,9.9,5.5 +01/02/2021 07:00,0,0,-1,1,96,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 12:00,0,O,O,0.6,9.9,5.5 +01/02/2021 07:05,0,0,-1,1,96,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 12:05,0,O,O,0.6,6.3,5.4 +01/02/2021 07:10,0,0,-1,1,96,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 12:10,0,O,O,0.6,6.3,5.4 +01/02/2021 07:15,0,0,-1,1,96,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 12:15,0,O,O,0.6,6.3,5.4 +01/02/2021 07:20,0,0,-1,1,96,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 12:20,0,O,O,0.6,6.3,5.4 +01/02/2021 07:25,0,0,-1,1,96,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 12:25,0,O,O,0.6,6.3,5.4 +01/02/2021 07:30,0,0,-1,1,96,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 12:30,0,O,O,0.6,6.3,5.4 +01/02/2021 07:35,0,0,-1,1,96,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 12:35,0,O,O,0.6,6.3,5.4 +01/02/2021 07:40,0,0,-1,1,96,0.0014,0.033,0.005,0,51,0,AN,,SolarAnywhere3_6,01/02/2021 12:40,0,O,O,0.6,6.3,5.4 +01/02/2021 07:45,1,0,-1,1,96,0.0014,0.033,0.005,5,94,3,AD,,SolarAnywhere3_6,01/02/2021 12:45,1,O,O,0.6,6.3,5.4 +01/02/2021 07:50,5,0,-1,1,96,0.0014,0.033,0.005,13,141,8,AD,,SolarAnywhere3_6,01/02/2021 12:50,5,O,O,0.6,6.3,5.4 +01/02/2021 07:55,10,0,-1,1,96,0.0014,0.033,0.005,23,188,14,AD,,SolarAnywhere3_6,01/02/2021 12:55,10,O,O,0.6,6.3,5.4 +01/02/2021 08:00,22,32,-1,1,96,0.0014,0.033,0.005,34,230,21,AD,,SolarAnywhere3_6,01/02/2021 13:00,20,O,O,0.6,6.3,5.4 +01/02/2021 08:05,20,0,-1,1,96,0.0014,0.033,0.005,45,256,27,AD,,SolarAnywhere3_6,01/02/2021 13:05,20,O,O,0.6,6.3,5.3 +01/02/2021 08:10,14,0,-1,1,96,0.0014,0.033,0.005,57,294,33,AD,,SolarAnywhere3_6,01/02/2021 13:10,14,O,O,0.6,6.3,5.3 +01/02/2021 08:15,19,0,-1,1,96,0.0014,0.033,0.005,69,330,38,AD,,SolarAnywhere3_6,01/02/2021 13:15,19,O,O,0.6,6.3,5.3 +01/02/2021 08:20,21,0,-1,1,95,0.0014,0.033,0.005,81,364,42,AD,,SolarAnywhere3_6,01/02/2021 13:20,21,O,O,0.6,6.3,5.3 +01/02/2021 08:25,23,0,-1,1,95,0.0014,0.033,0.005,93,397,46,AD,,SolarAnywhere3_6,01/02/2021 13:25,23,O,O,0.6,6.3,5.3 +01/02/2021 08:30,55,38,-1,1,95,0.0014,0.033,0.005,106,427,51,AD,,SolarAnywhere3_6,01/02/2021 13:30,50,O,O,0.6,6.3,5.3 +01/02/2021 08:35,80,165,-1,1,95,0.0014,0.033,0.005,118,455,54,AD,,SolarAnywhere3_6,01/02/2021 13:35,57,O,O,0.6,6.3,5.3 +01/02/2021 08:40,98,286,-1,1,95,0.0014,0.033,0.005,131,481,58,AD,,SolarAnywhere3_6,01/02/2021 13:40,55,O,O,0.6,6.3,5.3 +01/02/2021 08:45,112,345,-1,1,95,0.0014,0.033,0.005,143,506,61,AD,,SolarAnywhere3_6,01/02/2021 13:45,56,O,O,0.6,6.3,5.3 +01/02/2021 08:50,74,35,-1,1,95,0.0014,0.033,0.005,155,528,64,AD,,SolarAnywhere3_6,01/02/2021 13:50,68,O,O,0.6,6.3,5.3 +01/02/2021 08:55,116,244,-1,1,95,0.0014,0.033,0.005,167,549,66,AD,,SolarAnywhere3_6,01/02/2021 13:55,71,O,O,0.6,6.3,5.3 +01/02/2021 09:00,87,39,-1,1,95,0.0014,0.033,0.005,179,568,69,AD,,SolarAnywhere3_6,01/02/2021 14:00,79,O,O,0.6,6.3,5.3 +01/02/2021 09:05,55,0,-1,1,94,0.0014,0.033,0.005,190,577,72,AD,,SolarAnywhere3_6,01/02/2021 14:05,55,O,O,0.6,6.3,5.4 +01/02/2021 09:10,83,9,-1,1,95,0.0014,0.033,0.005,202,594,75,AD,,SolarAnywhere3_6,01/02/2021 14:10,81,O,O,0.6,6.3,5.4 +01/02/2021 09:15,59,0,-1,1,95,0.0014,0.033,0.005,213,609,77,AD,,SolarAnywhere3_6,01/02/2021 14:15,59,O,O,0.6,6.3,5.4 +01/02/2021 09:20,62,0,-1,1,95,0.0014,0.033,0.005,225,624,80,AD,,SolarAnywhere3_6,01/02/2021 14:20,62,O,O,0.6,6.3,5.4 +01/02/2021 09:25,63,0,-1,1,95,0.0014,0.033,0.005,236,638,82,AD,,SolarAnywhere3_6,01/02/2021 14:25,63,O,O,0.6,6.3,5.4 +01/02/2021 09:30,86,0,-1,1,95,0.0014,0.033,0.005,246,651,83,AD,,SolarAnywhere3_6,01/02/2021 14:30,86,O,O,0.6,6.3,5.4 +01/02/2021 09:35,105,11,-1,1,95,0.0014,0.033,0.005,257,663,85,AD,,SolarAnywhere3_6,01/02/2021 14:35,102,O,O,0.6,6.3,5.4 +01/02/2021 09:40,72,0,-1,1,95,0.0014,0.033,0.005,267,674,87,AD,,SolarAnywhere3_6,01/02/2021 14:40,72,O,O,0.6,6.3,5.4 +01/02/2021 09:45,69,0,-1,1,95,0.0014,0.033,0.005,277,684,89,AD,,SolarAnywhere3_6,01/02/2021 14:45,69,O,O,0.6,6.3,5.4 +01/02/2021 09:50,72,0,-1,1,95,0.0014,0.033,0.005,287,694,91,AD,,SolarAnywhere3_6,01/02/2021 14:50,72,O,O,0.6,6.3,5.4 +01/02/2021 09:55,71,0,-1,1,95,0.0014,0.033,0.005,296,703,92,AD,,SolarAnywhere3_6,01/02/2021 14:55,71,O,O,0.6,6.3,5.4 +01/02/2021 10:00,78,0,-1,1,95,0.0014,0.033,0.005,306,712,94,AD,,SolarAnywhere3_6,01/02/2021 15:00,78,O,O,0.6,6.3,5.4 +01/02/2021 10:05,79,0,-1,1,95,0.0014,0.033,0.005,314,712,97,AD,,SolarAnywhere3_6,01/02/2021 15:05,79,O,O,0.6,5.8,6.1 +01/02/2021 10:10,79,0,-1,1,95,0.0014,0.033,0.005,322,719,98,AD,,SolarAnywhere3_6,01/02/2021 15:10,79,O,O,0.6,5.8,6.1 +01/02/2021 10:15,82,0,-1,1,95,0.0014,0.033,0.005,330,726,99,AD,,SolarAnywhere3_6,01/02/2021 15:15,82,O,O,0.6,5.8,6.1 +01/02/2021 10:20,84,0,-1,2,95,0.0014,0.033,0.005,338,733,100,AD,,SolarAnywhere3_6,01/02/2021 15:20,84,O,O,0.6,5.8,6.1 +01/02/2021 10:25,78,0,-1,2,95,0.0014,0.033,0.005,346,739,102,AD,,SolarAnywhere3_6,01/02/2021 15:25,78,O,O,0.6,5.8,6.1 +01/02/2021 10:30,78,0,-1,2,95,0.0014,0.033,0.005,353,745,103,AD,,SolarAnywhere3_6,01/02/2021 15:30,78,O,O,0.6,5.8,6.1 +01/02/2021 10:35,75,0,0,2,95,0.0014,0.033,0.005,360,750,104,AD,,SolarAnywhere3_6,01/02/2021 15:35,75,O,O,0.6,5.8,6.1 +01/02/2021 10:40,78,0,0,2,95,0.0014,0.033,0.005,366,755,105,AD,,SolarAnywhere3_6,01/02/2021 15:40,78,O,O,0.6,5.8,6.1 +01/02/2021 10:45,79,0,0,2,95,0.0014,0.033,0.005,373,760,107,AD,,SolarAnywhere3_6,01/02/2021 15:45,79,O,O,0.6,5.8,6.1 +01/02/2021 10:50,83,0,0,2,95,0.0014,0.033,0.005,378,764,107,AD,,SolarAnywhere3_6,01/02/2021 15:50,83,O,O,0.6,5.8,6.1 +01/02/2021 10:55,97,0,0,3,95,0.0014,0.033,0.005,384,768,108,AD,,SolarAnywhere3_6,01/02/2021 15:55,97,O,O,0.6,5.8,6.1 +01/02/2021 11:00,89,0,0,3,95,0.0014,0.033,0.005,389,771,109,AD,,SolarAnywhere3_6,01/02/2021 16:00,89,O,O,0.6,5.8,6.1 +01/02/2021 11:05,95,0,0,3,95,0.0014,0.033,0.005,392,761,113,AD,,SolarAnywhere3_6,01/02/2021 16:05,95,O,O,0.6,5.8,9.3 +01/02/2021 11:10,101,0,0,3,95,0.0014,0.033,0.005,396,764,114,AD,,SolarAnywhere3_6,01/02/2021 16:10,101,O,O,0.6,5.8,9.3 +01/02/2021 11:15,106,0,0,3,94,0.0014,0.033,0.005,400,767,114,AD,,SolarAnywhere3_6,01/02/2021 16:15,106,O,O,0.6,5.8,9.3 +01/02/2021 11:20,131,0,0,3,93,0.0014,0.033,0.005,404,769,116,AD,,SolarAnywhere3_6,01/02/2021 16:20,131,O,O,0.6,5.8,9.3 +01/02/2021 11:25,114,0,0,3,93,0.0014,0.033,0.005,407,771,116,AD,,SolarAnywhere3_6,01/02/2021 16:25,114,O,O,0.6,5.8,9.3 +01/02/2021 11:30,119,0,0,3,92,0.0014,0.033,0.005,409,773,116,AD,,SolarAnywhere3_6,01/02/2021 16:30,119,O,O,0.6,5.8,9.3 +01/02/2021 11:35,121,0,0,4,92,0.0014,0.033,0.005,412,775,117,AD,,SolarAnywhere3_6,01/02/2021 16:35,121,O,O,0.6,5.8,9.3 +01/02/2021 11:40,123,0,0,4,91,0.0014,0.033,0.005,414,776,118,AD,,SolarAnywhere3_6,01/02/2021 16:40,123,O,O,0.6,5.8,9.3 +01/02/2021 11:45,126,0,0,4,91,0.0014,0.033,0.005,415,777,117,AD,,SolarAnywhere3_6,01/02/2021 16:45,126,O,O,0.6,5.8,9.3 +01/02/2021 11:50,124,0,0,4,90,0.0014,0.033,0.005,417,778,118,AD,,SolarAnywhere3_6,01/02/2021 16:50,124,O,O,0.6,5.8,9.3 +01/02/2021 11:55,134,0,0,4,89,0.0014,0.033,0.005,417,778,118,AD,,SolarAnywhere3_6,01/02/2021 16:55,134,O,O,0.6,5.8,9.3 +01/02/2021 12:00,118,0,0,4,89,0.0014,0.033,0.005,418,779,119,AD,,SolarAnywhere3_6,01/02/2021 17:00,118,O,O,0.6,5.8,9.3 +01/02/2021 12:05,115,0,0,4,88,0.0014,0.033,0.005,416,766,122,AD,,SolarAnywhere3_6,01/02/2021 17:05,115,O,O,0.6,5.8,10.6 +01/02/2021 12:10,107,0,0,4,88,0.0014,0.033,0.005,416,765,123,AD,,SolarAnywhere3_6,01/02/2021 17:10,107,O,O,0.6,5.8,10.6 +01/02/2021 12:15,125,0,0,4,87,0.0014,0.033,0.005,415,765,122,AD,,SolarAnywhere3_6,01/02/2021 17:15,125,O,O,0.6,5.8,10.6 +01/02/2021 12:20,132,0,0,4,87,0.0014,0.033,0.005,413,764,122,AD,,SolarAnywhere3_6,01/02/2021 17:20,132,O,O,0.6,5.8,10.6 +01/02/2021 12:25,162,16,0,4,87,0.0014,0.033,0.005,412,763,122,AD,,SolarAnywhere3_6,01/02/2021 17:25,156,O,O,0.6,5.8,10.6 +01/02/2021 12:30,234,109,0,4,86,0.0014,0.033,0.005,410,761,122,AD,,SolarAnywhere3_6,01/02/2021 17:30,193,O,O,0.6,5.8,10.6 +01/02/2021 12:35,519,759,0,4,86,0.0014,0.033,0.005,407,760,121,AD,,SolarAnywhere3_6,01/02/2021 17:35,234,O,O,0.6,5.8,10.6 +01/02/2021 12:40,254,155,-1,5,85,0.0014,0.033,0.005,405,758,122,AD,,SolarAnywhere3_6,01/02/2021 17:40,196,O,O,0.6,5.8,10.6 +01/02/2021 12:45,239,127,-1,5,85,0.0014,0.033,0.005,401,756,120,AD,,SolarAnywhere3_6,01/02/2021 17:45,192,O,O,0.6,5.8,10.6 +01/02/2021 12:50,263,273,-1,5,85,0.0014,0.033,0.005,398,753,121,AD,,SolarAnywhere3_6,01/02/2021 17:50,162,O,O,0.6,5.8,10.6 +01/02/2021 12:55,170,27,-1,5,84,0.0014,0.033,0.005,394,750,120,AD,,SolarAnywhere3_6,01/02/2021 17:55,160,O,O,0.6,5.8,10.6 +01/02/2021 13:00,180,43,-1,5,84,0.0014,0.033,0.005,390,747,120,AD,,SolarAnywhere3_6,01/02/2021 18:00,164,O,O,0.6,5.8,10.6 +01/02/2021 13:05,225,116,-1,5,83,0.0014,0.033,0.005,383,731,122,AD,,SolarAnywhere3_6,01/02/2021 18:05,184,O,O,0.6,10.9,9.5 +01/02/2021 13:10,219,110,-1,5,83,0.0014,0.033,0.005,378,727,122,AD,,SolarAnywhere3_6,01/02/2021 18:10,180,O,O,0.6,10.9,9.5 +01/02/2021 13:15,157,22,-1,5,83,0.0014,0.033,0.005,373,723,121,AD,,SolarAnywhere3_6,01/02/2021 18:15,149,O,O,0.6,10.9,9.5 +01/02/2021 13:20,141,11,-1,5,83,0.0014,0.033,0.005,367,718,120,AD,,SolarAnywhere3_6,01/02/2021 18:20,137,O,O,0.6,10.9,9.5 +01/02/2021 13:25,135,9,-1,5,83,0.0014,0.033,0.005,361,713,120,AD,,SolarAnywhere3_6,01/02/2021 18:25,132,O,O,0.6,10.9,9.5 +01/02/2021 13:30,107,0,-1,5,83,0.0014,0.033,0.005,354,708,118,AD,,SolarAnywhere3_6,01/02/2021 18:30,107,O,O,0.6,10.9,9.5 +01/02/2021 13:35,80,0,-2,5,83,0.0014,0.033,0.005,347,702,117,AD,,SolarAnywhere3_6,01/02/2021 18:35,80,O,O,0.6,10.9,9.5 +01/02/2021 13:40,84,0,-2,5,83,0.0014,0.033,0.005,340,696,116,AD,,SolarAnywhere3_6,01/02/2021 18:40,84,O,O,0.6,10.9,9.5 +01/02/2021 13:45,82,0,-2,5,83,0.0014,0.033,0.005,332,689,115,AD,,SolarAnywhere3_6,01/02/2021 18:45,82,O,O,0.6,10.9,9.5 +01/02/2021 13:50,98,0,-2,5,83,0.0014,0.033,0.005,324,682,114,AD,,SolarAnywhere3_6,01/02/2021 18:50,98,O,O,0.6,10.9,9.5 +01/02/2021 13:55,90,0,-2,5,83,0.0014,0.033,0.005,316,675,112,AD,,SolarAnywhere3_6,01/02/2021 18:55,90,O,O,0.6,10.9,9.5 +01/02/2021 14:00,108,1,-2,5,83,0.0014,0.033,0.005,308,666,112,AD,,SolarAnywhere3_6,01/02/2021 19:00,108,O,O,0.6,10.9,9.5 +01/02/2021 14:05,135,30,-2,5,83,0.0014,0.033,0.005,300,672,107,AD,,SolarAnywhere3_6,01/02/2021 19:05,126,O,O,0.6,10.9,11.1 +01/02/2021 14:10,98,0,-2,5,83,0.0014,0.033,0.005,291,663,106,AD,,SolarAnywhere3_6,01/02/2021 19:10,98,O,O,0.6,10.9,11.1 +01/02/2021 14:15,109,6,-2,5,83,0.0014,0.033,0.005,282,653,105,AD,,SolarAnywhere3_6,01/02/2021 19:15,107,O,O,0.6,10.9,11.1 +01/02/2021 14:20,107,7,-2,5,83,0.0014,0.033,0.005,272,643,102,AD,,SolarAnywhere3_6,01/02/2021 19:20,105,O,O,0.6,10.9,11.1 +01/02/2021 14:25,70,0,-2,5,83,0.0014,0.033,0.005,262,632,101,AD,,SolarAnywhere3_6,01/02/2021 19:25,70,O,O,0.6,10.9,11.1 +01/02/2021 14:30,67,0,-2,5,83,0.0014,0.033,0.005,252,620,99,AD,,SolarAnywhere3_6,01/02/2021 19:30,67,O,O,0.6,10.9,11.1 +01/02/2021 14:35,64,0,-2,5,82,0.0014,0.033,0.005,241,607,97,AD,,SolarAnywhere3_6,01/02/2021 19:35,64,O,O,0.6,10.9,11.1 +01/02/2021 14:40,160,256,-2,5,82,0.0014,0.033,0.005,230,593,94,AD,,SolarAnywhere3_6,01/02/2021 19:40,102,O,O,0.6,10.9,11.1 +01/02/2021 14:45,131,94,-2,5,82,0.0014,0.033,0.005,219,579,92,AD,,SolarAnywhere3_6,01/02/2021 19:45,110,O,O,0.6,10.9,11.1 +01/02/2021 14:50,137,202,-2,5,82,0.0014,0.033,0.005,208,563,90,AD,,SolarAnywhere3_6,01/02/2021 19:50,95,O,O,0.6,10.9,11.1 +01/02/2021 14:55,236,547,-2,5,82,0.0014,0.033,0.005,197,547,88,AD,,SolarAnywhere3_6,01/02/2021 19:55,127,O,O,0.6,10.9,11.1 +01/02/2021 15:00,157,430,-2,5,82,0.0014,0.033,0.005,185,529,85,AD,,SolarAnywhere3_6,01/02/2021 20:00,76,O,O,0.6,10.9,11.1 +01/02/2021 15:05,110,162,-2,5,82,0.0014,0.033,0.005,175,527,81,AD,,SolarAnywhere3_6,01/02/2021 20:05,81,O,O,0.6,10.9,12.5 +01/02/2021 15:10,125,319,-2,5,81,0.0014,0.033,0.005,163,506,78,AD,,SolarAnywhere3_6,01/02/2021 20:10,71,O,O,0.6,10.9,12.5 +01/02/2021 15:15,109,219,-2,5,81,0.0014,0.033,0.005,151,485,74,AD,,SolarAnywhere3_6,01/02/2021 20:15,74,O,O,0.6,10.9,12.5 +01/02/2021 15:20,144,461,-2,5,81,0.0014,0.033,0.005,139,461,71,AD,,SolarAnywhere3_6,01/02/2021 20:20,76,O,O,0.6,10.9,12.5 +01/02/2021 15:25,87,161,-2,5,81,0.0014,0.033,0.005,127,436,68,AD,,SolarAnywhere3_6,01/02/2021 20:25,65,O,O,0.6,10.9,12.5 +01/02/2021 15:30,88,237,-2,5,81,0.0014,0.033,0.005,114,409,63,AD,,SolarAnywhere3_6,01/02/2021 20:30,58,O,O,0.6,10.9,12.5 +01/02/2021 15:35,66,107,-2,4,80,0.0014,0.033,0.005,102,380,59,AD,,SolarAnywhere3_6,01/02/2021 20:35,54,O,O,0.6,10.9,12.5 +01/02/2021 15:40,68,187,-1,4,80,0.0014,0.033,0.005,90,349,55,AD,,SolarAnywhere3_6,01/02/2021 20:40,49,O,O,0.6,10.9,12.5 +01/02/2021 15:45,46,44,-1,4,80,0.0014,0.033,0.005,78,317,50,AD,,SolarAnywhere3_6,01/02/2021 20:45,42,O,O,0.6,10.9,12.5 +01/02/2021 15:50,27,0,-1,4,80,0.0014,0.033,0.005,66,282,44,AD,,SolarAnywhere3_6,01/02/2021 20:50,27,O,O,0.6,10.9,12.5 +01/02/2021 15:55,28,8,-1,4,80,0.0014,0.033,0.005,54,246,38,AD,,SolarAnywhere3_6,01/02/2021 20:55,28,O,O,0.6,10.9,12.5 +01/02/2021 16:00,17,0,-1,4,79,0.0014,0.033,0.005,42,209,31,AD,,SolarAnywhere3_6,01/02/2021 21:00,17,O,O,0.6,10.9,12.5 +01/02/2021 16:05,19,0,-1,4,79,0.0014,0.033,0.005,32,189,24,AD,,SolarAnywhere3_6,01/02/2021 21:05,19,O,O,0.6,14.3,12.3 +01/02/2021 16:10,14,0,-1,4,79,0.0014,0.033,0.005,21,148,17,AD,,SolarAnywhere3_6,01/02/2021 21:10,14,O,O,0.6,14.3,12.3 +01/02/2021 16:15,3,0,-1,4,80,0.0014,0.033,0.005,12,105,10,AD,,SolarAnywhere3_6,01/02/2021 21:15,3,O,O,0.6,14.3,12.3 +01/02/2021 16:20,1,0,-1,4,80,0.0014,0.033,0.005,4,64,4,AD,,SolarAnywhere3_6,01/02/2021 21:20,1,O,O,0.6,14.3,12.3 +01/02/2021 16:25,0,0,-1,4,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 21:25,0,O,O,0.6,14.3,12.3 +01/02/2021 16:30,0,0,-1,4,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 21:30,0,O,O,0.6,14.3,12.3 +01/02/2021 16:35,0,0,-1,4,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 21:35,0,O,O,0.6,14.3,12.3 +01/02/2021 16:40,0,0,-1,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 21:40,0,O,O,0.6,14.3,12.3 +01/02/2021 16:45,0,0,-1,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 21:45,0,O,O,0.6,14.3,12.3 +01/02/2021 16:50,0,0,-1,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 21:50,0,O,O,0.6,14.3,12.3 +01/02/2021 16:55,0,0,-1,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 21:55,0,O,O,0.6,14.3,12.3 +01/02/2021 17:00,0,0,-1,3,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:00,0,O,O,0.6,14.3,12.3 +01/02/2021 17:05,0,0,-1,3,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:05,0,O,O,0.6,14.3,11.4 +01/02/2021 17:10,0,0,-1,3,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:10,0,O,O,0.6,14.3,11.4 +01/02/2021 17:15,0,0,-1,3,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:15,0,O,O,0.6,14.3,11.4 +01/02/2021 17:20,0,0,-1,3,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:20,0,O,O,0.6,14.3,11.4 +01/02/2021 17:25,0,0,-1,3,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:25,0,O,O,0.6,14.3,11.4 +01/02/2021 17:30,0,0,-1,3,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:30,0,O,O,0.6,14.3,11.4 +01/02/2021 17:35,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:35,0,O,O,0.6,14.3,11.4 +01/02/2021 17:40,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:40,0,O,O,0.6,14.3,11.4 +01/02/2021 17:45,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:45,0,O,O,0.6,14.3,11.4 +01/02/2021 17:50,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:50,0,O,O,0.6,14.3,11.4 +01/02/2021 17:55,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 22:55,0,O,O,0.6,14.3,11.4 +01/02/2021 18:00,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:00,0,O,O,0.6,14.3,11.4 +01/02/2021 18:05,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:05,0,O,O,0.6,14.3,9.7 +01/02/2021 18:10,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:10,0,O,O,0.6,14.3,9.7 +01/02/2021 18:15,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:15,0,O,O,0.6,14.3,9.7 +01/02/2021 18:20,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:20,0,O,O,0.6,14.3,9.7 +01/02/2021 18:25,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:25,0,O,O,0.6,14.3,9.7 +01/02/2021 18:30,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:30,0,O,O,0.6,14.3,9.7 +01/02/2021 18:35,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:35,0,O,O,0.6,14.3,9.7 +01/02/2021 18:40,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:40,0,O,O,0.6,14.3,9.7 +01/02/2021 18:45,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:45,0,O,O,0.6,14.3,9.7 +01/02/2021 18:50,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:50,0,O,O,0.6,14.3,9.7 +01/02/2021 18:55,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/02/2021 23:55,0,O,O,0.6,14.3,9.7 +01/02/2021 19:00,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:00,0,O,O,0.6,14.3,9.7 +01/02/2021 19:05,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:05,0,O,O,0.6,10.2,8.4 +01/02/2021 19:10,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:10,0,O,O,0.6,10.2,8.4 +01/02/2021 19:15,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:15,0,O,O,0.6,10.2,8.4 +01/02/2021 19:20,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:20,0,O,O,0.6,10.2,8.4 +01/02/2021 19:25,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:25,0,O,O,0.6,10.2,8.4 +01/02/2021 19:30,0,0,0,3,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:30,0,O,O,0.6,10.2,8.4 +01/02/2021 19:35,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:35,0,O,O,0.6,10.2,8.4 +01/02/2021 19:40,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:40,0,O,O,0.6,10.2,8.4 +01/02/2021 19:45,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:45,0,O,O,0.6,10.2,8.4 +01/02/2021 19:50,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:50,0,O,O,0.6,10.2,8.4 +01/02/2021 19:55,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 00:55,0,O,O,0.6,10.2,8.4 +01/02/2021 20:00,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:00,0,O,O,0.6,10.2,8.4 +01/02/2021 20:05,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:05,0,O,O,0.6,10.2,7.8 +01/02/2021 20:10,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:10,0,O,O,0.6,10.2,7.8 +01/02/2021 20:15,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:15,0,O,O,0.6,10.2,7.8 +01/02/2021 20:20,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:20,0,O,O,0.6,10.2,7.8 +01/02/2021 20:25,0,0,0,2,80,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:25,0,O,O,0.6,10.2,7.8 +01/02/2021 20:30,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:30,0,O,O,0.6,10.2,7.8 +01/02/2021 20:35,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:35,0,O,O,0.6,10.2,7.8 +01/02/2021 20:40,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:40,0,O,O,0.6,10.2,7.8 +01/02/2021 20:45,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:45,0,O,O,0.6,10.2,7.8 +01/02/2021 20:50,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:50,0,O,O,0.6,10.2,7.8 +01/02/2021 20:55,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 01:55,0,O,O,0.6,10.2,7.8 +01/02/2021 21:00,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:00,0,O,O,0.6,10.2,7.8 +01/02/2021 21:05,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:05,0,O,O,0.6,10.2,7.8 +01/02/2021 21:10,0,0,0,2,81,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:10,0,O,O,0.6,10.2,7.8 +01/02/2021 21:15,0,0,0,2,82,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:15,0,O,O,0.6,10.2,7.8 +01/02/2021 21:20,0,0,0,2,82,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:20,0,O,O,0.6,10.2,7.8 +01/02/2021 21:25,0,0,-1,2,82,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:25,0,O,O,0.6,10.2,7.8 +01/02/2021 21:30,0,0,-1,2,82,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:30,0,O,O,0.6,10.2,7.8 +01/02/2021 21:35,0,0,-1,2,82,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:35,0,O,O,0.6,10.2,7.8 +01/02/2021 21:40,0,0,-1,2,83,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:40,0,O,O,0.6,10.2,7.8 +01/02/2021 21:45,0,0,-1,2,83,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:45,0,O,O,0.6,10.2,7.8 +01/02/2021 21:50,0,0,-2,2,83,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:50,0,O,O,0.6,10.2,7.8 +01/02/2021 21:55,0,0,-2,2,83,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 02:55,0,O,O,0.6,10.2,7.8 +01/02/2021 22:00,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:00,0,O,O,0.6,10.2,7.8 +01/02/2021 22:05,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:05,0,O,O,0.6,8.3,8.4 +01/02/2021 22:10,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:10,0,O,O,0.6,8.3,8.4 +01/02/2021 22:15,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:15,0,O,O,0.6,8.3,8.4 +01/02/2021 22:20,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:20,0,O,O,0.6,8.3,8.4 +01/02/2021 22:25,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:25,0,O,O,0.6,8.3,8.4 +01/02/2021 22:30,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:30,0,O,O,0.6,8.3,8.4 +01/02/2021 22:35,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:35,0,O,O,0.6,8.3,8.4 +01/02/2021 22:40,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:40,0,O,O,0.6,8.3,8.4 +01/02/2021 22:45,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:45,0,O,O,0.6,8.3,8.4 +01/02/2021 22:50,0,0,-2,2,84,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:50,0,O,O,0.6,8.3,8.4 +01/02/2021 22:55,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 03:55,0,O,O,0.6,8.3,8.4 +01/02/2021 23:00,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:00,0,O,O,0.6,8.3,8.4 +01/02/2021 23:05,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:05,0,O,O,0.6,8.3,8.6 +01/02/2021 23:10,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:10,0,O,O,0.6,8.3,8.6 +01/02/2021 23:15,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:15,0,O,O,0.6,8.3,8.6 +01/02/2021 23:20,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:20,0,O,O,0.6,8.3,8.6 +01/02/2021 23:25,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:25,0,O,O,0.6,8.3,8.6 +01/02/2021 23:30,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:30,0,O,O,0.6,8.3,8.6 +01/02/2021 23:35,0,0,-2,2,85,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:35,0,O,O,0.6,8.3,8.6 +01/02/2021 23:40,0,0,-2,2,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:40,0,O,O,0.6,8.3,8.6 +01/02/2021 23:45,0,0,-2,2,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:45,0,O,O,0.6,8.3,8.6 +01/02/2021 23:50,0,0,-2,2,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:50,0,O,O,0.6,8.3,8.6 +01/02/2021 23:55,0,0,-2,2,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 04:55,0,O,O,0.6,8.3,8.6 +01/03/2021 00:00,0,0,-2,2,86,0.0014,0.033,0.005,0,0,0,AN,,SolarAnywhere3_6,01/03/2021 05:00,0,O,O,0.6,8.3,8.6 diff --git a/pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv b/pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv new file mode 100644 index 0000000000..6e4f1bed4b --- /dev/null +++ b/pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv @@ -0,0 +1,74 @@ +0,Burlington United States,NA,-5,44.465,-73.205,41,"Data Version: 3.6 / Type: Typical Year / LatLon Resolution: 0.010 / Time Resolution: 60 minutes / Averaging Method: End of Period / TD: No / Copyright 2010-2022 Clean Power Research®, L.L.C. DownloadID=9a0a90e9-cb05-44f6-892c-a1506e8a1185" +ObservationTime(LST),Global Horizontal Irradiance (GHI) W/m2,Direct Normal Irradiance (DNI) W/m2,AmbientTemperature (deg C),WindSpeed (m/s),Relative Humidity (%),Liquid Precipitation (kg/m2),Solid Precipitation (kg/m2),Snow Depth (m),Clear Sky GHI,Clear Sky DNI,Clear Sky DHI,IrradianceObservationType,LeadTime,DataVersion,ObservationTime(GMT),Diffuse Horizontal Irradiance (DIF) W/m2,AmbientTemperatureObservationType,WindSpeedObservationType,Albedo,Particulate Matter 10 (µg/m3),Particulate Matter 2.5 (µg/m3) +01/01/2000 01:00,0,0,-6,,,,,,,,,AN,,SolarAnywhereTGY2021,01/01/2000 06:00,0,O,O,,, +01/01/2000 02:00,0,0,-5,,,,,,,,,AN,,SolarAnywhereTGY2021,01/01/2000 07:00,0,O,O,,, +01/01/2000 03:00,0,0,-5,,,,,,,,,AN,,SolarAnywhereTGY2021,01/01/2000 08:00,0,O,O,,, +01/01/2000 04:00,0,0,-5,,,,,,,,,AN,,SolarAnywhereTGY2021,01/01/2000 09:00,0,O,O,,, +01/01/2000 05:00,0,0,-5,,,,,,,,,AN,,SolarAnywhereTGY2021,01/01/2000 10:00,0,O,O,,, +01/01/2000 06:00,0,0,-4,,,,,,,,,AN,,SolarAnywhereTGY2021,01/01/2000 11:00,0,O,O,,, +01/01/2000 07:00,0,0,-4,,,,,,,,,AN,,SolarAnywhereTGY2021,01/01/2000 12:00,0,O,O,,, +01/01/2000 08:00,3,1,-3,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 13:00,3,O,O,,, +01/01/2000 09:00,50,24,-3,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 14:00,47,O,O,,, +01/01/2000 10:00,171,245,-2,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 15:00,109,O,O,,, +01/01/2000 11:00,234,237,-2,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 16:00,154,O,O,,, +01/01/2000 12:00,220,168,-2,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 17:00,156,O,O,,, +01/01/2000 13:00,202,107,1,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 18:00,162,O,O,,, +01/01/2000 14:00,122,41,1,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 19:00,108,O,O,,, +01/01/2000 15:00,141,165,1,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 20:00,101,O,O,,, +01/01/2000 16:00,65,101,3,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 21:00,53,O,O,,, +01/01/2000 17:00,2,1,4,,,,,,,,,AD,,SolarAnywhereTGY2021,01/01/2000 22:00,2,O,O,,, +01/01/2000 18:00,0,0,2,,,,,,,,,AN,,SolarAnywhereTGY2021,01/01/2000 23:00,0,O,O,,, +01/01/2000 19:00,0,0,2,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 00:00,0,O,O,,, +01/01/2000 20:00,0,0,2,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 01:00,0,O,O,,, +01/01/2000 21:00,0,0,2,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 02:00,0,O,O,,, +01/01/2000 22:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 03:00,0,O,O,,, +01/01/2000 23:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 04:00,0,O,O,,, +01/02/2000 00:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 05:00,0,O,O,,, +01/02/2000 01:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 06:00,0,O,O,,, +01/02/2000 02:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 07:00,0,O,O,,, +01/02/2000 03:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 08:00,0,O,O,,, +01/02/2000 04:00,0,0,-1,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 09:00,0,O,O,,, +01/02/2000 05:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 10:00,0,O,O,,, +01/02/2000 06:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 11:00,0,O,O,,, +01/02/2000 07:00,0,0,0,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 12:00,0,O,O,,, +01/02/2000 08:00,2,0,0,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 13:00,2,O,O,,, +01/02/2000 09:00,48,23,0,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 14:00,45,O,O,,, +01/02/2000 10:00,105,29,1,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 15:00,98,O,O,,, +01/02/2000 11:00,161,53,2,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 16:00,143,O,O,,, +01/02/2000 12:00,135,5,2,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 17:00,133,O,O,,, +01/02/2000 13:00,108,0,4,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 18:00,108,O,O,,, +01/02/2000 14:00,72,0,3,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 19:00,72,O,O,,, +01/02/2000 15:00,58,0,3,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 20:00,58,O,O,,, +01/02/2000 16:00,33,3,3,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 21:00,33,O,O,,, +01/02/2000 17:00,2,0,3,,,,,,,,,AD,,SolarAnywhereTGY2021,01/02/2000 22:00,2,O,O,,, +01/02/2000 18:00,0,0,4,,,,,,,,,AN,,SolarAnywhereTGY2021,01/02/2000 23:00,0,O,O,,, +01/02/2000 19:00,0,0,4,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 00:00,0,O,O,,, +01/02/2000 20:00,0,0,4,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 01:00,0,O,O,,, +01/02/2000 21:00,0,0,4,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 02:00,0,O,O,,, +01/02/2000 22:00,0,0,5,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 03:00,0,O,O,,, +01/02/2000 23:00,0,0,5,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 04:00,0,O,O,,, +01/03/2000 00:00,0,0,5,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 05:00,0,O,O,,, +01/03/2000 01:00,0,0,7,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 06:00,0,O,O,,, +01/03/2000 02:00,0,0,7,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 07:00,0,O,O,,, +01/03/2000 03:00,0,0,6,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 08:00,0,O,O,,, +01/03/2000 04:00,0,0,7,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 09:00,0,O,O,,, +01/03/2000 05:00,0,0,7,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 10:00,0,O,O,,, +01/03/2000 06:00,0,0,6,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 11:00,0,O,O,,, +01/03/2000 07:00,0,0,7,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 12:00,0,O,O,,, +01/03/2000 08:00,4,0,7,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 13:00,4,O,O,,, +01/03/2000 09:00,47,7,8,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 14:00,46,O,O,,, +01/03/2000 10:00,124,86,5,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 15:00,102,O,O,,, +01/03/2000 11:00,99,6,4,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 16:00,97,O,O,,, +01/03/2000 12:00,116,0,3,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 17:00,116,O,O,,, +01/03/2000 13:00,130,1,2,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 18:00,130,O,O,,, +01/03/2000 14:00,165,96,2,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 19:00,133,O,O,,, +01/03/2000 15:00,110,47,2,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 20:00,98,O,O,,, +01/03/2000 16:00,36,5,1,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 21:00,35,O,O,,, +01/03/2000 17:00,1,0,1,,,,,,,,,AD,,SolarAnywhereTGY2021,01/03/2000 22:00,1,O,O,,, +01/03/2000 18:00,0,0,-1,,,,,,,,,AN,,SolarAnywhereTGY2021,01/03/2000 23:00,0,O,O,,, +01/03/2000 19:00,0,0,-1,,,,,,,,,AN,,SolarAnywhereTGY2021,01/04/2000 00:00,0,O,O,,, +01/03/2000 20:00,0,0,-1,,,,,,,,,AN,,SolarAnywhereTGY2021,01/04/2000 01:00,0,O,O,,, +01/03/2000 21:00,0,0,-1,,,,,,,,,AN,,SolarAnywhereTGY2021,01/04/2000 02:00,0,O,O,,, +01/03/2000 22:00,0,0,-1,,,,,,,,,AN,,SolarAnywhereTGY2021,01/04/2000 03:00,0,O,O,,, +01/03/2000 23:00,0,0,-1,,,,,,,,,AN,,SolarAnywhereTGY2021,01/04/2000 04:00,0,O,O,,, +01/04/2000 00:00,0,0,-1,,,,,,,,,AN,,SolarAnywhereTGY2021,01/04/2000 05:00,0,O,O,,, diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 0f199022c0..c401940306 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -2,6 +2,7 @@ import requests import pandas as pd +import numpy as np import time import json @@ -190,7 +191,6 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, start_time = time.time() # Current time in seconds since the Epoch # Attempt to retrieve results until the max response time has been exceeded while True: - time.sleep(5) # Sleep for 5 seconds before each data retrieval attempt results = requests.get(url+'/WeatherDataResult/'+weather_request_id, headers=headers) # noqa: E501 results_json = results.json() if results_json.get('Status') == 'Done': @@ -201,6 +201,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, raise RuntimeError(f"Bad request: {results_json['Message']}") elif (time.time()-start_time) > max_response_time: raise TimeoutError('Time exceeded the `max_response_time`.') + time.sleep(5) # Sleep for 5 seconds before each data retrieval attempt # Extract time series data data = pd.DataFrame(results_json['WeatherDataResults'][0]['WeatherDataPeriods']['WeatherDataPeriods']) # noqa: E501 @@ -314,6 +315,10 @@ def parse_solaranywhere(fbuf, map_variables=True): # Set index to UTC data.index = pd.to_datetime(data['ObservationTime(GMT)'], format='%m/%d/%Y %H:%M', utc=True) + + # Missing values can be represented as: blanks, 'NaN', or -999 + data = data.replace(-999, np.nan) + if map_variables: data = data.rename(columns=VARIABLE_MAP) diff --git a/pvlib/tests/conftest.py b/pvlib/tests/conftest.py index b3e9fcd5a1..89a730e553 100644 --- a/pvlib/tests/conftest.py +++ b/pvlib/tests/conftest.py @@ -95,6 +95,19 @@ def assert_frame_equal(left, right, **kwargs): not has_bsrn_credentials, reason='requires bsrn credentials') +try: + # Attempt to load SolarAnywhere API key used for testing + # pvlib.iotools.get_solaranywhere + solaranywhere_api_key = os.environ["SOLARANYWHERE_API_KEY"] + has_solaranywhere_credentials = True +except KeyError: + has_solaranywhere_credentials = False + +requires_solaranywhere_credentials = pytest.mark.skipif( + not has_solaranywhere_credentials, + reason='requires solaranywhere credentials') + + try: import statsmodels # noqa: F401 has_statsmodels = True diff --git a/pvlib/tests/iotools/test_solaranywhere.py b/pvlib/tests/iotools/test_solaranywhere.py new file mode 100644 index 0000000000..5b4978fb9a --- /dev/null +++ b/pvlib/tests/iotools/test_solaranywhere.py @@ -0,0 +1,225 @@ +import pandas as pd +import pytest +import pvlib +import os +from ..conftest import (DATA_DIR, RERUNS, RERUNS_DELAY, + requires_solaranywhere_credentials) + +# High spatial resolution and 5-min data, true dynamics enabled +TESTFILE_HIGH_RESOLUTION = DATA_DIR / 'Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv' # noqa: E501 +# TGY test file (v3.6) containing GHI/DHI and temperature. +# Note, the test file only contains the first three days. +TESTFILE_TMY = DATA_DIR / 'Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv' # noqa: E501 + + +@pytest.fixture(scope="module") +def solaranywhere_api_key(): + """Supplies the pvlib's SolarAnywhere API key for testing purposes. + Users can freely registre for an API key.""" + solaranywhere_api_key = os.environ["SOLARANYWHERE_API_KEY"] + return solaranywhere_api_key + + +@pytest.fixture +def high_resolution_index(): + index = pd.date_range(start='2021-01-01 05:05', end='2021-01-03 05:00', + freq='5min', tz='UTC') + index.name = 'ObservationTime(GMT)' + return index + + +@pytest.fixture +def tmy_index(): + index = pd.date_range(start='2000-01-01 06:00', periods=3*24, freq='1h', + tz='UTC') + index.name = 'ObservationTime(GMT)' + index.freq = None + return index + + +@pytest.fixture +def tmy_ghi_series(tmy_index): + ghi = [ + 0, 0, 0, 0, 0, 0, 0, 3, 50, 171, 234, 220, 202, 122, 141, 65, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 48, 105, 161, 135, 108, 72, 58, + 33, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 47, 124, 99, 116, + 130, 165, 110, 36, 1, 0, 0, 0, 0, 0, 0, 0 + ] + return pd.Series(data=ghi, index=tmy_index, name='ghi') + + +def test_read_solaranywhere_high_resolution(high_resolution_index): + data, meta = pvlib.iotools.read_solaranywhere(TESTFILE_HIGH_RESOLUTION, + map_variables=False) + # Check that metadata is parsed correctly + assert meta['latitude'] == 44.4675 + assert meta['longitude'] == -73.2075 + assert meta['altitude'] == 41.0 + assert meta['name'] == 'Burlington United States' + assert meta['TZ'] == -5.0 + assert meta['Data Version'] == '3.6' + assert meta['LatLon Resolution'] == '0.005' + # Check that columns are parsed correctly + assert 'Albedo' in data.columns + assert 'Global Horizontal Irradiance (GHI) W/m2' in data.columns + assert 'Direct Normal Irradiance (DNI) W/m2' in data.columns + assert 'WindSpeed (m/s)' in data.columns + assert 'WindSpeedObservationType' in data.columns + assert 'Particulate Matter 10 (µg/m3)' in data.columns + # Check that data is parsed correctly + assert data.loc['2021-01-01 12:00:00+0000', 'Albedo'] == 0.6 + assert data.loc['2021-01-01 12:00:00+0000', 'WindSpeed (m/s)'] == 0 + # Assert that the index is parsed correctly + pd.testing.assert_index_equal(data.index, high_resolution_index) + + +def test_read_solaranywhere_map_variables(high_resolution_index): + # Check that variables are mapped by default to pvlib names + data, meta = pvlib.iotools.read_solaranywhere(TESTFILE_HIGH_RESOLUTION) + mapped_column_names = ['ghi', 'dni', 'dhi', 'temp_air', 'wind_speed', + 'relative_humidity', 'ghi_clear', 'dni_clear', + 'dhi_clear', 'albedo'] + for c in mapped_column_names: + assert c in data.columns + assert meta['latitude'] == 44.4675 + assert meta['longitude'] == -73.2075 + assert meta['altitude'] == 41.0 + + +def test_read_solaranywhere_tmy(tmy_index, tmy_ghi_series): + # Check that TMY files are correctly parsed + data, meta = pvlib.iotools.read_solaranywhere(TESTFILE_TMY) + # Check that columns names are correct and mapped to pvlib names + assert 'ghi' in data.columns + assert 'dni' in data.columns + assert 'dhi' in data.columns + assert 'temp_air' in data.columns + # Check that metadata is parsed correctly + assert meta['latitude'] == 44.465 + assert meta['longitude'] == -73.205 + assert meta['altitude'] == 41.0 + assert meta['name'] == 'Burlington United States' + assert meta['TZ'] == -5.0 + assert meta['Data Version'] == '3.6' + assert meta['LatLon Resolution'] == '0.010' + assert meta['Time Resolution'] == '60 minutes' + # Assert that the index is parsed correctly + pd.testing.assert_index_equal(data.index, tmy_index) + # Test one column + pd.testing.assert_series_equal(data['ghi'], tmy_ghi_series) + + +def test_get_solaranywhere_bad_probability_of_exceedance(): + # Test if ValueError is raised if probability_of_exceedance is not integer + with pytest.raises(ValueError, match="must be an integer"): + pvlib.iotools.get_solaranywhere( + latitude=44, longitude=-73, api_key='empty', + source='SolarAnywherePOELatest', probability_of_exceedance=0.5) + + +def test_get_solaranywhere_missing_start_end(): + # Test if ValueError is raised if start/end is missing for non-TMY request + with pytest.raises(ValueError, match="`end` is required."): + pvlib.iotools.get_solaranywhere( + latitude=44, longitude=-73, api_key='empty', + source='SolarAnywhereLatest') + + +@pytest.fixture +def time_series_index(): + index = pd.date_range(start='2020-01-01 00:02:30', periods=288, + freq='5min', tz='UTC') + index.name = 'ObservationTime' + index.freq = None + return index + + +@pytest.fixture +def timeseries_dni_series(time_series_index): + dni = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 9, 10, 1, 3, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 3, 3, 12, 8, 9, 9, 11, 4, 8, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 + ] + return pd.Series(data=dni, index=time_series_index, name='dni') + + +@requires_solaranywhere_credentials +@pytest.mark.remote_data +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) +def test_get_solaranywhere_no_timezone_information( + high_resolution_index, solaranywhere_api_key, time_series_index, + timeseries_dni_series): + # Test if data can be retrieved. This test only retrieves one day of data + # to minimize the request time. + data, meta = pvlib.iotools.get_solaranywhere( + latitude=44.4675, longitude=-73.2075, api_key=solaranywhere_api_key, + # test specific version of SolarAnywhere + source='SolarAnywhere3_6', + # specify start/end without timezone information + start=pd.Timestamp(2020, 1, 1), end=pd.Timestamp(2020, 1, 2), + spatial_resolution=0.005, time_resolution=5, true_dynamics=True) + + # Check metadta and that true-dynamics is set + assert meta['WeatherSiteName'] == 'SolarAnywhere3_6' + assert meta['ApplyTrueDynamics'] is True + assert meta['time_resolution'] == 5 + assert meta['latitude'] == 44.4675 + assert meta['longitude'] == -73.2075 + assert meta['altitude'] == 41.0 + + # Check that variables have been mapped (default convention) + assert 'StartTime' in data.columns + assert 'ObservationTime' in data.columns + assert 'EndTime' in data.columns + assert 'ghi' in data.columns + assert 'dni' in data.columns + assert 'dhi' in data.columns + assert 'temp_air' in data.columns + assert 'wind_speed' in data.columns + assert 'albedo' in data.columns + assert 'DataVersion' in data.columns + + # Assert index (checks that time resolution is 5 min) + pd.testing.assert_index_equal(data.index, time_series_index) + # Test one column + pd.testing.assert_series_equal(data['dni'], timeseries_dni_series) + + +def test_get_solaranywhere_max_response_time(solaranywhere_api_key): + # Test if ValueError is raised if start/end is missing for non-TMY request + with pytest.raises(TimeoutError, match="Time exceeded"): + pvlib.iotools.get_solaranywhere( + latitude=44.4675, longitude=-73.2075, + api_key=solaranywhere_api_key, + start=pd.Timestamp('2020-01-01 00:00:00+0000'), + end=pd.Timestamp('2020-01-05 12:00:00+0000'), + max_response_time=0.00001) + + +def test_get_solaranywhere_not_available(solaranywhere_api_key): + # Test if RuntimeError is raised if location in the ocean is requested + with pytest.raises(RuntimeError, match="Tile is outside of our coverage"): + pvlib.iotools.get_solaranywhere( + latitude=40, longitude=-70, + api_key=solaranywhere_api_key, + start=pd.Timestamp('2020-01-01 00:00:00+0000'), + end=pd.Timestamp('2020-01-05 12:00:00+0000')) + + +# Second live test +# True dynamics is False +# Different variables + +# Mock tets: +# TGY From 7b0fd25e7362ba1ebccd6568cdc6f624796a78fd Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 16 Aug 2022 17:26:03 +0200 Subject: [PATCH 11/31] Set encoding to iso-8859-1 --- pvlib/iotools/solaranywhere.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index c401940306..8c9efd11a7 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -253,7 +253,7 @@ def read_solaranywhere(filename, map_variables=True): .. [1] `SolarAnywhere historical data file formats `_ """ - with open(str(filename), 'r') as fbuf: + with open(str(filename), 'r', encoding='iso-8859-1') as fbuf: content = parse_solaranywhere(fbuf, map_variables=map_variables) return content From acff9e1b32f6cf6c4690ac99b8cc9148417d67f6 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Thu, 18 Aug 2022 13:45:11 +0200 Subject: [PATCH 12/31] Remove solaranywhere_api_key --- .github/workflows/pytest-remote-data.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/pytest-remote-data.yml b/.github/workflows/pytest-remote-data.yml index 347871658b..ad57d2c175 100644 --- a/.github/workflows/pytest-remote-data.yml +++ b/.github/workflows/pytest-remote-data.yml @@ -97,7 +97,6 @@ jobs: env: # copy GitHub Secrets into environment variables for the tests to access NREL_API_KEY: ${{ secrets.NRELAPIKEY }} - SOLARANYWHERE_API_KEY: ${{ secrets.SOLARANYWHERE_API_KEY }} BSRN_FTP_USERNAME: ${{ secrets.BSRN_FTP_USERNAME }} BSRN_FTP_PASSWORD: ${{ secrets.BSRN_FTP_PASSWORD }} run: pytest pvlib/tests/iotools pvlib/tests/test_forecast.py --cov=./ --cov-report=xml --remote-data From 192e1b3d56349277aea8db0a95bea78b9f8be69b Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Thu, 18 Aug 2022 13:52:25 +0200 Subject: [PATCH 13/31] Remove parse_solaranywhere I am removing the parse_solaranywhere function since the get_solaranywhere function is not using it (the API retrieval uses a different file structure than the downloadable fiels). --- docs/sphinx/source/reference/iotools.rst | 1 - docs/sphinx/source/whatsnew/v0.9.2.rst | 2 +- pvlib/iotools/__init__.py | 1 - pvlib/iotools/solaranywhere.py | 46 ++++-------------------- 4 files changed, 7 insertions(+), 43 deletions(-) diff --git a/docs/sphinx/source/reference/iotools.rst b/docs/sphinx/source/reference/iotools.rst index 3862923d3f..575aeae4cd 100644 --- a/docs/sphinx/source/reference/iotools.rst +++ b/docs/sphinx/source/reference/iotools.rst @@ -39,7 +39,6 @@ of sources and file formats relevant to solar energy modeling. iotools.parse_cams iotools.get_solaranywhere iotools.read_solaranywhere - iotools.parse_solaranywhere A :py:class:`~pvlib.location.Location` object may be created from metadata in some files. diff --git a/docs/sphinx/source/whatsnew/v0.9.2.rst b/docs/sphinx/source/whatsnew/v0.9.2.rst index 3c498a3625..7906a86a23 100644 --- a/docs/sphinx/source/whatsnew/v0.9.2.rst +++ b/docs/sphinx/source/whatsnew/v0.9.2.rst @@ -16,7 +16,7 @@ Enhancements rotation angles. (:issue:`1471`, :pull:`1480`) * Add :py:func:`pvlib.iotools.read_solaranywhere` and :py:func:`pvlib.iotools.get_solaranywhere` for reading and retrieving - SolarAnywhere solar irradiance data. (:pull:`1497`, :discussion:`1310`) + SolarAnywhere solar irradiance data. (:pull:`1497`) * Improve error message about uneven time intervals for :py:func:`~pvlib.clearsky.detect_clearsky` and :py:func:`~pvlib.temperature.prilliman` (:issue:`1476`, :pull:`1490`) diff --git a/pvlib/iotools/__init__.py b/pvlib/iotools/__init__.py index 4b471d90b4..5fc5933325 100644 --- a/pvlib/iotools/__init__.py +++ b/pvlib/iotools/__init__.py @@ -23,4 +23,3 @@ from pvlib.iotools.sodapro import parse_cams # noqa: F401 from pvlib.iotools.solaranywhere import get_solaranywhere # noqa: F401 from pvlib.iotools.solaranywhere import read_solaranywhere # noqa: F401 -from pvlib.iotools.solaranywhere import parse_solaranywhere # noqa: F401 diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 8c9efd11a7..88e5823be6 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -246,7 +246,7 @@ def read_solaranywhere(filename, map_variables=True): See Also -------- - pvlib.iotools.get_solaranywhere, pvlib.iotools.parse_solaranywhere + pvlib.iotools.get_solaranywhere References ---------- @@ -254,44 +254,12 @@ def read_solaranywhere(filename, map_variables=True): `_ """ with open(str(filename), 'r', encoding='iso-8859-1') as fbuf: - content = parse_solaranywhere(fbuf, map_variables=map_variables) - return content + # Extract first line of file which contains the metadata + firstline = fbuf.readline().strip().split(',') + # Read remaining part of file which contains the time series data + data = pd.read_csv(fbuf) - -def parse_solaranywhere(fbuf, map_variables=True): - """ - Parse a file-like buffer with data in the format of a SolarAnywhere file. - - The SolarAnywhere file format and variables are described in [1]_. Note, - the SolarAnywhere file format resembles the TMY3 file format but contains - additional variables and meatadata. - - Parameters - ---------- - fbuf: file-like object - File-like object containing data to read. - map_variables: bool, default: True - When true, renames columns of the DataFrame to pvlib variable names - where applicable. See variable :const:`VARIABLE_MAP`. - - Returns - ------- - data: pandas.DataFrame - Timeseries data from SolarAnywhere. Index is localized to UTC. - metadata: dict - Metadata available in the file. - - See Also - -------- - pvlib.iotools.read_solaranywhere, pvlib.iotools.get_solaranywhere - - References - ---------- - .. [1] `SolarAnywhere historical data file formats - `_ - """ - # Parse metadata contained within the first line - firstline = fbuf.readline().strip().split(',') + # Parse metadata meta = {} meta['USAF'] = int(firstline.pop(0)) meta['name'] = firstline.pop(0) @@ -310,8 +278,6 @@ def parse_solaranywhere(fbuf, map_variables=True): k, v = i.split(':') meta[k.strip()] = v.strip() - # Read remaining part of file which contains the time series data - data = pd.read_csv(fbuf) # Set index to UTC data.index = pd.to_datetime(data['ObservationTime(GMT)'], format='%m/%d/%Y %H:%M', utc=True) From 1917da1056f432e96d3cd4e1b00c26573ef28edc Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Thu, 18 Aug 2022 15:20:08 +0200 Subject: [PATCH 14/31] Update tests --- pvlib/tests/iotools/test_solaranywhere.py | 43 +++++++++++++---------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/pvlib/tests/iotools/test_solaranywhere.py b/pvlib/tests/iotools/test_solaranywhere.py index 5b4978fb9a..9972b4b7f2 100644 --- a/pvlib/tests/iotools/test_solaranywhere.py +++ b/pvlib/tests/iotools/test_solaranywhere.py @@ -135,31 +135,30 @@ def time_series_index(): @pytest.fixture -def timeseries_dni_series(time_series_index): - dni = [ +def timeseries_temp_air(time_series_index): + temp_air = [ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 9, 10, 1, 3, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 0, 3, 3, 12, 8, 9, 9, 11, 4, 8, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ] - return pd.Series(data=dni, index=time_series_index, name='dni') + return pd.Series(data=temp_air, index=time_series_index, name='temp_air') -@requires_solaranywhere_credentials +#@requires_solaranywhere_credentials @pytest.mark.remote_data @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) def test_get_solaranywhere_no_timezone_information( high_resolution_index, solaranywhere_api_key, time_series_index, - timeseries_dni_series): + timeseries_temp_air): # Test if data can be retrieved. This test only retrieves one day of data # to minimize the request time. data, meta = pvlib.iotools.get_solaranywhere( @@ -170,7 +169,7 @@ def test_get_solaranywhere_no_timezone_information( start=pd.Timestamp(2020, 1, 1), end=pd.Timestamp(2020, 1, 2), spatial_resolution=0.005, time_resolution=5, true_dynamics=True) - # Check metadta and that true-dynamics is set + # Check metadata, including that true-dynamics is set assert meta['WeatherSiteName'] == 'SolarAnywhere3_6' assert meta['ApplyTrueDynamics'] is True assert meta['time_resolution'] == 5 @@ -193,9 +192,12 @@ def test_get_solaranywhere_no_timezone_information( # Assert index (checks that time resolution is 5 min) pd.testing.assert_index_equal(data.index, time_series_index) # Test one column - pd.testing.assert_series_equal(data['dni'], timeseries_dni_series) + pd.testing.assert_series_equal(data['temp_air'], timeseries_temp_air) +@requires_solaranywhere_credentials +@pytest.mark.remote_data +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) def test_get_solaranywhere_max_response_time(solaranywhere_api_key): # Test if ValueError is raised if start/end is missing for non-TMY request with pytest.raises(TimeoutError, match="Time exceeded"): @@ -207,6 +209,9 @@ def test_get_solaranywhere_max_response_time(solaranywhere_api_key): max_response_time=0.00001) +@requires_solaranywhere_credentials +@pytest.mark.remote_data +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) def test_get_solaranywhere_not_available(solaranywhere_api_key): # Test if RuntimeError is raised if location in the ocean is requested with pytest.raises(RuntimeError, match="Tile is outside of our coverage"): From 2ec4627ee14463d7c0635ab17cac4169182e2bc6 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Thu, 6 Oct 2022 12:09:02 +0200 Subject: [PATCH 15/31] Update error message handling --- pvlib/iotools/solaranywhere.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 88e5823be6..3eb9629db1 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -163,11 +163,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, probability_of_exceedance # Add start/end time if requesting non-TMY data - if (('TGY' not in source) & ('TDY' not in source) & ('TMY' not in source) & - ('POE' not in source)): - if (start is None) or (end is None): - raise ValueError('When requesting non-TMY data, specifying `start`' - ' and `end` is required.') + if (start is not None) or (end is not None): # start/end are required to have an associated time zone if start.tz is None: start = start.tz_localize('UTC') @@ -195,10 +191,8 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, results_json = results.json() if results_json.get('Status') == 'Done': if results_json['WeatherDataResults'][0]['Status'] == 'Failure': - raise RuntimeError(results_json['WeatherDataResults'][0]['ErrorMessages']) # noqa: E501 + raise RuntimeError(results_json['WeatherDataResults'][0]['ErrorMessages'][0]['Message']) # noqa: E501 break - elif results_json.get('StatusCode') == 'BadRequest': - raise RuntimeError(f"Bad request: {results_json['Message']}") elif (time.time()-start_time) > max_response_time: raise TimeoutError('Time exceeded the `max_response_time`.') time.sleep(5) # Sleep for 5 seconds before each data retrieval attempt From ece667f2bc3dda718e24fc5bb20203a727b66149 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:24:24 +0100 Subject: [PATCH 16/31] Update iotools.rst --- docs/sphinx/source/reference/iotools.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sphinx/source/reference/iotools.rst b/docs/sphinx/source/reference/iotools.rst index b41a30b203..6e55400d0c 100644 --- a/docs/sphinx/source/reference/iotools.rst +++ b/docs/sphinx/source/reference/iotools.rst @@ -46,7 +46,8 @@ of sources and file formats relevant to solar energy modeling. iotools.get_acis_station_data iotools.get_acis_available_stations iotools.read_panond - + iotools.get_solaranywhere + iotools.read_solaranywhere A :py:class:`~pvlib.location.Location` object may be created from metadata in some files. From 2711135c82374de2ec0f942451414177b71d0f8c Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:24:28 +0100 Subject: [PATCH 17/31] Update v0.10.3.rst --- docs/sphinx/source/whatsnew/v0.10.3.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index 30fd46648f..e6eb5f4f83 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -17,6 +17,9 @@ Enhancements shaded fraction in returned variables. (:pull:`1871`) * Added :py:func:`~pvlib.iam.convert` and :py:func:`~pvlib.iam.fit` that convert between IAM models, and that fit an IAM model to data. (:issue:`1824`, :pull:`1827`) +* Add :py:func:`pvlib.iotools.read_solaranywhere` and + :py:func:`pvlib.iotools.get_solaranywhere` for reading and retrieving + SolarAnywhere solar irradiance data. (:pull:`1497`, :discussion:`1310`) Bug fixes ~~~~~~~~~ From 3f440a98e3ffd118580136496a53a889337ce6d6 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:24:31 +0100 Subject: [PATCH 18/31] Update __init__.py --- pvlib/iotools/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pvlib/iotools/__init__.py b/pvlib/iotools/__init__.py index 9935719b29..90a610fb63 100644 --- a/pvlib/iotools/__init__.py +++ b/pvlib/iotools/__init__.py @@ -27,3 +27,5 @@ from pvlib.iotools.acis import get_acis_mpe # noqa: F401 from pvlib.iotools.acis import get_acis_station_data # noqa: F401 from pvlib.iotools.acis import get_acis_available_stations # noqa: F401 +from pvlib.iotools.solaranywhere import get_solaranywhere # noqa: F401 +from pvlib.iotools.solaranywhere import read_solaranywhere # noqa: F401 From 40ed8340dca128479b1f43476fa24782b3796584 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:33:28 +0100 Subject: [PATCH 19/31] Address code review by kandersolar --- pvlib/iotools/solaranywhere.py | 22 ++++++++++++---------- pvlib/tests/iotools/test_solaranywhere.py | 8 ++++---- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 3eb9629db1..9fdfd83d0e 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -49,7 +49,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, spatial_resolution=0.01, true_dynamics=False, probability_of_exceedance=None, variables=DEFAULT_VARIABLES, missing_data='FillAverage', - url=URL, map_variables=True, max_response_time=300): + url=URL, map_variables=True, timeout=300): """Retrieve historical irradiance time series data from SolarAnywhere. The SolarAnywhere API is described in [1]_ and [2]_. A detailed list of @@ -85,7 +85,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, Only available for the 5-min time resolution. probability_of_exceedance: int, optional Probability of exceedance in the range of 1 to 99. Only relevant when - requesting probability of exceedance (POE) time series. + requesting probability of exceedance (POE) time series. [%] variables: list-like, default: :const:`DEFAULT_VARIABLES` Variables to retrieve (described in [4]_). Available variables depend on whether historical or TMY data is requested. @@ -96,7 +96,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, map_variables: bool, default: True When true, renames columns of the DataFrame to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. - max_response_time: float, default: 300 + timeout: float, default: 300 Time in seconds to wait for requested data to become available. Returns @@ -193,15 +193,14 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, if results_json['WeatherDataResults'][0]['Status'] == 'Failure': raise RuntimeError(results_json['WeatherDataResults'][0]['ErrorMessages'][0]['Message']) # noqa: E501 break - elif (time.time()-start_time) > max_response_time: - raise TimeoutError('Time exceeded the `max_response_time`.') + elif (time.time()-start_time) > timeout: + raise TimeoutError('Time exceeded the `timeout`.') time.sleep(5) # Sleep for 5 seconds before each data retrieval attempt # Extract time series data data = pd.DataFrame(results_json['WeatherDataResults'][0]['WeatherDataPeriods']['WeatherDataPeriods']) # noqa: E501 # Set index and convert to UTC time data.index = pd.to_datetime(data['ObservationTime']) - data.index = data.index.tz_convert('UTC') if map_variables: data = data.rename(columns=VARIABLE_MAP) @@ -215,7 +214,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, return data, meta -def read_solaranywhere(filename, map_variables=True): +def read_solaranywhere(filename, map_variables=True, encoding='iso-8859-1'): """ Read a SolarAnywhere formatted file into a pandas DataFrame. @@ -225,11 +224,14 @@ def read_solaranywhere(filename, map_variables=True): Parameters ---------- - fbuf: file-like object - File-like object containing data to read. + fbuf: str + Filename map_variables: bool, default: True When true, renames columns of the DataFrame to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. + encoding : str, default : 'iso-8859-1' + Encoding of the file. For SolarAnywhere TMY3 files the 'iso-8859-1' + encoding is recommended due to the usage of special characters. Returns ------- @@ -247,7 +249,7 @@ def read_solaranywhere(filename, map_variables=True): .. [1] `SolarAnywhere historical data file formats `_ """ - with open(str(filename), 'r', encoding='iso-8859-1') as fbuf: + with open(str(filename), 'r', encoding=encoding) as fbuf: # Extract first line of file which contains the metadata firstline = fbuf.readline().strip().split(',') # Read remaining part of file which contains the time series data diff --git a/pvlib/tests/iotools/test_solaranywhere.py b/pvlib/tests/iotools/test_solaranywhere.py index 9972b4b7f2..7cf7173e61 100644 --- a/pvlib/tests/iotools/test_solaranywhere.py +++ b/pvlib/tests/iotools/test_solaranywhere.py @@ -153,7 +153,7 @@ def timeseries_temp_air(time_series_index): return pd.Series(data=temp_air, index=time_series_index, name='temp_air') -#@requires_solaranywhere_credentials +@requires_solaranywhere_credentials @pytest.mark.remote_data @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) def test_get_solaranywhere_no_timezone_information( @@ -198,15 +198,15 @@ def test_get_solaranywhere_no_timezone_information( @requires_solaranywhere_credentials @pytest.mark.remote_data @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_solaranywhere_max_response_time(solaranywhere_api_key): - # Test if ValueError is raised if start/end is missing for non-TMY request +def test_get_solaranywhere_timeout(solaranywhere_api_key): + # Test if the service times out when the timeout parameter is close to zero with pytest.raises(TimeoutError, match="Time exceeded"): pvlib.iotools.get_solaranywhere( latitude=44.4675, longitude=-73.2075, api_key=solaranywhere_api_key, start=pd.Timestamp('2020-01-01 00:00:00+0000'), end=pd.Timestamp('2020-01-05 12:00:00+0000'), - max_response_time=0.00001) + timeout=0.00001) @requires_solaranywhere_credentials From 59216d27dd47f9f73b40c016946bba83990afd40 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 11:53:42 +0100 Subject: [PATCH 20/31] Update v0.9.2.rst --- docs/sphinx/source/whatsnew/v0.9.2.rst | 51 +++++++++----------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.9.2.rst b/docs/sphinx/source/whatsnew/v0.9.2.rst index 10656a89c7..30c84cd005 100644 --- a/docs/sphinx/source/whatsnew/v0.9.2.rst +++ b/docs/sphinx/source/whatsnew/v0.9.2.rst @@ -1,66 +1,59 @@ .. _whatsnew_0920: -v0.9.2 (August 19, 2022) ------------------------- +v0.9.2 (TBD) +----------------------- + +Deprecations +~~~~~~~~~~~~ Enhancements ~~~~~~~~~~~~ -* albedo can now be provided as a column in the `weather` DataFrame input to - :py:meth:`pvlib.modelchain.ModelChain.run_model`. (:issue:`1387`, :pull:`1478`) -* albedo is now available as an input to :py:meth:`pvlib.pvsystem.PVSystem.get_irradiance` - and :py:meth:`pvlib.pvsystem.Array.get_irradiance`. (:pull:`1478`) * :py:func:`pvlib.iotools.read_surfrad` now also accepts remote files - with https links in addition to files on the SURFRAD FTP server. + with https links in addition to files on the SURFRAD FTP server (:pull:`1459`) * Add :py:func:`pvlib.tracking.calc_surface_orientation` for calculating single-axis tracker ``surface_tilt`` and ``surface_azimuth`` from rotation angles. (:issue:`1471`, :pull:`1480`) -* Add :py:func:`pvlib.iotools.read_solaranywhere` and - :py:func:`pvlib.iotools.get_solaranywhere` for reading and retrieving - SolarAnywhere solar irradiance data. (:pull:`1497`) * Improve error message about uneven time intervals for - :py:func:`~pvlib.clearsky.detect_clearsky` and :py:func:`~pvlib.temperature.prilliman`. + :py:func:`~pvlib.clearsky.detect_clearsky` and :py:func:`~pvlib.temperature.prilliman` (:issue:`1476`, :pull:`1490`) -* Add support for `PEP517 `_ & `PEP518 `_ - with setuptools build backend. (:pull:`1495`) - Bug fixes ~~~~~~~~~ * :py:func:`pvlib.irradiance.get_total_irradiance` and :py:func:`pvlib.solarposition.spa_python` now raise an error instead - of silently ignoring unknown parameters. (:pull:`1437`) + of silently ignoring unknown parameters (:pull:`1437`) * Fix a bug in :py:func:`pvlib.solarposition.sun_rise_set_transit_ephem` where passing localized timezones with large UTC offsets could return - rise/set/transit times for the wrong day in recent versions of ``ephem``. + rise/set/transit times for the wrong day in recent versions of ``ephem`` (:issue:`1449`, :pull:`1448`) * :py:func:`pvlib.iotools.read_tmy3` is now able to accept midnight timestamps as either 24:00 (which is the standard) as well as 00:00. Previously 00:00 timestamps would incorrectly be moved one day forward. (:pull:`1494`) * :py:func:`pvlib.iotools.get_psm3` now raises a deprecation warning if - the ``leap_day`` parameter is not specified in a single-year request. - Starting in pvlib 0.11.0 ``leap_day`` will default to True instead of False. + the `leap_day` parameter is not specified in a single-year request. + Starting in pvlib 0.11.0 `leap_day` will default to True instead of False. (:issue:`1481`, :pull:`1511`) Testing ~~~~~~~ -* Switched CI testing provider from Azure to GitHub Actions. (:pull:`1306`) -* Speed up CI setup using micromamba instead of conda. (:pull:`1493`) -* Drop python 3.6 (reached end of life Dec 2021) and add 3.10 to test matrix. (:pull:`1507`) +* Switched CI testing provider from Azure to GitHub Actions (:pull:`1306`) +* Speed up CI setup using micromamba instead of conda (:pull:`1493`) +* Drop python 3.6 (reached end of life Dec 2021) and add 3.10 to test matrix (:pull:`1507`) + Documentation ~~~~~~~~~~~~~ * Added a reference to :py:func:`pvlib.inverter.sandia_multi`. (:pull:`1479`) * Add gallery example of simulating rearside irradiance for a fixed-tilt - array with pvfactors. (:pull:`1470`) -* Updated reference links to CAMS Radiation. (:issue:`1515`, :pull:`1529`) + array with pvfactors (:pull:`1470`) Benchmarking ~~~~~~~~~~~~~ * Updated version of numba in asv.conf from 0.36.1 to 0.40.0 to solve numba/numpy conflict. (:issue:`1439`, :pull:`1440`) -* Added benchmarks for the ``pvlib.scaling`` module. (:pull:`1445`) -* Added a basic CI asv check. (:issue:`1446`, :pull:`1454`) +* Added benchmarks for the `pvlib.scaling` module (:pull:`1445`) +* Added a basic CI asv check (:issue:`1446`, :pull:`1454`) Requirements ~~~~~~~~~~~~ @@ -73,16 +66,8 @@ Contributors * Naman Priyadarshi (:ghuser:`Naman-Priyadarshi`) * Chencheng Luo (:ghuser:`roger-lcc`) * Prajwal Borkar (:ghuser:`PrajwalBorkar`) -* Cliff Hansen (:ghuser:`cwhanse`) * Kevin Anderson (:ghuser:`kanderso-nrel`) * Cliff Hansen (:ghuser:`cwhanse`) * Jules Chéron (:ghuser:`jules-ch`) * Kurt Rhee (:ghuser:`kurt-rhee`) * Will Hobbs (:ghuser:`williamhobbs`) -* Stephen Schneider (:ghuser:`sjschneider`) -* :ghuser:`Kaesekopf` -* :ghuser:`hf-kklein` -* Mark Campanelli (:ghuser:`campanelli-sunpower`) -* Anton Driesse (:ghuser:`adriesse`) -* Kristen Wagner (:ghuser:`kwagnercpr`) -* Mark Mikofski (:ghuser:`mikofski`) From 6c3ca6172ef5647b1928aa211abcf83a3f6c6a75 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 13:04:27 +0100 Subject: [PATCH 21/31] Update v0.9.2.rst --- docs/sphinx/source/whatsnew/v0.9.2.rst | 48 ++++++++++++++++---------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.9.2.rst b/docs/sphinx/source/whatsnew/v0.9.2.rst index 30c84cd005..2616734036 100644 --- a/docs/sphinx/source/whatsnew/v0.9.2.rst +++ b/docs/sphinx/source/whatsnew/v0.9.2.rst @@ -1,59 +1,63 @@ .. _whatsnew_0920: -v0.9.2 (TBD) ------------------------ - -Deprecations -~~~~~~~~~~~~ +v0.9.2 (August 19, 2022) +------------------------ Enhancements ~~~~~~~~~~~~ +* albedo can now be provided as a column in the `weather` DataFrame input to + :py:meth:`pvlib.modelchain.ModelChain.run_model`. (:issue:`1387`, :pull:`1478`) +* albedo is now available as an input to :py:meth:`pvlib.pvsystem.PVSystem.get_irradiance` + and :py:meth:`pvlib.pvsystem.Array.get_irradiance`. (:pull:`1478`) * :py:func:`pvlib.iotools.read_surfrad` now also accepts remote files - with https links in addition to files on the SURFRAD FTP server + with https links in addition to files on the SURFRAD FTP server. (:pull:`1459`) * Add :py:func:`pvlib.tracking.calc_surface_orientation` for calculating single-axis tracker ``surface_tilt`` and ``surface_azimuth`` from rotation angles. (:issue:`1471`, :pull:`1480`) * Improve error message about uneven time intervals for - :py:func:`~pvlib.clearsky.detect_clearsky` and :py:func:`~pvlib.temperature.prilliman` + :py:func:`~pvlib.clearsky.detect_clearsky` and :py:func:`~pvlib.temperature.prilliman`. (:issue:`1476`, :pull:`1490`) +* Add support for `PEP517 `_ & `PEP518 `_ + with setuptools build backend. (:pull:`1495`) + Bug fixes ~~~~~~~~~ * :py:func:`pvlib.irradiance.get_total_irradiance` and :py:func:`pvlib.solarposition.spa_python` now raise an error instead - of silently ignoring unknown parameters (:pull:`1437`) + of silently ignoring unknown parameters. (:pull:`1437`) * Fix a bug in :py:func:`pvlib.solarposition.sun_rise_set_transit_ephem` where passing localized timezones with large UTC offsets could return - rise/set/transit times for the wrong day in recent versions of ``ephem`` + rise/set/transit times for the wrong day in recent versions of ``ephem``. (:issue:`1449`, :pull:`1448`) * :py:func:`pvlib.iotools.read_tmy3` is now able to accept midnight timestamps as either 24:00 (which is the standard) as well as 00:00. Previously 00:00 timestamps would incorrectly be moved one day forward. (:pull:`1494`) * :py:func:`pvlib.iotools.get_psm3` now raises a deprecation warning if - the `leap_day` parameter is not specified in a single-year request. - Starting in pvlib 0.11.0 `leap_day` will default to True instead of False. + the ``leap_day`` parameter is not specified in a single-year request. + Starting in pvlib 0.11.0 ``leap_day`` will default to True instead of False. (:issue:`1481`, :pull:`1511`) Testing ~~~~~~~ -* Switched CI testing provider from Azure to GitHub Actions (:pull:`1306`) -* Speed up CI setup using micromamba instead of conda (:pull:`1493`) -* Drop python 3.6 (reached end of life Dec 2021) and add 3.10 to test matrix (:pull:`1507`) - +* Switched CI testing provider from Azure to GitHub Actions. (:pull:`1306`) +* Speed up CI setup using micromamba instead of conda. (:pull:`1493`) +* Drop python 3.6 (reached end of life Dec 2021) and add 3.10 to test matrix. (:pull:`1507`) Documentation ~~~~~~~~~~~~~ * Added a reference to :py:func:`pvlib.inverter.sandia_multi`. (:pull:`1479`) * Add gallery example of simulating rearside irradiance for a fixed-tilt - array with pvfactors (:pull:`1470`) + array with pvfactors. (:pull:`1470`) +* Updated reference links to CAMS Radiation. (:issue:`1515`, :pull:`1529`) Benchmarking ~~~~~~~~~~~~~ * Updated version of numba in asv.conf from 0.36.1 to 0.40.0 to solve numba/numpy conflict. (:issue:`1439`, :pull:`1440`) -* Added benchmarks for the `pvlib.scaling` module (:pull:`1445`) -* Added a basic CI asv check (:issue:`1446`, :pull:`1454`) +* Added benchmarks for the ``pvlib.scaling`` module. (:pull:`1445`) +* Added a basic CI asv check. (:issue:`1446`, :pull:`1454`) Requirements ~~~~~~~~~~~~ @@ -66,8 +70,16 @@ Contributors * Naman Priyadarshi (:ghuser:`Naman-Priyadarshi`) * Chencheng Luo (:ghuser:`roger-lcc`) * Prajwal Borkar (:ghuser:`PrajwalBorkar`) +* Cliff Hansen (:ghuser:`cwhanse`) * Kevin Anderson (:ghuser:`kanderso-nrel`) * Cliff Hansen (:ghuser:`cwhanse`) * Jules Chéron (:ghuser:`jules-ch`) * Kurt Rhee (:ghuser:`kurt-rhee`) * Will Hobbs (:ghuser:`williamhobbs`) +* Stephen Schneider (:ghuser:`sjschneider`) +* :ghuser:`Kaesekopf` +* :ghuser:`hf-kklein` +* Mark Campanelli (:ghuser:`campanelli-sunpower`) +* Anton Driesse (:ghuser:`adriesse`) +* Kristen Wagner (:ghuser:`kwagnercpr`) +* Mark Mikofski (:ghuser:`mikofski`) \ No newline at end of file From e0abc762eb396fcd766de82acc96fcc4f0ef9b7d Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 13:04:44 +0100 Subject: [PATCH 22/31] Update tests --- pvlib/tests/iotools/test_solaranywhere.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pvlib/tests/iotools/test_solaranywhere.py b/pvlib/tests/iotools/test_solaranywhere.py index 7cf7173e61..04ea6b5286 100644 --- a/pvlib/tests/iotools/test_solaranywhere.py +++ b/pvlib/tests/iotools/test_solaranywhere.py @@ -109,6 +109,8 @@ def test_read_solaranywhere_tmy(tmy_index, tmy_ghi_series): pd.testing.assert_series_equal(data['ghi'], tmy_ghi_series) +@pytest.mark.remote_data +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) def test_get_solaranywhere_bad_probability_of_exceedance(): # Test if ValueError is raised if probability_of_exceedance is not integer with pytest.raises(ValueError, match="must be an integer"): @@ -117,11 +119,14 @@ def test_get_solaranywhere_bad_probability_of_exceedance(): source='SolarAnywherePOELatest', probability_of_exceedance=0.5) -def test_get_solaranywhere_missing_start_end(): +@pytest.mark.remote_data +@requires_solaranywhere_credentials +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) +def test_get_solaranywhere_missing_start_end(solaranywhere_api_key): # Test if ValueError is raised if start/end is missing for non-TMY request with pytest.raises(ValueError, match="`end` is required."): pvlib.iotools.get_solaranywhere( - latitude=44, longitude=-73, api_key='empty', + latitude=44, longitude=-73, api_key=solaranywhere_api_key, source='SolarAnywhereLatest') From de116aa9b7962a610be34c0172cd446a4a694bb9 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 15:59:20 +0100 Subject: [PATCH 23/31] Update flake8.yml --- .github/workflows/flake8.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index eabdbdbf38..16873b4fb6 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -20,7 +20,7 @@ jobs: git remote add upstream https://github.com/pvlib/pvlib-python.git git fetch upstream $GITHUB_BASE_REF - name: Run Flake8 linter - run: git diff upstream/$GITHUB_BASE_REF HEAD | flake8 + run: git diff upstream/main -- "*.py" | flake8 --exclude pvlib/version.py --ignore E201,E241,E226,W503,W504 --max-line-length 79 From 4744615300b534453bd7f17c94311010341098b3 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:23:11 +0100 Subject: [PATCH 24/31] Update v0.10.3.rst --- docs/sphinx/source/whatsnew/v0.10.3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index e6eb5f4f83..4a73078448 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -19,7 +19,7 @@ Enhancements convert between IAM models, and that fit an IAM model to data. (:issue:`1824`, :pull:`1827`) * Add :py:func:`pvlib.iotools.read_solaranywhere` and :py:func:`pvlib.iotools.get_solaranywhere` for reading and retrieving - SolarAnywhere solar irradiance data. (:pull:`1497`, :discussion:`1310`) + SolarAnywhere solar irradiance data. (:pull:`1497`, :discuss:`1310`) Bug fixes ~~~~~~~~~ From 0bcb74d4d307fce000b3f47974d4e72372bc78fd Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:23:21 +0100 Subject: [PATCH 25/31] Update solaranywhere documentation --- pvlib/iotools/solaranywhere.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index 9fdfd83d0e..cefd98db5a 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -87,8 +87,9 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, Probability of exceedance in the range of 1 to 99. Only relevant when requesting probability of exceedance (POE) time series. [%] variables: list-like, default: :const:`DEFAULT_VARIABLES` - Variables to retrieve (described in [4]_). Available variables depend - on whether historical or TMY data is requested. + Variables to retrieve (described in [4]_), must include + 'ObservationTime'. Available variables depend on whether historical or + TMY data is requested. missing_data: {'Omit', 'FillAverage'}, default: 'FillAverage' Method for treating missing data. url: str, default: :const:`pvlib.iotools.solaranywhere.URL` @@ -103,7 +104,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, ------- data: pandas.DataFrame Timeseries data from SolarAnywhere. The index is the observation time - (middle of period) localized to UTC. + (middle of period). metadata: dict Metadata available (includes site latitude, longitude, and altitude). @@ -164,6 +165,9 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, # Add start/end time if requesting non-TMY data if (start is not None) or (end is not None): + # Convert start/end to datetime in case they are specified as strings + start = pd.to_datetime(start) + end = pd.to_datetime(end) # start/end are required to have an associated time zone if start.tz is None: start = start.tz_localize('UTC') @@ -199,7 +203,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, # Extract time series data data = pd.DataFrame(results_json['WeatherDataResults'][0]['WeatherDataPeriods']['WeatherDataPeriods']) # noqa: E501 - # Set index and convert to UTC time + # Set datetime index data.index = pd.to_datetime(data['ObservationTime']) if map_variables: data = data.rename(columns=VARIABLE_MAP) @@ -207,6 +211,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, # Parse metadata meta = results_json['WeatherDataResults'][0]['WeatherSourceInformation'] meta['time_resolution'] = results_json['WeatherDataResults'][0]['WeatherDataPeriods']['TimeResolution_Minutes'] # noqa: E501 + meta['spatial_resolution'] = spatial_resolution # Rename and convert applicable metadata parameters to floats meta['latitude'] = float(meta.pop('Latitude')) meta['longitude'] = float(meta.pop('Longitude')) @@ -224,7 +229,7 @@ def read_solaranywhere(filename, map_variables=True, encoding='iso-8859-1'): Parameters ---------- - fbuf: str + filename: str Filename map_variables: bool, default: True When true, renames columns of the DataFrame to pvlib variable names @@ -236,7 +241,7 @@ def read_solaranywhere(filename, map_variables=True, encoding='iso-8859-1'): Returns ------- data: pandas.DataFrame - Timeseries data from SolarAnywhere. Index is localized to UTC. + Timeseries data from SolarAnywhere. metadata: dict Metadata available in the file. From 2ddd01f868bcf1e9a18d29f509e8789c7dcd3c16 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:23:31 +0100 Subject: [PATCH 26/31] Add additional solaranywhere tests --- pvlib/tests/iotools/test_solaranywhere.py | 92 ++++++++++++++++++----- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/pvlib/tests/iotools/test_solaranywhere.py b/pvlib/tests/iotools/test_solaranywhere.py index 04ea6b5286..b89b9eefd9 100644 --- a/pvlib/tests/iotools/test_solaranywhere.py +++ b/pvlib/tests/iotools/test_solaranywhere.py @@ -73,7 +73,7 @@ def test_read_solaranywhere_high_resolution(high_resolution_index): pd.testing.assert_index_equal(data.index, high_resolution_index) -def test_read_solaranywhere_map_variables(high_resolution_index): +def test_read_solaranywhere_map_variables(): # Check that variables are mapped by default to pvlib names data, meta = pvlib.iotools.read_solaranywhere(TESTFILE_HIGH_RESOLUTION) mapped_column_names = ['ghi', 'dni', 'dhi', 'temp_air', 'wind_speed', @@ -124,7 +124,7 @@ def test_get_solaranywhere_bad_probability_of_exceedance(): @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) def test_get_solaranywhere_missing_start_end(solaranywhere_api_key): # Test if ValueError is raised if start/end is missing for non-TMY request - with pytest.raises(ValueError, match="`end` is required."): + with pytest.raises(ValueError, match="simulation start and end time"): pvlib.iotools.get_solaranywhere( latitude=44, longitude=-73, api_key=solaranywhere_api_key, source='SolarAnywhereLatest') @@ -132,8 +132,8 @@ def test_get_solaranywhere_missing_start_end(solaranywhere_api_key): @pytest.fixture def time_series_index(): - index = pd.date_range(start='2020-01-01 00:02:30', periods=288, - freq='5min', tz='UTC') + index = pd.date_range(start='2019-12-31 19:02:30-05:00', periods=288, + freq='5min') index.name = 'ObservationTime' index.freq = None return index @@ -161,23 +161,23 @@ def timeseries_temp_air(time_series_index): @requires_solaranywhere_credentials @pytest.mark.remote_data @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_solaranywhere_no_timezone_information( - high_resolution_index, solaranywhere_api_key, time_series_index, - timeseries_temp_air): +def test_get_solaranywhere_no_timezone( + solaranywhere_api_key, time_series_index, timeseries_temp_air): # Test if data can be retrieved. This test only retrieves one day of data # to minimize the request time. data, meta = pvlib.iotools.get_solaranywhere( latitude=44.4675, longitude=-73.2075, api_key=solaranywhere_api_key, - # test specific version of SolarAnywhere - source='SolarAnywhere3_6', # specify start/end without timezone information start=pd.Timestamp(2020, 1, 1), end=pd.Timestamp(2020, 1, 2), + # test specific version of SolarAnywhere + source='SolarAnywhere3_6', spatial_resolution=0.005, time_resolution=5, true_dynamics=True) # Check metadata, including that true-dynamics is set assert meta['WeatherSiteName'] == 'SolarAnywhere3_6' assert meta['ApplyTrueDynamics'] is True assert meta['time_resolution'] == 5 + assert meta['spatial_resolution'] == 0.005 assert meta['latitude'] == 44.4675 assert meta['longitude'] == -73.2075 assert meta['altitude'] == 41.0 @@ -203,14 +203,74 @@ def test_get_solaranywhere_no_timezone_information( @requires_solaranywhere_credentials @pytest.mark.remote_data @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_solaranywhere_timeout(solaranywhere_api_key): +def test_get_solaranywhere_other_options( + solaranywhere_api_key, time_series_index, timeseries_temp_air): + # Test if data can be retrieved. This test only retrieves one day of data + # to minimize the request time. + data, meta = pvlib.iotools.get_solaranywhere( + latitude=44.4675, longitude=-73.2075, api_key=solaranywhere_api_key, + # specify start/end as str with timezone information + start='2020-01-01 00:00:00+0000', + end='2020-01-02 00:00:00+0000', + # test specific version of SolarAnywhere + source='SolarAnywhere3_7', + # test fewer variables + variables=[ + 'ObservationTime', + 'GlobalHorizontalIrradiance_WattsPerMeterSquared', + ], + map_variables=False) + + # Check metadata + assert meta['WeatherSiteName'] == 'SolarAnywhere3_7' + assert meta['ApplyTrueDynamics'] is False # default setting + assert meta['time_resolution'] == 60 # default resolution + assert meta['spatial_resolution'] == 0.01 # default resolution + assert meta['latitude'] == 44.4675 + assert meta['longitude'] == -73.2075 + assert meta['altitude'] == 41.0 + + # Check that variables have been mapped (default convention) + assert 'StartTime' not in data.columns + assert 'ObservationTime' in data.columns + assert 'EndTime' not in data.columns + # Check that ghi is not mapped + assert 'ghi' not in data.columns + assert 'GlobalHorizontalIrradiance_WattsPerMeterSquared' in data.columns + assert 'dni' not in data.columns + assert 'dhi' not in data.columns + assert 'temp_air' not in data.columns + assert 'wind_speed' not in data.columns + assert 'albedo' not in data.columns + assert 'DataVersion' not in data.columns + + +@requires_solaranywhere_credentials +@pytest.mark.remote_data +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) +def test_get_solaranywhere_probability_exceedance_error(solaranywhere_api_key): + # Test if ValueError is raised when passing start/end to typical year + with pytest.raises(ValueError, match="start and end time must be null"): + data, meta = pvlib.iotools.get_solaranywhere( + latitude=44.4675, longitude=-73.2075, + api_key=solaranywhere_api_key, + # Probabiliy of exceedance year should not have start/end specified + start=pd.Timestamp('2020-01-01 00:00:00+0000'), + end=pd.Timestamp('2020-01-05 12:00:00+0000'), + source='SolarAnywherePOELatest', + probability_of_exceedance=20) + + +@requires_solaranywhere_credentials +@pytest.mark.remote_data +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) +def test_get_solaranywhere_timeout_tgy(solaranywhere_api_key): # Test if the service times out when the timeout parameter is close to zero with pytest.raises(TimeoutError, match="Time exceeded"): pvlib.iotools.get_solaranywhere( latitude=44.4675, longitude=-73.2075, api_key=solaranywhere_api_key, - start=pd.Timestamp('2020-01-01 00:00:00+0000'), - end=pd.Timestamp('2020-01-05 12:00:00+0000'), + source='SolarAnywhereTGYLatest', timeout=0.00001) @@ -225,11 +285,3 @@ def test_get_solaranywhere_not_available(solaranywhere_api_key): api_key=solaranywhere_api_key, start=pd.Timestamp('2020-01-01 00:00:00+0000'), end=pd.Timestamp('2020-01-05 12:00:00+0000')) - - -# Second live test -# True dynamics is False -# Different variables - -# Mock tets: -# TGY From 0a7f9e2cadcecb981ea1b784f736373e795e401f Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 17:57:12 +0100 Subject: [PATCH 27/31] Update .github/workflows/flake8.yml Co-authored-by: Kevin Anderson --- .github/workflows/flake8.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml index 16873b4fb6..82b413236f 100644 --- a/.github/workflows/flake8.yml +++ b/.github/workflows/flake8.yml @@ -20,7 +20,7 @@ jobs: git remote add upstream https://github.com/pvlib/pvlib-python.git git fetch upstream $GITHUB_BASE_REF - name: Run Flake8 linter - run: git diff upstream/main -- "*.py" | flake8 + run: git diff upstream/$GITHUB_BASE_REF HEAD -- "*.py" | flake8 --exclude pvlib/version.py --ignore E201,E241,E226,W503,W504 --max-line-length 79 From 736b0cc389f84f21ccfb8f4f64c699a4a2d42223 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 22:57:12 +0100 Subject: [PATCH 28/31] Implement review changes from kandersolar --- pvlib/iotools/solaranywhere.py | 13 +++++++++---- pvlib/tests/iotools/test_solaranywhere.py | 20 ++++++++++---------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index cefd98db5a..a19bc972d1 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -67,7 +67,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, First timestamp of the requested period. If a timezone is not specified, UTC is assumed. Not applicable for TMY data. end: datetime like, optional - Last timtestamp of the requested period. If a timezone is not + Last timestamp of the requested period. If a timezone is not specified, UTC is assumed. Not applicable for TMY data. source: str, default: 'SolarAnywhereLatest' Data source. Options include: 'SolarAnywhereLatest' (historical data), @@ -279,10 +279,15 @@ def read_solaranywhere(filename, map_variables=True, encoding='iso-8859-1'): k, v = i.split(':') meta[k.strip()] = v.strip() - # Set index to UTC - data.index = pd.to_datetime(data['ObservationTime(GMT)'], - format='%m/%d/%Y %H:%M', utc=True) + meta['LatLon Resolution'] = float(meta['LatLon Resolution']) + # Set index + data.index = pd.to_datetime(data['ObservationTime(LST)'], + format='%m/%d/%Y %H:%M') + # Set timezone + data = data.tz_localize(int(meta['TZ'] * 3600)) + # Remove notion of LST in case the index is later converted to another tz + data.index.name = data.index.name.replace('(LST)', '') # Missing values can be represented as: blanks, 'NaN', or -999 data = data.replace(-999, np.nan) diff --git a/pvlib/tests/iotools/test_solaranywhere.py b/pvlib/tests/iotools/test_solaranywhere.py index b89b9eefd9..d78678ad27 100644 --- a/pvlib/tests/iotools/test_solaranywhere.py +++ b/pvlib/tests/iotools/test_solaranywhere.py @@ -22,17 +22,17 @@ def solaranywhere_api_key(): @pytest.fixture def high_resolution_index(): - index = pd.date_range(start='2021-01-01 05:05', end='2021-01-03 05:00', - freq='5min', tz='UTC') - index.name = 'ObservationTime(GMT)' + index = pd.date_range(start='2021-01-01 00:05-0500', + end='2021-01-03 00:00-0500', freq='5min') + index.name = 'ObservationTime' return index @pytest.fixture def tmy_index(): - index = pd.date_range(start='2000-01-01 06:00', periods=3*24, freq='1h', - tz='UTC') - index.name = 'ObservationTime(GMT)' + index = pd.date_range( + start='2000-01-01 01:00-0500', periods=3*24, freq='1h') + index.name = 'ObservationTime' index.freq = None return index @@ -58,7 +58,7 @@ def test_read_solaranywhere_high_resolution(high_resolution_index): assert meta['name'] == 'Burlington United States' assert meta['TZ'] == -5.0 assert meta['Data Version'] == '3.6' - assert meta['LatLon Resolution'] == '0.005' + assert meta['LatLon Resolution'] == 0.005 # Check that columns are parsed correctly assert 'Albedo' in data.columns assert 'Global Horizontal Irradiance (GHI) W/m2' in data.columns @@ -67,8 +67,8 @@ def test_read_solaranywhere_high_resolution(high_resolution_index): assert 'WindSpeedObservationType' in data.columns assert 'Particulate Matter 10 (µg/m3)' in data.columns # Check that data is parsed correctly - assert data.loc['2021-01-01 12:00:00+0000', 'Albedo'] == 0.6 - assert data.loc['2021-01-01 12:00:00+0000', 'WindSpeed (m/s)'] == 0 + assert data.loc['2021-01-01 07:00-0500', 'Albedo'] == 0.6 + assert data.loc['2021-01-01 07:00-0500', 'WindSpeed (m/s)'] == 0 # Assert that the index is parsed correctly pd.testing.assert_index_equal(data.index, high_resolution_index) @@ -101,7 +101,7 @@ def test_read_solaranywhere_tmy(tmy_index, tmy_ghi_series): assert meta['name'] == 'Burlington United States' assert meta['TZ'] == -5.0 assert meta['Data Version'] == '3.6' - assert meta['LatLon Resolution'] == '0.010' + assert meta['LatLon Resolution'] == 0.010 assert meta['Time Resolution'] == '60 minutes' # Assert that the index is parsed correctly pd.testing.assert_index_equal(data.index, tmy_index) From a009d058dbe0e280c2e822e62335e4441f5cb4b2 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Tue, 19 Dec 2023 23:23:32 +0100 Subject: [PATCH 29/31] Update test_solaranywhere.py --- pvlib/tests/iotools/test_solaranywhere.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/tests/iotools/test_solaranywhere.py b/pvlib/tests/iotools/test_solaranywhere.py index d78678ad27..dc38b42ab1 100644 --- a/pvlib/tests/iotools/test_solaranywhere.py +++ b/pvlib/tests/iotools/test_solaranywhere.py @@ -67,8 +67,8 @@ def test_read_solaranywhere_high_resolution(high_resolution_index): assert 'WindSpeedObservationType' in data.columns assert 'Particulate Matter 10 (µg/m3)' in data.columns # Check that data is parsed correctly - assert data.loc['2021-01-01 07:00-0500', 'Albedo'] == 0.6 - assert data.loc['2021-01-01 07:00-0500', 'WindSpeed (m/s)'] == 0 + assert data.loc['2021-01-01 07:00:00-05:00', 'Albedo'] == 0.6 + assert data.loc['2021-01-01 07:00:00-05:00', 'WindSpeed (m/s)'] == 0 # Assert that the index is parsed correctly pd.testing.assert_index_equal(data.index, high_resolution_index) From 62ba7d9c4486c1e12b80480f44490838e38f4119 Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:55:58 +0100 Subject: [PATCH 30/31] Apply suggestions from code review Co-authored-by: Cliff Hansen --- pvlib/iotools/solaranywhere.py | 12 ++++++------ pvlib/tests/iotools/test_solaranywhere.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index a19bc972d1..ea2de978e3 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -53,7 +53,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, """Retrieve historical irradiance time series data from SolarAnywhere. The SolarAnywhere API is described in [1]_ and [2]_. A detailed list of - available options for the input parameters can be found in [3]_. + API options can be found in [3]_. Parameters ---------- @@ -77,12 +77,12 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, 'SolarAnywhere3_2' (see [3]_ for a full list of options). time_resolution: {60, 30, 15, 5}, default: 60 Time resolution in minutes. For TMY data, time resolution has to be 60 - min. (hourly). + minutes (hourly). spatial_resolution: {0.1, 0.01, 0.005}, default: 0.01 Spatial resolution in degrees. true_dynamics: bool, default: False Whether to apply SolarAnywhere TrueDynamics statistical processing. - Only available for the 5-min time resolution. + Only available for the 5-minute time resolution. probability_of_exceedance: int, optional Probability of exceedance in the range of 1 to 99. Only relevant when requesting probability of exceedance (POE) time series. [%] @@ -96,7 +96,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, Base url of SolarAnywhere API. map_variables: bool, default: True When true, renames columns of the DataFrame to pvlib variable names - where applicable. See variable :const:`VARIABLE_MAP`. + where applicable. See :const:`VARIABLE_MAP`. timeout: float, default: 300 Time in seconds to wait for requested data to become available. @@ -225,7 +225,7 @@ def read_solaranywhere(filename, map_variables=True, encoding='iso-8859-1'): The SolarAnywhere file format and variables are described in [1]_. Note, the SolarAnywhere file format resembles the TMY3 file format but contains - additional variables and meatadata. + additional variables and metadata. Parameters ---------- @@ -233,7 +233,7 @@ def read_solaranywhere(filename, map_variables=True, encoding='iso-8859-1'): Filename map_variables: bool, default: True When true, renames columns of the DataFrame to pvlib variable names - where applicable. See variable :const:`VARIABLE_MAP`. + where applicable. See :const:`VARIABLE_MAP`. encoding : str, default : 'iso-8859-1' Encoding of the file. For SolarAnywhere TMY3 files the 'iso-8859-1' encoding is recommended due to the usage of special characters. diff --git a/pvlib/tests/iotools/test_solaranywhere.py b/pvlib/tests/iotools/test_solaranywhere.py index dc38b42ab1..018c583be8 100644 --- a/pvlib/tests/iotools/test_solaranywhere.py +++ b/pvlib/tests/iotools/test_solaranywhere.py @@ -15,7 +15,7 @@ @pytest.fixture(scope="module") def solaranywhere_api_key(): """Supplies the pvlib's SolarAnywhere API key for testing purposes. - Users can freely registre for an API key.""" + Users can freely register for an API key.""" solaranywhere_api_key = os.environ["SOLARANYWHERE_API_KEY"] return solaranywhere_api_key From 6db7fa5aa9f91ac8a21700e8a8134e524c91803a Mon Sep 17 00:00:00 2001 From: "Adam R. Jensen" <39184289+AdamRJensen@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:56:44 +0100 Subject: [PATCH 31/31] Switch to isinstance --- pvlib/iotools/solaranywhere.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/iotools/solaranywhere.py b/pvlib/iotools/solaranywhere.py index ea2de978e3..dfa7420ccc 100644 --- a/pvlib/iotools/solaranywhere.py +++ b/pvlib/iotools/solaranywhere.py @@ -158,7 +158,7 @@ def get_solaranywhere(latitude, longitude, api_key, start=None, end=None, payload['Options']['ApplyTrueDynamics'] = True if probability_of_exceedance is not None: - if type(probability_of_exceedance) != int: + if not isinstance(probability_of_exceedance, int): raise ValueError('`probability_of_exceedance` must be an integer') payload['Options']['ProbabilityOfExceedance'] = \ probability_of_exceedance