|
4 | 4 | https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-tmy-v4-0-0-download/ |
5 | 5 | https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-conus-v4-0-0-download/ |
6 | 6 | https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-full-disc-v4-0-0-download/ |
| 7 | +https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-polar-v4-0-0-download/ |
| 8 | +https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-polar-tmy-v4-0-0-download/ |
7 | 9 | """ |
8 | 10 |
|
9 | 11 | import csv |
|
19 | 21 | PSM4_TMY_ENDPOINT = "nsrdb-GOES-tmy-v4-0-0-download.csv" |
20 | 22 | PSM4_CON_ENDPOINT = "nsrdb-GOES-conus-v4-0-0-download.csv" |
21 | 23 | PSM4_FUL_ENDPOINT = "nsrdb-GOES-full-disc-v4-0-0-download.csv" |
| 24 | +PSM4_POLAR_ENDPOINT = "nsrdb-polar-v4-0-0-download.csv" |
| 25 | +PSM4_POLAR_TMY_ENDPOINT = "nsrdb-polar-tmy-v4-0-0-download.csv" |
22 | 26 | PSM4_AGG_URL = urljoin(NSRDB_API_BASE, PSM4_AGG_ENDPOINT) |
23 | 27 | PSM4_TMY_URL = urljoin(NSRDB_API_BASE, PSM4_TMY_ENDPOINT) |
24 | 28 | PSM4_CON_URL = urljoin(NSRDB_API_BASE, PSM4_CON_ENDPOINT) |
25 | 29 | PSM4_FUL_URL = urljoin(NSRDB_API_BASE, PSM4_FUL_ENDPOINT) |
| 30 | +PSM4_POLAR_URL = urljoin(NSRDB_API_BASE, PSM4_POLAR_ENDPOINT) |
| 31 | +PSM4_POLAR_TMY_URL = urljoin(NSRDB_API_BASE, PSM4_POLAR_TMY_ENDPOINT) |
26 | 32 |
|
27 | 33 | PARAMETERS = ( |
28 | 34 | 'air_temperature', 'dew_point', 'dhi', 'dni', 'ghi', 'surface_albedo', |
@@ -624,6 +630,151 @@ def get_nsrdb_psm4_full_disc(latitude, longitude, api_key, email, |
624 | 630 | return read_nsrdb_psm4(fbuf, map_variables) |
625 | 631 |
|
626 | 632 |
|
| 633 | +def get_nsrdb_psm4_polar(latitude, longitude, api_key, email, |
| 634 | + year, *, time_step=60, |
| 635 | + parameters=PARAMETERS, leap_day=True, |
| 636 | + full_name=PVLIB_PYTHON, |
| 637 | + affiliation=PVLIB_PYTHON, utc=False, |
| 638 | + map_variables=True, url=None, timeout=30): |
| 639 | + """ |
| 640 | + Retrieve timeseries weather data from the PSM4 NSRDB Polar API. |
| 641 | +
|
| 642 | + The NSRDB is described in [1]_ and the NSRDB PSM4 NSRDB Polar v4 dataset is |
| 643 | + described in [2]_, [3]_. The Polar dataset extends NSRDB coverage to high latitude |
| 644 | + regions, that are outside the field of view of the geostationary satellites. |
| 645 | +
|
| 646 | + The dataset provides data from 2013 onwards over the exclusive economic zone |
| 647 | + above 60 degrees north. The data have a spatial resolution of 4 km and a |
| 648 | + temporal resolution of 1 hour. |
| 649 | +
|
| 650 | + Parameters |
| 651 | + ---------- |
| 652 | + latitude : float or int |
| 653 | + in decimal degrees, between 60 and 90 degrees north |
| 654 | + longitude : float or int |
| 655 | + in decimal degrees, between -180 and 180, east is positive |
| 656 | + api_key : str |
| 657 | + NLR Developer Network API key |
| 658 | + email : str |
| 659 | + NLR API uses this to automatically communicate messages back |
| 660 | + to the user only if necessary |
| 661 | + year : int or str |
| 662 | + PSM4 API parameter specifing year (e.g. ``2023``) to download. The |
| 663 | + allowed values update periodically, so consult the NSRDB reference |
| 664 | + below for the current set of options. Called ``names`` in NSRDB API. |
| 665 | + time_step : int, default 60 |
| 666 | + time step in minutes. Called ``interval`` in NSRDB API. |
| 667 | + Only 60 is supported for the Polar dataset. |
| 668 | + parameters : list of str, optional |
| 669 | + meteorological fields to fetch. If not specified, defaults to |
| 670 | + ``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list of |
| 671 | + available fields. Alternatively, pvlib names may also be used (e.g. |
| 672 | + 'ghi' rather than 'GHI'); see :const:`REQUEST_VARIABLE_MAP`. To |
| 673 | + retrieve all available fields, set ``parameters=[]``. |
| 674 | + leap_day : bool, default : True |
| 675 | + include leap day in the results |
| 676 | + full_name : str, default 'pvlib python' |
| 677 | + optional |
| 678 | + affiliation : str, default 'pvlib python' |
| 679 | + optional |
| 680 | + utc: bool, default : False |
| 681 | + retrieve data with timestamps converted to UTC. False returns |
| 682 | + timestamps in local standard time of the selected location |
| 683 | + map_variables : bool, default True |
| 684 | + When true, renames columns of the Dataframe to pvlib variable names |
| 685 | + where applicable. See variable :const:`VARIABLE_MAP`. |
| 686 | + url : str, optional |
| 687 | + Full API endpoint URL. If not specified, the PSM4 Polar v4 URL is |
| 688 | + used. |
| 689 | + timeout : int, default 30 |
| 690 | + time in seconds to wait for server response before timeout |
| 691 | +
|
| 692 | + Returns |
| 693 | + ------- |
| 694 | + data : pandas.DataFrame |
| 695 | + timeseries data from NLR PSM4 |
| 696 | + metadata : dict |
| 697 | + metadata from NLR PSM4 about the record, see |
| 698 | + :func:`pvlib.iotools.read_nsrdb_psm4` for fields |
| 699 | +
|
| 700 | + Raises |
| 701 | + ------ |
| 702 | + requests.HTTPError |
| 703 | + if the request response status is not ok, then the ``'errors'`` field |
| 704 | + from the JSON response or any error message in the content will be |
| 705 | + raised as an exception, for example if the `api_key` was rejected or if |
| 706 | + the coordinates were not found in the NSRDB |
| 707 | +
|
| 708 | + Notes |
| 709 | + ----- |
| 710 | + The required NLR developer key, `api_key`, is available for free by |
| 711 | + registering at the `NLR Developer Network <https://developer.nlr.gov/>`_. |
| 712 | +
|
| 713 | + .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may |
| 714 | + result in rejected requests. |
| 715 | +
|
| 716 | + .. warning:: PSM4 is limited to data found in the NSRDB, please consult |
| 717 | + the references below for locations with available data. |
| 718 | +
|
| 719 | + See Also |
| 720 | + -------- |
| 721 | + pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy, |
| 722 | + pvlib.iotools.get_nsrdb_psm4_conus, pvlib.iotools.get_nsrdb_psm4_full_disc, |
| 723 | + pvlib.iotools.read_nsrdb_psm4 |
| 724 | +
|
| 725 | + References |
| 726 | + ---------- |
| 727 | + .. [1] `NLR National Solar Radiation Database (NSRDB) |
| 728 | + <https://developer.nlr.gov/docs/solar/nsrdb/>`_ |
| 729 | + .. [2] `NLR NSRDB Polar Data |
| 730 | + <https://nsrdb.nlr.gov/data-sets/polar-data/>`_ |
| 731 | + .. [3] `NSRDB Polar V4.0.0 |
| 732 | + <https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-polar-v4-0-0-download/>`_ |
| 733 | + """ |
| 734 | + # The well know text (WKT) representation of geometry notation is strict. |
| 735 | + # A POINT object is a string with longitude first, then the latitude, with |
| 736 | + # four decimals each, and exactly one space between them. |
| 737 | + longitude = ('%9.4f' % longitude).strip() |
| 738 | + latitude = ('%8.4f' % latitude).strip() |
| 739 | + # TODO: make format_WKT(object_type, *args) in tools.py |
| 740 | + |
| 741 | + # convert pvlib names in parameters to PSM4 convention |
| 742 | + parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters] |
| 743 | + |
| 744 | + # required query-string parameters for request to PSM4 API |
| 745 | + params = { |
| 746 | + 'api_key': api_key, |
| 747 | + 'full_name': full_name, |
| 748 | + 'email': email, |
| 749 | + 'affiliation': affiliation, |
| 750 | + 'reason': PVLIB_PYTHON, |
| 751 | + 'mailing_list': 'false', |
| 752 | + 'wkt': 'POINT(%s %s)' % (longitude, latitude), |
| 753 | + 'names': year, |
| 754 | + 'attributes': ','.join(parameters), |
| 755 | + 'leap_day': str(leap_day).lower(), |
| 756 | + 'utc': str(utc).lower(), |
| 757 | + 'interval': time_step |
| 758 | + } |
| 759 | + # request CSV download from NLR PSM4 |
| 760 | + if url is None: |
| 761 | + url = PSM4_POLAR_URL |
| 762 | + |
| 763 | + response = requests.get(url, params=params, timeout=timeout) |
| 764 | + if not response.ok: |
| 765 | + # if the API key is rejected, then the response status will be 403 |
| 766 | + # Forbidden, and then the error is in the content and there is no JSON |
| 767 | + try: |
| 768 | + errors = response.json()['errors'] |
| 769 | + except JSONDecodeError: |
| 770 | + errors = response.content.decode('utf-8') |
| 771 | + raise requests.HTTPError(errors, response=response) |
| 772 | + # the CSV is in the response content as a UTF-8 bytestring |
| 773 | + # to use pandas we need to create a file buffer from the response |
| 774 | + fbuf = io.StringIO(response.content.decode('utf-8')) |
| 775 | + return read_nsrdb_psm4(fbuf, map_variables) |
| 776 | + |
| 777 | + |
627 | 778 | def read_nsrdb_psm4(filename, map_variables=True): |
628 | 779 | """ |
629 | 780 | Read an NSRDB PSM4 weather file (formatted as SAM CSV). |
|
0 commit comments