8000 Create iam.py, consistent naming for IAM functions by cwhanse · Pull Request #783 · pvlib/pvlib-python · GitHub
[go: up one dir, main page]

Skip to content

Create iam.py, consistent naming for IAM functions #783

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 35 commits into from
Oct 18, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
73f4638
move iam functions to iam.py
cwhanse Oct 2, 2019
a89cc01
move function tests to test_iam.py
cwhanse Oct 2, 2019
0f1c90c
adjust PVSystem methods, add deprecation for functions and PVSystem m…
cwhanse Oct 2, 2019
0a55b54
adjust PVSystem tests, test for function deprecation
cwhanse Oct 2, 2019
8505a0d
move sapm aoi function, adjust ModelChain methods
cwhanse Oct 2, 2019
f48742c
remove _ typo
cwhanse Oct 2, 2019
1bcde31
lint fixes
cwhanse Oct 2, 2019
c51bc08
move fixture to correct place
cwhanse Oct 2, 2019
5e01712
move sapm_module_params fixture to conftest.py
cwhanse Oct 2, 2019
e5bebe1
fix cut/paste errors
cwhanse Oct 3, 2019
8000 549fc2c
add missing keys to fixture, add missing text to pvsystem.sapm docstring
cwhanse Oct 3, 2019
42210e4
fix and update pvsystem.sapm tests
cwhanse Oct 3, 2019
1475db6
remove DataFrame test for sapm, lint fixes
cwhanse Oct 3, 2019
006768d
implement PVSystem.get_iam, add deprecation test for pvsystem.sapm_ao…
cwhanse Oct 3, 2019
c10af4c
lint
cwhanse Oct 3, 2019
b9e39ac
Merge branch 'master' into iam
cwhanse Oct 3, 2019
ab1fbbc
test fixes, add Material to sapm_module_params fixture
cwhanse Oct 3, 2019
4dadd4b
test fixes
cwhanse Oct 4, 2019
7f14c95
add martin_ruiz to modelchain
cwhanse Oct 4, 2019
9164592
finish adding martin_ruiz to modelchain
cwhanse Oct 4, 2019
2fe7396
add test for ModelChain.infer_aoi_model, improve coverage
cwhanse Oct 4, 2019
3d6bcf5
repair delete mistake
cwhanse Oct 4, 2019
64a6b3c
test for invalid aoi model parameters in ModelChain
cwhanse Oct 4, 2019
152ef7a
update api, whatsnew
cwhanse Oct 4, 2019
46db9d7
test fix
cwhanse Oct 4, 2019
ea6fe81
fixture for aoi_model tests
cwhanse Oct 4, 2019
2529351
bad indent
cwhanse Oct 4, 2019
f641cd0
docstring and lint
cwhanse Oct 4, 2019
f331949
lint
cwhanse Oct 4, 2019
ec723c6
module docstring, changes to tests
cwhanse Oct 15, 2019
cf998ea
test fixes
cwhanse Oct 15, 2019
23f2677
another test fix
cwhanse Oct 16, 2019
1e63f04
improve coverage
cwhanse Oct 16, 2019
5fb7cc8
fix exception test
cwhanse Oct 16, 2019
b43b28c
fix the fix
cwhanse Oct 16, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
implement PVSystem.get_iam, add deprecation test for pvsystem.sapm_ao…
…i_loss
  • Loading branch information
cwhanse committed Oct 3, 2019
commit 006768d4e1c91a9f50f95a9d4c2404878636e70f
13 changes: 12 additions & 1 deletion pvlib/iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@
from pvlib.tools import cosd, sind, tand, asind


