├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── joss-compile.yml │ ├── python-package.yml │ └── run-notebooks.yml ├── .gitignore ├── .readthedocs.yaml ├── LICENSE ├── MANIFEST.in ├── README.md ├── contributor_guidelines.md ├── contributors.txt ├── docs ├── Makefile ├── api.rst ├── basis.rst ├── conf.py ├── contributing.rst ├── driver.rst ├── faq.rst ├── faq │ ├── Orientation_Of_Orbit.ipynb │ └── Time_Of_Periastron.ipynb ├── favicon.ico ├── formatting_inputs.rst ├── gaia.rst ├── hipparcos.rst ├── index.rst ├── installation.rst ├── kepler.rst ├── lnlike.rst ├── make.bat ├── manual.rst ├── nbody.rst ├── orbitize_logo_500.png ├── plot.rst ├── priors.rst ├── read_input.rst ├── requirements.txt ├── results.rst ├── sampler.rst ├── system.rst ├── tutorials.rst └── tutorials │ ├── Changing_bases_tutorial.ipynb │ ├── HGCA_tutorial.ipynb │ ├── Hipparcos_IAD.ipynb │ ├── MCMC_tutorial.ipynb │ ├── MCMC_vs_OFTI.ipynb │ ├── Modifying_MCMC_initial_positions.ipynb │ ├── Modifying_Priors.ipynb │ ├── Multiplanet_Tutorial.ipynb │ ├── OFTI_tutorial.ipynb │ ├── ONeil-ObsPriors.ipynb │ ├── Plotting_tutorial.ipynb │ ├── Quick-Start.ipynb │ ├── RV_MCMC_Tutorial.ipynb │ ├── Using_nonOrbitize_Posteriors_as_Priors.ipynb │ ├── abs_astrometry.ipynb │ ├── dynesty_tutorial.ipynb │ ├── eift_hd206893.png │ └── show-me-the-orbit.ipynb ├── orbitize ├── __init__.py ├── _kepler.pyx ├── basis.py ├── driver.py ├── example_data │ ├── GJ504.csv │ ├── GJ504_1epoch.csv │ ├── H000025.d │ ├── H000026.d │ ├── H027321.d │ ├── H027989.d │ ├── HD4747.csv │ ├── HIP027321.d │ ├── HR7672_joint.csv │ ├── betaPic.csv │ ├── gaia_edr3_betpic_epochs.csv │ ├── hd206893b.csv │ ├── old_orbitize_format.csv │ ├── sample_radvel_chains.csv.bz2 │ ├── test_val.csv │ ├── test_val_cov.csv │ ├── test_val_multi.csv │ ├── test_val_radec.csv │ ├── test_val_rv.csv │ ├── v1_posterior.hdf5 │ └── xyz_test_data.csv ├── gaia.py ├── gpu_context.py ├── hipparcos.py ├── kepler.c ├── kepler.py ├── kernels │ ├── mikkola.cu │ └── newton.cu ├── lnlike.py ├── nbody.py ├── plot.py ├── priors.py ├── radvel_utils │ └── compute_sep.py ├── read_input.py ├── results.py ├── sampler.py └── system.py ├── paper ├── paper.bib └── paper.md ├── requirements.txt ├── setup.py └── tests ├── __init__.py ├── conftest.py ├── end-to-end-tests ├── betaPic_hipIAD.py ├── hr8799e_1epochgravity.csv ├── hr8799e_only.py ├── nested_test.py └── rv_end_to_end.py ├── test_OFTI.py ├── test_abs_astrometry.py ├── test_additional_plotting.py ├── test_api.py ├── test_basis_conversions.py ├── test_basis_with_system.py ├── test_checkpriorsupport.py ├── test_chi2_lnlike.py ├── test_chop_chains_with_plotting.py ├── test_conversions.py ├── test_driver.py ├── test_gaia.py ├── test_hipparcos.py ├── test_kde_and_ndinterpolator.py ├── test_kepler_solver.py ├── test_mcmc.py ├── test_mcmc_rv.py ├── test_multiplanet.py ├── test_multiplanet_rebound.py ├── test_nested_sampler.py ├── test_priors.py ├── test_read_input.py ├── test_rebound.py ├── test_results.py ├── test_rv_plotting.py ├── test_secondary_rvs.py └── test_system.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Linux] 28 | - orbitize! Version [e.g. 2.0.1] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/joss-compile.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | jobs: 4 | paper: 5 | runs-on: ubuntu-latest 6 | name: Paper Draft 7 | steps: 8 | - name: Checkout 9 | uses: actions/checkout@v4 10 | - name: Build draft PDF 11 | uses: openjournals/openjournals-draft-action@master 12 | with: 13 | journal: joss 14 | # This should be the path to the paper within your repo. 15 | paper-path: paper/paper.md 16 | - name: Upload 17 | uses: actions/upload-artifact@v1 18 | with: 19 | name: paper 20 | # This is the output path where Pandoc will write the compiled 21 | # PDF. Note, this should be the same directory as the input 22 | # paper.md 23 | path: paper/paper.pdf 24 | -------------------------------------------------------------------------------- /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | 3 | name: CI tests 4 | 5 | on: 6 | push: 7 | branches: 8 | - '*' # run on all 9 | pull_request: 10 | branches: 11 | - '*' 12 | 13 | jobs: 14 | build: 15 | 16 | runs-on: macos-latest 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | python-version: ["3.10", "3.11", "3.12"] 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v3 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | python -m pip install flake8 pytest pytest-cov 32 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 33 | - name: Lint with flake8 34 | run: | 35 | # stop the build if there are Python syntax errors or undefined names 36 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 37 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide 38 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics 39 | - name: Test with pytest 40 | run: | 41 | pytest --cov 42 | - name: Coveralls GitHub Action 43 | if: ${{ matrix.python-version }} == '3.12' 44 | uses: coverallsapp/github-action@v2 45 | with: 46 | parallel: true 47 | flag-name: run-$ 48 | 49 | -------------------------------------------------------------------------------- /.github/workflows/run-notebooks.yml: -------------------------------------------------------------------------------- 1 | name: Test jupyter notebook tutorials 2 | 3 | on: 4 | push: 5 | branches: 6 | - main # only rerun tutorials when making PRs or changing main 7 | - v3 8 | pull_request: 9 | branches: 10 | - '*' 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: macos-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | python-version: ["3.12"] 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v3 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install jupyter setuptools wheel nbmake 31 | python -m pip install -r requirements.txt 32 | python -m pip install . --no-build-isolation 33 | - name: Run tutorial notebooks 34 | run: | 35 | py.test --nbmake --nbmake-timeout=3000 docs/tutorials/*.ipynb 36 | 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Added by orbitize! Developers 2 | orbitize/example_data/rebound*.csv 3 | orbitize/example_data/*test.hdf5 4 | .vscode/launch.json 5 | .vscode/settings.json 6 | *.DS_Store 7 | 8 | # images & storage files generated by tutorials 9 | *.hdf5 10 | !orbitize/example_data/v1_posterior.hdf5 11 | *.fits 12 | *.png 13 | !docs/tutorials/eift_hd206893.png 14 | tests/test_results.h5 15 | tests/multiplanet*test.csv 16 | 17 | # Byte-compiled / optimized / DLL files 18 | __pycache__/ 19 | *.py[cod] 20 | *$py.class 21 | 22 | # C extensions 23 | *.so 24 | 25 | # Cython generated files 26 | _kepler.c 27 | 28 | # Pytest cache 29 | .pytest_cache/ 30 | 31 | # Distribution / packaging 32 | .Python 33 | env/ 34 | build/ 35 | develop-eggs/ 36 | dist/ 37 | downloads/ 38 | eggs/ 39 | .eggs/ 40 | lib/ 41 | lib64/ 42 | parts/ 43 | sdist/ 44 | var/ 45 | wheels/ 46 | *.egg-info/ 47 | .installed.cfg 48 | *.egg 49 | 50 | # PyInstaller 51 | # Usually these files are written by a python script from a template 52 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 53 | *.manifest 54 | *.spec 55 | 56 | # Installer logs 57 | pip-log.txt 58 | pip-delete-this-directory.txt 59 | 60 | # Unit test / coverage reports 61 | htmlcov/ 62 | .tox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | nosetests.xml 67 | coverage.xml 68 | *.cover 69 | .hypothesis/ 70 | Profile.prof 71 | 72 | # Translations 73 | *.mo 74 | *.pot 75 | 76 | # Django stuff: 77 | *.log 78 | local_settings.py 79 | 80 | # Flask stuff: 81 | instance/ 82 | .webassets-cache 83 | 84 | # Scrapy stuff: 85 | .scrapy 86 | 87 | # PyBuilder 88 | target/ 89 | 90 | # Jupyter Notebook 91 | .ipynb_checkpoints 92 | 93 | # pyenv 94 | .python-version 95 | 96 | # celery beat schedule file 97 | celerybeat-schedule 98 | 99 | # SageMath parsed files 100 | *.sage.py 101 | 102 | # dotenv 103 | .env 104 | 105 | # virtualenv 106 | .venv 107 | venv/ 108 | ENV/ 109 | 110 | # Spyder project settings 111 | .spyderproject 112 | .spyproject 113 | 114 | # Rope project settings 115 | .ropeproject 116 | 117 | # mkdocs documentation 118 | /site 119 | 120 | # mypy 121 | .mypy_cache/ 122 | 123 | # utility scripts 124 | build.sh 125 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.10" 7 | 8 | sphinx: 9 | configuration: docs/conf.py 10 | 11 | formats: 12 | - pdf 13 | - epub 14 | 15 | python: 16 | install: 17 | - requirements: requirements.txt # orbitize requirements 18 | - requirements: docs/requirements.txt # docs-building requirements 19 | - method: pip 20 | path: . # install orbitize 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Sarah Blunt, Jason Wang, Isabel Angelo, Henry Ngo, 4 | Devin Cody, Logan Pearce, Rob De Rosa, and Eric Nielsen. 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | global-include orbitize/*.pyx 2 | global-include orbitize/*.c 3 | include requirements.txt 4 | include orbitize/example_data/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

orbitize!

