Skip to content

Moon phase/illumination functions begun with PyEphem implementation - #39

Merged
eteq merged 15 commits into
astropy:masterfrom
bmorris3:moon
Aug 4, 2015
Merged

eteq merged 15 commits into
astropy:masterfrom
bmorris3:moon

Conversation

@bmorris3

Copy link
Copy Markdown
Contributor

As per Issue #35 , the machinery for computing the position of the moon with jplephem is on the back burner until a bug in astropy is resolved. In the mean time, I've implemented some of the lunar convenience functions using PyEphem, with intentions to swap it out in favor of our permanent jplephem solution when the astropy bug is corrected.

I also implemented moon_altaz() with tests, but commented them out, since these the routines are affected by the aforementioned bug.

@adrn

adrn commented Jul 25, 2015

Copy link
Copy Markdown
Member

A topic for discussion: looking at all of these functions that start with moon_*, do we really want to add these all as methods to the Observer class? I'm not sure either way, but it is starting to feel like the Observer class is getting a bit bloated...

@bmorris3

Copy link
Copy Markdown
Contributor Author

@ejeschke Sorry, I missed it in the doctoring!

Comment thread astroplan/core.py

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.

I don't quite understand why here (and in moon_phase) you have the moon and sun keywords. Doesn't it make more sense to just use time? Otherwise the user might pass in moon/sun objects that don't correctly match time, and then all kinds of stuff might go bonkers (without an error). After all, one of the Zen of python elements is "Errors should never pass silently."...

If this is a performance thing, I would instead suggest caching. I.e. something like:

# somewhere you'll have to create self._cached_moons... in __init__ is probably the easiest
if time in self._cached_moons:  # this might need to be time.jd? Not sure if `Time` objects are hashable
    moon = self._cached_moons[time]
else:
    moon = get_moon(time, self.location, self.pressure)
    self._cached_moons[time] = moon
# similar for the sun

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It was meant to be a performance thing, since you're likely to ask for the illumination and position of the moon at the same time. I'll remove that feature for now and hold onto caching for discussion later – there are bunch of other places where we could cache too.

@eteq

eteq commented Jul 30, 2015

Copy link
Copy Markdown
Member

@bmorris3: I'm a bit confused by how this is now implemented. As I recall from one of your notebooks, the astropy/jplephem approach got the (GCRS) RA/Dec right (at least insofar as it matched pyephem), but it was only the GCRS->AltAz conversion that featured a bug. But the code you have here uses pyephem to compute the ra and dec but not the alt/az. Or am I misunderstanding something?

So I was thinking this would make it so that moon_altaz would still return a SkyCoord (in the AltAz frame), but where the alt/az was actually calulated using pyephem rather than the built-in astropy transformations.

@bmorris3

Copy link
Copy Markdown
Contributor Author

@eteq You're right, the get_moon method produces the coordinates of the Moon without transforming to AltAz, since the GCRS->AltAz transformation is the buggy one. My intention is to mimic the get_sun function, which will return a SkyCoord with the RA/Dec of the Sun if computed with only a time as input, and if also computed with a location it will return the altaz. I've implemented the PyEphem and jplephem bits necessary to compute the RA/Dec without doing the AltAz transformation.

I (somehow) didn't realize you could initialize a SkyCoord from an AltAz frame, I guess I assumed it would be improperly defined without an associated location. I've updated the moon_altaz method to do so in d4aa918.

Comment thread astroplan/core.py Outdated

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.

You'll need to pass in all the other elements of the altaz frame that apply for this observer. The easiest/most interanlly consistent way is probably:

SkyCoord(alt=moon_alt*u.rad, az=moon_az*u.rad, frame=self.altaz(time))

@eteq

eteq commented Jul 30, 2015

Copy link
Copy Markdown
Member