# a dict of required parameter names for each IAM model
# keys are the function names for the IAM models
IAM_MODEL_PARAMS = {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure what to think of this emerging pattern in pvlib. It will be a lot to maintain in the long run as it expands to more and more classes of models. While we make use of it internally (for now), I think the added benefit for users is not very big. At this moment, I don't want to put the effort into implementing a solution with something like inspect, but I wonder if we should rethink making these attributes public instead of private.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm also uneasy about continued use of constants following this pattern. I think it's valuable to have lists of model parameter names somewhere, perhaps not in this form. A data model encompassing a system's physical and model parameters would help here.

Copy link
Member

Choose a reason for hiding this comment

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

Created #789 to track this. I'm ok leaving it alone for this PR.

'ashrae': set(['b']),
'physical': set(['n', 'K', 'L']),
'martin_ruiz': set(['a_r']),
'sapm': set(['B0', 'B1', 'B2', 'B3', 'B4', 'B5']),
'interp': set([])
}


def ashrae(aoi, b=0.05):
r"""
Determine the incidence angle modifier using the ASHRAE transmission
8000 Expand Down Expand Up @@ -347,7 +358,7 @@ def sapm(aoi, module, upper=None):
zeros.

module : dict-like
A dict, Series, or DataFrame defining the SAPM performance
A dict or Series with the SAPM IAM model parameters.
parameters. See the :py:func:`sapm` notes section for more
details.

Expand Down
6 changes: 3 additions & 3 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -590,15 +590,15 @@ def infer_aoi_model(self):
'aoi_model="no_loss".')

def ashrae_aoi_loss(self):
self.aoi_modifier = self.system.iam_ashrae(self.aoi)
self.aoi_modifier = self.system.get_iam(self.aoi, iam_model='ashrae')
return self

def physical_aoi_loss(self):
self.aoi_modifier = self.system.iam_physical(self.aoi)
self.aoi_modifier = self.system.get_iam(self.aoi, iam_model='physical')
return self

def sapm_aoi_loss(self):
self.aoi_modifier = self.system.iam_sapm(self.aoi)
self.aoi_modifier = self.system.get_iam(self.aoi, iam_model='sapm')
return self

def no_aoi_loss(self):
Expand Down
98 changes: 39 additions & 59 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,72 +316,65 @@ def get_irradiance(self, solar_zenith, solar_azimuth, dni, ghi, dhi,
albedo=self.albedo,
**kwargs)

def iam_ashrae(self, aoi):
def get_iam(self, aoi, iam_model='physical'):
"""
Determine the incidence angle modifier using
``self.module_parameters['b']``, ``aoi``,
and the :py:func:`iam.ashrae` function.
Determine the incidence angle modifier using the method specified by
``iam_model``.

Uses default arguments if keys not in module_parameters.
Parameters for the selected IAM model are expected to be in
``PVSystem.module_parameters``.
Copy link
Member

Choose a reason for hiding this comment

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

should add something about defaults like "... otherwise default values will be used."


Parameters
----------
aoi : numeric
The angle of incidence in degrees.

aoi_model : string, default 'physical'
The IAM model to be used. Valid strings are 'physical', 'ashrae',
'martin_ruiz' and 'sapm'.

Returns
-------
modifier : numeric
iam : numeric
The AOI modifier.
"""
kwargs = _build_kwargs(['b'], self.module_parameters)

return iam.ashrae(aoi, **kwargs)
Raises
------
ValueError if `iam_model` is not a valid model name.
"""
model = iam_model.lower()
if model in ['ashrae', 'physical', 'martin_ruiz']:
param_names = iam.IAM_MODEL_PARAMS[model]
kwargs = _build_kwargs(param_names, self.module_parameters)
func = iam.__getattribute__(model)
Copy link
Member

Choose a reason for hiding this comment

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

It's usually preferable to call the getattr function instead of a __getattribute__ method.

return func(aoi, **kwargs)
elif model=='sapm':
return iam.sapm(aoi, self.module_parameters)
elif model=='interp':
raise ValueError(model + ' is not implemented as an IAM model'
'option for PVSystem')
else:
raise ValueError(model + ' is not a valid IAM model')

def ashraeiam(self, aoi):
"""
Deprecated. Use ``PVSystem.iam_ashrae`` instead.
Deprecated. Use ``PVSystem.iam`` instead.
"""
import warnings
warnings.warn('PVSystem.ashraeiam is deprecated and will be removed in'
'v0.8, use PVSystem.iam_ashrae instead',
'v0.8, use PVSystem.get_iam instead',
pvlibDeprecationWarning)
return PVSystem.iam_ashrae(self, aoi)

