diff --git a/docs/sphinx/source/reference/iotools.rst b/docs/sphinx/source/reference/iotools.rst index 514aeac2f5..14271cf3ee 100644 --- a/docs/sphinx/source/reference/iotools.rst +++ b/docs/sphinx/source/reference/iotools.rst @@ -31,6 +31,7 @@ of sources and file formats relevant to solar energy modeling. iotools.read_pvgis_tmy iotools.get_pvgis_hourly iotools.read_pvgis_hourly + iotools.get_pvgis_horizon iotools.get_bsrn iotools.read_bsrn iotools.parse_bsrn diff --git a/docs/sphinx/source/whatsnew/v0.9.6.rst b/docs/sphinx/source/whatsnew/v0.9.6.rst index a310dc2116..fe46e89075 100644 --- a/docs/sphinx/source/whatsnew/v0.9.6.rst +++ b/docs/sphinx/source/whatsnew/v0.9.6.rst @@ -7,9 +7,11 @@ v0.9.6 (Anticipated June 2023) Breaking Changes ~~~~~~~~~~~~~~~~ +* Modified the ``surface_azimuth`` parameter in :py:func:`pvlib.iotools.get_pvgis_hourly` to conform to the + pvlib azimuth convention (counterclockwise from north). Previously 0 degrees represented south. + (:issue:`1724`, :pull:`1739`) * For consistency with the rest of pvlib, the ``tilt`` parameter is renamed to ``surface_tilt`` in :py:func:`pvlib.soiling.hsu`. (:issue:`1717`, :pull:`1738`) - Deprecations ~~~~~~~~~~~~ @@ -21,6 +23,8 @@ Deprecations Enhancements ~~~~~~~~~~~~ +* Added function to retrieve horizon data from PVGIS + :py:func:`pvlib.iotools.get_pvgis_horizon`. (:issue:`1290`, :pull:`1395`) * Added ``map_variables`` argument to the :py:func:`pvlib.iotools.read_tmy3` in order to offer the option of mapping column names to standard pvlib names. (:issue:`1517`, :pull:`1623`) @@ -31,7 +35,6 @@ Enhancements * :py:func:`pvlib.iotools.get_psm3` now uses the new NSRDB 3.2.2 endpoint for hourly and half-hourly single-year datasets. (:issue:`1591`, :pull:`1736`) - Bug fixes ~~~~~~~~~ * `data` can no longer be left unspecified in @@ -59,9 +62,17 @@ Contributors ~~~~~~~~~~~~ * Lakshya Garg (:ghuser:`Lakshyadevelops`) * Adam R. Jensen (:ghuser:`adamrjensen`) +* Ben Pierce (:ghuser:`bgpierc`) +* Joseph Palakapilly (:ghuser:`JPalakapillyKWH`) +* Cliff Hansen (:ghuser:`cwhanse`) +* Anton Driesse (:ghuser:`adriesse`) +* Will Holmgren (:ghuser:`wholmgren`) +* Mark Mikofski (:ghuser:`mikofski`) +* Karel De Brabandere (:ghuser:`kdebrab`) +* Josh Stein (:ghuser:`jsstein`) +* Kevin Anderson (:ghuser:`kandersolar`) * Siddharth Kaul (:ghuser:`k10blogger`) * Kshitiz Gupta (:ghuser:`kshitiz305`) * Stefan de Lange (:ghuser:`langestefan`) * :ghuser:`ooprathamm` * Kevin Anderson (:ghuser:`kandersolar`) - diff --git a/pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND b/pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND new file mode 100644 index 0000000000..c13700ce76 --- /dev/null +++ b/pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND @@ -0,0 +1,146 @@ +PVObject_=pvGInverter + Comment=ChintPower CPS SCH275KTL-DO/US-800 Manufacturer 2020 + Version=6.81 + ParObj1=2020 + Flags=$00381562 + + PVObject_Commercial=pvCommercial + Comment=www.chintpower.com (China) + Flags=$0041 + Manufacturer=ChintPower + Model=CPS SCH275KTL-DO/US-800 + DataSource=Manufacturer 2020 + YearBeg=2020 + Width=0.680 + Height=0.337 + Depth=1.100 + Weight=95.000 + NPieces=0 + PriceDate=02/06/20 00:02 + Currency=EUR + Remarks, Count=2 + Str_1=Protection: -30 - +60, IP 66: outdoor installable + Str_2 + End of Remarks + End of PVObject pvCommercial + Transfo=Without + + Converter=TConverter + PNomConv=250.000 + PMaxOUT=250.000 + VOutConv=800.0 + VMppMin=500 + VMPPMax=1500 + VAbsMax=1500 + PSeuil=500.0 + EfficMax=99.01 + EfficEuro=98.49 + FResNorm=0.00 + ModeOper=MPPT + CompPMax=Lim + CompVMax=Lim + MonoTri=Tri + ModeAffEnum=Efficf_POut + UnitAffEnum=kW + PNomDC=253.000 + PMaxDC=375.000 + IDCMax=0.0 + IMaxDC=360.0 + INomAC=181.0 + IMaxAC=199.0 + TPNom=45.0 + TPMax=40.0 + TPLim1=50.0 + TPLimAbs=60.0 + PLim1=225.000 + PLimAbs=90.000 + PInEffMax =150000.000 + PThreshEff=3332.4 + HasdefaultPThresh=False + + ProfilPIO=TCubicProfile + NPtsMax=11 + NPtsEff=9 + LastCompile=$8085 + Mode=1 + Point_1=1250,0 + Point_2=7500,6923 + Point_3=12500,11875 + Point_4=25000,24250 + Point_5=50000,49100 + Point_6=75000,73875 + Point_7=150000,148515 + Point_8=250000,246500 + Point_9=275000,270325 + Point_10=0,0 + Point_11=0,0 + End of TCubicProfile + VNomEff=880.0,1174.0,1300.0, + EfficMaxV=98.260,99.040,98.860, + EfficEuroV=97.986,98.860,98.661, + + ProfilPIOV1=TCubicProfile + NPtsMax=11 + NPtsEff=9 + LastCompile=$8089 + Mode=1 + Point_1=300.0,0.0 + Point_2=13012.7,12500.0 + Point_3=25720.2,25000.0 + Point_4=51093.4,50000.0 + Point_5=76437.0,75000.0 + Point_6=127213.5,125000.0 + Point_7=190995.2,187500.0 + Point_8=255440.9,250000.0 + Point_9=281301.1,275000.0 + Point_10=0.0,0.0 + Point_11=0.0,0.0 + End of TCubicProfile + + ProfilPIOV2=TCubicProfile + NPtsMax=11 + NPtsEff=9 + LastCompile=$8089 + Mode=1 + Point_1=300.0,0.0 + Point_2=12850.8,12500.0 + Point_3=25401.3,25000.0 + Point_4=50581.7,50000.0 + Point_5=75795.9,75000.0 + Point_6=126211.6,125000.0 + Point_7=189623.8,187500.0 + Point_8=253138.9,250000.0 + Point_9=278763.3,275000.0 + Point_10=0.0,0.0 + Point_11=0.0,0.0 + End of TCubicProfile + + ProfilPIOV3=TCubicProfile + NPtsMax=11 + NPtsEff=9 + LastCompile=$8089 + Mode=1 + Point_1=300.0,0.0 + Point_2=12953.4,12500.0 + Point_3=25512.8,25000.0 + Point_4=50679.1,50000.0 + Point_5=75895.6,75000.0 + Point_6=126441.4,125000.0 + Point_7=189835.0,187500.0 + Point_8=253472.6,250000.0 + Point_9=279017.9,275000.0 + Point_10=0.0,0.0 + Point_11=0.0,0.0 + End of TCubicProfile + End of TConverter + NbInputs=36 + NbMPPT=12 + TanPhiMin=-0.750 + TanPhiMax=0.750 + NbMSInterne=2 + MasterSlave=No_M_S + IsolSurvey =Yes + DC_Switch=Yes + MS_Thresh=0.8 + Night_Loss=5.00 +End of PVObject pvGInverter diff --git a/pvlib/data/ET-M772BH550GL.PAN b/pvlib/data/ET-M772BH550GL.PAN new file mode 100644 index 0000000000..9b2a6a29af --- /dev/null +++ b/pvlib/data/ET-M772BH550GL.PAN @@ -0,0 +1,75 @@ +PVObject_=pvModule + Version=7.2 + Flags=$00900243 + + PVObject_Commercial=pvCommercial + Comment=ET SOLAR + Flags=$0041 + Manufacturer=ET SOLAR + Model=ET-M772BH550GL + DataSource=Manufacturer 2021 + YearBeg=2021 + Width=1.134 + Height=2.278 + Depth=0.035 + Weight=32.000 + NPieces=100 + PriceDate=06/04/22 12:39 + End of PVObject pvCommercial + + Technol=mtSiMono + NCelS=72 + NCelP=2 + NDiode=3 + SubModuleLayout=slTwinHalfCells + FrontSurface=fsARCoating + GRef=1000 + TRef=25.0 + PNom=550.0 + PNomTolUp=0.90 + BifacialityFactor=0.700 + Isc=14.000 + Voc=49.90 + Imp=13.110 + Vmp=41.96 + muISC=7.28 + muVocSpec=-128.0 + muPmpReq=-0.340 + RShunt=300 + Rp_0=2000 + Rp_Exp=5.50 + RSerie=0.203 + Gamma=0.980 + muGamma=-0.0001 + VMaxIEC=1500 + VMaxUL=1500 + Absorb=0.90 + ARev=3.200 + BRev=16.716 + RDiode=0.010 + VRevDiode=-0.70 + IMaxDiode=30.0 + AirMassRef=1.500 + CellArea=165.1 + SandiaAMCorr=50.000 + + PVObject_IAM=pvIAM + Flags=$00 + IAMMode=UserProfile + IAMProfile=TCubicProfile + NPtsMax=9 + NPtsEff=9 + LastCompile=$B18D + Mode=3 + Point_1=0.0,1.00000 + Point_2=20.0,1.00000 + Point_3=30.0,1.00000 + Point_4=40.0,0.99000 + Point_5=50.0,0.98000 + Point_6=60.0,0.96000 + Point_7=70.0,0.89000 + Point_8=80.0,0.66000 + Point_9=90.0,0.00000 + End of TCubicProfile + End of PVObject pvIAM +End of PVObject pvModule diff --git a/pvlib/data/test_read_pvgis_horizon.csv b/pvlib/data/test_read_pvgis_horizon.csv new file mode 100644 index 0000000000..0b3ed27bbc --- /dev/null +++ b/pvlib/data/test_read_pvgis_horizon.csv @@ -0,0 +1,49 @@ +horizon_azimuth,horizon_elevation +0,9.9 +7.5,13 +15,14.5 +22.5,15.7 +30,14.9 +37.5,15.3 +45,15.7 +52.5,15.7 +60,13 +67.5,11.5 +75,11.1 +82.5,11.5 +90,10.3 +97.5,11.5 +105,10.3 +112.5,9.5 +120,10.7 +127.5,11.8 +135,11.8 +142.5,8.8 +150,8.4 +157.5,7.3 +165,5.7 +172.5,5.7 +180,4.6 +187.5,3.4 +195,0.8 +202.5,0 +210,0 +217.5,0 +225,0 +232.5,0 +240,0 +247.5,0 +255,0 +262.5,0 +270,0 +277.5,0 +285,0 +292.5,0 +300,0 +307.5,0 +315,1.1 +322.5,1.9 +330,3.8 +337.5,5 +345,6.5 +352.5,9.2 diff --git a/pvlib/iotools/__init__.py b/pvlib/iotools/__init__.py index 1121e7d682..ecc70cab2b 100644 --- a/pvlib/iotools/__init__.py +++ b/pvlib/iotools/__init__.py @@ -15,10 +15,11 @@ from pvlib.iotools.pvgis import get_pvgis_tmy, read_pvgis_tmy # noqa: F401 from pvlib.iotools.pvgis import read_pvgis_hourly # noqa: F401 from pvlib.iotools.pvgis import get_pvgis_hourly # noqa: F401 +from pvlib.iotools.pvgis import get_pvgis_horizon # noqa: F401 from pvlib.iotools.bsrn import get_bsrn # noqa: F401 from pvlib.iotools.bsrn import read_bsrn # noqa: F401 from pvlib.iotools.bsrn import parse_bsrn # noqa: F401 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.panond import read_panond, parse_panond \ No newline at end of file +from pvlib.iotools.pvsyst import read_panond, parse_panond \ No newline at end of file diff --git a/pvlib/iotools/panond.py b/pvlib/iotools/panond.py deleted file mode 100644 index 3d424de5e6..0000000000 --- a/pvlib/iotools/panond.py +++ /dev/null @@ -1,135 +0,0 @@ -""" -Get .PAN or .OND file data into a nested dictionary. -""" - -import io - -def num_type(value): - # Determine if a value is float, int or leave as string - if '.' in value: - try: # Detect float - value_out = float(value) - return value_out - - except ValueError: # Otherwise leave as string - value_out = value - return value_out - - else: - - try: # Detect int - value_out = int(value) - return value_out - - except ValueError: # Otherwise leave as string - value_out = value - return value_out - - -def element_type(element): - # Determine if an element is a list then pass to num_type() - if ',' in element: # Detect a list - values = element.split(',') - element_out = [] - for val in values: # Determine datatype of each value - element_out.append(num_type(val)) - - return element_out - - else: - return num_type(element) - - -def parse_panond(fbuf): - """ - Parse a .pan or .ond text file into a nested dictionary. - - Parameters - ---------- - fbuf : File-like object - Buffer of a .pan or .ond file - - Returns - ------- - comp : Nested Dictionary - Contents of the .pan or .ond file following the indentation of the file. - The value of datatypes are assumed during reading. The value units are - the default used by PVsyst. - - Raises - ------ - - Notes - ----- - - See Also - -------- - - References - ---------- - """ - comp = {} # Component - dict_levels = [comp] - - fbuf.seek(0) - lines = fbuf.getvalue().splitlines() - for i in range(0, len(lines) - 1): # Reading blank lines. Stopping one short to avoid index error. Last line never contains important data. - if lines[i] == '': # Skipping blank lines - continue - - indent_lvl_1 = (len(lines[i]) - len(lines[i].lstrip(' '))) // 2 - indent_lvl_2 = (len(lines[i + 1]) - len(lines[i + 1].lstrip(' '))) // 2 - line_data = lines[i].split('=') - key = line_data[0].strip() - value = element_type(line_data[1].strip()) if len(line_data) > 1 else None - if indent_lvl_2 > indent_lvl_1: # add a level to the dict. The key here will be ignored. Not vital to file function. - current_level = dict_levels[indent_lvl_1] - new_level = {} - current_level[key] = new_level - dict_levels = dict_levels[: indent_lvl_1 + 1] + [new_level] - current_level = dict_levels[indent_lvl_1 + 1] - current_level[key] = value - - elif indent_lvl_2 <= indent_lvl_1: # add key/value to dict - current_level = dict_levels[indent_lvl_1] - current_level[key] = value - - return comp - - -def read_panond(file): - """ - Retrieve Module or Inverter data from a .pan or .ond text file, respectively. - - Parameters - ---------- - file : string or path object - Name or path of a .pan/.ond file - - Returns - ------- - content : Nested Dictionary - Contents of the .pan or .ond file following the indentation of the file. - The value of datatypes are assumed during reading. The value units are - the default used by PVsyst. - - Raises - ------ - - Notes - ----- - - See Also - -------- - - References - ---------- - """ - - with open(file, "r", encoding='utf-8-sig') as file: - f_content = file.read() - fbuf = io.StringIO(f_content) - - content = parse_panond(fbuf) - - return content diff --git a/pvlib/iotools/pvgis.py b/pvlib/iotools/pvgis.py index edfb28c124..3d44022b60 100644 --- a/pvlib/iotools/pvgis.py +++ b/pvlib/iotools/pvgis.py @@ -45,7 +45,7 @@ def get_pvgis_hourly(latitude, longitude, start=None, end=None, raddatabase=None, components=True, - surface_tilt=0, surface_azimuth=0, + surface_tilt=0, surface_azimuth=180, outputformat='json', usehorizon=True, userhorizon=None, pvcalculation=False, @@ -76,9 +76,15 @@ def get_pvgis_hourly(latitude, longitude, start=None, end=None, Otherwise only global irradiance is returned. surface_tilt: float, default: 0 Tilt angle from horizontal plane. Ignored for two-axis tracking. - surface_azimuth: float, default: 0 - Orientation (azimuth angle) of the (fixed) plane. 0=south, 90=west, - -90: east. Ignored for tracking systems. + surface_azimuth: float, default: 180 + Orientation (azimuth angle) of the (fixed) plane. Counter-clockwise + from north (north=0, south=180). This is offset 180 degrees from + the convention used by PVGIS. Ignored for tracking systems. + + .. versionchanged:: 0.10.0 + The `surface_azimuth` parameter now follows the pvlib convention, which + is counterclockwise from north. However, the convention used by the + PVGIS website and pvlib<=0.9.5 is offset by 180 degrees. usehorizon: bool, default: True Include effects of horizon userhorizon: list of float, default: None @@ -144,6 +150,13 @@ def get_pvgis_hourly(latitude, longitude, start=None, end=None, time stamp convention, e.g., SARAH and SARAH2 provide instantaneous values, whereas values from ERA5 are averages for the hour. + Warning + ------- + The azimuth orientation specified in the output metadata does not + correspond to the pvlib convention, but is offset 180 degrees. This is + despite the fact that the input parameter `surface_tilt` has to be + specified according to the pvlib convention. + Notes ----- data includes the following fields: @@ -191,7 +204,7 @@ def get_pvgis_hourly(latitude, longitude, start=None, end=None, """ # noqa: E501 # use requests to format the query string by passing params dictionary params = {'lat': latitude, 'lon': longitude, 'outputformat': outputformat, - 'angle': surface_tilt, 'aspect': surface_azimuth, + 'angle': surface_tilt, 'aspect': surface_azimuth-180, 'pvcalculation': int(pvcalculation), 'pvtechchoice': pvtechchoice, 'mountingplace': mountingplace, 'trackingtype': trackingtype, 'components': int(components), @@ -315,6 +328,11 @@ def read_pvgis_hourly(filename, pvgis_format=None, map_variables=True): metadata : dict metadata + Warning + ------- + The azimuth orientation specified in the output metadata does not + correspond to the pvlib convention, but is offset 180 degrees. + Raises ------ ValueError @@ -665,3 +683,57 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=None): data = data.rename(columns=VARIABLE_MAP) return data, months_selected, inputs, meta + + +def get_pvgis_horizon(latitude, longitude, url=URL, **kwargs): + """Get horizon data from PVGIS. + + Parameters + ---------- + latitude : float + Latitude in degrees north + longitude : float + Longitude in degrees east + url: str, default: :const:`pvlib.iotools.pvgis.URL` + Base URL for PVGIS + kwargs: + Passed to requests.get + + Returns + ------- + data : pd.Series + Pandas Series of the retrived horizon elevation angles. Index is the + corresponding horizon azimuth angles. + metadata : dict + Metadata returned by PVGIS. + + Notes + ----- + The horizon azimuths are specified clockwise from north, e.g., south=180. + This is the standard pvlib convention, although the PVGIS website specifies + south=0. + + References + ---------- + .. [1] `PVGIS horizon profile tool + `_ + """ + params = {'lat': latitude, 'lon': longitude, 'outputformat': 'json'} + res = requests.get(url + 'printhorizon', params=params, **kwargs) + if not res.ok: + try: + err_msg = res.json() + except Exception: + res.raise_for_status() + else: + raise requests.HTTPError(err_msg['message']) + json_output = res.json() + metadata = json_output['meta'] + data = pd.DataFrame(json_output['outputs']['horizon_profile']) + data.columns = ['horizon_azimuth', 'horizon_elevation'] + # Convert azimuth to pvlib convention (north=0, south=180) + data['horizon_azimuth'] += 180 + data.set_index('horizon_azimuth', inplace=True) + data = data['horizon_elevation'] # convert to pd.Series + data = data[data.index < 360] # remove duplicate north point (0 and 360) + return data, metadata diff --git a/pvlib/iotools/pvsyst.py b/pvlib/iotools/pvsyst.py new file mode 100644 index 0000000000..bcb1e95b06 --- /dev/null +++ b/pvlib/iotools/pvsyst.py @@ -0,0 +1,137 @@ +""" +Get .PAN or .OND file data into a nested dictionary. +""" + +import io + +def num_type(value): + # Determine if a value is float, int or leave as string + + if '.' in value: + try: # Detect float + value_out = float(value) + return value_out + + except ValueError: # Otherwise leave as string + value_out = value + return value_out + + else: + + try: # Detect int + value_out = int(value) + return value_out + + except ValueError: # Otherwise leave as string + value_out = value + return value_out + + +def element_type(element): + # Determine if an element is a list then pass to num_type() + + if ',' in element: # Detect a list + values = element.split(',') + element_out = [] + for val in values: # Determine datatype of each value + element_out.append(num_type(val)) + + return element_out + + else: + return num_type(element) + + +def parse_panond(fbuf): + """ + Parse a .pan or .ond text file into a nested dictionary. + + Parameters + ---------- + fbuf : File-like object + Buffer of a .pan or .ond file + + Returns + ------- + comp : Nested Dictionary + Contents of the .pan or .ond file following the indentation of the file. + The value of datatypes are assumed during reading. The value units are + the default used by PVsyst. + + Raises + ------ + + Notes + ----- + + See Also + -------- + + References + ---------- + """ + comp = {} # Component + dict_levels = [comp] + + fbuf.seek(0) + lines = fbuf.getvalue().splitlines() + for i in range(0, len(lines) - 1): # Reading blank lines. Stopping one short to avoid index error. Last line never contains important data. + if lines[i] == '': # Skipping blank lines + continue + + indent_lvl_1 = (len(lines[i]) - len(lines[i].lstrip(' '))) // 2 + indent_lvl_2 = (len(lines[i + 1]) - len(lines[i + 1].lstrip(' '))) // 2 + line_data = lines[i].split('=') + key = line_data[0].strip() + value = element_type(line_data[1].strip()) if len(line_data) > 1 else None + if indent_lvl_2 > indent_lvl_1: # add a level to the dict. The key here will be ignored. Not vital to file function. + current_level = dict_levels[indent_lvl_1] + new_level = {} + current_level[key] = new_level + dict_levels = dict_levels[: indent_lvl_1 + 1] + [new_level] + current_level = dict_levels[indent_lvl_1 + 1] + current_level[key] = value + + elif indent_lvl_2 <= indent_lvl_1: # add key/value to dict + current_level = dict_levels[indent_lvl_1] + current_level[key] = value + + return comp + + +def read_panond(file): + """ + Retrieve Module or Inverter data from a .pan or .ond text file, respectively. + + Parameters + ---------- + file : string or path object + Name or path of a .pan/.ond file + + Returns + ------- + content : Nested Dictionary + Contents of the .pan or .ond file following the indentation of the file. + The value of datatypes are assumed during reading. The value units are + the default used by PVsyst. + + Raises + ------ + + Notes + ----- + + See Also + -------- + + References + ---------- + """ + + with open(file, "r", encoding='utf-8-sig') as file: + f_content = file.read() + fbuf = io.StringIO(f_content) + + content = parse_panond(fbuf) + + return content diff --git a/pvlib/tests/iotools/test_pvgis.py b/pvlib/tests/iotools/test_pvgis.py index 579c26914c..41281208eb 100644 --- a/pvlib/tests/iotools/test_pvgis.py +++ b/pvlib/tests/iotools/test_pvgis.py @@ -9,8 +9,9 @@ import requests from pvlib.iotools import get_pvgis_tmy, read_pvgis_tmy from pvlib.iotools import get_pvgis_hourly, read_pvgis_hourly +from pvlib.iotools import get_pvgis_horizon from ..conftest import (DATA_DIR, RERUNS, RERUNS_DELAY, assert_frame_equal, - fail_on_pvlib_version) + fail_on_pvlib_version, assert_series_equal) from pvlib._deprecation import pvlibDeprecationWarning @@ -206,14 +207,14 @@ def test_read_pvgis_hourly_bad_extension(): args_radiation_csv = { - 'surface_tilt': 30, 'surface_azimuth': 0, 'outputformat': 'csv', + 'surface_tilt': 30, 'surface_azimuth': 180, 'outputformat': 'csv', 'usehorizon': False, 'userhorizon': None, 'raddatabase': 'PVGIS-SARAH', 'start': 2016, 'end': 2016, 'pvcalculation': False, 'components': True} url_hourly_radiation_csv = 'https://re.jrc.ec.europa.eu/api/seriescalc?lat=45&lon=8&outputformat=csv&angle=30&aspect=0&usehorizon=0&pvtechchoice=crystSi&mountingplace=free&trackingtype=0&components=1&raddatabase=PVGIS-SARAH&startyear=2016&endyear=2016' # noqa: E501 args_pv_json = { - 'surface_tilt': 30, 'surface_azimuth': 0, 'outputformat': 'json', + 'surface_tilt': 30, 'surface_azimuth': 180, 'outputformat': 'json', 'usehorizon': True, 'userhorizon': None, 'raddatabase': 'PVGIS-SARAH2', 'start': pd.Timestamp(2013, 1, 1), 'end': pd.Timestamp(2014, 5, 1), 'pvcalculation': True, 'peakpower': 10, 'pvtechchoice': 'CIS', 'loss': 5, @@ -509,6 +510,23 @@ def test_get_pvgis_map_variables(pvgis_tmy_mapped_columns): assert all([c in pvgis_tmy_mapped_columns for c in actual.columns]) +@pytest.mark.remote_data +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) +def test_read_pvgis_horizon(): + pvgis_data, _ = get_pvgis_horizon(35.171051, -106.465158) + horizon_data = pd.read_csv(DATA_DIR / 'test_read_pvgis_horizon.csv', + index_col=0) + horizon_data = horizon_data['horizon_elevation'] + assert_series_equal(pvgis_data, horizon_data) + + +@pytest.mark.remote_data +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) +def test_read_pvgis_horizon_invalid_coords(): + with pytest.raises(requests.HTTPError, match='lat: Incorrect value'): + _, _ = get_pvgis_horizon(100, 50) # unfeasible latitude + + def test_read_pvgis_tmy_map_variables(pvgis_tmy_mapped_columns): fn = DATA_DIR / 'tmy_45.000_8.000_2005_2016.json' actual, _, _, _ = read_pvgis_tmy(fn, map_variables=True) diff --git a/pvlib/tests/iotools/test_panond.py b/pvlib/tests/iotools/test_pvsyst.py similarity index 96% rename from pvlib/tests/iotools/test_panond.py rename to pvlib/tests/iotools/test_pvsyst.py index c4c0e503ee..626d23e506 100644 --- a/pvlib/tests/iotools/test_panond.py +++ b/pvlib/tests/iotools/test_pvsyst.py @@ -2,11 +2,12 @@ test iotools for panond """ -from ...iotools import read_panond, parse_panond +from pvlib.iotools import read_panond, parse_panond +from pvlib.tests.conftest import DATA_DIR import io # Not sure if I am creating these test scenarios correctly -fn_file = 'C:/Users/Contractor1/Downloads/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND' +fn_file = DATA_DIR / 'CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND' ond_file = read_panond(fn_file) fn_str = """PVObject_=pvGInverter @@ -159,7 +160,7 @@ f_obj = io.StringIO(fn_str) ond_str = parse_panond(f_obj) -fn_file = 'C:/Users/Contractor1/Downloads/ET-M772BH550GL.PAN' +fn_file = DATA_DIR / 'ET-M772BH550GL.PAN' mod_file = read_panond(fn_file) fn_str = """PVObject_=pvModule @@ -240,5 +241,3 @@ """ f_obj = io.StringIO(fn_str) mod_str = parse_panond(f_obj) - -stop = 1 \ No newline at end of file