Skip to content

initial implementation of pvsyst_celltemp function (#552) #628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 11, 2018
Merged
9 changes: 9 additions & 0 deletions docs/sphinx/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,15 @@ PVWatts model
pvsystem.pvwatts_dc
pvsystem.pvwatts_ac
pvsystem.pvwatts_losses
pvsystem.pvwatts_losses

PVsyst model
------------

.. autosummary::
:toctree: generated/

pvsystem.pvsyst_celltemp

Other
-----
Expand Down
2 changes: 2 additions & 0 deletions docs/sphinx/source/whatsnew/v0.6.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Enhancements
* Add warning message when :py:func:`pvlib.spa` is reloaded. (:issue:`401`)
* 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
Expand Down Expand Up @@ -86,4 +87,5 @@ Contributors
* Cliff Hansen (:ghuser:`cwhanse`)
* Mark Mikofski (:ghuser:`mikofski`)
* Anton Driesse (:ghuser:`adriesse`)
* Cameron Stark (:ghuser:`camerontstark`)
* Jonathan Gaffiot (:ghuser:`jgaffiot`)
81 changes: 81 additions & 0 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1887,6 +1887,87 @@ 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 cell temperature using the PVSyst model.

Parameters
----------
poa_global : numeric
Total incident irradiance in W/m^2.

wind_speed : numeric
Wind speed in m/s at a height of 10 meters.

temp_air : numeric
Ambient dry bulb temperature in degrees C.

eta_m : numeric
Module external efficiency as a fraction, i.e., DC power / poa_global.

alpha_absorption : float
Absorption coefficient, default is 0.9.

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.

* forced_convection_coeff : float
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)}

if isinstance(temp_model, str):
natural_convenction_coeff, forced_convection_coeff = temp_models[
temp_model.lower()
]
elif isinstance(temp_model, (tuple, list)):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CameronTStark in the future maybe consider duck typing instead of type checking. Even though duck typing is the Pythonic preference, it may not always work best. I think your solution here is fine, but in general I think duck typing usually results in more robust code. Eg:

else:
    natural_convenction_coeff, forced_convection_coeff = temp_model
    # already raises ValueError or TypeError
    # or use try: except to raise a more descriptive exception

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikofski Yes, thanks for the suggestion! I've been trying to pay attention more to duck typing in my code and thought about it for this instance but since this was my first contribution and sapm_celltemp() had the Look Before You Leap type check in it already I figured I'd just follow convention.

natural_convenction_coeff, forced_convection_coeff = temp_model
else:
raise TypeError(
"Please format temp_model as a str, or tuple/list."
)

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):
"""
Calculates the SAPM spectral loss coefficient, F1.
Expand Down
40 changes: 40 additions & 0 deletions pvlib/test/test_pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,46 @@ def test_PVSystem_sapm_celltemp(mocker):
assert out.shape == (1, 2)


def test_pvsyst_celltemp_default():
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, 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, 0.1,
temp_model=[26.5, 7.68])
assert_allclose(list_non_model, 31.233, 0.001)


def test_pvsyst_celltemp_model_wrong_type():
with pytest.raises(TypeError):
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):
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)
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.0, 23.96551, 5.0], index=times)
assert_series_equal(expected, pvtemps)


def test_adrinverter(sam_data):
inverters = sam_data['adrinverter']
testinv = 'Ablerex_Electronics_Co___Ltd___' \
Expand Down