Skip to content

Commit beba7ba

Browse files
prototype (#1)
* first iteration
1 parent cb4c135 commit beba7ba

File tree

4 files changed

+463
-0
lines changed

4 files changed

+463
-0
lines changed

docs/sphinx/source/reference/iotools.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ of sources and file formats relevant to solar energy modeling.
4343
iotools.get_acis_station_data
4444
iotools.get_acis_available_stations
4545
iotools.read_panond
46+
iotools.get_solcast_tmy
47+
iotools.get_solcast_historic
48+
iotools.get_solcast_forecast
49+
iotools.get_solcast_live
4650

4751

4852
A :py:class:`~pvlib.location.Location` object may be created from metadata

pvlib/iotools/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@
2727
from pvlib.iotools.acis import get_acis_mpe # noqa: F401
2828
from pvlib.iotools.acis import get_acis_station_data # noqa: F401
2929
from pvlib.iotools.acis import get_acis_available_stations # noqa: F401
30+
from pvlib.iotools.solcast import get_solcast_forecast # noqa: F401
31+
from pvlib.iotools.solcast import get_solcast_live # noqa: F401
32+
from pvlib.iotools.solcast import get_solcast_historic # noqa: F401
33+
from pvlib.iotools.solcast import get_solcast_tmy # noqa: F401

pvlib/iotools/solcast.py

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
""" Functions to access data from the Solcast API.
2+
"""
3+
4+
import requests
5+
import pandas as pd
6+
from dataclasses import dataclass
7+
8+
9+
BASE_URL = "https://api.solcast.com.au/data"
10+
11+
@dataclass
12+
class ParameterMap:
13+
solcast_name: str
14+
pvlib_name: str
15+
conversion: callable=lambda x: x
16+
17+
# define the conventions between Solcast and PVLib nomenclature and units
18+
VARIABLE_MAP = [
19+
ParameterMap("air_temp", "temp_air"), # air_temp -> temp_air (deg C)
20+
ParameterMap("surface_pressure", "pressure", lambda x: x*100), # surface_pressure (hPa) -> pressure (Pa)
21+
ParameterMap("dewpoint_temp", "temp_dew"), # dewpoint_temp -> temp_dew (deg C)
22+
ParameterMap("gti", "poa_global"), # gti (W/m^2) -> poa_global (W/m^2)
23+
ParameterMap("wind_speed_10m", "wind_speed"), # wind_speed_10m (m/s) -> wind_speed (m/s)
24+
ParameterMap("wind_direction_10m", "wind_direction"), # wind_direction_10m (deg) -> wind_direction (deg) (Convention?)
25+
ParameterMap(
26+
"azimuth", "solar_azimuth", lambda x: abs(x) if x <= 0 else 360 - x
27+
), # azimuth -> solar_azimuth (degrees) (different convention)
28+
ParameterMap("precipitable_water", "precipitable_water", lambda x: x*10), # precipitable_water (kg/m2) -> precipitable_water (cm)
29+
ParameterMap("zenith", "solar_zenith") # zenith -> solar_zenith
30+
]
31+
32+
33+
def get_solcast_tmy(
34+
latitude, longitude, api_key, map_variables=True, **kwargs
35+
):
36+
"""Get the irradiance and weather for a Typical Meteorological Year (TMY) at a requested location.
37+
38+
Derived from satellite (clouds and irradiance over non-polar continental areas) and
39+
numerical weather models (other data). The TMY is calculated with data from 2007 to 2023.
40+
41+
Parameters
42+
----------
43+
latitude : float
44+
in decimal degrees, between -90 and 90, north is positive
45+
longitude : float
46+
in decimal degrees, between -180 and 180, east is positive
47+
api_key : str
48+
To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register.
49+
map_variables: bool, default: True
50+
When true, renames columns of the DataFrame to pvlib variable names
51+
where applicable. See variable :const:`VARIABLE_MAP`.
52+
kwargs:
53+
Optional parameters passed to the API. See https://docs.solcast.com.au/ for full list of parameters.
54+
55+
Returns
56+
-------
57+
df : pandas.DataFrame
58+
containing the values for the parameters requested
59+
60+
Examples
61+
--------
62+
get_solcast_tmy(
63+
latitude=-33.856784,
64+
longitude=151.215297,
65+
api_key="your-key"
66+
)
67+
68+
you can pass any of the parameters listed in the API docs, like time_zone:
69+
70+
get_solcast_tmy(
71+
latitude=-33.856784,
72+
longitude=151.215297,
73+
time_zone=10,
74+
api_key="your-key"
75+
)
76+
77+
"""
78+
79+
params = dict(
80+
latitude=latitude,
81+
longitude=longitude,
82+
format="json",
83+
**kwargs
84+
)
85+
86+
return _get_solcast(
87+
endpoint="tmy/radiation_and_weather",
88+
params=params,
89+
api_key=api_key,
90+
map_variables=map_variables
91+
)
92+
93+
94+
def get_solcast_historic(
95+
latitude,
96+
longitude,
97+
start,
98+
api_key,
99+
end=None,
100+
duration=None,
101+
map_variables=True,
102+
**kwargs
103+
):
104+
"""Get historical irradiance and weather estimated actuals
105+
106+
for up to 31 days of data at a time for a requested location,
107+
derived from satellite (clouds and irradiance
108+
over non-polar continental areas) and numerical weather models (other data).
109+
Data is available from 2007-01-01T00:00Z up to real time estimated actuals.
110+
111+
Parameters
112+
----------
113+
latitude : float
114+
in decimal degrees, between -90 and 90, north is positive
115+
longitude : float
116+
in decimal degrees, between -180 and 180, east is positive
117+
start : datetime-like
118+
First day of the requested period
119+
end : optional, datetime-like
120+
Last day of the requested period
121+
duration : optional, default is None
122+
Must include one of end_date and duration. ISO_8601 compliant duration for the historic data.
123+
Must be within 31 days of the start_date.
124+
map_variables: bool, default: True
125+
When true, renames columns of the DataFrame to pvlib variable names
126+
where applicable. See variable :const:`VARIABLE_MAP`.
127+
api_key : str
128+
To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register.
129+
kwargs:
130+
Optional parameters passed to the GET request
131+
132+
See https://docs.solcast.com.au/ for full list of parameters.
133+
134+
Returns
135+
-------
136+
df : pandas.DataFrame
137+
containing the values for the parameters requested
138+
139+
Examples
140+
--------
141+
get_solcast_historic(
142+
latitude=-33.856784,
143+
longitude=151.215297,
144+
start='2007-01-01T00:00Z',
145+
duration='P1D',
146+
api_key="your-key"
147+
)
148+
149+
you can pass any of the parameters listed in the API docs, for example using the end parameter instead
150+
151+
get_solcast_historic(
152+
latitude=-33.856784,
153+
longitude=151.215297,
154+
start='2007-01-01T00:00Z',
155+
end='2007-01-02T00:00Z',
156+
api_key="your-key"
157+
)
158+
"""
159+
160+
params = dict(
161+
latitude=latitude,
162+
longitude=longitude,
163+
start=start,
164+
end=end,
165+
duration=duration,
166+
api_key=api_key,
167+
format="json",
168+
**kwargs
169+
)
170+
171+
return _get_solcast(
172+
endpoint="historic/radiation_and_weather",
173+
params=params,
174+
api_key=api_key,
175+
map_variables=map_variables
176+
)
177+
178+
def get_solcast_forecast(
179+
latitude, longitude, api_key, map_variables=True, **kwargs
180+
):
181+
"""Get irradiance and weather forecasts from the present time up to 14 days ahead
182+
183+
Parameters
184+
----------
185+
latitude : float
186+
in decimal degrees, between -90 and 90, north is positive
187+
longitude : float
188+
in decimal degrees, between -180 and 180, east is positive
189+
api_key : str
190+
To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register.
191+
map_variables: bool, default: True
192+
When true, renames columns of the DataFrame to pvlib variable names
193+
where applicable. See variable :const:`VARIABLE_MAP`.
194+
kwargs:
195+
Optional parameters passed to the GET request
196+
197+
See https://docs.solcast.com.au/ for full list of parameters.
198+
199+
Returns
200+
-------
201+
df : pandas.DataFrame
202+
containing the values for the parameters requested
203+
204+
Examples
205+
--------
206+
get_solcast_forecast(
207+
latitude=-33.856784,
208+
longitude=151.215297,
209+
api_key="your-key"
210+
)
211+
212+
you can pass any of the parameters listed in the API docs, like asking for specific variables
213+
get_solcast_forecast(
214+
latitude=-33.856784,
215+
longitude=151.215297,
216+
output_parameters='dni,clearsky_dni',
217+
api_key="your-key"
218+
)
219+
"""
220+
221+
params = dict(
222+
latitude=latitude,
223+
longitude=longitude,
224+
format="json",
225+
**kwargs
226+
)
227+
228+
return _get_solcast(
229+
endpoint="forecast/radiation_and_weather",
230+
params=params,
231+
api_key=api_key,
232+
map_variables=map_variables
233+
)
234+
235+
def get_solcast_live(
236+
latitude, longitude, api_key, map_variables=True, **kwargs
237+
):
238+
"""Get irradiance and weather estimated actuals for near real-time and past 7 days
239+
240+
Parameters
241+
----------
242+
latitude : float
243+
in decimal degrees, between -90 and 90, north is positive
244+
longitude : float
245+
in decimal degrees, between -180 and 180, east is positive
246+
api_key : str
247+
To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register.
248+
map_variables: bool, default: True
249+
When true, renames columns of the DataFrame to pvlib variable names
250+
where applicable. See variable :const:`VARIABLE_MAP`.
251+
kwargs:
252+
Optional parameters passed to the GET request
253+
254+
See https://docs.solcast.com.au/ for full list of parameters.
255+
256+
Returns
257+
-------
258+
df : pandas.DataFrame
259+
containing the values for the parameters requested
260+
261+
Examples
262+
--------
263+
get_solcast_live(
264+
latitude=-33.856784,
265+
longitude=151.215297,
266+
api_key="your-key"
267+
)
268+
269+
you can pass any of the parameters listed in the API docs, like
270+
271+
get_solcast_live(
272+
latitude=-33.856784,
273+
longitude=151.215297,
274+
terrain_shading=True,
275+
api_key="your-key"
276+
)
277+
278+
use map_variables=False to avoid converting the data to PVLib's conventions
279+
280+
get_solcast_live(
281+
latitude=-33.856784,
282+
longitude=151.215297,
283+
map_variables=False,
284+
api_key="your-key"
285+
)
286+
"""
287+
288+
params = dict(
289+
latitude=latitude,
290+
longitude=longitude,
291+
format="json",
292+
**kwargs
293+
)
294+
295+
return _get_solcast(
296+
endpoint="live/radiation_and_weather",
297+
params=params,
298+
api_key=api_key,
299+
map_variables=map_variables
300+
)
301+
302+
def solcast2pvlib(df):
303+
"""Formats the data from Solcast to PVLib's conventions.
304+
"""
305+
# move from period_end to period_middle as per pvlib convention
306+
df["period_mid"] = pd.to_datetime(df.period_end) - pd.Timedelta(df.period.values[0]) / 2
307+
df = df.set_index("period_mid").drop(columns=["period_end", "period"])
308+
309+
# rename and convert variables
310+
for variable in VARIABLE_MAP:
311+
if variable.solcast_name in df.columns:
312+
df.rename(columns={variable.solcast_name: variable.pvlib_name}, inplace=True)
313+
df[variable.pvlib_name] = df[variable.pvlib_name].apply(variable.conversion)
314+
return df
315+
316+
def _get_solcast(
317+
endpoint,
318+
params,
319+
api_key,
320+
map_variables
321+
):
322+
"""retrieves weather, irradiance and power data from the Solcast API
323+
324+
Parameters
325+
----------
326+
endpoint : str
327+
one of Solcast API endpoint:
328+
- live/radiation_and_weather
329+
- forecast/radiation_and_weather
330+
- historic/radiation_and_weather
331+
- tmy/radiation_and_weather
332+
params : dict
333+
parameters to be passed to the API
334+
api_key : str
335+
To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register.
336+
map_variables: bool, default: True
337+
When true, renames columns of the DataFrame to pvlib variable names
338+
where applicable. See variable :const:`VARIABLE_MAP`.
339+
340+
Returns
341+
-------
342+
A pandas.DataFrame with the data if the request is successful, an error message otherwise
343+
"""
344+
345+
response = requests.get(
346+
url= '/'.join([BASE_URL, endpoint]),
347+
params=params,
348+
headers={"Authorization": f"Bearer {api_key}"}
349+
)
350+
351+
if response.status_code == 200:
352+
j = response.json()
353+
df = pd.DataFrame.from_dict(j[list(j.keys())[0]])
354+
if map_variables:
355+
return solcast2pvlib(df)
356+
else:
357+
return df
358+
else:
359+
raise Exception(response.json())

0 commit comments

Comments
 (0)