diff --git a/.appveyor.yml b/.appveyor.yml index 6aa44f7a6..ceff95cf4 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,21 +1,24 @@ build: off environment: - PYTHON_VERSION: 3.7 + PYTHON_VERSION: 3.8 MINICONDA: C:\\Miniconda37-x64 matrix: + - TEST_FILES: tests\test_orbit.py tests\test_orbits.py + ADDL_CONDA_PKGS: astropy astroquery + COMPILE_NOOPENMP: + BUILD_WHEELS: "true" + - TEST_FILES: "tests\\ --ignore=tests\\test_actionAngleTorus.py --ignore=tests\\test_snapshotpotential.py --ignore=tests\\test_qdf.py --ignore=tests\\test_pv2qdf.py --ignore=tests\\test_diskdf.py --ignore=tests\\test_orbit.py --ignore=tests\\test_orbits.py --ignore=tests\\test_streamdf.py --ignore=tests\\test_streamgapdf.py --ignore=tests\\test_evolveddiskdf.py --ignore=tests\\test_quantity.py --ignore=tests\\test_nemo.py --ignore=tests\\test_amuse.py --ignore=tests\\test_coords.py" ADDL_CONDA_PKGS: COMPILE_NOOPENMP: - - - TEST_FILES: tests\test_orbit.py tests\test_orbits.py - ADDL_CONDA_PKGS: astropy astroquery - COMPILE_NOOPENMP: "--no-openmp" + BUILD_WHEELS: "false" - TEST_FILES: tests\test_quantity.py tests\test_coords.py ADDL_CONDA_PKGS: astropy COMPILE_NOOPENMP: "--no-openmp" + BUILD_WHEELS: "false" platform: - x64 @@ -42,3 +45,46 @@ install: test_script: - pytest -v %TEST_FILES% --cov galpy --cov-config .coveragerc_travis --disable-pytest-warnings + +after_test: + # Build wheels for different python versions of BUILD_WHEELS, otherwise done + - ps: | + if ($env:BUILD_WHEELS -eq "false") { + Exit-AppVeyorBuild + } + - conda deactivate + - conda remove --name test-environment --all + # Python 3.8 + - conda create -n py38 python=3.8 numpy scipy matplotlib setuptools pip pytest gsl + - conda activate py38 + - pip install wheel + - set INCLUDE=%CONDA_PREFIX%\Library\include;%INCLUDE% + - set LIB=%CONDA_PREFIX%\Library\lib;%LIB% + - set LIBPATH=%CONDA_PREFIX%\Library\lib;%LIBPATH% + - python setup.py bdist_wheel + - conda deactivate + # Python 3.7 + - conda create -n py37 python=3.7 numpy scipy matplotlib setuptools pip pytest gsl + - conda activate py37 + - conda remove --name py38 --all + - pip install wheel + - set INCLUDE=%CONDA_PREFIX%\Library\include;%INCLUDE% + - set LIB=%CONDA_PREFIX%\Library\lib;%LIB% + - set LIBPATH=%CONDA_PREFIX%\Library\lib;%LIBPATH% + - python setup.py bdist_wheel + - conda deactivate + # Python 3.6 + - conda create -n py36 python=3.6 numpy scipy matplotlib setuptools pip pytest gsl + - conda activate py36 + - conda remove --name py37 --all + - pip install wheel + - set INCLUDE=%CONDA_PREFIX%\Library\include;%INCLUDE% + - set LIB=%CONDA_PREFIX%\Library\lib;%LIB% + - set LIBPATH=%CONDA_PREFIX%\Library\lib;%LIBPATH% + - python setup.py bdist_wheel + - conda deactivate + # Upload as artifacts + - ps: | + if ($env:BUILD_WHEELS -eq "true") { + Get-ChildItem dist\*.whl | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } + } \ No newline at end of file diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..b8e0e862a --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,18 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - pinned + - security +# Label to use when marking an issue as stale +staleLabel: wontfix +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. If the + issue has been resolved since the last activity, please close the issue. + Thank you for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 3e44f1268..91712e6f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,15 @@ -dist: xenial +dist: bionic sudo: false language: python # Working towards support https://numpy.org/neps/nep-0029-deprecation_policy.html python: - - "3.7" + - "3.8" env: #split tests global: - REQUIRES_PYNBODY=false - REQUIRES_ASTROPY=false - REQUIRES_ASTROQUERY=false - - PYTHON_COVREPORTS_VERSION=3.7 # Version for which reports are uploaded + - PYTHON_COVREPORTS_VERSION=3.8 # Version for which reports are uploaded matrix: - TEST_FILES='tests/ --ignore=tests/test_qdf.py --ignore=tests/test_pv2qdf.py --ignore=tests/test_diskdf.py --ignore=tests/test_orbit.py --ignore=tests/test_streamdf.py --ignore=tests/test_streamgapdf.py --ignore=tests/test_evolveddiskdf.py --ignore=tests/test_quantity.py --ignore=tests/test_nemo.py --ignore=tests/test_amuse.py --ignore=tests/test_coords.py --ignore=tests/test_jeans.py --ignore=tests/test_orbits.py' REQUIRES_PYNBODY=true - TEST_FILES='tests/test_quantity.py tests/test_coords.py' REQUIRES_ASTROPY=true # needs to be separate for different config @@ -18,12 +18,14 @@ env: #split tests - TEST_FILES='tests/test_diskdf.py' - TEST_FILES='tests/test_qdf.py tests/test_pv2qdf.py tests/test_streamgapdf.py' - TEST_FILES='tests/test_streamdf.py' -matrix: # only run crucial tests for python 2.7, 3.6 +matrix: # only run crucial tests for python 2.7, 3.6, 3.7 include: - python: "2.7" env: TEST_FILES='tests/test_orbit.py tests/test_orbits.py' REQUIRES_PYNBODY=true REQUIRES_ASTROPY=true REQUIRES_ASTROQUERY=true - python: "3.6" env: TEST_FILES='tests/test_orbit.py tests/test_orbits.py' REQUIRES_PYNBODY=true REQUIRES_ASTROPY=true REQUIRES_ASTROQUERY=true + - python: "3.7" + env: TEST_FILES='tests/test_orbit.py tests/test_orbits.py' REQUIRES_PYNBODY=true REQUIRES_ASTROPY=true REQUIRES_ASTROQUERY=true addons: apt: packages: @@ -49,7 +51,9 @@ before_install: - conda config --set always_yes yes --set changeps1 no - conda update conda - conda config --add channels conda-forge - - conda create -n test-environment python=$TRAVIS_PYTHON_VERSION "numpy>=1.16" scipy matplotlib numexpr setuptools pip "cython>=0.20" pytest +# setuptools >= 45 no longer supports Python 2 + - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then SETUPTOOLS_VERSION="<45"; else SETUPTOOLS_VERSION=""; fi + - conda create -n test-environment python=$TRAVIS_PYTHON_VERSION "numpy>=1.16" scipy matplotlib numexpr "setuptools$SETUPTOOLS_VERSION" pip "cython>=0.20" pytest - source activate test-environment #Switch to conda defaults python, because conda-forge python has issues with gcc compiler similar to https://github.com/conda/conda/issues/6030 - conda install python=$TRAVIS_PYTHON_VERSION -c defaults diff --git a/MANIFEST.in b/MANIFEST.in index 1f4065e14..64f70a5b9 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include README.rst README.dev README.nemo LICENSE HISTORY.txt AUTHORS.txt +include README.md README.dev README.nemo LICENSE HISTORY.txt AUTHORS.txt include gsl-config.bat include galpy/df/data/*.sav include galpy/orbit/named_objects.json diff --git a/README.md b/README.md new file mode 100644 index 000000000..5c362b587 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +

+
+ Galactic Dynamics in python +

