Skip to content

Expose first_solar_spectral_correction #466

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 15 commits into from
May 31, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
13 changes: 10 additions & 3 deletions pvlib/modelchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand All @@ -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'])
Expand Down
85 changes: 85 additions & 0 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Member

Choose a reason for hiding this comment

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

Doc string should have some kind of reference to self.module_parameters. Most of the other PVSystem method doc strings have some examples. This method is a little more complicated though, so might be worth being more explicit about what is looked for. For example...

Use the :py:func:first_solar_spectral_correction function to calculate the spectral loss modifier. The spectral loss modifier is determined by searching for one of the following keys in self.module_parameters (in order): 'first_solar_spectral_coefficients', 'Technology', 'Material'.

Copy link
Member Author

Choose a reason for hiding this comment

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

    Use the :py:func:`first_solar_spectral_correction` function to
    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
----------
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
Copy link
Member

Choose a reason for hiding this comment

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

typo (is can) that is carried over from the original function's doc string.

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()
Copy link
Member

Choose a reason for hiding this comment

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

module vs. cell is unfortunate here. No objection to what you've done, but we might need a separate discussion to try to clean up the terminology within the library.

Copy link
Member Author

Choose a reason for hiding this comment

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

Here I'm inclined to break the API and change the keyword argument from module_type to cell_type. module_type is only used by the first_solar function.

coefficients = None

if (module_type is not None and coefficients is None) or \
Copy link
Member

Choose a reason for hiding this comment

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

Recommend removing this if/else statement. If this method doesn't have the information necessary to successfully compute the spectral correction, then this method should say so rather than defaulting to 1.

Consistent with Python's style, the atmosphere.first_solar_spectral_correction function throws a TypeError with some hint as to what the user needs to supply. We could (ideally/optionally) catch that error here and reraise it with a more specific message for how to modify module_parameters to make this work.

I am guessing that raising an exception here will cause issues in a ModelChain calculation for the few module/cell types for which _infer_cell_type mapping returns None. One approach to avoid that may be to make the ModelChain.infer_spectral_model preemptively call pvsystem._infer_cell_type.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good idea. I'll move the test to ModelChain.infer_spectral_model and put a warning in place of that check in the actual functions.

(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):
Expand Down