Skip to content

Add support for diffuse irradiance components and component-specific IAM to ModelChain #2846

Open

Description

@cbcrespo

I am currently at the final stage of my GSoC (#2750). After expanding support for diffuse components in transposition functions and adding support for diffuse component-specific IAM in the Array and PVSystem classes, the remaining step is to integrate this functionality into ModelChain.

Here are some points I think are worth discussing:

Default behavior

The existing behavior applied fd to all diffuse irradiance. fd was essentially always 1, meaning no optical losses were accounted for for diffuse irradiance.

What should the new default behavior be?

marion_diffuse is a good candidate for a default diffuse IAM model, since it is the only model that supports all three diffuse components: sky diffuse, horizon, and ground. (martin_ruiz_diffuse and schlick_diffuse support sky ground only).

As for marion_diffuse's model parameter (i.e. the direct IAM model used by the Marion integration method), I'm considering doing something like the existing infer_aoi_model which selects a model based on the available parameters when the user does not explicitly choose one.

FD

Do we want to keep FD around as an option?

Handling missing IAM for existing components

Let's say the user chooses the perez transposition model, and therefore diffuse irradiance includes the horizon component, and then chooses the martin_ruiz_diffuse IAM model, which does not provide an IAM for the horizon component. How should this be handled?

Options:

  • Throw an error prompting the user to select a compatible combination of transposition and IAM models
  • Apply the sky diffuse IAM to the horizon component
  • Apply no optical losses to the horizon component
  • Something else?

Activity

  1. cwhanse commented on Aug 19, 2026

    @cwhanse
    Member

    My thoughts: keep fd as a module parameter, and add a new kwarg to ModelChain say aoi_diffuse_model default None. If aoi_diffuse_model is present, then execute a new step aoi_diffuse_model in ModelChain.run_model methods, which populates ModelChainResult.aoi_diffuse_modifier. Then modelchain.effective_irradiance_model uses aoi_diffuse_modifer inside this little function:

    def _eff_irrad(module_parameters, total_irrad, spect_mod, aoi_mod):

    I'd default aoi_diffuse_model = None to keep behavior consistent with prior releases.

    If a user provides aoi_diffuse_model, the burden of choosing a matching transposition model should be on them. We could add some kind of logic that warns about incompatible combinations. To my thinking this is better than trying to make choices for a user.

    Where the aoi_diffuse_model doesn't cover a component (e.g., horizon) we could supplement the output of the diffuse AOI model with 1s for the missing components so that all three components are covered in the output. That keeps the downstream code simpler.

    Final thought: this integration is a bit complicated so I'm OK if it we decide it's out of scope.

  2. cbcrespo commented on Aug 19, 2026

    @cbcrespo
    ContributorAuthor

    Personally, I don't agree with setting aoi_diffuse_model to None by default. I'd say most users likely won't personalise every parameter, and if the functionality exists, I think the default behaviour should be to make use of it to get more accurate results.

    If a user provides aoi_diffuse_model, the burden of choosing a matching transposition model should be on them. We could add some kind of logic that warns about incompatible combinations. To my thinking this is better than trying to make choices for a user.

    To be clear, are you advocating for raising an error, a warning, or something else?

    Final thought: this integration is a bit complicated so I'm OK if it we decide it's out of scope.

    I would like to try :) even if code review and adjustments spill past my GSoC project timeline, I am willing to dedicate some more time to those adjustments. I already got started on the implementation, and I think I can have a PR ready by Friday (GSoC deadline is Monday), even if it isn't perfect yet.

  3. cwhanse commented on Aug 19, 2026

    @cwhanse
    Member

    To be clear, are you advocating for raising an error, a warning, or something else?

    A warning, if we also choose to populate the missing IAMs with 1. Which is kind of the default anyway, if a diffuse AOI model doesn't address a component such as horizon irradiance, it shouldn't modify that component.

  4. ramaroesilva commented on Aug 19, 2026

    @ramaroesilva
    Contributor

    thanks for the input @cwhanse. follow-up from me:

    keep fd as a module parameter

    Just curious, what's the point of having a parameter which is always 1 (at least within pvlib's built-in module database)? Are there module descriptors outside pvlib's database - likely not public - that have non-1 values?

    I'd default aoi_diffuse_model = None to keep behavior consistent with prior releases.

    While I understand the idea, wouldn't it be better to have a more robust configuration as default? Having marion+spam for cases with a more well-described PV system and marion+martin_ruiz for PVWatts-like cases seems to me as a solid default. This could be even better if haydavies was replaced with perez - proposed by @AdamRJensen in #1861 - since it includes horizon.

    Where the aoi_diffuse_model doesn't cover a component (e.g., horizon) we could supplement the output of the diffuse AOI model with 1s for the missing components so that all three components are covered in the output. That keeps the downstream code simpler.

    Seems reasonable to me. Better to ignore some diffuse losses than all of them, and if marion becomes the default, it will cover all components.

    Final thought: this integration is a bit complicated so I'm OK if it we decide it's out of scope.

    Being @cbcrespo, the odds are good for us :-)

  5. cwhanse commented on Aug 19, 2026

    @cwhanse
    Member

    Just curious, what's the point of having a parameter which is always 1 (at least within pvlib's built-in module database)? Are there module descriptors outside pvlib's database - likely not public - that have non-1 values?

    None that I know of. That equation derives from the SAPM, which (for a time) was being used for CPV, so perhaps that's where the fd parameter originates.

    I object to defaulting to a particular diffuse AOI model for two reasons: we'd have to choose a default model, and out of respect for current users.

    I don't think choosing a default AOI / diffuse AOI model is pvlib's job. We make users explicitly select an aoi_model model (or None for no model) when a ModelChain is instantiated. That's better than silently choosing for them. Some would argue that pvlib shouldn't have chosen default clear-sky or transposition models, either. As @AdamRJensen points out, a default model is unlikely to be the best choice for most applications. We've attempted to avoid positioning pvlib as a referee of "best" models although some judgment is inevitable (otherwise pvlib would have dozens if not hundreds of diffuse irradiance models of minor interest, for example).

    If we set aoi_diffuse_model to one of the specific models, then existing code will silently run and the results will be slightly different than any past archived results. That's not good. If we require a user to choose an aoi_diffuse_model, existing code won't run, so this is a breaking change requiring deprecation.

    For these reasons, I prefer defaulting to aoi_diffuse_model = None.

  6. ramaroesilva commented on Aug 20, 2026

    @ramaroesilva
    Contributor

    Thank you for further explaining your point of view, I know it takes time and effort to frame and explain one's own reasoning to others.

    All good for me!

  7. echedey-ls commented on Aug 20, 2026

    @echedey-ls
    Member

    Just a heads up: the new parameter should probably be called iam_diffuse_model rather than aoi_diffuse_model, as per #2748. I agree with everything else in this conversation. Thank you @cbcrespo for continuing development after GSoC deadline!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions