Skip to content

sunrise and sunset from pyephem #588

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

Merged
merged 35 commits into from
Oct 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
90ea2e0
Initial commit for ephem_next_rise_set
cwhanse Sep 21, 2018
0b9df18
Stickler issues
cwhanse Sep 21, 2018
86cdfa7
Change test result and rounding to nearest minute
cwhanse Sep 21, 2018
06aef3f
Edit test condition, round not floor
cwhanse Sep 21, 2018
a22d846
Change explicit rounding to pandas .round('min')
cwhanse Sep 22, 2018
719009d
More test fixes
cwhanse Sep 22, 2018
aa4f1d6
Fix sec keyword
cwhanse Sep 22, 2018
c08d213
Add test for transit
cwhanse Sep 23, 2018
f192547
Fix del statement
cwhanse Sep 23, 2018
377658b
Fix column order
cwhanse Sep 23, 2018
f338781
Change reference solution to NREL SPA website
cwhanse Sep 23, 2018
a3bedc0
Adjust pressure and temp for pyephem
cwhanse Sep 23, 2018
17564dd
Add horizon kwarg for pyephem
cwhanse Sep 23, 2018
c29ed06
Separate rise, set test for spa and pyephem
cwhanse Sep 23, 2018
a3d143d
Add horizon kwarg to pyephem, fix test condition for spa
cwhanse Sep 23, 2018
99a75a0
Add horizon kwarg to calc_time
cwhanse Sep 23, 2018
95187d5
Extend tests for next_rise_set_ephem
cwhanse Sep 24, 2018
e318bad
Remove dtype from expected...spa
cwhanse Sep 24, 2018
a88a5b6
Add previous rise/set capability
cwhanse Sep 28, 2018
2121aa0
style fixes
cwhanse Sep 28, 2018
c75b10e
Fix expected result
cwhanse Sep 28, 2018
f85a46a
Fix timezone test
cwhanse Sep 29, 2018
6238321
Documentation updates
cwhanse Sep 29, 2018
3618022
Fix spacing
cwhanse Sep 29, 2018
36bf3cc
Add transit, change golden to fixture, add horizon kwarg and error tests
cwhanse Sep 30, 2018
39956cc
style fixes
cwhanse Sep 30, 2018
c0ae1c5
Merge branch 'master' into ephem_rise_set
cwhanse Sep 30, 2018
f615f12
Fix references to fixture golden_mst, golden
cwhanse Sep 30, 2018
47c95c5
Add transit output to rise_set_transit_ephem
cwhanse Sep 30, 2018
bef44f6
Fix transit conflict
cwhanse Sep 30, 2018
c9b5348
Fix column order
cwhanse Sep 30, 2018
92e0677
Minor test adjustments
cwhanse Sep 30, 2018
db08f8d
Add handling of utcoffset
cwhanse Oct 1, 2018
c05bae7
Inline comment
cwhanse Oct 1, 2018
da2a0e2
Merge branch 'master' into ephem_rise_set
wholmgren Oct 4, 2018
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/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Additional functions for quantities closely related to solar position.
solarposition.calc_time
solarposition.pyephem_earthsun_distance
solarposition.nrel_earthsun_distance
solarposition.rise_set_transit_ephem
spa.calculate_deltat

The spa module contains the implementation of the built-in NREL SPA
Expand Down
4 changes: 4 additions & 0 deletions docs/sphinx/source/whatsnew/v0.6.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ API Changes
* Deprecated ``tmy``, ``tmy.readtmy2`` and ``tmy.readtmy3``;
they will be removed in v0.7. Use the new :py:func:`pvlib.iotools.read_tmy2`
and :py:func:`pvlib.iotools.read_tmy3` instead. (:issue:`261`)
* Added kwarg `horizon` to :func:`~pvlib.solarposition.pyephem` and :func:`~pvlib.solarposition.calc_time` with default value `'+0:00'`


Enhancements
~~~~~~~~~~~~
* :func:`~pvlib.solarposition.rise_set_transit_ephem` returns sunrise, sunset and transit times using pyephem
* Created :py:func:`pvlib.iotools.read_srml` and :py:func:`pvlib.iotools.read_srml_month_from_solardat`
to read University of Oregon Solar Radiation Monitoring Laboratory data. (:issue:`589`)


