pvlib / pvlib-python

A set of documented functions for simulating the performance of photovoltaic energy systems.
https://pvlib-python.readthedocs.io
BSD 3-Clause "New" or "Revised" License
1.16k stars 976 forks source link

Consider allowing `return_components` in all transposition model functions #1553

Open kandersolar opened 1 year ago

kandersolar commented 1 year ago

Access to sky diffuse irradiance in component form (typically, isotropic + circumsolar + horizon) is needed for diffuse IAM and shading/view factor loss modeling. pvlib.irradiance.perez has the ability to return separate in-plane sky diffuse components in addition to the combined quantity sky_diffuse, but currently perez is the only transposition function with that ability. Adding this return_components option to more models was briefly discussed in #434 but it was decided to limit it to perez at that time. I'd like to pick up that discussion in the context of pvlib's current and future expanded capabilities regarding diffuse IAM and shade modeling.

As I understand them, the isotropic, haydavies, klucher, and reindl models can all be considered as fitting naturally into the same isotropic/circumsolar/horizon organization as perez (though the simpler models might have one or two of those components equal to zero). I'm not sure about the king model. So, with the possible exception of king, it seems that there is no conceptual barrier to extending the other models with a return_components option to return the same components as perez does.

In addition to making the function level of pvlib more useful on its own, a uniform interface for diffuse components across all (or most) transposition functions would enable improvements to the higher-level constructs ModelChain and infinite_sheds:

One concern about extending return_components to all models in #434 was the API (returning a DataFrame, e.g.). I think I'm okay with what is currently used in perez, but bring it up for discussion here.

spaneja commented 1 year ago

Hey @kanderso-nrel - thanks for setting this up. I think this is a great idea, and it is something I have somewhat already done in my code. I am happy to work on some of these implementations so that they have a return_components option.

As I understand this, regardless of the model type, all returns will have separate components for horizon, circumsolar, and isotropic (and models without certain components will return 0 where not applicable).

Going forward, and barring objection, I will work on the haydavies model to start.

kandersolar commented 1 year ago

regardless of the model type, all returns will have separate components for horizon, circumsolar, and isotropic (and models without certain components will return 0 where not applicable).

Yes, this is what I am imagining. By having all models return the same structure (with all components, even if a particular model doesn't produce all of them), higher-level functionality like ModelChain can use their return values equally without having to have special cases for the simpler models.

Once return_components is supported by all the transposition functions we could also add it to the get_sky_diffuse wrapper function.

mikofski commented 1 year ago

This has suddenly become a very important issue to me. The diffuse sky on the backside is very sensitive to the amount of circumsolar. For example during the middle of the day, circumsolar could be coming from nearly overhead, and would be unlikely to reach the rear of the modules, but in the current formulation if returned as diffuse then the backside irradiance is overpredicted!

kurt-rhee commented 1 year ago

Hey all,

I was playing with the perez function with return_components = True and noticed that is possible for the some of the components to give negative values. For example:

poa = perez(
    surface_tilt=tracking_angles['surface_tilt'],
    surface_azimuth=tracking_angles['surface_azimuth'],
    dhi=meteo['DHI'],
    dni=meteo['DNI'],
    dni_extra=dni_extra,
    solar_zenith=solar_position['apparent_zenith'],
    solar_azimuth=solar_position['azimuth'],
    airmass=airmass,
    model='allsitescomposite1990',
    return_components=True   
)

poa['horizon'].min()
-10.895910944561619

A negative horizon band does not make physical sense to me, but then again the model from my understand is meant to be empirical, so setting the minimum horizon band to zero may throw off the empirical fits. I was wondering if any of you had thoughts on what the proper way to treat these components may be.

kandersolar commented 1 year ago

I think that's intentional, at least for the horizon component: #1238

AdamRJensen commented 1 year ago

This is indeed intentional. Since the 1987 version of the Perez model a negative F2 coefficient has been possible:

Although negative coefficients are physically meaningless (since by definition this would mean negative energy received from a region in the dome), the use of negative F2' coefficients is equivalent, as far as flat plate surfaces are concerned, to adding a third brighter region at the top of the sky hemisphere. This new setup yields noticeable performance improvements particularly for climates where cloudy conditions prevail.

and

It will be noted that all performance gains from negative coefficients results from better handling of overcast conditions.

and from the 1990 Perez model:

The negative value for F2 is traceable to a relative brightening of the zenithal region for overcast conditions the physical nature of which is well understood[ 36 ]

kurt-rhee commented 1 year ago

Very informative, thank you all!

markcampanelli commented 6 months ago

Would the powers that be be willing to make the component name’s consistent across all model functions? For example, perez returns sky_diffuse, while poa_components returns poa_sky_diffuse. It also seems that poa_circumsolar, etc. would be more consistent and descriptive for those diffuse components. (A counter argument might be than the underlying plane need not necessarily be for a solar PV array.)

In addition, would it be possible/premature to add the return_components option to get_total_irradiance?