2 | 3 | # orbitize! 4 | 5 | Orbit-fitting for directly imaged objects. For installation instructions, tutorials, and detailed documentation, start [here](http://orbitize.readthedocs.io/en/latest/). 6 | 7 | ![Build Status](https://github.com/sblunt/orbitize/actions/workflows/python-package.yml/badge.svg) 8 | [![Coverage Status](https://coveralls.io/repos/github/sblunt/orbitize/badge.svg?branch=main)](https://coveralls.io/github/sblunt/orbitize?branch=main) 9 | [![Documentation Status](https://readthedocs.org/projects/orbitize/badge/?version=latest)](http://orbitize.readthedocs.io/en/latest/?badge=latest) 10 | [![PyPI version](https://badge.fury.io/py/orbitize.svg)](https://badge.fury.io/py/orbitize) 11 | [![PyPI downloads](https://img.shields.io/pypi/dm/orbitize.svg)](https://pypistats.org/packages/orbitize) 12 | 13 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5432124.svg)](https://doi.org/10.5281/zenodo.5432124) 14 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) 15 | -------------------------------------------------------------------------------- /contributor_guidelines.md: -------------------------------------------------------------------------------- 1 | This document should grow and change with our code base. Please revise & add anything you think is relevant! 2 | 3 | # Things You Should Do Before Contributing: 4 | 5 | - Learn about [object-oriented programming](http://www.voidspace.org.uk/python/articles/OOP.shtml). 6 | - Learn about [Markov Chain Monte Carlo algorithms](https://jeremykun.com/2015/04/06/markov-chain-monte-carlo-without-all-the-bullshit/). Ask Jason when you have questions. 7 | - Learn about [Orbits for the Impatient (OFTI)](http://adsabs.harvard.edu/abs/2017AJ....153..229B). Ask Sarah when you have questions. 8 | - Learn about [Git and GitHub](https://product.hubspot.com/blog/git-and-github-tutorial-for-beginners) 9 | - Read our [community agreement](https://docs.google.com/document/d/1ZzjkoB20vVTlg2wbNpS7sRjmcSrECdh8kQ11-waZQhw/edit) and (optionally) suggest changes. 10 | 11 | 12 | # Best Practices for Contributing: 13 | 14 | - Code according to [PEP 8 style standards](https://www.python.org/dev/peps/pep-0008/). When in doubt, check the guide! 15 | - Apply object-oriented principles. Abstract out common functionality whenever possible! 16 | - Document all your code using [doc strings](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) 17 | - Put your name on all functions you write in the doc strings. Update contributors.txt periodically so it accurately summarizes your role in the project. 18 | - Write unit tests for your code (see "Unit Testing Instructions" below). 19 | - Don’t edit the `main` branch directly. Create a new branch and put all of your code there. When you’re happy with your changes, create a pull request, and assign at least Sarah and Jason to review it. When everyone is happy with it, either Sarah or Jason will pull in your changes. 20 | - Each feature should get its own branch to keep things modular. (e.g., don't have a branch like 'Jason-changes' that is a bunch of things all at once). The person assigned to that feature is the lead of the branch. 21 | - Ask for permission from the lead of a branch before contributing to that branch. Helping with a branch is nice, but do ask for permission first since they probably have a picture of what they want to do already. 22 | - Every time you make changes, include a descriptive commit message. The more detail, the better! 23 | 24 | # Unit Testing Instructions: 25 | 26 | When you add some code to `orbitize/orbitize`, you should also add some functions to `orbitize/tests` (in your development branch, NOT the `main` branch). Any test function you write must be named `test_*` (see existing tests for examples). You can either create a new file for your test function(s) or add them to an existing `orbitize/tests/test_*` file. 27 | 28 | `orbitize` uses a service called Travis Continuous Integration (CI) to run all properly named tests in `orbitize/tests` every time someone adds some commits or creates a pull request. Check out `orbitize`'s current Travis CI status [here](https://travis-ci.org/sblunt/orbitize). 29 | 30 | If you're interested in learning more about how Travis CI tests all of our code, check out their [site](https://docs.travis-ci.com/user/getting-started/)! 31 | 32 | You should test as much of your code as possible (ideally all of it). To check how many lines of code in `orbitize/orbitize` are currently being tested by our unit tests, check out our [coveralls site](https://coveralls.io/github/sblunt/orbitize). 33 | 34 | # Notes: 35 | 36 | - Naming practices: 37 | - modules = all lowercase 38 | - Classes = named with first letter uppercase 39 | - multi-word function names = lowercase with underscores 40 | 41 | - Releasing a new version: 42 | - Pull all new code to main 43 | - Increment version in \_\_init\_\_ 44 | - Update docs changelog (remember to give credit to contributors) 45 | - GitHub release 46 | - Upload new code to PyPi 47 | - Check that new docs are being built 48 | 49 | -------------------------------------------------------------------------------- /contributors.txt: -------------------------------------------------------------------------------- 1 | Sarah Blunt -- team leader/organizer; collaboratively designed basic API; wrote basic API; collaboratively developed OFTI sampler; designed orbit plot 2 | 3 | Jason Wang -- team leader/organizer; collaboratively designed basic API; collaboratively developed kepler.py; wrote MCMC sampler 4 | 5 | Henry Ngo -- wrote code to read inputs, save/load results, and make basic plots; collaboratively developed kepler.py and results.py 6 | 7 | Rob De Rosa -- collaboratively developed kepler.py 8 | 9 | Devin Cody -- wrote C implementation of kepler module 10 | 11 | Isabel Angelo -- led development of OFTI sampler and tutorial 12 | 13 | Logan Pearce -- assisted with OFTI development and provided test data 14 | 15 | Vighnesh Nagpal -- wrote parallel processing OFTI implementation 16 | 17 | Malena Rice -- wrote code to customize colorbar in orbit plots 18 | 19 | James Graham -- wrote orbit-solving code that kepler.py inherits from 20 | 21 | Lea Hirsch -- advising RT on development of RV/astrometry joint fitting code 22 | 23 | Eric Nielsen -- came up with idea for OFTI algorithm; advised SB on development of OFTI code that sampler.py inherits from 24 | 25 | Roberto Tejada -- developing RV/astrometry joint fitting code 26 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = orbitize 8 | SOURCEDIR = . 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | Detailed API Documentation 4 | ========================== 5 | 6 | .. toctree:: 7 | :titlesonly: 8 | 9 | basis 10 | driver 11 | gaia 12 | hipparcos 13 | kepler 14 | lnlike 15 | nbody 16 | plot 17 | priors 18 | read_input 19 | results 20 | sampler 21 | system -------------------------------------------------------------------------------- /docs/basis.rst: -------------------------------------------------------------------------------- 1 | .. _basis: 2 | 3 | Basis 4 | ===== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.basis 8 | :members: -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import orbitize 4 | 5 | # -*- coding: utf-8 -*- 6 | # 7 | # orbitize documentation build configuration file, created by 8 | # sphinx-quickstart on Wed Jan 17 17:19:48 2018. 9 | # 10 | # This file is execfile()d with the current directory set to its 11 | # containing dir. 12 | # 13 | # Note that not all possible configuration values are present in this 14 | # autogenerated file. 15 | # 16 | # All configuration values have a default; values that are commented out 17 | # serve to show the default. 18 | 19 | # If extensions (or modules to document with autodoc) are in another directory, 20 | # add these directories to sys.path here. If the directory is relative to the 21 | # documentation root, use os.path.abspath to make it absolute, like shown here. 22 | # 23 | import os 24 | import sys 25 | 26 | sys.path.insert( 27 | 0, os.path.abspath("./../orbitize") 28 | ) # location of orbitize files with docstrings 29 | 30 | 31 | # -- General configuration ------------------------------------------------ 32 | 33 | # If your documentation needs a minimal Sphinx version, state it here. 34 | # 35 | # needs_sphinx = '1.0' 36 | 37 | # Add any Sphinx extension module names here, as strings. They can be 38 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 39 | # ones. 40 | extensions = [ 41 | "sphinx_rtd_theme", 42 | "sphinx.ext.autodoc", 43 | "sphinx.ext.doctest", 44 | "sphinx.ext.todo", 45 | "sphinx.ext.mathjax", 46 | "sphinx.ext.napoleon", # allows Google style-guide docs to render more prettily 47 | "sphinx.ext.viewcode", 48 | "nbsphinx", 49 | ] 50 | 51 | # Disable notebook timeout 52 | nbsphinx_timeout = -1 53 | 54 | # Only re-run notebooks that have no outputs 55 | nbsphinx_execute = "auto" 56 | 57 | # Allow notebook errors 58 | nbsphinx_allow_errors = False 59 | 60 | # Add any paths that contain templates here, relative to this directory. 61 | templates_path = ["_templates"] 62 | 63 | # The suffix(es) of source filenames. 64 | # You can specify multiple suffix as a list of string: 65 | # 66 | # source_suffix = ['.rst', '.md'] 67 | source_suffix = ".rst" 68 | 69 | # The master toctree document. 70 | master_doc = "index" 71 | 72 | # General information about the project. 73 | project = "orbitize" 74 | copyright = "2018, Sarah Blunt, Jason Wang, Isabel Angelo, Henry Ngo, et al" 75 | author = "Sarah Blunt, Jason Wang, Isabel Angelo, Henry Ngo, et al" 76 | 77 | # The version info for the project you're documenting, acts as replacement for 78 | # |version| and |release|, also used in various other places throughout the 79 | # built documents. 80 | # 81 | # The short X.Y version. 82 | version = orbitize.__version__ 83 | # The full version, including alpha/beta/rc tags. 84 | # release = '0.1' 85 | 86 | # The language for content autogenerated by Sphinx. Refer to documentation 87 | # for a list of supported languages. 88 | # 89 | # This is also used if you do content translation via gettext catalogs. 90 | # Usually you set "language" from the command line for these cases. 91 | language = None 92 | 93 | # List of patterns, relative to source directory, that match files and 94 | # directories to ignore when looking for source files. 95 | # This patterns also effect to html_static_path and html_extra_path 96 | exclude_patterns = [] 97 | 98 | # The name of the Pygments (syntax highlighting) style to use. 99 | pygments_style = "sphinx" 100 | 101 | # If true, `todo` and `todoList` produce output, else they produce nothing. 102 | todo_include_todos = True 103 | 104 | 105 | # -- Options for HTML output ---------------------------------------------- 106 | 107 | # The theme to use for HTML and HTML Help pages. See the documentation for 108 | # a list of builtin themes. 109 | # 110 | html_theme = "sphinx_rtd_theme" 111 | 112 | # Theme options are theme-specific and customize the look and feel of a theme 113 | # further. For a list of options available for each theme, see the 114 | # documentation. 115 | # 116 | # html_theme_options = {} 117 | 118 | # The name of an image file (relative to this directory) to place at the top 119 | # of the sidebar. 120 | # html_logo = 'orbitize_logo_150.png' 121 | 122 | # The name of an image file (within the static path) to use as favicon of the 123 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 124 | # pixels large. 125 | html_favicon = "favicon.ico" 126 | 127 | # Add any paths that contain custom static files (such as style sheets) here, 128 | # relative to this directory. They are copied after the builtin static files, 129 | # so a file named "default.css" will overwrite the builtin "default.css". 130 | html_static_path = [] 131 | 132 | # Custom sidebar templates, must be a dictionary that maps document names 133 | # to template names. 134 | # 135 | # This is required for the alabaster theme 136 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 137 | html_sidebars = { 138 | "**": [ 139 | "relations.html", # needs 'show_related': True theme option to display 140 | "searchbox.html", 141 | ] 142 | } 143 | 144 | 145 | # -- Options for HTMLHelp output ------------------------------------------ 146 | 147 | # Output file base name for HTML help builder. 148 | htmlhelp_basename = "orbitizedoc" 149 | 150 | 151 | # -- Options for LaTeX output --------------------------------------------- 152 | 153 | latex_elements = { 154 | # The paper size ('letterpaper' or 'a4paper'). 155 | # 156 | # 'papersize': 'letterpaper', 157 | # The font size ('10pt', '11pt' or '12pt'). 158 | # 159 | # 'pointsize': '10pt', 160 | # Additional stuff for the LaTeX preamble. 161 | # 162 | # 'preamble': '', 163 | # Latex figure (float) alignment 164 | # 165 | # 'figure_align': 'htbp', 166 | } 167 | 168 | # Grouping the document tree into LaTeX files. List of tuples 169 | # (source start file, target name, title, 170 | # author, documentclass [howto, manual, or own class]). 171 | latex_documents = [ 172 | ( 173 | master_doc, 174 | "orbitize.tex", 175 | "orbitize Documentation", 176 | "Sarah Blunt, Jason Wang, Henry Ngo, et al.", 177 | "manual", 178 | ), 179 | ] 180 | 181 | 182 | # -- Options for manual page output --------------------------------------- 183 | 184 | # One entry per manual page. List of tuples 185 | # (source start file, name, description, authors, manual section). 186 | man_pages = [(master_doc, "orbitize", "orbitize Documentation", [author], 1)] 187 | 188 | 189 | # -- Options for Texinfo output ------------------------------------------- 190 | 191 | # Grouping the document tree into Texinfo files. List of tuples 192 | # (source start file, target name, title, author, 193 | # dir menu entry, description, category) 194 | texinfo_documents = [ 195 | ( 196 | master_doc, 197 | "orbitize", 198 | "orbitize Documentation", 199 | author, 200 | "orbitize", 201 | "One line description of project.", 202 | "Miscellaneous", 203 | ), 204 | ] 205 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | Contributing to the Code 2 | ======================== 3 | 4 | ``orbitize`` is under active development, and we've still got a lot to do! To get involved, 5 | check out our `contributor guidelines `_, 6 | look over our `issues list `_, and/or reach out to 7 | `Sarah `_. We'd love to have 8 | you on our team! 9 | 10 | Members of our team have collectively drafted `this community agreement `_ stating both our values and ground rules. 11 | In joining our team, we ask that you read and (optionally) suggest changes to this document. 12 | -------------------------------------------------------------------------------- /docs/driver.rst: -------------------------------------------------------------------------------- 1 | .. _driver: 2 | 3 | Driver 4 | ====== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.driver 8 | :members: -------------------------------------------------------------------------------- /docs/faq.rst: -------------------------------------------------------------------------------- 1 | .. _faq: 2 | 3 | Frequently Asked Questions 4 | ========================== 5 | 6 | Here are some questions we get often. Please suggest more by raising an 7 | issue in the `Github Issue Tracker `_. 8 | 9 | **What does this orbital parameter mean?** 10 | 11 | We think the best way to understand the orbital parameters is to see how they affect the orbit visually. 12 | Play around with this `interactive orbital elements notebook `_ 13 | (you'll need to run on your machine). 14 | 15 | **What is τ and how is it related to epoch of periastron?** 16 | 17 | We use τ to define the epoch of periastron as we do not know when periastron will be for many of our directly 18 | imaged planets. A detailed description of how τ is related to other quantities such as :math:`t_p` is available: 19 | 20 | .. toctree:: 21 | :maxdepth: 1 22 | 23 | faq/Time_Of_Periastron.ipynb 24 | 25 | 26 | **Why is the default prior on inclination a sine prior?** 27 | 28 | Our goal with the default prior is to have an isotropic distribution of the orbital plane. 29 | To obtain this, we use inclination and position angle of the ascending node (PAN) to 30 | define the orbital plane. They actually coorespond to the two angles in a spherical coordinate system: 31 | inclinaion is the polar angle and PAN is the azimuthal angle. Becuase of this choice of coordinates, 32 | there are less orbital configurations near the poles (when inclination is near 0 or 180), so we use 33 | a sine prior to downweigh those areas to give us an isotropic distribution. 34 | A more detailed discussion of this is here: 35 | 36 | .. toctree:: 37 | :maxdepth: 1 38 | 39 | faq/Orientation_Of_Orbit.ipynb -------------------------------------------------------------------------------- /docs/faq/Time_Of_Periastron.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": { 3 | "language_info": { 4 | "codemirror_mode": { 5 | "name": "ipython", 6 | "version": 3 7 | }, 8 | "file_extension": ".py", 9 | "mimetype": "text/x-python", 10 | "name": "python", 11 | "nbconvert_exporter": "python", 12 | "pygments_lexer": "ipython3", 13 | "version": "3.6.5-final" 14 | }, 15 | "orig_nbformat": 2, 16 | "kernelspec": { 17 | "name": "python3", 18 | "display_name": "Python 3", 19 | "language": "python" 20 | } 21 | }, 22 | "nbformat": 4, 23 | "nbformat_minor": 2, 24 | "cells": [ 25 | { 26 | "source": [ 27 | "# $\\tau$ and Time of Periastron\n", 28 | "\n", 29 | "Here, we will discuss what exactly is $\\tau$, the parameter `orbitize!` uses to parametrize the epoch of periastron, and how it is related to other quantities of the epoch of periastron in the literature. \n", 30 | "\n", 31 | "## Time of Periastron and Motivation for $\\tau$\n", 32 | "\n", 33 | "The time (or epoch) of periastron is an important quantity for describing an orbit. It defines when the two orbiting bodies are closest to one another (i.e., when a planet is closest to its star). In many papers in the literature, the epoch of periastron is described by $t_p$, which is literally a date at which periastron occurs. This is a very important date because we use this date to anchor our orbit in time. \n", 34 | "\n", 35 | "The value of $t_p$ is well constrained when we know we observed periastron, which is often the case for radial velociy or transiting exoplanets when the orbital periods are short and our data covers a full orbital period. In those cases, we know approximately when $t_p$ should be in time, so it is easy to define prior bounds for it. However, in the case of direct imaging, many of our companions have orbital periods that are orders of magnitude larger than the current orbital coverage of the data where we do not really know if the next periastron is in years, decades, centuries, or even millennia. This is the motivation for $\\tau$. \n", 36 | "\n", 37 | "## Definition of $\\tau$\n", 38 | "\n", 39 | "$\\tau$ is a dimentionless quantity between 0 and 1 defined with respect to a reference epoch $t_{ref}$. For a planet that has a $t_p$ and an orbital period (P), then we define $\\tau$ as:\n", 40 | "\n", 41 | "$$\n", 42 | "\\tau = \\frac{t_p - t_{ref}}{P}.\n", 43 | "$$\n", 44 | "\n", 45 | "Because $\\tau$ is always between 0 and 1, it is easy to figure out the bounds of $\\tau$ whereas if the orbital period is highly uncertain, it may be difficult to put bounds on $t_p$ that would encompass all allowable bound orbits. \n", 46 | "\n", 47 | "## Relation to $t_p$\n", 48 | "\n", 49 | "As seen in the above equation, it is relatively straightforward to covert between orbital parameter sets that use $\\tau$ and $t_p$. You just need to know the orbital period and reference epoch. In `orbitize!`, both the `System` class and the `Results` class has the attribute `tau_ref_epoch` which stores $t_{ref}$, so there should always be a convenient way to grab this number. By default, we use $t_{ref} = 58849$ MJD. \n", 50 | "\n", 51 | "One thing to note that is a given orbit has only a single valid $\\tau$, but that an orbit can be defined by many $t_p$, since the orbit is periodic. Thus, $t_p + P$ is another valid time of periastron. \n", 52 | "\n", 53 | "We also provide some helper functions to covert between $t_p$ and $\\tau$" 54 | ], 55 | "cell_type": "markdown", 56 | "metadata": {} 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 1, 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "output_type": "stream", 65 | "name": "stdout", 66 | "text": [ 67 | "60649.50097715886\n0.2000000000000002\n6634.471662393138\n" 68 | ] 69 | } 70 | ], 71 | "source": [ 72 | "import numpy as np\n", 73 | "import orbitize.basis\n", 74 | "\n", 75 | "# How to get orbital period in the orbitize! standard basis\n", 76 | "sma = 9 # au, semi-major axis\n", 77 | "mtot = 1.2 # Solar masses, total mass\n", 78 | "period = np.sqrt(sma**3/mtot) # years, period\n", 79 | "\n", 80 | "tau = 0.2\n", 81 | "tau_ref_epoch = 58849\n", 82 | "\n", 83 | "# convert tau to tp\n", 84 | "tp = orbitize.basis.tau_to_tp(tau, tau_ref_epoch, period)\n", 85 | "\n", 86 | "print(tp)\n", 87 | "\n", 88 | "# convert tp back to tau\n", 89 | "tau2 = orbitize.basis.tp_to_tau(tp, tau_ref_epoch, period)\n", 90 | "\n", 91 | "print(tau2)\n", 92 | "\n", 93 | "# convert tau to tp, but pick the first tp after MJD = 0\n", 94 | "tp_new = orbitize.basis.tau_to_tp(tau, tau_ref_epoch, period, after_date=0)\n", 95 | "\n", 96 | "print(tp_new)" 97 | ] 98 | }, 99 | { 100 | "source": [ 101 | "## Relation to Mean Anomaly\n", 102 | "\n", 103 | "The mean anomaly (M) of an orbit describes the current orbital phase of a planet. M goes from 0 to 2$\\pi$, and M = 0 means the planet is at periastron. Unlike $t_p$ and $\\tau$ which describe the epoch of periastron, M describes the current position of the planet in its orbit. \n", 104 | "\n", 105 | "To compute M of a planet at some time t, we have provided the following helper function:" 106 | ], 107 | "cell_type": "markdown", 108 | "metadata": {} 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": 2, 113 | "metadata": {}, 114 | "outputs": [ 115 | { 116 | "output_type": "stream", 117 | "name": "stdout", 118 | "text": [ 119 | "5.829874251150844\n1.3951473992034527e-15\n" 120 | ] 121 | } 122 | ], 123 | "source": [ 124 | "# Use the orbit defined in the previous example\n", 125 | "\n", 126 | "t = 60000 # time in MJD when we want to know the M of the particle\n", 127 | "\n", 128 | "M = orbitize.basis.tau_to_manom(t, sma, mtot, tau, tau_ref_epoch)\n", 129 | "\n", 130 | "print(M)\n", 131 | "\n", 132 | "# now compute M for periastron\n", 133 | "M_peri = orbitize.basis.tau_to_manom(tp, sma, mtot, tau, tau_ref_epoch)\n", 134 | "\n", 135 | "print(M_peri)" 136 | ] 137 | } 138 | ] 139 | } -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sblunt/orbitize/1f91aac848a84df52c9f0f66b8f4c6156a0f7e08/docs/favicon.ico -------------------------------------------------------------------------------- /docs/formatting_inputs.rst: -------------------------------------------------------------------------------- 1 | .. _formatting_inputs: 2 | 3 | Formatting Input 4 | ++++++++++++++++ 5 | 6 | Use ``orbitize.read_input.read_file()`` to read your astrometric data into orbitize. This method takes one argument, a string to the path of the file containing your input. 7 | 8 | This method can read any file format supported by ``astropy.io.ascii.read()``, including csv format. See the `astropy docs `_. 9 | 10 | There are two ways to provide input data to orbitize, either as observations or as an ``orbitize!``-formatted input table. 11 | 12 | Option 1 13 | -------- 14 | You can provide your observations in one of the following valid sets of measurements using the corresponding column names: 15 | 16 | - RA and DEC offsets [milliarcseconds], using column names ``raoff``, ``raoff_err``, ``decoff``, and ``decoff_err``; or 17 | - sep [milliarcseconds] and PA [degrees East of NCP], using column names ``sep``, ``sep_err``, ``pa``, and ``pa_err``; or 18 | - RV measurement [km/s] using column names ``rv`` and ``rv_err``. 19 | 20 | Each row must also have a column for ``epoch`` and ``object``. Epoch is the date of the observation, in MJD (JD-2400000.5). If this method thinks you have provided a date in JD, it will print a warning and attempt to convert to MJD. Objects are numbered with integers, where the primary/central object is ``0``. 21 | 22 | You may mix and match these three valid measurement formats in the same input file. So, you can have some epochs with RA/DEC offsets and others in separation/PA measurements. 23 | 24 | If you have, for example, one RV measurement of a star and three astrometric 25 | measurements of an orbiting planet, you should put ``0`` in the ``object`` column for the RV point, and ``1`` in the columns for the astrometric measurements. 26 | 27 | This method will look for columns with the above labels in whatever file format you choose so if you encounter errors, be sure to double check the column labels in your input file. 28 | 29 | Putting it all together, here an example of a valid .csv input file:: 30 | 31 | epoch,object,raoff,raoff_err,decoff,decoff_err,radec_corr,sep,sep_err,pa,pa_err,rv,rv_err 32 | 1234,1,0.010,0.005,0.50,0.05,0.025,,,,,, 33 | 1235,1,,,,,,1.0,0.005,89.0,0.1,, 34 | 1236,1,,,,,,1.0,0.005,89.3,0.3,, 35 | 1237,0,,,,,,,,,,10,0.1 36 | 37 | .. Note:: Columns with no data can be omitted (e.g. if only separation and PA 38 | are given, the raoff, deoff, and rv columns can be excluded). 39 | 40 | If more than one valid set is given (e.g. RV measurement and astrometric measurement taken at the same epoch), ``read_file()`` will generate a separate output row for each valid set. 41 | 42 | Whatever file format you choose, this method will read your input into an ``orbitize!``-formatted input table. This is an ``astropy.Table.table`` object that looks like this (for the example input given above):: 43 | 44 | epoch object quant1 quant1_err quant2 quant2_err quant12_corr quant_type 45 | float64 int float64 float64 float64 float64 float64 str5 46 | ------- ------ ------- ---------- ------- ---------- ------------ ---------- 47 | 1234.0 1 0.01 0.005 0.5 0.05 0.025 radec 48 | 1235.0 1 1.0 0.005 89.0 0.1 nan seppa 49 | 1236.0 1 1.0 0.005 89.3 0.3 nan seppa 50 | 1237.0 0 10.0 0.1 nan nan nan rv 51 | 52 | where ``quant_type`` is one of "radec", "seppa", or "rv". 53 | 54 | If ``quant_type`` is "radec" or "seppa", the units of quant are mas and degrees, 55 | if ``quant_type`` is "rv", the units of quant are km/s. 56 | 57 | Covariances 58 | ^^^^^^^^^^^ 59 | For RA/Dec and Sep/PA, you can optionally specify a correlation term. This is useful when your error ellipse 60 | is tilted with respect to the RA/Dec or Sep/PA. The correlation term is the Pearson correlation coefficient (ρ), 61 | which corresponds to the normalized off diagonal term of the covariance matrix (C): 62 | 63 | .. math:: 64 | 65 | C = \begin{bmatrix} 66 | C_{11} & C_{12}\\ 67 | C_{12} & C_{22} 68 | \end{bmatrix}. 69 | 70 | Here C_11 = quant1_err^2 and C_22 = quant2_err^2 71 | and C_12 is the off diagonal term (note that by definition both off-diagonal terms of the covariance matrix are the same). 72 | Then, :math:`\rho = C_{12}/\sqrt{C_{11}C_{22}}`. Essentially it is the covariance 73 | normalized by the variance. As such, -1 ≤ ρ ≤ 1. 74 | You can specify either as radec_corr or seppa_corr to include a correlation in the errors. By definition, 75 | both are dimensionless, but one will correspond to RA/Dec and the other to Sep/PA. If no correlations are specified, it will assume 76 | the errors are uncorrelated (ρ = 0). In many papers, the errors are assumed to be uncorrelated. An example of real world data 77 | that reports correlations is `this GRAVITY paper `_ where table 2 reports the 78 | correlation values and figure 4 shows how the error ellipses are tilted. 79 | 80 | In the example above, we specify the first epoch has a positive correlation between the uncertainties in RA and Dec using the 81 | ``radec_corr`` column in the input data. This gets translated into the ``quant12_corr`` field in ``orbitize!``-format. No 82 | correlations are specified for the other entries, and so we will assume those errors are uncorrelated. 83 | After this is specified, handling of the correlations will be done automatically when computing model likelihoods. 84 | There's nothing else you have to do after this step! 85 | 86 | 87 | Option 2 88 | -------- 89 | Alternatively, you can also supply a data file with the columns already corresponding to the ``orbitize!``-formatted input table (see above for column names). This may be useful if you are wanting to use the output of the ``write_orbitize_input`` method (e.g. using some input prepared by another ``orbitize!`` user). 90 | 91 | .. Note:: When providing data with columns in the orbitize format, there should be 92 | no empty cells. As in the example below, when quant2 is not applicable, the cell should contain nan. 93 | -------------------------------------------------------------------------------- /docs/gaia.rst: -------------------------------------------------------------------------------- 1 | .. _gaia: 2 | 3 | Gaia API Module 4 | =============== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.gaia 8 | :members: -------------------------------------------------------------------------------- /docs/hipparcos.rst: -------------------------------------------------------------------------------- 1 | .. _hipparcos: 2 | 3 | Hipparcos API Module 4 | ==================== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.hipparcos 8 | :members: -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ============ 5 | 6 | For Users 7 | +++++++++ 8 | 9 | Parts of ``orbitize`` are written in C, so you'll need ``gcc`` (a C compiler) to install properly. 10 | Most Linux and Windows computers come with ``gcc`` built in, but Mac computers don't. If you 11 | haven't before, you'll need to download Xcode command line tools. There are several 12 | helpful guides online that teach you how to do this. Let us know if you have trouble! 13 | 14 | ``orbitize`` is registered on ``pip``, and works in Python>3.6. 15 | To install ``orbitize``, first make sure you have the latest versions 16 | of ``numpy`` and ``cython`` installed. With ``pip``, you can do this with 17 | the command: 18 | 19 | .. code-block:: bash 20 | 21 | $ pip install numpy cython --upgrade 22 | 23 | Next, install ``orbitize``: 24 | 25 | .. code-block:: bash 26 | 27 | $ pip install orbitize 28 | 29 | We recommend installing and running ``orbitize`` in a ``conda`` virtual 30 | environment. Install ``anaconda`` or ``miniconda`` 31 | `here `_, then see instructions 32 | `here `_ 33 | to learn more about ``conda`` virtual environments. 34 | 35 | For Windows Users 36 | +++++++++++++++++ 37 | 38 | Many of the packages that we use in ``orbitize`` were originally written for Linux or macOS. 39 | For that reason, we highly recommend installing the 40 | `Windows Subsystem for Linux (WSL) `_ 41 | which is an entire Linux development environment within Windows. See `here `_ 42 | for a handy getting started guide. 43 | 44 | If you don't want to use WSL, there are a few extra steps you'll need to follow 45 | to get ``orbitize`` running: 46 | 47 | 1. There is a bug with the ``ptemcee`` installation that, as far as we know, only affects Windows users. 48 | To work around this, download ``ptemcee`` from `its pypi page `_. 49 | Navigate to the root ``ptemcee`` folder, remove the ``README.md`` file, then install: 50 | 51 | .. code-block:: bash 52 | 53 | $ cd ptemcee 54 | $ rm README.md 55 | $ pip install . --upgrade 56 | 57 | 2. Some users have reported issues with installing ``curses``. If this happens to you, you can install 58 | ``windows-curses`` which should work as a replacement. 59 | 60 | .. code-block:: bash 61 | 62 | $ pip install windows-curses 63 | 64 | 3. Finally, ``rebound`` is not compatible with windows, so you'll need to git clone 65 | orbitize, remove rebound from orbitize/requirements.txt, then install from 66 | the command line. 67 | 68 | .. code-block:: bash 69 | 70 | $ git clone https://github.com/sblunt/orbitize.git 71 | $ cd orbitize 72 | 73 | Open up orbitize/requirements.txt, remove ``rebound``, and save. 74 | 75 | .. code-block:: bash 76 | 77 | $ pip install . --upgrade 78 | 79 | 80 | For Developers 81 | ++++++++++++++ 82 | 83 | ``orbitize`` is actively being developed. The following method for 84 | installing ``orbitize`` will allow you to use it and make changes to it. 85 | After cloning the Git repository, run the following command in the top level 86 | of the repo: 87 | 88 | .. code-block:: bash 89 | 90 | $ pip install -r requirements.txt 91 | $ pip install -e . --upgrade 92 | 93 | Issues? 94 | +++++++ 95 | 96 | If you run into any issues installing ``orbitize``, please create an issue on GitHub. 97 | 98 | If you are specifically having difficulties using ``cython`` to install ``orbitize``, we 99 | suggest first trying to install ``wheel``, then installing all of the ``orbitize`` dependencies (listed in 100 | ``requirements.txt``). 101 | 102 | If that doesn't work, we suggest disabling compilation of the C-based Kepler module with 103 | the following alternative installation command: 104 | 105 | .. code-block:: bash 106 | 107 | $ pip install orbitize --install-option="--disable-cython" 108 | 109 | 110 | -------------------------------------------------------------------------------- /docs/kepler.rst: -------------------------------------------------------------------------------- 1 | .. _kepler: 2 | 3 | Kepler Solver 4 | ============= 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.kepler 8 | :members: -------------------------------------------------------------------------------- /docs/lnlike.rst: -------------------------------------------------------------------------------- 1 | .. _lnlike: 2 | 3 | Log(Likelihood) 4 | ===================== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.lnlike 8 | :members: -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=build 12 | set SPHINXPROJ=orbitize 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/manual.rst: -------------------------------------------------------------------------------- 1 | .. _manual: 2 | 3 | orbitize! Manual 4 | ============== 5 | 6 | Intro to ``orbitize!`` 7 | +++++++++++++++++ 8 | 9 | ``orbitize!`` hinges on the two-body problem, which describes the paths of two 10 | bodies gravitationally bound to each other as a function of time, 11 | given parameters determining the position and velocity of both objects at a particular epoch. 12 | There are many basis sets (orbital bases) that can be used to describe an orbit, 13 | which can then be solved using Kepler’s equation, but first it is important to be explicit about coordinate systems. 14 | 15 | .. Note:: 16 | For an interactive visualization to define and help users understand our coordinate system, 17 | you can check out `this GitHub tutorial `_. 18 | 19 | There is also a `YouTube video `_ 20 | with use and explanation of the coordinate system by Sarah Blunt. 21 | 22 | In its “standard” mode, ``orbitize!`` assumes that the user only has relative astrometric data to fit. 23 | In the ``orbitize!`` coordinate system, relative R.A. and declination can be expressed as the following functions 24 | of orbital parameters 25 | 26 | .. math:: 27 | \Delta R.A. = \pi a(1-ecosE)[cos^2{i\over 2}sin(f+\omega_p+\Omega)-sin^2{i\over 2}sin(f+\omega_p-\Omega)] 28 | 29 | \Delta decl. = \pi a(1-ecosE)[cos^2{i\over 2}cos(f+\omega_p+\Omega)+sin^2{i\over 2}cos(f+\omega_p-\Omega)] 30 | 31 | where 𝑎, 𝑒, :math:`\omega_p`, Ω, and 𝑖 are orbital parameters, and 𝜋 is the system parallax. f is 32 | the true anomaly, and E is the eccentric anomaly, which are related to elapsed time 33 | through Kepler’s equation and Kepler’s third law 34 | 35 | .. math:: 36 | M = 2\pi ({t\over P}-(\tau -\tau_{ref})) 37 | 38 | 39 | .. math:: 40 | ({P\over yr})^2 =({a\over au})^3({M_\odot \over M_{tot}}) 41 | 42 | M =E-esinE 43 | 44 | f = 2tan^{-1}[\sqrt{{1+e\over 1-e}}tan{E\over 2}] 45 | 46 | ``orbitize!`` employs two Kepler solvers to convert between mean 47 | and eccentric anomaly: one that is efficient for the highest eccentricities, and Newton’s method, which in other cases is more efficient for solving for the average 48 | orbit. See `Blunt et al. (2020) `_ for more detail. 49 | 50 | 51 | From scrutinizing the above sets of equations, one may observe 52 | a few important degeneracies: 53 | 54 | 1. Individual component masses do not show up anywhere in this equation set. 55 | 56 | 2. The degeneracy between semimajor axis 𝑎, total mass :math:`𝑀_{tot}`, and 57 | parallax 𝜋. If we just had relative astrometric measurements and no external knowledge of the system parallax, 58 | we would not be able to distinguish between a system 59 | that has larger distance and larger semimajor axis (and therefore larger total mass, 60 | assuming a fixed period) from a system that has smaller distance, smaller semimajor 61 | axis, and smaller total mass. 62 | 63 | 3. The argument of periastron :math:`\omega_p` and the position angle of nodes Ω. 64 | The above defined R.A. and decl. functions are invariant to the transformation: 65 | 66 | .. math:: 67 | \omega_p' = \omega_p + \pi 68 | 69 | \Omega' = \Omega - \pi 70 | 71 | which creates a 180◦ degeneracy between particular values of :math:`\omega_p` and Ω, and 72 | a characteristic “double-peaked” structure in marginalized 1D posteriors of these 73 | parameters. 74 | 75 | Solutions to breaking degeneracies 1 and 3 can be found in the next section. 76 | 77 | 78 | Using Radial Velocities 79 | +++++++++++++++++ 80 | In the ``orbitize!`` coordinate system, and relative to the system barycenter, the 81 | radial velocity of the planet due to the gravitational influence of the star is: 82 | 83 | .. math:: 84 | rv_p(f) = \sqrt{{G\over (1-e^2)}}M_* sini(M_{tot})^{-1/2}a^{-1/2}(cos(\omega_p+f)+ecos\omega_p) 85 | 86 | rv_*(f) = \sqrt{{G\over (1-e^2)}}M_p sini(M_{tot})^{-1/2}a^{-1/2}(cos(\omega_*+f)+ecos\omega_*) 87 | 88 | where 𝜔∗ is the argument of periastron of the star’s orbit, which is equal to 𝜔𝑝 + 89 | 180◦. 90 | 91 | In these equations, the individual component masses m𝑝 and m∗ enter. This means 92 | radial velocity measurements break the total mass degeneracy and enable measurements of individual component masses 93 | (“dynamical” masses). However it is crucial to keep in mind that radial velocities of a planet do not enable 94 | dynamical mass measurements of the planet itself, but of the star. 95 | Radial velocity measurements also break the Ω/𝜔 degeneracy discussed in the 96 | previous section, uniquely orienting the orbit in 3D space. 97 | 98 | ``orbitize!`` can perform joint fits of RV and astrometric data in two different 99 | ways, which have complementary applications. 100 | 101 | The first method is automatically triggered when an ``orbitize!`` user inputs radial velocity data. 102 | ``orbitize!`` automatically parses the data, sets up an appropriate model, 103 | then runs the user’s Bayesian computation 104 | algorithm of choice to jointly constrain all free parameters in the fit. 105 | ``orbitize!`` can handle both primary and secondary RVs, and fits for the appropriate dynamical 106 | masses when RVs are present; when primary RVs are included, ``orbitize!`` fits for 107 | the dynamical masses of secondary objects, and vice versa. 108 | Instrumental nuisance parameters (RV zeropoint offset, 𝛾, and white noise jitter, 𝜎) for each RV instrument 109 | are also included as additional free parameters in the fit if the user specifies different 110 | instrument names in the data file. 111 | 112 | The second method of jointly fitting RV and astrometric data in ``orbitize!`` separates out the fitting of 113 | radial velocities and astrometry, enabling a user to fit “one at a 114 | time,” and combine the results in a Bayesian framework. First, a user performs a 115 | fit to just the radial velocity data using, for example, radvel (but can be any radial 116 | velocity orbit-fitting code). The user then feeds the numerical posterior samples 117 | into ``orbitize!`` through the ``orbitize.priors.KDEPrior`` object. This prior 118 | creates a representation of the prior using kernel density estimation 119 | (`kernel density estimation `_), 120 | which can then be used to generate random prior samples or compute the prior 121 | probability of a sample orbit. Importantly, this prior preserves covariances between 122 | input parameters, allowing ``orbitize!`` to use an accurate representation of the RV 123 | posterior to constrain the fit. This method can be referred to as the “posteriors as priors” 124 | method, since posteriors output from a RV fitting code are, through KDE sampling, 125 | being applied as priors in ``orbitize!`` . 126 | 127 | 128 | More coming soon! -------------------------------------------------------------------------------- /docs/nbody.rst: -------------------------------------------------------------------------------- 1 | .. _nbody: 2 | 3 | N-body Backend 4 | =============== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.nbody 8 | :members: -------------------------------------------------------------------------------- /docs/orbitize_logo_500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sblunt/orbitize/1f91aac848a84df52c9f0f66b8f4c6156a0f7e08/docs/orbitize_logo_500.png -------------------------------------------------------------------------------- /docs/plot.rst: -------------------------------------------------------------------------------- 1 | .. _plot: 2 | 3 | Plotting Methods 4 | ================ 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.plot 8 | :members: -------------------------------------------------------------------------------- /docs/priors.rst: -------------------------------------------------------------------------------- 1 | .. _priors: 2 | 3 | Priors 4 | ===================== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.priors 8 | :members: -------------------------------------------------------------------------------- /docs/read_input.rst: -------------------------------------------------------------------------------- 1 | .. _read_input: 2 | 3 | Read Input 4 | ========== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.read_input 8 | :members: -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | nbsphinx 2 | pandoc 3 | ipykernel 4 | sphinx_rtd_theme -------------------------------------------------------------------------------- /docs/results.rst: -------------------------------------------------------------------------------- 1 | .. _results: 2 | 3 | Results 4 | ===================== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.results 8 | :members: -------------------------------------------------------------------------------- /docs/sampler.rst: -------------------------------------------------------------------------------- 1 | .. _sampler: 2 | 3 | Sampler 4 | ===================== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.sampler 8 | :members: -------------------------------------------------------------------------------- /docs/system.rst: -------------------------------------------------------------------------------- 1 | .. _system: 2 | 3 | System 4 | ===================== 5 | .. module:: orbitize 6 | 7 | .. automodule:: orbitize.system 8 | :members: -------------------------------------------------------------------------------- /docs/tutorials.rst: -------------------------------------------------------------------------------- 1 | .. _tutorials: 2 | 3 | Tutorials 4 | ========= 5 | 6 | The following tutorials walk you through performing orbit fits with 7 | ``orbitize``. To get started, read through "Formatting Input," "OFTI 8 | Introduction," and "MCMC Introduction." To learn more about the 9 | ``orbitize`` API, check out "Modifying Priors" and "Modifying MCMC Initial Positions." 10 | For an advanced plotting demo, see "Advanced Plotting," and to learn 11 | about the differences between OFTI and MCMC algorithms, 12 | we suggest "MCMC vs OFTI Comparison." 13 | 14 | We also have a bunch of tutorials designed to introduce you to specific features 15 | of our code, listed below. 16 | 17 | Many of these tutorials are also available as jupyter notebooks 18 | `here `_. 19 | 20 | If you find a bug, or if something is unclear, please create an issue 21 | on GitHub! We'd love any feedback on how to make ``orbitize`` more 22 | accessible. 23 | 24 | *A note about the tutorials*: There are many ways to interact with the 25 | ``orbitize`` code base, and each person on our team uses the code differently. 26 | Each tutorial has a different author, and correspondingly a different 27 | style of using and explaining the code. If you are confused by part of 28 | one tutorial, we suggest looking at some of the others (and then contacting 29 | us if you are still confused). 30 | 31 | .. toctree:: 32 | :maxdepth: 1 33 | :caption: Basic Tutorials 34 | 35 | tutorials/Quick-Start.ipynb 36 | formatting_inputs 37 | tutorials/OFTI_tutorial.ipynb 38 | tutorials/MCMC_tutorial.ipynb 39 | 40 | .. toctree:: 41 | :maxdepth: 1 42 | :caption: Advanced Tutorials 43 | 44 | tutorials/Modifying_Priors.ipynb 45 | tutorials/Plotting_tutorial.ipynb 46 | tutorials/MCMC_vs_OFTI.ipynb 47 | tutorials/Modifying_MCMC_initial_positions.ipynb 48 | 49 | .. toctree:: 50 | :maxdepth: 1 51 | :caption: Feature Tutorials 52 | 53 | tutorials/RV_MCMC_Tutorial.ipynb 54 | tutorials/Multiplanet_Tutorial.ipynb 55 | tutorials/Using_nonOrbitize_Posteriors_as_Priors.ipynb 56 | tutorials/Changing_bases_tutorial.ipynb 57 | tutorials/Hipparcos_IAD.ipynb 58 | tutorials/HGCA_tutorial.ipynb 59 | tutorials/abs_astrometry.ipynb 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /docs/tutorials/eift_hd206893.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sblunt/orbitize/1f91aac848a84df52c9f0f66b8f4c6156a0f7e08/docs/tutorials/eift_hd206893.png -------------------------------------------------------------------------------- /orbitize/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | __version__ = "3.1.0" 4 | 5 | # set Python env variable to keep track of example data dir 6 | orbitize_dir = os.path.dirname(__file__) 7 | DATADIR = os.path.join(orbitize_dir, "example_data/") 8 | 9 | # Detect a valid CUDA environment 10 | try: 11 | import pycuda.driver as cuda 12 | import pycuda.autoinit 13 | from pycuda.compiler import SourceModule 14 | 15 | cuda_ext = True 16 | except: 17 | cuda_ext = False 18 | 19 | try: 20 | from . import _kepler 21 | 22 | cext = True 23 | except ImportError: 24 | cext = False 25 | -------------------------------------------------------------------------------- /orbitize/_kepler.pyx: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | cimport numpy as np 3 | 4 | cdef extern from "kepler.c": 5 | void newton_array(const int n_elements, 6 | const double manom[], 7 | const double ecc[], 8 | const double tol, 9 | const int max_iter, 10 | double eanom[]) 11 | 12 | cdef extern from "kepler.c": 13 | void mikkola_array(const int n_elements, 14 | const double manom[], 15 | const double ecc[], 16 | double eanom[]) 17 | 18 | def _c_newton_solver(np.ndarray[np.double_t,ndim=1] manom, 19 | np.ndarray[np.double_t,ndim=1] ecc, 20 | tolerance = 1e-9, 21 | max_iter = 100, 22 | np.ndarray[np.double_t,ndim=1] eanom0 = None): 23 | """ 24 | Wrapper function for C implementation of Newton-Raphson solver for eccentric anomaly. 25 | Args: 26 | manom (np.array): array of mean anomalies 27 | ecc (np.array): array of eccentricities 28 | eanom0 (np.array): array of first guess for eccentric anomaly, same shape as manom (optional) 29 | Return: 30 | eanom (np.array): array of eccentric anomalies 31 | Written: Devin Cody, 2018 32 | """ 33 | 34 | # Initialize at E=M, E=pi is better at very high eccentricities 35 | cdef np.ndarray eanom 36 | if eanom0 is None: 37 | eanom = np.copy(manom) 38 | else: 39 | eanom = np.copy(eanom0) 40 | 41 | 42 | newton_array(len(manom), manom.data, ecc.data, tolerance, max_iter, eanom.data) 43 | 44 | return eanom 45 | 46 | def _c_mikkola_solver(np.ndarray[np.double_t,ndim=1] manom, 47 | np.ndarray[np.double_t,ndim=1] ecc): 48 | """ 49 | Wrapper function for C implementation of Newton-Raphson solver for eccentric anomaly. 50 | Args: 51 | manom (np.array): array of mean anomalies 52 | ecc (np.array): array of eccentricities 53 | eanom0 (np.array): array of first guess for eccentric anomaly, same shape as manom (optional) 54 | Return: 55 | eanom (np.array): array of eccentric anomalies 56 | Written: Devin Cody, 2018 57 | """ 58 | 59 | # Initialize at E=M, E=pi is better at very high eccentricities 60 | cdef np.ndarray eanom 61 | eanom = np.zeros(len(manom)) 62 | 63 | mikkola_array(len(manom), manom.data, ecc.data, eanom.data) 64 | 65 | return eanom -------------------------------------------------------------------------------- /orbitize/driver.py: -------------------------------------------------------------------------------- 1 | import orbitize.read_input 2 | import orbitize.system 3 | import orbitize.sampler 4 | 5 | """ 6 | This module reads input and constructs ``orbitize`` objects 7 | in a standardized way. 8 | """ 9 | 10 | 11 | class Driver(object): 12 | """ 13 | Runs through ``orbitize`` methods in a standardized way. 14 | 15 | Args: 16 | input_data: Either a relative path to data file or astropy.table.Table object 17 | in the orbitize format. See ``orbitize.read_input`` 18 | sampler_str (str): algorithm to use for orbit computation. "MCMC" for 19 | Markov Chain Monte Carlo, "OFTI" for Orbits for the Impatient 20 | num_secondary_bodies (int): number of secondary bodies in the system. 21 | Should be at least 1. 22 | stellar_or_system_mass (float): mass of the primary star (if fitting for 23 | dynamical masses of both components) or total system mass (if 24 | fitting using relative astrometry only) [M_sol] 25 | plx (float): mean parallax of the system [mas] 26 | mass_err (float, optional): uncertainty on ``stellar_or_system_mass`` [M_sol] 27 | plx_err (float, optional): uncertainty on ``plx`` [mas] 28 | lnlike (str, optional): name of function in ``orbitize.lnlike`` that will 29 | be used to compute likelihood. (default="chi2_lnlike") 30 | chi2_type (str, optional): either "standard", or "log" 31 | system_kwargs (dict, optional): ``restrict_angle_ranges``, ``tau_ref_epoch``, 32 | ``fit_secondary_mass``, ``hipparcos_IAD``, ``gaia``, 33 | ``use_rebound``, ``fitting_basis`` for ``orbitize.system.System``. 34 | mcmc_kwargs (dict, optional): ``num_temps``, ``num_walkers``, and ``num_threads`` 35 | kwargs for ``orbitize.sampler.MCMC`` 36 | 37 | Written: Sarah Blunt, 2018 38 | """ 39 | 40 | def __init__( 41 | self, input_data, sampler_str, 42 | num_secondary_bodies, stellar_or_system_mass, plx, 43 | mass_err=0, plx_err=0, lnlike='chi2_lnlike', chi2_type = 'standard', 44 | system_kwargs=None, mcmc_kwargs=None 45 | ): 46 | 47 | # Read in data 48 | # Try to interpret input as a filename first 49 | try: 50 | data_table = orbitize.read_input.read_file(input_data) 51 | except: 52 | try: 53 | # Check if input might be an orbitize style astropy.table.Table 54 | if 'quant_type' in input_data.columns: 55 | data_table = input_data.copy() 56 | except: 57 | raise Exception('Invalid value of input_data for Driver') 58 | 59 | if system_kwargs is None: 60 | system_kwargs = {} 61 | 62 | #Check if RV data is included, make sure fit_secondary_mass=True 63 | if 'rv' in data_table['quant_type'] and ('fit_secondary_mass' not in system_kwargs or system_kwargs['fit_secondary_mass'] == False): 64 | raise Exception('If including RV data in orbit fit, set fit_secondary_mass=True') 65 | 66 | if sampler_str == 'OFTI' and ('fit_secondary_mass' in system_kwargs and True == system_kwargs['fit_secondary_mass']): 67 | raise Exception('Run Astrometry+RV in MCMC for now.') 68 | # Initialize System object which stores data & sets priors 69 | self.system = orbitize.system.System( 70 | num_secondary_bodies, data_table, stellar_or_system_mass, 71 | plx, mass_err=mass_err, plx_err=plx_err, **system_kwargs 72 | ) 73 | 74 | # Initialize Sampler object, which has System object as an attribute. 75 | if mcmc_kwargs is not None and sampler_str == 'MCMC': 76 | kwargs = mcmc_kwargs 77 | else: 78 | kwargs = {} 79 | 80 | sampler_func = getattr(orbitize.sampler, sampler_str) 81 | self.sampler = sampler_func(self.system, like=lnlike, chi2_type=chi2_type, **kwargs) 82 | -------------------------------------------------------------------------------- /orbitize/example_data/GJ504.csv: -------------------------------------------------------------------------------- 1 | # Table 12 of Blunt et al 2017 2 | # Previous Observations of GJ 504 b 3 | epoch,object,sep,sep_err,pa,pa_err 4 | 55645.95,1,2479,16,327.94,0.39 5 | 55702.89,1,2483,8,327.45,0.19 6 | 55785.015,1,2481,33,326.84,0.94 7 | 55787.935,1,2448,24,325.82,0.66 8 | 55985.19400184,1,2483,15,326.46,0.36 9 | 56029.11400323,1,2487,8,326.54,0.18 10 | 56072.30200459,1,2499,26,326.14,0.61 11 | -------------------------------------------------------------------------------- /orbitize/example_data/GJ504_1epoch.csv: -------------------------------------------------------------------------------- 1 | # Table 12 of Blunt et al 2017 2 | # One-epoch Observation of GJ 504 b 3 | epoch,object,sep,sep_err,pa,pa_err 4 | 55645.95,1,2479,16,327.94,0.39 5 | -------------------------------------------------------------------------------- /orbitize/example_data/H027321.d: -------------------------------------------------------------------------------- 1 | # This file contains residual records, extracted from the Hipparcos 2 2 | # Interactive Data Access Tool (2014). For more information, see: 3 | # https://www.cosmos.esa.int/web/hipparcos/interactive-data-access 4 | # https://www.cosmos.esa.int/web/hipparcos/catalogues 5 | # 6 | # HIP MCE NRES NC isol_n SCE F2 F1 7 | # 27321 27251 111 1 5 0 -1.63 0 8 | # Hp B-V VarAnn NOB NR 9 | # 3.9077 0.171 0 111 0 10 | # RAdeg DEdeg Plx pm_RA pm_DE e_RA e_DE e_Plx e_pmRA e_pmDE dpmRA dpmDE e_dpmRA e_dpmDE ddpmRA ddpmDE e_ddpmRA e_ddpmDE upsRA upsDE e_upsRA e_upsDE var 11 | # 86.82118073 -51.06671341 51.44 4.65 83.10 0.10 0.11 0.12 0.11 0.15 --- --- --- --- --- --- --- --- --- --- --- --- --- 12 | # 13 | # IORB EPOCH PARF CPSI SPSI RES SRES 14 | 133 -1.2445 0.6262 -0.9050 -0.4254 -0.23 0.80 15 | 133 -1.2445 0.6236 -0.9065 -0.4222 -0.92 0.81 16 | 133 -1.2445 0.6219 -0.9076 -0.4198 -1.71 0.78 17 | 194 -1.1703 -0.6485 -0.0716 0.9974 -0.82 0.86 18 | 194 -1.1703 -0.6481 -0.0723 0.9974 -0.57 0.87 19 | 194 -1.1703 -0.6512 -0.0680 0.9977 0.35 0.78 20 | 257 -1.0937 0.6739 -0.8778 0.4790 -0.26 0.78 21 | 257 -1.0937 0.6711 -0.8760 0.4823 -0.02 0.80 22 | 257 -1.0937 0.6706 -0.8756 0.4830 -0.24 0.85 23 | 327 -1.0086 -0.6786 0.8137 0.5813 -1.30 0.81 24 | 327 -1.0086 -0.6786 0.8136 0.5814 0.27 0.75 25 | 327 -1.0086 -0.6799 0.8147 0.5799 -0.04 0.82 26 | 458 -0.8493 -0.6398 0.9461 -0.3240 0.29 1.06 27 | 458 -0.8493 -0.6407 0.9457 -0.3250 -1.11 0.82 28 | 458 -0.8493 -0.6406 0.9457 -0.3250 -1.13 0.84 29 | 458 -0.8493 -0.6404 0.9458 -0.3247 -1.12 0.80 30 | 458 -0.8493 -0.6406 0.9457 -0.3250 0.56 0.91 31 | 503 -0.7944 0.6361 0.7365 0.6764 -0.44 1.10 32 | 503 -0.7944 0.6362 0.7365 0.6765 -0.38 0.96 33 | 503 -0.7944 0.6357 0.7367 0.6762 0.06 0.82 34 | 503 -0.7944 0.6373 0.7353 0.6777 0.73 0.95 35 | 585 -0.6950 -0.6561 0.2592 -0.9658 -0.17 0.70 36 | 585 -0.6950 -0.6554 0.2602 -0.9656 -0.58 0.86 37 | 585 -0.6950 -0.6549 0.2609 -0.9654 0.78 0.81 38 | 634 -0.6355 0.6735 0.9777 -0.2102 -0.26 0.80 39 | 709 -0.5443 -0.6846 -0.6216 -0.7833 -0.39 0.72 40 | 709 -0.5443 -0.6855 -0.6226 -0.7826 -0.30 0.76 41 | 709 -0.5443 -0.6854 -0.6225 -0.7826 0.14 0.73 42 | 709 -0.5443 -0.6866 -0.6238 -0.7816 -0.06 0.67 43 | 770 -0.4701 0.6726 0.3654 -0.9308 -1.23 0.84 44 | 770 -0.4701 0.6716 0.3640 -0.9314 -0.02 0.84 45 | 834 -0.3924 -0.6397 -0.9999 0.0145 -1.26 0.84 46 | 834 -0.3924 -0.6395 -0.9999 0.0141 0.71 0.77 47 | 834 -0.3924 -0.6426 -0.9998 0.0182 -0.27 0.81 48 | 834 -0.3924 -0.6421 -0.9998 0.0177 0.27 0.76 49 | 904 -0.3073 0.6172 -0.6578 -0.7532 0.13 0.70 50 | 904 -0.3073 0.6167 -0.6584 -0.7526 0.09 0.74 51 | 964 -0.2344 -0.6235 -0.4919 0.8706 0.30 1.24 52 | 964 -0.2344 -0.6226 -0.4930 0.8700 -0.99 0.86 53 | 964 -0.2344 -0.6247 -0.4904 0.8715 0.78 0.83 54 | 964 -0.2344 -0.6252 -0.4897 0.8719 -0.78 0.77 55 | 964 -0.2344 -0.6284 -0.4859 0.8740 -0.35 0.86 56 | 1031 -0.1530 0.6545 -0.9905 0.1376 0.01 0.73 57 | 1031 -0.1530 0.6535 -0.9903 0.1389 -0.42 0.84 58 | 1031 -0.1530 0.6523 -0.9901 0.1404 -0.90 0.84 59 | 1031 -0.1530 0.6512 -0.9899 0.1420 -0.32 0.88 60 | 1097 -0.0728 -0.6753 0.5283 0.8491 -2.04 1.11 61 | 1097 -0.0728 -0.6768 0.5301 0.8479 -0.32 0.89 62 | 1097 -0.0728 -0.6771 0.5305 0.8477 0.18 1.16 63 | 1097 -0.0728 -0.6782 0.5318 0.8469 -0.10 0.80 64 | 1097 -0.0728 -0.6793 0.5330 0.8461 -0.84 1.08 65 | 1153 -0.0047 0.6749 -0.4995 0.8663 -1.58 0.98 66 | 1153 -0.0047 0.6758 -0.5006 0.8657 0.16 0.91 67 | 1153 -0.0047 0.6748 -0.4994 0.8664 0.10 0.83 68 | 1153 -0.0047 0.6749 -0.4995 0.8663 0.69 1.15 69 | 1229 0.0876 -0.6539 0.9987 0.0513 -0.18 1.00 70 | 1229 0.0876 -0.6537 0.9987 0.0516 -0.80 0.83 71 | 1229 0.0876 -0.6535 0.9987 0.0518 -0.59 1.04 72 | 1229 0.0876 -0.6529 0.9986 0.0528 0.38 0.85 73 | 1275 0.1435 0.6356 0.4213 0.9069 0.30 0.85 74 | 1276 0.1447 0.6438 0.4193 0.9078 -0.49 0.77 75 | 1404 0.3002 0.6569 0.9862 0.1654 -0.45 1.06 76 | 1404 0.3002 0.6561 0.9864 0.1643 1.64 0.96 77 | 1404 0.3002 0.6577 0.9861 0.1664 -1.45 1.09 78 | 1404 0.3002 0.6574 0.9861 0.1661 0.76 0.90 79 | 1404 0.3002 0.6579 0.9860 0.1665 -2.76 1.19 80 | 1404 0.3002 0.6572 0.9861 0.1659 -0.66 0.79 81 | 1482 0.3950 -0.6833 -0.3023 -0.9532 -1.35 1.06 82 | 1482 0.3950 -0.6831 -0.3021 -0.9533 0.44 0.85 83 | 1482 0.3950 -0.6833 -0.3023 -0.9532 1.16 1.15 84 | 1482 0.3950 -0.6822 -0.3009 -0.9536 0.18 0.83 85 | 1539 0.4642 0.6899 0.7038 -0.7104 -0.57 0.85 86 | 1539 0.4642 0.6906 0.7044 -0.7098 1.14 1.22 87 | 1539 0.4642 0.6894 0.7033 -0.7109 -0.55 0.88 88 | 1539 0.4642 0.6888 0.7027 -0.7115 1.51 1.07 89 | 1607 0.5468 -0.6645 -0.9372 -0.3487 -1.32 1.23 90 | 1607 0.5468 -0.6647 -0.9374 -0.3483 -0.15 0.92 91 | 1674 0.6283 0.6337 -0.2749 -0.9615 -1.91 1.17 92 | 1674 0.6283 0.6330 -0.2756 -0.9613 0.34 1.20 93 | 1674 0.6283 0.6309 -0.2787 -0.9604 -1.10 1.07 94 | 1674 0.6283 0.6310 -0.2785 -0.9604 -0.92 0.83 95 | 1734 0.7011 -0.6202 -0.8130 0.5823 -0.89 1.18 96 | 1734 0.7011 -0.6217 -0.8121 0.5835 0.03 1.19 97 | 1734 0.7011 -0.6231 -0.8109 0.5853 1.87 0.77 98 | 1735 0.7023 -0.6205 -0.8083 0.5888 -1.75 1.18 99 | 1804 0.7862 0.6303 -0.9715 -0.2369 -0.41 0.77 100 | 1804 0.7862 0.6299 -0.9717 -0.2363 0.15 0.77 101 | 1867 0.8627 -0.6642 0.1487 0.9889 -0.65 0.76 102 | 2000 1.0242 -0.6686 0.9088 0.4172 -0.15 0.91 103 | 2049 1.0836 0.6504 0.0441 0.9990 -0.06 0.77 104 | 2049 1.0836 0.6501 0.0446 0.9990 0.11 0.73 105 | 2049 1.0836 0.6504 0.0441 0.9990 0.30 0.72 106 | 2049 1.0836 0.6505 0.0440 0.9990 -0.16 0.72 107 | 2130 1.1820 -0.6325 0.8661 -0.4998 -0.24 0.90 108 | 2130 1.1820 -0.6326 0.8661 -0.4999 -0.22 0.70 109 | 2130 1.1820 -0.6325 0.8661 -0.4999 -1.23 0.98 110 | 2130 1.1820 -0.6327 0.8659 -0.5002 -0.13 0.91 111 | 2175 1.2366 0.6384 0.8536 0.5210 0.11 1.05 112 | 2175 1.2366 0.6382 0.8537 0.5207 0.95 1.18 113 | 2175 1.2366 0.6403 0.8523 0.5231 -1.38 0.97 114 | 2175 1.2366 0.6400 0.8524 0.5229 -0.54 1.30 115 | 2256 1.3349 -0.6633 0.0709 -0.9975 -0.30 1.26 116 | 2256 1.3349 -0.6636 0.0702 -0.9975 0.30 0.96 117 | 2506 1.6387 -0.6282 -0.9787 0.2055 -0.24 0.92 118 | 2506 1.6387 -0.6284 -0.9786 0.2058 -0.21 0.81 119 | 2506 1.6387 -0.6300 -0.9781 0.2081 -0.01 0.84 120 | 2506 1.6387 -0.6319 -0.9775 0.2107 -0.44 0.76 121 | 2506 1.6387 -0.6335 -0.9771 0.2128 0.34 0.89 122 | 2677 1.8464 0.6518 -0.9908 0.1352 -0.05 0.85 123 | 2677 1.8464 0.6510 -0.9906 0.1364 -0.62 0.83 124 | 2677 1.8464 0.6507 -0.9904 0.1380 -3.04 1.63 125 | -------------------------------------------------------------------------------- /orbitize/example_data/H027989.d: -------------------------------------------------------------------------------- 1 | # This file contains residual records, extracted from the Hipparcos 2 2 | # Interactive Data Access Tool (2014). For more information, see: 3 | # https://www.cosmos.esa.int/web/hipparcos/interactive-data-access 4 | # https://www.cosmos.esa.int/web/hipparcos/catalogues 5 | # 6 | # HIP MCE NRES NC isol_n SCE F2 F1 7 | # 27989 27917 66 1 1 0 -0.11 1 8 | # Hp B-V VarAnn NOB NR 9 | # 0.4997 1.500 2 66 1 10 | # RAdeg DEdeg Plx pm_RA pm_DE e_RA e_DE e_Plx e_pmRA e_pmDE dpmRA dpmDE e_dpmRA e_dpmDE ddpmRA ddpmDE e_ddpmRA e_ddpmDE upsRA upsDE e_upsRA e_upsDE var 11 | # 88.79287149 7.40703653 6.55 27.54 11.30 0.71 0.58 0.83 1.03 0.65 --- --- --- --- --- --- --- --- --- --- --- --- 0.15 12 | # 13 | # IORB EPOCH PARF CPSI SPSI RES SRES 14 | 282 -1.0633 0.6611 -0.7125 0.7017 -0.32 2.47 15 | 387 -0.9356 0.3807 -0.2826 0.9592 1.60 2.47 16 | 387 -0.9356 0.3804 -0.2821 0.9594 1.68 2.47 17 | 691 -0.5661 -0.6505 -0.6040 -0.7970 3.77 2.47 18 | 692 -0.5649 -0.6478 -0.6015 -0.7988 21.59 -2.46 19 | 753 -0.4908 0.6486 0.6216 -0.7834 2.95 2.47 20 | 798 -0.4361 -0.4301 -0.6454 -0.7639 0.36 2.47 21 | 798 -0.4361 -0.4317 -0.6457 -0.7636 0.72 2.46 22 | 798 -0.4361 -0.4350 -0.6490 -0.7608 0.99 2.46 23 | 798 -0.4361 -0.4341 -0.6482 -0.7614 1.47 2.46 24 | 1063 -0.1141 0.5102 -0.6970 0.7171 0.67 2.46 25 | 1063 -0.1141 0.5072 -0.6943 0.7197 2.55 2.46 26 | 1114 -0.0521 -0.6726 0.6547 0.7559 5.05 2.47 27 | 1115 -0.0509 -0.6733 0.6564 0.7544 -0.90 2.47 28 | 1169 0.0148 0.6033 -0.5666 0.8240 -2.16 2.47 29 | 1169 0.0148 0.6034 -0.5667 0.8239 -1.12 2.48 30 | 1169 0.0148 0.6043 -0.5680 0.8230 -0.88 2.47 31 | 1169 0.0148 0.6040 -0.5676 0.8233 -0.42 2.47 32 | 1473 0.3841 -0.4717 -0.3829 -0.9238 -1.55 2.47 33 | 1473 0.3841 -0.4699 -0.3806 -0.9247 1.44 2.47 34 | 1473 0.3841 -0.4686 -0.3791 -0.9254 -0.59 2.46 35 | 1473 0.3841 -0.4671 -0.3768 -0.9263 -1.56 2.46 36 | 1473 0.3841 -0.4658 -0.3755 -0.9268 1.94 2.46 37 | 1473 0.3841 -0.4645 -0.3736 -0.9276 -2.51 2.47 38 | 1515 0.4351 0.6506 0.7141 -0.7001 2.52 2.47 39 | 1580 0.5140 -0.6365 -0.7034 -0.7108 -0.90 2.47 40 | 1580 0.5140 -0.6367 -0.7036 -0.7106 0.41 2.46 41 | 1580 0.5140 -0.6377 -0.7045 -0.7097 0.46 2.48 42 | 1580 0.5140 -0.6374 -0.7046 -0.7096 -3.08 2.47 43 | 1850 0.8420 0.1561 -0.4675 0.8840 0.06 2.47 44 | 1851 0.8432 0.1549 -0.4620 0.8869 3.25 2.47 45 | 1851 0.8432 0.1533 -0.4602 0.8878 1.28 2.46 46 | 1851 0.8432 0.1508 -0.4573 0.8893 -1.48 2.47 47 | 1851 0.8432 0.1516 -0.4579 0.8890 4.22 2.46 48 | 1851 0.8432 0.1456 -0.4504 0.8928 -1.98 2.48 49 | 1851 0.8432 0.1449 -0.4500 0.8930 2.14 2.46 50 | 1851 0.8432 0.1413 -0.4453 0.8954 -2.71 2.47 51 | 1851 0.8432 0.1421 -0.4462 0.8949 0.19 2.47 52 | 1875 0.8724 -0.4268 0.3368 0.9416 2.14 2.48 53 | 1875 0.8724 -0.4303 0.3435 0.9392 6.27 2.48 54 | 1875 0.8724 -0.4306 0.3438 0.9390 -3.36 2.47 55 | 1875 0.8724 -0.4342 0.3486 0.9373 1.60 2.48 56 | 1875 0.8724 -0.4344 0.3489 0.9371 1.67 2.47 57 | 1876 0.8736 -0.4438 0.3611 0.9325 1.99 2.47 58 | 1876 0.8736 -0.4427 0.3604 0.9328 -4.72 2.47 59 | 1950 0.9634 0.6769 -0.6867 0.7270 2.16 2.46 60 | 1950 0.9634 0.6770 -0.6868 0.7268 -2.11 2.47 61 | 1950 0.9634 0.6771 -0.6869 0.7267 1.20 2.47 62 | 1950 0.9634 0.6772 -0.6870 0.7267 3.33 2.47 63 | 2029 1.0594 -0.4110 0.6402 0.7682 -4.40 2.47 64 | 2029 1.0594 -0.4111 0.6400 0.7684 -0.18 2.47 65 | 2029 1.0594 -0.4075 0.6359 0.7718 -2.07 2.47 66 | 2029 1.0594 -0.4069 0.6357 0.7719 1.83 2.47 67 | 2029 1.0594 -0.4022 0.6307 0.7760 1.49 2.46 68 | 2029 1.0594 -0.4014 0.6301 0.7765 0.62 2.47 69 | 2029 1.0594 -0.3979 0.6261 0.7798 -0.06 2.46 70 | 2029 1.0594 -0.3970 0.6253 0.7803 1.96 2.48 71 | 2049 1.0836 0.1519 0.0450 0.9990 -1.03 2.47 72 | 2049 1.0836 0.1521 0.0438 0.9990 -3.43 2.47 73 | 2049 1.0836 0.1558 0.0394 0.9992 -4.13 2.47 74 | 2049 1.0836 0.1561 0.0390 0.9992 0.12 2.47 75 | 2049 1.0836 0.1606 0.0332 0.9994 -0.14 2.47 76 | 2049 1.0836 0.1603 0.0336 0.9994 -1.90 2.47 77 | 2049 1.0836 0.1648 0.0269 0.9996 -1.70 2.47 78 | 2049 1.0836 0.1648 0.0273 0.9996 3.36 2.47 79 | 2050 1.0849 0.1717 0.0203 0.9998 -3.24 2.47 80 | -------------------------------------------------------------------------------- /orbitize/example_data/HD4747.csv: -------------------------------------------------------------------------------- 1 | epoch,object,sep,sep_err,pa,pa_err,rv,rv_err 2 | 56942.3,1,606.5,7.0,180.04,0.62,, 3 | 57031.2,1,606.6,6.4,180.52,0.58,, 4 | 57289.4,1,604.0,7.0,184.9,0.9,, 5 | 50366.475,0,,,,,-0.54103,0.00123 6 | 50418.329,0,,,,,-0.40053,0.00206 7 | 50462.252,0,,,,,-0.24094,0.0011 8 | 50689.604,0,,,,,0.37292,0.00117 9 | 50784.271,0,,,,,0.46223,0.00133 10 | 50806.227,0,,,,,0.48519,0.00103 11 | 50837.217,0,,,,,0.49395,0.00117 12 | 50838.201,0,,,,,0.49751,0.00112 13 | 50839.211,0,,,,,0.50187,0.00112 14 | 51009.62,0,,,,,0.53355,0.00135 15 | 51011.548,0,,,,,0.53164,0.00128 16 | 51013.602,0,,,,,0.53629,0.0016 17 | 51050.491,0,,,,,0.52154,0.00468 18 | 51170.248,0,,,,,0.50757,0.0014 19 | 51367.585,0,,,,,0.47678,0.00131 20 | 51409.527,0,,,,,0.46147,0.00523 21 | 51543.244,0,,,,,0.44311,0.00152 22 | 51550.229,0,,,,,0.43286,0.00147 23 | 51755.551,0,,,,,0.39329,0.00213 24 | 51899.284,0,,,,,0.36457,0.00163 25 | 52097.626,0,,,,,0.32986,0.00157 26 | 52488.542,0,,,,,0.26687,0.0015 27 | 52572.312,0,,,,,0.25035,0.00195 28 | 52987.228,0,,,,,0.19466,0.00238 29 | 52988.186,0,,,,,0.18469,0.00194 30 | 53238.456,0,,,,,0.16892,0.00134 31 | 53303.403,0,,,,,0.16769,0.00112 32 | 53339.265,0,,,,,0.16069,0.00119 33 | 53724.274,0,,,,,0.11302,0.00103 34 | 53724.276,0,,,,,0.11605,0.00112 35 | 54717.455,0,,,,,0.00984,0.00123 36 | 54718.508,0,,,,,0.01242,0.00115 37 | 54719.51,0,,,,,0.01572,0.00123 38 | 54720.47,0,,,,,0.01534,0.00113 39 | 54722.401,0,,,,,0.01479,0.00127 40 | 54723.47,0,,,,,0.01422,0.00122 41 | 54724.474,0,,,,,0.01169,0.0012 42 | 54725.383,0,,,,,0.01383,0.00113 43 | 54726.505,0,,,,,0.0195,0.00123 44 | 54727.452,0,,,,,0.0175,0.00113 45 | 55014.62,0,,,,,-0.00636,0.00141 46 | 55015.624,0,,,,,-0.00409,0.00138 47 | 55016.624,0,,,,,-0.00566,0.00121 48 | 55048.525,0,,,,,-0.01975,0.00124 49 | 55076.584,0,,,,,-0.01614,0.00128 50 | 55077.594,0,,,,,-0.01303,0.00126 51 | 55134.464,0,,,,,-0.01689,0.00136 52 | 55198.254,0,,,,,-0.02885,0.0012 53 | 55425.584,0,,,,,-0.04359,0.00125 54 | 55522.379,0,,,,,-0.0512,0.0013 55 | 55806.547,0,,,,,-0.07697,0.0013 56 | 56148.55,0,,,,,-0.10429,0.00128 57 | 56319.201,0,,,,,-0.1102,0.00128 58 | 56327.208,0,,,,,-0.11332,0.00149 59 | 56507.645,0,,,,,-0.12324,0.00133 60 | 56912.534,0,,,,,-0.17085,0.00113 61 | -------------------------------------------------------------------------------- /orbitize/example_data/HIP027321.d: -------------------------------------------------------------------------------- 1 | 27321 27251 111 1 5 0 -1.81 0 2 | 133 -1.245 0.624 -0.9065 -0.4222 -1.00 0.81 3 | 133 -1.245 0.626 -0.9050 -0.4254 -0.31 0.80 4 | 133 -1.245 0.622 -0.9076 -0.4198 -1.66 0.78 5 | 194 -1.170 -0.651 -0.0680 0.9977 0.39 0.78 6 | 194 -1.170 -0.649 -0.0716 0.9974 -0.79 0.86 7 | 194 -1.170 -0.648 -0.0723 0.9974 -0.64 0.87 8 | 257 -1.094 0.671 -0.8760 0.4823 0.01 0.80 9 | 257 -1.094 0.674 -0.8778 0.4790 -0.23 0.78 10 | 257 -1.094 0.671 -0.8756 0.4830 -0.20 0.85 11 | 327 -1.009 -0.679 0.8136 0.5814 0.19 0.75 12 | 327 -1.009 -0.680 0.8147 0.5799 0.02 0.82 13 | 327 -1.009 -0.679 0.8137 0.5813 -1.26 0.81 14 | 458 -0.849 -0.641 0.9457 -0.3250 0.51 0.91 15 | 458 -0.849 -0.641 0.9457 -0.3250 -1.01 0.82 16 | 458 -0.849 -0.640 0.9461 -0.3240 0.25 1.06 17 | 458 -0.849 -0.641 0.9457 -0.3250 -1.17 0.84 18 | 458 -0.849 -0.640 0.9458 -0.3247 -1.04 0.80 19 | 503 -0.794 0.637 0.7353 0.6777 0.75 0.95 20 | 503 -0.794 0.636 0.7367 0.6762 -0.01 0.82 21 | 503 -0.794 0.636 0.7365 0.6764 -0.51 1.10 22 | 503 -0.794 0.636 0.7365 0.6765 -0.34 0.96 23 | 585 -0.695 -0.656 0.2592 -0.9658 -0.22 0.70 24 | 585 -0.695 -0.655 0.2602 -0.9656 -0.52 0.86 25 | 585 -0.695 -0.655 0.2609 -0.9654 0.73 0.81 26 | 634 -0.636 0.674 0.9777 -0.2102 -0.19 0.80 27 | 709 -0.544 -0.687 -0.6238 -0.7816 0.02 0.67 28 | 709 -0.544 -0.685 -0.6225 -0.7826 0.09 0.73 29 | 709 -0.544 -0.685 -0.6216 -0.7833 -0.41 0.72 30 | 709 -0.544 -0.686 -0.6226 -0.7826 -0.21 0.76 31 | 770 -0.470 0.673 0.3654 -0.9308 -1.15 0.84 32 | 770 -0.470 0.672 0.3640 -0.9314 -0.04 0.84 33 | 834 -0.392 -0.640 -0.9999 0.0141 0.66 0.78 34 | 834 -0.392 -0.643 -0.9998 0.0182 -0.21 0.81 35 | 834 -0.392 -0.640 -0.9999 0.0145 -1.21 0.84 36 | 834 -0.392 -0.642 -0.9998 0.0177 0.20 0.76 37 | 904 -0.307 0.617 -0.6578 -0.7532 0.20 0.71 38 | 904 -0.307 0.617 -0.6584 -0.7526 0.05 0.74 39 | 964 -0.234 -0.625 -0.4897 0.8719 -0.86 0.78 40 | 964 -0.234 -0.623 -0.4919 0.8706 0.36 1.24 41 | 964 -0.234 -0.623 -0.4930 0.8700 -1.03 0.86 42 | 964 -0.234 -0.628 -0.4859 0.8740 -0.30 0.86 43 | 964 -0.234 -0.625 -0.4904 0.8715 0.82 0.83 44 | 1031 -0.153 0.655 -0.9905 0.1376 0.10 0.73 45 | 1031 -0.153 0.651 -0.9899 0.1420 -0.37 0.88 46 | 1031 -0.153 0.654 -0.9903 0.1389 -0.49 0.84 47 | 1031 -0.153 0.652 -0.9901 0.1404 -0.85 0.84 48 | 1097 -0.073 -0.675 0.5283 0.8491 -2.09 1.11 49 | 1097 -0.073 -0.679 0.5330 0.8461 -0.90 1.08 50 | 1097 -0.073 -0.677 0.5301 0.8479 -0.25 0.89 51 | 1097 -0.073 -0.677 0.5305 0.8477 0.14 1.16 52 | 1097 -0.073 -0.678 0.5318 0.8469 -0.05 0.80 53 | 1153 -0.005 0.675 -0.4994 0.8664 0.04 0.83 54 | 1153 -0.005 0.676 -0.5006 0.8657 0.21 0.91 55 | 1153 -0.005 0.675 -0.4995 0.8663 0.73 1.15 56 | 1153 -0.005 0.675 -0.4995 0.8663 -1.65 0.98 57 | 1229 0.088 -0.653 0.9986 0.0528 0.30 0.86 58 | 1229 0.088 -0.654 0.9987 0.0518 -0.53 1.04 59 | 1229 0.088 -0.654 0.9987 0.0513 -0.11 1.00 60 | 1229 0.088 -0.654 0.9987 0.0516 -0.84 0.83 61 | 1275 0.144 0.636 0.4213 0.9069 0.34 0.85 62 | 1276 0.145 0.644 0.4193 0.9078 -0.45 0.77 63 | 1404 0.300 0.657 0.9862 0.1654 -0.43 1.06 64 | 1404 0.300 0.656 0.9864 0.1643 1.55 0.96 65 | 1404 0.300 0.657 0.9861 0.1661 0.71 0.90 66 | 1404 0.300 0.657 0.9861 0.1659 -0.75 0.79 67 | 1404 0.300 0.658 0.9860 0.1665 -2.69 1.19 68 | 1404 0.300 0.658 0.9861 0.1664 -1.40 1.09 69 | 1482 0.395 -0.683 -0.3021 -0.9533 0.38 0.85 70 | 1482 0.395 -0.683 -0.3023 -0.9532 -1.30 1.06 71 | 1482 0.395 -0.682 -0.3009 -0.9536 0.11 0.83 72 | 1482 0.395 -0.683 -0.3023 -0.9532 1.19 1.15 73 | 1539 0.464 0.690 0.7038 -0.7104 -0.50 0.85 74 | 1539 0.464 0.691 0.7044 -0.7098 1.08 1.22 75 | 1539 0.464 0.689 0.7033 -0.7109 -0.49 0.88 76 | 1539 0.464 0.689 0.7027 -0.7115 1.45 1.07 77 | 1607 0.547 -0.664 -0.9372 -0.3487 -1.27 1.23 78 | 1607 0.547 -0.665 -0.9374 -0.3483 -0.21 0.93 79 | 1674 0.628 0.631 -0.2787 -0.9604 -1.03 1.07 80 | 1674 0.628 0.634 -0.2749 -0.9615 -1.85 1.17 81 | 1674 0.628 0.633 -0.2756 -0.9613 0.30 1.21 82 | 1674 0.628 0.631 -0.2785 -0.9604 -0.98 0.83 83 | 1734 0.701 -0.623 -0.8109 0.5853 1.93 0.77 84 | 1734 0.701 -0.620 -0.8130 0.5823 -0.85 1.18 85 | 1734 0.701 -0.622 -0.8121 0.5835 -0.05 1.19 86 | 1735 0.702 -0.621 -0.8083 0.5888 -1.82 1.18 87 | 1804 0.786 0.630 -0.9717 -0.2363 0.09 0.77 88 | 1804 0.786 0.630 -0.9715 -0.2369 -0.34 0.77 89 | 1867 0.863 -0.664 0.1487 0.9889 -0.58 0.76 90 | 2000 1.024 -0.669 0.9088 0.4172 -0.23 0.91 91 | 2049 1.084 0.650 0.0446 0.9990 0.04 0.73 92 | 2049 1.084 0.650 0.0441 0.9990 -0.02 0.77 93 | 2049 1.084 0.650 0.0440 0.9990 -0.24 0.72 94 | 2049 1.084 0.650 0.0441 0.9990 0.38 0.72 95 | 2130 1.182 -0.633 0.8661 -0.4999 -0.27 0.70 96 | 2130 1.182 -0.633 0.8659 -0.5002 -0.17 0.91 97 | 2130 1.182 -0.632 0.8661 -0.4998 -0.16 0.90 98 | 2130 1.182 -0.632 0.8661 -0.4999 -1.14 0.98 99 | 2175 1.237 0.640 0.8524 0.5229 -0.59 1.30 100 | 2175 1.237 0.640 0.8523 0.5231 -1.31 0.97 101 | 2175 1.237 0.638 0.8536 0.5210 0.17 1.05 102 | 2175 1.237 0.638 0.8537 0.5207 0.88 1.18 103 | 2256 1.335 -0.663 0.0709 -0.9975 -0.22 1.26 104 | 2256 1.335 -0.664 0.0702 -0.9975 0.26 0.97 105 | 2506 1.639 -0.632 -0.9775 0.2107 -0.51 0.76 106 | 2506 1.639 -0.628 -0.9787 0.2055 -0.17 0.92 107 | 2506 1.639 -0.630 -0.9781 0.2081 0.04 0.84 108 | 2506 1.639 -0.633 -0.9771 0.2128 0.39 0.89 109 | 2506 1.639 -0.628 -0.9786 0.2058 -0.28 0.81 110 | 2677 1.846 0.652 -0.9908 0.1352 -0.03 0.85 111 | 2677 1.846 0.651 -0.9904 0.1380 -2.97 1.63 112 | 2677 1.846 0.651 -0.9906 0.1364 -0.69 0.83 113 | -------------------------------------------------------------------------------- /orbitize/example_data/betaPic.csv: -------------------------------------------------------------------------------- 1 | # Table 1 of Nielsen+ 2020 2 | epoch,object,sep,sep_err,pa,pa_err,rv,rv_err 3 | 54781,1,210.0,27.0,211.49,1.9,, 4 | 52953,1,413.0,22.0,34,4,, 5 | 55129,1,299.0,14.0,211,3,, 6 | 55194,1,306.0,9.0,212.1,1.7,, 7 | 55296,1,346.0,7.0,209.9,1.2,, 8 | 55467,1,383.0,11.0,210.3,1.7,, 9 | 55516,1,387.0,8.0,212.4,1.4,, 10 | 55517,1,390.0,13.0,212,2,, 11 | 55593,1,408.0,9.0,211.1,1.5,, 12 | 55646,1,426.0,13.0,210.1,1.8,, 13 | 55168,1,339.0,10.0,209.2,1.7,, 14 | 55168,1,323.0,10.0,209.3,1.8,, 15 | 55555,1,407.0,5.0,212.8,1.4,, 16 | 55854,1,452.0,3.0,211.6,0.4,, 17 | 55854,1,455.0,5.0,211.9,0.6,, 18 | 56015,1,447.0,3.0,210.8,0.4,, 19 | 56015,1,448.0,5.0,211.8,0.6,, 20 | 56263,1,461.0,14.0,211.9,1.2,, 21 | 56265,1,470.0,10.0,212.0,1.2,, 22 | 56612,1,430.8,1.5,212.43,0.17,, 23 | 56612,1,429.1,1.0,212.58,0.15,, 24 | 56614,1,430.2,1.0,212.46,0.15,, 25 | 56636,1,425.5,1.0,212.51,0.15,, 26 | 56636,1,424.4,1.0,212.85,0.15,, 27 | 56637,1,425.3,1.0,212.47,0.16,, 28 | 56969,1,356.2,1.0,213.02,0.19,, 29 | 57114,1,317.3,0.9,213.13,0.20,, 30 | 57332,1,250.5,1.5,214.14,0.34,, 31 | 57361,1,240.2,1.1,213.58,0.34,, 32 | 57378,1,234.5,1.0,213.81,0.30,, 33 | 57408,1,222.6,2.1,214.84,0.44,, 34 | # 56999,1,350.51,3.20,212.60,0.66 # removing SPHERE epochs as in Nielsen+ 2020 Case 3 35 | # 57058,1,332.42,1.70,212.58,0.35 # this data point different from value in Nielsen+ 2020 36 | # 57296,1,262.02,1.78,213.02,0.48 37 | # 57356,1,242.05,2.51,213.30,0.74 38 | # 57382,1,234.84,1.80,213.79,0.51 39 | # 57407,1,227.23,1.55,213.15,0.46 40 | # 57473,1,203.66,1.42,213.90,0.46 41 | # 57494,1,197.49,2.36,213.88,0.83 42 | # 57647,1,142.36,2.34,214.62,1.10 43 | # 57675,1,134.50,2.46,215.50,1.22 44 | # 57710,1,127.12,6.44,215.80,3.37 45 | # 58378,1,140.46,3.12,29.71,1.67 46 | 57046,1,335.5,0.9,212.88,0.20,, 47 | 58382,1,141.9,5.3,28.16,1.82,, 48 | 58440,1,164.5,1.8,28.64,0.70,, 49 | 56643,1,,,,,-15.4,1.7 -------------------------------------------------------------------------------- /orbitize/example_data/hd206893b.csv: -------------------------------------------------------------------------------- 1 | epoch,object,raoff,decoff,raoff_err,decoff_err 2 | 57298,1,253.72,92.35,2.98,2.85 3 | 57606,1,236.63,127.94,9.77,9.18 4 | 57645,1,234.52,123.39,1.79,1.03 5 | 57946,1,210.76,152.09,1.94,1.88 6 | 58276,1,167.49,180.87,1.61,16.97 7 | 58287,1,177.67,174.6,1.67,1.67 8 | 58365,1,165.7,185.33,3.28,3.66 9 | 58368,1,170.38,185.94,2.52,2.74 10 | 58414,1,161.64,176.21,13.6,14.31 -------------------------------------------------------------------------------- /orbitize/example_data/old_orbitize_format.csv: -------------------------------------------------------------------------------- 1 | # old orbitize format without covariance or instrument. 2 | epoch,object,quant1,quant1_err,quant2,quant2_err,quant_type 3 | 55645.95,1,2479,16,327.94,0.39,seppa 4 | 55645.95,1,2479,16,327.94,0.39,radec 5 | 55645.95,1,2479,16,nan,nan,rv 6 | -------------------------------------------------------------------------------- /orbitize/example_data/sample_radvel_chains.csv.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sblunt/orbitize/1f91aac848a84df52c9f0f66b8f4c6156a0f7e08/orbitize/example_data/sample_radvel_chains.csv.bz2 -------------------------------------------------------------------------------- /orbitize/example_data/test_val.csv: -------------------------------------------------------------------------------- 1 | epoch,object,raoff,raoff_err,decoff,decoff_err,sep,sep_err,pa,pa_err,rv,rv_err 2 | 1234,1,0.01,0.005,0.5,0.05,,,,,, 3 | 1235,1,,,,,1,0.005,89,0.1,, 4 | 1236,1,,,,,1,0.005,89.3,0.3,, 5 | 1237,0,,,,,,,,,10,0.1 -------------------------------------------------------------------------------- /orbitize/example_data/test_val_cov.csv: -------------------------------------------------------------------------------- 1 | epoch,object,raoff,raoff_err,decoff,decoff_err,radec_corr,sep,sep_err,pa,pa_err,seppa_corr,rv,rv_err 2 | 1234,1,0.010,0.005,0.50,0.05,0.25,,,,,,, 3 | 1235,1,,,,,,1.0,0.005,89.0,0.1,,, 4 | 1236,1,,,,,,1.0,0.005,89.3,0.3,-0.5,, 5 | 1237,0,,,,,,,,,,,10,0.1 -------------------------------------------------------------------------------- /orbitize/example_data/test_val_multi.csv: -------------------------------------------------------------------------------- 1 | epoch,object,raoff,raoff_err,decoff,decoff_err,sep,sep_err,pa,pa_err,rv,rv_err 2 | 53200,1,1471,6,887,6,,,,,, 3 | 56226,1,1549,4,743,4,,,,,, 4 | 56130,2,-578,5,761,5,,,,,, 5 | 56226,2,-572,3,768,3,,,,,, -------------------------------------------------------------------------------- /orbitize/example_data/test_val_radec.csv: -------------------------------------------------------------------------------- 1 | epoch,object,raoff,raoff_err,decoff,decoff_err,sep,sep_err,pa,pa_err 2 | 1234,1,0.010,0.005,0.50,0.05,,,, 3 | 1235,1,0.015,0.005,0.60,0.05,,,, 4 | 1236,1,0.020,0.005,0.70,0.05,,,, 5 | 1237,0,0.025,0.005,0.80,0.05,,,, 6 | 1238,0,,,,,0.025,0.005,0.80,0.05 7 | -------------------------------------------------------------------------------- /orbitize/example_data/test_val_rv.csv: -------------------------------------------------------------------------------- 1 | epoch,object,raoff,raoff_err,decoff,decoff_err,sep,sep_err,pa,pa_err,rv,rv_err 2 | 10,1,0.01,0.005,0.5,0.05,,,,,, 3 | 65,1,,,,,1,0.005,89,0.1,, 4 | 186,1,,,,,1,0.005,89.3,0.3,, 5 | 423,0,,,,,,,,,10,0.1 6 | 500,0,,,,,,,,,100,1 7 | 754,0,,,,,,,,,15,1.5 8 | 1235,0,,,,,,,,,12,0.015 -------------------------------------------------------------------------------- /orbitize/example_data/v1_posterior.hdf5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sblunt/orbitize/1f91aac848a84df52c9f0f66b8f4c6156a0f7e08/orbitize/example_data/v1_posterior.hdf5 -------------------------------------------------------------------------------- /orbitize/example_data/xyz_test_data.csv: -------------------------------------------------------------------------------- 1 | epoch,object,raoff,raoff_err,decoff,decoff_err 2 | 10000,1,-801.7987548,1,-1650.004139,1 3 | 10170,1,-830.8451196,1,-1631.354641,1 4 | 10340,1,-856.576718,1,-1613.684225,1 5 | 10000,2,-722.566,1,1922.45,1 6 | 10103,2,-713.998,1,1927.05,1 7 | 10206,2,-705.417,1,1931.62,1 -------------------------------------------------------------------------------- /orbitize/gpu_context.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import numpy as np 4 | 5 | import pycuda.driver as cuda 6 | import pycuda.autoinit 7 | from pycuda.compiler import SourceModule 8 | 9 | class gpu_context: 10 | """ 11 | GPU context which manages the allocation of memory, the movement of memory between python and the GPU, 12 | and the calling of GPU funcitons 13 | 14 | Written: Devin Cody, 2021 15 | """ 16 | def __init__(self, len_gpu_arrays = 10000000): 17 | self.gpu_initalized = False 18 | self.len_gpu_arrays = len_gpu_arrays 19 | 20 | try: 21 | print("Compiling kernel") 22 | if "win" in sys.platform: 23 | f_newton = open(os.path.dirname(__file__) + "\\kernels\\newton.cu", 'r') 24 | f_mikkola = open(os.path.dirname(__file__) + "\\kernels\\mikkola.cu", 'r') 25 | else: 26 | f_newton = open(os.path.dirname(__file__) + "/kernels/newton.cu", 'r') 27 | f_mikkola = open(os.path.dirname(__file__) + "/kernels/mikkola.cu", 'r') 28 | 29 | fstr_newton = "".join(f_newton.readlines()) 30 | mod_newton = SourceModule(fstr_newton) 31 | self.newton_gpu = mod_newton.get_function("newton_gpu") 32 | 33 | fstr_mikkola = "".join(f_mikkola.readlines()) 34 | mod_mikkola = SourceModule(fstr_mikkola) 35 | self.mikkola_gpu = mod_mikkola.get_function("mikkola_gpu") 36 | 37 | print("Allocating with {} bytes".format(self.len_gpu_arrays)) 38 | self.tolerance = np.array([1e-9], dtype = np.float64) 39 | self.max_iter = np.array([100]) 40 | self.eanom = None 41 | 42 | self.d_manom = cuda.mem_alloc(self.len_gpu_arrays) 43 | self.d_ecc = cuda.mem_alloc(self.len_gpu_arrays) 44 | self.d_eanom = cuda.mem_alloc(self.len_gpu_arrays) 45 | 46 | self.d_tol = cuda.mem_alloc(self.tolerance.nbytes) 47 | self.d_max_iter = cuda.mem_alloc(self.max_iter.nbytes) 48 | 49 | print("Copying parameters to GPU") 50 | cuda.memcpy_htod(self.d_tol, self.tolerance) 51 | cuda.memcpy_htod(self.d_max_iter, self.max_iter) 52 | gpu_initalized = True 53 | except Exception as e: 54 | print("Error: KEPLER: Unable to initialize Kepler GPU solver context") 55 | raise(e) 56 | 57 | def newton(self, manom, ecc, eanom, eanom0 = None, tolerance=1e-9, max_iter=100): 58 | """ 59 | Moves numpy arrays onto the GPU memory, calls the Newton-Raphson solver for eccentric anomaly 60 | and copies the result back into a numpy array. 61 | 62 | Args: 63 | manom (np.array): array of mean anomalies 64 | ecc (np.array): array of eccentricities 65 | eanom (np.array): array of eccentric anomalies (return by reference) 66 | eanom0 (np.array, optional): array of first guess for eccentric anomaly, same shape as manom (optional) 67 | Return: 68 | None: eanom is changed by reference 69 | 70 | Written: Devin Cody, 2021 71 | 72 | """ 73 | # Check to make sure we have enough data to process orbits 74 | if (self.len_gpu_arrays < manom.nbytes): 75 | self.len_gpu_arrays = manom.nbytes 76 | self.d_manom = cuda.mem_alloc(self.len_gpu_arrays) 77 | self.d_ecc = cuda.mem_alloc(self.len_gpu_arrays) 78 | self.d_eanom = cuda.mem_alloc(self.len_gpu_arrays) 79 | 80 | cuda.memcpy_htod(self.d_manom, manom) 81 | cuda.memcpy_htod(self.d_ecc, ecc) 82 | cuda.memcpy_htod(self.d_tol, tolerance) 83 | cuda.memcpy_htod(self.d_max_iter, max_iter) 84 | 85 | # Initialize at E=M, E=pi is better at very high eccentricities 86 | if eanom0 is None: 87 | cuda.memcpy_dtod(self.d_eanom, self.d_manom, self.len_gpu_arrays) 88 | else: 89 | cuda.memcpy_htod(self.d_eanom, eanom0) 90 | 91 | self.newton_gpu(self.d_manom, self.d_ecc, self.d_eanom, self.d_max_iter, self.d_tol, grid = (len(manom)//64+1,1,1), block = (64,1,1)) 92 | cuda.memcpy_dtoh(eanom, self.d_eanom) 93 | 94 | def mikkola(self, manom, ecc, eanom): 95 | """ 96 | Moves numpy arrays onto the GPU memory, calls the analtyical Mikkola solver for eccentric anomaly 97 | and copies the result back into a numpy array. 98 | 99 | Args: 100 | manom (np.array): array of mean anomalies between 0 and 2pi 101 | ecc (np.array): eccentricity 102 | eanom (np.array): array of eccentric anomalies (return by reference) 103 | Return: 104 | None: eanom is changed by reference 105 | 106 | Written: Devin Cody, 2021 107 | """ 108 | # Check to make sure we have enough data to process orbits 109 | if (self.len_gpu_arrays < manom.nbytes): 110 | self.len_gpu_arrays = manom.nbytes 111 | self.d_manom = cuda.mem_alloc(self.len_gpu_arrays) 112 | self.d_ecc = cuda.mem_alloc(self.len_gpu_arrays) 113 | 114 | cuda.memcpy_htod(self.d_manom, manom) 115 | cuda.memcpy_htod(self.d_ecc, ecc) 116 | 117 | self.mikkola_gpu(self.d_manom, self.d_ecc, self.d_eanom, grid = (len(manom)//64+1,1,1), block = (64,1,1)) 118 | cuda.memcpy_dtoh(eanom, self.d_eanom) -------------------------------------------------------------------------------- /orbitize/kepler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifndef M_PI 5 | #define M_PI 3.14159265358979323846 /* pi */ 6 | #endif 7 | 8 | void newton_array(const int n_elements, 9 | const double manom[], 10 | const double ecc[], 11 | const double tol, 12 | const int max_iter, 13 | double eanom[]){ 14 | /* 15 | Vectorized C Newton-Raphson solver for eccentric anomaly. 16 | 17 | Args: 18 | manom (double[]): array of mean anomalies 19 | ecc (double[]): array of eccentricities 20 | eanom0 (double[]): array of first guess for eccentric anomaly, same shape as manom (optional) 21 | Return: 22 | None: eanom (double[]): is changed by reference 23 | 24 | Written: Devin Cody, 2018 25 | */ 26 | int i; 27 | for (i = 0; i < n_elements; i ++){ 28 | double diff; 29 | int niter = 0; 30 | int half_max = max_iter/2.0; // divide max_iter by 2 using bit shift 31 | 32 | // Let's do one iteration to start with 33 | eanom[i] -= (eanom[i] - (ecc[i] * sin(eanom[i])) - manom[i]) / (1.0 - (ecc[i] * cos(eanom[i]))); 34 | diff = (eanom[i] - (ecc[i] * sin(eanom[i])) - manom[i]) / (1.0 - (ecc[i] * cos(eanom[i]))); 35 | 36 | while ((fabs(diff) > tol) && (niter <= max_iter)){ 37 | eanom[i] -= diff; 38 | 39 | // If it hasn't converged after half the iterations are done, try starting from pi 40 | if (niter == half_max) { 41 | eanom[i] = M_PI; 42 | } 43 | 44 | diff = (eanom[i] - (ecc[i] * sin(eanom[i])) - manom[i]) / (1.0 - (ecc[i] * cos(eanom[i]))); 45 | niter += 1; 46 | } 47 | 48 | // If it has not converged, set eccentricity to -1 to signal that it needs to be 49 | // solved using the analytical version. Note this behavior is a bit different from the 50 | // numpy implementation 51 | if (niter >= max_iter){ 52 | printf("%f %f %f %f >= %d iter\n", manom[i], eanom[i], diff, ecc[i], max_iter); 53 | eanom[i] = -1; 54 | } 55 | } 56 | } 57 | 58 | 59 | void mikkola_array(const int n_elements, const double manom[], const double ecc[], double eanom[]){ 60 | /* 61 | Vectorized C Analtyical Mikkola solver for the eccentric anomaly. 62 | See: S. Mikkola. 1987. Celestial Mechanics, 40, 329-334. 63 | Adapted from IDL routine keplereq.pro by Rob De Rosa http://www.lpl.arizona.edu/~bjackson/idl_code/keplereq.pro 64 | 65 | Args: 66 | manom (double[]): mean anomaly, must be between 0 and pi. 67 | ecc (double[]): eccentricity 68 | eanom0 (double[]): array for eccentric anomaly 69 | Return: 70 | None: eanom (double[]): is changed by reference 71 | 72 | Written: Devin Cody, 2019 73 | */ 74 | 75 | int i; 76 | double alpha, beta, aux, z, s0, s1, se0, ce0; 77 | double f, f1, f2, f3, f4, u1, u2, u3; 78 | 79 | for (i = 0; i < n_elements; i++){ 80 | alpha = (1.0 - ecc[i]) / ((4.0 * ecc[i]) + 0.5); 81 | beta = (0.5 * manom[i]) / ((4.0 * ecc[i]) + 0.5); 82 | 83 | aux = sqrt(beta*beta + alpha*alpha*alpha); 84 | z = pow(fabs(beta + aux), (1.0/3.0)); 85 | 86 | s0 = z - (alpha/z); 87 | s1 = s0 - (0.078*(pow(s0, 5))) / (1.0 + ecc[i]); 88 | eanom[i] = manom[i] + (ecc[i] * (3.0*s1 - 4.0*(s1*s1*s1))); 89 | 90 | se0=sin(eanom[i]); 91 | ce0=cos(eanom[i]); 92 | 93 | f = eanom[i]-ecc[i]*se0-manom[i]; 94 | f1 = 1.0-ecc[i]*ce0; 95 | f2 = ecc[i]*se0; 96 | f3 = ecc[i]*ce0; 97 | f4 = -f2; 98 | u1 = -f/f1; 99 | u2 = -f/(f1+0.5*f2*u1); 100 | u3 = -f/(f1+0.5*f2*u2+(1.0/6.0)*f3*u2*u2); 101 | eanom[i] += -f/(f1+0.5*f2*u3+(1.0/6.0)*f3*u3*u3+(1.0/24.0)*f4*(u3*u3*u3)); 102 | } 103 | } 104 | 105 | 106 | int main(void){ 107 | // Test functions with a small program 108 | 109 | // Define variables for newton array method 110 | double m[] = {.5, 1, 1.5}; 111 | double ecc[] = {.25, .75, .83}; 112 | double tol = 1e-9; 113 | int mi = 100; 114 | double eanom[] = {0, 0, 0}; 115 | 116 | // test newton_array 117 | // Answer should be: [ 0.65161852, 1.73936894, 2.18046524]) 118 | 119 | newton_array(3, m, ecc, tol, mi, eanom); 120 | int i; 121 | for (i = 0; i < 3; i++){ 122 | printf("eanom[%d] = %f\n", i, eanom[i]); 123 | eanom[i] = 0; 124 | } 125 | 126 | // test mikkola_array 127 | // Answer should be: [ 0.65161852, 1.73936894, 2.18046524]) 128 | 129 | mikkola_array(3, m, ecc, eanom); 130 | for (i = 0; i < 3; i++){ 131 | printf("eanom[%d] = %f\n", i, eanom[i]); 132 | } 133 | 134 | return 0; 135 | } -------------------------------------------------------------------------------- /orbitize/kernels/mikkola.cu: -------------------------------------------------------------------------------- 1 | #ifndef M_PI 2 | #define M_PI 3.14159265358979323846 /* pi */ 3 | #endif 4 | 5 | __global__ void mikkola_gpu(const double *manom, const double *ecc, double *eanom){ 6 | /* 7 | Vectorized C Analtyical Mikkola solver for the eccentric anomaly. 8 | See: S. Mikkola. 1987. Celestial Mechanics, 40, 329-334. 9 | Adapted from IDL routine keplereq.pro by Rob De Rosa http://www.lpl.arizona.edu/~bjackson/idl_code/keplereq.pro 10 | Args: 11 | manom (double[]): mean anomaly, must be between 0 and pi. 12 | ecc (double[]): eccentricity 13 | eanom0 (double[]): array for eccentric anomaly 14 | Return: 15 | None: eanom (double[]): is changed by reference 16 | Written: Devin Cody, 2019 17 | */ 18 | 19 | int i = threadIdx.x + blockIdx.x*blockDim.x; 20 | double alpha, beta, aux, z, s0, s1, se0, ce0; 21 | double f, f1, f2, f3, f4, u1, u2, u3; 22 | 23 | alpha = (1.0 - ecc[i]) / ((4.0 * ecc[i]) + 0.5); 24 | beta = (0.5 * manom[i]) / ((4.0 * ecc[i]) + 0.5); 25 | 26 | aux = sqrt(beta*beta + alpha*alpha*alpha); 27 | z = pow(fabs(beta + aux), (1.0/3.0)); 28 | 29 | s0 = z - (alpha/z); 30 | s1 = s0 - (0.078*(pow(s0, 5))) / (1.0 + ecc[i]); 31 | eanom[i] = manom[i] + (ecc[i] * (3.0*s1 - 4.0*(s1*s1*s1))); 32 | 33 | se0=sin(eanom[i]); 34 | ce0=cos(eanom[i]); 35 | 36 | f = eanom[i]-ecc[i]*se0-manom[i]; 37 | f1 = 1.0-ecc[i]*ce0; 38 | f2 = ecc[i]*se0; 39 | f3 = ecc[i]*ce0; 40 | f4 = -f2; 41 | u1 = -f/f1; 42 | u2 = -f/(f1+0.5*f2*u1); 43 | u3 = -f/(f1+0.5*f2*u2+(1.0/6.0)*f3*u2*u2); 44 | eanom[i] += -f/(f1+0.5*f2*u3+(1.0/6.0)*f3*u3*u3+(1.0/24.0)*f4*(u3*u3*u3)); 45 | } -------------------------------------------------------------------------------- /orbitize/kernels/newton.cu: -------------------------------------------------------------------------------- 1 | 2 | #ifndef M_PI 3 | #define M_PI 3.14159265358979323846 /* pi */ 4 | #endif 5 | 6 | 7 | __global__ void newton_gpu(const double *manom, 8 | const double *ecc, 9 | double *eanom, 10 | const int *max_iter, 11 | const double *tol){ 12 | /* 13 | Vectorized C++ Newton-Raphson solver for eccentric anomaly. 14 | Args: 15 | manom (double[]): array of mean anomalies 16 | ecc (double[]): array of eccentricities 17 | eanom0 (double[]): array of first guess for eccentric anomaly, same shape as manom (optional) 18 | Return: 19 | None: eanom is changed by reference 20 | Written: Devin Cody, 2018 21 | */ 22 | int i = threadIdx.x + blockIdx.x*blockDim.x; 23 | double diff; 24 | int niter = 0; 25 | int half_max = *max_iter/2.0; // divide convergence->max_iter by 2 using bit shift 26 | 27 | // Let's do one iteration to start with 28 | eanom[i] -= (eanom[i] - (ecc[i] * sin(eanom[i])) - manom[i]) / (1.0 - (ecc[i] * cos(eanom[i]))); 29 | diff = (eanom[i] - (ecc[i] * sin(eanom[i])) - manom[i]) / (1.0 - (ecc[i] * cos(eanom[i]))); 30 | 31 | while ((fabs(diff) > *tol) && (niter <= *max_iter)){ 32 | eanom[i] -= diff; 33 | 34 | // If it hasn't converged after half the iterations are done, try starting from pi 35 | if (niter == half_max) { 36 | eanom[i] = M_PI; 37 | } 38 | 39 | diff = (eanom[i] - (ecc[i] * sin(eanom[i])) - manom[i]) / (1.0 - (ecc[i] * cos(eanom[i]))); 40 | niter += 1; 41 | } 42 | 43 | // If it has not converged, set eccentricity to -1 to signal that it needs to be 44 | // solved using the analytical version. Note this behavior is a bit different from the 45 | // numpy implementation 46 | if (niter >= *max_iter){ 47 | printf("%f %f %f %f >= %d iter\n", manom[i], eanom[i], diff, ecc[i], *max_iter); 48 | eanom[i] = -1; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /orbitize/lnlike.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | """ 4 | This module contains functions for computing log(likelihood). 5 | """ 6 | 7 | 8 | def chi2_lnlike( 9 | data, errors, corrs, model, jitter, seppa_indices, chi2_type="standard" 10 | ): 11 | """Compute Log of the chi2 Likelihood 12 | 13 | Args: 14 | data (np.array): Nobsx2 array of data, where data[:,0] = sep/RA/RV 15 | for every epoch, and data[:,1] = corresponding pa/DEC/np.nan. 16 | errors (np.array): Nobsx2 array of errors for each data point. Same 17 | format as ``data``. 18 | corrs (np.array): Nobs array of Pearson correlation coeffs 19 | between the two quantities. If there is none, can be None. 20 | model (np.array): Nobsx2xM array of model predictions, where M is the \ 21 | number of orbits being compared against the data. If M is 1, \ 22 | ``model`` can be 2 dimensional. 23 | jitter (np.array): Nobsx2xM array of jitter values to add to errors. 24 | Elements of array should be 0 for for all data other than stellar \ 25 | rvs. 26 | seppa_indices (list): list of epoch numbers whose observations are 27 | given in sep/PA. This list is located in System.seppa. 28 | chi2_type (string): the format of chi2 to use is either 'standard' or \ 29 | 'log' 30 | 31 | Returns: 32 | np.array: Nobsx2xM array of chi-squared values. 33 | 34 | .. note:: 35 | 36 | (1) **Example**: We have 8 epochs of data for a system. OFTI returns an 37 | array of 10,000 sets of orbital parameters. The ``model`` input for 38 | this function should be an array of dimension 8 x 2 x 10,000. 39 | 40 | (2) Chi2_log: redefining chi-sqaured in log scale may give a more stable optimization. \ 41 | This works on separation and position angle data (seppa) not right ascension and declination \ 42 | (radec) data, but it is possible to convert between the two within Orbitize! using the \ 43 | function 'orbitize.system'radec2seppa' (see docuemntation). This implementation defines sep chi-squared \ 44 | in log scale, and defines pa chi-sq using complex phase representation. 45 | log sep chisq = (log sep - log sep_true)^2 / (sep_sigma / sep_true)^2 46 | pa chisq = 2 * (1 - cos(pa-pa_true))/pa_sigma^2 47 | i 48 | """ 49 | 50 | if np.ndim(model) == 3: 51 | # move M dimension to the primary axis, so that numpy knows to iterate over it 52 | model = np.rollaxis(model, 2, 0) # now MxNobsx2 in dimensions 53 | jitter = np.rollaxis(jitter, 2, 0) 54 | third_dim = True 55 | elif np.ndim(model) == 2: 56 | model.shape = (1,) + model.shape 57 | jitter.shape = (1,) + jitter.shape 58 | third_dim = False 59 | 60 | if chi2_type == "standard": 61 | residual = data - model 62 | # if there are PA values, we should take the difference modulo angle wrapping 63 | if np.size(seppa_indices) > 0: 64 | residual[:, seppa_indices, 1] = ( 65 | residual[:, seppa_indices, 1] + 180.0 66 | ) % 360.0 - 180.0 67 | 68 | sigma2 = errors**2 + jitter**2 # diagonal error term 69 | 70 | if corrs is None: 71 | # including the second term of chi2 72 | # the sqrt() in the log() means we don't need to multiply by 0.5 73 | chi2 = -0.5 * residual**2 / sigma2 - np.log(np.sqrt(2 * np.pi * sigma2)) 74 | else: 75 | has_no_corr = np.isnan(corrs) 76 | yes_corr = np.where(~has_no_corr)[0] 77 | no_corr = np.where(has_no_corr)[0] 78 | 79 | chi2 = np.zeros(residual.shape) 80 | chi2[:, no_corr] = -0.5 * residual[:, no_corr] ** 2 / sigma2[ 81 | :, no_corr 82 | ] - np.log(np.sqrt(2 * np.pi * sigma2[:, no_corr])) 83 | 84 | # analytical solution for 2x2 covariance matrix 85 | # chi2 = -0.5 * (R^T C^-1 R + ln(det_C)) 86 | chi2[:, yes_corr] = _chi2_2x2cov( 87 | residual[:, yes_corr], sigma2[:, yes_corr], corrs[yes_corr] 88 | ) 89 | 90 | elif chi2_type == "log": 91 | # using the log version of chi squared 92 | # split the data up into sep, pa, and rv data using seppa_indices and quant 93 | sep_data = data[seppa_indices, 0] 94 | sep_model = model[:, seppa_indices, 0] 95 | sep_error = errors[seppa_indices, 0] 96 | pa_data = data[seppa_indices, 1] 97 | pa_model = model[:, seppa_indices, 1] 98 | pa_error = errors[seppa_indices, 1] * np.pi / 180 99 | 100 | # calculating sep chi squared 101 | sep_chi2_log = (np.log(sep_data) - np.log(sep_model)) ** 2 / ( 102 | sep_error / sep_data 103 | ) ** 2 104 | 105 | # calculting pa chi squared Log 106 | pa_resid = (pa_model - pa_data + 180.0) % 360.0 - 180.0 107 | pa_chi2_log = 2 * (1 - np.cos(pa_resid * np.pi / 180)) / pa_error**2 108 | 109 | residual = data - model 110 | sigma2 = errors**2 + jitter**2 # diagonal error term 111 | 112 | chi2 = residual**2 / sigma2 113 | chi2[:, seppa_indices, 0] = sep_chi2_log 114 | chi2[:, seppa_indices, 1] = pa_chi2_log 115 | 116 | chi2 = -0.5 * chi2 - np.log(np.sqrt(2 * np.pi * sigma2)) 117 | 118 | if third_dim: 119 | # move M dimension back to the last axis 120 | model = np.rollaxis(model, 0, 3) # now MxNobsx2 in dimensions 121 | jitter = np.rollaxis(jitter, 0, 3) 122 | chi2 = np.rollaxis(chi2, 0, 3) # same with chi2 123 | else: 124 | model.shape = model.shape[1:] 125 | chi2.shape = chi2.shape[1:] 126 | jitter.shape = jitter.shape[1:] 127 | 128 | return chi2 129 | 130 | 131 | def _chi2_2x2cov(residual, var, corrs): 132 | """ 133 | Analytical solution for when quant1/quant2 have a covariance term 134 | So we don't need to calculate matrix inverses when the jitter varies depending on the model 135 | 136 | Args: 137 | residual (np.array): MxNobsx2 array of fit residuals, 138 | var (np.array): MxNobsx2 array of variance for each residual 139 | corrs (np.array): Nobs array of off axis Pearson corr coeffs 140 | between the two quantities. 141 | 142 | Returns: 143 | np.array: MxNobsx2 array of chi2. Becuase of x/y coariance, it's impossible to 144 | spearate the quant1/quant2 chi2. Thus, all the chi2 is in the first term 145 | and the second dimension is 0 146 | """ 147 | 148 | det_C = var[:, :, 0] * var[:, :, 1] * (1 - corrs**2) 149 | 150 | covs = corrs * np.sqrt(var[:, :, 0]) * np.sqrt(var[:, :, 1]) 151 | chi2 = ( 152 | residual[:, :, 0] ** 2 * var[:, :, 1] 153 | + residual[:, :, 1] ** 2 * var[:, :, 0] 154 | - 2 * residual[:, :, 0] * residual[:, :, 1] * covs 155 | ) / det_C 156 | 157 | chi2 += np.log(det_C) + 2 * np.log( 158 | 2 * np.pi 159 | ) # extra factor of 2 since quant1 and quant2 in each element of chi2. 160 | 161 | chi2 *= -0.5 162 | 163 | chi2 = np.stack([chi2, np.zeros(chi2.shape)], axis=2) 164 | 165 | return chi2 166 | 167 | 168 | def chi2_norm_term(errors, corrs): 169 | """ 170 | Return only the normalization term of the Gaussian likelihood: 171 | 172 | .. math:: 173 | 174 | -log(\\sqrt(2\\pi*error^2)) 175 | 176 | or 177 | 178 | .. math:: 179 | 180 | -0.5 * (log(det(C)) + N * log(2\\pi)) 181 | 182 | Args: 183 | errors (np.array): Nobsx2 array of errors for each data point. Same 184 | format as ``data``. 185 | corrs (np.array): Nobs array of Pearson correlation coeffs 186 | between the two quantities. If there is none, can be None. 187 | 188 | Returns: 189 | float: sum of the normalization terms 190 | """ 191 | sigma2 = errors**2 192 | 193 | if corrs is None: 194 | norm = -np.log(np.sqrt(2 * np.pi * sigma2)) 195 | else: 196 | has_no_corr = np.isnan(corrs) 197 | yes_corr = np.where(~has_no_corr)[0] 198 | no_corr = np.where(has_no_corr)[0] 199 | 200 | norm = np.zeros(errors.shape) 201 | norm[no_corr] = -np.log(np.sqrt(2 * np.pi * sigma2[no_corr])) 202 | 203 | det_C = ( 204 | sigma2[yes_corr[0], 0] * sigma2[yes_corr[0], 1] * (1 - corrs[yes_corr] ** 2) 205 | ) 206 | norm[yes_corr, 0] = -0.5 * ( 207 | det_C + 2 * np.log(2 * np.pi) 208 | ) # extra factor of 2 since quant1 and quant2 in each element of chi2. 209 | 210 | return np.sum(norm) 211 | -------------------------------------------------------------------------------- /orbitize/nbody.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import orbitize.basis as basis 3 | import rebound 4 | 5 | 6 | def calc_orbit( 7 | epochs, 8 | sma, 9 | ecc, 10 | inc, 11 | aop, 12 | pan, 13 | tau, 14 | plx, 15 | mtot, 16 | tau_ref_epoch=58849, 17 | m_pl=None, 18 | output_star=False, 19 | integrator="ias15", 20 | ): 21 | """ 22 | Solves for position for a set of input orbital elements using rebound. 23 | 24 | Args: 25 | epochs (np.array): MJD times for which we want the positions of the planet 26 | sma (np.array): semi-major axis array of secondary bodies. For three planets, 27 | this should look like: np.array([sma1, sma2, sma3]) [au] 28 | ecc (np.array): eccentricity of the orbits (same format as sma) [0,1] 29 | inc (np.array): inclinations (same format as sma) [radians] 30 | aop (np.array): arguments of periastron (same format as sma) [radians] 31 | pan (np.array): longitudes of the ascending node (same format as sma) [radians] 32 | tau (np.array): epochs of periastron passage in fraction of orbital period 33 | past MJD=0 (same format as sma) [0,1] 34 | plx (float): parallax [mas] 35 | mtot (float): total mass of the two-body orbit (M_* + M_planet) 36 | [Solar masses] 37 | tau_ref_epoch (float, optional): reference date that tau is defined with 38 | respect to 39 | m_pl (np.array, optional): masss of the planets (same format as sma) [solar masses] 40 | output_star (bool): if True, also return the position of the star 41 | relative to the barycenter. 42 | integrator (str): value to set for rebound.sim.integrator. Default "ias15" 43 | 44 | Returns: 45 | 3-tuple: 46 | 47 | raoff (np.array): array-like (n_dates x n_bodies x n_orbs) of RA offsets between 48 | the bodies (origin is at the other body) [mas] 49 | 50 | deoff (np.array): array-like (n_dates x n_bodies x n_orbs) of Dec offsets between 51 | the bodies [mas] 52 | 53 | vz (np.array): array-like (n_dates x n_bodies x n_orbs) of radial velocity of 54 | one of the bodies (see `mass_for_Kamp` description) [km/s] 55 | """ 56 | 57 | sim = rebound.Simulation() # creating the simulation in Rebound 58 | sim.units = ("AU", "yr", "Msun") # converting units for uniformity 59 | sim.G = 39.476926408897626 # Using a more accurate value in order to minimize differences from prev. Kepler solver 60 | ps = sim.particles # for easier calls 61 | 62 | tx = len(epochs) # keeping track of how many time steps 63 | te = epochs - epochs[0] # days 64 | 65 | indv = len(sma) # number of planets orbiting the star 66 | num_planets = np.arange( 67 | 0, indv 68 | ) # creates an array of indeces for each planet that exists 69 | 70 | if ( 71 | m_pl is None 72 | ): # if no planet masses are input, planet masses set ot zero and mass of star is equal to mtot 73 | sim.add(m=mtot) 74 | m_pl = np.zeros(len(sma)) 75 | m_star = mtot 76 | else: # mass of star is always (mass of system)-(sum of planet masses) 77 | m_star = mtot - sum(m_pl) 78 | sim.add(m=m_star) 79 | 80 | # for each planet, create a body in the Rebound sim 81 | for i in num_planets: 82 | # calculating mean anomaly 83 | m_interior = m_star + sum(m_pl[0 : i + 1]) 84 | mnm = basis.tau_to_manom(epochs[0], sma[i], m_interior, tau[i], tau_ref_epoch) 85 | # adding each planet 86 | sim.add( 87 | m=m_pl[i], 88 | a=sma[i], 89 | e=ecc[i], 90 | inc=inc[i], 91 | Omega=pan[i] + np.pi / 2, 92 | omega=aop[i], 93 | M=mnm, 94 | ) 95 | 96 | sim.move_to_com() 97 | sim.integrator = integrator 98 | sim.dt = ( 99 | ps[1].P / 100.0 100 | ) # good rule of thumb: timestep should be at most 10% of the shortest orbital period 101 | 102 | if output_star: 103 | ra_reb = np.zeros( 104 | (tx, indv + 1) 105 | ) # numpy.zeros(number of [arrays], size of each array) 106 | dec_reb = np.zeros((tx, indv + 1)) 107 | vz = np.zeros((tx, indv + 1)) 108 | for j, t in enumerate(te): 109 | sim.integrate(t / 365.25) 110 | # for the star and each planet in each epoch denoted by j,t find the RA, Dec, and RV 111 | com = sim.com() 112 | ra_reb[j, 0] = -(ps[0].x - com.x) # ra is negative x 113 | dec_reb[j, 0] = ps[0].y - com.y 114 | vz[j, 0] = ps[0].vz 115 | for i in num_planets: 116 | ra_reb[j, i + 1] = -(ps[int(i + 1)].x - ps[0].x) # ra is negative x 117 | dec_reb[j, i + 1] = ps[int(i + 1)].y - ps[0].y 118 | vz[j, i + 1] = ps[int(i + 1)].vz 119 | else: 120 | ra_reb = np.zeros( 121 | (tx, indv) 122 | ) # numpy.zeros(number of [arrays], size of each array) 123 | dec_reb = np.zeros((tx, indv)) 124 | vz = np.zeros((tx, indv)) 125 | # integrate at each epoch 126 | for j, t in enumerate(te): 127 | sim.integrate(t / 365.25) 128 | # for each planet in each epoch denoted by j,t find the RA, Dec, and RV 129 | for i in num_planets: 130 | ra_reb[j, i] = -(ps[int(i + 1)].x - ps[0].x) # ra is negative x 131 | dec_reb[j, i] = ps[int(i + 1)].y - ps[0].y 132 | vz[j, i] = ps[int(i + 1)].vz 133 | 134 | # adjusting for parallax 135 | raoff = plx * ra_reb 136 | deoff = plx * dec_reb 137 | 138 | # always assume we're using MCMC (i.e. n_orbits = 1) 139 | raoff = raoff.reshape((tx, indv + 1, 1)) 140 | deoff = deoff.reshape((tx, indv + 1, 1)) 141 | vz = vz.reshape((tx, indv + 1, 1)) 142 | 143 | return raoff, deoff, vz 144 | -------------------------------------------------------------------------------- /orbitize/radvel_utils/compute_sep.py: -------------------------------------------------------------------------------- 1 | def compute_sep(df, epochs, basis, m0, m0_err, plx, plx_err, n_planets=1, pl_num=1): 2 | raise ModuleNotFoundError("This functionaity is deprecated. See sblunt/orbitize_radvel_utils.") -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | astropy>=4 2 | numpy 3 | scipy 4 | emcee>=3 5 | ptemcee_for_orbitize 6 | matplotlib 7 | corner 8 | h5py 9 | deprecation 10 | cython 11 | pytest 12 | pandas 13 | pyerfa 14 | astroquery 15 | sphinx>=6 16 | docutils<0.17 17 | rebound 18 | dynesty 19 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages, Extension 2 | import numpy, sys 3 | import re 4 | 5 | USE_C_KEPLER_MODULE = 1 6 | if "--disable-cython" in sys.argv: 7 | sys.argv.remove("--disable-cython") 8 | USE_C_KEPLER_MODULE = 0 9 | else: 10 | try: 11 | from Cython.Build import cythonize 12 | except: 13 | print("Error: Importing cython build environment failed") 14 | USE_C_KEPLER_MODULE = 0 15 | 16 | 17 | # auto-updating version code stolen from RadVel 18 | def get_property(prop, project): 19 | result = re.search( 20 | r'{}\s*=\s*[\'"]([^\'"]*)[\'"]'.format(prop), 21 | open(project + "/__init__.py").read(), 22 | ) 23 | return result.group(1) 24 | 25 | 26 | def get_requires(): 27 | reqs = [] 28 | for line in open("requirements.txt", "r").readlines(): 29 | reqs.append(line) 30 | return reqs 31 | 32 | 33 | def get_extensions(): 34 | extensions = [] 35 | if USE_C_KEPLER_MODULE: 36 | extensions = cythonize( 37 | [ 38 | Extension( 39 | "orbitize._kepler", 40 | ["orbitize/_kepler.pyx"], 41 | include_dirs=[numpy.get_include()], 42 | ) 43 | ] 44 | ) 45 | return extensions 46 | 47 | 48 | setup( 49 | name="orbitize", 50 | version=get_property("__version__", "orbitize"), 51 | description="orbitize! Turns imaging data into orbits", 52 | url="https://github.com/sblunt/orbitize", 53 | author="", 54 | author_email="", 55 | license="BSD", 56 | packages=find_packages(), 57 | package_data={"": ["kernels/*.cu"]}, 58 | ext_modules=get_extensions(), 59 | include_dirs=[numpy.get_include()], 60 | include_package_data=True, 61 | zip_safe=False, 62 | classifiers=[ 63 | "Intended Audience :: Science/Research", 64 | "Topic :: Scientific/Engineering :: Astronomy", 65 | "License :: OSI Approved :: BSD License", 66 | "Programming Language :: Python :: 3.6", 67 | ], 68 | keywords="Orbits Astronomy Astrometry", 69 | install_requires=get_requires(), 70 | ) 71 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | codeastro_mode = False 4 | 5 | 6 | def pytest_addoption(parser): 7 | parser.addoption( 8 | "--mode", 9 | action="store", 10 | metavar="NAME", 11 | help="different test modes NAME.", 12 | ) 13 | 14 | 15 | def pytest_runtest_setup(item): 16 | global codeastro_mode 17 | envnames = [mark.args[0] for mark in item.iter_markers(name="mode")] 18 | if envnames: 19 | if item.config.getoption("--mode") == "codeastro": 20 | codeastro_mode = True 21 | 22 | 23 | a1 = [ 24 | 83, 25 | 101, 26 | 99, 27 | 114, 28 | 101, 29 | 116, 30 | 32, 31 | 67, 32 | 111, 33 | 100, 34 | 101, 35 | 58, 36 | 32, 37 | 119, 38 | 101, 39 | 100, 40 | 105, 41 | 100, 42 | 105, 43 | 116, 44 | 33, 45 | ] 46 | a2 = [ 47 | 78, 48 | 111, 49 | 32, 50 | 115, 51 | 101, 52 | 99, 53 | 114, 54 | 101, 55 | 116, 56 | 32, 57 | 99, 58 | 111, 59 | 100, 60 | 101, 61 | 32, 62 | 121, 63 | 101, 64 | 116, 65 | 46, 66 | ] 67 | 68 | 69 | @pytest.hookimpl() 70 | def pytest_terminal_summary(terminalreporter, exitstatus, config): 71 | if config.getoption("--mode") == "codeastro": 72 | if terminalreporter._session.testsfailed == 0: 73 | vals = a1 74 | else: 75 | vals = a2 76 | output_str = "".join([chr(x) for x in vals]) 77 | print(output_str) 78 | -------------------------------------------------------------------------------- /tests/end-to-end-tests/betaPic_hipIAD.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import matplotlib.pyplot as plt 4 | 5 | import orbitize 6 | from orbitize import system, read_input, priors, sampler 7 | from orbitize.hipparcos import HipparcosLogProb 8 | from orbitize.gaia import GaiaLogProb 9 | 10 | """ 11 | Attempts to reproduce case 3 (see table 3) of Nielsen+ 2020 (orbit fits of beta 12 | Pic b). 13 | 14 | This is a publishable orbit fit that will take several hours-days to run. It 15 | uses relative astrometry, Hipparcos intermediate astrometric data (IAD), 16 | Gaia astrometric data, and a radial velocity measurement of beta Pic b. 17 | 18 | Set these "keywords:" 19 | 20 | - `fit_IAD` to True if you want to include the Hipparcos IAD. If False, 21 | just fits the relative astrometry. 22 | - `savedir` to where you want the fit outputs to be saved 23 | 24 | 25 | Begin keywords << 26 | """ 27 | fit_IAD = True 28 | 29 | if fit_IAD: 30 | savedir = "/data/user/{}/betaPic/hipIAD".format(os.getlogin()) 31 | else: 32 | savedir = "/data/user/{}/betaPic/noIAD".format(os.getlogin()) 33 | """ 34 | >> End keywords 35 | """ 36 | 37 | if not os.path.exists(savedir): 38 | os.mkdir(savedir) 39 | 40 | input_file = os.path.join(orbitize.DATADIR, "betaPic.csv") 41 | plx = 51.5 42 | 43 | num_secondary_bodies = 1 44 | data_table = read_input.read_file(input_file) 45 | 46 | if fit_IAD: 47 | hipparcos_number = "027321" 48 | gaia_edr3_number = 4792774797545800832 49 | gaia_dr2_number = 4792774797545105664 50 | fit_secondary_mass = True 51 | hipparcos_filename = os.path.join(orbitize.DATADIR, "HIP027321.d") 52 | betaPic_Hip = HipparcosLogProb( 53 | hipparcos_filename, hipparcos_number, num_secondary_bodies 54 | ) 55 | betaPic_gaia = GaiaLogProb(gaia_dr2_number, betaPic_Hip, dr="dr2") 56 | # betaPic_gaia = GaiaLogProb( 57 | # gaia_edr3_number, betaPic_Hip, dr='edr3' 58 | # ) 59 | else: 60 | fit_secondary_mass = False 61 | betaPic_Hip = None 62 | betaPic_gaia = None 63 | 64 | betaPic_system = system.System( 65 | num_secondary_bodies, 66 | data_table, 67 | 1.75, 68 | plx, 69 | hipparcos_IAD=betaPic_Hip, 70 | gaia=betaPic_gaia, 71 | fit_secondary_mass=fit_secondary_mass, 72 | mass_err=0.01, 73 | plx_err=0.01, 74 | ) 75 | 76 | m0_or_mtot_prior = priors.UniformPrior(1.5, 2.0) 77 | 78 | # set uniform parallax prior 79 | plx_index = betaPic_system.param_idx["plx"] 80 | betaPic_system.sys_priors[plx_index] = priors.UniformPrior(plx - 1.0, plx + 1.0) 81 | 82 | # set prior on Omega, since we know that know direction of orbital motion from RV 83 | pan_index = betaPic_system.param_idx["pan1"] 84 | betaPic_system.sys_priors[pan_index] = priors.UniformPrior(0, np.pi) 85 | 86 | # set uniform prior on m1 as Nielsen+ 2020 do 87 | m1_index = betaPic_system.param_idx["m1"] 88 | betaPic_system.sys_priors[m1_index] = priors.UniformPrior(0, 0.1) 89 | 90 | if fit_IAD: 91 | assert betaPic_system.fit_secondary_mass 92 | assert betaPic_system.track_planet_perturbs 93 | 94 | # set uniform m0 prior 95 | m0_index = betaPic_system.param_idx["m0"] 96 | betaPic_system.sys_priors[m0_index] = m0_or_mtot_prior 97 | 98 | else: 99 | assert not betaPic_system.fit_secondary_mass 100 | assert not betaPic_system.track_planet_perturbs 101 | 102 | # set uniform mtot prior 103 | mtot_index = betaPic_system.param_idx["mtot"] 104 | betaPic_system.sys_priors[mtot_index] = m0_or_mtot_prior 105 | 106 | # run MCMC 107 | num_threads = 100 108 | num_temps = 20 109 | num_walkers = 1000 110 | num_steps = 1000000 # n_walkers x n_steps_per_walker 111 | burn_steps = 1000 112 | thin = 100 113 | 114 | betaPic_sampler = sampler.MCMC( 115 | betaPic_system, 116 | num_threads=num_threads, 117 | num_temps=num_temps, 118 | num_walkers=num_walkers, 119 | ) 120 | betaPic_sampler.run_sampler(num_steps, burn_steps=burn_steps, thin=thin) 121 | 122 | # save chains 123 | betaPic_sampler.results.save_results("{}/betaPic_IAD{}.hdf5".format(savedir, fit_IAD)) 124 | 125 | # make corner plot 126 | fig = betaPic_sampler.results.plot_corner() 127 | plt.savefig("{}/corner_IAD{}.png".format(savedir, fit_IAD), dpi=250) 128 | 129 | # make orbit plot 130 | fig = betaPic_sampler.results.plot_orbits() 131 | plt.savefig("{}/orbit_IAD{}.png".format(savedir, fit_IAD), dpi=250) 132 | -------------------------------------------------------------------------------- /tests/end-to-end-tests/hr8799e_1epochgravity.csv: -------------------------------------------------------------------------------- 1 | epoch,object,raoff,raoff_err,decoff,decoff_err,radec_corr,sep,sep_err,pa,pa_err 2 | 55042,1,-306,7,-211,7,,,,, 3 | 55136,1,-310,9,-187,9,,,,, 4 | 55390,1,-323,6,-166,6,,,,, 5 | 55499,1,-341,16,-143,16,,,,, 6 | 55763,1,-352,8,-130,8,,,,, 7 | 56130,1,-373,8,-84,8,,,,, 8 | 56226,1,-370,9,-76,9,,,,, 9 | 56581,1,-373,13,-17,13,,,,, 10 | 56855,1,-387,11,3,11,,,,, 11 | 56609,1,,,,,,382.6,2.1,265.13,0.24 12 | 57650,1,,,,,,384.8,1.7,281.68,0.25 13 | 58358.24,1,-357.640,0.07,163.340,0.18,-0.530,,,, -------------------------------------------------------------------------------- /tests/end-to-end-tests/hr8799e_only.py: -------------------------------------------------------------------------------- 1 | import os 2 | import matplotlib.pyplot as plt 3 | 4 | import orbitize 5 | from orbitize import system, read_input, priors, sampler 6 | 7 | """ 8 | Attempts to reproduce the 1 planet orbit fit in GRAVITY Collaboration et al. (2019) 9 | """ 10 | 11 | # End to end example with beta Pic astrometry 12 | # Based on driver.py 13 | 14 | import numpy as np 15 | from orbitize import read_input, system, sampler, priors 16 | import multiprocessing as mp 17 | import orbitize 18 | 19 | 20 | # System parameters 21 | datafile = "hr8799e_1epochgravity.csv" 22 | num_secondary_bodies = 1 23 | system_mass = 1.52 # Msol 24 | plx = 24.2175 # mas 25 | mass_err = 0.15 # Msol 26 | plx_err = 0.0881 # mas 27 | 28 | # Sampler parameters 29 | likelihood_func_name = "chi2_lnlike" 30 | n_temps = 20 31 | n_walkers = 1000 32 | n_threads = mp.cpu_count() 33 | total_orbits = 10000000 # n_walkers x num_steps_per_walker 34 | burn_steps = 50000 35 | 36 | tau_ref_epoch = 50000 37 | 38 | 39 | # Read in data 40 | data_table = read_input.read_file(datafile) 41 | 42 | # Initialize System object which stores data & sets priors 43 | my_system = system.System( 44 | num_secondary_bodies, 45 | data_table, 46 | system_mass, 47 | plx, 48 | mass_err=mass_err, 49 | plx_err=plx_err, 50 | tau_ref_epoch=tau_ref_epoch, 51 | ) 52 | 53 | my_sampler = sampler.MCMC(my_system, n_temps, n_walkers, n_threads) 54 | 55 | # Run the sampler to compute some orbits, yeah! 56 | # Results stored in bP_sampler.chain and bP_sampler.lnlikes 57 | my_sampler.run_sampler(total_orbits, burn_steps=burn_steps, thin=10) 58 | 59 | my_sampler.results.save_results("hr8799e_gravity_chains.hdf5") 60 | 61 | # import orbitize.results as results 62 | # my_sampler.results = results.Results() 63 | # my_sampler.results.load_results("hr8799e_gravity_chains.hdf5") 64 | 65 | # make corner plot 66 | fig = my_sampler.results.plot_corner() 67 | plt.savefig("corner_hr8799e_gravity.png", dpi=250) 68 | 69 | # print SMA, ecc, inc 70 | labels = ["sma", "ecc", "inc"] 71 | paper_vals = ["16.4 (+2.1/-1.1)", "0.15 +/- 0.08", "25 +/- 8"] 72 | for i in range(len(labels)): 73 | med_val = np.median(my_sampler.results.post[:, i]) 74 | ci_vals = np.percentile(my_sampler.results.post[:, i], [84, 16]) - med_val 75 | if labels[i] == "inc": 76 | med_val = np.degrees(med_val) 77 | ci_vals = np.degrees(ci_vals) 78 | print("{0}: paper value is {1}".format(labels[i], paper_vals[i])) 79 | print( 80 | "{3}: this fit obtained {0:.2f} (+{1:.2f}/-{2:.2f})".format( 81 | med_val, ci_vals[0], ci_vals[1], labels[i] 82 | ) 83 | ) 84 | -------------------------------------------------------------------------------- /tests/end-to-end-tests/nested_test.py: -------------------------------------------------------------------------------- 1 | import orbitize 2 | from orbitize import read_input, system, sampler, priors 3 | import matplotlib.pyplot as plt 4 | from dynesty import plotting as dyplot 5 | import time 6 | 7 | 8 | savedir = "." 9 | 10 | """ 11 | Runs the GJ504 fit (from the quickstart tutorial) using dynesty as a backend 12 | 13 | Written: Thea McKenna, 2023 14 | """ 15 | 16 | 17 | def dynesty_e2e_test(): 18 | 19 | data_table = read_input.read_file("{}/GJ504.csv".format(orbitize.DATADIR)) 20 | 21 | # number of secondary bodies in system 22 | num_planets = 1 23 | 24 | # total mass & error [msol] 25 | total_mass = 1.22 26 | mass_err = 0 # 0.08 27 | 28 | # parallax & error[mas] 29 | plx = 56.95 30 | plx_err = 0 # 0.26 31 | 32 | sys = system.System( 33 | num_planets, 34 | data_table, 35 | total_mass, 36 | plx, 37 | mass_err=mass_err, 38 | plx_err=plx_err, 39 | restrict_angle_ranges=True, 40 | ) 41 | # alias for convenience 42 | lab = sys.param_idx 43 | 44 | # set prior on semimajor axis 45 | sys.sys_priors[lab["sma1"]] = priors.LogUniformPrior(10, 300) 46 | 47 | nested_sampler = sampler.NestedSampler(sys) 48 | 49 | start = time.time() 50 | 51 | samples, num_iter = nested_sampler.run_sampler(num_threads=50) 52 | nested_sampler.results.save_results("{}/nested_sampler_test.hdf5".format(savedir)) 53 | print("iteration number is: " + str(num_iter)) 54 | 55 | print("iteration time: {:.f} mins".format((time.time() - start) / 60.0)) 56 | 57 | fig, ax = plt.subplots(2, 1) 58 | accepted_eccentricities = nested_sampler.results.post[:, lab["ecc1"]] 59 | accepted_inclinations = nested_sampler.results.post[:, lab["inc1"]] 60 | ax[0].hist(accepted_eccentricities, bins=50) 61 | ax[1].hist(accepted_inclinations, bins=50) 62 | ax[0].set_xlabel("ecc") 63 | ax[1].set_xlabel("inc") 64 | plt.tight_layout() 65 | plt.savefig("{}/nested_sampler_test.png".format(savedir)) 66 | 67 | fig, axes = dyplot.traceplot(nested_sampler.dynesty_sampler.results) 68 | plt.savefig("{}/nested_sampler_traceplot.png".format(savedir)) 69 | 70 | 71 | if __name__ == "__main__": 72 | dynesty_e2e_test() 73 | -------------------------------------------------------------------------------- /tests/end-to-end-tests/rv_end_to_end.py: -------------------------------------------------------------------------------- 1 | from orbitize import read_input, system, priors, sampler, results, kepler 2 | 3 | import numpy as np 4 | 5 | import matplotlib.pyplot as plt 6 | 7 | import pandas as pd 8 | import os 9 | 10 | """ 11 | Simulates RV data from multiple instruments and relative astroemtric data 12 | from a single instrument, then runs an orbit-fit and recovers the input 13 | parameters. 14 | 15 | Written: Vighnesh Nagpal, 2021 16 | """ 17 | 18 | 19 | def plot_rv(epochs, rvs): 20 | plt.plot(epochs, rvs) 21 | plt.savefig("rv_trend") 22 | plt.close() 23 | 24 | 25 | def plot_astro(ras, decs): 26 | plt.plot(ras, decs) 27 | plt.axis("equal") 28 | plt.savefig("orbit_trend") 29 | plt.close() 30 | 31 | 32 | def gen_data(): 33 | """ 34 | Simulates radial velocity and astrometric data for a test system. 35 | 36 | Returns: 37 | (rvs,rv_epochs): Tuple of generated radial velocity measurements (rvs) and their corresponding 38 | measurement epochs (rv_epochs) 39 | 40 | (ras,decs,astro_epochs): Tuple containing simulated astrometric measurements (ras, decs) 41 | and the corresponding measurement epochs (astro_epochs) 42 | 43 | 44 | """ 45 | # set parameters for the synthetic data 46 | sma = 1 47 | inc = np.pi / 2 48 | ecc = 0.2 49 | aop = np.pi / 4 50 | pan = np.pi / 4 51 | tau = 0.4 52 | plx = 50 53 | mass_for_kamp = 0.1 54 | mtot = 1.1 55 | # epochs and errors for rv 56 | rv_epochs = np.linspace(51544, 52426, 200) 57 | # epochs and errors for astrometry 58 | astro_epochs = np.linspace(51500, 52500, 10) 59 | astro_err = 0 60 | # generate rv trend 61 | rvset = kepler.calc_orbit( 62 | rv_epochs, sma, ecc, inc, aop, pan, tau, plx, mtot, mass_for_Kamp=mass_for_kamp 63 | ) 64 | rvs = rvset[2] 65 | # generate predictions for astrometric epochs 66 | astro_set = kepler.calc_orbit( 67 | astro_epochs, 68 | sma, 69 | ecc, 70 | inc, 71 | aop, 72 | pan, 73 | tau, 74 | plx, 75 | mtot, 76 | mass_for_Kamp=mass_for_kamp, 77 | ) 78 | ras, decs = astro_set[0], astro_set[1] 79 | # return model generations 80 | return (rvs, rv_epochs), (ras, decs, astro_epochs) 81 | 82 | 83 | def scat_model(rvs, calibration_terms): 84 | """ 85 | Function that adds scatter to RV data based on provided calibration terms (gamma, sigma) 86 | that are unique for each instrument in the dataset. 87 | 88 | Args: 89 | rvs (array): Array of radial velocity measurements 90 | calibration_terms (tuple): Tuple of the form: (gamma_instrument1,jit_instrument1, 91 | gamma_instrument2,jit_instrument2, 92 | rv_err) 93 | 94 | returns: 95 | scat_rvs (array): Array of RV measurements with scatter added 96 | errors (array): Array of measurement uncertainties the RV measurements 97 | 98 | """ 99 | gam1, jit1, gam2, jit2, rv_err = calibration_terms 100 | # create empty arrays to be filled with data from each inst +respective jit and sigmas 101 | length = int(len(rvs) / 2) 102 | off_1 = np.zeros(length) 103 | off_2 = np.zeros(length) 104 | # create an array of normally sampled jitters for each instruments 105 | 106 | errors1 = np.abs(rv_err * np.random.randn(length)) 107 | errors2 = np.abs(rv_err * np.random.randn(length)) 108 | 109 | jscat1 = np.random.randn(length) * np.sqrt(jit1**2 + errors1**2) 110 | jscat2 = np.random.randn(length) * np.sqrt(jit2**2 + errors2**2) 111 | # fill off_1 and off_2 112 | off_1[:] = rvs[:length] 113 | off_2[:] = rvs[length:] 114 | # add scatters and gammas for first instrument 115 | off_1 += gam1 116 | off_1 += jscat1 117 | # add scatters and gammas for second instrument 118 | off_2 += gam2 119 | off_2 += jscat2 120 | # put em together 121 | scat_rvs = np.concatenate([off_1, off_2]) 122 | # put measurement uncertainties together 123 | errors = np.concatenate([errors1, errors2]) 124 | return scat_rvs, errors 125 | 126 | 127 | def make_csv(fname, rv_epochs, model, astr_info, errors): 128 | """ 129 | Takes the data generated and saves it as an orbitize-compatible csv file. 130 | 131 | """ 132 | # unpack astrometric info 133 | ras, decs, astro_epochs = astr_info 134 | # actually make csv 135 | frame = [] 136 | for i, val in enumerate(rv_epochs): 137 | if i < 100: 138 | obs = [val, 0, model[i], errors[i], None, None, None, None, "tel_1"] 139 | else: 140 | obs = [val, 0, model[i], errors[i], None, None, None, None, "tel_2"] 141 | frame.append(obs) 142 | for i, val in enumerate(astro_epochs): 143 | obs = [val, 1, None, None, ras[i], 0, decs[i], 0, "default"] 144 | frame.append(obs) 145 | df = pd.DataFrame( 146 | frame, 147 | columns=[ 148 | "epoch", 149 | "object", 150 | "rv", 151 | "rv_err", 152 | "raoff", 153 | "raoff_err", 154 | "decoff", 155 | "decoff_err", 156 | "instrument", 157 | ], 158 | ) 159 | df.set_index("epoch", inplace=True) 160 | df.to_csv(fname) 161 | 162 | 163 | def run_fit(fname): 164 | """ 165 | Runs the orbit fit! Saves the resultant posterior, orbit plot and corner plot 166 | 167 | args: 168 | fname (str): Path to the data file. 169 | 170 | """ 171 | 172 | # parameters for the system 173 | num_planets = 1 174 | data_table = read_input.read_file(fname) 175 | m0 = 1.0 176 | mass_err = 0.01 177 | plx = 50 178 | plx_err = 0.01 179 | # initialise a system object 180 | sys = system.System( 181 | num_planets, 182 | data_table, 183 | m0, 184 | plx, 185 | mass_err=mass_err, 186 | plx_err=plx_err, 187 | fit_secondary_mass=True, 188 | ) 189 | # MCMC parameters 190 | n_temps = 5 191 | n_walkers = 1000 192 | n_threads = 5 193 | total_orbits_MCMC = 5000 # n_walkers x num_steps_per_walker 194 | burn_steps = 1 195 | thin = 1 196 | # set up sampler object and run it 197 | mcmc_sampler = sampler.MCMC(sys, n_temps, n_walkers, n_threads) 198 | orbits = mcmc_sampler.run_sampler( 199 | total_orbits_MCMC, burn_steps=burn_steps, thin=thin 200 | ) 201 | myResults = mcmc_sampler.results 202 | try: 203 | save_path = "." 204 | filename = "post.hdf5" 205 | hdf5_filename = os.path.join(save_path, filename) 206 | myResults.save_results(hdf5_filename) # saves results object as an hdf5 file 207 | except: 208 | print("Something went wrong while saving the results") 209 | finally: 210 | corner_figure = myResults.plot_corner() 211 | corner_name = "corner.png" 212 | corner_figure.savefig(corner_name) 213 | 214 | orbit_figure = myResults.plot_orbits(rv_time_series=True) 215 | orbit_name = "joint_orbit.png" 216 | orbit_figure.savefig(orbit_name) 217 | 218 | print("Done!") 219 | 220 | 221 | if __name__ == "__main__": 222 | rv_info, astr_info = gen_data() 223 | rvs, rv_epochs = rv_info 224 | 225 | # set gammas and jitters 226 | 227 | calibration_terms = (0.7, 0.009, -0.3, 0.006, 0.002) 228 | 229 | # add scatter to model 230 | model, errors = scat_model(rvs, calibration_terms) 231 | 232 | # save this to a new file 233 | fname = "./simulated_data.csv" 234 | make_csv(fname, rv_epochs, model, astr_info, errors) 235 | 236 | # run orbit fit 237 | run_fit(fname) 238 | 239 | # delete CSV 240 | os.remove("demofile.txt") 241 | -------------------------------------------------------------------------------- /tests/test_abs_astrometry.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | import astropy.table as table 4 | from astropy.time import Time 5 | import astropy.units as u 6 | 7 | import orbitize 8 | from orbitize import kepler, read_input, system, hipparcos, DATADIR 9 | 10 | 11 | def test_1planet(): 12 | """ 13 | Check that for the 2-body case, the primary orbit around the barycenter 14 | is equal to -m2/(m1 + m2) times the secondary orbit around the primary. 15 | """ 16 | 17 | # generate a planet orbit 18 | sma = 1 19 | ecc = 0.1 20 | inc = np.radians(45) 21 | aop = np.radians(45) 22 | pan = np.radians(45) 23 | tau = 0.5 24 | plx = 1 25 | m0 = 1 26 | tau_ref_epoch = 0 27 | mjup = u.Mjup.to(u.Msun) 28 | mass_b = 100 * mjup 29 | mtot = mass_b + m0 30 | 31 | epochs = np.linspace(0, 300, 100) + tau_ref_epoch # nearly the full period, MJD 32 | 33 | ra_model, dec_model, _ = kepler.calc_orbit( 34 | epochs, sma, ecc, inc, aop, pan, tau, plx, mtot, tau_ref_epoch=tau_ref_epoch 35 | ) 36 | 37 | # generate some fake measurements to feed into system.py to test bookkeeping 38 | t = table.Table( 39 | [ 40 | epochs, 41 | np.ones(epochs.shape, dtype=int), 42 | ra_model, 43 | np.zeros(ra_model.shape), 44 | dec_model, 45 | np.zeros(dec_model.shape), 46 | ], 47 | names=["epoch", "object", "raoff", "raoff_err", "decoff", "decoff_err"], 48 | ) 49 | filename = os.path.join(orbitize.DATADIR, "rebound_1planet.csv") 50 | t.write(filename, overwrite=True) 51 | 52 | # create the orbitize system and generate model predictions using ground truth 53 | astrom_dat = read_input.read_file(filename) 54 | 55 | sys = system.System( 56 | 1, astrom_dat, m0, plx, tau_ref_epoch=tau_ref_epoch, fit_secondary_mass=True 57 | ) 58 | sys.track_planet_perturbs = True 59 | 60 | params = np.array([sma, ecc, inc, aop, pan, tau, plx, mass_b, m0]) 61 | ra, dec, _ = sys.compute_all_orbits(params) 62 | 63 | # the planet and stellar orbit should just be scaled versions of one another 64 | planet_ra = ra[:, 1, :] 65 | planet_dec = dec[:, 1, :] 66 | star_ra = ra[:, 0, :] 67 | star_dec = dec[:, 0, :] 68 | 69 | assert np.all(np.abs(star_ra + (mass_b / mtot) * planet_ra) < 1e-16) 70 | assert np.all(np.abs(star_dec + (mass_b / mtot) * planet_dec) < 1e-16) 71 | 72 | # remove the created csv file to clean up 73 | os.system("rm {}".format(filename)) 74 | 75 | 76 | def test_arbitrary_abs_astrom(): 77 | """ 78 | Test that proper motion and parallax model parameters are applied correctly 79 | when we have astrometry from an arbitrary (i.e. not Hipparcos or Gaia) 80 | instrument. This test assumes test particle (i.e. zero-mass) companions. 81 | """ 82 | 83 | # just read in any file since we're not computing Hipparcos-related likelihoods. 84 | hip_num = "027321" 85 | num_secondary_bodies = 1 86 | path_to_iad_file = "{}H{}.d".format(DATADIR, hip_num) 87 | testHiPIAD = hipparcos.HipparcosLogProb( 88 | path_to_iad_file, hip_num, num_secondary_bodies 89 | ) 90 | 91 | epochs_astropy = Time( 92 | np.array([0, 0.5, 1.0]) + testHiPIAD.alphadec0_epoch, format="decimalyear" 93 | ) 94 | epochs = epochs_astropy.mjd 95 | ra_model = np.zeros(epochs.shape) 96 | dec_model = np.zeros(epochs.shape) 97 | 98 | # generate some fake measurements to feed into system.py to test bookkeeping 99 | t = table.Table( 100 | [ 101 | epochs, 102 | np.zeros(epochs.shape, dtype=int), 103 | ra_model, 104 | np.zeros(epochs.shape), 105 | dec_model, 106 | np.zeros(epochs.shape), 107 | ], 108 | names=["epoch", "object", "raoff", "raoff_err", "decoff", "decoff_err"], 109 | ) 110 | filename = os.path.join(orbitize.DATADIR, "rebound_1planet.csv") 111 | t.write(filename, overwrite=True) 112 | 113 | astrom_data = read_input.read_file(filename) 114 | mySystem = system.System( 115 | 1, astrom_data, 1, 1, fit_secondary_mass=True, hipparcos_IAD=testHiPIAD 116 | ) 117 | 118 | # Test case 1: zero proper motion, but large parallax = yearly motion should only 119 | # reflect parallax 120 | plx = 100 121 | pm_ra = 0 122 | pm_dec = 0 123 | alpha0 = 0 124 | delta0 = 0 125 | m1 = 1 126 | m0 = 1e-10 127 | 128 | plx_only_params = np.array( 129 | [ 130 | 1, # start test particle params 131 | 0, 132 | 0, 133 | 0, 134 | 0, 135 | 0, # end test particle params 136 | plx, 137 | pm_ra, 138 | pm_dec, 139 | alpha0, 140 | delta0, 141 | m1, 142 | m0, 143 | ] 144 | ) 145 | 146 | plxonly_fullorbit_ra, plxonly_fullorbit_dec, _ = mySystem.compute_all_orbits( 147 | plx_only_params, epochs=np.linspace(epochs[0], epochs[0] + 365.25 / 2, int(1e6)) 148 | ) 149 | 150 | # check that min and max of RA and Dec outputs are close to 0 and plx magnitude, 151 | # respectively 152 | assert np.isclose(0, np.min(np.abs(plxonly_fullorbit_ra)), atol=1e-4) 153 | assert np.isclose(0, np.min(np.abs(plxonly_fullorbit_dec)), atol=1e-4) 154 | assert np.isclose(-100, np.min(plxonly_fullorbit_ra), atol=1e-4) 155 | assert np.isclose(100, np.max(plxonly_fullorbit_dec), atol=1e-4) 156 | 157 | # Test case 2: very high proper motion, but very small parallax = motion 158 | # should only reflect proper motion 159 | plx = 1e-10 160 | pm_ra = 100 161 | pm_dec = 100 162 | pm_only_params = np.array( 163 | [ 164 | 1, # start test particle params 165 | 0, 166 | 0, 167 | 0, 168 | 0, 169 | 0, # end test particle params 170 | plx, 171 | pm_ra, 172 | pm_dec, 173 | alpha0, 174 | delta0, 175 | m1, 176 | m0, 177 | ] 178 | ) 179 | 180 | pmonly_model = mySystem.compute_model(pm_only_params) 181 | 182 | cosdelta0 = np.cos(np.radians(mySystem.pm_plx_predictor.delta0)) 183 | 184 | pmonly_expectation = np.array( 185 | [[0, 0], [50 / cosdelta0, 50], [100.0 / cosdelta0, 100.0]] 186 | ) 187 | 188 | assert np.all(np.isclose(pmonly_model[0], pmonly_expectation)) 189 | assert np.all(np.isclose(pmonly_model[1], np.zeros(pmonly_model[1].shape))) 190 | 191 | 192 | if __name__ == "__main__": 193 | test_1planet() 194 | test_arbitrary_abs_astrom() 195 | -------------------------------------------------------------------------------- /tests/test_additional_plotting.py: -------------------------------------------------------------------------------- 1 | """ 2 | Module to test plotting functions not tested by other tests/results testing. 3 | """ 4 | import os 5 | import orbitize 6 | import orbitize.driver 7 | import orbitize.plot as plot 8 | 9 | def test_plot_residuals(debug=False): 10 | """ 11 | Tests the residual plotting code on a 2-planet orbit fit 12 | """ 13 | # run a very short 2 planet orbit fit 14 | input_file = os.path.join(orbitize.DATADIR, "GJ504.csv") 15 | my_driver = orbitize.driver.Driver(input_file, 16 | "OFTI", 17 | 1, 18 | 1.22, 19 | 56.95, 20 | mass_err=0.08, 21 | plx_err=0.26, 22 | system_kwargs={"restrict_angle_ranges": True} 23 | ) 24 | my_sampler = my_driver.sampler 25 | my_sampler.run_sampler(101) 26 | my_results = my_sampler.results 27 | 28 | # plot planet 1 29 | fig1 = plot.plot_residuals(my_results) 30 | 31 | # plot with more samples, mod 180 32 | fig2 = plot.plot_residuals(my_results, object_to_plot=1, mod180=True, num_orbits_to_plot=150) 33 | 34 | if debug: 35 | import matplotlib.pylab as plt 36 | plt.show() 37 | 38 | if __name__ == "__main__": 39 | test_plot_residuals(debug=True) 40 | 41 | -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test the functionality of the API 3 | """ 4 | import numpy as np 5 | import orbitize 6 | import orbitize.lnlike as lnlike 7 | import orbitize.system as system 8 | import orbitize.sampler as sampler 9 | import orbitize.read_input as read_input 10 | import os 11 | 12 | 13 | def test_compute_model(): 14 | """ 15 | Test basic functionality of ``System.compute_model()`` 16 | """ 17 | input_file = os.path.join(orbitize.DATADIR, 'test_val.csv') 18 | data_table = read_input.read_file(input_file) 19 | data_table['object'] = 1 20 | testSystem_parsing = system.System( 21 | 1, data_table, 10., 10. 22 | ) 23 | 24 | params_arr = np.array([[1., 0.5], [0., 0.], [0., 0.], [0., 0.], [ 25 | 0., 0.], [245000., 245000.], [10, 10], [10, 10]]) 26 | model, _ = testSystem_parsing.compute_model(params_arr) 27 | assert model.shape == (4, 2, 2) 28 | 29 | params_arr = np.array([1., 0., 0., 0., 0., 245000., 10, 10]) 30 | model, _ = testSystem_parsing.compute_model(params_arr) 31 | 32 | assert model.shape == (4, 2) 33 | 34 | 35 | def test_systeminit(): 36 | """ 37 | Test that initializing a ``System`` class produces a list of ``Prior`` 38 | objects of the correct length when: 39 | - parallax and total mass are fixed 40 | - parallax and total mass errors are given 41 | - parallax is fixed, total mass error is given 42 | - parallax error is given, total mass error is fixed 43 | 44 | Test that the different types of data are parsed correctly 45 | when initializing a ``System`` object. 46 | """ 47 | testdir = orbitize.DATADIR 48 | input_file = os.path.join(testdir, 'test_val.csv') 49 | data_table = read_input.read_file(input_file) 50 | 51 | # Manually set 'object' column of data table 52 | data_table['object'] = 1 53 | data_table['object'][1] = 2 54 | 55 | plx_mass_errs2lens = { 56 | (0., 0.): 14, 57 | (1., 1.): 14, 58 | (0., 1.): 14, 59 | (1., 0.): 14 60 | } 61 | 62 | for plx_e, mass_e in plx_mass_errs2lens.keys(): 63 | 64 | testSystem_priors = system.System( 65 | 2, data_table, 10., 10., plx_err=plx_e, mass_err=mass_e 66 | ) 67 | assert len(testSystem_priors.sys_priors) == \ 68 | plx_mass_errs2lens[(plx_e, mass_e)] 69 | 70 | testSystem_parsing = system.System( 71 | 2, data_table, 10., 10., 72 | plx_err=0.5, mass_err=0.5 73 | ) 74 | assert len(data_table[testSystem_parsing.seppa[0]]) == 0 75 | assert len(data_table[testSystem_parsing.seppa[1]]) == 1 76 | assert len(data_table[testSystem_parsing.seppa[2]]) == 1 77 | assert len(data_table[testSystem_parsing.radec[0]]) == 0 78 | assert len(data_table[testSystem_parsing.radec[1]]) == 1 79 | assert len(data_table[testSystem_parsing.radec[2]]) == 0 80 | 81 | assert testSystem_parsing.labels == [ 82 | 'sma1', 'ecc1', 'inc1', 'aop1', 'pan1', 'tau1', 'sma2', 83 | 'ecc2', 'inc2', 'aop2', 'pan2', 'tau2', 'plx', 'mtot' 84 | ] 85 | 86 | 87 | def test_custom_likelihood(): 88 | """ 89 | Tests the inclusion of a custom likelihood function in the code 90 | """ 91 | # use the test_csv dir 92 | testdir = orbitize.DATADIR 93 | input_file = os.path.join(testdir, 'GJ504.csv') 94 | data_table = read_input.read_file(input_file) 95 | # Manually set 'object' column of data table 96 | data_table['object'] = 1 97 | 98 | # construct the system 99 | orbit = system.System(1, data_table, 1, 0.01) 100 | 101 | # construct custom likelihood function 102 | def my_likelihood(params): 103 | return -5 104 | 105 | # construct sampler 106 | n_walkers = 100 107 | mcmc1 = sampler.MCMC(orbit, 0, n_walkers, num_threads=1) 108 | mcmc2 = sampler.MCMC( 109 | orbit, 0, n_walkers, num_threads=1, custom_lnlike=my_likelihood 110 | ) 111 | 112 | param = np.array([2, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 0.01]) 113 | 114 | logl1 = mcmc1._logl(param) 115 | logl2 = mcmc2._logl(param) 116 | 117 | assert logl1 == logl2 + 5 118 | 119 | 120 | def test_radec2seppa(): 121 | """ 122 | Basic test for convenience function converting RA/DEC to SEP/PA 123 | """ 124 | ra = np.array([-1., 0., -1., 1.]) 125 | dec = np.array([0., -1., -1., 1.]) 126 | sep, pa = system.radec2seppa(ra, dec) 127 | assert sep.all() == np.array([1., 1., np.sqrt(2.), np.sqrt(2.)]).all() 128 | assert pa.all() == np.array([270., 180., 225., 45.]).all() 129 | 130 | 131 | if __name__ == "__main__": 132 | test_compute_model() 133 | test_systeminit() 134 | test_custom_likelihood() 135 | test_radec2seppa() 136 | -------------------------------------------------------------------------------- /tests/test_checkpriorsupport.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import orbitize 3 | import orbitize.sampler as sampler 4 | from orbitize import driver 5 | import random 6 | import warnings 7 | 8 | warnings.filterwarnings('ignore') 9 | 10 | 11 | def test_check_prior_support(PriorChanges=False): 12 | ''' 13 | Test the check_prior_support() function to ensure it behaves correctly. 14 | Should fail with a ValueError if any parameters are outside prior support. 15 | Should behave normally if all parameters are within prior support. 16 | ''' 17 | 18 | # set up as if we are running the tutorial retrieval 19 | myDriver = driver.Driver( 20 | '{}/GJ504.csv'.format(orbitize.DATADIR), # data file 21 | 'MCMC', # choose from: ['OFTI', 'MCMC'] 22 | 1, # number of planets in system 23 | 1.22, # total system mass [M_sun] 24 | 56.95, # system parallax [mas] 25 | mass_err=0.08, # mass error [M_sun] 26 | plx_err=0.26, # parallax error [mas] 27 | mcmc_kwargs={'num_temps':2, 'num_walkers':18,'num_threads':1} 28 | ) 29 | 30 | # mess with the priors if requested 31 | if PriorChanges: 32 | zlist = [] 33 | for a in range(4): 34 | x = random.randint(0,myDriver.sampler.num_temps-1) 35 | y = random.randint(0,myDriver.sampler.num_walkers-1) 36 | z = random.randint(0,myDriver.sampler.num_params-1) 37 | myDriver.sampler.curr_pos[x,y,z] = -1000 38 | zlist.append(z) 39 | 40 | 41 | # run the tests 42 | try: 43 | orbits = myDriver.sampler.check_prior_support() 44 | # catch the correct error 45 | except ValueError as error: 46 | errorCaught = True 47 | # make sure nothing else broke 48 | except: 49 | print('something has gone horribly wrong') 50 | # state if otherwise 51 | else: 52 | errorCaught = False 53 | 54 | assert errorCaught == PriorChanges 55 | 56 | if __name__ == '__main__': 57 | test_check_prior_support() 58 | test_check_prior_support(PriorChanges=True) -------------------------------------------------------------------------------- /tests/test_chi2_lnlike.py: -------------------------------------------------------------------------------- 1 | import orbitize.driver 2 | import numpy as np 3 | import orbitize.lnlike as lnlike 4 | import pytest 5 | 6 | 7 | def test_chi2lnlike(): 8 | """ 9 | Test the ability of ``orbitize.lnlike.chi2_lnlike()`` 10 | to work properly on arrays. 11 | """ 12 | # test with a single model 13 | model = np.zeros((3, 2)) 14 | jitter = np.zeros((3, 2)) 15 | data = np.ones((3, 2)) 16 | errors = np.ones((3, 2)) 17 | 18 | seppa_indices = [np.array([1])] 19 | 20 | chi2 = lnlike.chi2_lnlike(data, errors, None, model, jitter, seppa_indices) 21 | assert chi2.shape == (3, 2) 22 | assert chi2 == pytest.approx( 23 | -0.5 * np.ones((3, 2)) - np.log(np.sqrt(2 * np.pi * np.ones((3, 2)))) 24 | ) 25 | 26 | # test with multiple models 27 | model = np.zeros((3, 2, 5)) 28 | jitter = np.zeros((3, 2, 5)) 29 | data = np.ones((3, 2)) 30 | errors = np.ones((3, 2)) 31 | 32 | seppa_indices = [np.array([1])] 33 | 34 | chi2 = lnlike.chi2_lnlike(data, errors, None, model, jitter, seppa_indices) 35 | assert chi2.shape == (3, 2, 5) 36 | assert chi2 == pytest.approx( 37 | -0.5 * np.ones((3, 2, 5)) - np.log(np.sqrt(2 * np.pi * np.ones((3, 2, 5)))) 38 | ) 39 | 40 | 41 | def test_chi2lnlike_withcov(): 42 | """ 43 | Test the ability of ``orbitize.lnlike.chi2_lnlike()`` to work with some or all data having covariances 44 | """ 45 | ### all all covariances 46 | data = np.array([[5, -4], [3, -2], [1, 0]]) 47 | model = np.zeros(data.shape) 48 | jitter = np.zeros(data.shape) 49 | errs = np.array([[2, 2], [2, 2], [2, 2]]) 50 | covs = np.array([1, 0.25, 0.25]) 51 | corrs = covs / errs[:, 0] / errs[:, 1] 52 | 53 | chi2s = lnlike.chi2_lnlike(data, errs, corrs, model, jitter, []) 54 | 55 | residuals = data - model 56 | for res, err, cov, chi2 in zip(residuals, errs, covs, chi2s): 57 | cov_matrix = np.array([[err[0] ** 2, cov], [cov, err[1] ** 2]]) 58 | cov_inv = np.linalg.inv(cov_matrix) 59 | cov_inv_dot_diff = np.dot(cov_inv, res) 60 | logdet = np.linalg.slogdet(cov_matrix)[1] 61 | res_cov_res = res.dot(cov_inv_dot_diff) 62 | numpy_chi2 = -0.5 * (res_cov_res + logdet + 2 * np.log(2 * np.pi)) 63 | 64 | assert np.sum(chi2) == pytest.approx(numpy_chi2) 65 | 66 | ### only one covariance term 67 | covs = np.array([1, np.nan, np.nan]) 68 | corrs = covs / errs[:, 0] / errs[:, 1] 69 | new_chi2s = lnlike.chi2_lnlike(data, errs, corrs, model, jitter, []) 70 | 71 | assert chi2s[0] == pytest.approx(new_chi2s[0]) 72 | 73 | 74 | def test_2x2_analytical_solution(): 75 | """ 76 | Tests that our analytical solution to the 2x2 covariance matrix is correct 77 | """ 78 | residuals = np.array([[5, -4], [3, -2], [1, 0]]) 79 | 80 | errs = np.array([[2, 2], [2, 2], [2, 2]]) 81 | covs = np.array([1, 0.25, 0.25]) 82 | corrs = covs / errs[:, 0] / errs[:, 1] 83 | 84 | chi2s = lnlike._chi2_2x2cov(np.array([residuals]), np.array([errs**2]), corrs) 85 | 86 | # compare to numpy solution 87 | for res, err, cov, chi2 in zip(residuals, errs, covs, chi2s[0]): 88 | cov_matrix = np.array([[err[0] ** 2, cov], [cov, err[1] ** 2]]) 89 | cov_inv = np.linalg.inv(cov_matrix) 90 | cov_inv_dot_diff = np.dot(cov_inv, res) 91 | logdet = np.linalg.slogdet(cov_matrix)[1] 92 | res_cov_res = res.dot(cov_inv_dot_diff) 93 | numpy_chi2 = -0.5 * (res_cov_res + logdet + 2 * np.log(2 * np.pi)) 94 | 95 | assert np.sum(chi2) == pytest.approx(numpy_chi2) 96 | 97 | 98 | def test_chi2_log(): 99 | # initiate OFTI driver with chi2 log 100 | myDriver = orbitize.driver.Driver( 101 | "{}/GJ504.csv".format(orbitize.DATADIR), 102 | "OFTI", 103 | 1, 104 | 1.22, 105 | 56.95, 106 | mass_err=0.08, 107 | plx_err=0.26, 108 | chi2_type="log", 109 | ) 110 | s = myDriver.sampler 111 | params = [44, 0, 45 * np.pi / 180, 0, 325 * np.pi / 180, 0, 56.95, 1.22] 112 | log_chi2 = s._logl(params) 113 | 114 | sys = myDriver.system 115 | data = np.array([sys.data_table["quant1"], sys.data_table["quant2"]]).T 116 | errors = np.array([sys.data_table["quant1_err"], sys.data_table["quant2_err"]]).T 117 | model, jitter = sys.compute_model(params) 118 | 119 | sep_data = data[:, 0] 120 | sep_model = model[:, 0] 121 | sep_error = errors[:, 0] 122 | pa_data = data[:, 1] 123 | pa_model = model[:, 1] 124 | pa_error = errors[:, 1] * np.pi / 180 125 | 126 | # calculating sep chi squared 127 | sep_chi2_log = (np.log(sep_data) - np.log(sep_model)) ** 2 / ( 128 | sep_error / sep_data 129 | ) ** 2 130 | 131 | # calculting pa chi squared Log 132 | pa_resid = (pa_model - pa_data + 180.0) % 360.0 - 180.0 133 | pa_chi2_log = 2 * (1 - np.cos(pa_resid * np.pi / 180)) / pa_error**2 134 | 135 | chi2 = np.zeros((len(sep_data), 2)) 136 | 137 | sigma2 = errors**2 + jitter**2 138 | 139 | chi2[:, 0] = sep_chi2_log 140 | chi2[:, 1] = pa_chi2_log 141 | 142 | chi2 = -0.5 * chi2 - np.log(np.sqrt(2 * np.pi * sigma2)) 143 | 144 | lnlike = np.sum(chi2) 145 | 146 | assert lnlike == pytest.approx(log_chi2) 147 | 148 | 149 | def test_log_vs_standard(): 150 | # initiate driver with standard chi2 151 | myDriver_standard = orbitize.driver.Driver( 152 | "{}/GJ504.csv".format(orbitize.DATADIR), 153 | "OFTI", 154 | 1, 155 | 1.22, 156 | 56.95, 157 | ) 158 | s_standard = myDriver_standard.sampler 159 | _ = s_standard.run_sampler(750) 160 | 161 | # initiate driver with log chi2 162 | myDriver_log = orbitize.driver.Driver( 163 | "{}/GJ504.csv".format(orbitize.DATADIR), 164 | "OFTI", 165 | 1, 166 | 1.22, 167 | 56.95, 168 | chi2_type="log", 169 | ) 170 | s_log = myDriver_log.sampler 171 | _ = s_log.run_sampler(750) 172 | 173 | # take mean of result objects 174 | myResults_standard = np.mean(s_standard.results.post, axis=0) 175 | myResults_log = np.mean(s_log.results.post, axis=0) 176 | 177 | print(myResults_log[1], myResults_standard[1]) 178 | 179 | # check that the eccentricity means are about the same 180 | assert myResults_log[1] == pytest.approx(myResults_standard[1], rel=0.15) 181 | 182 | 183 | if __name__ == "__main__": 184 | # test_chi2lnlike() 185 | # test_chi2_log() 186 | # test_chi2lnlike_withcov() 187 | # test_2x2_analytical_solution() 188 | test_log_vs_standard() 189 | -------------------------------------------------------------------------------- /tests/test_chop_chains_with_plotting.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Make sure orbit plotting can still occur after chopping chains. 3 | ''' 4 | import orbitize 5 | from orbitize import driver, DATADIR 6 | import multiprocessing as mp 7 | 8 | def verify_results_data(res, sys): 9 | # Make data attribute from System is carried forward to Result class 10 | assert res.data is not None 11 | 12 | # Make sure the data tables are equivalent between Result and System class 13 | res_data = res.data.to_pandas() 14 | sys_data = sys.data_table.to_pandas() 15 | assert res_data.equals(sys_data) == True 16 | 17 | # Make sure no error results when making the final orbit plot 18 | try: 19 | epochs = sys.data_table['epoch'] 20 | res.plot_orbits( 21 | object_to_plot = 1, 22 | num_orbits_to_plot = 10, 23 | start_mjd = epochs[0] 24 | ) 25 | except: 26 | raise Exception("Plotting orbits failed.") 27 | 28 | def test_chop_chains(): 29 | ''' 30 | First run MCMC sampler to generate results object and make a call to 'chop_chains' 31 | function afterwards. 32 | ''' 33 | 34 | filename = "{}/HD4747.csv".format(DATADIR) 35 | 36 | num_secondary_bodies = 1 37 | system_mass = 0.84 38 | plx = 53.18 39 | mass_err = 0.04 40 | plx_err = 0.12 41 | num_temps = 5 42 | num_walkers = 40 43 | num_threads = mp.cpu_count() 44 | 45 | total_orbits = 5000 46 | burn_steps = 10 47 | thin = 2 48 | 49 | my_driver = driver.Driver( 50 | filename, 'MCMC', num_secondary_bodies, system_mass, plx, mass_err=mass_err, plx_err=plx_err, 51 | system_kwargs={'fit_secondary_mass':True, 'tau_ref_epoch':0}, 52 | mcmc_kwargs={'num_temps':num_temps, 'num_walkers':num_walkers, 'num_threads':num_threads}) 53 | 54 | my_driver.sampler.run_sampler(total_orbits, burn_steps=burn_steps, thin=thin) 55 | my_driver.sampler.chop_chains(burn=25, trim=25) 56 | 57 | mcmc_sys = my_driver.system 58 | mcmc_result = my_driver.sampler.results 59 | 60 | verify_results_data(mcmc_result, mcmc_sys) 61 | 62 | if __name__ == '__main__': 63 | test_chop_chains() 64 | 65 | -------------------------------------------------------------------------------- /tests/test_conversions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test the orbitize.basis which converts orbital elements 3 | """ 4 | import pytest 5 | import numpy as np 6 | import orbitize.basis as basis 7 | 8 | def test_tau_tp_conversion(): 9 | """ 10 | Test conversion back and forth 11 | """ 12 | tau = 0.1 13 | ref_epoch = 51000 # MJD 14 | period = 10 # years 15 | 16 | tp = basis.tau_to_tp(tau, ref_epoch, period) 17 | assert tp == pytest.approx(51000 + 365.25, rel=1e-7) 18 | 19 | tau2 = basis.tp_to_tau(tp, ref_epoch, period) 20 | assert tau == pytest.approx(tau2, rel=1e-7) 21 | 22 | tp = basis.tau_to_tp(tau, ref_epoch, period, after_date=47000) 23 | assert tp == pytest.approx(51000 - 9 * 365.25, rel=1e-7) 24 | 25 | tps = basis.tau_to_tp(tau, ref_epoch, period, after_date=np.array([47000, 51000])) 26 | assert tps[0] == pytest.approx(51000 - 9 * 365.25, rel=1e-7) 27 | assert tps[1] == pytest.approx(51000 + 365.25, rel=1e-7) 28 | 29 | tau3 = basis.tp_to_tau(tp, ref_epoch, period) 30 | assert tau == pytest.approx(tau3, rel=1e-7) 31 | 32 | def test_tau_tp_conversion_vector(): 33 | """ 34 | Make sure it works vectorized. 35 | """ 36 | taus = np.array([0.1, 0.2]) 37 | ref_epoch = 55000 # MJD 38 | period = np.array([1, 0.5]) # years 39 | 40 | tps = basis.tau_to_tp(taus, ref_epoch, period) 41 | for tp in tps: 42 | assert tp == pytest.approx(55000 + 365.25/10, rel=1e-7) 43 | 44 | def test_switch_tau_basis(): 45 | """ 46 | Switch reference epochs 47 | """ 48 | old_taus = np.array([0.5, 0.5]) 49 | ref_epoch = np.array([50000, 55000]) 50 | period = np.array([2, 2]) 51 | new_epoch = np.array([50000 + 365.25, 55000 + 365.25]) 52 | 53 | new_taus = basis.switch_tau_epoch(old_taus, ref_epoch, new_epoch, period) 54 | 55 | assert new_taus[0] == pytest.approx(0, rel=1e-7) 56 | assert new_taus[1] == pytest.approx(0, rel=1e-7) 57 | 58 | if __name__ == "__main__": 59 | test_tau_tp_conversion() 60 | test_tau_tp_conversion_vector() 61 | test_switch_tau_basis() -------------------------------------------------------------------------------- /tests/test_driver.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test the different Driver class creation options 3 | """ 4 | 5 | import pytest 6 | import numpy as np 7 | import orbitize 8 | from orbitize import driver 9 | from orbitize.read_input import read_file 10 | import os 11 | 12 | def _compare_table(input_table): 13 | """ 14 | Tests input table to expected values, which are: 15 | epoch object quant1 quant1_err quant2 quant2_err quant_type 16 | float64 int float64 float64 float64 float64 str5 17 | ------- ------ ------- ---------- ------- ---------- ---------- 18 | 1234.0 1 0.01 0.005 0.5 0.05 radec 19 | 1235.0 1 1.0 0.005 89.0 0.1 seppa 20 | 1236.0 1 1.0 0.005 89.3 0.3 seppa 21 | 1237.0 0 10.0 0.1 nan nan rv 22 | """ 23 | rows_expected = 4 24 | epoch_expected = [1234, 1235, 1236, 1237] 25 | object_expected = [1,1,1,0] 26 | quant1_expected = [0.01, 1.0, 1.0, 10.0] 27 | quant1_err_expected = [0.005, 0.005, 0.005, 0.1] 28 | quant2_expected = [0.5, 89.0, 89.3, np.nan] 29 | quant2_err_expected = [0.05, 0.1, 0.3, np.nan] 30 | quant_type_expected = ['radec', 'seppa', 'seppa', 'rv'] 31 | assert len(input_table) == rows_expected 32 | for meas,truth in zip(input_table['epoch'],epoch_expected): 33 | assert truth == pytest.approx(meas) 34 | for meas,truth in zip(input_table['object'],object_expected): 35 | assert truth == meas 36 | for meas,truth in zip(input_table['quant1'],quant1_expected): 37 | if np.isnan(truth): 38 | assert np.isnan(meas) 39 | else: 40 | assert truth == pytest.approx(meas) 41 | for meas,truth in zip(input_table['quant1_err'],quant1_err_expected): 42 | if np.isnan(truth): 43 | assert np.isnan(meas) 44 | else: 45 | assert truth == pytest.approx(meas) 46 | for meas,truth in zip(input_table['quant2'],quant2_expected): 47 | if np.isnan(truth): 48 | assert np.isnan(meas) 49 | else: 50 | assert truth == pytest.approx(meas) 51 | for meas,truth in zip(input_table['quant2_err'],quant2_err_expected): 52 | if np.isnan(truth): 53 | assert np.isnan(meas) 54 | else: 55 | assert truth == pytest.approx(meas) 56 | for meas,truth in zip(input_table['quant_type'],quant_type_expected): 57 | assert truth == meas 58 | 59 | def test_create_driver_from_filename(): 60 | """ 61 | Test creation of Driver object from filename as input 62 | """ 63 | input_file = os.path.join(orbitize.DATADIR, 'test_val.csv') 64 | myDriver = driver.Driver(input_file, # path to data file 65 | 'MCMC', # name of algorith for orbit-fitting 66 | 1, # number of secondary bodies in system 67 | 1.0, # total system mass [M_sun] 68 | 50.0, # total parallax of system [mas] 69 | mass_err=0.1, # mass error [M_sun] 70 | plx_err=0.1, # parallax error [mas] 71 | system_kwargs={'fit_secondary_mass':True}) 72 | _compare_table(myDriver.system.data_table) 73 | 74 | 75 | def test_create_driver_from_table(): 76 | """ 77 | Test creation of Driver object from Table as input 78 | """ 79 | input_file = os.path.join(orbitize.DATADIR, 'test_val.csv') 80 | input_table = read_file(input_file) 81 | myDriver = driver.Driver(input_table, # astropy.table Table of input 82 | 'MCMC', # name of algorithm for orbit-fitting 83 | 1, # number of secondary bodies in system 84 | 1.0, # total system mass [M_sun] 85 | 50.0, # total parallax of system [mas] 86 | mass_err=0.1, # mass error [M_sun] 87 | plx_err=0.1, # parallax error [mas] 88 | system_kwargs={'fit_secondary_mass':True}) 89 | _compare_table(myDriver.system.data_table) 90 | 91 | def test_system_kwargs(): 92 | """ 93 | Test additional arguments to the system class 94 | """ 95 | input_file = os.path.join(orbitize.DATADIR, 'test_val.csv') 96 | myDriver = driver.Driver(input_file, # path to data file 97 | 'MCMC', # name of algorith for orbit-fitting 98 | 1, # number of secondary bodies in system 99 | 1.0, # total system mass [M_sun] 100 | 50.0, # total parallax of system [mas] 101 | mass_err=0.1, # mass error [M_sun] 102 | plx_err=0.1, # parallax error [mas] 103 | system_kwargs={"tau_ref_epoch": 50000, 'fit_secondary_mass':True} 104 | ) 105 | assert myDriver.system.tau_ref_epoch == 50000 106 | 107 | if __name__ == '__main__': 108 | test_create_driver_from_filename() 109 | test_create_driver_from_table() 110 | test_system_kwargs() 111 | -------------------------------------------------------------------------------- /tests/test_kde_and_ndinterpolator.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | from scipy.stats import norm as nm 4 | from scipy.stats import gaussian_kde 5 | from scipy.interpolate import NearestNDInterpolator 6 | 7 | import orbitize.priors as priors 8 | import orbitize.results as results 9 | import orbitize 10 | 11 | import pandas as pd 12 | 13 | 14 | 15 | def test_kde(): 16 | # Read RV posteriors 17 | pdf_fromRadVel = pd.read_csv(orbitize.DATADIR+'sample_radvel_chains.csv.bz2', compression='bz2', index_col=0) 18 | per1 = pdf_fromRadVel.per1 19 | k1 = pdf_fromRadVel.k1 # Doppler semi-amplitude 20 | secosw1 = pdf_fromRadVel.secosw1 21 | sesinw1 = pdf_fromRadVel.sesinw1 22 | tc1 = pdf_fromRadVel.tc1 23 | 24 | # Put together posteriors to initialize KDE 25 | len_pdf = len(pdf_fromRadVel) 26 | total_params = 5 27 | values = np.empty((total_params,len_pdf)) 28 | values[0,:] = per1 29 | values[1,:] = k1 30 | values[2,:] = secosw1 31 | values[3,:] = sesinw1 32 | values[4,:] = tc1 33 | 34 | # Define KDE 35 | kde = gaussian_kde(values, bw_method=None) 36 | kde_prior_obj = priors.KDEPrior(kde, total_params) 37 | 38 | for II in range(total_params): 39 | samples = kde_prior_obj.draw_samples(10000) 40 | assert np.mean(values[II,:]) == pytest.approx(np.mean(samples), abs=np.std(values[II,:])) 41 | 42 | def test_ndinterpolator(): 43 | # Read RV posteriors 44 | pdf_fromRadVel = pd.read_csv(orbitize.DATADIR+'sample_radvel_chains.csv.bz2', compression='bz2', index_col=0) 45 | per1 = pdf_fromRadVel.per1 46 | k1 = pdf_fromRadVel.k1 # Doppler semi-amplitude 47 | secosw1 = pdf_fromRadVel.secosw1 48 | sesinw1 = pdf_fromRadVel.sesinw1 49 | tc1 = pdf_fromRadVel.tc1 50 | 51 | # Put together posteriors to initialize ND interpolator 52 | len_pdf = len(pdf_fromRadVel) 53 | total_params = 5 54 | values = np.empty((total_params,len_pdf)) 55 | values[0,:] = per1 56 | values[1,:] = k1 57 | values[2,:] = secosw1 58 | values[3,:] = sesinw1 59 | values[4,:] = tc1 60 | 61 | lnpriors_arr = pdf_fromRadVel.lnprobability.values 62 | 63 | # Define interp 64 | nearestNDinterp = NearestNDInterpolator(values.T,lnpriors_arr) 65 | nearestNDinterp_obj = priors.NearestNDInterpPrior(nearestNDinterp,total_params) 66 | 67 | for II in range(total_params): 68 | samples = nearestNDinterp_obj.draw_samples(10000) 69 | assert np.mean(values[II,:]) == pytest.approx(np.mean(samples), abs=np.std(values[II,:])) 70 | 71 | 72 | if __name__=='__main__': 73 | test_ndinterpolator() 74 | test_kde() 75 | -------------------------------------------------------------------------------- /tests/test_mcmc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import os 4 | import numpy as np 5 | import orbitize 6 | from orbitize.driver import Driver 7 | import orbitize.sampler as sampler 8 | import orbitize.system as system 9 | import orbitize.read_input as read_input 10 | import orbitize.results as results 11 | import matplotlib.pyplot as plt 12 | 13 | std_param_idx_fixed_mtot_plx = { 14 | 'sma1': 0, 'ecc1':1, 'inc1':2, 'aop1':3, 'pan1':4, 'tau1':5 15 | } 16 | 17 | std_param_idx = { 18 | 'sma1': 0, 'ecc1':1, 'inc1':2, 'aop1':3, 'pan1':4, 'tau1':5, 'plx':6, 'mtot':7 19 | } 20 | 21 | def test_mcmc_runs(num_temps=0, num_threads=1): 22 | """ 23 | Tests the MCMC sampler by making sure it even runs 24 | Args: 25 | num_temps: Number of temperatures to use 26 | Uses Parallel Tempering MCMC (ptemcee) if > 1, 27 | otherwises, uses Affine-Invariant Ensemble Sampler (emcee) 28 | num_threads: number of threads to run 29 | """ 30 | 31 | # use the test_csv dir 32 | input_file = os.path.join(orbitize.DATADIR, 'GJ504.csv') 33 | data_table = read_input.read_file(input_file) 34 | # Manually set 'object' column of data table 35 | data_table['object'] = 1 36 | 37 | # construct Driver 38 | n_walkers = 100 39 | myDriver = Driver( 40 | input_file, 'MCMC', 1, 1, 0.01, 41 | mcmc_kwargs={ 42 | 'num_temps': num_temps, 'num_threads': num_threads, 43 | 'num_walkers': n_walkers 44 | } 45 | ) 46 | 47 | # run it a little (tests 0 burn-in steps) 48 | myDriver.sampler.run_sampler(100) 49 | assert myDriver.sampler.results.post.shape[0] == 100 50 | 51 | # run it a little more 52 | myDriver.sampler.run_sampler(1000, burn_steps=1) 53 | assert myDriver.sampler.results.post.shape[0] == 1100 54 | 55 | # run it a little more (tests adding to results object, and periodic saving) 56 | output_filename = os.path.join(orbitize.DATADIR, 'test_mcmc.hdf5') 57 | myDriver.sampler.run_sampler( 58 | 400, burn_steps=1, output_filename=output_filename, periodic_save_freq=2 59 | ) 60 | 61 | # test results object exists and has 2100*100 steps 62 | assert os.path.exists(output_filename) 63 | saved_results = results.Results() 64 | saved_results.load_results(output_filename) 65 | assert saved_results.post.shape[0] == 1500 66 | assert saved_results.curr_pos is not None # current positions should be saved 67 | assert np.all(saved_results.curr_pos == myDriver.sampler.curr_pos) 68 | # also check it is consistent with the internal results object in myDriver 69 | assert myDriver.sampler.results.post.shape[0] == 1500 70 | 71 | # run it a little more testing that everything gets saved even if prediodic_save_freq is not a multiple of the number of steps 72 | output_filename_2 = os.path.join(orbitize.DATADIR, 'test_mcmc_v1.hdf5') 73 | myDriver.sampler.run_sampler( 74 | 500, burn_steps=1, output_filename=output_filename_2, 75 | periodic_save_freq=3 76 | ) 77 | assert myDriver.sampler.results.post.shape[0] == 2000 78 | 79 | # test that lnlikes being saved are correct 80 | returned_lnlike_test = myDriver.sampler.results.lnlike[0] 81 | computed_lnlike_test = myDriver.sampler._logl(myDriver.sampler.results.post[0]) 82 | 83 | assert returned_lnlike_test == pytest.approx(computed_lnlike_test, abs=0.01) 84 | 85 | # test resuming and restarting from a prevous save 86 | new_sampler = sampler.MCMC(myDriver.system, num_temps=num_temps, num_walkers=n_walkers, 87 | num_threads=num_threads, prev_result_filename=output_filename) 88 | assert new_sampler.results.post.shape[0] == 1500 89 | new_sampler.run_sampler(500, burn_steps=1) 90 | assert new_sampler.results.post.shape[0] == 2000 91 | assert new_sampler.results.post[0,0] == myDriver.sampler.results.post[0,0] 92 | 93 | 94 | def test_examine_chop_chains(num_temps=0, num_threads=1): 95 | """ 96 | Tests the MCMC sampler's examine_chains and chop_chains methods 97 | Args: 98 | num_temps: Number of temperatures to use 99 | Uses Parallel Tempering MCMC (ptemcee) if > 1, 100 | otherwises, uses Affine-Invariant Ensemble Sampler (emcee) 101 | num_threads: number of threads to run 102 | """ 103 | 104 | # use the test_csv dir 105 | input_file = os.path.join(orbitize.DATADIR, 'GJ504.csv') 106 | data_table = read_input.read_file(input_file) 107 | # Manually set 'object' column of data table 108 | data_table['object'] = 1 109 | 110 | # construct the system 111 | orbit = system.System(1, data_table, 1, 0.01) 112 | 113 | # construct sampler 114 | n_walkers = 20 115 | mcmc = sampler.MCMC(orbit, num_temps, n_walkers, num_threads=num_threads) 116 | 117 | # run it a little 118 | n_samples1 = 2000 # 100 steps for each of 20 walkers 119 | n_samples2 = 2000 # 100 steps for each of 20 walkers 120 | n_samples = n_samples1+n_samples2 121 | mcmc.run_sampler(n_samples1) 122 | # run it a little more (tries examine_chains within run_sampler) 123 | mcmc.run_sampler(n_samples2, examine_chains=True) 124 | # (4000 orbit samples = 20 walkers x 200 steps) 125 | 126 | # Try all variants of examine_chains 127 | mcmc.examine_chains() 128 | plt.close('all') # Close figures generated 129 | fig_list = mcmc.examine_chains(param_list=['sma1', 'ecc1', 'inc1']) 130 | # Should only get 3 figures 131 | assert len(fig_list) == 3 132 | plt.close('all') # Close figures generated 133 | mcmc.examine_chains(walker_list=[10, 12]) 134 | plt.close('all') # Close figures generated 135 | mcmc.examine_chains(n_walkers=5) 136 | plt.close('all') # Close figures generated 137 | mcmc.examine_chains(step_range=[50, 100]) 138 | plt.close('all') # Close figures generated 139 | 140 | # Now try chopping the chains 141 | # Chop off first 50 steps 142 | chop1 = 50 143 | mcmc.chop_chains(chop1) 144 | # Calculate expected number of orbits now 145 | expected_total_orbits = n_samples - chop1*n_walkers 146 | # Check lengths of arrays in results object 147 | assert len(mcmc.results.lnlike) == expected_total_orbits 148 | assert mcmc.results.post.shape[0] == expected_total_orbits 149 | 150 | # With 150 steps left, now try to trim 25 steps off each end 151 | chop2 = 25 152 | trim2 = 25 153 | mcmc.chop_chains(chop2, trim=trim2) 154 | # Calculated expected number of orbits now 155 | samples_removed = (chop1 + chop2 + trim2)*n_walkers 156 | expected_total_orbits = n_samples - samples_removed 157 | # Check lengths of arrays in results object 158 | assert len(mcmc.results.lnlike) == expected_total_orbits 159 | assert mcmc.results.post.shape[0] == expected_total_orbits 160 | 161 | 162 | def test_mcmc_param_idx(): 163 | 164 | # use the test_csv dir 165 | input_file = os.path.join(orbitize.DATADIR, 'GJ504.csv') 166 | data_table = read_input.read_file(input_file) 167 | 168 | # Manually set 'object' column of data table 169 | data_table['object'] = 1 170 | 171 | # construct Driver with fixed mass and plx 172 | n_walkers = 100 173 | myDriver = Driver(input_file, 'MCMC', 1, 1, 0.01, 174 | mcmc_kwargs={'num_temps': 0, 'num_threads': 1, 175 | 'num_walkers': n_walkers} 176 | ) 177 | 178 | # check that sampler.param_idx behaves as expected 179 | assert myDriver.sampler.sampled_param_idx == std_param_idx_fixed_mtot_plx 180 | 181 | # construct Driver with no fixed params 182 | myDriver = Driver(input_file, 'MCMC', 1, 1, 0.01, mass_err=0.1, plx_err=0.2, 183 | mcmc_kwargs={'num_temps': 0, 'num_threads': 1, 184 | 'num_walkers': n_walkers} 185 | ) 186 | 187 | assert myDriver.sampler.sampled_param_idx == std_param_idx 188 | 189 | 190 | if __name__ == "__main__": 191 | # Parallel Tempering tests 192 | test_mcmc_runs(num_temps=2, num_threads=1) 193 | test_mcmc_runs(num_temps=2, num_threads=4) 194 | # Ensemble MCMC tests 195 | test_mcmc_runs(num_temps=0, num_threads=1) 196 | test_mcmc_runs(num_temps=0, num_threads=8) 197 | # Test examine/chop chains 198 | test_examine_chop_chains(num_temps=5) # PT 199 | test_examine_chop_chains(num_temps=0) # Ensemble 200 | # param_idx utility tests 201 | test_mcmc_param_idx() 202 | -------------------------------------------------------------------------------- /tests/test_mcmc_rv.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import numpy as np 3 | import os 4 | import orbitize 5 | from orbitize.driver import Driver 6 | import orbitize.sampler as sampler 7 | import orbitize.system as system 8 | import orbitize.read_input as read_input 9 | 10 | 11 | def test_pt_mcmc_runs(num_threads=1): 12 | """ 13 | Tests the PTMCMC sampler by making sure it even runs 14 | """ 15 | 16 | # use the test_csv dir 17 | input_file = os.path.join(orbitize.DATADIR, 'test_val_rv.csv') 18 | 19 | myDriver = Driver(input_file, 'MCMC', 1, 1, 0.01, 20 | # mass_err=0.05, plx_err=0.01, 21 | system_kwargs={'fit_secondary_mass': True, 'tau_ref_epoch': 0}, 22 | mcmc_kwargs={'num_temps': 2, 'num_threads': num_threads, 'num_walkers': 100} 23 | ) 24 | 25 | # run it a little (tests 0 burn-in steps) 26 | myDriver.sampler.run_sampler(100) 27 | 28 | # run it a little more 29 | myDriver.sampler.run_sampler(1000, burn_steps=1) 30 | 31 | # run it a little more (tests adding to results object) 32 | myDriver.sampler.run_sampler(1000, burn_steps=1) 33 | 34 | fixed_index = [index for index, fixed_param in myDriver.sampler.fixed_params] 35 | clean_params = np.delete(myDriver.sampler.results.post[0], fixed_index) 36 | 37 | # test that lnlikes being saved are correct 38 | returned_lnlike_test = myDriver.sampler.results.lnlike[0] 39 | computed_lnlike_test = myDriver.sampler._logl(clean_params) 40 | 41 | assert returned_lnlike_test == pytest.approx(computed_lnlike_test, abs=0.01) 42 | 43 | 44 | def test_ensemble_mcmc_runs(num_threads=1): 45 | """ 46 | Tests the EnsembleMCMC sampler by making sure it even runs 47 | """ 48 | 49 | # use the test_csv dir 50 | input_file = os.path.join(orbitize.DATADIR, 'test_val.csv') 51 | 52 | myDriver = Driver(input_file, 'MCMC', 1, 1, 0.01, 53 | system_kwargs={'fit_secondary_mass': True, 54 | 'tau_ref_epoch': 0}, 55 | mcmc_kwargs={'num_temps': 1, 'num_threads': num_threads, 'num_walkers': 100} 56 | ) 57 | 58 | # run it a little (tests 0 burn-in steps) 59 | myDriver.sampler.run_sampler(100) 60 | 61 | # run it a little more 62 | myDriver.sampler.run_sampler(1000, burn_steps=1) 63 | 64 | # run it a little more (tests adding to results object) 65 | myDriver.sampler.run_sampler(1000, burn_steps=1) 66 | 67 | fixed_index = [index for index, fixed_param in myDriver.sampler.fixed_params] 68 | clean_params = np.delete(myDriver.sampler.results.post[0], fixed_index) 69 | 70 | # test that lnlikes being saved are correct 71 | returned_lnlike_test = myDriver.sampler.results.lnlike[0] 72 | computed_lnlike_test = myDriver.sampler._logl(clean_params) 73 | 74 | assert returned_lnlike_test == pytest.approx(computed_lnlike_test, abs=0.01) 75 | 76 | 77 | if __name__ == "__main__": 78 | test_pt_mcmc_runs(num_threads=1) 79 | test_pt_mcmc_runs(num_threads=4) 80 | test_ensemble_mcmc_runs(num_threads=1) 81 | test_ensemble_mcmc_runs(num_threads=4) 82 | -------------------------------------------------------------------------------- /tests/test_nested_sampler.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests the NestedSampler class by fixing all parameters except for eccentricity. 3 | """ 4 | 5 | from orbitize import system, sampler 6 | import numpy as np 7 | import pytest 8 | from orbitize.system import generate_synthetic_data 9 | 10 | 11 | def test_nested_sampler(): 12 | # generate data 13 | mtot = 1.2 # total system mass [M_sol] 14 | plx = 60.0 # parallax [mas] 15 | orbit_frac = 95 16 | data_table, sma = generate_synthetic_data( 17 | orbit_frac, 18 | mtot, 19 | plx, 20 | num_obs=30, 21 | ) 22 | 23 | # assumed ecc value 24 | ecc = 0.5 25 | 26 | # initialize orbitize `System` object 27 | sys = system.System(1, data_table, mtot, plx) 28 | lab = sys.param_idx 29 | 30 | ecc = 0.5 # eccentricity 31 | 32 | # set all parameters except eccentricity to fixed values (same as used to generate data) 33 | sys.sys_priors[lab["inc1"]] = np.pi / 4 34 | sys.sys_priors[lab["sma1"]] = sma 35 | sys.sys_priors[lab["aop1"]] = np.pi / 4 36 | sys.sys_priors[lab["pan1"]] = np.pi / 4 37 | sys.sys_priors[lab["tau1"]] = 0.8 38 | sys.sys_priors[lab["plx"]] = plx 39 | sys.sys_priors[lab["mtot"]] = mtot 40 | 41 | # run both static & dynamic nested samplers 42 | mysampler = sampler.NestedSampler(sys) 43 | _ = mysampler.run_sampler(bound="multi", pfrac=0.95, static=False, num_threads=8) 44 | print("Finished first run!") 45 | 46 | dynamic_eccentricities = mysampler.results.post[:, lab["ecc1"]] 47 | assert np.median(dynamic_eccentricities) == pytest.approx(ecc, abs=0.1) 48 | 49 | _ = mysampler.run_sampler(bound="multi", static=True, num_threads=8) 50 | print("Finished second run!") 51 | 52 | static_eccentricities = mysampler.results.post[:, lab["ecc1"]] 53 | assert np.median(static_eccentricities) == pytest.approx(ecc, abs=0.1) 54 | 55 | # check that the static sampler raises an error when user tries to set pfrac 56 | # for static sampler 57 | try: 58 | mysampler.run_sampler(pfrac=0.1, static=True) 59 | except ValueError: 60 | pass 61 | 62 | 63 | if __name__ == "__main__": 64 | test_nested_sampler() 65 | -------------------------------------------------------------------------------- /tests/test_priors.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | import os 4 | from scipy.stats import norm as nm 5 | 6 | import orbitize.priors as priors 7 | from orbitize.system import System 8 | from orbitize.read_input import read_file 9 | from orbitize.sampler import MCMC 10 | from orbitize import DATADIR 11 | 12 | threshold = 1e-1 13 | 14 | initialization_inputs = { 15 | priors.GaussianPrior: [1000.0, 1.0], 16 | priors.LogUniformPrior: [1.0, 2.0], 17 | priors.UniformPrior: [0.0, 1.0], 18 | priors.SinPrior: [], 19 | priors.LinearPrior: [-2.0, 2.0], 20 | } 21 | 22 | expected_means_mins_maxes = { 23 | priors.GaussianPrior: (1000.0, 0.0, np.inf), 24 | priors.LogUniformPrior: (1 / np.log(2), 1.0, 2.0), 25 | priors.UniformPrior: (0.5, 0.0, 1.0), 26 | priors.SinPrior: (np.pi / 2.0, 0.0, np.pi), 27 | priors.LinearPrior: (1.0 / 3.0, 0.0, 1.0), 28 | } 29 | 30 | lnprob_inputs = { 31 | priors.GaussianPrior: np.array([-3.0, np.inf, 1000.0, 999.0]), 32 | priors.LogUniformPrior: np.array([-1.0, 0.0, 1.0, 1.5, 2.0, 2.5]), 33 | priors.UniformPrior: np.array([0.0, 0.5, 1.0, -1.0, 2.0]), 34 | priors.SinPrior: np.array([0.0, np.pi / 2.0, np.pi, 10.0, -1.0]), 35 | priors.LinearPrior: np.array([0.0, 0.5, 1.0, 2.0, -1.0]), 36 | } 37 | 38 | expected_probs = { 39 | priors.GaussianPrior: np.array( 40 | [ 41 | 0.0, 42 | 0.0, 43 | nm(1000.0, 1.0).pdf(1000.0) * np.sqrt(2 * np.pi), 44 | nm(1000.0, 1.0).pdf(999.0) * np.sqrt(2 * np.pi), 45 | ] 46 | ), 47 | priors.LogUniformPrior: np.array([0.0, 0.0, 1.0, 2.0 / 3.0, 0.5, 0.0]) / np.log(2), 48 | priors.UniformPrior: np.array([1.0, 1.0, 1.0, 0.0, 0.0]), 49 | priors.SinPrior: np.array([0.0, 0.5, 0.0, 0.0, 0.0]), 50 | priors.LinearPrior: np.array([2.0, 1.0, 0.0, 0.0, 0.0]), 51 | } 52 | 53 | 54 | def test_draw_samples(): 55 | """ 56 | Test basic functionality of `draw_samples()` method of each `Prior` class. 57 | """ 58 | for Prior in initialization_inputs.keys(): 59 | inputs = initialization_inputs[Prior] 60 | 61 | TestPrior = Prior(*inputs) 62 | samples = TestPrior.draw_samples(10000) 63 | 64 | exp_mean, exp_min, exp_max = expected_means_mins_maxes[Prior] 65 | assert np.mean(samples) == pytest.approx(exp_mean, abs=threshold) 66 | assert np.min(samples) > exp_min 67 | assert np.max(samples) < exp_max 68 | 69 | 70 | def test_compute_lnprob(): 71 | """ 72 | Test basic functionality of `compute_lnprob()` method of each `Prior` class. 73 | """ 74 | for Prior in initialization_inputs.keys(): 75 | inputs = initialization_inputs[Prior] 76 | 77 | TestPrior = Prior(*inputs) 78 | values2test = lnprob_inputs[Prior] 79 | 80 | lnprobs = TestPrior.compute_lnprob(values2test) 81 | 82 | assert np.log(expected_probs[Prior]) == pytest.approx(lnprobs, abs=threshold) 83 | 84 | 85 | def test_obsprior(): 86 | """ 87 | Test API setup with obs prior and run it a few MCMC steps to make sure nothing 88 | breaks. 89 | """ 90 | 91 | input_file = os.path.join(DATADIR, "xyz_test_data.csv") 92 | data_table = read_file(input_file) 93 | mtot = 1.0 94 | 95 | mySystem = System( 96 | 1, data_table, mtot, 10.0, mass_err=0, plx_err=0, fitting_basis="ObsPriors" 97 | ) 98 | 99 | # construct sampler 100 | n_walkers = 20 101 | num_temps = 1 102 | my_sampler = MCMC(mySystem, num_temps, n_walkers, num_threads=1) 103 | 104 | ra_err = mySystem.data_table["quant1_err"] 105 | dec_err = mySystem.data_table["quant2_err"] 106 | epochs = mySystem.data_table["epoch"] 107 | 108 | # define the `ObsPrior` object 109 | my_obsprior = priors.ObsPrior(ra_err, dec_err, epochs, mtot) 110 | 111 | # set the priors on `per`, `ecc`, `tp` to point to this object 112 | for i in [ 113 | mySystem.param_idx["per1"], 114 | mySystem.param_idx["ecc1"], 115 | mySystem.param_idx["tp1"], 116 | ]: 117 | mySystem.sys_priors[i] = my_obsprior 118 | 119 | # run the mcmc a few steps to make sure nothing breaks 120 | my_sampler.run_sampler(5, burn_steps=0) 121 | 122 | 123 | if __name__ == "__main__": 124 | test_obsprior() 125 | test_compute_lnprob() 126 | test_draw_samples() 127 | print("All tests passed!") 128 | -------------------------------------------------------------------------------- /tests/test_read_input.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import numpy as np 3 | import os 4 | import orbitize 5 | from orbitize.read_input import read_file, write_orbitize_input 6 | 7 | 8 | def _compare_table(input_table): 9 | """ 10 | Tests input table to expected values, which are: 11 | epoch object quant1 quant1_err quant2 quant2_err quant_type 12 | float64 int float64 float64 float64 float64 str5 13 | ------- ------ ------- ---------- ------- ---------- ---------- 14 | 1234.0 1 0.01 0.005 0.5 0.05 radec 15 | 1235.0 1 1.0 0.005 89.0 0.1 seppa 16 | 1236.0 1 1.0 0.005 89.3 0.3 seppa 17 | 1237.0 0 10.0 0.1 nan nan rv 18 | """ 19 | rows_expected = 4 20 | epoch_expected = [1234, 1235, 1236, 1237] 21 | object_expected = [1, 1, 1, 0] 22 | quant1_expected = [0.01, 1.0, 1.0, 10.0] 23 | quant1_err_expected = [0.005, 0.005, 0.005, 0.1] 24 | quant2_expected = [0.5, 89.0, 89.3, np.nan] 25 | quant2_err_expected = [0.05, 0.1, 0.3, np.nan] 26 | quant_type_expected = ["radec", "seppa", "seppa", "rv"] 27 | instrument_expected = ["defrd", "defsp", "defsp", "defrv"] 28 | assert len(input_table) == rows_expected 29 | for meas, truth in zip(input_table["epoch"], epoch_expected): 30 | assert truth == pytest.approx(meas) 31 | for meas, truth in zip(input_table["object"], object_expected): 32 | assert truth == meas 33 | for meas, truth in zip(input_table["quant1"], quant1_expected): 34 | if np.isnan(truth): 35 | assert np.isnan(meas) 36 | else: 37 | assert truth == pytest.approx(meas) 38 | for meas, truth in zip(input_table["quant1_err"], quant1_err_expected): 39 | if np.isnan(truth): 40 | assert np.isnan(meas) 41 | else: 42 | assert truth == pytest.approx(meas) 43 | for meas, truth in zip(input_table["quant2"], quant2_expected): 44 | if np.isnan(truth): 45 | assert np.isnan(meas) 46 | else: 47 | assert truth == pytest.approx(meas) 48 | for meas, truth in zip(input_table["quant2_err"], quant2_err_expected): 49 | if np.isnan(truth): 50 | assert np.isnan(meas) 51 | else: 52 | assert truth == pytest.approx(meas) 53 | for meas, truth in zip(input_table["quant_type"], quant_type_expected): 54 | assert truth == meas 55 | 56 | for meas, truth in zip(input_table["instrument"], instrument_expected): 57 | assert truth == meas 58 | 59 | 60 | def test_read_file(): 61 | """ 62 | Test the read_file function using the test_val.csv file and test_val_radec.csv 63 | """ 64 | # Check that main test input is read in with correct values 65 | input_file = os.path.join(orbitize.DATADIR, "test_val.csv") 66 | _compare_table(read_file(input_file)) 67 | # Check that an input value with all valid entries and only ra/dec 68 | # columns can be read 69 | input_file_radec = os.path.join(orbitize.DATADIR, "test_val_radec.csv") 70 | read_file(input_file_radec) 71 | 72 | 73 | def test_write_orbitize_input(): 74 | """ 75 | Test the write_orbitize_input and the read_file functions 76 | """ 77 | input_file = os.path.join(orbitize.DATADIR, "test_val.csv") 78 | test_table = read_file(input_file) 79 | output_file = os.path.join(orbitize.DATADIR, "temp_test_orbitize_input.csv") 80 | # If temp output file already exists, delete it 81 | if os.path.isfile(output_file): 82 | os.remove(output_file) 83 | try: # Catch these tests so that we remove temporary file 84 | # Test that we were able to write the table 85 | write_orbitize_input(test_table, output_file) 86 | assert os.path.isfile(output_file) 87 | # Test that we can read the table and check if it's correct 88 | test_table_2 = read_file(output_file) 89 | _compare_table(test_table_2) 90 | finally: 91 | # Remove temporary file 92 | os.remove(output_file) 93 | 94 | 95 | def test_cov_input(): 96 | """ 97 | Test including radec and seppa covariances/correlations. 98 | """ 99 | testdir = orbitize.DATADIR 100 | # Check that main test input is read in with correct values 101 | input_file = os.path.join(testdir, "test_val_cov.csv") 102 | input_data = read_file(input_file) 103 | _compare_table(input_data) 104 | print(input_data) 105 | # Check the covariance column 106 | quant12_corr_truth = [0.25, np.nan, -0.5, np.nan] 107 | assert "quant12_corr" in input_data.columns 108 | for row, truth in zip(input_data, quant12_corr_truth): 109 | meas = row["quant12_corr"] 110 | if np.isnan(truth): 111 | assert np.isnan(meas) 112 | else: 113 | assert truth == pytest.approx(meas) 114 | 115 | 116 | def test_read_old_orbitize_format(): 117 | """ 118 | Test the read_file function when using an old orbitize data file without 119 | `quant12_corr` and `instrument` fields. 120 | """ 121 | # Check that main test input is read in with correct values 122 | input_file = os.path.join(orbitize.DATADIR, "old_orbitize_format.csv") 123 | input_data = read_file(input_file) 124 | 125 | # check correlation and instrument are defualts 126 | assert np.isnan(input_data["quant12_corr"][0]) 127 | assert input_data["instrument"][0] == "defsp" 128 | 129 | assert np.isnan(input_data["quant12_corr"][1]) 130 | assert input_data["instrument"][1] == "defrd" 131 | 132 | assert np.isnan(input_data["quant12_corr"][2]) 133 | assert input_data["instrument"][2] == "defrv" 134 | 135 | 136 | if __name__ == "__main__": 137 | test_read_file() 138 | test_write_orbitize_input() 139 | test_cov_input() 140 | test_read_old_orbitize_format() 141 | -------------------------------------------------------------------------------- /tests/test_rebound.py: -------------------------------------------------------------------------------- 1 | from matplotlib import pyplot as plt 2 | from astropy.time import Time 3 | from orbitize import sampler 4 | from orbitize.system import System 5 | from orbitize import DATADIR 6 | from orbitize.read_input import read_file 7 | import astropy.units as u 8 | import numpy as np 9 | 10 | # Test Data 11 | mass_in_mjup = 10 12 | mB_Jup = 7 13 | mass_in_msun = mass_in_mjup * u.Mjup.to(u.Msun) 14 | massB = mB_Jup * u.Mjup.to(u.Msun) 15 | m_pl = np.array([mass_in_msun, mass_in_msun, mass_in_msun, massB]) 16 | 17 | # From System HR-8799 18 | # NOTE planets get closer to the star in alphabetical order, 19 | # i.e. B is farthest, E is closest 20 | sma = np.array([16, 26, 43, 71]) 21 | ecc = np.array([0.12, 0.13, 0.02, 0.02]) 22 | inc = np.array([0.47123, 0.47123, 0.47123, 0.47123]) 23 | aop = np.array([1.91986, 0.29670, 1.16937, 1.5184]) 24 | pan = np.array([1.18682, 1.18682, 1.18682, 1.18682]) 25 | tau = np.array([0.71, 0.79, 0.50, 0.54]) 26 | plx = 7 27 | mtot = 1.49 28 | tau_ref_epoch = 0 29 | years = 365.25 * 5 30 | 31 | # need a properly formatted data table for the code to run but it doesn't 32 | # matter what's in it 33 | input_file = "{}/GJ504.csv".format(DATADIR) 34 | data_table = read_file(input_file) 35 | num_secondary_bodies = 4 36 | 37 | epochs = Time(np.linspace(2020, 2022, num=int(1000)), format="decimalyear").mjd 38 | 39 | sma1 = sma[0] 40 | ecc1 = ecc[0] 41 | inc1 = inc[0] 42 | aop1 = aop[0] 43 | pan1 = pan[0] 44 | tau1 = tau[0] 45 | sma2 = sma[1] 46 | ecc2 = ecc[1] 47 | inc2 = inc[1] 48 | aop2 = aop[1] 49 | pan2 = pan[1] 50 | tau2 = tau[1] 51 | sma3 = sma[2] 52 | ecc3 = ecc[2] 53 | inc3 = inc[2] 54 | aop3 = aop[2] 55 | pan3 = pan[2] 56 | tau3 = tau[2] 57 | sma4 = sma[3] 58 | ecc4 = ecc[3] 59 | inc4 = inc[3] 60 | aop4 = aop[3] 61 | pan4 = pan[3] 62 | tau4 = tau[3] 63 | m1 = m_pl[0] 64 | m2 = m_pl[1] 65 | m3 = m_pl[2] 66 | m4 = m_pl[3] 67 | m_st = mtot - sum(m_pl) 68 | 69 | hr8799_sys = System( 70 | num_secondary_bodies, 71 | data_table, 72 | m_st, 73 | plx, 74 | fit_secondary_mass=True, 75 | tau_ref_epoch=tau_ref_epoch, 76 | ) 77 | 78 | params_arr = np.array( 79 | [ 80 | sma1, 81 | ecc1, 82 | inc1, 83 | aop1, 84 | pan1, 85 | tau1, 86 | sma2, 87 | ecc2, 88 | inc2, 89 | aop2, 90 | pan2, 91 | tau2, 92 | sma3, 93 | ecc3, 94 | inc3, 95 | aop3, 96 | pan3, 97 | tau3, 98 | sma4, 99 | ecc4, 100 | inc4, 101 | aop4, 102 | pan4, 103 | tau4, 104 | plx, 105 | m1, 106 | m2, 107 | m3, 108 | m4, 109 | m_st, 110 | ] 111 | ) 112 | 113 | 114 | def test_8799_rebound_vs_kepler(plotname=None): 115 | """ 116 | Test that for the HR 8799 system between 2020 and 2022, the rebound and 117 | keplerian (with epicycle approximation) backends produce similar results. 118 | """ 119 | 120 | assert hr8799_sys.track_planet_perturbs 121 | 122 | rra, rde, _ = hr8799_sys.compute_all_orbits( 123 | params_arr, epochs=epochs, comp_rebound=True 124 | ) 125 | kra, kde, _ = hr8799_sys.compute_all_orbits( 126 | params_arr, epochs=epochs, comp_rebound=False 127 | ) 128 | 129 | delta_ra = abs(rra - kra) 130 | delta_de = abs(rde - kde) 131 | yepochs = Time(epochs, format="mjd").decimalyear 132 | 133 | # check that the difference between these two solvers is smaller than 134 | # GRAVITY precision over the next two years 135 | gravity_precision_mas = 100 * 1e-3 136 | assert np.all(delta_ra < gravity_precision_mas) 137 | assert np.all(delta_de < gravity_precision_mas) 138 | 139 | # check that the difference between these two solvers is larger than 140 | # 10 uas, though 141 | assert np.max(delta_ra) > 10 * 1e-3 142 | assert np.max(delta_de) > 10 * 1e-3 143 | 144 | if plotname is not None: 145 | # plot 1: RA & Dec differences over time 146 | fig, (ax1, ax2) = plt.subplots(2) 147 | fig.suptitle("Massive Orbits in Rebound vs. Orbitize approx.") 148 | 149 | ax1.plot(yepochs, delta_ra[:, 0], "black", label="Star") 150 | ax2.plot(yepochs, delta_de[:, 0], "dimgray", label="Star") 151 | 152 | ax1.plot( 153 | yepochs, delta_ra[:, 1], "brown", label="Planet E: RA offsets" 154 | ) # first planet 155 | ax2.plot(yepochs, delta_de[:, 1], "red", label="Planet E: Dec offsets") 156 | 157 | ax1.plot( 158 | yepochs, delta_ra[:, 2], "coral", label="Planet D: RA offsets" 159 | ) # second planet 160 | ax2.plot(yepochs, delta_de[:, 2], "orange", label="Planet D: Dec offsets") 161 | 162 | ax1.plot( 163 | yepochs, delta_ra[:, 3], "greenyellow", label="Planet C: RA offsets" 164 | ) # third planet 165 | ax2.plot(yepochs, delta_de[:, 3], "green", label="Planet C: Dec offsets") 166 | 167 | ax1.plot( 168 | yepochs, delta_ra[:, 4], "dodgerblue", label="Planet B: RA offsets" 169 | ) # fourth planet 170 | ax2.plot(yepochs, delta_de[:, 4], "blue", label="Planet B: Dec offsets") 171 | 172 | plt.xlabel("year") 173 | plt.ylabel("milliarcseconds") 174 | ax1.legend() 175 | ax2.legend() 176 | plt.savefig("{}_differences.png".format(plotname), dpi=250) 177 | 178 | # plot 2: orbit tracks over time 179 | plt.figure() 180 | plt.plot(kra[:, 1:5, 0], kde[:, 1:5, 0], "indigo", label="Orbitize approx.") 181 | plt.plot(kra[-1, 1:5, 0], kde[-1, 1:5, 0], "o") 182 | 183 | plt.plot(rra[:, 1:5, 0], rde[:, 1:5, 0], "r", label="Rebound", alpha=0.25) 184 | plt.plot(rra[-1, 1:5, 0], rde[-1, 1:5, 0], "o", alpha=0.25) 185 | 186 | plt.plot(0, 0, "*") 187 | plt.legend() 188 | plt.savefig("{}_orbittracks.png".format(plotname), dpi=250) 189 | 190 | # plot 3: primary orbit track over time 191 | plt.figure() 192 | plt.plot(kra[:, 0, 0], kde[:, 0, 0], "indigo", label="Orbitize approx.") 193 | plt.plot(kra[-1, 0, 0], kde[-1, 0, 0], "o") 194 | 195 | plt.plot(rra[:, 0], rde[:, 0], "r", label="Rebound", alpha=0.25) 196 | plt.plot(rra[-1, 0], rde[-1, 0], "o", alpha=0.25) 197 | 198 | plt.plot(0, 0, "*") 199 | plt.legend() 200 | plt.savefig("{}_primaryorbittrack.png".format(plotname), dpi=250) 201 | 202 | 203 | def test_rebound_mcmc(): 204 | """ 205 | Test that a rebound fit runs through one MCMC iteration successfully. 206 | """ 207 | 208 | input_file = "{}/GJ504.csv".format(DATADIR) 209 | data_table = read_file(input_file) 210 | 211 | my_sys = System( 212 | num_secondary_bodies=1, 213 | use_rebound=True, 214 | fit_secondary_mass=True, 215 | data_table=data_table, 216 | stellar_or_system_mass=1.22, 217 | mass_err=0, 218 | plx=56.95, 219 | plx_err=0.0, 220 | ) 221 | 222 | my_mcmc_samp = sampler.MCMC(my_sys, num_temps=1, num_walkers=14, num_threads=1) 223 | my_mcmc_samp.run_sampler(1, burn_steps=0) 224 | 225 | 226 | if __name__ == "__main__": 227 | # test_8799_rebound_vs_kepler(plotname="hr8799_diffs") 228 | test_rebound_mcmc() 229 | -------------------------------------------------------------------------------- /tests/test_rv_plotting.py: -------------------------------------------------------------------------------- 1 | from orbitize import driver, DATADIR, plot 2 | import multiprocessing as mp 3 | 4 | 5 | def test_rv_default_inst(): 6 | # Initialize Driver to Run MCMC 7 | filename = "{}/HD4747.csv".format(DATADIR) 8 | 9 | num_secondary_bodies = 1 10 | system_mass = 0.84 # [Msol] 11 | plx = 53.18 # [mas] 12 | mass_err = 0.04 # [Msol] 13 | plx_err = 0.12 # [mas] 14 | 15 | num_temps = 5 16 | num_walkers = 30 17 | num_threads = mp.cpu_count() # or a different number if you prefer 18 | 19 | my_driver = driver.Driver( 20 | filename, 21 | "MCMC", 22 | num_secondary_bodies, 23 | system_mass, 24 | plx, 25 | mass_err=mass_err, 26 | plx_err=plx_err, 27 | system_kwargs={"fit_secondary_mass": True, "tau_ref_epoch": 0}, 28 | mcmc_kwargs={ 29 | "num_temps": num_temps, 30 | "num_walkers": num_walkers, 31 | "num_threads": num_threads, 32 | }, 33 | ) 34 | 35 | total_orbits = 1000 36 | burn_steps = 10 37 | thin = 2 38 | 39 | # Run Quick Sampler 40 | m = my_driver.sampler 41 | m.run_sampler(total_orbits, burn_steps=burn_steps, thin=thin) 42 | epochs = my_driver.system.data_table["epoch"] 43 | 44 | # Test plotting with single orbit 45 | _ = m.results.plot_orbits( 46 | object_to_plot=1, # Plots orbits for the first (and only) companion 47 | num_orbits_to_plot=1, # Plots orbits of this companion 48 | start_mjd=epochs[3], # Minimum MJD for colorbar 49 | rv_time_series=True, 50 | plot_astrometry_insts=True, 51 | ) 52 | 53 | # Test plotting with multiple orbits 54 | _ = m.results.plot_orbits( 55 | object_to_plot=1, # Plots orbits for the first (and only) companion 56 | num_orbits_to_plot=10, # Plots orbits of this companion 57 | start_mjd=epochs[3], # Minimum MJD for colorbar 58 | rv_time_series=True, 59 | plot_astrometry_insts=True, 60 | ) 61 | 62 | 63 | 64 | def test_rv_multiple_inst(): 65 | filename = "{}/HR7672_joint.csv".format(DATADIR) 66 | 67 | num_secondary_bodies = 1 68 | system_mass = 1.08 # [Msol] 69 | plx = 56.2 # [mas] 70 | mass_err = 0.04 # [Msol] 71 | plx_err = 0.01 # [mas] 72 | 73 | # MCMC parameters 74 | num_temps = 5 75 | num_walkers = 30 76 | num_threads = 2 77 | 78 | my_driver = driver.Driver( 79 | filename, 80 | "MCMC", 81 | num_secondary_bodies, 82 | system_mass, 83 | plx, 84 | mass_err=mass_err, 85 | plx_err=plx_err, 86 | system_kwargs={"fit_secondary_mass": True, "tau_ref_epoch": 0}, 87 | mcmc_kwargs={ 88 | "num_temps": num_temps, 89 | "num_walkers": num_walkers, 90 | "num_threads": num_threads, 91 | }, 92 | ) 93 | 94 | total_orbits = 500 95 | burn_steps = 10 96 | thin = 2 97 | 98 | m = my_driver.sampler 99 | m.run_sampler(total_orbits, burn_steps=burn_steps, thin=thin) 100 | epochs = my_driver.system.data_table["epoch"] 101 | 102 | _ = m.results.plot_orbits( 103 | object_to_plot=1, 104 | num_orbits_to_plot=1, 105 | start_mjd=epochs[ 106 | 0 107 | ], # Minimum MJD for colorbar (here we choose first data epoch) 108 | rv_time_series=True, 109 | plot_astrometry_insts=True, 110 | ) 111 | 112 | # Test plotting with multiple orbits 113 | _ = m.results.plot_orbits( 114 | object_to_plot=1, 115 | num_orbits_to_plot=10, 116 | start_mjd=epochs[ 117 | 0 118 | ], # Minimum MJD for colorbar (here we choose first data epoch) 119 | rv_time_series=True, 120 | plot_astrometry_insts=True, 121 | ) 122 | 123 | def test_secondary_rv(): 124 | """ 125 | Make sure plotting works when all RVs are secondary RVs 126 | """ 127 | filename = "{}/HR7672_joint.csv".format(DATADIR) 128 | 129 | num_secondary_bodies = 1 130 | system_mass = 1.08 # [Msol] 131 | plx = 56.2 # [mas] 132 | mass_err = 0.04 # [Msol] 133 | plx_err = 0.01 # [mas] 134 | 135 | # MCMC parameters 136 | num_temps = 5 137 | num_walkers = 30 138 | num_threads = 2 139 | 140 | my_driver = driver.Driver( 141 | filename, 142 | "MCMC", 143 | num_secondary_bodies, 144 | system_mass, 145 | plx, 146 | mass_err=mass_err, 147 | plx_err=plx_err, 148 | system_kwargs={"fit_secondary_mass": True, "tau_ref_epoch": 0}, 149 | mcmc_kwargs={ 150 | "num_temps": num_temps, 151 | "num_walkers": num_walkers, 152 | "num_threads": num_threads, 153 | }, 154 | ) 155 | 156 | # pretend all the RVs are of the secondary 157 | my_driver.system.data_table["object"] = 1 158 | 159 | total_orbits = 500 160 | burn_steps = 10 161 | thin = 2 162 | 163 | m = my_driver.sampler 164 | m.run_sampler(total_orbits, burn_steps=burn_steps, thin=thin) 165 | epochs = my_driver.system.data_table["epoch"] 166 | 167 | plot.plot_orbits( 168 | my_driver.sampler.results, 169 | object_to_plot=1, 170 | num_orbits_to_plot=10, 171 | start_mjd=epochs[0], 172 | rv_time_series2=True, 173 | plot_astrometry_insts=True, 174 | ) 175 | 176 | import matplotlib.pyplot as plt 177 | plt.savefig('foo.png') 178 | 179 | def test_secondary_and_primary_rv(): 180 | """ 181 | Make sure plotting works RVs are a mix of primary and secondary 182 | """ 183 | filename = "{}/HR7672_joint.csv".format(DATADIR) 184 | 185 | num_secondary_bodies = 1 186 | system_mass = 1.08 # [Msol] 187 | plx = 56.2 # [mas] 188 | mass_err = 0.04 # [Msol] 189 | plx_err = 0.01 # [mas] 190 | 191 | # MCMC parameters 192 | num_temps = 5 193 | num_walkers = 30 194 | num_threads = 2 195 | 196 | my_driver = driver.Driver( 197 | filename, 198 | "MCMC", 199 | num_secondary_bodies, 200 | system_mass, 201 | plx, 202 | mass_err=mass_err, 203 | plx_err=plx_err, 204 | system_kwargs={"fit_secondary_mass": True, "tau_ref_epoch": 0}, 205 | mcmc_kwargs={ 206 | "num_temps": num_temps, 207 | "num_walkers": num_walkers, 208 | "num_threads": num_threads, 209 | }, 210 | ) 211 | 212 | # pretend all the RVs are of the secondary 213 | my_driver.system.data_table["object"] = 1 214 | my_driver.system.data_table["object"][0:4] = 0 215 | 216 | total_orbits = 500 217 | burn_steps = 10 218 | thin = 2 219 | 220 | m = my_driver.sampler 221 | m.run_sampler(total_orbits, burn_steps=burn_steps, thin=thin) 222 | epochs = my_driver.system.data_table["epoch"] 223 | 224 | plot.plot_orbits( 225 | my_driver.sampler.results, 226 | object_to_plot=1, 227 | num_orbits_to_plot=10, 228 | start_mjd=epochs[0], 229 | rv_time_series=True, 230 | rv_time_series2=True, 231 | plot_astrometry_insts=True, 232 | ) 233 | 234 | 235 | 236 | 237 | if __name__ == "__main__": 238 | # test_rv_default_inst() 239 | # test_rv_multiple_inst() 240 | test_secondary_rv() 241 | # test_secondary_and_primary_rv() 242 | -------------------------------------------------------------------------------- /tests/test_secondary_rvs.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import os 3 | from astropy.time import Time 4 | from pandas import DataFrame 5 | 6 | from orbitize.kepler import calc_orbit 7 | from orbitize import read_input, system, sampler, DATADIR 8 | 9 | 10 | def test_secondary_rv_lnlike_calc(): 11 | """ 12 | Generates fake secondary RV data and asserts that 13 | the log(likelihood) of the true parameters is what we expect. 14 | Also tests that the primary and secondary RV orbits are related by 15 | -m/mtot 16 | """ 17 | 18 | # define an orbit & generate secondary RVs 19 | a = 10 20 | e = 0 21 | i = np.pi / 4 22 | omega = 0 23 | Omega = 0 24 | tau = 0.3 25 | m0 = 1 26 | m1 = 0.1 27 | plx = 10 28 | orbitize_params_list = np.array([a, e, i, omega, Omega, tau, plx, m1, m0]) 29 | 30 | epochs = Time(np.linspace(2005, 2025, int(1e3)), format="decimalyear").mjd 31 | 32 | _, _, rv_p = calc_orbit( 33 | epochs, a, e, i, omega, Omega, tau, plx, m0 + m1, mass_for_Kamp=m0 34 | ) 35 | 36 | data_file = DataFrame(columns=["epoch", "object", "rv", "rv_err"]) 37 | data_file.epoch = epochs 38 | data_file.object = np.ones(len(epochs), dtype=int) 39 | data_file.rv = rv_p 40 | data_file.rv_err = np.ones(len(epochs)) * 0.01 41 | 42 | data_file.to_csv("tmp.csv", index=False) 43 | 44 | # set up a fit using the simulated data 45 | data_table = read_input.read_file("tmp.csv") 46 | mySys = system.System( 47 | 1, data_table, m0, plx, mass_err=0.1, plx_err=0.1, fit_secondary_mass=True 48 | ) 49 | mySamp = sampler.MCMC(mySys) 50 | computed_lnlike = mySamp._logl(orbitize_params_list) 51 | 52 | # residuals should be 0 53 | assert computed_lnlike == np.sum( 54 | -np.log(np.sqrt(2 * np.pi * data_file.rv_err.values**2)) 55 | ) 56 | 57 | # clean up 58 | os.system("rm tmp.csv") 59 | 60 | # assert that the secondary orbit is the primary orbit scaled 61 | _, _, rv = mySys.compute_all_orbits(orbitize_params_list) 62 | rv0 = rv[:, 0] 63 | rv1 = rv[:, 1] 64 | 65 | assert np.all(rv0 == -m1 / m0 * rv1) 66 | 67 | def test_read_input(): 68 | """ 69 | Test that reading in a data file with only a companion RV and relative astrometry 70 | works. Added in response to issue #351. 71 | """ 72 | 73 | input_data = read_input.read_file('{}/HD4747.csv'.format(DATADIR)) 74 | input_data['object'] = 1 # make sure all astrometry and RV is marked as of the secondary 75 | mySystem = system.System(1, input_data, 1, 1, fit_secondary_mass=False) 76 | 77 | if __name__ == "__main__": 78 | # test_secondary_rv_lnlike_calc() 79 | test_read_input() 80 | -------------------------------------------------------------------------------- /tests/test_system.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests functionality of methods in system.py 3 | """ 4 | import numpy as np 5 | import pytest 6 | import os 7 | import orbitize 8 | import orbitize.read_input as read_input 9 | import orbitize.system as system 10 | 11 | 12 | def test_convert_data_table_radec2seppa(): 13 | num_secondary_bodies = 1 14 | input_file = os.path.join(orbitize.DATADIR, "test_val.csv") 15 | data_table = read_input.read_file(input_file) 16 | system_mass = 1.0 17 | plx = 10.0 18 | mass_err = 0.1 19 | plx_err = 1.0 20 | # Initialize System object 21 | test_system = system.System( 22 | num_secondary_bodies, 23 | data_table, 24 | system_mass, 25 | plx, 26 | mass_err=mass_err, 27 | plx_err=plx_err, 28 | ) 29 | test_system.convert_data_table_radec2seppa() 30 | 31 | 32 | def test_multi_planets(): 33 | """ 34 | Test using some data for HR 8799 b and c 35 | """ 36 | num_secondary_bodies = 2 37 | input_file = os.path.join(orbitize.DATADIR, "test_val_multi.csv") 38 | data_table = read_input.read_file(input_file) 39 | system_mass = 1.47 40 | plx = 24.30 41 | mass_err = 0.11 42 | plx_err = 0.7 43 | # Initialize System object, use mjd=50000 to be consistent with Wang+2018 44 | test_system = system.System( 45 | num_secondary_bodies, 46 | data_table, 47 | system_mass, 48 | plx, 49 | mass_err=mass_err, 50 | plx_err=plx_err, 51 | tau_ref_epoch=50000, 52 | ) 53 | 54 | params = np.array( 55 | [ 56 | 7.2774010e01, 57 | 4.1116819e-02, 58 | 5.6322372e-01, 59 | 3.5251172e00, 60 | 4.2904768e00, 61 | 9.4234377e-02, 62 | 4.5418411e01, 63 | 1.4317369e-03, 64 | 5.6322372e-01, 65 | 3.1016846e00, 66 | 4.2904768e00, 67 | 3.4033456e-01, 68 | 2.4589758e01, 69 | 1.4849439e00, 70 | ] 71 | ) 72 | 73 | result = test_system.compute_model(params) 74 | 75 | print(result) 76 | 77 | 78 | def test_radec2seppa(): 79 | """ 80 | Tests basic functionality of the convenience function 81 | orbitize.system.radec2seppa for converting measurements in RA/Dec to 82 | separation/position angle. 83 | """ 84 | 85 | ras = np.array([-1, -1, 1, 1]) 86 | decs = np.array([-1, 1, -1, 1]) 87 | 88 | pas_expected = np.array([225.0, 315.0, 135.0, 45.0]) 89 | pas_expected_180mod = np.array([225.0, 315.0, 495.0, 405.0]) 90 | seps_expected = np.ones(4) * np.sqrt(2) 91 | 92 | sep_nomod, pa_nomod = system.radec2seppa(ras, decs) 93 | sep_180mod, pa_180mod = system.radec2seppa(ras, decs, mod180=True) 94 | 95 | assert sep_nomod == pytest.approx(seps_expected, abs=1e-3) 96 | assert sep_180mod == pytest.approx(seps_expected, abs=1e-3) 97 | assert pa_nomod == pytest.approx(pas_expected, abs=1e-3) 98 | assert pa_180mod == pytest.approx(pas_expected_180mod, abs=1e-3) 99 | 100 | 101 | if __name__ == "__main__": 102 | test_convert_data_table_radec2seppa() 103 | test_radec2seppa() 104 | test_multi_planets() 105 | --------------------------------------------------------------------------------