Bug fixes
~~~~~~~~~
Expand All @@ -35,4 +38,5 @@ Testing
Contributors
~~~~~~~~~~~~
* Will Holmgren (:ghuser:`wholmgren`)
* Cliff Hansen (:ghuser:`cwhanse`)
* Leland Boeman (:ghuser:`lboeman`)
133 changes: 126 additions & 7 deletions pvlib/solarposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# Rob Andrews (@Calama-Consulting), Calama Consulting, 2014
# Will Holmgren (@wholmgren), University of Arizona, 2014
# Tony Lorenzo (@alorenzo175), University of Arizona, 2015
# Cliff hansen (@cwhanse), Sandia National Laboratories, 2018

from __future__ import division
import os
Expand Down Expand Up @@ -438,7 +439,26 @@ def get_sun_rise_set_transit(time, latitude, longitude, how='numpy',
return result


def _ephem_setup(latitude, longitude, altitude, pressure, temperature):
def _ephem_convert_to_seconds_and_microseconds(date):
# utility from unreleased PyEphem 3.6.7.1
"""Converts a PyEphem date into seconds"""
microseconds = int(round(24 * 60 * 60 * 1000000 * date))
seconds, microseconds = divmod(microseconds, 1000000)
seconds -= 2209032000 # difference between epoch 1900 and epoch 1970
return seconds, microseconds


def _ephem_to_timezone(date, tzinfo):
# utility from unreleased PyEphem 3.6.7.1
""""Convert a PyEphem Date into a timezone aware python datetime"""
seconds, microseconds = _ephem_convert_to_seconds_and_microseconds(date)
date = dt.datetime.fromtimestamp(seconds, tzinfo)
date = date.replace(microsecond=microseconds)
return date


def _ephem_setup(latitude, longitude, altitude, pressure, temperature,
horizon):
import ephem
# initialize a PyEphem observer
obs = ephem.Observer()
Expand All @@ -447,14 +467,97 @@ def _ephem_setup(latitude, longitude, altitude, pressure, temperature):
obs.elevation = altitude
obs.pressure = pressure / 100. # convert to mBar
obs.temp = temperature
obs.horizon = horizon

# the PyEphem sun
sun = ephem.Sun()
return obs, sun


def rise_set_transit_ephem(time, latitude, longitude,
next_or_previous='next',
altitude=0,
pressure=101325, temperature=12, horizon='0:00'):
"""
Calculate the next sunrise and sunset times using the PyEphem package.

Parameters
----------
time : pandas.DatetimeIndex
Must be localized
latitude : float
positive is north of 0
longitude : float
positive is east of 0
next_or_previous : str
'next' or 'previous' sunrise and sunset relative to time
altitude : float, default 0
distance above sea level in meters.
pressure : int or float, optional, default 101325
air pressure in Pascals.
temperature : int or float, optional, default 12
air temperature in degrees C.
horizon : string, format +/-X:YY
arc degrees:arc minutes from geometrical horizon for sunrise and
sunset, e.g., horizon='+0:00' to use sun center crossing the
geometrical horizon to define sunrise and sunset,
horizon='-0:34' for when the sun's upper edge crosses the
geometrical horizon

Returns
-------
pandas.DataFrame
index is the same as input `time` argument
columns are 'sunrise', 'sunset', and 'transit'

See also
--------
pyephem
"""

try:
import ephem
except ImportError:
raise ImportError('PyEphem must be installed')

# times must be localized
if not time.tz:
raise ValueError('rise_set_ephem: times must be localized')

obs, sun = _ephem_setup(latitude, longitude, altitude,
pressure, temperature, horizon)
# create lists of sunrise and sunset time localized to time.tz
if next_or_previous.lower() == 'next':
rising = obs.next_rising
setting = obs.next_setting
transit = obs.next_transit
elif next_or_previous.lower() == 'previous':
rising = obs.previous_rising
setting = obs.previous_setting
transit = obs.previous_transit
else:
raise ValueError("next_or_previous must be either 'next' or" +
" 'previous'")

sunrise = []
sunset = []
trans = []
for thetime in time:
thetime = thetime.to_pydatetime()
# pyephem drops timezone when converting to its internal datetime
# format, so handle timezone explicitly here
obs.date = ephem.Date(thetime - thetime.utcoffset())
sunrise.append(_ephem_to_timezone(rising(sun), time.tz))
sunset.append(_ephem_to_timezone(setting(sun), time.tz))
trans.append(_ephem_to_timezone(transit(sun), time.tz))

return pd.DataFrame(index=time, data={'sunrise': sunrise,
'sunset': sunset,
'transit': trans})


def pyephem(time, latitude, longitude, altitude=0, pressure=101325,
temperature=12):
temperature=12, horizon='+0:00'):
"""
Calculate the solar position using the PyEphem package.

Expand All @@ -463,17 +566,26 @@ def pyephem(time, latitude, longitude, altitude=0, pressure=101325,
time : pandas.DatetimeIndex
Localized or UTC.
latitude : float
positive is north of 0
longitude : float
positive is east of 0
altitude : float, default 0
distance above sea level.
distance above sea level in meters.
pressure : int or float, optional, default 101325
air pressure in Pascals.
temperature : int or float, optional, default 12
air temperature in degrees C.
horizon : string, optional, default '+0:00'
arc degrees:arc minutes from geometrical horizon for sunrise and
sunset, e.g., horizon='+0:00' to use sun center crossing the
geometrical horizon to define sunrise and sunset,
horizon='-0:34' for when the sun's upper edge crosses the
geometrical horizon

Returns
-------
DataFrame
pandas.DataFrame
index is the same as input `time` argument
The DataFrame will have the following columns:
apparent_elevation, elevation,
apparent_azimuth, azimuth,
Expand All @@ -499,7 +611,7 @@ def pyephem(time, latitude, longitude, altitude=0, pressure=101325,
sun_coords = pd.DataFrame(index=time)

obs, sun = _ephem_setup(latitude, longitude, altitude,
pressure, temperature)
pressure, temperature, horizon)

# make and fill lists of the sun's altitude and azimuth
# this is the pressure and temperature corrected apparent alt/az.
Expand Down Expand Up @@ -711,7 +823,8 @@ def ephemeris(time, latitude, longitude, pressure=101325, temperature=12):


def calc_time(lower_bound, upper_bound, latitude, longitude, attribute, value,
altitude=0, pressure=101325, temperature=12, xtol=1.0e-12):
altitude=0, pressure=101325, temperature=12, horizon='+0:00',
xtol=1.0e-12):
"""
Calculate the time between lower_bound and upper_bound
where the attribute is equal to value. Uses PyEphem for
Expand All @@ -736,6 +849,12 @@ def calc_time(lower_bound, upper_bound, latitude, longitude, attribute, value,
atmospheric correction.
temperature : int or float, optional, default 12
Air temperature in degrees C.
horizon : string, optional, default '+0:00'
arc degrees:arc minutes from geometrical horizon for sunrise and
sunset, e.g., horizon='+0:00' to use sun center crossing the
geometrical horizon to define sunrise and sunset,
horizon='-0:34' for when the sun's upper edge crosses the
geometrical horizon
xtol : float, optional, default 1.0e-12
The allowed error in the result from value

Expand All @@ -758,7 +877,7 @@ def calc_time(lower_bound, upper_bound, latitude, longitude, attribute, value,
raise ImportError('The calc_time function requires scipy')

obs, sun = _ephem_setup(latitude, longitude, altitude,
pressure, temperature)
pressure, temperature, horizon)

def compute_attr(thetime, target, attr):
obs.date = thetime
Expand Down
5 changes: 2 additions & 3 deletions pvlib/test/test_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import pvlib
from pvlib.location import Location

from test_solarposition import expected_solpos
from test_solarposition import expected_solpos, golden_mst

from conftest import requires_scipy

Expand Down Expand Up @@ -252,8 +252,7 @@ def test_from_tmy_2():
assert_frame_equal(loc.tmy_data, data)


def test_get_solarposition(expected_solpos):
from test_solarposition import golden_mst
def test_get_solarposition(expected_solpos, golden_mst):
times = pd.date_range(datetime.datetime(2003,10,17,12,30,30),
periods=1, freq='D', tz=golden_mst.tz)
ephem_data = golden_mst.get_solarposition(times, temperature=11)
Expand Down
Loading