+ +[galpy](http://www.galpy.org) is a Python 2 and 3 package for galactic dynamics. It supports orbit integration in a variety of potentials, evaluating and sampling various distribution functions, and the calculation of action-angle coordinates for all static potentials. `galpy` is an [astropy](http://www.astropy.org/) [affiliated package](http://www.astropy.org/affiliated/) and provides full support for astropy’s [Quantity](http://docs.astropy.org/en/stable/api/astropy.units.Quantity.html) framework for variables with units. + +[![image](https://travis-ci.org/jobovy/galpy.svg?branch=master)](http://travis-ci.org/jobovy/galpy) [![image](https://ci.appveyor.com/api/projects/status/wmgs1sq3i7tbtap2/branch/master?svg=true)](https://ci.appveyor.com/project/jobovy/galpy) [![image](https://img.shields.io/coveralls/jobovy/galpy.svg)](https://coveralls.io/r/jobovy/galpy?branch=master) [![image](http://codecov.io/github/jobovy/galpy/coverage.svg?branch=master)](http://codecov.io/github/jobovy/galpy?branch=master) [![image](https://readthedocs.org/projects/galpy/badge/?version=latest)](http://docs.galpy.org/en/latest/) [![image](http://img.shields.io/pypi/v/galpy.svg)](https://pypi.python.org/pypi/galpy/) [![image](https://anaconda.org/conda-forge/galpy/badges/installer/conda.svg)](https://anaconda.org/conda-forge/galpy) [![image](http://img.shields.io/badge/license-New%20BSD-brightgreen.svg)](https://github.com/jobovy/galpy/blob/master/LICENSE) [![image](http://img.shields.io/badge/DOI-10.1088/0067%2D%2D0049/216/2/29-blue.svg)](http://dx.doi.org/10.1088/0067-0049/216/2/29) [![image](http://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat)](http://www.astropy.org/) [![image](https://slackin-galpy.herokuapp.com/badge.svg)](https://galpy.slack.com/) [![image](https://img.shields.io/badge/join-slack-E01563.svg?style=flat&logo=slack&logoWidth=10)](https://slackin-galpy.herokuapp.com) + +AUTHOR +====== + +Jo Bovy - bovy at astro dot utoronto dot ca + +See +[AUTHORS.txt](https://github.com/jobovy/galpy/blob/master/AUTHORS.txt) +for a full list of contributors. + +If you find this code useful in your research, please let me know. **If +you use galpy in a publication, please cite** [Bovy +(2015)](http://adsabs.harvard.edu/abs/2015ApJS..216...29B) **and link to +http://github.com/jobovy/galpy**. See [the acknowledgement documentation +section](http://docs.galpy.org/en/latest/index.html#acknowledging-galpy) +for a more detailed guide to citing parts of the code. Please also send +me a reference to the paper or send a pull request including your paper +in the list of galpy papers on [this +page](http://docs.galpy.org/en/latest/) (this page is at +doc/source/index.rst). Thanks! + +LOOKING FOR HELP? +================= + +The latest documentation can be found +[here](http://docs.galpy.org/en/latest/). You can also join the +[galpy slack community](https://galpy.slack.com/) for any questions +related to `galpy`; join +[here](https://slackin-galpy.herokuapp.com). + +If you find *any* bug in the code, please report these using the [Issue +Tracker](http://github.com/jobovy/galpy/issues) or by joining the [galpy +slack community](https://galpy.slack.com/). + +If you are having issues with the installation of `galpy`, please first +consult the [Installation +FAQ](http://docs.galpy.org/en/latest/installation.html#installation-faq). + +PYTHON VERSIONS AND DEPENDENCIES +================================ + +`galpy` supports both Python 2 and 3. Specifically, galpy supports +Python 2.7 and Python 3.6 and 3.7. It should also work on earlier Python +3.\* versions, but this is not extensively tested on an ongoing basis. +Travis CI builds regularly check support for Python 2.7 and 3.7 (and of +3.6 using a more limited, core set of tests) and Appveyor builds +regularly check support for Python 3.7 on Windows. + +This package requires [Numpy](https://numpy.org/), +[Scipy](http://www.scipy.org/), and +[Matplotlib](http://matplotlib.sourceforge.net/). Certain advanced +features require the GNU Scientific Library +([GSL](http://www.gnu.org/software/gsl/)), with action calculations +requiring version 1.14 or higher. Use of `SnapshotRZPotential` and +`InterpSnapshotRZPotential` requires +[pynbody](https://github.com/pynbody/pynbody). Support for providing +inputs and getting outputs as Quantities with units is provided through +[astropy](http://www.astropy.org/). + +CONTRIBUTING TO GALPY +===================== + +If you are interested in contributing to galpy\'s development, take a +look at [this brief +guide](https://github.com/jobovy/galpy/wiki/Guide-for-new-contributors) +on the wiki. This will hopefully help you get started! + +Some further development notes can be found on the +[wiki](http://github.com/jobovy/galpy/wiki/). This includes a list of +small and larger extensions of galpy that would be useful +[here](http://github.com/jobovy/galpy/wiki/Possible-galpy-extensions) as +well as a longer-term roadmap +[here](http://github.com/jobovy/galpy/wiki/Roadmap). Please let the main +developer know if you need any help contributing! + +DISK DF CORRECTIONS +=================== + +The dehnendf and shudf disk distribution functions can be corrected to +follow the desired surface-mass density and radial-velocity-dispersion +profiles more closely (see +[1999AJ\....118.1201D](http://adsabs.harvard.edu/abs/1999AJ....118.1201D)). +Calculating these corrections is expensive, and a large set of +precalculated corrections can be found +[here](http://github.com/downloads/jobovy/galpy/galpy-dfcorrections.tar.gz) +\[tar.gz archive\]. Install these by downloading them and unpacking them +into the galpy/df/data directory before running the setup.py +installation. E.g.: + + curl -O https://github.s3.amazonaws.com/downloads/jobovy/galpy/galpy-dfcorrections.tar.gz + tar xvzf galpy-dfcorrections.tar.gz -C ./galpy/df/data/ diff --git a/README.rst b/README.rst deleted file mode 100644 index 47cc0b6cb..000000000 --- a/README.rst +++ /dev/null @@ -1,151 +0,0 @@ -galpy -====== - -**Galactic Dynamics in python** - -.. image:: https://travis-ci.org/jobovy/galpy.svg?branch=master - :target: http://travis-ci.org/jobovy/galpy - -.. image:: https://ci.appveyor.com/api/projects/status/wmgs1sq3i7tbtap2/branch/master?svg=true - :target: https://ci.appveyor.com/project/jobovy/galpy - -.. image:: https://img.shields.io/coveralls/jobovy/galpy.svg - :target: https://coveralls.io/r/jobovy/galpy?branch=master - -.. image:: http://codecov.io/github/jobovy/galpy/coverage.svg?branch=master - :target: http://codecov.io/github/jobovy/galpy?branch=master - -.. image:: https://readthedocs.org/projects/galpy/badge/?version=latest - :target: http://galpy.readthedocs.io/en/latest/ - -.. image:: http://img.shields.io/pypi/v/galpy.svg - :target: https://pypi.python.org/pypi/galpy/ - -.. image:: https://anaconda.org/conda-forge/galpy/badges/installer/conda.svg - :target: https://anaconda.org/conda-forge/galpy - -.. image:: http://img.shields.io/badge/license-New%20BSD-brightgreen.svg - :target: https://github.com/jobovy/galpy/blob/master/LICENSE - -.. image:: http://img.shields.io/badge/DOI-10.1088/0067%2D%2D0049/216/2/29-blue.svg - :target: http://dx.doi.org/10.1088/0067-0049/216/2/29 - -.. image:: http://img.shields.io/badge/powered%20by-AstroPy-orange.svg?style=flat - :target: http://www.astropy.org/ - -.. image:: https://slackin-galpy.herokuapp.com/badge.svg - :target: https://galpy.slack.com/ - -.. image:: https://img.shields.io/badge/join-slack-E01563.svg?style=flat&logo=slack&logoWidth=10 - :target: https://slackin-galpy.herokuapp.com - -AUTHOR -------- - -Jo Bovy - bovy at astro dot utoronto dot ca - -See `AUTHORS.txt -`__ for a -full list of contributors. - -If you find this code useful in your research, please let me -know. **If you use galpy in a publication, please cite** `Bovy (2015) -`__ **and link to -http://github.com/jobovy/galpy**. See `the acknowledgement documentation section -`__ -for a more detailed guide to citing parts of the code. Please also -send me a reference to the paper or send a pull request including your -paper in the list of galpy papers on `this page -`__ (this page is at -doc/source/index.rst). Thanks! - - -LOOKING FOR HELP? ------------------ - -The latest documentation can be found `here `__. You can also join the `galpy slack community `__ for any questions related to `galpy`; join `here `__. - -If you find *any* bug in the code, please report these using the `Issue Tracker `__ or by joining the `galpy slack community `__. - -If you are having issues with the installation of ``galpy``, please first consult the `Installation FAQ `__. - -PYTHON VERSIONS AND DEPENDENCIES ---------------------------------- - -``galpy`` supports both Python 2 and 3. Specifically, galpy supports -Python 2.7 and Python 3.6 and 3.7. It should also work on earlier -Python 3.* versions, but this is not extensively tested on an ongoing -basis. Travis CI builds regularly check support for Python 2.7 and 3.7 -(and of 3.6 using a more limited, core set of tests) and Appveyor -builds regularly check support for Python 3.7 on Windows. - -This package requires `Numpy `__, `Scipy -`__, and `Matplotlib -`__. Certain advanced features -require the GNU Scientific Library (`GSL -`__), with action calculations -requiring version 1.14 or higher. Use of ``SnapshotRZPotential`` and -``InterpSnapshotRZPotential`` requires `pynbody -`__. Support for providing inputs -and getting outputs as Quantities with units is provided through -`astropy `__. - -CONTRIBUTING TO GALPY ----------------------- - -If you are interested in contributing to galpy's development, take a look at `this brief guide `__ on the wiki. This will hopefully help you get started! - -Some further development notes can be found on the `wiki -`__. This includes a list of -small and larger extensions of galpy that would be useful `here -`__ as -well as a longer-term roadmap `here -`__. Please let the main -developer know if you need any help contributing! - -DETAILED BUILD, COVERAGE, AND DOCUMENTATION STATUS ---------------------------------------------------- - -**master**: - -.. image:: https://travis-ci.org/jobovy/galpy.svg?branch=master - :target: http://travis-ci.org/jobovy/galpy - -.. image:: https://img.shields.io/coveralls/jobovy/galpy.svg - :target: https://coveralls.io/r/jobovy/galpy?branch=master - -.. image:: http://codecov.io/github/jobovy/galpy/coverage.svg?branch=master - :target: http://codecov.io/github/jobovy/galpy?branch=master - -.. image:: https://readthedocs.org/projects/galpy/badge/?branch=master?version=latest - :target: http://galpy.readthedocs.io/en/master/ - - -**development branch** (if it exists): - -.. image:: https://travis-ci.org/jobovy/galpy.svg?branch=dev - :target: http://travis-ci.org/jobovy/galpy/branches - -.. image:: https://img.shields.io/coveralls/jobovy/galpy.svg?branch=dev - :target: https://coveralls.io/r/jobovy/galpy?branch=dev - -.. image:: http://codecov.io/github/jobovy/galpy/coverage.svg?branch=dev - :target: http://codecov.io/github/jobovy/galpy?branch=dev - -.. image:: https://readthedocs.org/projects/galpy/badge/?branch=master?version=latest - :target: http://galpy.readthedocs.io/en/dev/ - -DISK DF CORRECTIONS --------------------- - -The dehnendf and shudf disk distribution functions can be corrected to -follow the desired surface-mass density and radial-velocity-dispersion -profiles more closely (see `1999AJ....118.1201D -`__). Calculating -these corrections is expensive, and a large set of precalculated -corrections can be found `here -`__ -\[tar.gz archive\]. Install these by downloading them and unpacking them into the galpy/df/data directory before running the setup.py installation. E.g.:: - - curl -O https://github.s3.amazonaws.com/downloads/jobovy/galpy/galpy-dfcorrections.tar.gz - tar xvzf galpy-dfcorrections.tar.gz -C ./galpy/df/data/ diff --git a/RELEASE_CHECKLIST.md b/RELEASE_CHECKLIST.md index 1cbd217eb..1c96cab40 100644 --- a/RELEASE_CHECKLIST.md +++ b/RELEASE_CHECKLIST.md @@ -24,11 +24,11 @@ - [ ] Push to testpypi: ``twine upload -r pypitest dist/*`` and can test with ``pip install -i https://testpypi.python.org/pypi galpy`` -- [ ] Push to pypi: ``twine upload -r pypi dist/*`` +- [ ] Download Windows wheels from AppVeyor from tagged build and put in ``dist/``. -- [ ] Build wheels for different python versions: ``python setup.py bdist_wheel`` and upload with ``twine upload -r pypi dist/*.whl`` +- [ ] Build Mac wheels for different python versions and put in ``dist/``. (note that Linux wheels are not supported) -- [ ] Create wheels for different python versions on other platforms and upload with ``twine upload dist/*.whl`` (note that Linux wheels are not supported) +- [ ] Push to pypi: ``twine upload -r pypi dist/*`` - [ ] Create the new conda builds at conda-forge —> now done automatically by bot, but still need to check that builds run correctly (should start within about half an hour from pushing the new release to PyPI) diff --git a/doc/source/conf.py b/doc/source/conf.py index 2781fe902..7a1ee7d3a 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -12,6 +12,7 @@ # serve to show the default. import sys, os +import datetime # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -22,7 +23,8 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.mathjax','sphinx.ext.ifconfig'] +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.mathjax','sphinx.ext.ifconfig', + 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -38,16 +40,16 @@ # General information about the project. project = u'galpy' -copyright = u'2010 - 2019, Jo Bovy' +copyright = u'2010 - {}, Jo Bovy'.format(datetime.datetime.now().year) # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '2.0' +version = '1.6.0' # The full version, including alpha/beta/rc tags. -release = '2.0.dev' +release = '1.6.0.dev' on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if on_rtd: version= 'v'+version @@ -120,7 +122,7 @@ def setup(app): # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = 'images/logo-small.png' +html_logo = 'images/galpy-logo-small.gif' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 diff --git a/doc/source/images/galpy-logo-small.gif b/doc/source/images/galpy-logo-small.gif new file mode 100644 index 000000000..ea23b7c0d Binary files /dev/null and b/doc/source/images/galpy-logo-small.gif differ diff --git a/doc/source/images/galpy-logo-small.png b/doc/source/images/galpy-logo-small.png new file mode 100644 index 000000000..82593b10f Binary files /dev/null and b/doc/source/images/galpy-logo-small.png differ diff --git a/doc/source/images/orbit-fromname-mwglobsorbits.png b/doc/source/images/orbit-fromname-mwglobsorbits.png new file mode 100644 index 000000000..fa981d3f9 Binary files /dev/null and b/doc/source/images/orbit-fromname-mwglobsorbits.png differ diff --git a/doc/source/images/orbit-fromname-mwsatsorbits.png b/doc/source/images/orbit-fromname-mwsatsorbits.png new file mode 100644 index 000000000..80f652a0f Binary files /dev/null and b/doc/source/images/orbit-fromname-mwsatsorbits.png differ diff --git a/doc/source/images/orbit-fromname-solarsyst.png b/doc/source/images/orbit-fromname-solarsyst.png new file mode 100644 index 000000000..3046cec20 Binary files /dev/null and b/doc/source/images/orbit-fromname-solarsyst.png differ diff --git a/doc/source/index.rst b/doc/source/index.rst index fb03786af..feb39bfac 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -10,13 +10,13 @@ Welcome to galpy's documentation ================================= -galpy is a Python 2 and 3 package for galactic dynamics. It supports -orbit integration in a variety of potentials, evaluating and sampling -various distribution functions, and the calculation of action-angle -coordinates for all static potentials. galpy is an `astropy -`_ `affiliated package -`_ and provides full support for -astropy's `Quantity +`galpy `__ is a Python 2 and 3 package for +galactic dynamics. It supports orbit integration in a variety of +potentials, evaluating and sampling various distribution functions, +and the calculation of action-angle coordinates for all static +potentials. galpy is an `astropy `_ +`affiliated package `_ and +provides full support for astropy's `Quantity `_ framework for variables with units. @@ -547,6 +547,12 @@ The following is a list of publications using ``galpy``; please let me (bovy at #. *Vilnius Photometry and Gaia Astrometry of Melotte 105*, Timothy Banks, Talar Yontan, Selcuk Bilir, & Remziye Canbay (2019) *J. Astron. Astrophys.*, in press (`arXiv/1912.08760 `_) +#. *Evidence for Galactic disc RR~Lyrae stars in the Solar neighbourhood*, Z. Prudil, I. Dékány, E. K. Grebel, & A. Kunder (2020) *Mon. Not. Roy. Astron. Soc.*, in press (`arXiv/2001.02486 `_) + +#. *The Chemical Compositions of Accreted and in situ Galactic Globular Clusters According to SDSS/APOGEE*, Danny Horta, Ricardo P. Schiavon, J. Ted Mackereth, et al. (2020) *Mon. Not. Roy. Astron. Soc.*, submitted (`arXiv/2001.03177 `_) + +#. *Age dating of an early Milky Way merger via asteroseismology of the naked-eye star ν Indi*, William J. Chaplin, Aldo M. Serenelli, Andrea Miglio, et al. (2020) *Nature Astron.*, in press (`arXiv/2001.04653 `_) + Indices and tables ================== diff --git a/doc/source/installation.rst b/doc/source/installation.rst index 595bed6aa..01651bb32 100644 --- a/doc/source/installation.rst +++ b/doc/source/installation.rst @@ -88,6 +88,9 @@ to, for example, install the ``dev`` branch. Installing from source on Windows --------------------------------- +.. TIP:: + You can install a pre-compiled Windows "wheel" of the latest ``master`` version that is automatically built on ``AppVeyor`` for all recent Python versions. Navigate to `the latest master build `__, click on the first job and then on "Artifacts", download the wheel for your version of Python, and install with ``pip install WHEEL_FILE.whl``. + Versions >1.3 should be able to be compiled on Windows systems using the Microsoft Visual Studio C compiler (>= 2015). For this you need to first install the GNU Scientific Library (GSL), for example using Anaconda (:ref:`see below `). Similar to on a UNIX system, you need to set paths to the header and library files where the GSL is located. On Windows, using the CDM commandline, this is done as:: set INCLUDE=%CONDA_PREFIX%\Library\include;%INCLUDE% diff --git a/doc/source/orbit.rst b/doc/source/orbit.rst index 84af37a80..10f1e2d37 100644 --- a/doc/source/orbit.rst +++ b/doc/source/orbit.rst @@ -305,7 +305,7 @@ orbits from the name of an object. For example, for the star `Lacaille 8760 >> [o.ra(), o.dec(), o.dist(), o.pmra(), o.pmdec(), o.vlos()] # [319.31362023999276, -38.86736390000036, 0.003970940656277758, -3258.5529999996584, -1145.3959999996205, 20.560000000006063] -but this also works for some globular clusters, e.g., to obtain `Omega Cen `__'s orbit and current location in the Milky Way do: +but this also works for globular clusters, e.g., to obtain `Omega Cen `__'s orbit and current location in the Milky Way do: >>> o= Orbit.from_name('Omega Cen') >>> from galpy.potential import MWPotential2014 @@ -317,7 +317,7 @@ but this also works for some globular clusters, e.g., to obtain `Omega Cen `__ catalog): + +>>> o= Orbit.from_name('MW globular clusters') +>>> print(len(o)) +# 150 +>>> print(o.name) +# ['NGC5286', 'Terzan12', 'Arp2', 'NGC5024', ... ] +>>> print(o.r()) +# [ 8.86999028 3.33270877 21.42173795 18.41411889, ...] + +It is then easy to, for example, integrate the orbits of all Milky-Way globular clusters in ``MWPotential2014`` and plot them in 3D: + +>>> ts= numpy.linspace(0.,300.,1001) +>>> o.integrate(ts,MWPotential2014) +>>> o.plot3d(alpha=0.4) +>>> xlim(-100.,100.) +>>> ylim(-100.,100) +>>> gca().set_zlim3d(-100.,100); + +.. image:: images/orbit-fromname-mwglobsorbits.png + :scale: 65 % + +Similarly, 'MW satellite galaxies' loads all of the Milky-Way satellite galaxies from `Fritz et al. (2018) `__: + +>>> o= Orbit.from_name('MW satellite galaxies') +>>> print(len(o)) +# 40 +>>> print(o.name) +# ['AquariusII', 'BootesI', 'BootesII', 'CanesVenaticiI', 'CanesVenaticiII', 'Carina', + 'CarinaII', 'CarinaIII', 'ComaBerenices', 'CraterII', 'Draco', 'DracoII', 'EridanusII', + 'Fornax', 'GrusI', 'Hercules', 'HorologiumI', 'HydraII', 'HydrusI', 'LeoI', 'LeoII', + 'LeoIV', 'LeoV', 'LMC', 'PhoenixI', 'PiscesII', 'ReticulumII', 'Sgr', 'Sculptor', + 'Segue1', 'Segue2', 'SMC', 'Sextans', 'TriangulumII', 'TucanaII', 'TucanaIII', + 'UrsaMajorI', 'UrsaMajorII', 'UrsaMinor', 'Willman1'] +>>> print(o.r()) +# [105.11517882 64.02897066 39.72074174 210.66938806 160.60529059 + 105.36807259 37.01725917 28.92738515 43.43084545 111.15279646 + 79.06498854 23.70062857 364.87901007 141.29780146 116.28700298 + 128.81345239 83.47228672 147.90512269 25.68800918 272.93146472 + 227.39435987 154.7574384 173.77516411 49.60813235 418.76813979 + 181.9540996 32.92030664 19.06561359 84.81046251 27.67609888 + 42.13122436 60.28760354 88.86197382 34.58139798 53.79147743 + 21.05413655 101.85224099 40.70060809 77.72601419 42.65853777] kpc + +and we can integrate and plot them in 3D as above: + +>>> o.plot3d(alpha=0.4) +>>> xlim(-400.,400.) +>>> ylim(-400.,400) +>>> gca().set_zlim3d(-400.,400) + +.. image:: images/orbit-fromname-mwsatsorbits.png + :scale: 65 % + +Because ``MWPotential2014`` has a relatively low-mass dark-matter halo, a bunch of the satellites are unbound (to make them bound, you can increase the mass of the halo by, for example, multiplying it by 1.5, as in ``MWPotential2014[2]*= 1.5``). + +Finally, for illustrative purposes, the solar system is included as a +collection as well. The solar system is set up such that the center of +what is normally the Galactocentric coordinate frame in ``galpy`` is +now the solar system barycenter and the coordinate frame is a +heliocentric one. The solar system data are taken from `Bovy et +al. (2010) `__ +and they represent the positions and planets on April 1, 2009. To load +the solar system do: + +>>> o= Orbit.from_name('solar system') + +Giving for example: + +>>> print(o.name) +# ['Mercury', 'Venus', 'Earth', 'Mars', 'Jupiter', 'Saturn', 'Uranus', 'Neptune'] + +You can then, for example, integrate the solar system for 10 years as follows + +>>> import astropy.units as u +>>> from galpy.potential import KeplerPotential +>>> from galpy.util.bovy_conversion import get_physical +>>> kp= KeplerPotential(amp=1.*u.Msun,**get_physical(o)) # Need to use **get_physical to get the ro= and vo= parameters, which differ from the default for the solar system +>>> ts= numpy.linspace(0.,10.,1001)*u.yr +>>> o.integrate(ts,kp) +>>> o.plot(d1='x',d2='y') + +which gives + +.. image:: images/orbit-fromname-solarsyst.png + :scale: 75 % + +Note that, as usual, physical outputs are in kpc, leading to very small numbers! + .. TIP:: Setting up an ``Orbit`` instance *without* arguments will return an Orbit instance representing the Sun: ``o= Orbit()``. This instance has physical units *turned on by default*, so methods will return outputs in physical units unless you ``o.turn_physical_off()``. diff --git a/galpy/__init__.py b/galpy/__init__.py index ce21d1df9..54dbb3180 100644 --- a/galpy/__init__.py +++ b/galpy/__init__.py @@ -1 +1 @@ -__version__ = "2.0.dev" +__version__ = "1.6.0.dev" diff --git a/galpy/actionAngle/actionAngleAdiabatic_c.py b/galpy/actionAngle/actionAngleAdiabatic_c.py index 48dd6fe0d..8dcff9fd4 100644 --- a/galpy/actionAngle/actionAngleAdiabatic_c.py +++ b/galpy/actionAngle/actionAngleAdiabatic_c.py @@ -16,8 +16,13 @@ else: #pragma: no cover _ext_suffix= '.so' for path in sys.path: + if not os.path.isdir(path): continue try: - _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) + if sys.platform == 'win32' and sys.version_info >= (3,8): # pragma: no cover + # winmode=0x008 is easy-going way to call LoadLibraryExA + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix),winmode=0x008) + else: + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) except OSError as e: if os.path.exists(os.path.join(path,'libgalpy%s' % _ext_suffix)): #pragma: no cover outerr= e diff --git a/galpy/actionAngle/actionAngleStaeckel_c.py b/galpy/actionAngle/actionAngleStaeckel_c.py index 7099576e0..5b6c7f9bd 100644 --- a/galpy/actionAngle/actionAngleStaeckel_c.py +++ b/galpy/actionAngle/actionAngleStaeckel_c.py @@ -17,8 +17,13 @@ else: #pragma: no cover _ext_suffix= '.so' for path in sys.path: + if not os.path.isdir(path): continue try: - _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) + if sys.platform == 'win32' and sys.version_info >= (3,8): # pragma: no cover + # winmode=0x008 is easy-going way to call LoadLibraryExA + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix),winmode=0x008) + else: + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) except OSError as e: if os.path.exists(os.path.join(path,'libgalpy%s' % _ext_suffix)): #pragma: no cover outerr= e diff --git a/galpy/actionAngle/actionAngleTorus_c.py b/galpy/actionAngle/actionAngleTorus_c.py index da09c7604..761ecf398 100644 --- a/galpy/actionAngle/actionAngleTorus_c.py +++ b/galpy/actionAngle/actionAngleTorus_c.py @@ -16,8 +16,13 @@ else: #pragma: no cover _ext_suffix= '.so' for path in sys.path: + if not os.path.isdir(path): continue try: - _lib = ctypes.CDLL(os.path.join(path,'libgalpy_actionAngleTorus%s' % _ext_suffix)) + if sys.platform == 'win32' and sys.version_info >= (3,8): # pragma: no cover + # winmode=0x008 is easy-going way to call LoadLibraryExA + _lib = ctypes.CDLL(os.path.join(path,'libgalpy_actionAngleTorus%s' % _ext_suffix),winmode=0x008) + else: + _lib = ctypes.CDLL(os.path.join(path,'libgalpy_actionAngleTorus%s' % _ext_suffix)) except OSError as e: if os.path.exists(os.path.join(path,'libgalpy_actionAngleTorus%s' % _ext_suffix)): #pragma: no cover outerr= e diff --git a/galpy/df/streamgapdf.py b/galpy/df/streamgapdf.py index 6f6c28e70..baf2ab05d 100644 --- a/galpy/df/streamgapdf.py +++ b/galpy/df/streamgapdf.py @@ -833,7 +833,7 @@ def _determine_impact_coordtransform(self,deltaAngleTrackImpact, 0.) #angle = 0 auxiliaryTrack= Orbit(prog_stream_offset[3]) if dt < 0.: - self._gap_trackts= numpy.linspace(0.,-2.*dt,2.*self._nTrackChunksImpact-1) + self._gap_trackts= numpy.linspace(0.,-2.*dt,2*self._nTrackChunksImpact-1) #Flip velocities before integrating auxiliaryTrack= auxiliaryTrack.flip() auxiliaryTrack.integrate(self._gap_trackts,self._pot) diff --git a/galpy/orbit/Orbits.py b/galpy/orbit/Orbits.py index 4f0a3395d..900a94e6e 100644 --- a/galpy/orbit/Orbits.py +++ b/galpy/orbit/Orbits.py @@ -4233,7 +4233,11 @@ def _call_internal(self,*args,**kwargs): t= args[0] # Parse t, first check whether we are dealing with the common case # where one wants all integrated times + # 2nd line: scalar Quantities have __len__, but raise TypeError + # for scalars t_exact_integration_times= hasattr(t,'__len__') \ + and not (_APY_LOADED and isinstance(t,units.Quantity) + and t.isscalar) \ and (len(t) == len(self.t)) \ and numpy.all(t == self.t) if _APY_LOADED and isinstance(t,units.Quantity): @@ -4250,11 +4254,11 @@ def _call_internal(self,*args,**kwargs): warnings.warn("You specified integration times as a Quantity, but are evaluating at times not specified as a Quantity; assuming that time given is in natural (internal) units (multiply time by unit to get output at physical time)",galpyWarning) if t_exact_integration_times: # Common case where one wants all integrated times return self.orbit.T - elif isinstance(t,(int,float)) and hasattr(self,'t') \ + elif isinstance(t,(int,float,numpy.number)) and hasattr(self,'t') \ and t in list(self.t): return numpy.array(self.orbit[:,list(self.t).index(t),:]).T else: - if isinstance(t,(int,float)): + if isinstance(t,(int,float,numpy.number)): nt= 1 t= numpy.atleast_1d(t) else: @@ -4586,15 +4590,21 @@ def plot(self,*args,**kwargs): kwargs.pop('pot',None) kwargs.pop('OmegaP',None) kwargs.pop('quantity',None) + auto_scale= not 'xrange' in kwargs and not 'yrange' in kwargs \ + and not kwargs.get('overplot',False) + labels= kwargs.pop('label',['Orbit {}'.format(ii+1) + for ii in range(self.size)]) + if self.size == 1 and isinstance(labels,str): labels= [labels] #Plot if not 'xlabel' in kwargs: kwargs['xlabel']= labeldict.get(d1,r'${}$'.format(d1)) if not 'ylabel' in kwargs: kwargs['ylabel']= labeldict.get(d2,r'${}$'.format(d2)) - for tx,ty in zip(x,y): + for ii,(tx,ty) in enumerate(zip(x,y)): + kwargs['label']= labels[ii] line2d= plot.bovy_plot(tx,ty,*args,**kwargs)[0] kwargs['overplot']= True - line2d.axes.autoscale(enable=True) + if auto_scale: line2d.axes.autoscale(enable=True) plot._add_ticks() return [line2d] @@ -4717,6 +4727,8 @@ def plot3d(self,*args,**kwargs): kwargs.pop('obs',None) kwargs.pop('use_physical',None) kwargs.pop('quantity',None) + auto_scale= not 'xrange' in kwargs and not 'yrange' in kwargs \ + and not 'zrange' in kwargs and not kwargs.get('overplot',False) #Plot if not 'xlabel' in kwargs: kwargs['xlabel']= labeldict.get(d1,r'${}$'.format(d1)) @@ -4727,7 +4739,7 @@ def plot3d(self,*args,**kwargs): for tx,ty,tz in zip(x,y,z): line3d= plot.bovy_plot3d(tx,ty,tz,*args,**kwargs)[0] kwargs['overplot']= True - line3d.axes.autoscale(enable=True) + if auto_scale: line3d.axes.autoscale(enable=True) plot._add_ticks() return [line3d] @@ -4757,6 +4769,8 @@ def animate(self,*args,**kwargs): #pragma: no cover json_filename= (None) if set, save the data necessary for the figure in this filename (e.g., json_filename= 'orbit_data/orbit.json'); this path is also used in the output HTML, so needs to be accessible + staticPlot= (False) if True, create a static plot that doesn't allow zooming, panning, etc. + ro= (Object-wide default) physical scale for distances to use to convert (can be Quantity) vo= (Object-wide default) physical scale for velocities to use to convert (can be Quantity) @@ -4894,10 +4908,9 @@ def animate(self,*args,**kwargs): #pragma: no cover height= kwargs.pop('height',400) load_jslibs= kwargs.pop('load_jslibs',True) if load_jslibs: - load_jslibs_code= """ + load_jslibs_code= """ - + +""".format(json_code=json_code,close_json_code=close_json_code, divid=self.divid,width=width,height=height, - button_margin_left=button_margin_left, + button_margin_left=button_margin_left,config=config, layout=layout,load_jslibs_code=load_jslibs_code, + x_data_list=x_data_list, y_data_list=y_data_list, + trace_num_10_list=trace_num_10_list, trace_num_20_list=trace_num_20_list, setup_trace1=setup_trace1,setup_trace2=setup_trace2, - setup_trace3=setup_trace3,delete_trace2=delete_trace2, - delete_trace4=delete_trace4,delete_trace6=delete_trace6, - delete_trace1=delete_trace1,delete_trace3=delete_trace3, - delete_trace5=delete_trace5, - update_trace12=update_trace12, - update_trace34=update_trace34, - update_trace56=update_trace56)) + setup_trace3=setup_trace3, trace_num_list= [ii for ii in range(self.size * len(d1s))])) class _1DInterp(object): """Class to simulate 2D interpolation when using a single orbit""" diff --git a/galpy/orbit/integrateFullOrbit.py b/galpy/orbit/integrateFullOrbit.py index fb674423b..f5c546a14 100644 --- a/galpy/orbit/integrateFullOrbit.py +++ b/galpy/orbit/integrateFullOrbit.py @@ -24,8 +24,13 @@ else: #pragma: no cover _ext_suffix= '.so' for path in sys.path: + if not os.path.isdir(path): continue try: - _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) + if sys.platform == 'win32' and sys.version_info >= (3,8): # pragma: no cover + # winmode=0x008 is easy-going way to call LoadLibraryExA + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix),winmode=0x008) + else: + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) except OSError as e: if os.path.exists(os.path.join(path,'libgalpy%s' % _ext_suffix)): #pragma: no cover outerr= e diff --git a/galpy/orbit/integrateLinearOrbit.py b/galpy/orbit/integrateLinearOrbit.py index 4fa80c43a..0031325b2 100644 --- a/galpy/orbit/integrateLinearOrbit.py +++ b/galpy/orbit/integrateLinearOrbit.py @@ -25,8 +25,13 @@ else: #pragma: no cover _ext_suffix= '.so' for path in sys.path: + if not os.path.isdir(path): continue try: - _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) + if sys.platform == 'win32' and sys.version_info >= (3,8): # pragma: no cover + # winmode=0x008 is easy-going way to call LoadLibraryExA + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix),winmode=0x008) + else: + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) except OSError as e: if os.path.exists(os.path.join(path,'libgalpy%s' % _ext_suffix)): #pragma: no cover outerr= e diff --git a/galpy/orbit/integratePlanarOrbit.py b/galpy/orbit/integratePlanarOrbit.py index 6c85e349b..93d88ead2 100644 --- a/galpy/orbit/integratePlanarOrbit.py +++ b/galpy/orbit/integratePlanarOrbit.py @@ -26,8 +26,13 @@ else: #pragma: no cover _ext_suffix= '.so' for path in sys.path: + if not os.path.isdir(path): continue try: - _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) + if sys.platform == 'win32' and sys.version_info >= (3,8): # pragma: no cover + # winmode=0x008 is easy-going way to call LoadLibraryExA + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix),winmode=0x8) + else: + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) except OSError as e: if os.path.exists(os.path.join(path,'libgalpy%s' % _ext_suffix)): #pragma: no cover outerr= e diff --git a/galpy/orbit/named_objects.json b/galpy/orbit/named_objects.json index ad18a68c1..33219b3da 100644 --- a/galpy/orbit/named_objects.json +++ b/galpy/orbit/named_objects.json @@ -1,7 +1,8 @@ { "_collections": { "MWglobularclusters": ["NGC5286","Terzan12","Arp2","NGC5024","NGC6638","Crater","BH261","NGC6553","NGC6749","NGC6528","NGC4372","NGC2808","IC4499","BH229","NGC6642","NGC6779","NGC6541","NGC6441","Pal4","NGC6341","NGC5694","NGC2298","Ton2","NGC6637","NGC6325","NGC4147","NGC6366","Pal7","NGC5986","NGC5927","Terzan1","NGC4833","Pal8","NGC7078","NGC6517","NGC6284","Pal14","NGC6539","NGC7089","NGC5272","NGC362","NGC6144","NGC6287","E3","NGC6205","NGC6402","FSR1735","Pal3","NGC6256","NGC6342","Djorg2","NGC6093","NGC5139","Terzan5","NGC6333","NGC6934","NGC6101","NGC6171","NGC5466","Pal5","ESO45211","NGC6266","Pal15","Pal13","Terzan2","NGC6540","Terzan4","BH184","NGC5053","NGC6723","FSR1716","BH176","NGC6809","NGC5897","NGC6496","NGC6715","NGC6388","Pal2","NGC1261","NGC6362","Whiting1","NGC6522","NGC6254","NGC6535","NGC6440","NGC6316","NGC5634","NGC7492","Terzan9","NGC6352","Terzan7","Terzan6","NGC6235","NGC5904","NGC6626","IC1257","NGC5946","NGC6717","NGC288","NGC2419","Terzan10","NGC6218","Pal6","NGC6426","NGC6304","NGC6273","NGC6544","NGC6624","NGC6356","Pal12","Djorg1","NGC6293","NGC7006","NGC104","Pal11","NGC5824","Terzan3","NGC6584","NGC3201","Rup106","NGC6838","NGC7099","NGC6229","NGC6139","Pyxis","NGC6121","NGC6681","NGC6652","NGC1904","NGC1851","NGC6397","Terzan8","NGC6569","NGC6981","NGC6401","NGC6760","NGC6380","NGC6355","NGC4590","Pal1","NGC6656","ESO28006","NGC6558","Pal10","E1","NGC6712","NGC6752","NGC6453","NGC6864","Eridanus"], - "MWsatellitegalaxies": ["AquariusII", "BootesI", "BootesII", "CanesVenaticiI", "CanesVenaticiII", "Carina", "CarinaII", "CarinaIII", "ComaBerenices", "CraterII", "Draco", "DracoII", "EridanusII", "Fornax", "GrusI", "Hercules", "HorologiumI", "HydraII", "HydrusI", "LeoI", "LeoII", "LeoIV", "LeoV", "LMC", "PhoenixI", "PiscesII", "ReticulumII", "Sgr", "Sculptor", "Segue1", "Segue2", "SMC", "Sextans", "TriangulumII", "TucanaII", "TucanaIII", "UrsaMajorI", "UrsaMajorII", "UrsaMinor", "Willman1"] + "MWsatellitegalaxies": ["AquariusII", "BootesI", "BootesII", "CanesVenaticiI", "CanesVenaticiII", "Carina", "CarinaII", "CarinaIII", "ComaBerenices", "CraterII", "Draco", "DracoII", "EridanusII", "Fornax", "GrusI", "Hercules", "HorologiumI", "HydraII", "HydrusI", "LeoI", "LeoII", "LeoIV", "LeoV", "LMC", "PhoenixI", "PiscesII", "ReticulumII", "Sgr", "Sculptor", "Segue1", "Segue2", "SMC", "Sextans", "TriangulumII", "TucanaII", "TucanaIII", "UrsaMajorI", "UrsaMajorII", "UrsaMinor", "Willman1"], + "solarsystem": ["Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"] }, "_synonyms": { "OmegaCen": "NGC5139", @@ -1779,5 +1780,109 @@ "pmdec": -2.273000, "vlos": -176.700000, "source": "arXiv:1807.09775" + }, + "Mercury": + { "ra": 272.53694669839313746706, + "dec": -23.26470710760313309606, + "distance": 0.00000000330782629431, + "pmra": 114048573.5889339447021484, + "pmdec": 1113250730.9213943481445312, + "vlos": 24.09379083329670834246, + "ro": 0.00000000484813681113, + "vo": 30.0, + "zo": 0.0, + "solarmotion": [0.0,0.0,0.0], + "source": "arXiv:0903.5308" + }, + "Venus": + { "ra": 261.59112990672980458839, + "dec": -33.02216800854332490189, + "distance": 0.00000000829181155636, + "pmra": -899463102.5785070657730103, + "pmdec": -1377271614.5834431648254395, + "vlos": -1.82400772039945135994, + "ro": 0.00000000484813681113, + "vo": 30.0, + "zo": 0.0, + "solarmotion": [0.0,0.0,0.0], + "source": "arXiv:0903.5308" + }, + "Earth": + { "ra": 262.96412147067331943617, + "dec": -33.59314845931364601483, + "distance": 0.00000000965631218281, + "pmra": -712425950.9163029193878174, + "pmdec": -1089092311.4748961925506592, + "vlos": 0.37735889881938400325, + "ro": 0.00000000484813681113, + "vo": 30.0, + "zo": 0.0, + "solarmotion": [0.0,0.0,0.0], + "source": "arXiv:0903.5308" + }, + "Mars": + { "ra": 127.89885319623307680104, + "dec": -44.74750104086731994357, + "distance": 0.00000000404254311334, + "pmra": -412616788.4622230529785156, + "pmdec": 620326241.2420622110366821, + "vlos": 10.39932018289293402802, + "ro": 0.00000000484813681113, + "vo": 30.0, + "zo": 0.0, + "solarmotion": [0.0,0.0,0.0], + "source": "arXiv:0903.5308" + }, + "Jupiter": + { "ra": 115.15711593351926467221, + "dec": -24.04306151473348052150, + "distance": 0.00000002182038447526, + "pmra": 10122386.4076392110437155, + "pmdec": -17153149.6331202201545238, + "vlos": 23.07793974607058729021, + "ro": 0.00000000484813681113, + "vo": 30.0, + "zo": 0.0, + "solarmotion": [0.0,0.0,0.0], + "source": "arXiv:0903.5308" + }, + "Saturn": + { "ra": 270.18477455320834224040, + "dec": -19.44459530285559978324, + "distance": 0.00000005032138429360, + "pmra": -83101997.5844055265188217, + "pmdec": -142355056.1714278459548950, + "vlos": -4.48093156668851833047, + "ro": 0.00000000484813681113, + "vo": 30.0, + "zo": 0.0, + "solarmotion": [0.0,0.0,0.0], + "source": "arXiv:0903.5308" + }, + "Uranus": + { "ra": 89.97246477323895419431, + "dec": 21.90938146499203753592, + "distance": 0.00000009262040283203, + "pmra": 26453654.0211135037243366, + "pmdec": -45851111.8113014400005341, + "vlos": 3.96038409978304484227, + "ro": 0.00000000484813681113, + "vo": 30.0, + "zo": 0.0, + "solarmotion": [0.0,0.0,0.0], + "source": "arXiv:0903.5308" + }, + "Neptune": + { "ra": 104.39324539298492311445, + "dec": -3.84039762053889033311, + "distance": 0.00000014167718752406, + "pmra": 12522815.9167627468705177, + "pmdec": -24543456.2938976883888245, + "vlos": 17.91906269190807421410, + "ro": 0.00000000484813681113, + "vo": 30.0, + "zo": 0.0, + "solarmotion": [0.0,0.0,0.0], + "source": "arXiv:0903.5308" } } diff --git a/galpy/potential/interpRZPotential.py b/galpy/potential/interpRZPotential.py index 182124800..ee6d165d5 100644 --- a/galpy/potential/interpRZPotential.py +++ b/galpy/potential/interpRZPotential.py @@ -22,8 +22,13 @@ else: #pragma: no cover _ext_suffix= '.so' for path in sys.path: + if not os.path.isdir(path): continue try: - _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) + if sys.platform == 'win32' and sys.version_info >= (3,8): # pragma: no cover + # winmode=0x008 is easy-going way to call LoadLibraryExA + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix),winmode=0x008) + else: + _lib = ctypes.CDLL(os.path.join(path,'libgalpy%s' % _ext_suffix)) except OSError as e: if os.path.exists(os.path.join(path,'libgalpy%s' % _ext_suffix)): #pragma: no cover outerr= e diff --git a/galpy/util/bovy_rk.c b/galpy/util/bovy_rk.c index d635286d1..180d30623 100644 --- a/galpy/util/bovy_rk.c +++ b/galpy/util/bovy_rk.c @@ -104,7 +104,12 @@ void bovy_rk4(void (*func)(double t, double *q, double *a, if ( interrupted ) { *err= -10; interrupted= 0; // need to reset, bc library and vars stay in memory +#ifdef USING_COVERAGE; + __gcov_flush(); +#endif +// LCOV_EXCL_START break; +// LCOV_EXCL_STOP } for (jj=0; jj < (ndt-1); jj++) { bovy_rk4_onestep(func,dim,yn,yn1,to,dt,nargs,potentialArgs,ynk,a); @@ -208,7 +213,12 @@ void bovy_rk6(void (*func)(double t, double *q, double *a, if ( interrupted ) { *err= -10; interrupted= 0; // need to reset, bc library and vars stay in memory +#ifdef USING_COVERAGE; + __gcov_flush(); +#endif +// LCOV_EXCL_START break; +// LCOV_EXCL_STOP } for (jj=0; jj < (ndt-1); jj++) { bovy_rk6_onestep(func,dim,yn,yn1,to,dt,nargs,potentialArgs,ynk,a, @@ -534,7 +544,12 @@ void bovy_dopr54(void (*func)(double t, double *q, double *a, if ( interrupted ) { *err= -10; interrupted= 0; // need to reset, bc library and vars stay in memory +#ifdef USING_COVERAGE; + __gcov_flush(); +#endif +// LCOV_EXCL_START break; +// LCOV_EXCL_STOP } bovy_dopr54_onestep(func,dim,yn,dt,&to,&dt_one, nargs,potentialArgs,rtol,atol, diff --git a/galpy/util/bovy_symplecticode.c b/galpy/util/bovy_symplecticode.c index e781c2c9a..75a7ac2ba 100644 --- a/galpy/util/bovy_symplecticode.c +++ b/galpy/util/bovy_symplecticode.c @@ -146,7 +146,12 @@ void leapfrog(void (*func)(double t, double *q, double *a, if ( interrupted ) { *err= -10; interrupted= 0; // need to reset, bc library and vars stay in memory +#ifdef USING_COVERAGE; + __gcov_flush(); +#endif +// LCOV_EXCL_START break; +// LCOV_EXCL_STOP } //drift half leapfrog_leapq(dim,qo,po,dt/2.,q12); @@ -266,7 +271,12 @@ void symplec4(void (*func)(double t, double *q, double *a, if ( interrupted ) { *err= -10; interrupted= 0; // need to reset, bc library and vars stay in memory +#ifdef USING_COVERAGE; + __gcov_flush(); +#endif +// LCOV_EXCL_START break; +// LCOV_EXCL_STOP } //drift for c1*dt leapfrog_leapq(dim,qo,po,c1*dt,q12); @@ -421,7 +431,12 @@ void symplec6(void (*func)(double t, double *q, double *a, if ( interrupted ) { *err= -10; interrupted= 0; // need to reset, bc library and vars stay in memory +#ifdef USING_COVERAGE; + __gcov_flush(); +#endif +// LCOV_EXCL_START break; +// LCOV_EXCL_STOP } //drift for c1*dt leapfrog_leapq(dim,qo,po,c1*dt,q12); diff --git a/galpy/util/leung_dop853.c b/galpy/util/leung_dop853.c index 0fca859ce..02caa9a3e 100644 --- a/galpy/util/leung_dop853.c +++ b/galpy/util/leung_dop853.c @@ -337,7 +337,12 @@ void dop853(void(*func)(double t, double *q, double *a, int nargs, struct potent if (interrupted) { *err_ = -10; interrupted = 0; // need to reset, bc library and vars stay in memory +#ifdef USING_COVERAGE; + __gcov_flush(); +#endif +// LCOV_EXCL_START break; +// LCOV_EXCL_STOP } h = pos_neg * max(fabs(h), 1e3 * uround); // keep time step not too small @@ -508,4 +513,4 @@ void dop853(void(*func)(double t, double *q, double *a, int nargs, struct potent free(rcont2); free(rcont1); //We're done -} \ No newline at end of file +} diff --git a/setup.py b/setup.py index 11d235253..72bf01a6f 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ long_description= '' previous_line= '' -with open('README.rst') as dfile: +with open('README.md') as dfile: for line in dfile: if not 'image' in line and not 'target' in line \ and not 'DETAILED' in line and not '**master**' in line \ @@ -57,7 +57,7 @@ extra_link_args= [] else: del sys.argv[coverage_pos] - extra_compile_args.extend(["-O0","--coverage"]) + extra_compile_args.extend(["-O0","--coverage","-D USING_COVERAGE"]) extra_link_args= ["--coverage"] #Option to compile everything into a single extension @@ -207,12 +207,13 @@ actionAngleTorus_c_incl= False setup(name='galpy', - version='2.0.dev', + version='1.6.0.dev', description='Galactic Dynamics in python', author='Jo Bovy', author_email='bovy@astro.utoronto.ca', license='New BSD', long_description=long_description, + long_description_content_type='text/markdown', url='http://github.com/jobovy/galpy', package_dir = {'galpy/': ''}, packages=['galpy','galpy/orbit','galpy/potential', @@ -220,7 +221,7 @@ 'galpy/actionAngle'], package_data={'galpy/orbit':['named_objects.json'], 'galpy/df':['data/*.sav'], - "": ["README.rst","README.dev","LICENSE","AUTHORS.rst"]}, + "": ["README.md","README.dev","LICENSE","AUTHORS.rst"]}, include_package_data=True, install_requires=['numpy>=1.7','scipy','matplotlib','pytest','six', 'future','setuptools'], diff --git a/tests/orbitint4sigint.py b/tests/orbitint4sigint.py index a50e3a71e..a106b94c3 100644 --- a/tests/orbitint4sigint.py +++ b/tests/orbitint4sigint.py @@ -12,8 +12,12 @@ o= Orbit([1.,0.1,1.1,0.1,0.1,0.]) elif sys.argv[2] == 'planar': o= Orbit([1.,0.1,1.1,0.1]) + print("Starting long C integration ...") + sys.stdout.flush() o.integrate(ts,mp,method=sys.argv[1]) elif sys.argv[2] == 'planardxdv': o= Orbit([1.,0.1,1.1,0.1]) + print("Starting long C integration ...") + sys.stdout.flush() o.integrate_dxdv([0.1,0.1,0.1,0.1],ts,mp,method=sys.argv[1]) sys.exit(0) diff --git a/tests/test_coords.py b/tests/test_coords.py index 8599b91c8..eee250759 100644 --- a/tests/test_coords.py +++ b/tests/test_coords.py @@ -474,7 +474,7 @@ def test_lbd_to_galcenrect_galpyvsastropy(): # galpy is left-handed, astropy right-handed assert numpy.fabs(gcXYZ[0]+c.x.to(u.kpc).value) < 10.**-10., "lbd to galcenrect conversion using galpy's methods does not agree with astropy" assert numpy.fabs(gcXYZ[1]-c.y.to(u.kpc).value) < 10.**-10., "lbd to galcenrect conversion using galpy's methods does not agree with astropy" - assert numpy.fabs(gcXYZ[2]-c.z.to(u.kpc).value) < 10.**-10., "lbd to galcenrect conversion using galpy's methods does not agree with astropy" + assert numpy.fabs(gcXYZ[2]-c.z.to(u.kpc).value) < 10.**-9.5, "lbd to galcenrect conversion using galpy's methods does not agree with astropy" # Also with negative Xsun l,b,d= 32., -12., 3. Zsun= 0.025 @@ -489,7 +489,7 @@ def test_lbd_to_galcenrect_galpyvsastropy(): # galpy is now right-handed, astropy right-handed assert numpy.fabs(gcXYZ[0]-c.x.to(u.kpc).value) < 10.**-10., "lbd to galcenrect conversion using galpy's methods does not agree with astropy" assert numpy.fabs(gcXYZ[1]-c.y.to(u.kpc).value) < 10.**-10., "lbd to galcenrect conversion using galpy's methods does not agree with astropy" - assert numpy.fabs(gcXYZ[2]-c.z.to(u.kpc).value) < 10.**-10., "lbd to galcenrect conversion using galpy's methods does not agree with astropy" + assert numpy.fabs(gcXYZ[2]-c.z.to(u.kpc).value) < 10.**-9.5, "lbd to galcenrect conversion using galpy's methods does not agree with astropy" _turn_on_apy() return None @@ -512,7 +512,7 @@ def test_lbd_to_galcencyl_galpyvsastropy(): # galpy is left-handed, astropy right-handed assert numpy.fabs(gcRpZ[0]-c.rho.to(u.kpc).value) < 10.**-10., "lbd to galcencyl conversion using galpy's methods does not agree with astropy" assert numpy.fabs(gcRpZ[1]-numpy.pi+c.phi.to(u.rad).value) < 10.**-10., "lbd to galcencyl conversion using galpy's methods does not agree with astropy" - assert numpy.fabs(gcRpZ[2]-c.z.to(u.kpc).value) < 10.**-10., "lbd to galcencyl conversion using galpy's methods does not agree with astropy" + assert numpy.fabs(gcRpZ[2]-c.z.to(u.kpc).value) < 10.**-9.5, "lbd to galcencyl conversion using galpy's methods does not agree with astropy" # Also with negative Xsun l,b,d= 32., -12., 3. Zsun= 0.025 @@ -528,7 +528,7 @@ def test_lbd_to_galcencyl_galpyvsastropy(): # galpy is now right-handed, astropy right-handed assert numpy.fabs(gcRpZ[0]-c.rho.to(u.kpc).value) < 10.**-10., "lbd to galcencyl conversion using galpy's methods does not agree with astropy" assert numpy.fabs(gcRpZ[1]-c.phi.to(u.rad).value) < 10.**-10., "lbd to galcencyl conversion using galpy's methods does not agree with astropy" - assert numpy.fabs(gcRpZ[2]-c.z.to(u.kpc).value) < 10.**-10., "lbd to galcencyl conversion using galpy's methods does not agree with astropy" + assert numpy.fabs(gcRpZ[2]-c.z.to(u.kpc).value) < 10.**-9.5, "lbd to galcencyl conversion using galpy's methods does not agree with astropy" _turn_on_apy() return None diff --git a/tests/test_orbit.py b/tests/test_orbit.py index b4a3ac72a..fb3d73586 100644 --- a/tests/test_orbit.py +++ b/tests/test_orbit.py @@ -3900,9 +3900,12 @@ def test_orbit_c_sigint_full(): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - time.sleep(4) + for line in iter(p.stdout.readline, b''): + if line.startswith(b"Starting long C integration ..."): + break + time.sleep(2) os.kill(p.pid,signal.SIGINT) - time.sleep(4) + time.sleep(1) cnt= 0 while p.poll() is None and cnt < ntries: # wait a little longer time.sleep(4) @@ -3910,7 +3913,7 @@ def test_orbit_c_sigint_full(): if p.poll() == 2 and WIN32: break - if p.poll() is None or p.poll() != 1: + if p.poll() is None or (p.poll() != 1 and p.poll() != -2): if p.poll() is None: msg= -100 else: msg= p.poll() raise AssertionError("Full orbit integration using %s should have been interrupted by SIGINT (CTRL-C), but was not because p.poll() == %i" % (integrator,msg)) @@ -3935,9 +3938,12 @@ def test_orbit_c_sigint_planar(): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - time.sleep(4) + for line in iter(p.stdout.readline, b''): + if line.startswith(b"Starting long C integration ..."): + break + time.sleep(2) os.kill(p.pid,signal.SIGINT) - time.sleep(4) + time.sleep(1) cnt= 0 while p.poll() is None and cnt < ntries: # wait a little longer time.sleep(4) @@ -3945,7 +3951,7 @@ def test_orbit_c_sigint_planar(): if p.poll() == 2 and WIN32: break - if p.poll() is None or p.poll() != 1: + if p.poll() is None or (p.poll() != 1 and p.poll() != -2): if p.poll() is None: msg= -100 else: msg= p.poll() raise AssertionError("Full orbit integration using %s should have been interrupted by SIGINT (CTRL-C), but was not because p.poll() == %i" % (integrator,msg)) @@ -3966,9 +3972,12 @@ def test_orbit_c_sigint_planardxdv(): stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - time.sleep(4) + for line in iter(p.stdout.readline, b''): + if line.startswith(b"Starting long C integration ..."): + break + time.sleep(2) os.kill(p.pid,signal.SIGINT) - time.sleep(4) + time.sleep(1) cnt= 0 while p.poll() is None and cnt < ntries: # wait a little longer time.sleep(4) @@ -3976,7 +3985,7 @@ def test_orbit_c_sigint_planardxdv(): if p.poll() == 2 and WIN32: break - if p.poll() is None or p.poll() != 1: + if p.poll() is None or (p.poll() != 1 and p.poll() != -2): if p.poll() is None: msg= -100 else: msg= p.poll() raise AssertionError("Full orbit integration using %s should have been interrupted by SIGINT (CTRL-C), but was not because p.poll() == %i" % (integrator,msg)) @@ -4779,6 +4788,29 @@ def test_from_name_collections(): named_data[individual_obj][attr]) return None +def test_from_name_solarsystem(): + # Test that the solar system matches Bovy et al. (2010)'s input data + from astropy import units + from galpy.orbit import Orbit + correct_xyz= numpy.array( + [[0.324190175,0.090955208,-0.022920510,-4.627851589,10.390063716,1.273504997], + [-0.701534590,-0.168809218,0.037947785,1.725066954,-7.205747212,-0.198268558], + [-0.982564148,-0.191145980,-0.000014724,1.126784520,-6.187988860,0.000330572], + [1.104185888,-0.826097003,-0.044595990,3.260215854,4.524583075,0.014760239], + [3.266443877,-3.888055863,-0.057015321,2.076140727,1.904040630,-0.054374153], + [-9.218802228,1.788299816,0.335737817,-0.496457364,-2.005021061,0.054667082], + [19.930781147,-2.555241579,-0.267710968,0.172224285,1.357933443,0.002836325], + [24.323085642,-17.606227355,-0.197974999,0.664855006,0.935497207,-0.034716967]]) + os= Orbit.from_name('solar system') + for (ii,o) in enumerate(os): + assert numpy.fabs((o.x()*units.kpc).to(units.AU).value-correct_xyz[ii,0]) < 1e-8, "Orbit.from_name('solar system') does not agree with Bovy et al. (2010) data" + assert numpy.fabs((o.y()*units.kpc).to(units.AU).value-correct_xyz[ii,1]) < 1e-8, "Orbit.from_name('solar system') does not agree with Bovy et al. (2010) data" + assert numpy.fabs((o.z()*units.kpc).to(units.AU).value-correct_xyz[ii,2]) < 1e-8, "Orbit.from_name('solar system') does not agree with Bovy et al. (2010) data" + assert numpy.fabs((o.vx()*units.km/units.s).to(units.AU/units.yr).value-correct_xyz[ii,3]) < 1e-8, "Orbit.from_name('solar system') does not agree with Bovy et al. (2010) data" + assert numpy.fabs((o.vy()*units.km/units.s).to(units.AU/units.yr).value-correct_xyz[ii,4]) < 1e-8, "Orbit.from_name('solar system') does not agree with Bovy et al. (2010) data" + assert numpy.fabs((o.vz()*units.km/units.s).to(units.AU/units.yr).value-correct_xyz[ii,5]) < 1e-8, "Orbit.from_name('solar system') does not agree with Bovy et al. (2010) data" + return None + def test_rguiding_errors(): from galpy.potential import TriaxialNFWPotential from galpy.orbit import Orbit @@ -4906,6 +4938,43 @@ def test_SkyCoord_init_with_radecisTrue(): assert numpy.fabs(o_sky.vlos()-o_radec.vlos()) < 1e-8, 'Orbit setup with SkyCoord and lb=True does not agree with Orbit setup directly with lb' return None +# Test related to issue #415: calling an Orbit with a single int time does not +# work properly +# Test from @jamesmlane +def test_orbit_call_single_time_as_int(): + from galpy import potential, orbit + pot = potential.MWPotential2014 + o= orbit.Orbit() + times = numpy.array([0,1,2]) + o.integrate(times,pot) + # Make sure this does not raise TypeErrpr + try: + o.x(times[0]) + except TypeError: + raise + # Test that the value makes sense + assert numpy.fabs(o.x(times[0])-o.x()) < 1e-10 + return None + +# Test related to issue #415: calling an Orbit with a single Quantity time +# does not work properly +# Test from @jamesmlane +def test_orbit_call_single_time_as_Quantity(): + from galpy import potential, orbit + from astropy import units as u + pot = potential.MWPotential2014 + o= orbit.Orbit() + times = numpy.array([0,1,2])*u.Gyr + o.integrate(times,pot) + # Make sure this does not raise TypeErrpr + try: + o.x(times[0]) + except TypeError: + raise + # Test that the value makes sense + assert numpy.fabs(o.x(times[0])-o.x()) < 1e-10 + return None + # Setup the orbit for the energy test def setup_orbit_energy(tp,axi=False,henon=False): # Need to treat Henon sep. here, bc cannot be scaled to be reasonable