Skip to content
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

[WIP] Implement the Plataforma Solar de Almerı́a (PSA) solar position algorithm #2152

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
113 changes: 113 additions & 0 deletions pvlib/solarposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,119 @@
return DFOut


_PSA_PARAMS = {
2020: [
2.267127827, -9.300339267e-4, 4.895036035, 1.720279602e-2,
6.239468336, 1.720200135e-2, 3.338320972e-2, 3.497596876e-4,
-1.544353226e-4, -8.689729360e-6, 4.090904909e-1, -6.213605399e-9,
4.418094944e-5, 6.697096103, 6.570984737e-2,
],
2001: [
2.1429, -0.0010394594, 4.8950630, 0.017202791698, 6.2400600,
0.0172019699, 0.03341607, 0.00034894, -0.0001134, -0.0000203,
0.4090928, -6.2140e-09, 0.0000396, 6.6974243242, 0.0657098283,
]
}

def psa(times, latitude, longitude, coefficients=2020):

Check failure on line 886 in pvlib/solarposition.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E302 expected 2 blank lines, found 1
"""
Calculate solar position using the algorithm developed at the
Plataforma Solar de Almería (PSA).

This algorithm can use two sets of coefficients: TODO
2001 - tuned to the range XX, with accuracy YY
2020 - tuned to the range XX, with accuracy YY

Parameters
----------
times : TYPE
DESCRIPTION.
latitude : TYPE
DESCRIPTION.
longitude : TYPE
DESCRIPTION.
coefficients : TYPE, optional
DESCRIPTION. The default is 2020.

Raises
------
ValueError
DESCRIPTION.

Returns
-------
TYPE
DESCRIPTION.

References
----------
.. [1] Blanco, M., Alarcón, D., López, T., Lara, M. "Computing the Solar
Vector" Solar Energy Vol. 70, No. 5, 2001.
:doi:`10.1016/S0038-092X(00)00156-0`
.. [2] Blanco, M., Milidonis, K., Bonanos A., "Updating the PSA sun
position algorithm." Solar Energy, Vol. 212, 2020.
:doi:`10.1016/j.solener.2020.10.084`
"""

if isinstance(coefficients, int):
try:
p = _PSA_PARAMS[coefficients]
except KeyError:
raise ValueError(f"unknown coefficients set: {coefficients}")

Check warning on line 930 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L926-L930

Added lines #L926 - L930 were not covered by tests
else:
p = coefficients

Check warning on line 932 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L932

Added line #L932 was not covered by tests

phi = np.radians(latitude)
lambda_t = longitude

Check warning on line 935 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L934-L935

Added lines #L934 - L935 were not covered by tests

# The julian day calculation in the reference is awkward, as it relies
# on C-style integer division (round toward zero) and thus requires
# tedious floating point divisions with manual integer casts to work
# around python's integer division (round down). It is also slow.
# Faster and simpler to calculate the "elapsed julian day" number
# via unixtime:
unixtime = _datetime_to_unixtime(times)

Check warning on line 943 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L943

Added line #L943 was not covered by tests
# unix time is the number of seconds since 1970-01-01, but PSA needs the
# number of days since 2000-01-01 12:00. The difference is 10957.5 days.
n = unixtime / 86400 - 10957.5
h = ((unixtime / 86400) % 1)*24

Check warning on line 947 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L946-L947

Added lines #L946 - L947 were not covered by tests

# ecliptic longitude (lambda_e) and obliquity (epsilon):
omega = p[0] + p[1] * n # Eq 3
L = p[2] + p[3] * n # Eq 4
g = p[4] + p[5] * n # Eq 5
lambda_e = (

Check warning on line 953 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L950-L953

Added lines #L950 - L953 were not covered by tests
L
+ p[6] * np.sin(g)
+ p[7] * np.sin(2*g)
+ p[8]
+ p[9] * np.sin(omega)
) # Eq 6

Check failure on line 959 in pvlib/solarposition.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E261 at least two spaces before inline comment
epsilon = p[10] + p[11] * n + p[12] * np.cos(omega) # Eq 7

Check warning on line 960 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L960

Added line #L960 was not covered by tests

# celestial right ascension (ra) and declination (d):
ra = np.arctan2(np.cos(epsilon) * np.sin(lambda_e), np.cos(lambda_e)) # Eq 8

Check failure on line 963 in pvlib/solarposition.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E501 line too long (81 > 79 characters)
ra = ra % (2 * np.pi)
d = np.arcsin(np.sin(epsilon) * np.sin(lambda_e)) # Eq 9

Check warning on line 965 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L963-L965

Added lines #L963 - L965 were not covered by tests

# local coordinates:
gmst = p[13] + p[14] * n + h # Eq 10
lmst = (gmst * 15 + lambda_t) * np.pi/180 # Eq 11
w = lmst - ra # Eq 12
theta_z = np.arccos(np.cos(phi) * np.cos(w) * np.cos(d) + np.sin(d) * np.sin(phi)) # Eq 13

Check failure on line 971 in pvlib/solarposition.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E501 line too long (95 > 79 characters)
gamma = np.arctan2(-np.sin(w), (np.tan(d) * np.cos(phi) - np.sin(phi) * np.cos(w))) # Eq 14

Check failure on line 972 in pvlib/solarposition.py

View workflow job for this annotation

GitHub Actions / flake8-linter

E501 line too long (96 > 79 characters)
EMR = 6371.01 # Earth Mean Radius in km
AU = 149597890 # Astronomical Unit in km
theta_z = theta_z + (EMR / AU) * np.sin(theta_z) # Eq 15,16

Check warning on line 975 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L968-L975

Added lines #L968 - L975 were not covered by tests

result = pd.DataFrame({

Check warning on line 977 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L977

Added line #L977 was not covered by tests
'zenith': np.degrees(theta_z),
'azimuth': np.degrees(gamma) % 360
}, index=times)

return result

Check warning on line 982 in pvlib/solarposition.py

View check run for this annotation

Codecov / codecov/patch

pvlib/solarposition.py#L982

Added line #L982 was not covered by tests


def calc_time(lower_bound, upper_bound, latitude, longitude, attribute, value,
altitude=0, pressure=101325, temperature=12, horizon='+0:00',
xtol=1.0e-12):
Expand Down
Loading