Skip to content

Solar system and lunar ephemerides via jplephem - #4746

Closed
bmorris3 wants to merge 5 commits into
astropy:masterfrom
bmorris3:solarsystem
Closed

bmorris3 wants to merge 5 commits into
astropy:masterfrom
bmorris3:solarsystem

Conversation

@bmorris3

@bmorris3 bmorris3 commented Mar 29, 2016 •

Copy link
Copy Markdown
Contributor

I've implemented an interface for grabbing solar system ephemerides from jplephem (MIT licensed), after caching a copy of the DE430 table (see the jplephem docs for info). There are two new public functions in this PR, get_moon(time, [location]) and get_planet(time, planet_index, [location]). Both return SkyCoord objects, the former for Earth's moon and the latter for solar system "planets" Mercury through Pluto inclusive.

Here are the new functions in action:

In [1]: from astropy.coordinates import get_moon, get_planet, EarthLocation
In [2]: from astropy.time import Time
In [6]: loc = EarthLocation.of_site('Apache Point')
In [7]: moon = get_moon(Time.now(), loc)
In [8]: moon
Out[8]:
<SkyCoord (GCRS: obstime=2016-03-29 06:35:36.927857, obsgeoloc=[-1463969.30185172 -5166673.34223433  3434985.71204565] m, obsgeovel=[ 0.  0.  0.] m / s): (ra, dec, distance) in (deg, deg, km)
    (250.94788165, -17.04585998, 400244.30166804)>

In [9]: jupiter = get_planet(Time.now(), 5, loc)
In [10]: jupiter
Out[10]:
<SkyCoord (GCRS: obstime=2016-03-29 06:36:28.310851, obsgeoloc=[-1463969.30185172 -5166673.34223433  3434985.71204565] m, obsgeovel=[ 0.  0.  0.] m / s): (ra, dec, distance) in (deg, deg, km)
    (167.22360841, 7.07220939, 673151288.66240811)>

I'm testing the outputs of my functions against queries from the JPL Horizons web interface for the Astrometric RA & Dec of the targets with the atmospheric refraction setting turned on. I calculate the positions of Mercury, the Moon and Jupiter on two dates, from observatories in both the northern and southern hemispheres of the Earth. The sky coordinates of planets agree to within one arcminute between astropy+jplephem and JPL Horizons on the web, but the moon coordinates vary by 10s of arcmin. I don't know precisely what causes the lunar discrepancy yet.

I'm submitting this PR prepared to make lots of changes after comments, since I hope some discussion will follow. This addresses #3219, #2244, #3217, #4269, astropy/astroplan#35 astropy/astroplan#111, and overlaps in functionality with PRs #4724, #4736.

The tests will fail in the ci chain because I've put jplephem in the optional dependencies, but is not currently accessible via conda (you can grab it with pip install jplephem). I'm not sure if this is a showstopper for us – would it be possible to push a copy of jplephem to conda?

cc @mhvk @cdeil @brandon-rhodes @adrn @astrofrog


Questions for @eteq or other coordinates experts:

  • are the GCRS coordinate transformations currently tested at distances <1 AU? I recall we had problems before for GCRS coordinates near Earth, but I think they were all fixed in Fix bug in GCRS<->ICRS conversion for near-earth objects #4221, Fix coordinate transformations near Earth #4254.
  • If no location is supplied to these functions, these functions default to Greenwich as a placeholder location. Is that OK? What's convention right now if defining a GCRS coordinate without an EarthLocation?
  • can some of the discrepancy between jplephem+astropy and JPL Horizons stem from the fact that the position/orientation of the Earth in the astropy GCRS frame (via ERFA?) won't be precisely consistent with the jplephem position of Earth relative to the solar system barycenter, which is the position vector from which all other coordinates are built in my implementation?

obsgeoloc=u.Quantity(location.geocentric, copy=False)))


def get_moon(time, location=None):

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 just feel like it is a waste not to include the following in this function:

if some_condition:
    warnings.warn('That is no moon!', AstropyUserWarning)

