@@ -330,10 +330,15 @@ class ModelChain:
330330 'interp' and 'no_loss'. The ModelChain instance will be passed as the
331331 first argument to a user-defined function.
332332
333- spectral_model : str, or function, optional
333+ spectral_model : str or function, optional
334334 If not specified, the model will be inferred from the parameters that
335- are common to all of system.arrays[i].module_parameters.
336- Valid strings are 'sapm', 'first_solar', 'no_loss'.
335+ are common to all of system.arrays[i].module_parameters. Valid strings
336+ are:
337+
338+ - ``'sapm'``
339+ - ``'first_solar'``
340+ - ``'no_loss'``
341+
337342 The ModelChain instance will be passed as the first argument to
338343 a user-defined function.
339344
@@ -855,9 +860,7 @@ def spectral_model(self):
855860
856861 @spectral_model .setter
857862 def spectral_model (self , model ):
858- if model is None :
859- self ._spectral_model = self .infer_spectral_model ()
860- elif isinstance (model , str ):
863+ if isinstance (model , str ):
861864 model = model .lower ()
862865 if model == 'first_solar' :
863866 self ._spectral_model = self .first_solar_spectral_loss
@@ -867,29 +870,60 @@ def spectral_model(self, model):
867870 self ._spectral_model = self .no_spectral_loss
868871 else :
869872 raise ValueError (model + ' is not a valid spectral loss model' )
873+ elif model is None :
874+ # None is a valid value, model inference will be done later
875+ self ._spectral_model = None
870876 else :
871877 self ._spectral_model = partial (model , self )
872878
873- def infer_spectral_model (self ):
879+ def infer_spectral_model (self ): # TODO: support other spectral models?
874880 """Infer spectral model from system attributes."""
875881 module_parameters = tuple (
876882 array .module_parameters for array in self .system .arrays )
877883 params = _common_keys (module_parameters )
878884 if {'A4' , 'A3' , 'A2' , 'A1' , 'A0' } <= params :
879885 return self .sapm_spectral_loss
880- elif ((('Technology' in params or
881- 'Material' in params ) and
882- (self .system ._infer_cell_type () is not None )) or
883- 'first_solar_spectral_coefficients' in params ):
886+ elif "first_solar_spectral_coefficients" in params :
887+ # user explicitly sets spectral coefficients
884888 return self .first_solar_spectral_loss
889+ elif (
890+ # cell type is known or can be inferred
891+ ("Technology" in params or "Material" in params )
892+ and (self .system ._infer_cell_type () is not None )
893+ ):
894+ # This suggests models that provide default parameters per cell
895+ # type can be used. However, some of them depend on other weather
896+ # parameters, so we need to check if they are available.
897+ # At this point, weather may not be available, so let's take that
898+ # into account.
899+ if self .results is not None and self .results .weather is not None :
900+ # weather is available
901+ if "precipitable_water" in self .results .weather :
902+ return self .first_solar_spectral_loss
903+ else :
904+ return self .no_spectral_loss
905+ else :
906+ # weather is not available, so it's unknown what model to use.
907+ # Warn user about this and default to no_spectral_loss.
908+ warnings .warn (
909+ "Weather data is not available to infer spectral model. "
910+ "Defaulting to 'no_loss'. Explicitly set the "
911+ "spectral model with the 'spectral_model' kwarg to "
912+ "suppress this warning."
913+ )
914+ return self .no_spectral_loss
885915 else :
886- raise ValueError ('could not infer spectral model from '
887- 'system.arrays[i].module_parameters. Check that '
888- 'the module_parameters for all Arrays in '
889- 'system.arrays contain valid '
890- 'first_solar_spectral_coefficients, a valid '
891- 'Material or Technology value, or set '
892- 'spectral_model="no_loss".' )
916+ # TODO: list valid models in message down below
917+ raise ValueError (
918+ "could not infer spectral model from "
919+ "system.arrays[i].module_parameters. Check that "
920+ "the module_parameters for all Arrays in "
921+ "system.arrays contain valid "
922+ "valid spectral coefficients, a valid "
923+ "Material or Technology value, or a valid model "
924+ "string; explicitly set the model with the "
925+ "'spectral_model' kwarg."
926+ )
893927
894928 def first_solar_spectral_loss (self ):
895929 self .results .spectral_modifier = self .system .first_solar_spectral_loss (
@@ -1678,7 +1712,13 @@ def run_model(self, weather):
16781712 weather = _to_tuple (weather )
16791713 self .prepare_inputs (weather )
16801714 self .aoi_model ()
1715+
1716+ # spectral model is optional, and weather may impact model inference
1717+ # check if spectral model inference is requested by means of "None"
1718+ if self ._spectral_model is None :
1719+ self ._spectral_model = self .infer_spectral_model ()
16811720 self .spectral_model ()
1721+
16821722 self .effective_irradiance_model ()
16831723
16841724 self ._run_from_effective_irrad (weather )
0 commit comments