def iam_physical(self, aoi):
"""
Determine the incidence angle modifier using ``aoi``,
``self.module_parameters['K']``,
``self.module_parameters['L']``,
``self.module_parameters['n']``,
and the
:py:func:`iam.physical` function.

Uses default arguments if keys not in module_parameters.

Parameters
----------
aoi : numeric
The angle of incidence in degrees.

Returns
-------
modifier : numeric
The AOI modifier.
"""
kwargs = _build_kwargs(['K', 'L', 'n'], self.module_parameters)

return iam.physical(aoi, **kwargs)
return PVSystem.get_iam(self, aoi, iam_model='ashrae')

def physicaliam(self, aoi):
"""
Deprecated. Use ``PVSystem.iam_physical`` instead.
Copy link
Member

Choose a reason for hiding this comment

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

this and similar docstring below need to be updated to point to PVSystem.get_iam

"""
import warnings
warnings.warn('PVSystem.physicaliam is deprecated and will be removed'
' in v0.8, use PVSystem.iam_physical instead',
' in v0.8, use PVSystem.get_iam instead',
pvlibDeprecationWarning)
return PVSystem.iam_physical(self, aoi)
return PVSystem.get_iam(self, aoi, iam_model='physical')

def calcparams_desoto(self, effective_irradiance, temp_cell, **kwargs):
"""
Expand Down Expand Up @@ -550,30 +543,13 @@ def sapm_spectral_loss(self, airmass_absolute):

def sapm_aoi_loss(self, aoi):
"""
Deprecated. Use ``PVSystem.iam_sapm`` instead.
Deprecated. Use ``PVSystem.iam`` instead.
"""
import warnings
warnings.warn('PVSystem.sapm_aoi_loss is deprecated and will be'
' removed in v0.8, use PVSystem.iam_sapm instead',
' removed in v0.8, use PVSystem.get_iam instead',
pvlibDeprecationWarning)
return PVSystem.iam_sapm(self, aoi)

def iam_sapm(self, aoi):
"""
Use the :py:func:`iam.sapm` function, the input parameters,
and ``self.module_parameters`` to calculate iam.

Parameters
----------
aoi : numeric
Angle of incidence in degrees.

