├── .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 |

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 | 
8 | [](https://coveralls.io/github/sblunt/orbitize?branch=main)
9 | [](http://orbitize.readthedocs.io/en/latest/?badge=latest)
10 | [](https://badge.fury.io/py/orbitize)
11 | [](https://pypistats.org/packages/orbitize)
12 |
13 | [](https://doi.org/10.5281/zenodo.5432124)
14 | [](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 |
--------------------------------------------------------------------------------