Skip to content

Add pvsyst solar position algorithm #2169

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/sphinx/source/reference/solarposition.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ unless you know that you need a different function.
solarposition.ephemeris
solarposition.pyephem
solarposition.spa_c
solarposition.pvsyst


Additional functions for quantities closely related to solar position.
Expand Down
102 changes: 95 additions & 7 deletions pvlib/solarposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import warnings

from pvlib import atmosphere
from pvlib.tools import datetime_to_djd, djd_to_datetime
from pvlib.tools import datetime_to_djd, djd_to_datetime, sind, cosd


def get_solarposition(time, latitude, longitude,
Expand Down Expand Up @@ -87,7 +87,6 @@

.. [3] NREL SPA code: https://midcdmz.nrel.gov/spa/
"""

if altitude is None and pressure is None:
altitude = 0.
pressure = 101325.
Expand Down Expand Up @@ -188,7 +187,6 @@
--------
pyephem, spa_python, ephemeris
"""

# Added by Rob Andrews (@Calama-Consulting), Calama Consulting, 2014
# Edited by Will Holmgren (@wholmgren), University of Arizona, 2014
# Edited by Tony Lorenzo (@alorenzo175), University of Arizona, 2015
Expand Down Expand Up @@ -240,8 +238,7 @@


def _spa_python_import(how):
"""Compile spa.py appropriately"""

"""Compile spa.py appropriately."""
from pvlib import spa

# check to see if the spa module was compiled with numba
Expand Down Expand Up @@ -354,11 +351,10 @@
.. [3] USNO delta T:
https://maia.usno.navy.mil/products/deltaT

See also
See Also
--------
pyephem, spa_c, ephemeris
"""

# Added by Tony Lorenzo (@alorenzo175), University of Arizona, 2015

lat = latitude
Expand Down Expand Up @@ -393,6 +389,98 @@
return result


def pvsyst(times, latitude, longitude):
"""
Calculate solar position according to pvsyst algorithm.

See [1]_ for algorithm description.

Parameters
----------
times : pandas.DatetimeIndex
Must be localized or UTC will be assumed.
latitude : float
Latitude in decimal degrees. Positive north of equator, negative
to south.
longitude : float
Longitude in decimal degrees. Positive east of prime meridian,
negative to west.

Returns
-------
pandas.DataFrame
index is the same as input `times` argument
The DataFrame will have the following columns:
elevation, azimuth, zenith, equation_of_time,
declination, and hour_angle.

References
----------
.. [1] `pvsysts solar geometry
<https://web.archive.org/web/20240302011841/https://www.pvsyst.com/help/solar_geometry.htm>`_
"""
dayofyear = times.dayofyear
dayorigin = 79 + np.mod(times.year, 4) / 4
daysinyear = 365 + times.is_leap_year

Check warning on line 424 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L422-L424

Added lines #L422 - L424 were not covered by tests

ecliptic_angle = 23.433 # ecliptic angle

Check warning on line 426 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L426

Added line #L426 was not covered by tests

declination = np.rad2deg(np.arcsin(

Check warning on line 428 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L428

Added line #L428 was not covered by tests
sind(ecliptic_angle)
* np.sin(2*np.pi*(dayofyear - dayorigin) / daysinyear)
))

declination = declination + np.where(

Check warning on line 433 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L433

Added line #L433 was not covered by tests
dayofyear > 172,
1.5*np.sin(2*np.pi*(dayofyear - 173)/daysinyear),
0,
)

year_angle = 2 * np.pi * (dayofyear - 1) / 365.25

Check warning on line 439 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L439

Added line #L439 was not covered by tests

equation_of_time = (

Check warning on line 441 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L441

Added line #L441 was not covered by tests
+ 0.0072*np.cos(year_angle)
- 0.0528*np.cos(2*year_angle)
- 0.0012*np.cos(3*year_angle)
- 0.1229*np.sin(year_angle)
- 0.1565*np.sin(2*year_angle)
- 0.0041*np.sin(3*year_angle)
)

tz_offset = (

Check warning on line 450 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L450

Added line #L450 was not covered by tests
times.tz_localize(None)
- times.tz_convert('UTC').tz_localize(None)
).total_seconds()/60/60

DHLegHSol = tz_offset - longitude/15 - equation_of_time

Check warning on line 455 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L455

Added line #L455 was not covered by tests

legal_time = times.hour + times.minute/60 + times.second/60/60
standard_time = legal_time - DHLegHSol

Check warning on line 458 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L457-L458

Added lines #L457 - L458 were not covered by tests

hour_angle = 15*(standard_time - 12)

Check warning on line 460 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L460

Added line #L460 was not covered by tests

elevation = np.rad2deg(np.arcsin(

Check warning on line 462 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L462

Added line #L462 was not covered by tests
sind(latitude)*sind(declination)
+ cosd(latitude)*cosd(declination)*cosd(hour_angle)
))

azimuth = np.rad2deg(np.arcsin(

Check warning on line 467 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L467

Added line #L467 was not covered by tests
cosd(declination)*sind(hour_angle) / cosd(elevation))
) + 180

data = pd.DataFrame({

Check warning on line 471 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L471

Added line #L471 was not covered by tests
'elevation': elevation,
'azimuth': azimuth,
'zenith': 90 - elevation,
'equation_of_time': equation_of_time*60, # [minutes]
'declination': declination,
'hour_angle': hour_angle,
},
index=times,
)
return data

Check warning on line 481 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L481

Added line #L481 was not covered by tests


def sun_rise_set_transit_spa(times, latitude, longitude, how='numpy',
delta_t=67.0, numthreads=4):
"""
Expand Down
Loading