Skip to content

Add marion_diffuse_tracking, to be used instead of marion_diffuse for vector surface_tilt inputs - #2824

Open
cbcrespo wants to merge 2 commits into
pvlib:mainfrom
cbcrespo:marion-efficient
Open

Add marion_diffuse_tracking, to be used instead of marion_diffuse for vector surface_tilt inputs#2824
cbcrespo wants to merge 2 commits into
pvlib:mainfrom
cbcrespo:marion-efficient

Conversation

@cbcrespo

@cbcrespo cbcrespo commented Jul 15, 2026

Copy link
Copy Markdown
Contributor
  • Closes pvlib.iam.marion_integrate uses too much memory for vector inputs #1402
  • I am familiar with the contributing guidelines
  • I attest that all AI-generated material has been vetted for accuracy and is in compliance with the pvlib license
  • Tests added
  • Updates entries in docs/sphinx/source/reference for API changes.
  • Adds description and name entries in the appropriate "what's new" file in docs/sphinx/source/whatsnew for all changes. Includes link to the GitHub Issue with :issue:`num` or this Pull Request with :pull:`num`. Includes contributor name and/or GitHub username (link with :ghuser:`user`).
  • New code is fully documented. Includes numpydoc compliant docstrings, examples, and comments where necessary.
  • Pull request is nearly complete and ready for detailed review.
  • Maintainer: Appropriate GitHub Labels (including remote-data) and Milestone are assigned to the Pull Request and linked Issue.

As noted in #1402, marion_diffuse uses too much memory (and takes a long time) when the surface_tilt input is a vector, which occurs for trackers. marion_diffuse calls marion_integrate for each surface_tilt and each region (sky, horizon or ground). marion_integrate integrates over a solid angle for each of those. @cwhanse, @markcampanelli, @adriesse, @kdebrab and others discussed how this issue could be circumvented via interpolation.

This PR proposes a solution via a new function called marion_diffuse_tracking, to be used instead of marion_diffuse for trackers.

marion_diffuse_tracking creates vectors of 0-90 or 0-180º (for bifacial arrays) with a 0.5º step, marion_integrate is called for each angle in that vector and each region only once, and the results are used to create an interpolator function (using scipy.interpolate.PchipInterpolator). This interpolator is then used to obtain an IAM value for each value in surface_tilt.

Regardless of the length of surface_tilt, marion_integrate is called a fixed number of times, helping to contain the computational burden.

In addition, the interpolation is cached, meaning that e.g. for a PVSystem object with multiple tracker Arrays which have identical tilt vectors, the interpolator will only have to be created once for each region.

More details on time and memory savings, as well as deviation, here. For one example for a 8760-length vector, marion_diffuse_tracking uses 4% of the max memory and 2% of the time of marion_diffuse.

@ramaroesilva

ramaroesilva commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

kudos to @kandersolar for raising the issue and everyone else who contributed to the conceptualization of this.

was now thinking that neither me and @cbcrespo have never modelled bifacial, and we have no idea how IAM is calculated for that (not even sure if marion_diffuse is applicable). would be important that someone who's experienced in this to take a look.

marion_diffuse_tracking creates vectors of 0-90 or 0-180º with a 0.5º step

likely it's not as simple as this....

Comment thread pvlib/iam.py Outdated
@RDaxini

RDaxini commented Aug 5, 2026

Copy link
Copy Markdown
Member

was now thinking that neither me and @cbcrespo have never modelled bifacial, and we have no idea how IAM is calculated for that (not even sure if marion_diffuse is applicable). would be important that someone who's experienced in this to take a look.

marion_diffuse_tracking creates vectors of 0-90 or 0-180º with a 0.5º step

likely it's not as simple as this....

I wonder if @shirubana is available to comment on this as bifacial PV is one of her many areas of expertise

@kandersolar

Copy link
Copy Markdown
Member

In the style of Marion's method, bifacial IAM can be pretty straightforward (assuming you already have the front-side IAMs in hand). By symmetry:

  • iam_rear_ground = iam_front_sky
  • iam_rear_sky = iam_front_ground
  • iam_rear_horizon = iam_front_horizon

Flaws of this approach include: slight inconsistency in the horizon IAM, as the horizon band is placed slightly above the horizon (If it was centered on the horizon, it would be perfect, alas), the assumption of no neighboring rows or other objects blocking the view (of course this is also true for the front side in Marion's method, so not an "additional" deficiency here), and the assumption of isotropic ground-reflected irradiance (ignoring the row's own shadow).

However I'm not sure we need to be thinking about bifacial modeling here. This is just a way to compute many Marion IAMs quickly. How best to model bifacial IAM is a separate problem which doesn't need to be addressed here IMHO.

@ramaroesilva

Copy link
Copy Markdown
Contributor

Thanks for the input @kandersolar. I think Carolina was worried how this would behave if a user sets up a bifacial system using ModelChain or simply wants to use this function directly as an efficient interpolation gimmick for bifacial. To be honest, I'm not even aware how bifacial is treated within pvlib, and I should check it as it makes it more difficult to imagine/reflect on the matter (from your feedback, it seems each side of the module is handled separately).

Besides this bifaciality, anything else to add? Do you see any issue with interpolating the IAM curve with 0.5 degree resolution (as an arbitrary, high-resolution/theoretically safe delta)?

@cwhanse cwhanse left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Editorial suggestions for the docstring.

Comment thread pvlib/iam.py
Comment on lines +650 to +653
Determine diffuse irradiance incidence angle modifiers using Marion's
method of integrating over solid angle. This function is designed for
trackers, where ``surface_tilt`` is a vector, to avoid the computational
burden of calling ``marion_integrate`` for each tilt angle.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Determine diffuse irradiance incidence angle modifiers using Marion's
method of integrating over solid angle. This function is designed for
trackers, where ``surface_tilt`` is a vector, to avoid the computational
burden of calling ``marion_integrate`` for each tilt angle.
Determine incidence angle modifiers (IAMs) for diffuse irradiance
using Marion's method of integrating over solid angles.
This function supports trackers for which ``surface_tilt`` is a vector.

Comment thread pvlib/iam.py
Comment on lines +654 to +655
Instead, the IAM function is integrated once for a range of tilt angles,
and then interpolated to the requested tilt angles. For fixed-tilt systems,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Instead, the IAM function is integrated once for a range of tilt angles,
and then interpolated to the requested tilt angles. For fixed-tilt systems,
The IAM function is integrated once for tilt angles specified by
``resolution``. IAM at other angles are determined by interpolation.
For fixed-tilt systems,

Comment thread pvlib/iam.py
(89.5 <= zenith <= 90)
* 'ground': radiation reflected from the ground (zenith >= 90)

See [1]_ for a detailed description of each class.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
See [1]_ for a detailed description of each class.
See [1]_ for a detailed description of each type.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pvlib.iam.marion_integrate uses too much memory for vector inputs

6 participants