Repository: pvlib/pvlib-python
Pull request #2467 (2025-06-03 16:46:10)

Deprecate `parse_epw`
 - [x] Towards #2444
 - [x] I am familiar with the [contributing guidelines](https://pvlib-python.readthedocs.io/en/latest/contributing/index.html)
 - [x] Tests added
 - [x] Updates entries in [`docs/sphinx/source/reference`](https://github.com/pvlib/pvlib-python/blob/main/docs/sphinx/source/reference) for API changes.
 - [x] Adds description and name entries in the appropriate "what's new" file in [`docs/sphinx/source/whatsnew`](https://github.com/pvlib/pvlib-python/tree/main/docs/sphinx/source/whatsnew) for all changes. Includes link to the GitHub Issue with `` :issue:`num` `` or this Pull Request with `` :pull:`num` ``. Includes contributor name and/or GitHub username (link with `` :ghuser:`user` ``).
 - [x] New code is fully documented. Includes [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html) compliant docstrings, examples, and comments where necessary.
 - [x] Pull request is nearly complete and ready for detailed review.
 - [x] Maintainer: Appropriate GitHub Labels (including `remote-data`) and Milestone are assigned to the Pull Request and linked Issue.



Patch:
diff --git a/docs/examples/agrivoltaics/plot_agrivoltaics_ground_irradiance.py b/docs/examples/agrivoltaics/plot_agrivoltaics_ground_irradiance.py
index 94d585c..0943175 100644
--- a/docs/examples/agrivoltaics/plot_agrivoltaics_ground_irradiance.py
+++ b/docs/examples/agrivoltaics/plot_agrivoltaics_ground_irradiance.py
@@ -116,7 +116,7 @@ temp_cell = pvlib.temperature.faiman(
 )
 
 power_dc = pvlib.pvsystem.pvwatts_dc(
-    g_poa_effective=irradiance['poa_global'],
+    effective_irradiance=irradiance['poa_global'],
     temp_cell=temp_cell,
     pdc0=pdc0,
     gamma_pdc=gamma_pdc)
diff --git a/docs/sphinx/source/user_guide/modeling_topics/pvsystem.rst b/docs/sphinx/source/user_guide/modeling_topics/pvsystem.rst
index 0057c66..eabd649 100644
--- a/docs/sphinx/source/user_guide/modeling_topics/pvsystem.rst
+++ b/docs/sphinx/source/user_guide/modeling_topics/pvsystem.rst
@@ -75,7 +75,7 @@ data irradiance and temperature.
 
 .. ipython:: python
 
-    pdc = system.pvwatts_dc(g_poa_effective=1000, temp_cell=30)
+    pdc = system.pvwatts_dc(effective_irradiance=1000, temp_cell=30)
     print(pdc)
 
 Methods attached to a PVSystem object wrap the corresponding functions in
@@ -84,8 +84,8 @@ using data stored in the PVSystem attributes. Compare the
 :py:meth:`~pvlib.pvsystem.PVSystem.pvwatts_dc` method signature to the
 :py:func:`~pvlib.pvsystem.pvwatts_dc` function signature:
 
-    * :py:meth:`PVSystem.pvwatts_dc(g_poa_effective, temp_cell) <pvlib.pvsystem.PVSystem.pvwatts_dc>`
-    * :py:func:`pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.) <pvlib.pvsystem.pvwatts_dc>`
+    * :py:meth:`PVSystem.pvwatts_dc(effective_irradiance, temp_cell) <pvlib.pvsystem.PVSystem.pvwatts_dc>`
+    * :py:func:`pvwatts_dc(effective_irradiance, temp_cell, pdc0, gamma_pdc, temp_ref=25.) <pvlib.pvsystem.pvwatts_dc>`
 
 How does this work? The :py:meth:`~pvlib.pvsystem.PVSystem.pvwatts_dc`
 method looks in `PVSystem.module_parameters` for the `pdc0`, and
diff --git a/docs/sphinx/source/whatsnew/v0.12.1.rst b/docs/sphinx/source/whatsnew/v0.12.1.rst
index 883aa1b..ea69d0a 100644
--- a/docs/sphinx/source/whatsnew/v0.12.1.rst
+++ b/docs/sphinx/source/whatsnew/v0.12.1.rst
@@ -17,8 +17,10 @@ Breaking Changes
 Deprecations
 ~~~~~~~~~~~~
 * The following ``parse_`` functions in :py:mod:`pvlib.iotools` are deprecated,
-  with the corresponding ``read_`` functions taking their place: (:issue:`2444`, :pull:`2458`, :pull:`2466`)
+  with the corresponding ``read_`` functions taking their place: (:issue:`2444`, :pull:`2458`,
+  :pull:`2467`, :pull:`2466`)
 
+  - :py:func:`~pvlib.iotools.parse_epw`
   - :py:func:`~pvlib.iotools.parse_psm3`
   - :py:func:`~pvlib.iotools.parse_cams`
   - :py:func:`~pvlib.iotools.parse_bsrn`
@@ -26,7 +28,9 @@ Deprecations
 * The ``server`` parameter in :py:func:`~pvlib.iotools.get_cams` has been renamed
   to ``url`` to be consistent with the other iotools.
   :pull:`2463`
-
+* Rename parameter name ``g_poa_effective`` to ``effective_irradiance`` in
+  :py:func:`~pvlib.pvsystem.PVSystem.pvwatts_dc` and :py:func:`~pvlib.pvsystem.pvwatts_dc`.
+  (:issue:`1253`, :pull:`2235`)
 
 Bug fixes
 ~~~~~~~~~
diff --git a/pvlib/iotools/epw.py b/pvlib/iotools/epw.py
index a777b69..dd46c50 100644
--- a/pvlib/iotools/epw.py
+++ b/pvlib/iotools/epw.py
@@ -6,6 +6,9 @@ import io
 from urllib.request import urlopen, Request
 import pandas as pd
 
+from pvlib.tools import _file_context_manager
+from pvlib._deprecation import deprecated
+
 
 def read_epw(filename, coerce_year=None):
     r'''
@@ -23,7 +26,8 @@ def read_epw(filename, coerce_year=None):
     Parameters
     ----------
     filename : String
-        Can be a relative file path, absolute file path, or url.
+        Can be a relative file path, absolute file path, url, or in-memory
+        file buffer.
 
     coerce_year : int, optional
         If supplied, the year of the data will be set to this value. This can
@@ -43,10 +47,6 @@ def read_epw(filename, coerce_year=None):
     metadata : dict
         The site metadata available in the file.
 
-    See Also
-    --------
-    pvlib.iotools.parse_epw
-
     Notes
     -----
 
@@ -226,18 +226,17 @@ def read_epw(filename, coerce_year=None):
             'Safari/537.36')})
         response = urlopen(request)
         with io.StringIO(response.read().decode(errors='ignore')) as csvdata:
-            data, meta = parse_epw(csvdata, coerce_year)
+            data, meta = _parse_epw(csvdata, coerce_year)
 
     else:
-        # Assume it's accessible via the file system
-        with open(str(filename), 'r') as csvdata:
-            data, meta = parse_epw(csvdata, coerce_year)
-
+        # Assume it's a buffer or accessible via the file system
+        with _file_context_manager(filename, 'r') as csvdata:
+            data, meta = _parse_epw(csvdata, coerce_year)
 
     return data, meta
 
 
-def parse_epw(csvdata, coerce_year=None):
+def _parse_epw(csvdata, coerce_year=None):
     """
     Given a file-like buffer with data in Energy Plus Weather (EPW) format,
     parse the data into a dataframe.
@@ -310,3 +309,7 @@ def parse_epw(csvdata, coerce_year=None):
     data.index = idx
 
     return data, meta
+
+
+parse_epw = deprecated(since="0.13.0", name="parse_epw",
+                       alternative="read_epw")(read_epw)
diff --git a/pvlib/iotools/pvgis.py b/pvlib/iotools/pvgis.py
index c1588c1..9f257d5 100644
--- a/pvlib/iotools/pvgis.py
+++ b/pvlib/iotools/pvgis.py
@@ -21,7 +21,7 @@ import requests
 import numpy as np
 import pandas as pd
 import pytz
-from pvlib.iotools import read_epw, parse_epw
+from pvlib.iotools import read_epw
 
 URL = 'https://re.jrc.ec.europa.eu/api/'
 
@@ -536,7 +536,7 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
             data, months_selected, inputs, meta = _parse_pvgis_tmy_csv(src)
     elif outputformat == 'epw':
         with io.StringIO(res.content.decode('utf-8')) as src:
-            data, meta = parse_epw(src)
+            data, meta = read_epw(src)
             months_selected, inputs = None, None
     elif outputformat == 'basic':
         err_msg = ("outputformat='basic' is no longer supported by pvlib, "
@@ -661,10 +661,7 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
 
     # EPW: use the EPW parser from the pvlib.iotools epw.py module
     if outputformat == 'epw':
-        try:
-            data, meta = parse_epw(filename)
-        except AttributeError:  # str/path has no .read() attribute
-            data, meta = read_epw(filename)
+        data, meta = read_epw(filename)
         months_selected, inputs = None, None
 
     # NOTE: json and csv output formats have parsers defined as private
diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py
index a1d93bd..23ca1a9 100644
--- a/pvlib/pvsystem.py
+++ b/pvlib/pvsystem.py
@@ -16,7 +16,7 @@ import pandas as pd
 from dataclasses import dataclass
 from abc import ABC, abstractmethod
 from typing import Optional, Union
-
+from pvlib._deprecation import renamed_kwarg_warning
 import pvlib  # used to avoid albedo name collision in the Array class
 from pvlib import (atmosphere, iam, inverter, irradiance,
                    singlediode as _singlediode, spectrum, temperature)
@@ -843,8 +843,10 @@ class PVSystem:
             for array, data in zip(self.arrays, data)
         )
 
+    @renamed_kwarg_warning(
+        "0.13.0", "g_poa_effective", "effective_irradiance")
     @_unwrap_single_value
-    def pvwatts_dc(self, g_poa_effective, temp_cell):
+    def pvwatts_dc(self, effective_irradiance, temp_cell):
         """
         Calculates DC power according to the PVWatts model using
         :py:func:`pvlib.pvsystem.pvwatts_dc`, `self.module_parameters['pdc0']`,
@@ -852,15 +854,15 @@ class PVSystem:
 
         See :py:func:`pvlib.pvsystem.pvwatts_dc` for details.
         """
-        g_poa_effective = self._validate_per_array(g_poa_effective)
+        effective_irradiance = self._validate_per_array(effective_irradiance)
         temp_cell = self._validate_per_array(temp_cell)
         return tuple(
-            pvwatts_dc(g_poa_effective, temp_cell,
+            pvwatts_dc(effective_irradiance, temp_cell,
                        array.module_parameters['pdc0'],
                        array.module_parameters['gamma_pdc'],
                        **_build_kwargs(['temp_ref'], array.module_parameters))
-            for array, g_poa_effective, temp_cell
-            in zip(self.arrays, g_poa_effective, temp_cell)
+            for array, effective_irradiance, temp_cell
+            in zip(self.arrays, effective_irradiance, temp_cell)
         )
 
     def pvwatts_losses(self):
@@ -2856,7 +2858,9 @@ def scale_voltage_current_power(data, voltage=1, current=1):
     return df_sorted
 
 
-def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
+@renamed_kwarg_warning(
+    "0.13.0", "g_poa_effective", "effective_irradiance")
+def pvwatts_dc(effective_irradiance, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
     r"""
     Implements NREL's PVWatts DC power model. The PVWatts DC model [1]_ is:
 
@@ -2872,7 +2876,7 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
 
     Parameters
     ----------
-    g_poa_effective: numeric
+    effective_irradiance: numeric
         Irradiance transmitted to the PV cells. To be
         fully consistent with PVWatts, the user must have already
         applied angle of incidence losses, but not soiling, spectral,
@@ -2900,7 +2904,7 @@ def pvwatts_dc(g_poa_effective, temp_cell, pdc0, gamma_pdc, temp_ref=25.):
            (2014).
     """  # noqa: E501
 
-    pdc = (g_poa_effective * 0.001 * pdc0 *
+    pdc = (effective_irradiance * 0.001 * pdc0 *
            (1 + gamma_pdc * (temp_cell - temp_ref)))
 
     return pdc


Test patch:
diff --git a/tests/iotools/test_epw.py b/tests/iotools/test_epw.py
index 43e8e4c..a2c7883 100644
--- a/tests/iotools/test_epw.py
+++ b/tests/iotools/test_epw.py
@@ -3,11 +3,33 @@ import pytest
 from pvlib.iotools import epw
 from tests.conftest import TESTS_DATA_DIR, RERUNS, RERUNS_DELAY
 
+from pvlib._deprecation import pvlibDeprecationWarning
+
 epw_testfile = TESTS_DATA_DIR / 'NLD_Amsterdam062400_IWEC.epw'
 
 
 def test_read_epw():
-    epw.read_epw(epw_testfile)
+    df, meta = epw.read_epw(epw_testfile)
+    assert len(df) == 8760
+    assert 'ghi' in df.columns
+    assert meta['latitude'] == 52.3
+
+
+def test_read_epw_buffer():
+    with open(epw_testfile, 'r') as f:
+        df, meta = epw.read_epw(f)
+    assert len(df) == 8760
+    assert 'ghi' in df.columns
+    assert meta['latitude'] == 52.3
+
+
+def test_parse_epw_deprecated():
+    with pytest.warns(pvlibDeprecationWarning, match='Use read_epw instead'):
+        with open(epw_testfile, 'r') as f:
+            df, meta = epw.parse_epw(f)
+    assert len(df) == 8760
+    assert 'ghi' in df.columns
+    assert meta['latitude'] == 52.3
 
 
 @pytest.mark.remote_data