@mhvk

mhvk commented Mar 29, 2016

Copy link
Copy Markdown
Contributor

@bmorris3 - great! Even if it does mean one GSOC project is now partially done...

One general comment: Right now, you hard code the specific ephemeris file that should be used; I think it would be best to leave an option for selecting it. Indeed, even better if we could make it a sufficiently abstracted interface that one could also use non-JPL ephemerides. Specifically, it turns out that within erfa there are pretty good approximations to the planet positions, which one can access via plan9 (see #4736, where it is used to calculate the position of the moon). From a quick look, it seems one could fairly easily substitute these.

Conversely, in the new Time.light_travel_time method (#4314), erfa is used to calculate an approximate position of the Earth; clearly, that could use an extension to use the precise one (but this is trivially added later).


planet_index : int
Index of the planet (1-9 for Mercury through Pluto), excluding the
special value 3, which corresponds to the Earth's Moon.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Are you sure 3 is the Moon? I haven't had time to look carefully, but I thought it usually encoded the earth-moon barycentre. In any case, all the more reasons to allow input using a string!

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.

I will be the first to admit that this interface is confusing. What's going on here is that it would be senseless to create a SkyCoord for Earth, so I want to prevent users from selecting planet_index=3 for the Earth. Then when I wrote the get_moon function, I didn't want to duplicate the code that accesses the DE430 kernel for both the planets and for the moon, so I wrote the underlying functions in get_planet to give the coordinates of the moon in the special case of planet_index=3. See the one-line get_moon implementation here.

This design choice isn't a weird relic from the weirdness of the DE430 kernel, it's me trying to write code that's not redundant. I'm open to alternatives to make this clear or more intuitive.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If we're using indices that match those used by the JPL kernel, 3 should have the same meaning as those have, otherwise it is just going to be too confusing in my opinion (especially since erfa.plan9 does the same thing!).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p.s. It is sensible to get a position of the Earth relative to the solar system barycentre, since this underlies any precise barycentric corrections.

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.

I understand where you're coming from. I was only designing a function for the purposes of creating SkyCoord objects, since my selfish motivations are to use these functions in astropy/astroplan#35 and astropy/astroplan#111.

match those used by the JPL kernel, 3 should have the same meaning as those have

For the purposes of this simple get_planet as a SkyCoord function, I am in favor of avoiding using the jplephem lexicon for the integers that represent planet barycenters and planets because they are non-intuitive and unnecessary to know for the average user trying to get the position of a planet as a SkyCoord.

I think part of the reason why it took so long for a PR like this to arrive is because the JPL kernel indices are confusing and messy. Hiding that mess was my top priority in this PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Well, for me, the reason I did only do a kludge myself is that I don't care where the planets are, but do care about very accurate barycentric corrections...

Anyway, I'm happy with unambiguous entries, but think it is only confusing if you use something that is almost but not exactly the same as the way the ephemeris files (and plan9) are organised.

@brandon-rhodes

Copy link
Copy Markdown

I am glad that jplephem might prove useful to AstroPy!

When I see a difference with HORIZONS that is small for the distant planets but large for the Moon, it usually involves time, since a small error in time causes a bigger problem for the fast-moving Moon than for the slower planets. I quickly go check things like whether my leap-second table is up to date and (if Earth rotation is involved) my Delta-T estimate. Try combing through the HORIZONS output, looking at every variable and model in use for time and for the bodies involved — any difference could be the source of the difference.

I have been able to get Skyfield to 0.001 arcsecond agreement with the Naval Observatory, and am now honing in on HORIZONS myself. I have been thinking of drawing up a list of errors that can happen, and maybe even writing a little routine that, given a date and position for something like the Moon, goes through all combinations of time scales and frames of reference, trying to determine which switches need to be flipped how to generate the number that a user is seeing from a web site.

It's a huge issue — users always want to check astronomy libraries against web sites (often not even good ones like HORIZONS), and trying to explain why an astronomy library is giving an answer that's an arcminute off is difficult when the web site is a black box and you can't see its methodology. I've often wished that the HORIZONS source code were available.

@StuartLittlefair

Copy link
Copy Markdown
Contributor

@bmorris3 to try and answer some of your questions:

  • GCRS coordinates without an EarthLocation currently default to "true" GCRS coordinates - i.e Geocentric. I think this is a sensible default in your case as well.
  • I don't think inconsistencies between the geocentre are responsible for the differences between your code and JPL HORIZONS. As written the GCRS frame you are making will transparently pass longitude (ra) and latitude (dec) to the SkyCoord; any discrepancies are already present in the earth_to_planet_vector.

As a comment on your PR, get_absolute_planet_position currently implements an ICRS->GCRS transformation by hand. It might be an idea to simply create an ICRS object directly from the barycentric position returned by jplephem for the specified body and use the coordinate transformation machinery from then on. This at least has the merit of making sure that all coordinate transforms are kept in one place.

I also think as written your coordinates are not properly taking into account the location of the observatory; merely setting the obsgeoloc attribute of the GCRS coordinate doesn't actually give a different position. Creating an ICRS object and transforming to a GCRS coordinate with a specified obsgeoloc would address this.

Also - I think you should be supplying times in TDB to the jplephem kernel.

@bsipocz

bsipocz commented Apr 1, 2016

Copy link
Copy Markdown
Member

The tests will fail in the ci chain because I've put jplephem in the optional dependencies, but is not currently accessible via conda (you can grab it with pip install jplephem). I'm not sure if this is a showstopper for us – would it be possible to push a copy of jplephem to conda?

@bmorris3 - Put in under PIP_DEPENDENCIES (but only for the builds where the other optionals are tested, eg where CONDA_DEPENDENCIES=$CONDA_ALL_DEPENDENCIES. It's perfectly fine to have optional dependencies that are not (yet) available on a conda channel, and install them via pip.

@bsipocz

bsipocz commented Apr 1, 2016

Copy link
Copy Markdown
Member

On the other hand, I see that there is a conda recipe in the jplephem repo. Would you @brandon-rhodes consider it adding to the openastronomy conda channel? https://github.com/OpenAstronomy/conda-channel

@bmorris3

bmorris3 commented Apr 1, 2016

Copy link
Copy Markdown
Contributor Author

Thanks @bsipocz, I hadn't noticed that line in the .travis.yml file. Maybe that last commit will produce more interesting test results.

Comment thread .travis.yml Outdated
- CONDA_ALL_DEPENDENCIES='Cython jinja2 scipy h5py matplotlib pyyaml scikit-image pandas pytz beautiful-soup ipython mpmath jplephem'
- PIP_DEPENDENCIES=''
- CONDA_ALL_DEPENDENCIES='Cython jinja2 scipy h5py matplotlib pyyaml scikit-image pandas pytz beautiful-soup ipython mpmath'
- PIP_DEPENDENCIES='jplephem'

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.

@bmorris3 - Don't add it here, as this list is always got installed. Instead add it into the env of each builds where optionals are installed (you can spot them where CONDA_DEPENDENCIES=$CONDA_ALL_DEPENDENCIES is set).

Comment thread .travis.yml
env: PYTHON_VERSION=2.7 SETUP_CMD='build_sphinx -w'
CONDA_DEPENDENCIES=$CONDA_ALL_DEPENDENCIES
PIP_DEPENDENCIES='wcsaxes --no-deps'
PIP_DEPENDENCIES='wcsaxes --no-deps jplephem'

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.

Like this, @bsipocz? Thanks for the help, sorry to be learning by experimentation.

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.

yes, this looks better :)

@brandon-rhodes

Copy link
Copy Markdown

@bsipocz What are the steps for adding it to the conda repository? I've never put a package in one before! :)

@bsipocz

bsipocz commented Apr 3, 2016

Copy link
Copy Markdown
Member

@brandon-rhodes - Most probably adding and entry to https://github.com/OpenAstronomy/conda-channel/blob/master/requirements.yml will do the job. If it doesn't work with the repo's default recipe then you can also add the one from jplephem.
I only added packages that worked out of the box, thus never dealt with custom recipes, but I'm sure @mwcraig will know the solution for most problems.

@astrofrog

Copy link
Copy Markdown
Member

@bmorris3 - what is the status of this? Is there any chance we can finish it up by Friday?

@mhvk

mhvk commented May 11, 2016

Copy link
Copy Markdown
Contributor

@astrofrog - agree it would be great to get this in, so let me try to summarize what I think are the hurdles. The most important one -- which should really be resolved before we merge -- is the question whether the positions match properly. It may be that much of this is in the transformation from ICRS coordinates relative to the solar system barycentre to coordinates relative to some observer on Earth. @StuartLittlefair suggested using the coordinate machinery for this transformation, rather than do it by hand, but that means one starts using approximate positions for the Earth, when one of the beauties of jplephem is that it gives us much more accurate ones.

My suggestion would be to proceed by first writing tests for the absolute (ICRS) positions -- as I noted above, these are very useful in themselves, so it is also good to have a public method for those (maybe just a different value of location; e.g., if we allow strings which by default get passed to EarthLocation.of_site, which could include items for barycentre and geocentre).

Apart from the above more serious hurdle, I think the main modification to be made is to make strings the default for planet names (I think it is safest not to allow integers at all for now).

Another smaller question was whether to allow a choice of ephemeris used; this could either be another DE... file, or plan9 in erfa. But I'd guess this could be done later too.

@bmorris3

bmorris3 commented May 11, 2016 •

Copy link
Copy Markdown
Contributor Author

@astrofrog – I won't be able to work on this again this week.

If someone wants to push this forward, a place to start would be to review @StuartLittlefair's PR to my branch bmorris3#1, which does some of the more rigorous comparisons that @mhvk, @brandon-rhodes and others have encouraged.

@StuartLittlefair

Copy link
Copy Markdown
Contributor

In the pull request @bmorris3 mentions above I've included the ability to request planet positions by index or string. I've also partially resolved the discrepancies with JPL Horizons.

Requested positions are in agreement(better than 1 arcsec) for geocentric observers, but there's a problem for observers with an EarthLocation specified that I think is to do with the GCRS coordinate transform machinery, but which I have not managed to pin down yet. The differences can be up to 1 degree for the Moon, but is less than an arc minute for other objects.

@mhvk

mhvk commented May 12, 2016

Copy link
Copy Markdown
Contributor

Arrggh, should have commented here rather than in @bmorris3's fork:

@StuartLittlefair - I looked at your improvements and generally they look good! Since @bmorris3 doesn't have time, might it be an idea to open a new PR with his and your commits combined, so one can comment there?

One question on principle: I noticed that you correct the time for light travel time. However, you calculate positions of both the body and the earth at this slightly earlier time; I think you should change the time only for the body in question.

On the final conversion to apparent position for a non-geocentric location: I wonder if the GCRS conversion doesn't somewhere implicitly assume that distances are large..

Finally, I think the routine just getting the barycentric position of the body should be public, as it is very useful.

@StuartLittlefair

Copy link
Copy Markdown
Contributor

Great suggestions as always @mhvk.

If I made a single, new, pull request, how would I make sure that @bmorris got proper credit?

@mhvk

mhvk commented May 12, 2016

Copy link
Copy Markdown
Contributor

Just don't squash all the commits! You presumably already have his branch locally, so what you would need to do is to rebase it on master (maybe git rebase -i upstream/master so you can squash e.g. the last 3 commits here into one, but keep yours separate). And then push it back to your branch on github and do a pull request from there.

@StuartLittlefair

Copy link
Copy Markdown
Contributor

As requested by @mhvk I have created PR #4890 to include @bmorris3 's original code and my modifications therein. That PR should be considered as superseding this one.

@pllim

pllim commented May 13, 2016

Copy link
Copy Markdown
Member

Closing this since development moved to #4890.

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.

8 participants