Ok gotcha. But one reason I was asking: calc_moon_phase_angle has an important subtlety: it's frame-dependent. That is, the angular separation of the moon from the sun is very different in a GCRS vs ICRS vs AltAz frame. In ICRS it's something very different from what you want because ICRS's origin is the solar system barycenter, rather than the Earth, so it's basically set by the earth and sun position relative to the barycenter, rather than the earth-moon-sun triangle. GCRS (which is what I think you're implicitly doing here) gets a lot closer, but it still is subtly different from what you'd see on the surface of the earth (which is what AltAz gives you).

Here, I think what you're doing in Observer.moon_phase is getting the moon as an AltAz, but the sun as GCRS. So I'd suggest adding one more step of converting the sun location to the appropriate AltAz before passing it into calc_moon_phase_angle.

Comment thread astroplan/moon.py Outdated

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@eteq : When I do this line to transform to AltAz as you suggested, I get an infinite recursion, so I've commented it out. Am I doing something silly here?

@eteq

eteq commented Jul 31, 2015

Copy link
Copy Markdown
Member

@bmorris3 - I figured out what’s up with the recursion thing. get_moon currently returns the moon in the GCRS frame. So with the transform_to commented out, both sun and moon are in GCRS. So then sun.separation(moon) is just a direct geometric calculation, because the two are in the same frame. If you transform sun to AltAz, then moon will then has to get transformed to AltAz to do the comparison. But that can’t be done automatically because you haven’t given the moon SkyCoord a location. I’m not sure why that is causing an infinite recursion error rather than a helpful message about location being missing, but the root problem is the frames not matching.

So that brings in the reason for why moon_altaz being implemented via pyephem is important. The “best” way to solve this would instead be to give the moon location information so that it can just do the GCRS->AltAz transformation automatically. But that won’t work because of the bug that made us use pyephem.

So the work-around for now is to have get_moon yield something in the AltAz frame, rather than in GCRS. Then when you have the Sun in a matched AltAz frame, sun.separation(moon) will require no frame transformation and all is happy.

@bmorris3

Copy link
Copy Markdown
Contributor Author

@eteq, I'm still getting the recursion depth error even after making the get_moon coordinate in the AltAz frame.

from astroplan.moon import get_moon
from astropy.coordinates import SkyCoord, EarthLocation, AltAz, get_sun
from astropy.time import Time
time = Time.now()
location = EarthLocation('-122d', '47d')
moon = get_moon(time, location)
sun = get_sun(time).transform_to(AltAz(location=location, obstime=time))
print(moon.separation(sun)) # Fails
print(sun.separation(moon)) # Fails

@eteq

eteq commented Aug 3, 2015

Copy link
Copy Markdown
Member

@bmorris3 - ok, now I think we've reached a bug in astropy rather than a problem with what frames are being compared. I suggest this as a work-around:
(this would replace the last two lines in the snippet you just showed)

from astropy.coordinates import Angle
from astropy.coordinates.angle_utilities import angular_separation

moonsph = moon.spherical
sunsph = moon.spherical

sep = Angle(angular_separation(moonsph.lon, moonsph.lat, sunsph.lon, sunsph.lat), u.deg)

that's basically the same as the guts of separation, but it short-circuits the frame transformation tests (which are the source of the problem)

@bmorris3

bmorris3 commented Aug 4, 2015

Copy link
Copy Markdown
Contributor Author

@eteq / @cdeil I think this is as far as this PR is going to go until a few related astropy-related issues (astropy/astropy#4033, astropy/astropy#3998, astropy/astropy#3938, astropy/astropy#3920) are sorted out, and we can replace the PyEphem placeholders with the long-term jplephem equivalents.

@cdeil

cdeil commented Aug 4, 2015

Copy link
Copy Markdown
Member

I'm +1 to remove the calc_ in these functions:

  • calc_moon_illumination -> moon_illumination
  • calc_moon_phase_angle -> moon_phase_angle

Otherwise 👍

@bmorris3

bmorris3 commented Aug 4, 2015

Copy link
Copy Markdown
Contributor Author

@cdeil I left the calc_s on because I was trying to keep calc_moon_illumination separate from the method on Observercalled moon_illumination. How can we best prevent overlapping names here?

@eteq

eteq commented Aug 4, 2015

Copy link
Copy Markdown
Member

I'm +0 on @cdeil's suggestion - it's fine to either remove the calc_ and otherwise leave it all as-is: from context the methods are always called as obs.moon_illumination while the function would be moon_illumination. And they do basically the same thing just with different information, right?

However, I think it's not that important either way, because those functions are not to be part of the public API, right? Rather, they're internal functions used by Observer.

Which reminds me of one additional (easy) change you need, @bmorris3: get_moon needs to be added to __all__ so that one can just do astroplan.get_moon instead of having to import the moon submodule.

@cdeil

cdeil commented Aug 4, 2015

Copy link
Copy Markdown
Member

If the function and method basically do the same thing, it's good if they have the same name, no?

Comment thread astroplan/moon.py Outdated

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.

This still says moon/sun instead of time/location (same below in moon_illumination)

@eteq

eteq commented Aug 4, 2015

Copy link
Copy Markdown
Member

Alright, looks good. Merging!

eteq added a commit that referenced this pull request Aug 4, 2015
Moon phase/illumination functions begun with PyEphem implementation
@eteq
eteq merged commit 7832649 into astropy:master Aug 4, 2015
@bmorris3
bmorris3 deleted the moon branch August 10, 2015 17:09
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