You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This issue is part of my GSoC 2026 project. The first goal was to add support for return_components to all diffuse transposition models in pvlib, which was previously supported by some models (e.g. perez) but not others. This parameter allows the user to choose whether they want only the total diffuse irradiance, or are interested in the split between different diffuse components (sky diffuse, circumsolar, horizon brightening).
This has now been completed for all diffuse transposition models in pvlib except for klucher, which does not inherently support different components. This means that we can now implement component-wise IAM calculations in ModelChain - which is the second part of my GSoC project. In practice, this means treating circumsolar as direct, and calculating a separate IAM for direct and each of the available diffuse components out of sky isotropic, horizon, and ground.
The end goal is to enable ModelChain to calculate effective irradiance using component-wise incidence angle modifiers (IAM), rather than applying a single IAM to direct irradiance and assuming no diffuse optical losses (current behavior).
The first step is to make sure that the ModelChain workflow also supports diffuse components, which is currently not the case. After that, component-wise IAM can be introduced. Both of these steps require changes to the three classes involved in the ModelChain framework: Array, PVSystem, and ModelChain itself. The ModelChain workflow relies on the PVSystem class, which in turn relies on the Array class.
The goal of this overarching issue is to discuss the overall plan. Other issues will be created for more in-depth discussions of more specific points.
Currently, the plan is as follows:
Array class:
Array.get_irradiance(): add a boolean diffuse_components argument (which gets passed to irradiance.get_total_irradiance())
PVSystem.get_irradiance(): add a boolean diffuse_components argument (which gets passed to Array.get_irradiance())
PVSystem.get_iam(): currently only returns IAM for direct irradiance. Support for diffuse component-specific IAM must be added. How this should be done depends on the decision about the architecture of Array.get_iam.
ModelChain class:
diffuse_components parameter should be added on init
passing diffuse_components=True and transposition_model='klucher' should throw an error
aoi_model is a parameter defined on init.
currently all direct IAM models are supported except for schlick. Is there a reason for this?
ModelChain.prepare_inputs():
calls PVSystem.get_irradiance(). Would need a small change to support diffuse_components.
ModelChain.aoi_model is a property. It has a setter that attributes different functions to it via user choice (defined on init via the aoi_model parameter) or via infer_aoi_model() if necessary. One possible approach is to rename the current aoi_model to aoi_model_direct and create a new aoi_model_diffuse, but the implementation details of this should probably be discussed in a separate issue.
Direct: all existing direct IAM models are supported except for schlick. Is there a reason for this?
Diffuse: currently not implemented in ModelChain. martin_ruiz_diffuse, marion_diffuse, and schlick_diffuse should all be allowed. One caveat is that marion_diffuse is an integration method which uses one of the direct IAM models from ‘ashrae’, ‘physical’, ‘martin_ruiz’, ‘sapm’, and ‘schlick’. This probably requires adding an additional optional aoi_model_marion parameter, which is only used if aoi_model_diffuse is marion.
Note: as noted by @markcampanelli on GSoC 2026: Standardizing Diffuse Irradiance Components and Extending ModelChain Optical Loss Modeling #2750, marion_diffuse has a high memory use on trackers, due to integration over a solid angle for a timeseries of surface_tilt as opposed to a single value. Over a long timeseries, this can generate redundant calculations (if the same angle is repeated twice). One possible solution is to first do an interpolation of the marion_diffuse integral results for each diffuse components and then use that to get the IAM for the timeseries of tilt angles, as suggested by @markcampanelli. I think this deserves a more in-depth discussion in a separate issue.
but while being mindful of which components are indeed present.
Finally, as you can see, this plan requires extensive changes, but all those changes are interdependent. I am aware it is generally not good to create huge PRs with many changes, but in this case, I was wondering how you all would advise me to split the work.
This issue is part of my GSoC 2026 project. The first goal was to add support for
return_componentsto all diffuse transposition models inpvlib, which was previously supported by some models (e.g. perez) but not others. This parameter allows the user to choose whether they want only the total diffuse irradiance, or are interested in the split between different diffuse components (sky diffuse, circumsolar, horizon brightening).This has now been completed for all diffuse transposition models in
pvlibexcept forklucher, which does not inherently support different components. This means that we can now implement component-wise IAM calculations inModelChain- which is the second part of my GSoC project. In practice, this means treating circumsolar as direct, and calculating a separate IAM for direct and each of the available diffuse components out of sky isotropic, horizon, and ground.The end goal is to enable
ModelChainto calculate effective irradiance using component-wise incidence angle modifiers (IAM), rather than applying a single IAM to direct irradiance and assuming no diffuse optical losses (current behavior).The first step is to make sure that the
ModelChainworkflow also supports diffuse components, which is currently not the case. After that, component-wise IAM can be introduced. Both of these steps require changes to the three classes involved in theModelChainframework:Array,PVSystem, andModelChainitself. TheModelChainworkflow relies on thePVSystemclass, which in turn relies on theArrayclass.The goal of this overarching issue is to discuss the overall plan. Other issues will be created for more in-depth discussions of more specific points.
Currently, the plan is as follows:
Arrayclass:Array.get_irradiance(): add a booleandiffuse_componentsargument (which gets passed toirradiance.get_total_irradiance())Array.get_iam(): currently only returns IAM for direct irradiance. Support for diffuse component-specific IAM must be added. Issue Support diffuse component-specific IAM inArray.get_iam()#2812 discusses how to implement this.PVSystemclass:PVSystem.get_irradiance(): add a booleandiffuse_componentsargument (which gets passed toArray.get_irradiance())PVSystem.get_iam(): currently only returns IAM for direct irradiance. Support for diffuse component-specific IAM must be added. How this should be done depends on the decision about the architecture ofArray.get_iam.ModelChainclass:diffuse_componentsparameter should be added oninitdiffuse_components=Trueandtransposition_model='klucher'should throw an erroraoi_modelis a parameter defined oninit.schlick. Is there a reason for this?ModelChain.prepare_inputs():PVSystem.get_irradiance(). Would need a small change to supportdiffuse_components.ModelChain.aoi_modelis a property. It has a setter that attributes different functions to it via user choice (defined on init via theaoi_modelparameter) or viainfer_aoi_model()if necessary. One possible approach is to rename the currentaoi_modeltoaoi_model_directand create a newaoi_model_diffuse, but the implementation details of this should probably be discussed in a separate issue.ModelChain.martin_ruiz_diffuse,marion_diffuse, andschlick_diffuseshould all be allowed. One caveat is thatmarion_diffuseis an integration method which uses one of the direct IAM models from ‘ashrae’, ‘physical’, ‘martin_ruiz’, ‘sapm’, and ‘schlick’. This probably requires adding an additional optionalaoi_model_marionparameter, which is only used ifaoi_model_diffuseismarion.marion_diffusehas a high memory use on trackers, due to integration over a solid angle for a timeseries ofsurface_tiltas opposed to a single value. Over a long timeseries, this can generate redundant calculations (if the same angle is repeated twice). One possible solution is to first do an interpolation of themarion_diffuseintegral results for each diffuse components and then use that to get the IAM for the timeseries of tilt angles, as suggested by @markcampanelli. I think this deserves a more in-depth discussion in a separate issue.ModelChain.effective_irradiance_model()spect_mod * (total_irrad['poa_direct'] * aoi_mod + fd * total_irrad['poa_diffuse'])spect_mod * ((total_irrad['poa_direct'] + total_irrad['poa_circumsolar']) * aoi_mod + iam_isotropic * total_irrad['poa_isotropic'] + iam_horizon * total_irrad['poa_horizon'] + iam_ground * total_irrad['poa_ground'])Finally, as you can see, this plan requires extensive changes, but all those changes are interdependent. I am aware it is generally not good to create huge PRs with many changes, but in this case, I was wondering how you all would advise me to split the work.