-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Handle poa_global and effective_irradiance for cell temperature models #1129
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
Changes from 8 commits
9317efb
68b7cf7
8166cf6
7e54534
40d8734
c720a75
2211fed
9174fe3
8c3e643
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1023,7 +1023,9 @@ def _set_celltemp(self, model): | |||||
------- | ||||||
self | ||||||
""" | ||||||
poa = _tuple_from_dfs(self.results.total_irrad, 'poa_global') | ||||||
|
||||||
poa = _irrad_for_celltemp(self.results.total_irrad, | ||||||
self.results.effective_irradiance) | ||||||
temp_air = _tuple_from_dfs(self.weather, 'temp_air') | ||||||
wind_speed = _tuple_from_dfs(self.weather, 'wind_speed') | ||||||
self.results.cell_temperature = model(poa, temp_air, wind_speed) | ||||||
|
@@ -1464,13 +1466,22 @@ def prepare_inputs_from_poa(self, data): | |||||
return self | ||||||
|
||||||
def _get_cell_temperature(self, data, | ||||||
total_irrad, temperature_model_parameters): | ||||||
poa, temperature_model_parameters): | ||||||
"""Extract the cell temperature data from a DataFrame. | ||||||
|
||||||
If 'cell_temperature' column exists then it is returned. If | ||||||
'module_temperature' column exists then it is used to calculate | ||||||
the cell temperature. If neither column exists then None is | ||||||
If 'cell_temperature' column exists in data then it is returned. If | ||||||
'module_temperature' column exists in data, then it is used with poa to | ||||||
calculate the cell temperature. If neither column exists then None is | ||||||
returned. | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
data : DataFrame (not a tuple of DataFrame) | ||||||
poa : Series (not a tuple of Series) | ||||||
|
||||||
Returns | ||||||
------- | ||||||
Series | ||||||
""" | ||||||
if 'cell_temperature' in data: | ||||||
return data['cell_temperature'] | ||||||
|
@@ -1483,14 +1494,14 @@ def _get_cell_temperature(self, data, | |||||
# use SAPM cell temperature model only | ||||||
return pvlib.temperature.sapm_cell_from_module( | ||||||
module_temperature=data['module_temperature'], | ||||||
poa_global=total_irrad['poa_global'], | ||||||
poa_global=poa, | ||||||
deltaT=temperature_model_parameters['deltaT']) | ||||||
|
||||||
def _prepare_temperature_single_array(self, data): | ||||||
"""Set cell_temperature using a single weather data frame.""" | ||||||
def _prepare_temperature_single_array(self, data, poa): | ||||||
"""Set cell_temperature using a single data frame.""" | ||||||
self.results.cell_temperature = self._get_cell_temperature( | ||||||
data, | ||||||
self.results.total_irrad, | ||||||
poa, | ||||||
self.system.temperature_model_parameters | ||||||
) | ||||||
if self.results.cell_temperature is None: | ||||||
|
@@ -1505,7 +1516,7 @@ def _prepare_temperature(self, data=None): | |||||
If 'data' contains 'cell_temperature', these values are assigned to | ||||||
attribute ``cell_temperature``. If 'data' contains 'module_temperature` | ||||||
and `temperature_model' is 'sapm', cell temperature is calculated using | ||||||
:py:func:`pvlib.temperature.sapm_celL_from_module`. Otherwise, cell | ||||||
:py:func:`pvlib.temperature.sapm_cell_from_module`. Otherwise, cell | ||||||
temperature is calculated by 'temperature_model'. | ||||||
|
||||||
Parameters | ||||||
|
@@ -1521,14 +1532,16 @@ def _prepare_temperature(self, data=None): | |||||
Assigns attribute ``results.cell_temperature``. | ||||||
|
||||||
""" | ||||||
poa = _irrad_for_celltemp(self.results.total_irrad, | ||||||
self.results.effective_irradiance) | ||||||
if not isinstance(data, tuple) and self.system.num_arrays > 1: | ||||||
# broadcast data to all arrays | ||||||
data = (data,) * self.system.num_arrays | ||||||
elif not isinstance(data, tuple): | ||||||
return self._prepare_temperature_single_array(data) | ||||||
return self._prepare_temperature_single_array(data, poa) | ||||||
given_cell_temperature = tuple(itertools.starmap( | ||||||
self._get_cell_temperature, | ||||||
zip(data, self.results.total_irrad, | ||||||
self.system.temperature_model_parameters) | ||||||
zip(data, poa, self.system.temperature_model_parameters) | ||||||
)) | ||||||
# If cell temperature has been specified for all arrays return | ||||||
# immediately and do not try to compute it. | ||||||
|
@@ -1716,10 +1729,8 @@ def run_model_from_effective_irradiance(self, data=None): | |||||
---------- | ||||||
data : DataFrame, or list or tuple of DataFrame | ||||||
Required column is ``'effective_irradiance'``. | ||||||
If optional column ``'cell_temperature'`` is provided, these values | ||||||
are used instead of `temperature_model`. If optional column | ||||||
``'module_temperature'`` is provided, `temperature_model` must be | ||||||
``'sapm'``. | ||||||
Optional columns include ``'cell_temperature'``, | ||||||
``'module_temperature'`` and ``'poa_global'``. | ||||||
|
||||||
If the ModelChain's PVSystem has multiple arrays, `data` must be a | ||||||
list or tuple with the same length and order as the PVsystem's | ||||||
|
@@ -1740,6 +1751,19 @@ def run_model_from_effective_irradiance(self, data=None): | |||||
|
||||||
Notes | ||||||
----- | ||||||
Optional `data` columns ``'cell_temperature'``, | ||||||
``'module_temperature'`` and ``'poa_global'`` are used for determining | ||||||
cell temperature. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not rendering as desired. might need a blank line here |
||||||
* If optional column ``'cell_temperature'`` is present, these values | ||||||
are used and `temperature_model` is ignored. | ||||||
* If optional column ``'module_temperature'`` is preset, | ||||||
`temperature_model` must be ``'sapm'``. | ||||||
* Otherwise, cell temperature is calculated using `temperature_model`. | ||||||
|
||||||
The cell temperature models require plane-of-array irradiance as input. | ||||||
If optional column ``'poa_global'`` is present, these data are used. | ||||||
If ``'poa_global'`` is not present, ``'effective_irradiance'`` is used. | ||||||
|
||||||
Assigns attributes: ``weather``, ``total_irrad``, | ||||||
``effective_irradiance``, ``cell_temperature``, ``dc``, ``ac``, | ||||||
``losses``, ``diode_params`` (if dc_model is a single diode model). | ||||||
|
@@ -1760,6 +1784,29 @@ def run_model_from_effective_irradiance(self, data=None): | |||||
return self | ||||||
|
||||||
|
||||||
def _irrad_for_celltemp(total_irrad, effective_irradiance): | ||||||
""" | ||||||
Determine irradiance to use for cell temperature models, in order | ||||||
of preference 'poa_global' then 'effective_irradiance' | ||||||
|
||||||
Returns | ||||||
------- | ||||||
Series of tuple of Series | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
tuple if total_irrad is a tuple of DataFrame | ||||||
|
||||||
""" | ||||||
if isinstance(total_irrad, tuple): | ||||||
if all(['poa_global' in df for df in total_irrad]): | ||||||
return _tuple_from_dfs(total_irrad, 'poa_global') | ||||||
else: | ||||||
return effective_irradiance | ||||||
else: | ||||||
if 'poa_global' in total_irrad: | ||||||
return total_irrad['poa_global'] | ||||||
else: | ||||||
return effective_irradiance | ||||||
|
||||||
|
||||||
def _snl_params(inverter_params): | ||||||
"""Return True if `inverter_params` includes parameters for the | ||||||
Sandia inverter model.""" | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -802,7 +802,7 @@ def test__prepare_temperature_arrays_weather(sapm_dc_snl_ac_system_same_arrays, | |
location, weather, | ||
total_irrad): | ||
data = weather.copy() | ||
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad | ||
data[['poa_global', 'poa_direct', 'poa_diffuse']] = total_irrad | ||
data_two = data.copy() | ||
mc = ModelChain(sapm_dc_snl_ac_system_same_arrays, location, | ||
aoi_model='no_loss', spectral_model='no_loss') | ||
|
@@ -918,6 +918,31 @@ def test_run_model_from_effective_irradiance(sapm_dc_snl_ac_system, location, | |
assert_series_equal(ac, expected) | ||
|
||
|
||
def test_run_model_from_effective_irradiance_no_poa_global( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we also need a test for when both effective irradiance and poa global are provided? I think not but thought it was worth mentioning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's worth adding this test. All of the tests for |
||
sapm_dc_snl_ac_system, location, weather, total_irrad): | ||
data = weather.copy() | ||
data['effective_irradiance'] = total_irrad['poa_global'] | ||
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss', | ||
spectral_model='no_loss') | ||
ac = mc.run_model_from_effective_irradiance(data).results.ac | ||
expected = pd.Series(np.array([149.280238, 96.678385]), | ||
index=data.index) | ||
assert_series_equal(ac, expected) | ||
|
||
|
||
def test_run_model_from_effective_irradiance_poa_global_differs( | ||
sapm_dc_snl_ac_system, location, weather, total_irrad): | ||
data = weather.copy() | ||
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad | ||
data['effective_irradiance'] = data['poa_global'] * 0.8 | ||
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss', | ||
spectral_model='no_loss') | ||
ac = mc.run_model_from_effective_irradiance(data).results.ac | ||
expected = pd.Series(np.array([118.302801, 76.099841]), | ||
index=data.index) | ||
assert_series_equal(ac, expected) | ||
|
||
|
||
@pytest.mark.parametrize("input_type", [tuple, list]) | ||
def test_run_model_from_effective_irradiance_arrays_error( | ||
sapm_dc_snl_ac_system_Array, location, weather, total_irrad, | ||
|
@@ -1745,3 +1770,26 @@ def test_modelchain__common_keys(): | |
assert {'b'} == modelchain._common_keys( | ||
(series, no_a) | ||
) | ||
|
||
|
||
def test__irrad_for_celltemp(): | ||
total_irrad = pd.DataFrame(index=[0, 1], columns=['poa_global'], | ||
data=[10., 20.]) | ||
empty = total_irrad.drop('poa_global', axis=1) | ||
effect_irrad = pd.Series(index=total_irrad.index, data=[5., 8.]) | ||
# test with single array inputs | ||
poa = modelchain._irrad_for_celltemp(total_irrad, effect_irrad) | ||
assert_series_equal(poa, total_irrad['poa_global']) | ||
poa = modelchain._irrad_for_celltemp(empty, effect_irrad) | ||
assert_series_equal(poa, effect_irrad) | ||
# test with tuples | ||
poa = modelchain._irrad_for_celltemp( | ||
(total_irrad, total_irrad), (effect_irrad, effect_irrad)) | ||
assert len(poa) == 2 | ||
assert_series_equal(poa[0], total_irrad['poa_global']) | ||
assert_series_equal(poa[1], total_irrad['poa_global']) | ||
poa = modelchain._irrad_for_celltemp( | ||
(empty, empty), (effect_irrad, effect_irrad)) | ||
assert len(poa) == 2 | ||
assert_series_equal(poa[0], effect_irrad) | ||
assert_series_equal(poa[1], effect_irrad) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.