Skip to content

Commit 09c5bda

Browse files
committed
infer_spectral_model tweak -> return list if possible
1 parent feb9964 commit 09c5bda

File tree

2 files changed

+48
-21
lines changed

2 files changed

+48
-21
lines changed

pvlib/modelchain.py

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ class ModelChain:
341341
a user-defined function.
342342
343343
See :py:func:`~pvlib.modelchain.ModelChain.infer_spectral_model` to
344-
infer the spectral model from system and weather information.
344+
get a list of available spectral models for the current system.
345345
346346
temperature_model : str or function, optional
347347
Valid strings are: 'sapm', 'pvsyst', 'faiman', 'fuentes', 'noct_sam'.
@@ -878,8 +878,10 @@ def spectral_model(self, model):
878878

879879
def infer_spectral_model(self, weather=None):
880880
"""
881-
Infer spectral model from system attributes, and optionally from
882-
the input weather dataframe, and set it to the ``ModelChain`` instance.
881+
Return a spectral model key or a list of possible spectral models based
882+
on the system attributes and weather data. If the weather data is not
883+
provided, the spectral model will be inferred from the system
884+
attributes only.
883885
884886
Parameters
885887
----------
@@ -890,35 +892,56 @@ def infer_spectral_model(self, weather=None):
890892
891893
Returns
892894
-------
893-
Inferred spectral correction model : string key for model setter
895+
Inferred spectral correction model : string key or list of string keys
896+
If no spectral model is inferred, 'no_loss' is returned.
897+
If only one spectral model is inferred, a string key is returned.
898+
If multiple spectral models are inferred, a list of string keys is
899+
returned.
900+
901+
The following spectral models keys may be returned:
902+
903+
- ``'sapm'`` for
904+
:py:func:`~pvlib.spectrum.spectral_factor_sapm`
905+
- ``'first_solar'`` for
906+
:py:func:`~pvlib.spectrum.spectral_factor_first_solar`.
907+
Requires ``'precipitable_water'`` in the weather dataframe.
908+
909+
If no spectral model is inferred, 'no_loss' is returned.
894910
895911
Examples
896912
--------
897913
>>> mc = ModelChain(system, location)
898914
>>> mc.spectral_model = mc.infer_spectral_model(weather=weather)
899915
"""
916+
possible_models_result = [] # list of possible spectral models
900917
module_parameters = tuple(
901918
array.module_parameters for array in self.system.arrays
902919
)
903920
params = _common_keys(module_parameters)
921+
922+
# infer all the possible spectral models and append them to the list
904923
if {"A4", "A3", "A2", "A1", "A0"} <= params:
905-
return "sapm"
906-
elif "first_solar_spectral_coefficients" in params:
907-
# user explicitly sets spectral coefficients
908-
return "first_solar"
909-
elif (
910-
# cell type is known or can be inferred
911-
("Technology" in params or "Material" in params)
912-
and (self.system._infer_cell_type() is not None)
913-
):
914-
# This suggests models that provide default parameters per cell
915-
# type can be used. However, some models depend on other weather
916-
# parameters, so we need to check if they are available.
917-
if weather is not None: # weather is available
918-
if "precipitable_water" in weather:
919-
return "first_solar"
920-
921-
return "no_loss"
924+
possible_models_result.append("sapm")
925+
if weather is not None: # models that depend on weather data
926+
if "precipitable_water" in weather:
927+
if "first_solar_spectral_coefficients" in params:
928+
# user explicitly sets first solar spectral coefficients
929+
possible_models_result.append("first_solar")
930+
# cell type is known or can be inferred
931+
if ("Technology" in params or "Material" in params) and (
932+
self.system._infer_cell_type() is not None
933+
):
934+
possible_models_result.append("first_solar")
935+
936+
# result resolution based on the number of inferred spectral models
937+
if (result_len := len(possible_models_result)) == 0:
938+
# if no spectral model is inferred, return no_loss
939+
return "no_loss"
940+
elif result_len == 1:
941+
# when only one spectral model is inferred, return it
942+
return possible_models_result[0]
943+
else: # multiple spectral models are inferred (avoiding duplicates)
944+
return list(set(possible_models_result))
922945

923946
def first_solar_spectral_loss(self):
924947
self.results.spectral_modifier = self.system.first_solar_spectral_loss(

pvlib/tests/test_modelchain.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,9 @@ def test_infer_spectral_model_with_weather(location, sapm_dc_snl_ac_system,
12811281
# instantiate example ModelChain to get the default spectral model
12821282
# default should resolve to no loss
12831283
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='physical')
1284+
assert mc.spectral_model == mc.no_spectral_loss
1285+
# - inference should resolve to sapm
1286+
mc.spectral_model = mc.infer_spectral_model(weather=weather)
12841287
assert mc.spectral_model == mc.sapm_spectral_loss
12851288
# - next inference should resolve to no loss
12861289
mc = ModelChain(
@@ -1289,6 +1292,7 @@ def test_infer_spectral_model_with_weather(location, sapm_dc_snl_ac_system,
12891292
aoi_model="physical",
12901293
spectral_model=None,
12911294
)
1295+
mc.spectral_model = mc.infer_spectral_model(weather=weather)
12921296
assert mc.spectral_model == mc.no_spectral_loss
12931297

12941298
# infer spectral model from weather

0 commit comments

Comments
 (0)