Returns
-------
iam : numeric
The SAPM angle of incidence loss coefficient F2.
"""
return iam.sapm(aoi, self.module_parameters)
return PVSystem.get_iam(self, aoi, iam_model='sapm')

def sapm_effective_irradiance(self, poa_direct, poa_diffuse,
airmass_absolute, aoi,
Expand Down Expand Up @@ -2779,3 +2755,7 @@ def pvwatts_ac(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637):

physicaliam = deprecated('0.7', alternative='iam.physical', name='physicaliam',
removal='0.8')(iam.physical)


sapm_aoi_loss = deprecated('0.7', alternative='iam.sapm', name='sapm_aoi_loss',
removal='0.8')(iam.sapm)
13 changes: 6 additions & 7 deletions pvlib/test/test_modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@


@pytest.fixture
def system(sam_data, cec_inverter_parameters, sapm_temperature_cs5p_220m):
modules = sam_data['sandiamod']
def system(sapm_module_params, cec_inverter_parameters, sapm_temperature_cs5p_220m):
module = 'Canadian_Solar_CS5P_220M___2009_'
module_parameters = modules[module].copy()
module_parameters = sapm_module_params.copy()
temp_model_params = sapm_temperature_cs5p_220m.copy()
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
module=module,
Expand Down Expand Up @@ -384,13 +383,13 @@ def constant_aoi_loss(mc):
mc.aoi_modifier = 0.9


@pytest.mark.parametrize('aoi_model, method', [
('sapm', 'iam_sapm'), ('ashrae', 'iam_ashrae'),
('physical', 'iam_physical')])
@pytest.mark.parametrize('aoi_model', [
('sapm', 'ashrae', 'physical', 'martin_ruiz')
])
def test_aoi_models(system, location, aoi_model, method, weather, mocker):
mc = ModelChain(system, location, dc_model='sapm',
aoi_model=aoi_model, spectral_model='no_loss')
m = mocker.spy(system, method)
m = mocker.spy(system, 'get_iam')
mc.run_model(weather.index, weather=weather)
assert m.call_count == 1
assert isinstance(mc.ac, pd.Series)
Expand Down
43 changes: 20 additions & 23 deletions pvlib/test/test_pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,24 +79,28 @@ def test_systemdef_dict():
assert expected == pvsystem.systemdef(meta, 5, 0, .1, 5, 5)


def test_PVSystem_iam_ashrae(mocker):
mocker.spy(_iam, 'ashrae')
module_parameters = pd.Series({'b': 0.05})
@pytest.mark.parametrize('iam_model','model_params', [
('ashrae', {'b': 0.05}),
('physical', {'K': 4, 'L': 0.002, 'n': 1.526}),
('martin_ruiz', {'a_r': 0.16}),
])
def test_PVSystem_get_iam(mocker, iam_model, model_params):
mocker.spy(_iam, iam_model)
module_parameters = pd.Series(model_params)
Copy link
Member

Choose a reason for hiding this comment

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

I suggest leaving this as a dict instead of converting to a Series.

system = pvsystem.PVSystem(module_parameters=module_parameters)
thetas = 1
iam = system.iam_ashrae(thetas)
_iam.ashrae.assert_called_once_with(thetas, b=0.05)
iam = system.get_iam(thetas, iam_model=iam_model)
_iam.ashrae.assert_called_once_with(thetas, **module_parameters)
assert iam < 1.


def test_PVSystem_iam_physical(mocker):
module_parameters = pd.Series({'K': 4, 'L': 0.002, 'n': 1.526})
system = pvsystem.PVSystem(module_parameters=module_parameters)
mocker.spy(_iam, 'physical')
thetas = 1
iam = system.iam_physical(thetas)
_iam.physical.assert_called_once_with(thetas, **module_parameters)
assert iam < 1.
def test_PVSystem_get_iam_sapm(sapm_module_params, mocker):
system = pvsystem.PVSystem(module_parameters=sapm_module_params)
mocker.spy(_iam, 'sapm')
aoi = 0
out = system.get_iam(aoi, 'sapm')
_iam.sapm.assert_called_once_with(aoi, sapm_module_params)
assert_allclose(out, 1.0, atol=0.01)


def test_retrieve_sam_raise_no_parameters():
Expand Down Expand Up @@ -279,15 +283,6 @@ def test_PVSystem_first_solar_spectral_loss(module_parameters, module_type,
assert_allclose(out, 1, atol=0.5)


def test_PVSystem_iam_sapm(sapm_module_params, mocker):
system = pvsystem.PVSystem(module_parameters=sapm_module_params)
mocker.spy(_iam, 'sapm')
aoi = 0
out = system.iam_sapm(aoi)
_iam.sapm.assert_called_once_with(aoi, sapm_module_params)
assert_allclose(out, 1.0, atol=0.01)


@pytest.mark.parametrize('test_input,expected', [
([1000, 100, 5, 45, 1000], 1.1400510967821877),
([np.array([np.nan, 1000, 1000]),
Expand Down Expand Up @@ -1453,7 +1448,7 @@ def test_PVSystem_pvwatts_ac_kwargs(mocker):


@fail_on_pvlib_version('0.8')
def test_deprecated_08():
def test_deprecated_08(sapm_module_params):
with pytest.warns(pvlibDeprecationWarning):
pvsystem.sapm_celltemp(1000, 25, 1)
with pytest.warns(pvlibDeprecationWarning):
Expand All @@ -1467,6 +1462,8 @@ def test_deprecated_08():
pvsystem.ashraeiam(45)
with pytest.warns(pvlibDeprecationWarning):
pvsystem.physicaliam(45)
with pytest.warns(pvlibDeprecationWarning):
pvsystem.sapm_aoi_loss(45, sapm_module_params)


@fail_on_pvlib_version('0.8')
Expand Down
0