From ffd685c4d78a094d08a6be356e6fde556064b486 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 23 May 2018 10:24:38 -0600 Subject: [PATCH 01/15] Add first_solar_spectral_correction method, improve infer_spectral_model --- pvlib/modelchain.py | 13 +++++-- pvlib/pvsystem.py | 85 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 8115939f1f..f847914205 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -532,7 +532,7 @@ def spectral_model(self, model): elif isinstance(model, str): model = model.lower() if model == 'first_solar': - raise NotImplementedError + self._spectral_model = self.first_solar_spectral_loss elif model == 'sapm': self._spectral_model = self.sapm_spectral_loss elif model == 'no_loss': @@ -546,13 +546,20 @@ def infer_spectral_model(self): params = set(self.system.module_parameters.keys()) if set(['A4', 'A3', 'A2', 'A1', 'A0']) <= params: return self.sapm_spectral_loss + elif ('Technology' in params or + 'Material' in params or + 'fs_spectral_coefficients' in params): + return self.first_solar_spectral_loss else: raise ValueError('could not infer spectral model from ' 'system.module_parameters') def first_solar_spectral_loss(self): - raise NotImplementedError - + self.spectral_modifier = self.system.first_solar_spectral_loss( + self.weather['precipitable_water'], + self.airmass['airmass_absolute']) + return self + def sapm_spectral_loss(self): self.spectral_modifier = self.system.sapm_spectral_loss( self.airmass['airmass_absolute']) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 465b117c66..c5fc097af1 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -421,6 +421,91 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, poa_direct, poa_diffuse, airmass_absolute, aoi, self.module_parameters, reference_irradiance=reference_irradiance) + def first_solar_spectral_correction(self, pw, airmass_absolute): + + """ + Use the :py:func:`first_solar_spectral_correction` function to + calculate the spectral loss modifier. + + Parameters + ---------- + pw : array-like + atmospheric precipitable water (cm). + + airmass_absolute : array-like + absolute (pressure corrected) airmass. + + Returns + ------- + modifier: array-like + spectral mismatch factor (unitless) which is can be multiplied + with broadband irradiance reaching a module's cells to estimate + effective irradiance, i.e., the irradiance that is converted to + electrical current. + """ + + if self.module_parameters['fs_spectral_coefficients']: + coefficients = self.module_parameters['fs_spectral_coefficients'] + module_type = None + else: + module_type = self._infer_cell_type() + coefficients = None + + if (module_type is not None and coefficients is None) or \ + (module_type is None and coefficients is not None): + return atmosphere.first_solar_spectral_correction(pw, + airmass_absolute, + module_type, + coefficients) + else: + # default to no_spectral_loss function + return 1 + + def _infer_cell_type(self): + + """ + Examines module_parameters and maps the Technology key for the CEC + database and the Material key for the Sandia database to a common + list of strings for cell type. + + Returns + ------- + cell_type: str + + """ + + _cell_type_dict = {'Multi-c-Si': 'multisi', + 'Mono-c-Si': 'monosi', + 'Thin Film': 'cigs', + 'a-Si/nc': 'asi', + 'CIS': 'cigs', + 'CIGS': 'cigs', + '1-a-Si': 'asi', + 'CdTe': 'cdte', + 'a-Si': 'asi', + '2-a-Si': None, + '3-a-Si': None, + 'HIT-Si': 'monosi', + 'mc-Si': 'multisi', + 'c-Si': 'multisi', + 'Si-Film': 'asi', + 'CdTe': 'cdte', + 'EFG mc-Si': 'multisi', + 'GaAs': None, + 'a-Si / mono-Si': 'monosi'} + + if 'Technology' in self.module_parameters.keys(): + # CEC module parameter set + cell_type = _cell_type_dict[self.module_parameters['Technology']] + elif 'Material' in self.module_parameters.keys(): + # Sandia module parameter set + cell_type = _cell_type_dict[self.module_parameters['Material']] + else: + cell_type = None + + return cell_type + + def singlediode(self, photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth, ivcurve_pnts=None): From 0881f01bc3bf64dad1d6dd1b7e40787752d68b17 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 25 May 2018 09:30:52 -0600 Subject: [PATCH 02/15] edits: input testing, first_solar_spectral_coefficients name --- pvlib/atmosphere.py | 6 ++++-- pvlib/modelchain.py | 12 ++++++++---- pvlib/pvsystem.py | 20 ++++++++------------ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/pvlib/atmosphere.py b/pvlib/atmosphere.py index 4878cc796d..5a63bfb5f2 100644 --- a/pvlib/atmosphere.py +++ b/pvlib/atmosphere.py @@ -320,7 +320,6 @@ def gueymard94_pw(temp_air, relative_humidity): return pw - def first_solar_spectral_correction(pw, airmass_absolute, module_type=None, coefficients=None): r""" @@ -466,8 +465,11 @@ def first_solar_spectral_correction(pw, airmass_absolute, module_type=None, coefficients = _coefficients[module_type.lower()] elif module_type is None and coefficients is not None: pass + elif module_type is None and coefficients is None: + raise TypeError('No valid input provided, both module_type and ' + + 'coefficients are None') else: - raise TypeError('ambiguous input, must supply only 1 of ' + + raise TypeError('Cannot resolve input, must supply only one of ' + 'module_type and coefficients') # Evaluate Spectral Shift diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index f847914205..4351318cb0 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -546,13 +546,17 @@ def infer_spectral_model(self): params = set(self.system.module_parameters.keys()) if set(['A4', 'A3', 'A2', 'A1', 'A0']) <= params: return self.sapm_spectral_loss - elif ('Technology' in params or - 'Material' in params or - 'fs_spectral_coefficients' in params): + elif ((('Technology' in params or + 'Material' in params) and + (pvsystem._infer_cell_type() is not None)) or + 'first_solar_spectral_coefficients' in params): return self.first_solar_spectral_loss else: raise ValueError('could not infer spectral model from ' - 'system.module_parameters') + 'system.module_parameters. Check that the ' + 'parameters contain valid ' + 'first_solar_spectral_coefficients or a valid ' + 'Material or Technology value') def first_solar_spectral_loss(self): self.spectral_modifier = self.system.first_solar_spectral_loss( diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index c5fc097af1..880745578b 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -438,28 +438,24 @@ def first_solar_spectral_correction(self, pw, airmass_absolute): Returns ------- modifier: array-like - spectral mismatch factor (unitless) which is can be multiplied + spectral mismatch factor (unitless) which can be multiplied with broadband irradiance reaching a module's cells to estimate effective irradiance, i.e., the irradiance that is converted to electrical current. """ - if self.module_parameters['fs_spectral_coefficients']: - coefficients = self.module_parameters['fs_spectral_coefficients'] + if self.module_parameters['first_solar_spectral_coefficients']: + coefficients = \ + self.module_parameters['first_solar_spectral_coefficients'] module_type = None else: module_type = self._infer_cell_type() coefficients = None - if (module_type is not None and coefficients is None) or \ - (module_type is None and coefficients is not None): - return atmosphere.first_solar_spectral_correction(pw, - airmass_absolute, - module_type, - coefficients) - else: - # default to no_spectral_loss function - return 1 + return atmosphere.first_solar_spectral_correction(pw, + airmass_absolute, + module_type, + coefficients) def _infer_cell_type(self): From 6d471885f9528123a78bcaecf846c5d4583266fe Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 25 May 2018 09:48:47 -0600 Subject: [PATCH 03/15] rename first_solar_spectral_correction method to first_solar_spectral_loss --- pvlib/pvsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 880745578b..3bb988069c 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -421,7 +421,7 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, poa_direct, poa_diffuse, airmass_absolute, aoi, self.module_parameters, reference_irradiance=reference_irradiance) - def first_solar_spectral_correction(self, pw, airmass_absolute): + def first_solar_spectral_loss(self, pw, airmass_absolute): """ Use the :py:func:`first_solar_spectral_correction` function to From 7a04b027c8933f7d865396323a1fa94c60fd2e86 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Fri, 25 May 2018 14:05:25 -0600 Subject: [PATCH 04/15] add test for pvsystem.first_solar_spectral_loss --- 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 42c696d392..fe08c75443 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -266,6 +266,20 @@ def test_PVSystem_sapm_spectral_loss(sapm_module_params): out = system.sapm_spectral_loss(airmass) +@pytest.mark.parametrize("module_type, expected", [ + ('cdte', np.array([1.01881074])) +]) + +def test_first_solar_spectral_loss(module_type, expected): + + pw = 3 + airmass_absolute = 3 + out = atmosphere.first_solar_spectral_correction(pw, airmass_absolute, + module_type, + coefficients=None) + assert_allclose(out, expected, atol=1e-4) + + @pytest.mark.parametrize('aoi,expected', [ (45, 0.9975036250000002), (np.array([[-30, 30, 100, np.nan]]), From 88fe65e54ea68568eb784645d2c24e1ff34bec0a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Sat, 26 May 2018 07:54:20 -0600 Subject: [PATCH 05/15] Fix declarations of weather and airmass in test_spectral_models --- pvlib/test/test_modelchain.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 9c13e4b2f8..f5ed43dba6 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -264,16 +264,20 @@ def constant_spectral_loss(mc): @requires_scipy @pytest.mark.parametrize('spectral_model, expected', [ - ('sapm', [182.338436597, -2.00000000e-02]), - pytest.mark.xfail(raises=NotImplementedError) - (('first_solar', [179.371460714, -2.00000000e-02])), - ('no_loss', [181.604438144, -2.00000000e-02]), - (constant_spectral_loss, [163.061569511, -2e-2]) + ('sapm', [1.0299227]), + ('first_solar', [1.0001506]), + ('no_loss', [1.0]), + (constant_spectral_loss, [0.9]) ]) def test_spectral_models(system, location, spectral_model, expected): + + times = pd.date_range('20160101 1200-0700', periods=1, freq='6H') + weather = pd.DataFrame(data=[0.3], index=times, columns=['precipitable_water']) + airmass = pd.DataFrame(data=[3.0], index=times, columns=['airmass_absolute']) mc = ModelChain(system, location, dc_model='sapm', - aoi_model='no_loss', spectral_model=spectral_model) - times = pd.date_range('20160101 1200-0700', periods=2, freq='6H') + aoi_model='no_loss', spectral_model=spectral_model, + weather=weather, + airmass=airmass) ac = mc.run_model(times).ac expected = pd.Series(np.array(expected), index=times) From 2da94e36116bf778cbdea02ece63f048f2a65491 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 28 May 2018 09:49:29 -0600 Subject: [PATCH 06/15] Correct errors and expected value in test_spectral_models --- pvlib/modelchain.py | 4 ++-- pvlib/test/test_modelchain.py | 18 ++++++++---------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 4351318cb0..8a30063bbf 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -518,8 +518,8 @@ def sapm_aoi_loss(self): return self def no_aoi_loss(self): - self.aoi_modifier = 1 - return self + self.aoi_modifier = 1.0 + return self @property def spectral_model(self): diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index f5ed43dba6..9cb81d1505 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -264,24 +264,22 @@ def constant_spectral_loss(mc): @requires_scipy @pytest.mark.parametrize('spectral_model, expected', [ - ('sapm', [1.0299227]), + ('sapm', [1.003971]), ('first_solar', [1.0001506]), - ('no_loss', [1.0]), - (constant_spectral_loss, [0.9]) + ('no_loss', [1.0]) ]) def test_spectral_models(system, location, spectral_model, expected): - times = pd.date_range('20160101 1200-0700', periods=1, freq='6H') weather = pd.DataFrame(data=[0.3], index=times, columns=['precipitable_water']) - airmass = pd.DataFrame(data=[3.0], index=times, columns=['airmass_absolute']) mc = ModelChain(system, location, dc_model='sapm', aoi_model='no_loss', spectral_model=spectral_model, - weather=weather, - airmass=airmass) - ac = mc.run_model(times).ac - + weather=weather) + spectral_modifier = mc.run_model(times).spectral_modifier + if not isinstance(spectral_modifier, pd.Series): + spectral_modifier = pd.Series(np.array(spectral_modifier), index=times) + print(spectral_modifier) expected = pd.Series(np.array(expected), index=times) - assert_series_equal(ac, expected, check_less_precise=2) + assert_series_equal(spectral_modifier, expected, check_less_precise=2) def constant_losses(mc): From 210e8134d812d768270417b961c99621d7f72f3d Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 28 May 2018 09:58:10 -0600 Subject: [PATCH 07/15] Fix space error --- pvlib/modelchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 8a30063bbf..6a77f3a303 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -519,7 +519,7 @@ def sapm_aoi_loss(self): def no_aoi_loss(self): self.aoi_modifier = 1.0 - return self + return self @property def spectral_model(self): From dec5d953d0937e29fb41bc50f2176518bc5be2e8 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Mon, 28 May 2018 10:15:16 -0600 Subject: [PATCH 08/15] Fix handling of weather in test_spectral_models --- pvlib/test/test_modelchain.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 9cb81d1505..77b9fe3627 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -272,9 +272,8 @@ def test_spectral_models(system, location, spectral_model, expected): times = pd.date_range('20160101 1200-0700', periods=1, freq='6H') weather = pd.DataFrame(data=[0.3], index=times, columns=['precipitable_water']) mc = ModelChain(system, location, dc_model='sapm', - aoi_model='no_loss', spectral_model=spectral_model, - weather=weather) - spectral_modifier = mc.run_model(times).spectral_modifier + aoi_model='no_loss', spectral_model=spectral_model) + spectral_modifier = mc.run_model(times=times, weather=weather).spectral_modifier if not isinstance(spectral_modifier, pd.Series): spectral_modifier = pd.Series(np.array(spectral_modifier), index=times) print(spectral_modifier) From 0732b4c34462b6fde6590e82bc991fe4deb90abe Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 29 May 2018 12:37:30 -0600 Subject: [PATCH 09/15] Change test_PVSystem_first_solar_spectral_loss and test_spectral_models --- pvlib/test/test_modelchain.py | 18 ++++++------------ pvlib/test/test_pvsystem.py | 13 +++++-------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 77b9fe3627..c1868f606b 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -263,22 +263,16 @@ def constant_spectral_loss(mc): mc.spectral_modifier = 0.9 @requires_scipy -@pytest.mark.parametrize('spectral_model, expected', [ - ('sapm', [1.003971]), - ('first_solar', [1.0001506]), - ('no_loss', [1.0]) -]) -def test_spectral_models(system, location, spectral_model, expected): - times = pd.date_range('20160101 1200-0700', periods=1, freq='6H') - weather = pd.DataFrame(data=[0.3], index=times, columns=['precipitable_water']) +@pytest.mark.parametrize('spectral_model', ['sapm', 'first_solar', 'no_loss']) + +def test_spectral_models(system, location, spectral_model): + times = pd.date_range('20160101 1200-0700', periods=3, freq='6H') + weather = pd.DataFrame(data=[0.3, 0.5, 1.0], index=times, columns=['precipitable_water']) mc = ModelChain(system, location, dc_model='sapm', aoi_model='no_loss', spectral_model=spectral_model) spectral_modifier = mc.run_model(times=times, weather=weather).spectral_modifier - if not isinstance(spectral_modifier, pd.Series): - spectral_modifier = pd.Series(np.array(spectral_modifier), index=times) print(spectral_modifier) - expected = pd.Series(np.array(expected), index=times) - assert_series_equal(spectral_modifier, expected, check_less_precise=2) + assert isinstance(spectral_modifier, (pd.Series, float, int)) def constant_losses(mc): diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index fe08c75443..806afc5249 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -266,17 +266,14 @@ def test_PVSystem_sapm_spectral_loss(sapm_module_params): out = system.sapm_spectral_loss(airmass) -@pytest.mark.parametrize("module_type, expected", [ - ('cdte', np.array([1.01881074])) -]) +@pytest.mark.parametrize("expected", [1.03173953]) -def test_first_solar_spectral_loss(module_type, expected): - +def test_PVSystem_first_solar_spectral_loss(sapm_module_params, expected): + system = pvsystem.PVSystem(module_parameters=sapm_module_params) pw = 3 airmass_absolute = 3 - out = atmosphere.first_solar_spectral_correction(pw, airmass_absolute, - module_type, - coefficients=None) + out = system.first_solar_spectral_loss(pw, airmass_absolute) + assert_allclose(out, expected, atol=1e-4) From ce78670d54fc310143e74e4e325cd8c6d71cc5be Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 29 May 2018 15:30:02 -0600 Subject: [PATCH 10/15] Fix error in first_solar_spectral_loss --- pvlib/pvsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 3bb988069c..f009436c20 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -444,7 +444,7 @@ def first_solar_spectral_loss(self, pw, airmass_absolute): electrical current. """ - if self.module_parameters['first_solar_spectral_coefficients']: + if 'first_solar_spectral_coefficients' in self.module_parameters.keys(): coefficients = \ self.module_parameters['first_solar_spectral_coefficients'] module_type = None From e8de2e09c3e00a549ce51d45cd1a6f81d3a6f7f7 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 29 May 2018 18:20:39 -0600 Subject: [PATCH 11/15] Formatting and flake8 --- pvlib/atmosphere.py | 1 + pvlib/modelchain.py | 6 +++--- pvlib/pvsystem.py | 32 ++++++++++++++++---------------- pvlib/test/test_modelchain.py | 11 +++++++---- pvlib/test/test_pvsystem.py | 7 +++---- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/pvlib/atmosphere.py b/pvlib/atmosphere.py index 5a63bfb5f2..ee57135ff9 100644 --- a/pvlib/atmosphere.py +++ b/pvlib/atmosphere.py @@ -320,6 +320,7 @@ def gueymard94_pw(temp_air, relative_humidity): return pw + def first_solar_spectral_correction(pw, airmass_absolute, module_type=None, coefficients=None): r""" diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 6a77f3a303..ab343b4b36 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -546,9 +546,9 @@ def infer_spectral_model(self): params = set(self.system.module_parameters.keys()) if set(['A4', 'A3', 'A2', 'A1', 'A0']) <= params: return self.sapm_spectral_loss - elif ((('Technology' in params or + elif ((('Technology' in params or 'Material' in params) and - (pvsystem._infer_cell_type() is not None)) or + (pvsystem._infer_cell_type() is not None)) or 'first_solar_spectral_coefficients' in params): return self.first_solar_spectral_loss else: @@ -563,7 +563,7 @@ def first_solar_spectral_loss(self): self.weather['precipitable_water'], self.airmass['airmass_absolute']) return self - + def sapm_spectral_loss(self): self.spectral_modifier = self.system.sapm_spectral_loss( self.airmass['airmass_absolute']) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index f009436c20..c7c8e3c95c 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -422,16 +422,16 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, self.module_parameters, reference_irradiance=reference_irradiance) def first_solar_spectral_loss(self, pw, airmass_absolute): - + """ - Use the :py:func:`first_solar_spectral_correction` function to + Use the :py:func:`first_solar_spectral_correction` function to calculate the spectral loss modifier. Parameters ---------- pw : array-like atmospheric precipitable water (cm). - + airmass_absolute : array-like absolute (pressure corrected) airmass. @@ -442,9 +442,10 @@ def first_solar_spectral_loss(self, pw, airmass_absolute): with broadband irradiance reaching a module's cells to estimate effective irradiance, i.e., the irradiance that is converted to electrical current. - """ + """ - if 'first_solar_spectral_coefficients' in self.module_parameters.keys(): + if 'first_solar_spectral_coefficients' in + self.module_parameters.keys(): coefficients = \ self.module_parameters['first_solar_spectral_coefficients'] module_type = None @@ -452,22 +453,22 @@ def first_solar_spectral_loss(self, pw, airmass_absolute): module_type = self._infer_cell_type() coefficients = None - return atmosphere.first_solar_spectral_correction(pw, - airmass_absolute, - module_type, - coefficients) - + return atmosphere.first_solar_spectral_correction(pw, + airmass_absolute, + module_type, + coefficients) + def _infer_cell_type(self): - + """ Examines module_parameters and maps the Technology key for the CEC database and the Material key for the Sandia database to a common list of strings for cell type. - + Returns ------- cell_type: str - + """ _cell_type_dict = {'Multi-c-Si': 'multisi', @@ -498,10 +499,9 @@ def _infer_cell_type(self): cell_type = _cell_type_dict[self.module_parameters['Material']] else: cell_type = None - + return cell_type - - + def singlediode(self, photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth, ivcurve_pnts=None): diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index c1868f606b..3443b48cb8 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -262,16 +262,18 @@ def test_aoi_models(system, location, aoi_model, expected): def constant_spectral_loss(mc): mc.spectral_modifier = 0.9 + @requires_scipy @pytest.mark.parametrize('spectral_model', ['sapm', 'first_solar', 'no_loss']) - def test_spectral_models(system, location, spectral_model): times = pd.date_range('20160101 1200-0700', periods=3, freq='6H') - weather = pd.DataFrame(data=[0.3, 0.5, 1.0], index=times, columns=['precipitable_water']) + weather = pd.DataFrame(data=[0.3, 0.5, 1.0], + index=times, + columns=['precipitable_water']) mc = ModelChain(system, location, dc_model='sapm', aoi_model='no_loss', spectral_model=spectral_model) - spectral_modifier = mc.run_model(times=times, weather=weather).spectral_modifier - print(spectral_modifier) + spectral_modifier = mc.run_model(times=times, + weather=weather).spectral_modifier assert isinstance(spectral_modifier, (pd.Series, float, int)) @@ -279,6 +281,7 @@ def constant_losses(mc): mc.losses = 0.9 mc.ac *= mc.losses + @requires_scipy @pytest.mark.parametrize('losses_model, expected', [ ('pvwatts', [163.280464174, 0]), diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 806afc5249..7ea9fde7fe 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -267,16 +267,15 @@ def test_PVSystem_sapm_spectral_loss(sapm_module_params): @pytest.mark.parametrize("expected", [1.03173953]) - def test_PVSystem_first_solar_spectral_loss(sapm_module_params, expected): system = pvsystem.PVSystem(module_parameters=sapm_module_params) pw = 3 airmass_absolute = 3 out = system.first_solar_spectral_loss(pw, airmass_absolute) - + assert_allclose(out, expected, atol=1e-4) - - + + @pytest.mark.parametrize('aoi,expected', [ (45, 0.9975036250000002), (np.array([[-30, 30, 100, np.nan]]), From bd7e119ba2708447e63f225e3c6727f7b4c19316 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 29 May 2018 18:25:24 -0600 Subject: [PATCH 12/15] Add constant_spectral_loss back to test_spectral_models arguments --- pvlib/test/test_modelchain.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pvlib/test/test_modelchain.py b/pvlib/test/test_modelchain.py index 3443b48cb8..600b803abd 100644 --- a/pvlib/test/test_modelchain.py +++ b/pvlib/test/test_modelchain.py @@ -264,7 +264,9 @@ def constant_spectral_loss(mc): @requires_scipy -@pytest.mark.parametrize('spectral_model', ['sapm', 'first_solar', 'no_loss']) +@pytest.mark.parametrize('spectral_model', [ + 'sapm', 'first_solar', 'no_loss', constant_spectral_loss +]) def test_spectral_models(system, location, spectral_model): times = pd.date_range('20160101 1200-0700', periods=3, freq='6H') weather = pd.DataFrame(data=[0.3, 0.5, 1.0], From 19de627a27c4aa7b3f91a2f6335b0774fbd548f3 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Tue, 29 May 2018 18:51:01 -0600 Subject: [PATCH 13/15] Fix line continuation in pvsystem.py --- pvlib/pvsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index c7c8e3c95c..2452021d42 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -444,7 +444,7 @@ def first_solar_spectral_loss(self, pw, airmass_absolute): electrical current. """ - if 'first_solar_spectral_coefficients' in + if 'first_solar_spectral_coefficients' in \ self.module_parameters.keys(): coefficients = \ self.module_parameters['first_solar_spectral_coefficients'] From 67a4561e0f83ecf86155784c63bb98b87a3d140a Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 30 May 2018 07:50:55 -0600 Subject: [PATCH 14/15] whatsnew, docstring for pvsystem.first_solar_spectral_loss, remove not implemented in modelchain.py docstring --- docs/sphinx/source/whatsnew/v0.6.0.rst | 4 ++++ pvlib/modelchain.py | 4 ++-- pvlib/pvsystem.py | 9 ++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.6.0.rst b/docs/sphinx/source/whatsnew/v0.6.0.rst index 0eed2c2a2d..625f3f119f 100644 --- a/docs/sphinx/source/whatsnew/v0.6.0.rst +++ b/docs/sphinx/source/whatsnew/v0.6.0.rst @@ -11,6 +11,8 @@ API Changes Enhancements ~~~~~~~~~~~~ * Add sea surface albedo in irradiance.py (:issue:`458`) +* Implement first_solar_spectral_loss in modelchain.py (:issue:'359') + Bug fixes ~~~~~~~~~ @@ -29,3 +31,5 @@ Contributors ~~~~~~~~~~~~ * Will Holmgren * Yu Cao +* Cliff Hansen + diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index ab343b4b36..29f4cfa674 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -273,8 +273,8 @@ class ModelChain(object): spectral_model: None, str, or function, default None If None, the model will be inferred from the contents of system.module_parameters. Valid strings are 'sapm', - 'first_solar' (not implemented), 'no_loss'. The ModelChain - instance will be passed as the first argument to a user-defined + 'first_solar', 'no_loss'. The ModelChain instance will be passed + as the first argument to a user-defined function. temp_model: str or function, default 'sapm' diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 2452021d42..7af269c0ed 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -425,7 +425,14 @@ def first_solar_spectral_loss(self, pw, airmass_absolute): """ Use the :py:func:`first_solar_spectral_correction` function to - calculate the spectral loss modifier. + calculate the spectral loss modifier. The model coefficients are + specific to the module's cell type, and are determined by searching + for one of the following keys in self.module_parameters (in order): + 'first_solar_spectral_coefficients (user-supplied coefficients) + 'Technology' - a string describing the cell type, can be read from + the CEC module parameter database + 'Material' - a string describing the cell type, can be read from + the Sandia module database. Parameters ---------- From e4724ae3c7a7c6f1849d15483024cc0c81408941 Mon Sep 17 00:00:00 2001 From: Cliff Hansen Date: Wed, 30 May 2018 12:00:12 -0600 Subject: [PATCH 15/15] Fix typo in pvsystem.py --- pvlib/pvsystem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 7af269c0ed..bcf1474448 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -428,7 +428,7 @@ def first_solar_spectral_loss(self, pw, airmass_absolute): calculate the spectral loss modifier. The model coefficients are specific to the module's cell type, and are determined by searching for one of the following keys in self.module_parameters (in order): - 'first_solar_spectral_coefficients (user-supplied coefficients) + 'first_solar_spectral_coefficients' (user-supplied coefficients) 'Technology' - a string describing the cell type, can be read from the CEC module parameter database 'Material' - a string describing the cell type, can be read from