From 800a42fa4c3232ee2ecf164fe508f31f2537d082 Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Thu, 6 Dec 2018 15:54:01 -0700 Subject: [PATCH 1/9] initial implementation of pvsyst_celltemp function (#552) --- pvlib/pvsystem.py | 67 +++++++++++++++++++++++++++++++++++++ pvlib/test/test_pvsystem.py | 24 +++++++++++++ 2 files changed, 91 insertions(+) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 64fa54d032..121b6f5e9b 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1886,6 +1886,73 @@ def sapm_celltemp(poa_global, wind_speed, temp_air, return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) +def pvsyst_celltemp(poa_global, wind_speed, temp_air, + eta_m=0.1, + alpha_absorption=0.9, + temp_model='freestanding'): + """ + Calculate PVSyst cell temperature. + + Parameters + ------------ + poa_global : float or Series + Total incident irradiance in W/m^2. + + wind_speed : float or Series + Wind speed in m/s at a height of 10 meters. + + temp_air : float or Series + Ambient dry bulb temperature in degrees C. + + eta_m : numeric + PV module efficiency as a fraction + + alpha_absorption : float + Absorption coefficient, default is 0.9 + + temp_model : string, list, or dict, default 'freestanding' + Model to be used. + + If string, can be: + + * 'freestanding' (default) + * 'insulated' + + If tuple/list, supply parameters in the following order: + + * natural_convenction_coeff : float + Natural convection coefficient. Freestanding default is 29, + fully insulated arrays is 15. Recommended values are between + 23.5 and 26.5. + + * forced_convection_coeff : float + Forced convection coefficient, default is 0. Recommended values + are between 6.25 and 7.68. + Returns + -------- + temp_cell : numeric or Series + Cell temperature in degrees Celsius + """ + + temp_models = {'freestanding': (29.0, 0), + 'insulated': (15.0, 0), + } + + if isinstance(temp_model, str): + natural_convenction_coeff, forced_convection_coeff = temp_models[temp_model.lower()] + elif isinstance(temp_model, (tuple, list)): + natural_convenction_coeff, forced_convection_coeff = temp_model + else: + raise TypeError("Please format temp_model as a str, or tuple/list. See function docstring for guidance") + + combined_convection_coeff = (forced_convection_coeff * wind_speed) + \ + natural_convenction_coeff + + absorption_coeff = alpha_absorption * poa_global * (1 - eta_m) + temp_difference = absorption_coeff/combined_convection_coeff + temp_cell = temp_air + temp_difference + + return temp_cell def sapm_spectral_loss(airmass_absolute, module): """ diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index c0e89c5095..8297d6254a 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1077,6 +1077,30 @@ def test_PVSystem_sapm_celltemp(mocker): assert isinstance(out, pd.DataFrame) assert out.shape == (1, 2) +def test_pvsyst_celltemp_default(): + default = pvsystem.pvsyst_celltemp(900, 5, 20, .1) + assert_allclose(default, 45.137, 0.001) + +def test_pvsyst_celltemp_non_model(): + tup_non_model = pvsystem.pvsyst_celltemp(900, 5, 20, .1, temp_model=(23.5, 6.25)) + assert_allclose(tup_non_model, 33.315, 0.001) + + list_non_model = pvsystem.pvsyst_celltemp(900, 5, 20, .1, temp_model=[26.5, 7.68]) + assert_allclose(list_non_model, 31.233, 0.001) + +def test_pvsyst_celltemp_with_index(): + times = pd.DatetimeIndex(start='2015-01-01', end='2015-01-02', freq='12H') + temps = pd.Series([0, 10, 5], index=times) + irrads = pd.Series([0, 500, 0], index=times) + winds = pd.Series([10, 5, 0], index=times) + + pvtemps = pvsystem.pvsyst_celltemp(irrads, winds, temps) + + expected = pd.Series([0., 23.96551, 5.], + index=times) + + assert_series_equal(expected, pvtemps) + def test_adrinverter(sam_data): inverters = sam_data['adrinverter'] From e5bd8ad3166224671711de8528f524085286f9b7 Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Fri, 7 Dec 2018 11:08:19 -0700 Subject: [PATCH 2/9] Reformat for stickler-ci --- pvlib/pvsystem.py | 33 +++++++++++++++++++++------------ pvlib/test/test_pvsystem.py | 18 ++++++++++-------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 121b6f5e9b..097d7695e5 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1886,10 +1886,15 @@ def sapm_celltemp(poa_global, wind_speed, temp_air, return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) -def pvsyst_celltemp(poa_global, wind_speed, temp_air, - eta_m=0.1, - alpha_absorption=0.9, - temp_model='freestanding'): + +def pvsyst_celltemp( + poa_global, + wind_speed, + temp_air, + eta_m=0.1, + alpha_absorption=0.9, + temp_model="freestanding", +): """ Calculate PVSyst cell temperature. @@ -1934,26 +1939,30 @@ def pvsyst_celltemp(poa_global, wind_speed, temp_air, Cell temperature in degrees Celsius """ - temp_models = {'freestanding': (29.0, 0), - 'insulated': (15.0, 0), - } + temp_models = {"freestanding": (29.0, 0), "insulated": (15.0, 0)} if isinstance(temp_model, str): - natural_convenction_coeff, forced_convection_coeff = temp_models[temp_model.lower()] + natural_convenction_coeff, forced_convection_coeff = temp_models[ + temp_model.lower() + ] elif isinstance(temp_model, (tuple, list)): natural_convenction_coeff, forced_convection_coeff = temp_model else: - raise TypeError("Please format temp_model as a str, or tuple/list. See function docstring for guidance") + raise TypeError( + "Please format temp_model as a str, or tuple/list." + ) - combined_convection_coeff = (forced_convection_coeff * wind_speed) + \ - natural_convenction_coeff + combined_convection_coeff = ( + forced_convection_coeff * wind_speed + ) + natural_convenction_coeff absorption_coeff = alpha_absorption * poa_global * (1 - eta_m) - temp_difference = absorption_coeff/combined_convection_coeff + temp_difference = absorption_coeff / combined_convection_coeff temp_cell = temp_air + temp_difference return temp_cell + def sapm_spectral_loss(airmass_absolute, module): """ Calculates the SAPM spectral loss coefficient, F1. diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 8297d6254a..637292a565 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1077,28 +1077,30 @@ def test_PVSystem_sapm_celltemp(mocker): assert isinstance(out, pd.DataFrame) assert out.shape == (1, 2) + def test_pvsyst_celltemp_default(): - default = pvsystem.pvsyst_celltemp(900, 5, 20, .1) + default = pvsystem.pvsyst_celltemp(900, 5, 20, 0.1) assert_allclose(default, 45.137, 0.001) + def test_pvsyst_celltemp_non_model(): - tup_non_model = pvsystem.pvsyst_celltemp(900, 5, 20, .1, temp_model=(23.5, 6.25)) + tup_non_model = pvsystem.pvsyst_celltemp(900, 5, 20, 0.1, + temp_model=(23.5, 6.25)) assert_allclose(tup_non_model, 33.315, 0.001) - list_non_model = pvsystem.pvsyst_celltemp(900, 5, 20, .1, temp_model=[26.5, 7.68]) + list_non_model = pvsystem.pvsyst_celltemp(900, 5, 20, 0.1, + temp_model=[26.5, 7.68]) assert_allclose(list_non_model, 31.233, 0.001) + def test_pvsyst_celltemp_with_index(): - times = pd.DatetimeIndex(start='2015-01-01', end='2015-01-02', freq='12H') + times = pd.DatetimeIndex(start="2015-01-01", end="2015-01-02", freq="12H") temps = pd.Series([0, 10, 5], index=times) irrads = pd.Series([0, 500, 0], index=times) winds = pd.Series([10, 5, 0], index=times) pvtemps = pvsystem.pvsyst_celltemp(irrads, winds, temps) - - expected = pd.Series([0., 23.96551, 5.], - index=times) - + expected = pd.Series([0.0, 23.96551, 5.0], index=times) assert_series_equal(expected, pvtemps) From e52c9cb2aea3f63d1ea71970863020bd4fbb320d Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Fri, 7 Dec 2018 11:26:04 -0700 Subject: [PATCH 3/9] Add error tests for pvsyst_celltemp --- pvlib/test/test_pvsystem.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 637292a565..0679b7b750 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1093,6 +1093,20 @@ def test_pvsyst_celltemp_non_model(): assert_allclose(list_non_model, 31.233, 0.001) +def test_pvsyst_celltemp_model_wrong_type(): + with pytest.raises(TypeError): + default = pvsystem.pvsyst_celltemp( + 900, 5, 20, 0.1, + temp_model={"won't": 23.5, "work": 7.68}) + + +def test_pvsyst_celltemp_model_non_option(): + with pytest.raises(KeyError): + default = pvsystem.pvsyst_celltemp( + 900, 5, 20, 0.1, + temp_model="not_an_option") + + def test_pvsyst_celltemp_with_index(): times = pd.DatetimeIndex(start="2015-01-01", end="2015-01-02", freq="12H") temps = pd.Series([0, 10, 5], index=times) From 63f79b133a3337ba7c8a6bc583f697e229b257c6 Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Fri, 7 Dec 2018 11:32:53 -0700 Subject: [PATCH 4/9] Remove unused variables per stickler-ci --- pvlib/test/test_pvsystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 0679b7b750..bff3277a54 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -1095,14 +1095,14 @@ def test_pvsyst_celltemp_non_model(): def test_pvsyst_celltemp_model_wrong_type(): with pytest.raises(TypeError): - default = pvsystem.pvsyst_celltemp( + pvsystem.pvsyst_celltemp( 900, 5, 20, 0.1, temp_model={"won't": 23.5, "work": 7.68}) def test_pvsyst_celltemp_model_non_option(): with pytest.raises(KeyError): - default = pvsystem.pvsyst_celltemp( + pvsystem.pvsyst_celltemp( 900, 5, 20, 0.1, temp_model="not_an_option") From 3b43a09dfc9444dff0437d72ad0ffca4f9fbd6db Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Fri, 7 Dec 2018 15:15:35 -0700 Subject: [PATCH 5/9] Add pvsystem.pvsyst_celltemp function to docs --- docs/sphinx/source/api.rst | 9 +++++++++ docs/sphinx/source/whatsnew/v0.6.1.rst | 2 ++ 2 files changed, 11 insertions(+) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 2716c7fbe6..83b306a0d1 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -268,6 +268,15 @@ PVWatts model pvsystem.pvwatts_dc pvsystem.pvwatts_ac pvsystem.pvwatts_losses + pvsystem.pvwatts_losses + +PVWatts model +------------- + +.. autosummary:: + :toctree: generated/ + + pvsystem.pvsyst_celltemp Other ----- diff --git a/docs/sphinx/source/whatsnew/v0.6.1.rst b/docs/sphinx/source/whatsnew/v0.6.1.rst index e92e5a11b8..c84ad379f5 100644 --- a/docs/sphinx/source/whatsnew/v0.6.1.rst +++ b/docs/sphinx/source/whatsnew/v0.6.1.rst @@ -46,6 +46,7 @@ Enhancements * Add warning message when :py:func:`pvlib.spa` is reloaded. * Add option for :py:func:`pvlib.irradiance.disc` to use relative airmass by supplying `pressure=None`. (:issue:`449`) +* Created :py:func:`pvlib.pvsystem.pvsyst_celltemp` to implement PVsyst's cell temperature model. (:issue:`552`) Bug fixes @@ -72,3 +73,4 @@ Contributors * Cliff Hansen (:ghuser:`cwhanse`) * Mark Mikofski (:ghuser:`mikofski`) * Anton Driesse (:ghuser:`adriesse`) +* Cameron Stark (:ghuser:`camerontstark`) From 116a7bafe4f0a32e48aa66b569688e53b24eaa4b Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Fri, 7 Dec 2018 15:51:34 -0700 Subject: [PATCH 6/9] correct api title --- docs/sphinx/source/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index 83b306a0d1..f6a6f0c972 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -270,7 +270,7 @@ PVWatts model pvsystem.pvwatts_losses pvsystem.pvwatts_losses -PVWatts model +PVsyst model ------------- .. autosummary:: From e9a9463c7518e63aabef68b1df30c35d684fed7d Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Tue, 11 Dec 2018 09:15:11 -0700 Subject: [PATCH 7/9] formatting changes per wholmgren and cwhanse --- docs/sphinx/source/api.rst | 2 +- pvlib/pvsystem.py | 45 +++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/docs/sphinx/source/api.rst b/docs/sphinx/source/api.rst index f6a6f0c972..567376554e 100644 --- a/docs/sphinx/source/api.rst +++ b/docs/sphinx/source/api.rst @@ -271,7 +271,7 @@ PVWatts model pvsystem.pvwatts_losses PVsyst model -------------- +------------ .. autosummary:: :toctree: generated/ diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 097d7695e5..ed29e359d8 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1887,56 +1887,61 @@ def sapm_celltemp(poa_global, wind_speed, temp_air, return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) -def pvsyst_celltemp( - poa_global, - wind_speed, - temp_air, - eta_m=0.1, - alpha_absorption=0.9, - temp_model="freestanding", +def pvsyst_celltemp(poa_global, wind_speed, temp_air, + eta_m=0.1, alpha_absorption=0.9, temp_model="freestanding", ): """ - Calculate PVSyst cell temperature. + Calculate cell temperature using the PVSyst model. Parameters - ------------ - poa_global : float or Series + ---------- + poa_global : numeric Total incident irradiance in W/m^2. - wind_speed : float or Series + wind_speed : numeric Wind speed in m/s at a height of 10 meters. - temp_air : float or Series + temp_air : numeric Ambient dry bulb temperature in degrees C. eta_m : numeric - PV module efficiency as a fraction + Module external efficiency as a fraction, i.e., DC power / poa_global. alpha_absorption : float - Absorption coefficient, default is 0.9 + Absorption coefficient, default is 0.9. - temp_model : string, list, or dict, default 'freestanding' + temp_model : string, tuple, or list, default 'freestanding' (no dict) Model to be used. If string, can be: * 'freestanding' (default) + Modules with rear surfaces exposed to open air (e.g. rack mounted). * 'insulated' + Modules with rear surfaces in close proximity to another + surface (e.g. roof mounted). If tuple/list, supply parameters in the following order: * natural_convenction_coeff : float Natural convection coefficient. Freestanding default is 29, - fully insulated arrays is 15. Recommended values are between - 23.5 and 26.5. + fully insulated arrays is 15. * forced_convection_coeff : float - Forced convection coefficient, default is 0. Recommended values - are between 6.25 and 7.68. + Forced convection coefficient, default is 0. + Returns - -------- + ------- temp_cell : numeric or Series Cell temperature in degrees Celsius + + References + ---------- + [1]"PVsyst 6 Help", Files.pvsyst.com, 2018. [Online]. Available: + http://files.pvsyst.com/help/index.html. [Accessed: 10- Dec- 2018]. + + [2] Faiman, D. (2008). “Assessing the outdoor operating temperature of + photovoltaic modules.” Progress in Photovoltaics 16(4): 307-315. """ temp_models = {"freestanding": (29.0, 0), "insulated": (15.0, 0)} From 6d902e7907b1f21eb5f522b13fbfdc7a52aa1880 Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Tue, 11 Dec 2018 12:27:26 -0700 Subject: [PATCH 8/9] Additional formatting improvements --- pvlib/pvsystem.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index ed29e359d8..0ac2d72076 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1887,9 +1887,8 @@ def sapm_celltemp(poa_global, wind_speed, temp_air, return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) -def pvsyst_celltemp(poa_global, wind_speed, temp_air, - eta_m=0.1, alpha_absorption=0.9, temp_model="freestanding", -): +def pvsyst_celltemp(poa_global, wind_speed, temp_air, eta_m=0.1, + alpha_absorption=0.9, temp_model="freestanding"): """ Calculate cell temperature using the PVSyst model. @@ -1916,7 +1915,8 @@ def pvsyst_celltemp(poa_global, wind_speed, temp_air, If string, can be: * 'freestanding' (default) - Modules with rear surfaces exposed to open air (e.g. rack mounted). + Modules with rear surfaces exposed to open air (e.g. rack + mounted). * 'insulated' Modules with rear surfaces in close proximity to another surface (e.g. roof mounted). From f8c9c528a6df3f3fe4c307d2d467e7faf49dafb0 Mon Sep 17 00:00:00 2001 From: Cameron Stark Date: Tue, 11 Dec 2018 13:56:51 -0700 Subject: [PATCH 9/9] Fix symbol in Reference --- pvlib/pvsystem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 0ac2d72076..6215c1a73d 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -1940,8 +1940,8 @@ def pvsyst_celltemp(poa_global, wind_speed, temp_air, eta_m=0.1, [1]"PVsyst 6 Help", Files.pvsyst.com, 2018. [Online]. Available: http://files.pvsyst.com/help/index.html. [Accessed: 10- Dec- 2018]. - [2] Faiman, D. (2008). “Assessing the outdoor operating temperature of - photovoltaic modules.” Progress in Photovoltaics 16(4): 307-315. + [2] Faiman, D. (2008). "Assessing the outdoor operating temperature of + photovoltaic modules." Progress in Photovoltaics 16(4): 307-315. """ temp_models = {"freestanding": (29.0, 0), "insulated": (15.0, 0)}