Skip to content

Implement ANTS-2D bifacial irradiance model#2740

Open
kandersolar wants to merge 74 commits into
pvlib:mainfrom
kandersolar:ants2d
Open

Implement ANTS-2D bifacial irradiance model#2740
kandersolar wants to merge 74 commits into
pvlib:mainfrom
kandersolar:ants2d

Conversation

@kandersolar

@kandersolar kandersolar commented Apr 21, 2026

Copy link
Copy Markdown
Member
  • [ ] Closes #xxxx
  • 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.

@AdamRJensen, @cwhanse, and I have a paper describing a new bifacial irradiance model called ANTS-2D. It is similar to pvlib's infinite_sheds model, but extended to allow:

  • a discretized module surface (for, e.g., cell-level irradiance values)
  • a discretized ground surface, with variable albedo
  • sloped terrain, in the fashion of pvlib's existing functionality in that area
  • the Perez transposition model, in addition to Hay-Davies and isotropic
  • computation of ground-level irradiance (for agriPV)
  • fast computation by using analytical integrated view factors instead of burdensome numerical integrals

Details available open-access here: https://doi.org/10.1109/JPHOTOV.2026.3677506

This PR is rather large. To summarize:

  • Add g0 and g1 parameters to the view factor functions in pvlib.bifacial.utils. These are analogous to x0 and x1 in vf_row_sky_2d_integ and extend the functions to subset the ground surface.
  • Change vf_ground_sky_2d_integ to use Hottel's crossed-string rule instead of burdensome numerical integration. This makes the npoints and vectorize parameters unnecessary.
  • Change some signatures in pvlib.bifacial.utils to be cleaner with the new calculations.
  • Minor edits in pvlib.bifacial.infinite_sheds to accommodate the utils changes
  • Create pvlib.bifacial.ant2d, which houses the model itself and uses the new utils functionality.

Let me know if it would help reviewers to split it up and review separate PRs, starting with utils.

Comment thread pvlib/bifacial/ants2d.py
Comment thread pvlib/bifacial/ants2d.py Outdated
Comment thread pvlib/bifacial/ants2d.py Outdated
Comment thread pvlib/bifacial/ants2d.py Outdated
Comment thread pvlib/bifacial/utils.py Outdated

@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.

Review of file ants2d.py. OK with file infinite_sheds.py.

Comment thread pvlib/bifacial/ants2d.py
Comment thread pvlib/bifacial/ants2d.py Outdated
Comment thread pvlib/bifacial/ants2d.py
Comment thread pvlib/bifacial/ants2d.py
Comment thread pvlib/bifacial/utils.py
Comment thread pvlib/bifacial/utils.py Outdated
Comment thread pvlib/bifacial/utils.py
Comment thread pvlib/bifacial/utils.py Outdated
Comment thread pvlib/bifacial/utils.py Outdated
kandersolar and others added 4 commits June 17, 2026 13:40

@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.

Thorough tests!

Comment thread tests/bifacial/test_ants2d.py Outdated
@williamhobbs

Copy link
Copy Markdown
Contributor

I think the documentation for ants2d.get_irradiance needs clarification on how to model fixed-tilt systems. I.e., how to convert tilt angle and azimuth into tracker_rotation and axis_azimuth (and similar for axis_tilt and cross_axis_slope).

@williamhobbs

Copy link
Copy Markdown
Contributor

In pvlib.irradiance.get_total_irradiance, the input for model for Perez-Driesse is perez-driesse, but here it is perez_driesse. I suggest changing it to perez-driesse for consistency, unless there is a reason to do it differently here.

Comment thread pvlib/bifacial/ants2d.py
(``n_ground_segments``, ``len(tracker_rotation)``). [unitless]
model : str, default 'perez'
Irradiance model - can be one of 'isotropic', 'haydavies', 'perez',
or 'perez_driesse'.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
or 'perez_driesse'.
or 'perez-driesse'.

Comment thread pvlib/bifacial/ants2d.py
or 'perez_driesse'.
dni_extra : numeric, optional
Extraterrestrial direct normal irradiance. Required when
``model='haydavies'``, ``model='perez'``, or ``model='perez_driese'``.

@williamhobbs williamhobbs Jul 13, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
``model='haydavies'``, ``model='perez'``, or ``model='perez_driese'``.
``model='haydavies'``, ``model='perez'``, or ``model='perez-driesse'``.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

note that there was also a type here

Comment thread pvlib/bifacial/ants2d.py
Computed from the datetime index of the inputs if not provided. [Wm⁻²]
airmass : numeric, optional
Relative airmass. Required when ``model='perez'`` or
``model='perez_driese'``. Computed from ``solar_zenith`` if needed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
``model='perez_driese'``. Computed from ``solar_zenith`` if needed.
``model='perez-driesse'``. Computed from ``solar_zenith`` if needed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

note that there was also a type here

Comment thread pvlib/bifacial/ants2d.py

# Section II.C.2

if model in ['haydavies', 'perez', 'perez_driesse']:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if model in ['haydavies', 'perez', 'perez_driesse']:
if model in ['haydavies', 'perez', 'perez-driesse']:

Comment thread pvlib/bifacial/ants2d.py
diffuse_model_func = haydavies
extra_kwargs = {}

elif model in ['perez', 'perez_driesse']:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
elif model in ['perez', 'perez_driesse']:
elif model in ['perez', 'perez-driesse']:

Comment thread pvlib/bifacial/ants2d.py

Notes
-----
- ``model='perez_driesse'`` was not evaluated in [1]_ and is included

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
- ``model='perez_driesse'`` was not evaluated in [1]_ and is included
- ``model='perez-driesse'`` was not evaluated in [1]_ and is included

Comment thread pvlib/bifacial/ants2d.py

# preparation steps

if airmass is None and model in ['perez', 'perez_driesse']:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
if airmass is None and model in ['perez', 'perez_driesse']:
if airmass is None and model in ['perez', 'perez-driesse']:

Comment thread pvlib/bifacial/ants2d.py
airmass = get_relative_airmass(solar_zenith)

if (dni_extra is None and
model in ['perez', 'perez_driesse', 'haydavies'] and

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
model in ['perez', 'perez_driesse', 'haydavies'] and
model in ['perez', 'perez-driesse', 'haydavies'] and

@williamhobbs

Copy link
Copy Markdown
Contributor

Could it be worthwhile to return unshaded irradiance, e.g., poa_front_direct_unshaded? It would help with some types of shade loss models [1, 2] and with general loss accounting (e.g., if someone wanted to make waterfall/Sankey charts).

I haven't fully thought through trade-offs and alternatives yet, but I thought it was worth bringing up.

[1] Anderson, Kevin. 2020. Maximizing Yield with Improved Single-Axis Backtracking on Cross-Axis Slopes: Preprint. Golden, CO: National Renewable Energy Laboratory. NREL/CP-5K00-76023. https://www.nlr.gov/docs/fy20osti/76023.pdf

[2] W. Hobbs, K. Anderson, M. Mikofski, and M. Ghiz, “An approach to modeling linear and non-linear self-shading losses with PVlib,” in PV Perform. Model. Collaborative Workshop, Sandia Nat. Lab., May 2024. [Online]. Available: https://www.sandia.gov/app/uploads/sites/243/dlm_uploads/2024/05/2024_PVPMC_hobbs_pvlib_self-shade.pdf

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.

4 participants