-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add a new and faster way to calculate the solarposition #730
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
Changes from all commits
d97cf22
07c9ea3
98f6539
19531ad
5d46472
6ae7dae
a43cf78
1c1b2c6
0749c55
bf88e3c
b20ca53
fe5bf63
71dc8c5
03bb143
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -7,6 +7,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||
# Will Holmgren (@wholmgren), University of Arizona, 2014 | ||||||||||||||||||||||||||||||||||||||||||||||
# Tony Lorenzo (@alorenzo175), University of Arizona, 2015 | ||||||||||||||||||||||||||||||||||||||||||||||
# Cliff hansen (@cwhanse), Sandia National Laboratories, 2018 | ||||||||||||||||||||||||||||||||||||||||||||||
# Daniel Lassahn (@meteoDaniel), meteocontrol Energy and Weather Services, 2019 | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
from __future__ import division | ||||||||||||||||||||||||||||||||||||||||||||||
import os | ||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -18,17 +19,16 @@ | |||||||||||||||||||||||||||||||||||||||||||||
from imp import reload | ||||||||||||||||||||||||||||||||||||||||||||||
except ImportError: | ||||||||||||||||||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
import numpy as np | ||||||||||||||||||||||||||||||||||||||||||||||
import pandas as pd | ||||||||||||||||||||||||||||||||||||||||||||||
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, datetime_to_julian | ||||||||||||||||||||||||||||||||||||||||||||||
from pvlib._deprecation import deprecated | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
NS_PER_HR = 1.e9 * 3600. # nanoseconds per hour | ||||||||||||||||||||||||||||||||||||||||||||||
JULIAN_YEARS = 365.2425 | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
def get_solarposition(time, latitude, longitude, | ||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -67,6 +67,9 @@ def get_solarposition(time, latitude, longitude, | |||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
'nrel_c' uses the NREL SPA C code [3]: :py:func:`spa_c` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
'spencer' uses the Spencer formula [4] :py:func:`spencer` | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
temperature : float, default 12 | ||||||||||||||||||||||||||||||||||||||||||||||
Degrees C. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -81,6 +84,9 @@ def get_solarposition(time, latitude, longitude, | |||||||||||||||||||||||||||||||||||||||||||||
solar radiation applications. Solar Energy, vol. 81, no. 6, p. 838, 2007. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
[3] NREL SPA code: http://rredc.nrel.gov/solar/codesandalgorithms/spa/ | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
[4] Spencer (1972) can be found in "Solar energy fundamentals and | ||||||||||||||||||||||||||||||||||||||||||||||
modeling techniques" from Zekai Sen | ||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
if altitude is None and pressure is None: | ||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -112,8 +118,10 @@ def get_solarposition(time, latitude, longitude, | |||||||||||||||||||||||||||||||||||||||||||||
pressure=pressure, | ||||||||||||||||||||||||||||||||||||||||||||||
temperature=temperature, **kwargs) | ||||||||||||||||||||||||||||||||||||||||||||||
elif method == 'ephemeris': | ||||||||||||||||||||||||||||||||||||||||||||||
ephem_df = ephemeris(time, latitude, longitude, pressure, temperature, | ||||||||||||||||||||||||||||||||||||||||||||||
**kwargs) | ||||||||||||||||||||||||||||||||||||||||||||||
ephem_df = ephemeris(time, latitude, longitude, pressure, temperature) | ||||||||||||||||||||||||||||||||||||||||||||||
elif method == 'spencer': | ||||||||||||||||||||||||||||||||||||||||||||||
ephem_df = spencer(time, latitude, longitude) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||
raise ValueError('Invalid solar position method') | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -495,7 +503,7 @@ def sun_rise_set_transit_ephem(times, latitude, longitude, | |||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Parameters | ||||||||||||||||||||||||||||||||||||||||||||||
---------- | ||||||||||||||||||||||||||||||||||||||||||||||
time : pandas.DatetimeIndex | ||||||||||||||||||||||||||||||||||||||||||||||
times : :class:`pandas.DatetimeIndex` | ||||||||||||||||||||||||||||||||||||||||||||||
Must be localized | ||||||||||||||||||||||||||||||||||||||||||||||
latitude : float | ||||||||||||||||||||||||||||||||||||||||||||||
Latitude in degrees, positive north of equator, negative to south | ||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1445,3 +1453,80 @@ def sun_rise_set_transit_geometric(times, latitude, longitude, declination, | |||||||||||||||||||||||||||||||||||||||||||||
sunset = _local_times_from_hours_since_midnight(times, sunset_hour) | ||||||||||||||||||||||||||||||||||||||||||||||
transit = _local_times_from_hours_since_midnight(times, transit_hour) | ||||||||||||||||||||||||||||||||||||||||||||||
return sunrise, sunset, transit | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
def spencer(times, latitude, longitude): | ||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||
Calculate the solar position using a formulation by Spencer 1971/1972. | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Parameters | ||||||||||||||||||||||||||||||||||||||||||||||
---------- | ||||||||||||||||||||||||||||||||||||||||||||||
times : pandas.DatetimeIndex` | ||||||||||||||||||||||||||||||||||||||||||||||
Corresponding timestamps, must be localized to the timezone for the | ||||||||||||||||||||||||||||||||||||||||||||||
``latitude`` and ``longitude``. | ||||||||||||||||||||||||||||||||||||||||||||||
latitude : float | ||||||||||||||||||||||||||||||||||||||||||||||
Latitude in degrees, positive north of equator, negative to south | ||||||||||||||||||||||||||||||||||||||||||||||
longitude : float | ||||||||||||||||||||||||||||||||||||||||||||||
Longitude in degrees, positive east of prime meridian, negative to west | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
Returns | ||||||||||||||||||||||||||||||||||||||||||||||
------- | ||||||||||||||||||||||||||||||||||||||||||||||
DataFrame | ||||||||||||||||||||||||||||||||||||||||||||||
The DataFrame will have the following columns: | ||||||||||||||||||||||||||||||||||||||||||||||
zenith (degrees), | ||||||||||||||||||||||||||||||||||||||||||||||
elevation (degrees), | ||||||||||||||||||||||||||||||||||||||||||||||
azimuth (degrees), | ||||||||||||||||||||||||||||||||||||||||||||||
equation_of_time (seconds), | ||||||||||||||||||||||||||||||||||||||||||||||
declination (degrees). | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
References | ||||||||||||||||||||||||||||||||||||||||||||||
---------- | ||||||||||||||||||||||||||||||||||||||||||||||
[1] Spencer (1972) can be found in | ||||||||||||||||||||||||||||||||||||||||||||||
Sen, Zekai. Solar energy fundamentals and modeling techniques: | ||||||||||||||||||||||||||||||||||||||||||||||
atmosphere, environment, climate change and renewable energy. | ||||||||||||||||||||||||||||||||||||||||||||||
Springer Science & Business Media, 2008. | ||||||||||||||||||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
julians, julian_2000 = datetime_to_julian(times) | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @meteoDaniel can you please replace all of these lines pvlib-python/pvlib/solarposition.py Lines 1490 to 1511 in 03bb143
with a call to solar_zenith_analytical(latitude, hourangle, declination) ?
|
||||||||||||||||||||||||||||||||||||||||||||||
julians_2000 = np.asarray(julians, dtype=np.float) - julian_2000 | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
latitude_radians = np.radians(latitude) | ||||||||||||||||||||||||||||||||||||||||||||||
day_time = (julians_2000 % 1) * 24 | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
declination = np.array(declination_spencer71(times.dayofyear)) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
# Equation of time (difference between standard time and solar time). | ||||||||||||||||||||||||||||||||||||||||||||||
meteoDaniel marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||||||||||||||||||
eot = np.array(equation_of_time_spencer71(times.dayofyear)) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
# True local time | ||||||||||||||||||||||||||||||||||||||||||||||
tlt = (day_time + longitude / 15 + eot / 60) % 24 - 12 | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect this is similar as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looking in more detail they are the nearly the same: but IMO this version here, looks a little easier to read, with the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But do you agree they are the same equation, that only differ by a factor of 1/15? we have noticed that different formulations have different "offsets" so perhaps adjusting the input may help? Or perhaps leave it, raise an issue to collapse this later, and maybe add a |
||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
# Solar hour angle | ||||||||||||||||||||||||||||||||||||||||||||||
ha = np.radians(tlt * 15) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
# Calculate sun elevation. | ||||||||||||||||||||||||||||||||||||||||||||||
sin_sun_elevation = ( | ||||||||||||||||||||||||||||||||||||||||||||||
np.sin(declination) * np.sin(latitude_radians) + | ||||||||||||||||||||||||||||||||||||||||||||||
np.cos(declination) * np.cos(latitude_radians) * np.cos(ha) | ||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
# Compute the sun's elevation and zenith angle. | ||||||||||||||||||||||||||||||||||||||||||||||
elevation = np.arcsin(sin_sun_elevation) | ||||||||||||||||||||||||||||||||||||||||||||||
zenith = np.pi / 2 - elevation | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
# Compute the sun's azimuth angle. | ||||||||||||||||||||||||||||||||||||||||||||||
y = -(np.sin(latitude_radians) * np.sin(elevation) - np.sin(declination)) \ | ||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @meteoDaniel can you please replace these lines pvlib-python/pvlib/solarposition.py Lines 1517 to 1524 in 03bb143
with a call to solar_azimuth_analytical(latitude, hourangle, declination, zenith) ?
|
||||||||||||||||||||||||||||||||||||||||||||||
/ (np.cos(latitude_radians) * np.cos(elevation)) | ||||||||||||||||||||||||||||||||||||||||||||||
azimuth = np.arccos(y) | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
# Convert azimuth angle from 0-pi to 0-2pi. | ||||||||||||||||||||||||||||||||||||||||||||||
tlt_filter = 0 <= tlt | ||||||||||||||||||||||||||||||||||||||||||||||
azimuth[tlt_filter] = 2 * np.pi - azimuth[tlt_filter] | ||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||
result = pd.DataFrame({'zenith': np.degrees(zenith), | ||||||||||||||||||||||||||||||||||||||||||||||
'elevation': np.degrees(elevation), | ||||||||||||||||||||||||||||||||||||||||||||||
'azimuth': np.degrees(azimuth), | ||||||||||||||||||||||||||||||||||||||||||||||
'declination': declination, | ||||||||||||||||||||||||||||||||||||||||||||||
'equation_of_time': eot}, | ||||||||||||||||||||||||||||||||||||||||||||||
index=times) | ||||||||||||||||||||||||||||||||||||||||||||||
return result |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,10 @@ | |
import pandas as pd | ||
import pytz | ||
|
||
JULIAN_2000 = 2451545 | ||
DAY_SECONDS = 60 * 60 * 24 | ||
DT_2000 = pd.to_datetime(dt.datetime(2000, 1, 1)).tz_localize('UTC') | ||
|
||
|
||
def cosd(angle): | ||
""" | ||
|
@@ -425,3 +429,30 @@ def _golden_sect_DataFrame(params, VL, VH, func): | |
raise Exception("EXCEPTION:iterations exceeded maximum (50)") | ||
|
||
return func(df, 'V1'), df['V1'] | ||
|
||
|
||
def datetime_to_julian(times): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like the duplication of |
||
""" | ||
Transforms pd.DateTimeIndex to Julian days | ||
|
||
Parameters | ||
---------- | ||
times : pandas.DatetimeIndex | ||
Corresponding timestamps, must be localized to the timezone for the | ||
``latitude`` and ``longitude`` | ||
Returns | ||
------- | ||
Float64Index | ||
The float index contains julian dates | ||
Float | ||
julian 2000 in UTC referenced to local time | ||
""" | ||
dt_2000 = DT_2000.tz_convert(times.tzinfo) | ||
hours_difference = (DT_2000.tz_localize(None) - | ||
dt_2000.tz_localize(None)).seconds / 3600 | ||
julian_2000 = JULIAN_2000 - (hours_difference/24) | ||
delta = times - dt_2000 | ||
delta_julians = (delta.seconds + delta.microseconds / 1e6) | ||
return ( | ||
julian_2000 + delta.days + delta_julians / DAY_SECONDS | ||
), julian_2000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend that we add a comment on the relative accuracy and speed of the function. Perhaps it belongs in a
Notes
section below.