From 8312435302cbcb5d051ba35c3ead4ee26b1a654a Mon Sep 17 00:00:00 2001 From: Kevin Anderson Date: Fri, 11 Sep 2026 14:13:33 +0200 Subject: [PATCH 1/2] replace OrderedDict with dict everywhere --- .../source/contributing/style_guide.rst | 2 +- .../user_guide/modeling_topics/clearsky.rst | 6 ++-- pvlib/clearsky.py | 23 +++++++------ pvlib/irradiance.py | 33 +++++++++---------- pvlib/pvsystem.py | 9 +++-- tests/test_clearsky.py | 22 ++++++------- tests/test_irradiance.py | 3 +- tests/test_pvsystem.py | 17 +++++----- 8 files changed, 54 insertions(+), 61 deletions(-) diff --git a/docs/sphinx/source/contributing/style_guide.rst b/docs/sphinx/source/contributing/style_guide.rst index 4eea571eda..be329e64e7 100644 --- a/docs/sphinx/source/contributing/style_guide.rst +++ b/docs/sphinx/source/contributing/style_guide.rst @@ -58,7 +58,7 @@ allows for multiple input types to work for many parameters. pvlib uses the following generic descriptors as short-hand to indicate which specific types may be used: -* dict-like : dict, OrderedDict, pd.Series +* dict-like : dict, pd.Series * numeric : scalar, np.array, pd.Series. Typically int or float dtype. * array-like : np.array, pd.Series. Typically int or float dtype. diff --git a/docs/sphinx/source/user_guide/modeling_topics/clearsky.rst b/docs/sphinx/source/user_guide/modeling_topics/clearsky.rst index cde1c3c58c..7334fdb5a8 100644 --- a/docs/sphinx/source/user_guide/modeling_topics/clearsky.rst +++ b/docs/sphinx/source/user_guide/modeling_topics/clearsky.rst @@ -297,7 +297,7 @@ A clear sky time series using only basic pvlib functions. The input data types determine the returned output type. Array input -results in an OrderedDict of array output, and Series input results in a +results in an dict of array output, and Series input results in a DataFrame output. The keys are 'ghi', 'dni', and 'dhi'. Grid with a clear sky irradiance for a few turbidity values. @@ -437,7 +437,7 @@ A clear sky time series using only basic pvlib functions. In [1]: plt.close(); The input data types determine the returned output type. Array input -results in an OrderedDict of array output, and Series input results in a +results in an dict of array output, and Series input results in a DataFrame output. The keys are 'ghi', 'dni', and 'dhi'. Irradiance as a function of solar elevation. @@ -518,7 +518,7 @@ Contour plots of irradiance as a function of both PW and AOD. In [1]: aod700, precipitable_water = np.meshgrid(aod700, precipitable_water) - # inputs are arrays, so solis is an OrderedDict + # inputs are arrays, so solis is an dict In [1]: solis = clearsky.simplified_solis(apparent_elevation, aod700, ...: precipitable_water, pressure, ...: dni_extra) diff --git a/pvlib/clearsky.py b/pvlib/clearsky.py index da9d197c6b..87c8c78f86 100644 --- a/pvlib/clearsky.py +++ b/pvlib/clearsky.py @@ -4,7 +4,6 @@ """ import os -from collections import OrderedDict import calendar import numpy as np @@ -56,7 +55,7 @@ def ineichen(apparent_zenith, airmass_absolute, linke_turbidity, Returns ------- - clearsky : DataFrame (if Series input) or OrderedDict of arrays + clearsky : DataFrame (if Series input) or dict of arrays Contains the columns/keys ``'dhi', 'dni', 'ghi'``, with the same unit as the input parameter ``dni_extra``. @@ -138,7 +137,7 @@ def ineichen(apparent_zenith, airmass_absolute, linke_turbidity, dhi = ghi - dni*cos_zenith - irrads = OrderedDict() + irrads = {} irrads['ghi'] = ghi irrads['dni'] = dni irrads['dhi'] = dhi @@ -363,8 +362,8 @@ def simplified_solis(apparent_elevation, aod700=0.1, precipitable_water=1., Returns ------- - clearsky : DataFrame (if Series input) or OrderedDict of arrays - DataFrame/OrderedDict contains the columns/keys + clearsky : DataFrame (if Series input) or dict of arrays + DataFrame/dict contains the columns/keys ``'dhi', 'dni', 'ghi'``. References @@ -408,7 +407,7 @@ def simplified_solis(apparent_elevation, aod700=0.1, precipitable_water=1., ghi = i0p * np.exp(-taug/sin_elev**g) * sin_elev dhi = i0p * np.exp(-taud/sin_elev**d) - irrads = OrderedDict() + irrads = {} irrads['ghi'] = ghi irrads['dni'] = dni irrads['dhi'] = dhi @@ -737,7 +736,7 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False, Boolean array or Series of whether or not the given time is clear. Return type is the same as the input type. - components : OrderedDict, optional + components : dict, optional Dict of arrays of whether or not the given time window is clear for each condition. Only provided if ``return_components`` is True. @@ -905,7 +904,7 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False, clear_samples = pd.Series(clear_samples, index=times) if return_components: - components = OrderedDict() + components = {} components['mean_diff_flag'] = c1 components['max_diff_flag'] = c2 components['line_length_flag'] = c3 @@ -971,8 +970,8 @@ def bird(zenith, airmass_relative, aod380, aod500, precipitable_water, Returns ------- - clearsky : DataFrame (if Series input) or OrderedDict of arrays - DataFrame/OrderedDict contains the columns/keys + clearsky : DataFrame (if Series input) or dict of arrays + DataFrame/dict contains the columns/keys ``'dhi', 'dni', 'ghi', 'direct_horizontal'`` in [W/m^2]. See also @@ -1039,8 +1038,8 @@ def bird(zenith, airmass_relative, aod380, aod500, precipitable_water, gh = (id_nh + ias) / (1.0 - albedo * rs) diffuse_horiz = gh - id_nh # TODO: be DRY, use decorator to wrap methods that need to return either - # OrderedDict or DataFrame instead of repeating this boilerplate code - irrads = OrderedDict() + # dict or DataFrame instead of repeating this boilerplate code + irrads = {} irrads['direct_horizontal'] = id_nh irrads['ghi'] = gh irrads['dni'] = id_ diff --git a/pvlib/irradiance.py b/pvlib/irradiance.py index 9d6ddbe665..38bf122b91 100644 --- a/pvlib/irradiance.py +++ b/pvlib/irradiance.py @@ -5,7 +5,6 @@ """ import datetime -from collections import OrderedDict from functools import partial import numpy as np @@ -1958,7 +1957,7 @@ def disc(ghi, solar_zenith, datetime_or_doy, pressure=101325, Returns ------- - output : OrderedDict or DataFrame + output : dict or DataFrame Contains the following keys: * ``dni``: The modeled direct normal irradiance @@ -2000,7 +1999,7 @@ def disc(ghi, solar_zenith, datetime_or_doy, pressure=101325, bad_values = (solar_zenith > max_zenith) | (ghi < 0) | (dni < 0) dni = np.where(bad_values, 0, dni) - output = OrderedDict() + output = {} output['dni'] = dni output['kt'] = kt output['airmass'] = am @@ -2479,7 +2478,7 @@ def gti_dirint(poa_global, aoi, solar_zenith, solar_azimuth, times, Returns ------- - data : DataFrame + data : dict or DataFrame Contains the following keys/columns: * ``ghi``: the modeled global horizontal irradiance. [Wm⁻²] @@ -2517,7 +2516,7 @@ def gti_dirint(poa_global, aoi, solar_zenith, solar_azimuth, times, ghi_gte_90, dni_gte_90, dhi_gte_90 = np.nan, np.nan, np.nan # put the AOI < 90 and AOI >= 90 conditions together - output = OrderedDict() + output = {} output['ghi'] = ghi.where(aoi_lt_90, ghi_gte_90) output['dni'] = dni.where(aoi_lt_90, dni_gte_90) output['dhi'] = dhi.where(aoi_lt_90, dhi_gte_90) @@ -2776,7 +2775,7 @@ def erbs(ghi, zenith, datetime_or_doy, min_cos_zenith=0.065, max_zenith=87): Returns ------- - data : OrderedDict or DataFrame + data : dict or DataFrame Contains the following keys/columns: * ``dni``: the modeled direct normal irradiance. [Wm⁻²] @@ -2823,7 +2822,7 @@ def erbs(ghi, zenith, datetime_or_doy, min_cos_zenith=0.065, max_zenith=87): # ensure that closure relationship remains valid dhi = np.where(bad_values, ghi, dhi) - data = OrderedDict() + data = {} data['dni'] = dni data['dhi'] = dhi data['kt'] = kt @@ -2885,7 +2884,7 @@ def erbs_driesse(ghi, zenith, datetime_or_doy=None, dni_extra=None, Returns ------- - data : OrderedDict or DataFrame + data : dict or DataFrame Contains the following keys/columns: * ``dni``: the modeled direct normal irradiance. [Wm⁻²] @@ -2960,7 +2959,7 @@ def erbs_driesse(ghi, zenith, datetime_or_doy=None, dni_extra=None, # ensure that closure relationship remains valid dhi = np.where(bad_values, ghi, dhi) - data = OrderedDict() + data = {} data['dni'] = dni data['dhi'] = dhi data['kt'] = kt @@ -3012,7 +3011,7 @@ def orgill_hollands(ghi, zenith, datetime_or_doy, dni_extra=None, Returns ------- - data : OrderedDict or DataFrame + data : dict or DataFrame Contains the following keys/columns: * ``dni``: the modeled direct normal irradiance. [Wm⁻²] @@ -3058,7 +3057,7 @@ def orgill_hollands(ghi, zenith, datetime_or_doy, dni_extra=None, # ensure that closure relationship remains valid dhi = np.where(bad_values, ghi, dhi) - data = OrderedDict() + data = {} data['dni'] = dni data['dhi'] = dhi data['kt'] = kt @@ -3113,7 +3112,7 @@ def boland(ghi, solar_zenith, datetime_or_doy, a_coeff=8.645, b_coeff=0.613, Returns ------- - data : OrderedDict or DataFrame + data : dict or DataFrame Contains the following keys/columns: * ``dni``: the modeled direct normal irradiance. [Wm⁻²] @@ -3169,7 +3168,7 @@ def boland(ghi, solar_zenith, datetime_or_doy, a_coeff=8.645, b_coeff=0.613, # ensure that closure relationship remains valid dhi = np.where(bad_values, ghi, dhi) - data = OrderedDict() + data = {} data['dni'] = dni data['dhi'] = dhi data['kt'] = kt @@ -3204,7 +3203,7 @@ def campbell_norman(zenith, transmittance, pressure=101325.0, Returns ------- - irradiance: DataFrame + irradiance: dict or DataFrame Modeled direct normal irradiance, direct horizontal irradiance, and global horizontal irradiance. [Wm⁻²] @@ -3223,7 +3222,7 @@ def campbell_norman(zenith, transmittance, pressure=101325.0, dhi = 0.3 * (1.0 - tau**airmass) * dni_extra * cos_zen ghi = dhi + dni * cos_zen - irrads = OrderedDict() + irrads = {} irrads['ghi'] = ghi irrads['dni'] = dni irrads['dhi'] = dhi @@ -3902,7 +3901,7 @@ def louche(ghi, solar_zenith, datetime_or_doy, max_zenith=90): Returns ------- - data: OrderedDict or DataFrame + data: dict or DataFrame Contains the following keys/columns: * ``dni``: the modeled direct normal irradiance, see :term:`dni`. @@ -3934,7 +3933,7 @@ def louche(ghi, solar_zenith, datetime_or_doy, max_zenith=90): # ensure that closure relationship remains valid dhi = np.where(bad_values, ghi, dhi) - data = OrderedDict() + data = {} data['dni'] = dni data['dhi'] = dhi data['kt'] = Kt diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 02b16edaf1..a04a77980b 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -3,7 +3,6 @@ performance of PV modules and inverters. """ -from collections import OrderedDict import functools import io import itertools @@ -2262,7 +2261,7 @@ def sapm(effective_irradiance, temp_cell, module, *, temperature_ref=25, Returns ------- - A DataFrame with the columns: + A dict or DataFrame with the columns: * i_sc : Short-circuit current (A) * i_mp : Current at the maximum-power point (A) @@ -2369,7 +2368,7 @@ def sapm(effective_irradiance, temp_cell, module, *, temperature_ref=25, # avoid repeated __getitem__ cells_in_series = module['Cells_in_Series'] - out = OrderedDict() + out = {} out['i_sc'] = ( module['Isco'] * Ee * (1 + module['Aisc']*(temp_cell - @@ -2684,7 +2683,7 @@ def max_power_point(photocurrent, saturation_current, resistance_series, Returns ------- - OrderedDict or pandas.DataFrame + dict or pandas.DataFrame ``(i_mp, v_mp, p_mp)`` Notes @@ -2702,7 +2701,7 @@ def max_power_point(photocurrent, saturation_current, resistance_series, ivp = {'i_mp': i_mp, 'v_mp': v_mp, 'p_mp': p_mp} out = pd.DataFrame(ivp, index=photocurrent.index) else: - out = OrderedDict() + out = {} out['i_mp'] = i_mp out['v_mp'] = v_mp out['p_mp'] = p_mp diff --git a/tests/test_clearsky.py b/tests/test_clearsky.py index ed1ce85c64..534c542a65 100644 --- a/tests/test_clearsky.py +++ b/tests/test_clearsky.py @@ -1,5 +1,3 @@ -from collections import OrderedDict - import numpy as np from numpy import nan import pandas as pd @@ -76,7 +74,7 @@ def test_ineichen_series_perez_enhancement(): def test_ineichen_scalar_input(): - expected = OrderedDict() + expected = {} expected['ghi'] = 1038.159219 expected['dni'] = 942.2081860378344 expected['dhi'] = 110.26529293612793 @@ -100,7 +98,7 @@ def test_ineichen_nans(): airmass_absolute = np.full(length, 1.) - expected = OrderedDict() + expected = {} expected['ghi'] = np.full(length, np.nan) expected['dni'] = np.full(length, np.nan) expected['dhi'] = np.full(length, np.nan) @@ -117,7 +115,7 @@ def test_ineichen_nans(): def test_ineichen_arrays(): - expected = OrderedDict() + expected = {} expected['ghi'] = (np. array([[[1095.77074798, 1054.17449885, 1014.15727338], @@ -283,7 +281,7 @@ def test_haurwitz(): def test_simplified_solis_scalar_elevation(): - expected = OrderedDict() + expected = {} expected['ghi'] = 1064.653145 expected['dni'] = 959.335463 expected['dhi'] = 129.125602 @@ -294,7 +292,7 @@ def test_simplified_solis_scalar_elevation(): def test_simplified_solis_scalar_neg_elevation(): - expected = OrderedDict() + expected = {} expected['ghi'] = 0 expected['dni'] = 0 expected['dhi'] = 0 @@ -368,7 +366,7 @@ def test_simplified_solis_precipitable_water(): def test_simplified_solis_small_scalar_pw(): - expected = OrderedDict() + expected = {} expected['ghi'] = 1107.84678941 expected['dni'] = 1001.15353307 expected['dhi'] = 128.58887606 @@ -379,7 +377,7 @@ def test_simplified_solis_small_scalar_pw(): def test_simplified_solis_return_arrays(): - expected = OrderedDict() + expected = {} expected['ghi'] = np.array([[ 1148.40081325, 913.42330823], [ 965.48550828, 760.04527609]]) @@ -423,7 +421,7 @@ def test_simplified_solis_nans_arrays(): dni_extra = np.full(length, 1370.) dni_extra[4] = np.nan - expected = OrderedDict() + expected = {} expected['ghi'] = np.full(length, np.nan) expected['dni'] = np.full(length, np.nan) expected['dhi'] = np.full(length, np.nan) @@ -461,7 +459,7 @@ def test_simplified_solis_nans_series(): dni_extra = np.full(length, 1370.) dni_extra[4] = np.nan - expected = OrderedDict() + expected = {} expected['ghi'] = np.full(length, np.nan) expected['dni'] = np.full(length, np.nan) expected['dhi'] = np.full(length, np.nan) @@ -603,7 +601,7 @@ def test_detect_clearsky_components(detect_clearsky_data): return_components=True) assert_series_equal(expected['Clear or not'], clear_samples, check_dtype=False, check_names=False) - assert isinstance(components, OrderedDict) + assert isinstance(components, dict) assert np.allclose(alpha, 0.9633903181941296) diff --git a/tests/test_irradiance.py b/tests/test_irradiance.py index f5f1c7ebd6..b0d9863ad5 100644 --- a/tests/test_irradiance.py +++ b/tests/test_irradiance.py @@ -1,5 +1,4 @@ import datetime -from collections import OrderedDict import warnings import numpy as np @@ -1252,7 +1251,7 @@ def test_erbs_all_scalar(): zenith = 10 doy = 180 - expected = OrderedDict() + expected = {} expected['dni'] = 8.42358014e+02 expected['dhi'] = 1.70439297e+02 expected['kt'] = 7.68919470e-01 diff --git a/tests/test_pvsystem.py b/tests/test_pvsystem.py index 78aaed70c4..866fc4bae4 100644 --- a/tests/test_pvsystem.py +++ b/tests/test_pvsystem.py @@ -1,4 +1,3 @@ -from collections import OrderedDict import itertools import numpy as np @@ -191,7 +190,7 @@ def test_sapm(sapm_module_params): out = pvsystem.sapm(1000, 25, sapm_module_params) - expected = OrderedDict() + expected = {} expected['i_sc'] = sapm_module_params['Isco'] expected['i_mp'] = sapm_module_params['Impo'] expected['v_oc'] = sapm_module_params['Voco'] @@ -1651,13 +1650,13 @@ def test_singlediode_series_expected(cec_module_params): out = pvsystem.singlediode(IL, I0, Rs, Rsh, nNsVth, method='lambertw') - expected = OrderedDict([('i_sc', array([0., 3.01079860, 6.00726296])), - ('v_oc', array([0., 9.96959733, 10.29603253])), - ('i_mp', array([0., 2.656285960, 5.290525645])), - ('v_mp', array([0., 8.321092255, 8.409413795])), - ('p_mp', array([0., 22.10320053, 44.49021934])), - ('i_x', array([0., 2.884132006, 5.746202281])), - ('i_xx', array([0., 2.052691562, 3.909673879]))]) + expected = dict([('i_sc', array([0., 3.01079860, 6.00726296])), + ('v_oc', array([0., 9.96959733, 10.29603253])), + ('i_mp', array([0., 2.656285960, 5.290525645])), + ('v_mp', array([0., 8.321092255, 8.409413795])), + ('p_mp', array([0., 22.10320053, 44.49021934])), + ('i_x', array([0., 2.884132006, 5.746202281])), + ('i_xx', array([0., 2.052691562, 3.909673879]))]) for k, v in out.items(): assert_allclose(v, expected[k], atol=1e-2) From dada7df904bf90585f3be76f29f4abd2c77c7192 Mon Sep 17 00:00:00 2001 From: Kevin Anderson Date: Fri, 11 Sep 2026 14:13:39 +0200 Subject: [PATCH 2/2] whatsnew --- docs/sphinx/source/whatsnew/v0.16.0.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.16.0.rst b/docs/sphinx/source/whatsnew/v0.16.0.rst index ace59c2496..078ea04b95 100644 --- a/docs/sphinx/source/whatsnew/v0.16.0.rst +++ b/docs/sphinx/source/whatsnew/v0.16.0.rst @@ -34,6 +34,25 @@ Breaking Changes and :py:func:`pvlib.iam.schlick_diffuse` from tuple to ``dict``, to be consistent with :py:func:`pvlib.iam.marion_diffuse`. (:issue:`2837`, :pull:`2842`) +* All ``OrderedDict`` outputs are now ``dict``. The following functions + are affected (:issue:`1684`, :pull:`2856`): + + * :py:func:`~pvlib.clearsky.ineichen` + * :py:func:`~pvlib.clearsky.simplified_solis` + * :py:func:`~pvlib.clearsky.detect_clearsky` + * :py:func:`~pvlib.clearsky.bird` + * :py:func:`~pvlib.irradiance.disc` + * :py:func:`~pvlib.irradiance.gti_dirint` + * :py:func:`~pvlib.irradiance.erbs` + * :py:func:`~pvlib.irradiance.erbs_driesse` + * :py:func:`~pvlib.irradiance.orgill_hollands` + * :py:func:`~pvlib.irradiance.boland` + * :py:func:`~pvlib.irradiance.campbell_norman` + * :py:func:`~pvlib.irradiance.louche` + * :py:func:`~pvlib.pvsystem.sapm` + * :py:func:`~pvlib.pvsystem.max_power_point` + + Deprecations ~~~~~~~~~~~~