Spaces:
Sleeping
Sleeping
Upload 350 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +5 -0
- Dockerfile +18 -0
- README.md +27 -5
- app.py +45 -0
- gala/mcp_output/README_MCP.md +45 -0
- gala/mcp_output/analysis.json +670 -0
- gala/mcp_output/diff_report.md +61 -0
- gala/mcp_output/mcp_plugin/__init__.py +0 -0
- gala/mcp_output/mcp_plugin/adapter.py +142 -0
- gala/mcp_output/mcp_plugin/main.py +13 -0
- gala/mcp_output/mcp_plugin/mcp_service.py +51 -0
- gala/mcp_output/requirements.txt +9 -0
- gala/mcp_output/start_mcp.py +30 -0
- gala/mcp_output/workflow_summary.json +201 -0
- gala/source/.clang-format +1 -0
- gala/source/.pre-commit-config.yaml +65 -0
- gala/source/.prettierrc.toml +1 -0
- gala/source/.readthedocs.yml +28 -0
- gala/source/AUTHORS.rst +22 -0
- gala/source/CHANGES.rst +725 -0
- gala/source/CODE_OF_CONDUCT.md +2 -0
- gala/source/LICENSE +21 -0
- gala/source/README.rst +98 -0
- gala/source/__init__.py +4 -0
- gala/source/codemeta.json +23 -0
- gala/source/conftest.py +41 -0
- gala/source/docs/Makefile +149 -0
- gala/source/docs/_static/Gala_Logo_RGB.png +0 -0
- gala/source/docs/_static/anim-prof.mp4 +3 -0
- gala/source/docs/_static/gala.css +70 -0
- gala/source/docs/_static/m104.ico +0 -0
- gala/source/docs/_static/orbit-anim1.mp4 +3 -0
- gala/source/docs/_static/orbit-anim2.mp4 +3 -0
- gala/source/docs/_static_animations.py +31 -0
- gala/source/docs/_templates/autosummary/base.rst +10 -0
- gala/source/docs/_templates/autosummary/class.rst +65 -0
- gala/source/docs/_templates/autosummary/module.rst +41 -0
- gala/source/docs/conf.py +356 -0
- gala/source/docs/contributing.rst +19 -0
- gala/source/docs/conventions.rst +49 -0
- gala/source/docs/coordinates/greatcircle.rst +184 -0
- gala/source/docs/coordinates/index.rst +241 -0
- gala/source/docs/docs.rst +16 -0
- gala/source/docs/dynamics/actionangle.rst +501 -0
- gala/source/docs/dynamics/index.rst +172 -0
- gala/source/docs/dynamics/mockstreams.rst +313 -0
- gala/source/docs/dynamics/nbody.rst +184 -0
- gala/source/docs/dynamics/nd-representations.rst +69 -0
- gala/source/docs/dynamics/nonlinear.rst +164 -0
- gala/source/docs/dynamics/orbits-in-detail.rst +343 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
gala/source/docs/_static/anim-prof.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
gala/source/docs/_static/orbit-anim1.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
gala/source/docs/_static/orbit-anim2.mp4 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
gala/source/docs/tutorials/data/m12m.cache filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
gala/source/tests/potential/potential/EXP-Hernquist.cache filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10
|
| 2 |
+
|
| 3 |
+
RUN useradd -m -u 1000 user && python -m pip install --upgrade pip
|
| 4 |
+
USER user
|
| 5 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 6 |
+
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 10 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 11 |
+
|
| 12 |
+
COPY --chown=user . /app
|
| 13 |
+
ENV MCP_TRANSPORT=http
|
| 14 |
+
ENV MCP_PORT=7860
|
| 15 |
+
|
| 16 |
+
EXPOSE 7860
|
| 17 |
+
|
| 18 |
+
CMD ["python", "gala/mcp_output/start_mcp.py"]
|
README.md
CHANGED
|
@@ -1,10 +1,32 @@
|
|
| 1 |
---
|
| 2 |
-
title: Gala
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
|
|
|
|
|
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Gala MCP
|
| 3 |
+
emoji: 🤖
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: purple
|
| 6 |
sdk: docker
|
| 7 |
+
sdk_version: "4.26.0"
|
| 8 |
+
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Gala MCP Service
|
| 13 |
+
|
| 14 |
+
Auto-generated MCP service for gala.
|
| 15 |
+
|
| 16 |
+
## Usage
|
| 17 |
+
|
| 18 |
+
```
|
| 19 |
+
https://None-gala-mcp.hf.space/mcp
|
| 20 |
+
```
|
| 21 |
+
|
| 22 |
+
## Connect with Cursor
|
| 23 |
+
|
| 24 |
+
```json
|
| 25 |
+
{
|
| 26 |
+
"mcpServers": {
|
| 27 |
+
"gala": {
|
| 28 |
+
"url": "https://None-gala-mcp.hf.space/mcp"
|
| 29 |
+
}
|
| 30 |
+
}
|
| 31 |
+
}
|
| 32 |
+
```
|
app.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
import os
|
| 3 |
+
import sys
|
| 4 |
+
|
| 5 |
+
mcp_plugin_path = os.path.join(os.path.dirname(__file__), "gala", "mcp_output", "mcp_plugin")
|
| 6 |
+
sys.path.insert(0, mcp_plugin_path)
|
| 7 |
+
|
| 8 |
+
app = FastAPI(
|
| 9 |
+
title="Gala MCP Service",
|
| 10 |
+
description="Auto-generated MCP service for gala",
|
| 11 |
+
version="1.0.0"
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
@app.get("/")
|
| 15 |
+
def root():
|
| 16 |
+
return {
|
| 17 |
+
"service": "Gala MCP Service",
|
| 18 |
+
"version": "1.0.0",
|
| 19 |
+
"status": "running",
|
| 20 |
+
"transport": os.environ.get("MCP_TRANSPORT", "http")
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
@app.get("/health")
|
| 24 |
+
def health_check():
|
| 25 |
+
return {"status": "healthy", "service": "gala MCP"}
|
| 26 |
+
|
| 27 |
+
@app.get("/tools")
|
| 28 |
+
def list_tools():
|
| 29 |
+
try:
|
| 30 |
+
from mcp_service import create_app
|
| 31 |
+
mcp_app = create_app()
|
| 32 |
+
tools = []
|
| 33 |
+
for tool_name, tool_func in mcp_app.tools.items():
|
| 34 |
+
tools.append({
|
| 35 |
+
"name": tool_name,
|
| 36 |
+
"description": tool_func.__doc__ or "No description available"
|
| 37 |
+
})
|
| 38 |
+
return {"tools": tools}
|
| 39 |
+
except Exception as e:
|
| 40 |
+
return {"error": f"Failed to load tools: {str(e)}"}
|
| 41 |
+
|
| 42 |
+
if __name__ == "__main__":
|
| 43 |
+
import uvicorn
|
| 44 |
+
port = int(os.environ.get("PORT", 7860))
|
| 45 |
+
uvicorn.run(app, host="0.0.0.0", port=port)
|
gala/mcp_output/README_MCP.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Gala: Galactic and Gravitational Dynamics in Python
|
| 2 |
+
|
| 3 |
+
## Project Introduction
|
| 4 |
+
|
| 5 |
+
Gala is a Python library designed for galactic and gravitational dynamics. It provides tools for handling stream coordinates, core dynamics functions, and potential functions. The library is structured to facilitate the study and simulation of astrophysical systems, offering a comprehensive suite of functions and classes for researchers and developers in the field.
|
| 6 |
+
|
| 7 |
+
## Installation Method
|
| 8 |
+
|
| 9 |
+
To install Gala, ensure you have Python installed along with the following required dependencies: `numpy`, `scipy`, and `astropy`. Optionally, you can install `matplotlib` for plotting capabilities.
|
| 10 |
+
|
| 11 |
+
You can install Gala using pip:
|
| 12 |
+
|
| 13 |
+
```
|
| 14 |
+
pip install gala
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
## Quick Start
|
| 18 |
+
|
| 19 |
+
Here's a quick example of how to use Gala's main functions:
|
| 20 |
+
|
| 21 |
+
1. Import the necessary modules:
|
| 22 |
+
|
| 23 |
+
```
|
| 24 |
+
from gala.coordinates import gd1
|
| 25 |
+
from gala.dynamics import core
|
| 26 |
+
from gala.potential import builtin
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
2. Use the functions and classes provided by these modules to perform your desired calculations. For example, you can handle GD1 stream coordinates, utilize core dynamics functions, or work with potential functions.
|
| 30 |
+
|
| 31 |
+
## Available Tools and Endpoints List
|
| 32 |
+
|
| 33 |
+
- **GD1 Stream Coordinates**: Module for handling GD1 stream coordinates.
|
| 34 |
+
- **Core Dynamics Functions**: Provides core functions and classes for dynamics calculations.
|
| 35 |
+
- **Core Potential Functions**: Offers core functions and classes for potential calculations.
|
| 36 |
+
|
| 37 |
+
## Common Issues and Notes
|
| 38 |
+
|
| 39 |
+
- **Dependencies**: Ensure all required dependencies (`numpy`, `scipy`, `astropy`) are installed. Optional dependency `matplotlib` is recommended for visualization.
|
| 40 |
+
- **Environment**: It is advisable to use a virtual environment to manage dependencies and avoid conflicts.
|
| 41 |
+
- **Performance**: For optimal performance, ensure your Python environment is up-to-date and consider using optimized numerical libraries.
|
| 42 |
+
|
| 43 |
+
## Reference Links or Documentation
|
| 44 |
+
|
| 45 |
+
For more detailed information, visit the [Gala GitHub Repository](https://github.com/adrn/gala). Here you can find comprehensive documentation, tutorials, and additional resources to help you get the most out of Gala.
|
gala/mcp_output/analysis.json
ADDED
|
@@ -0,0 +1,670 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"summary": {
|
| 3 |
+
"repository_url": "https://github.com/adrn/gala",
|
| 4 |
+
"summary": "Imported via zip fallback, file count: 187",
|
| 5 |
+
"file_tree": {
|
| 6 |
+
".github/actions/build-exp/action.yml": {
|
| 7 |
+
"size": 2168
|
| 8 |
+
},
|
| 9 |
+
".github/dependabot.yml": {
|
| 10 |
+
"size": 118
|
| 11 |
+
},
|
| 12 |
+
".github/pull_request_template.md": {
|
| 13 |
+
"size": 279
|
| 14 |
+
},
|
| 15 |
+
".github/workflows/benchmarks.yml": {
|
| 16 |
+
"size": 3185
|
| 17 |
+
},
|
| 18 |
+
".github/workflows/tests.yml": {
|
| 19 |
+
"size": 7580
|
| 20 |
+
},
|
| 21 |
+
".github/workflows/tutorials.yml": {
|
| 22 |
+
"size": 2038
|
| 23 |
+
},
|
| 24 |
+
".github/workflows/wheels.yml": {
|
| 25 |
+
"size": 3533
|
| 26 |
+
},
|
| 27 |
+
".pre-commit-config.yaml": {
|
| 28 |
+
"size": 1807
|
| 29 |
+
},
|
| 30 |
+
".prettierrc.toml": {
|
| 31 |
+
"size": 16
|
| 32 |
+
},
|
| 33 |
+
".readthedocs.yml": {
|
| 34 |
+
"size": 545
|
| 35 |
+
},
|
| 36 |
+
"CODE_OF_CONDUCT.md": {
|
| 37 |
+
"size": 138
|
| 38 |
+
},
|
| 39 |
+
"codemeta.json": {
|
| 40 |
+
"size": 1411
|
| 41 |
+
},
|
| 42 |
+
"conftest.py": {
|
| 43 |
+
"size": 1007
|
| 44 |
+
},
|
| 45 |
+
"docs/_static_animations.py": {
|
| 46 |
+
"size": 924
|
| 47 |
+
},
|
| 48 |
+
"docs/conf.py": {
|
| 49 |
+
"size": 10780
|
| 50 |
+
},
|
| 51 |
+
"docs/dynamics/references.txt": {
|
| 52 |
+
"size": 258
|
| 53 |
+
},
|
| 54 |
+
"docs/references.txt": {
|
| 55 |
+
"size": 283
|
| 56 |
+
},
|
| 57 |
+
"docs/supporting/data/Eilers2019-circ-velocity.txt": {
|
| 58 |
+
"size": 888
|
| 59 |
+
},
|
| 60 |
+
"docs/supporting/define-milky-way-model.py": {
|
| 61 |
+
"size": 11944
|
| 62 |
+
},
|
| 63 |
+
"docs/tutorials/Arbitrary-density-SCF.py": {
|
| 64 |
+
"size": 8116
|
| 65 |
+
},
|
| 66 |
+
"docs/tutorials/Milky-Way-model.py": {
|
| 67 |
+
"size": 6344
|
| 68 |
+
},
|
| 69 |
+
"docs/tutorials/data/m12m-basis.yml": {
|
| 70 |
+
"size": 151
|
| 71 |
+
},
|
| 72 |
+
"docs/tutorials/integrate-barred-potential.py": {
|
| 73 |
+
"size": 11556
|
| 74 |
+
},
|
| 75 |
+
"docs/tutorials/pyia-gala-orbit.py": {
|
| 76 |
+
"size": 8636
|
| 77 |
+
},
|
| 78 |
+
"docs/tutorials/spherical-spline-tutorial.py": {
|
| 79 |
+
"size": 13882
|
| 80 |
+
},
|
| 81 |
+
"docs/tutorials/stream-mass-loss.py": {
|
| 82 |
+
"size": 8985
|
| 83 |
+
},
|
| 84 |
+
"docs/tutorials/time-evolving-potential.py": {
|
| 85 |
+
"size": 12151
|
| 86 |
+
},
|
| 87 |
+
"docs/tutorials/v1_11_new_features.py": {
|
| 88 |
+
"size": 11698
|
| 89 |
+
},
|
| 90 |
+
"paper/paper.md": {
|
| 91 |
+
"size": 2603
|
| 92 |
+
},
|
| 93 |
+
"pyproject.toml": {
|
| 94 |
+
"size": 6060
|
| 95 |
+
},
|
| 96 |
+
"setup.py": {
|
| 97 |
+
"size": 17809
|
| 98 |
+
},
|
| 99 |
+
"src/gala/__init__.py": {
|
| 100 |
+
"size": 115
|
| 101 |
+
},
|
| 102 |
+
"src/gala/_compat_utils.py": {
|
| 103 |
+
"size": 295
|
| 104 |
+
},
|
| 105 |
+
"src/gala/_optional_deps.py": {
|
| 106 |
+
"size": 1326
|
| 107 |
+
},
|
| 108 |
+
"src/gala/coordinates/__init__.py": {
|
| 109 |
+
"size": 326
|
| 110 |
+
},
|
| 111 |
+
"src/gala/coordinates/gd1.py": {
|
| 112 |
+
"size": 3634
|
| 113 |
+
},
|
| 114 |
+
"src/gala/coordinates/greatcircle.py": {
|
| 115 |
+
"size": 17915
|
| 116 |
+
},
|
| 117 |
+
"src/gala/coordinates/helpers.py": {
|
| 118 |
+
"size": 1610
|
| 119 |
+
},
|
| 120 |
+
"src/gala/coordinates/jhelum.py": {
|
| 121 |
+
"size": 3551
|
| 122 |
+
},
|
| 123 |
+
"src/gala/coordinates/magellanic_stream.py": {
|
| 124 |
+
"size": 3105
|
| 125 |
+
},
|
| 126 |
+
"src/gala/coordinates/oph.py": {
|
| 127 |
+
"size": 3710
|
| 128 |
+
},
|
| 129 |
+
"src/gala/coordinates/orphan.py": {
|
| 130 |
+
"size": 6635
|
| 131 |
+
},
|
| 132 |
+
"src/gala/coordinates/pal13.py": {
|
| 133 |
+
"size": 3592
|
| 134 |
+
},
|
| 135 |
+
"src/gala/coordinates/pal5.py": {
|
| 136 |
+
"size": 3891
|
| 137 |
+
},
|
| 138 |
+
"src/gala/coordinates/pm_cov_transform.py": {
|
| 139 |
+
"size": 4435
|
| 140 |
+
},
|
| 141 |
+
"src/gala/coordinates/poincarepolar.py": {
|
| 142 |
+
"size": 1316
|
| 143 |
+
},
|
| 144 |
+
"src/gala/coordinates/reflex.py": {
|
| 145 |
+
"size": 1661
|
| 146 |
+
},
|
| 147 |
+
"src/gala/coordinates/sgr.py": {
|
| 148 |
+
"size": 7378
|
| 149 |
+
},
|
| 150 |
+
"src/gala/coordinates/velocity_frame_transforms.py": {
|
| 151 |
+
"size": 2288
|
| 152 |
+
},
|
| 153 |
+
"src/gala/dynamics/__init__.py": {
|
| 154 |
+
"size": 234
|
| 155 |
+
},
|
| 156 |
+
"src/gala/dynamics/actionangle/__init__.py": {
|
| 157 |
+
"size": 103
|
| 158 |
+
},
|
| 159 |
+
"src/gala/dynamics/actionangle/actionangle_o2gf.py": {
|
| 160 |
+
"size": 23921
|
| 161 |
+
},
|
| 162 |
+
"src/gala/dynamics/actionangle/actionangle_staeckel.py": {
|
| 163 |
+
"size": 3154
|
| 164 |
+
},
|
| 165 |
+
"src/gala/dynamics/actionangle/analyticactionangle.py": {
|
| 166 |
+
"size": 11871
|
| 167 |
+
},
|
| 168 |
+
"src/gala/dynamics/core.py": {
|
| 169 |
+
"size": 32983
|
| 170 |
+
},
|
| 171 |
+
"src/gala/dynamics/lyapunov/__init__.py": {
|
| 172 |
+
"size": 80
|
| 173 |
+
},
|
| 174 |
+
"src/gala/dynamics/mockstream/__init__.py": {
|
| 175 |
+
"size": 117
|
| 176 |
+
},
|
| 177 |
+
"src/gala/dynamics/mockstream/core.py": {
|
| 178 |
+
"size": 5509
|
| 179 |
+
},
|
| 180 |
+
"src/gala/dynamics/mockstream/mockstream_generator.py": {
|
| 181 |
+
"size": 15603
|
| 182 |
+
},
|
| 183 |
+
"src/gala/dynamics/nbody/__init__.py": {
|
| 184 |
+
"size": 30
|
| 185 |
+
},
|
| 186 |
+
"src/gala/dynamics/nbody/core.py": {
|
| 187 |
+
"size": 10060
|
| 188 |
+
},
|
| 189 |
+
"src/gala/dynamics/nonlinear.py": {
|
| 190 |
+
"size": 10325
|
| 191 |
+
},
|
| 192 |
+
"src/gala/dynamics/orbit.py": {
|
| 193 |
+
"size": 48470
|
| 194 |
+
},
|
| 195 |
+
"src/gala/dynamics/plot.py": {
|
| 196 |
+
"size": 4619
|
| 197 |
+
},
|
| 198 |
+
"src/gala/dynamics/representation_nd.py": {
|
| 199 |
+
"size": 8785
|
| 200 |
+
},
|
| 201 |
+
"src/gala/dynamics/util.py": {
|
| 202 |
+
"size": 11912
|
| 203 |
+
},
|
| 204 |
+
"src/gala/integrate/__init__.py": {
|
| 205 |
+
"size": 75
|
| 206 |
+
},
|
| 207 |
+
"src/gala/integrate/core.py": {
|
| 208 |
+
"size": 6343
|
| 209 |
+
},
|
| 210 |
+
"src/gala/integrate/cyintegrators/__init__.py": {
|
| 211 |
+
"size": 149
|
| 212 |
+
},
|
| 213 |
+
"src/gala/integrate/cyintegrators/dopri/__init__.py": {
|
| 214 |
+
"size": 0
|
| 215 |
+
},
|
| 216 |
+
"src/gala/integrate/cyintegrators/dopri/licence.txt": {
|
| 217 |
+
"size": 1258
|
| 218 |
+
},
|
| 219 |
+
"src/gala/integrate/lookup.py": {
|
| 220 |
+
"size": 2646
|
| 221 |
+
},
|
| 222 |
+
"src/gala/integrate/pyintegrators/__init__.py": {
|
| 223 |
+
"size": 148
|
| 224 |
+
},
|
| 225 |
+
"src/gala/integrate/pyintegrators/dopri853.py": {
|
| 226 |
+
"size": 5726
|
| 227 |
+
},
|
| 228 |
+
"src/gala/integrate/pyintegrators/leapfrog.py": {
|
| 229 |
+
"size": 6603
|
| 230 |
+
},
|
| 231 |
+
"src/gala/integrate/pyintegrators/rk5.py": {
|
| 232 |
+
"size": 6177
|
| 233 |
+
},
|
| 234 |
+
"src/gala/integrate/pyintegrators/ruth4.py": {
|
| 235 |
+
"size": 4903
|
| 236 |
+
},
|
| 237 |
+
"src/gala/integrate/timespec.py": {
|
| 238 |
+
"size": 4550
|
| 239 |
+
},
|
| 240 |
+
"src/gala/io.py": {
|
| 241 |
+
"size": 1176
|
| 242 |
+
},
|
| 243 |
+
"src/gala/logging.py": {
|
| 244 |
+
"size": 946
|
| 245 |
+
},
|
| 246 |
+
"src/gala/potential/__init__.py": {
|
| 247 |
+
"size": 142
|
| 248 |
+
},
|
| 249 |
+
"src/gala/potential/common.py": {
|
| 250 |
+
"size": 11179
|
| 251 |
+
},
|
| 252 |
+
"src/gala/potential/frame/__init__.py": {
|
| 253 |
+
"size": 82
|
| 254 |
+
},
|
| 255 |
+
"src/gala/potential/frame/builtin/__init__.py": {
|
| 256 |
+
"size": 55
|
| 257 |
+
},
|
| 258 |
+
"src/gala/potential/frame/builtin/transformations.py": {
|
| 259 |
+
"size": 7285
|
| 260 |
+
},
|
| 261 |
+
"src/gala/potential/frame/core.py": {
|
| 262 |
+
"size": 692
|
| 263 |
+
},
|
| 264 |
+
"src/gala/potential/hamiltonian/__init__.py": {
|
| 265 |
+
"size": 28
|
| 266 |
+
},
|
| 267 |
+
"src/gala/potential/potential/__init__.py": {
|
| 268 |
+
"size": 542
|
| 269 |
+
},
|
| 270 |
+
"src/gala/potential/potential/builtin/__init__.py": {
|
| 271 |
+
"size": 68
|
| 272 |
+
},
|
| 273 |
+
"src/gala/potential/potential/builtin/core.py": {
|
| 274 |
+
"size": 59261
|
| 275 |
+
},
|
| 276 |
+
"src/gala/potential/potential/builtin/pybuiltin.py": {
|
| 277 |
+
"size": 3319
|
| 278 |
+
},
|
| 279 |
+
"src/gala/potential/potential/builtin/special.py": {
|
| 280 |
+
"size": 13787
|
| 281 |
+
},
|
| 282 |
+
"src/gala/potential/potential/builtin/time_interpolated.py": {
|
| 283 |
+
"size": 16390
|
| 284 |
+
},
|
| 285 |
+
"src/gala/potential/potential/core.py": {
|
| 286 |
+
"size": 55457
|
| 287 |
+
},
|
| 288 |
+
"src/gala/potential/potential/interop.py": {
|
| 289 |
+
"size": 15067
|
| 290 |
+
},
|
| 291 |
+
"src/gala/potential/potential/io.py": {
|
| 292 |
+
"size": 10811
|
| 293 |
+
},
|
| 294 |
+
"src/gala/potential/potential/symmetry.py": {
|
| 295 |
+
"size": 9532
|
| 296 |
+
},
|
| 297 |
+
"src/gala/potential/potential/util.py": {
|
| 298 |
+
"size": 7088
|
| 299 |
+
},
|
| 300 |
+
"src/gala/potential/scf/__init__.py": {
|
| 301 |
+
"size": 198
|
| 302 |
+
},
|
| 303 |
+
"src/gala/potential/scf/core.py": {
|
| 304 |
+
"size": 8733
|
| 305 |
+
},
|
| 306 |
+
"src/gala/units.py": {
|
| 307 |
+
"size": 12239
|
| 308 |
+
},
|
| 309 |
+
"src/gala/util.py": {
|
| 310 |
+
"size": 4310
|
| 311 |
+
},
|
| 312 |
+
"tests/benchmarks/test_integrate_benchmark.py": {
|
| 313 |
+
"size": 1163
|
| 314 |
+
},
|
| 315 |
+
"tests/benchmarks/test_mockstream_benchmark.py": {
|
| 316 |
+
"size": 4980
|
| 317 |
+
},
|
| 318 |
+
"tests/benchmarks/test_potentials_benchmark.py": {
|
| 319 |
+
"size": 3890
|
| 320 |
+
},
|
| 321 |
+
"tests/coordinates/gd1_coord.txt": {
|
| 322 |
+
"size": 868
|
| 323 |
+
},
|
| 324 |
+
"tests/coordinates/idl_vgsr_vhel.txt": {
|
| 325 |
+
"size": 12419
|
| 326 |
+
},
|
| 327 |
+
"tests/coordinates/sergey_orphan.txt": {
|
| 328 |
+
"size": 10109
|
| 329 |
+
},
|
| 330 |
+
"tests/coordinates/test_all_streamframes.py": {
|
| 331 |
+
"size": 1069
|
| 332 |
+
},
|
| 333 |
+
"tests/coordinates/test_gd1.py": {
|
| 334 |
+
"size": 1735
|
| 335 |
+
},
|
| 336 |
+
"tests/coordinates/test_greatcircle.py": {
|
| 337 |
+
"size": 7929
|
| 338 |
+
},
|
| 339 |
+
"tests/coordinates/test_jhelum.py": {
|
| 340 |
+
"size": 956
|
| 341 |
+
},
|
| 342 |
+
"tests/coordinates/test_orphan.py": {
|
| 343 |
+
"size": 1560
|
| 344 |
+
},
|
| 345 |
+
"tests/coordinates/test_pal5.py": {
|
| 346 |
+
"size": 974
|
| 347 |
+
},
|
| 348 |
+
"tests/coordinates/test_pm_cov_transform.py": {
|
| 349 |
+
"size": 2938
|
| 350 |
+
},
|
| 351 |
+
"tests/coordinates/test_reflex.py": {
|
| 352 |
+
"size": 1771
|
| 353 |
+
},
|
| 354 |
+
"tests/coordinates/test_sgr.py": {
|
| 355 |
+
"size": 2292
|
| 356 |
+
},
|
| 357 |
+
"tests/coordinates/test_velocity_frame_transforms.py": {
|
| 358 |
+
"size": 3193
|
| 359 |
+
},
|
| 360 |
+
"tests/dynamics/actionangle/_genfunc/__init__.py": {
|
| 361 |
+
"size": 0
|
| 362 |
+
},
|
| 363 |
+
"tests/dynamics/actionangle/_genfunc/genfunc_3d.py": {
|
| 364 |
+
"size": 16639
|
| 365 |
+
},
|
| 366 |
+
"tests/dynamics/actionangle/_genfunc/solver.py": {
|
| 367 |
+
"size": 6039
|
| 368 |
+
},
|
| 369 |
+
"tests/dynamics/actionangle/_genfunc/test_potentials.py": {
|
| 370 |
+
"size": 9660
|
| 371 |
+
},
|
| 372 |
+
"tests/dynamics/actionangle/_genfunc/toy_potentials.py": {
|
| 373 |
+
"size": 5430
|
| 374 |
+
},
|
| 375 |
+
"tests/dynamics/actionangle/_genfunc/visualize_surfaces.py": {
|
| 376 |
+
"size": 2977
|
| 377 |
+
},
|
| 378 |
+
"tests/dynamics/actionangle/actionangle_helpers.py": {
|
| 379 |
+
"size": 4709
|
| 380 |
+
},
|
| 381 |
+
"tests/dynamics/actionangle/staeckel_helpers.py": {
|
| 382 |
+
"size": 2978
|
| 383 |
+
},
|
| 384 |
+
"tests/dynamics/actionangle/test_actionangle_o2gf.py": {
|
| 385 |
+
"size": 8465
|
| 386 |
+
},
|
| 387 |
+
"tests/dynamics/actionangle/test_actionangle_staeckel.py": {
|
| 388 |
+
"size": 5114
|
| 389 |
+
},
|
| 390 |
+
"tests/dynamics/actionangle/test_analyticactionangle.py": {
|
| 391 |
+
"size": 6117
|
| 392 |
+
},
|
| 393 |
+
"tests/dynamics/mockstream/test_coord.py": {
|
| 394 |
+
"size": 593
|
| 395 |
+
},
|
| 396 |
+
"tests/dynamics/mockstream/test_df.py": {
|
| 397 |
+
"size": 4083
|
| 398 |
+
},
|
| 399 |
+
"tests/dynamics/mockstream/test_mockstream.py": {
|
| 400 |
+
"size": 27377
|
| 401 |
+
},
|
| 402 |
+
"tests/dynamics/mockstream/test_mockstream_class.py": {
|
| 403 |
+
"size": 4377
|
| 404 |
+
},
|
| 405 |
+
"tests/dynamics/nbody/test_nbody.py": {
|
| 406 |
+
"size": 9203
|
| 407 |
+
},
|
| 408 |
+
"tests/dynamics/test_dynamics_core.py": {
|
| 409 |
+
"size": 14198
|
| 410 |
+
},
|
| 411 |
+
"tests/dynamics/test_dynamics_util.py": {
|
| 412 |
+
"size": 3802
|
| 413 |
+
},
|
| 414 |
+
"tests/dynamics/test_nonlinear.py": {
|
| 415 |
+
"size": 10282
|
| 416 |
+
},
|
| 417 |
+
"tests/dynamics/test_orbit.py": {
|
| 418 |
+
"size": 20176
|
| 419 |
+
},
|
| 420 |
+
"tests/dynamics/test_plot.py": {
|
| 421 |
+
"size": 4553
|
| 422 |
+
},
|
| 423 |
+
"tests/dynamics/test_representation_nd.py": {
|
| 424 |
+
"size": 1537
|
| 425 |
+
},
|
| 426 |
+
"tests/integrate/__init__.py": {
|
| 427 |
+
"size": 0
|
| 428 |
+
},
|
| 429 |
+
"tests/integrate/test_cyintegrators.py": {
|
| 430 |
+
"size": 4477
|
| 431 |
+
},
|
| 432 |
+
"tests/integrate/test_pyintegrators.py": {
|
| 433 |
+
"size": 4395
|
| 434 |
+
},
|
| 435 |
+
"tests/integrate/test_timespec.py": {
|
| 436 |
+
"size": 2313
|
| 437 |
+
},
|
| 438 |
+
"tests/integration/README.md": {
|
| 439 |
+
"size": 178
|
| 440 |
+
},
|
| 441 |
+
"tests/integration/test_bar_rotating_frame.py": {
|
| 442 |
+
"size": 7655
|
| 443 |
+
},
|
| 444 |
+
"tests/potential/frame/test_builtin.py": {
|
| 445 |
+
"size": 2600
|
| 446 |
+
},
|
| 447 |
+
"tests/potential/frame/test_transformations.py": {
|
| 448 |
+
"size": 3544
|
| 449 |
+
},
|
| 450 |
+
"tests/potential/hamiltonian/hamiltonian_helpers.py": {
|
| 451 |
+
"size": 6076
|
| 452 |
+
},
|
| 453 |
+
"tests/potential/hamiltonian/test_hamiltonian.py": {
|
| 454 |
+
"size": 1935
|
| 455 |
+
},
|
| 456 |
+
"tests/potential/hamiltonian/test_with_frame_potential.py": {
|
| 457 |
+
"size": 6767
|
| 458 |
+
},
|
| 459 |
+
"tests/potential/potential/Composite.yml": {
|
| 460 |
+
"size": 531
|
| 461 |
+
},
|
| 462 |
+
"tests/potential/potential/EXP-Hernquist-basis.yml": {
|
| 463 |
+
"size": 163
|
| 464 |
+
},
|
| 465 |
+
"tests/potential/potential/EXP-field-basis.yml": {
|
| 466 |
+
"size": 85
|
| 467 |
+
},
|
| 468 |
+
"tests/potential/potential/HarmonicOscillator1D.yml": {
|
| 469 |
+
"size": 60
|
| 470 |
+
},
|
| 471 |
+
"tests/potential/potential/Plummer.yml": {
|
| 472 |
+
"size": 184
|
| 473 |
+
},
|
| 474 |
+
"tests/potential/potential/ccomposite.yml": {
|
| 475 |
+
"size": 690
|
| 476 |
+
},
|
| 477 |
+
"tests/potential/potential/exp_basis.yml": {
|
| 478 |
+
"size": 157
|
| 479 |
+
},
|
| 480 |
+
"tests/potential/potential/generate_agama.py": {
|
| 481 |
+
"size": 983
|
| 482 |
+
},
|
| 483 |
+
"tests/potential/potential/generate_exp.py": {
|
| 484 |
+
"size": 7015
|
| 485 |
+
},
|
| 486 |
+
"tests/potential/potential/lm10.yml": {
|
| 487 |
+
"size": 1284
|
| 488 |
+
},
|
| 489 |
+
"tests/potential/potential/potential_helpers.py": {
|
| 490 |
+
"size": 20993
|
| 491 |
+
},
|
| 492 |
+
"tests/potential/potential/test_all_builtin.py": {
|
| 493 |
+
"size": 21079
|
| 494 |
+
},
|
| 495 |
+
"tests/potential/potential/test_composite.py": {
|
| 496 |
+
"size": 6171
|
| 497 |
+
},
|
| 498 |
+
"tests/potential/potential/test_cpotential.py": {
|
| 499 |
+
"size": 621
|
| 500 |
+
},
|
| 501 |
+
"tests/potential/potential/test_exp.py": {
|
| 502 |
+
"size": 21742
|
| 503 |
+
},
|
| 504 |
+
"tests/potential/potential/test_interop_agama.py": {
|
| 505 |
+
"size": 3457
|
| 506 |
+
},
|
| 507 |
+
"tests/potential/potential/test_interop_galpy.py": {
|
| 508 |
+
"size": 6895
|
| 509 |
+
},
|
| 510 |
+
"tests/potential/potential/test_io.py": {
|
| 511 |
+
"size": 4199
|
| 512 |
+
},
|
| 513 |
+
"tests/potential/potential/test_potential_core.py": {
|
| 514 |
+
"size": 5222
|
| 515 |
+
},
|
| 516 |
+
"tests/potential/potential/test_potential_util.py": {
|
| 517 |
+
"size": 1769
|
| 518 |
+
},
|
| 519 |
+
"tests/potential/potential/test_special.py": {
|
| 520 |
+
"size": 1361
|
| 521 |
+
},
|
| 522 |
+
"tests/potential/potential/test_spherical_spline.py": {
|
| 523 |
+
"size": 5832
|
| 524 |
+
},
|
| 525 |
+
"tests/potential/potential/test_symmetry.py": {
|
| 526 |
+
"size": 24931
|
| 527 |
+
},
|
| 528 |
+
"tests/potential/potential/test_time_interpolated.py": {
|
| 529 |
+
"size": 17566
|
| 530 |
+
},
|
| 531 |
+
"tests/potential/scf/data/README.md": {
|
| 532 |
+
"size": 87
|
| 533 |
+
},
|
| 534 |
+
"tests/potential/scf/data/plummer_coeff_nmax10_lmax5.txt": {
|
| 535 |
+
"size": 19991
|
| 536 |
+
},
|
| 537 |
+
"tests/potential/scf/data/plummer_coeff_var_nmax10_lmax5.txt": {
|
| 538 |
+
"size": 29802
|
| 539 |
+
},
|
| 540 |
+
"tests/potential/scf/test_accp_fortran.py": {
|
| 541 |
+
"size": 4560
|
| 542 |
+
},
|
| 543 |
+
"tests/potential/scf/test_bfe.py": {
|
| 544 |
+
"size": 6941
|
| 545 |
+
},
|
| 546 |
+
"tests/potential/scf/test_bfe_interp.py": {
|
| 547 |
+
"size": 1435
|
| 548 |
+
},
|
| 549 |
+
"tests/potential/scf/test_class.py": {
|
| 550 |
+
"size": 3451
|
| 551 |
+
},
|
| 552 |
+
"tests/potential/scf/test_computecoeff.py": {
|
| 553 |
+
"size": 8489
|
| 554 |
+
},
|
| 555 |
+
"tests/potential/scf/test_computecoeff_discrete.py": {
|
| 556 |
+
"size": 2725
|
| 557 |
+
},
|
| 558 |
+
"tests/potential/scf/test_computecoeff_fortran.py": {
|
| 559 |
+
"size": 1562
|
| 560 |
+
},
|
| 561 |
+
"tests/regression/test_potential_timeinterpolated_539.py": {
|
| 562 |
+
"size": 734
|
| 563 |
+
},
|
| 564 |
+
"tests/test_units.py": {
|
| 565 |
+
"size": 3085
|
| 566 |
+
}
|
| 567 |
+
},
|
| 568 |
+
"processed_by": "zip_fallback",
|
| 569 |
+
"success": true
|
| 570 |
+
},
|
| 571 |
+
"structure": {
|
| 572 |
+
"packages": [
|
| 573 |
+
"source.src.gala",
|
| 574 |
+
"source.tests.integrate"
|
| 575 |
+
]
|
| 576 |
+
},
|
| 577 |
+
"dependencies": {
|
| 578 |
+
"has_environment_yml": false,
|
| 579 |
+
"has_requirements_txt": false,
|
| 580 |
+
"pyproject": true,
|
| 581 |
+
"setup_cfg": false,
|
| 582 |
+
"setup_py": true
|
| 583 |
+
},
|
| 584 |
+
"entry_points": {
|
| 585 |
+
"imports": [],
|
| 586 |
+
"cli": [],
|
| 587 |
+
"modules": []
|
| 588 |
+
},
|
| 589 |
+
"llm_analysis": {
|
| 590 |
+
"core_modules": [
|
| 591 |
+
{
|
| 592 |
+
"package": "source.src.gala.coordinates",
|
| 593 |
+
"module": "gd1",
|
| 594 |
+
"functions": [
|
| 595 |
+
"function1",
|
| 596 |
+
"function2"
|
| 597 |
+
],
|
| 598 |
+
"classes": [
|
| 599 |
+
"Class1",
|
| 600 |
+
"Class2"
|
| 601 |
+
],
|
| 602 |
+
"description": "Module for handling GD1 stream coordinates."
|
| 603 |
+
},
|
| 604 |
+
{
|
| 605 |
+
"package": "source.src.gala.dynamics",
|
| 606 |
+
"module": "core",
|
| 607 |
+
"functions": [
|
| 608 |
+
"function1",
|
| 609 |
+
"function2"
|
| 610 |
+
],
|
| 611 |
+
"classes": [
|
| 612 |
+
"Class1",
|
| 613 |
+
"Class2"
|
| 614 |
+
],
|
| 615 |
+
"description": "Core dynamics functions and classes."
|
| 616 |
+
},
|
| 617 |
+
{
|
| 618 |
+
"package": "source.src.gala.potential",
|
| 619 |
+
"module": "builtin.core",
|
| 620 |
+
"functions": [
|
| 621 |
+
"function1",
|
| 622 |
+
"function2"
|
| 623 |
+
],
|
| 624 |
+
"classes": [
|
| 625 |
+
"Class1",
|
| 626 |
+
"Class2"
|
| 627 |
+
],
|
| 628 |
+
"description": "Core potential functions and classes."
|
| 629 |
+
}
|
| 630 |
+
],
|
| 631 |
+
"cli_commands": [],
|
| 632 |
+
"import_strategy": {
|
| 633 |
+
"primary": "import",
|
| 634 |
+
"fallback": "blackbox",
|
| 635 |
+
"confidence": 0.85
|
| 636 |
+
},
|
| 637 |
+
"dependencies": {
|
| 638 |
+
"required": [
|
| 639 |
+
"numpy",
|
| 640 |
+
"scipy",
|
| 641 |
+
"astropy"
|
| 642 |
+
],
|
| 643 |
+
"optional": [
|
| 644 |
+
"matplotlib"
|
| 645 |
+
]
|
| 646 |
+
},
|
| 647 |
+
"risk_assessment": {
|
| 648 |
+
"import_feasibility": 0.8,
|
| 649 |
+
"intrusiveness_risk": "medium",
|
| 650 |
+
"complexity": "medium"
|
| 651 |
+
}
|
| 652 |
+
},
|
| 653 |
+
"deepwiki_analysis": {
|
| 654 |
+
"repo_url": "https://github.com/adrn/gala",
|
| 655 |
+
"repo_name": "gala",
|
| 656 |
+
"content": "Galactic and gravitational dynamics in Python\nRepository Not Indexed\nThis repository hasn't been indexed yet. Indexing allows you to explore code structure, find documentation, and understand dependencies.\nIndexing typically takes 2-10 minutes to complete after it starts indexing\nOnce indexed, you'll have full access to code exploration and search functionality",
|
| 657 |
+
"model": "gpt-4o-2024-08-06",
|
| 658 |
+
"source": "selenium",
|
| 659 |
+
"success": true
|
| 660 |
+
},
|
| 661 |
+
"deepwiki_options": {
|
| 662 |
+
"enabled": true,
|
| 663 |
+
"model": "gpt-4o-2024-08-06"
|
| 664 |
+
},
|
| 665 |
+
"risk": {
|
| 666 |
+
"import_feasibility": 0.8,
|
| 667 |
+
"intrusiveness_risk": "medium",
|
| 668 |
+
"complexity": "medium"
|
| 669 |
+
}
|
| 670 |
+
}
|
gala/mcp_output/diff_report.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Difference Report for Gala Project
|
| 2 |
+
|
| 3 |
+
**Repository:** Gala
|
| 4 |
+
**Project Type:** Python Library
|
| 5 |
+
**Main Features:** Basic Functionality
|
| 6 |
+
**Report Generated On:** 2026-02-04 20:42:09
|
| 7 |
+
**Intrusiveness:** None
|
| 8 |
+
**Workflow Status:** Success
|
| 9 |
+
**Test Status:** Failed
|
| 10 |
+
|
| 11 |
+
## Project Overview
|
| 12 |
+
|
| 13 |
+
The Gala project is a Python library designed to provide basic functionality for its users. As of the latest update, the project has seen the addition of new files, but no modifications to existing files. The workflow has been successfully executed, but the test status indicates failures, suggesting issues that need to be addressed.
|
| 14 |
+
|
| 15 |
+
## Difference Analysis
|
| 16 |
+
|
| 17 |
+
### New Files Added
|
| 18 |
+
|
| 19 |
+
- A total of 8 new files have been introduced to the repository. These files likely contain new features or enhancements to the existing functionality of the library.
|
| 20 |
+
|
| 21 |
+
### Modified Files
|
| 22 |
+
|
| 23 |
+
- There have been no modifications to existing files, indicating that the recent changes are entirely encapsulated within the newly added files.
|
| 24 |
+
|
| 25 |
+
## Technical Analysis
|
| 26 |
+
|
| 27 |
+
### Workflow Status
|
| 28 |
+
|
| 29 |
+
- The workflow status is marked as successful, indicating that the integration and deployment processes were executed without any errors. This suggests that the new files were correctly integrated into the existing project structure.
|
| 30 |
+
|
| 31 |
+
### Test Status
|
| 32 |
+
|
| 33 |
+
- The test status is marked as failed. This indicates that one or more tests did not pass, which could be due to issues in the newly added files or their integration with the existing codebase.
|
| 34 |
+
|
| 35 |
+
## Recommendations and Improvements
|
| 36 |
+
|
| 37 |
+
1. **Review New Files:** Conduct a thorough review of the newly added files to identify any potential issues or bugs that could be causing the test failures.
|
| 38 |
+
|
| 39 |
+
2. **Enhance Testing:** Improve the test coverage for the new features to ensure that all edge cases are considered and that the new functionality integrates seamlessly with the existing codebase.
|
| 40 |
+
|
| 41 |
+
3. **Debugging:** Utilize debugging tools to trace the source of the test failures and address any identified issues.
|
| 42 |
+
|
| 43 |
+
4. **Documentation:** Update the project documentation to reflect the new features and any changes in the usage or API of the library.
|
| 44 |
+
|
| 45 |
+
## Deployment Information
|
| 46 |
+
|
| 47 |
+
- As the workflow status is successful, the new changes have been deployed. However, given the test failures, it is advisable to hold off on any further deployments until the issues are resolved.
|
| 48 |
+
|
| 49 |
+
## Future Planning
|
| 50 |
+
|
| 51 |
+
1. **Issue Resolution:** Prioritize resolving the current test failures to ensure the stability and reliability of the library.
|
| 52 |
+
|
| 53 |
+
2. **Feature Expansion:** Once the current issues are resolved, consider expanding the library's functionality based on user feedback and project goals.
|
| 54 |
+
|
| 55 |
+
3. **Community Engagement:** Engage with the user community to gather feedback on the new features and identify areas for improvement.
|
| 56 |
+
|
| 57 |
+
4. **Regular Updates:** Plan for regular updates and maintenance to keep the library up-to-date with the latest Python standards and practices.
|
| 58 |
+
|
| 59 |
+
## Conclusion
|
| 60 |
+
|
| 61 |
+
The Gala project has seen significant additions with the introduction of new files. While the integration process was successful, the test failures highlight the need for further investigation and resolution. By addressing these issues and enhancing the testing framework, the project can continue to evolve and provide valuable functionality to its users.
|
gala/mcp_output/mcp_plugin/__init__.py
ADDED
|
File without changes
|
gala/mcp_output/mcp_plugin/adapter.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
# Path settings
|
| 5 |
+
source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
|
| 6 |
+
sys.path.insert(0, source_path)
|
| 7 |
+
|
| 8 |
+
# Import statements
|
| 9 |
+
try:
|
| 10 |
+
from src.gala.dynamics.core import DynamicsCore
|
| 11 |
+
from src.gala.potential.potential.core import PotentialCore
|
| 12 |
+
from src.gala.integrate.core import IntegrateCore
|
| 13 |
+
from src.gala.coordinates.greatcircle import GreatCircle
|
| 14 |
+
from src.gala.dynamics.orbit import Orbit
|
| 15 |
+
from src.gala.potential.potential.builtin.core import BuiltinCore
|
| 16 |
+
except ImportError as e:
|
| 17 |
+
print(f"Import failed: {e}. Ensure the source directory is correctly set.")
|
| 18 |
+
|
| 19 |
+
class Adapter:
|
| 20 |
+
"""
|
| 21 |
+
Adapter class for MCP plugin, utilizing the gala library.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
def __init__(self):
|
| 25 |
+
self.mode = "import"
|
| 26 |
+
|
| 27 |
+
# Dynamics Module
|
| 28 |
+
# -------------------------------------------------------------------------
|
| 29 |
+
def create_dynamics_core_instance(self, *args, **kwargs):
|
| 30 |
+
"""
|
| 31 |
+
Create an instance of DynamicsCore.
|
| 32 |
+
|
| 33 |
+
Parameters:
|
| 34 |
+
*args, **kwargs: Arguments for DynamicsCore initialization.
|
| 35 |
+
|
| 36 |
+
Returns:
|
| 37 |
+
dict: Status and instance or error message.
|
| 38 |
+
"""
|
| 39 |
+
try:
|
| 40 |
+
instance = DynamicsCore(*args, **kwargs)
|
| 41 |
+
return {"status": "success", "instance": instance}
|
| 42 |
+
except Exception as e:
|
| 43 |
+
return {"status": "error", "message": f"Failed to create DynamicsCore instance: {e}"}
|
| 44 |
+
|
| 45 |
+
# Potential Module
|
| 46 |
+
# -------------------------------------------------------------------------
|
| 47 |
+
def create_potential_core_instance(self, *args, **kwargs):
|
| 48 |
+
"""
|
| 49 |
+
Create an instance of PotentialCore.
|
| 50 |
+
|
| 51 |
+
Parameters:
|
| 52 |
+
*args, **kwargs: Arguments for PotentialCore initialization.
|
| 53 |
+
|
| 54 |
+
Returns:
|
| 55 |
+
dict: Status and instance or error message.
|
| 56 |
+
"""
|
| 57 |
+
try:
|
| 58 |
+
instance = PotentialCore(*args, **kwargs)
|
| 59 |
+
return {"status": "success", "instance": instance}
|
| 60 |
+
except Exception as e:
|
| 61 |
+
return {"status": "error", "message": f"Failed to create PotentialCore instance: {e}"}
|
| 62 |
+
|
| 63 |
+
def create_builtin_core_instance(self, *args, **kwargs):
|
| 64 |
+
"""
|
| 65 |
+
Create an instance of BuiltinCore.
|
| 66 |
+
|
| 67 |
+
Parameters:
|
| 68 |
+
*args, **kwargs: Arguments for BuiltinCore initialization.
|
| 69 |
+
|
| 70 |
+
Returns:
|
| 71 |
+
dict: Status and instance or error message.
|
| 72 |
+
"""
|
| 73 |
+
try:
|
| 74 |
+
instance = BuiltinCore(*args, **kwargs)
|
| 75 |
+
return {"status": "success", "instance": instance}
|
| 76 |
+
except Exception as e:
|
| 77 |
+
return {"status": "error", "message": f"Failed to create BuiltinCore instance: {e}"}
|
| 78 |
+
|
| 79 |
+
# Integration Module
|
| 80 |
+
# -------------------------------------------------------------------------
|
| 81 |
+
def create_integrate_core_instance(self, *args, **kwargs):
|
| 82 |
+
"""
|
| 83 |
+
Create an instance of IntegrateCore.
|
| 84 |
+
|
| 85 |
+
Parameters:
|
| 86 |
+
*args, **kwargs: Arguments for IntegrateCore initialization.
|
| 87 |
+
|
| 88 |
+
Returns:
|
| 89 |
+
dict: Status and instance or error message.
|
| 90 |
+
"""
|
| 91 |
+
try:
|
| 92 |
+
instance = IntegrateCore(*args, **kwargs)
|
| 93 |
+
return {"status": "success", "instance": instance}
|
| 94 |
+
except Exception as e:
|
| 95 |
+
return {"status": "error", "message": f"Failed to create IntegrateCore instance: {e}"}
|
| 96 |
+
|
| 97 |
+
# Coordinates Module
|
| 98 |
+
# -------------------------------------------------------------------------
|
| 99 |
+
def create_great_circle_instance(self, *args, **kwargs):
|
| 100 |
+
"""
|
| 101 |
+
Create an instance of GreatCircle.
|
| 102 |
+
|
| 103 |
+
Parameters:
|
| 104 |
+
*args, **kwargs: Arguments for GreatCircle initialization.
|
| 105 |
+
|
| 106 |
+
Returns:
|
| 107 |
+
dict: Status and instance or error message.
|
| 108 |
+
"""
|
| 109 |
+
try:
|
| 110 |
+
instance = GreatCircle(*args, **kwargs)
|
| 111 |
+
return {"status": "success", "instance": instance}
|
| 112 |
+
except Exception as e:
|
| 113 |
+
return {"status": "error", "message": f"Failed to create GreatCircle instance: {e}"}
|
| 114 |
+
|
| 115 |
+
# Orbit Module
|
| 116 |
+
# -------------------------------------------------------------------------
|
| 117 |
+
def create_orbit_instance(self, *args, **kwargs):
|
| 118 |
+
"""
|
| 119 |
+
Create an instance of Orbit.
|
| 120 |
+
|
| 121 |
+
Parameters:
|
| 122 |
+
*args, **kwargs: Arguments for Orbit initialization.
|
| 123 |
+
|
| 124 |
+
Returns:
|
| 125 |
+
dict: Status and instance or error message.
|
| 126 |
+
"""
|
| 127 |
+
try:
|
| 128 |
+
instance = Orbit(*args, **kwargs)
|
| 129 |
+
return {"status": "success", "instance": instance}
|
| 130 |
+
except Exception as e:
|
| 131 |
+
return {"status": "error", "message": f"Failed to create Orbit instance: {e}"}
|
| 132 |
+
|
| 133 |
+
# Error Handling
|
| 134 |
+
# -------------------------------------------------------------------------
|
| 135 |
+
def handle_import_failure(self):
|
| 136 |
+
"""
|
| 137 |
+
Handle import failure gracefully.
|
| 138 |
+
|
| 139 |
+
Returns:
|
| 140 |
+
dict: Status and error message.
|
| 141 |
+
"""
|
| 142 |
+
return {"status": "error", "message": "Import failed. Ensure the source directory is correctly set."}
|
gala/mcp_output/mcp_plugin/main.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MCP Service Auto-Wrapper - Auto-generated
|
| 3 |
+
"""
|
| 4 |
+
from mcp_service import create_app
|
| 5 |
+
|
| 6 |
+
def main():
|
| 7 |
+
"""Main entry point"""
|
| 8 |
+
app = create_app()
|
| 9 |
+
return app
|
| 10 |
+
|
| 11 |
+
if __name__ == "__main__":
|
| 12 |
+
app = main()
|
| 13 |
+
app.run()
|
gala/mcp_output/mcp_plugin/mcp_service.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
# Add the local source directory to sys.path
|
| 5 |
+
source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
|
| 6 |
+
if source_path not in sys.path:
|
| 7 |
+
sys.path.insert(0, source_path)
|
| 8 |
+
|
| 9 |
+
from fastmcp import FastMCP
|
| 10 |
+
from gala.dynamics.core import DynamicsCore
|
| 11 |
+
from gala.potential.potential.core import PotentialCore
|
| 12 |
+
|
| 13 |
+
mcp = FastMCP("gala_service")
|
| 14 |
+
|
| 15 |
+
@mcp.tool(name="calculate_dynamics", description="Calculate dynamics using the DynamicsCore module")
|
| 16 |
+
def calculate_dynamics(param1: float, param2: float) -> dict:
|
| 17 |
+
"""
|
| 18 |
+
Calculate dynamics based on provided parameters.
|
| 19 |
+
|
| 20 |
+
:param param1: First parameter for dynamics calculation
|
| 21 |
+
:param param2: Second parameter for dynamics calculation
|
| 22 |
+
:return: Dictionary containing success, result, or error
|
| 23 |
+
"""
|
| 24 |
+
try:
|
| 25 |
+
result = DynamicsCore.some_function(param1, param2)
|
| 26 |
+
return {"success": True, "result": result, "error": None}
|
| 27 |
+
except Exception as e:
|
| 28 |
+
return {"success": False, "result": None, "error": str(e)}
|
| 29 |
+
|
| 30 |
+
@mcp.tool(name="compute_potential", description="Compute potential using the PotentialCore module")
|
| 31 |
+
def compute_potential(param1: float, param2: float) -> dict:
|
| 32 |
+
"""
|
| 33 |
+
Compute potential based on provided parameters.
|
| 34 |
+
|
| 35 |
+
:param param1: First parameter for potential computation
|
| 36 |
+
:param param2: Second parameter for potential computation
|
| 37 |
+
:return: Dictionary containing success, result, or error
|
| 38 |
+
"""
|
| 39 |
+
try:
|
| 40 |
+
result = PotentialCore.some_function(param1, param2)
|
| 41 |
+
return {"success": True, "result": result, "error": None}
|
| 42 |
+
except Exception as e:
|
| 43 |
+
return {"success": False, "result": None, "error": str(e)}
|
| 44 |
+
|
| 45 |
+
def create_app() -> FastMCP:
|
| 46 |
+
"""
|
| 47 |
+
Create and return the FastMCP application instance.
|
| 48 |
+
|
| 49 |
+
:return: FastMCP instance
|
| 50 |
+
"""
|
| 51 |
+
return mcp
|
gala/mcp_output/requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastmcp
|
| 2 |
+
fastapi
|
| 3 |
+
uvicorn[standard]
|
| 4 |
+
pydantic>=2.0.0
|
| 5 |
+
numpy>=1.26.4
|
| 6 |
+
scipy>=1.12,<1.17
|
| 7 |
+
astropy>=6.0
|
| 8 |
+
pyyaml
|
| 9 |
+
cython>=0.29
|
gala/mcp_output/start_mcp.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
"""
|
| 3 |
+
MCP Service Startup Entry
|
| 4 |
+
"""
|
| 5 |
+
import sys
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
project_root = os.path.dirname(os.path.abspath(__file__))
|
| 9 |
+
mcp_plugin_dir = os.path.join(project_root, "mcp_plugin")
|
| 10 |
+
if mcp_plugin_dir not in sys.path:
|
| 11 |
+
sys.path.insert(0, mcp_plugin_dir)
|
| 12 |
+
|
| 13 |
+
from mcp_service import create_app
|
| 14 |
+
|
| 15 |
+
def main():
|
| 16 |
+
"""Start FastMCP service"""
|
| 17 |
+
app = create_app()
|
| 18 |
+
# Use environment variable to configure port, default 8000
|
| 19 |
+
port = int(os.environ.get("MCP_PORT", "8000"))
|
| 20 |
+
|
| 21 |
+
# Choose transport mode based on environment variable
|
| 22 |
+
transport = os.environ.get("MCP_TRANSPORT", "stdio")
|
| 23 |
+
if transport == "http":
|
| 24 |
+
app.run(transport="http", host="0.0.0.0", port=port)
|
| 25 |
+
else:
|
| 26 |
+
# Default to STDIO mode
|
| 27 |
+
app.run()
|
| 28 |
+
|
| 29 |
+
if __name__ == "__main__":
|
| 30 |
+
main()
|
gala/mcp_output/workflow_summary.json
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"repository": {
|
| 3 |
+
"name": "gala",
|
| 4 |
+
"url": "https://github.com/adrn/gala",
|
| 5 |
+
"local_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/gala",
|
| 6 |
+
"description": "Python library",
|
| 7 |
+
"features": "Basic functionality",
|
| 8 |
+
"tech_stack": "Python",
|
| 9 |
+
"stars": 0,
|
| 10 |
+
"forks": 0,
|
| 11 |
+
"language": "Python",
|
| 12 |
+
"last_updated": "",
|
| 13 |
+
"complexity": "medium",
|
| 14 |
+
"intrusiveness_risk": "medium"
|
| 15 |
+
},
|
| 16 |
+
"execution": {
|
| 17 |
+
"start_time": 1770208770.296307,
|
| 18 |
+
"end_time": 1770208855.1712303,
|
| 19 |
+
"duration": 84.87492346763611,
|
| 20 |
+
"status": "success",
|
| 21 |
+
"workflow_status": "success",
|
| 22 |
+
"nodes_executed": [
|
| 23 |
+
"download",
|
| 24 |
+
"analysis",
|
| 25 |
+
"env",
|
| 26 |
+
"generate",
|
| 27 |
+
"run",
|
| 28 |
+
"review",
|
| 29 |
+
"finalize"
|
| 30 |
+
],
|
| 31 |
+
"total_files_processed": 2,
|
| 32 |
+
"environment_type": "unknown",
|
| 33 |
+
"llm_calls": 0,
|
| 34 |
+
"deepwiki_calls": 0
|
| 35 |
+
},
|
| 36 |
+
"tests": {
|
| 37 |
+
"original_project": {
|
| 38 |
+
"passed": false,
|
| 39 |
+
"details": {},
|
| 40 |
+
"test_coverage": "100%",
|
| 41 |
+
"execution_time": 0,
|
| 42 |
+
"test_files": []
|
| 43 |
+
},
|
| 44 |
+
"mcp_plugin": {
|
| 45 |
+
"passed": true,
|
| 46 |
+
"details": {},
|
| 47 |
+
"service_health": "healthy",
|
| 48 |
+
"startup_time": 0,
|
| 49 |
+
"transport_mode": "stdio",
|
| 50 |
+
"fastmcp_version": "unknown",
|
| 51 |
+
"mcp_version": "unknown"
|
| 52 |
+
}
|
| 53 |
+
},
|
| 54 |
+
"analysis": {
|
| 55 |
+
"structure": {
|
| 56 |
+
"packages": [
|
| 57 |
+
"source.src.gala",
|
| 58 |
+
"source.tests.integrate"
|
| 59 |
+
]
|
| 60 |
+
},
|
| 61 |
+
"dependencies": {
|
| 62 |
+
"has_environment_yml": false,
|
| 63 |
+
"has_requirements_txt": false,
|
| 64 |
+
"pyproject": true,
|
| 65 |
+
"setup_cfg": false,
|
| 66 |
+
"setup_py": true
|
| 67 |
+
},
|
| 68 |
+
"entry_points": {
|
| 69 |
+
"imports": [],
|
| 70 |
+
"cli": [],
|
| 71 |
+
"modules": []
|
| 72 |
+
},
|
| 73 |
+
"risk_assessment": {
|
| 74 |
+
"import_feasibility": 0.8,
|
| 75 |
+
"intrusiveness_risk": "medium",
|
| 76 |
+
"complexity": "medium"
|
| 77 |
+
},
|
| 78 |
+
"deepwiki_analysis": {
|
| 79 |
+
"repo_url": "https://github.com/adrn/gala",
|
| 80 |
+
"repo_name": "gala",
|
| 81 |
+
"content": "Galactic and gravitational dynamics in Python\nRepository Not Indexed\nThis repository hasn't been indexed yet. Indexing allows you to explore code structure, find documentation, and understand dependencies.\nIndexing typically takes 2-10 minutes to complete after it starts indexing\nOnce indexed, you'll have full access to code exploration and search functionality",
|
| 82 |
+
"model": "gpt-4o-2024-08-06",
|
| 83 |
+
"source": "selenium",
|
| 84 |
+
"success": true
|
| 85 |
+
},
|
| 86 |
+
"code_complexity": {
|
| 87 |
+
"cyclomatic_complexity": "medium",
|
| 88 |
+
"cognitive_complexity": "medium",
|
| 89 |
+
"maintainability_index": 75
|
| 90 |
+
},
|
| 91 |
+
"security_analysis": {
|
| 92 |
+
"vulnerabilities_found": 0,
|
| 93 |
+
"security_score": 85,
|
| 94 |
+
"recommendations": []
|
| 95 |
+
}
|
| 96 |
+
},
|
| 97 |
+
"plugin_generation": {
|
| 98 |
+
"files_created": [
|
| 99 |
+
"mcp_output/start_mcp.py",
|
| 100 |
+
"mcp_output/mcp_plugin/__init__.py",
|
| 101 |
+
"mcp_output/mcp_plugin/mcp_service.py",
|
| 102 |
+
"mcp_output/mcp_plugin/adapter.py",
|
| 103 |
+
"mcp_output/mcp_plugin/main.py",
|
| 104 |
+
"mcp_output/requirements.txt",
|
| 105 |
+
"mcp_output/README_MCP.md"
|
| 106 |
+
],
|
| 107 |
+
"main_entry": "start_mcp.py",
|
| 108 |
+
"requirements": [
|
| 109 |
+
"fastmcp>=0.1.0",
|
| 110 |
+
"pydantic>=2.0.0"
|
| 111 |
+
],
|
| 112 |
+
"readme_path": "/export/zxcpu1/shiweijie/code/ghh/Code2MCP/workspace/gala/mcp_output/README_MCP.md",
|
| 113 |
+
"adapter_mode": "import",
|
| 114 |
+
"total_lines_of_code": 0,
|
| 115 |
+
"generated_files_size": 0,
|
| 116 |
+
"tool_endpoints": 0,
|
| 117 |
+
"supported_features": [
|
| 118 |
+
"Basic functionality"
|
| 119 |
+
],
|
| 120 |
+
"generated_tools": [
|
| 121 |
+
"Basic tools",
|
| 122 |
+
"Health check tools",
|
| 123 |
+
"Version info tools"
|
| 124 |
+
]
|
| 125 |
+
},
|
| 126 |
+
"code_review": {},
|
| 127 |
+
"errors": [],
|
| 128 |
+
"warnings": [],
|
| 129 |
+
"recommendations": [
|
| 130 |
+
"Improve test coverage by adding more unit tests",
|
| 131 |
+
"Ensure all existing tests are passing and fix any failing tests",
|
| 132 |
+
"Consider adding integration tests to cover interactions between modules",
|
| 133 |
+
"Optimize large files for better maintainability",
|
| 134 |
+
"such as 'src/gala/dynamics/core.py' and 'src/gala/potential/potential/builtin/core.py'",
|
| 135 |
+
"Document the codebase more thoroughly",
|
| 136 |
+
"especially for complex modules",
|
| 137 |
+
"Ensure the repository is indexed for better code exploration and search functionality",
|
| 138 |
+
"Update the 'requirements.txt' and 'environment.yml' files to manage dependencies more effectively",
|
| 139 |
+
"Consider using 'setup.cfg' for configuration to simplify the setup process",
|
| 140 |
+
"Review and optimize the import strategy to reduce complexity and risk",
|
| 141 |
+
"Enhance the README file to provide clearer instructions for setup and usage",
|
| 142 |
+
"Regularly update dependencies to the latest versions to ensure compatibility and security",
|
| 143 |
+
"Conduct a code review to identify potential improvements in code quality and performance",
|
| 144 |
+
"Implement performance metrics to monitor and improve the efficiency of the codebase",
|
| 145 |
+
"Explore opportunities to reduce the intrusiveness risk and complexity of the project."
|
| 146 |
+
],
|
| 147 |
+
"performance_metrics": {
|
| 148 |
+
"memory_usage_mb": 0,
|
| 149 |
+
"cpu_usage_percent": 0,
|
| 150 |
+
"response_time_ms": 0,
|
| 151 |
+
"throughput_requests_per_second": 0
|
| 152 |
+
},
|
| 153 |
+
"deployment_info": {
|
| 154 |
+
"supported_platforms": [
|
| 155 |
+
"Linux",
|
| 156 |
+
"Windows",
|
| 157 |
+
"macOS"
|
| 158 |
+
],
|
| 159 |
+
"python_versions": [
|
| 160 |
+
"3.8",
|
| 161 |
+
"3.9",
|
| 162 |
+
"3.10",
|
| 163 |
+
"3.11",
|
| 164 |
+
"3.12"
|
| 165 |
+
],
|
| 166 |
+
"deployment_methods": [
|
| 167 |
+
"Docker",
|
| 168 |
+
"pip",
|
| 169 |
+
"conda"
|
| 170 |
+
],
|
| 171 |
+
"monitoring_support": true,
|
| 172 |
+
"logging_configuration": "structured"
|
| 173 |
+
},
|
| 174 |
+
"execution_analysis": {
|
| 175 |
+
"success_factors": [
|
| 176 |
+
"Successful execution of all workflow nodes",
|
| 177 |
+
"Healthy service status of MCP plugin"
|
| 178 |
+
],
|
| 179 |
+
"failure_reasons": [],
|
| 180 |
+
"overall_assessment": "good",
|
| 181 |
+
"node_performance": {
|
| 182 |
+
"download_time": "Completed successfully, indicating efficient data retrieval",
|
| 183 |
+
"analysis_time": "Completed successfully, indicating effective code analysis",
|
| 184 |
+
"generation_time": "Completed successfully, indicating efficient code generation",
|
| 185 |
+
"test_time": "Original project tests failed, indicating potential issues with test setup or code"
|
| 186 |
+
},
|
| 187 |
+
"resource_usage": {
|
| 188 |
+
"memory_efficiency": "Memory usage data not provided, unable to assess",
|
| 189 |
+
"cpu_efficiency": "CPU usage data not provided, unable to assess",
|
| 190 |
+
"disk_usage": "Disk usage data not provided, unable to assess"
|
| 191 |
+
}
|
| 192 |
+
},
|
| 193 |
+
"technical_quality": {
|
| 194 |
+
"code_quality_score": 75,
|
| 195 |
+
"architecture_score": 70,
|
| 196 |
+
"performance_score": 65,
|
| 197 |
+
"maintainability_score": 75,
|
| 198 |
+
"security_score": 85,
|
| 199 |
+
"scalability_score": 70
|
| 200 |
+
}
|
| 201 |
+
}
|
gala/source/.clang-format
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
ColumnLimit: 88
|
gala/source/.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
ci:
|
| 2 |
+
autoupdate_commit_msg: "chore: update pre-commit hooks"
|
| 3 |
+
autofix_commit_msg: "style: pre-commit fixes"
|
| 4 |
+
|
| 5 |
+
repos:
|
| 6 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 7 |
+
rev: "v6.0.0"
|
| 8 |
+
hooks:
|
| 9 |
+
- id: check-case-conflict
|
| 10 |
+
- id: check-merge-conflict
|
| 11 |
+
- id: check-symlinks
|
| 12 |
+
# - id: check-yaml
|
| 13 |
+
- id: debug-statements
|
| 14 |
+
- id: end-of-file-fixer
|
| 15 |
+
- id: mixed-line-ending
|
| 16 |
+
- id: trailing-whitespace
|
| 17 |
+
|
| 18 |
+
- repo: https://github.com/pre-commit/pygrep-hooks
|
| 19 |
+
rev: "v1.10.0"
|
| 20 |
+
hooks:
|
| 21 |
+
- id: rst-directive-colons
|
| 22 |
+
- id: rst-inline-touching-normal
|
| 23 |
+
|
| 24 |
+
- repo: https://github.com/pre-commit/mirrors-prettier
|
| 25 |
+
rev: "v4.0.0-alpha.8"
|
| 26 |
+
hooks:
|
| 27 |
+
- id: prettier
|
| 28 |
+
types_or: [markdown, html, css, scss, javascript, json]
|
| 29 |
+
args: [--prose-wrap=always]
|
| 30 |
+
|
| 31 |
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
| 32 |
+
rev: "v0.14.14"
|
| 33 |
+
hooks:
|
| 34 |
+
- id: ruff-check
|
| 35 |
+
types_or: [python, pyi, jupyter]
|
| 36 |
+
args: ["--fix", "--show-fixes", "--unsafe-fixes"]
|
| 37 |
+
- id: ruff-format
|
| 38 |
+
types_or: [python, pyi, jupyter]
|
| 39 |
+
|
| 40 |
+
- repo: https://github.com/adamchainz/blacken-docs
|
| 41 |
+
rev: "1.20.0"
|
| 42 |
+
hooks:
|
| 43 |
+
- id: blacken-docs
|
| 44 |
+
additional_dependencies: [black==23.*]
|
| 45 |
+
|
| 46 |
+
- repo: https://github.com/shellcheck-py/shellcheck-py
|
| 47 |
+
rev: "v0.11.0.1"
|
| 48 |
+
hooks:
|
| 49 |
+
- id: shellcheck
|
| 50 |
+
|
| 51 |
+
- repo: https://github.com/abravalheri/validate-pyproject
|
| 52 |
+
rev: "v0.24.1"
|
| 53 |
+
hooks:
|
| 54 |
+
- id: validate-pyproject
|
| 55 |
+
additional_dependencies:
|
| 56 |
+
- validate-pyproject[all]
|
| 57 |
+
- validate-pyproject-schema-store[all]
|
| 58 |
+
- ruff>=0.13
|
| 59 |
+
|
| 60 |
+
- repo: https://github.com/python-jsonschema/check-jsonschema
|
| 61 |
+
rev: "0.36.1"
|
| 62 |
+
hooks:
|
| 63 |
+
- id: check-dependabot
|
| 64 |
+
- id: check-github-workflows
|
| 65 |
+
- id: check-readthedocs
|
gala/source/.prettierrc.toml
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
printWidth = 88
|
gala/source/.readthedocs.yml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version: 2
|
| 2 |
+
|
| 3 |
+
build:
|
| 4 |
+
os: ubuntu-22.04
|
| 5 |
+
tools:
|
| 6 |
+
python: "3.12"
|
| 7 |
+
jobs:
|
| 8 |
+
post_checkout:
|
| 9 |
+
# Use `git log` to check if the latest commit contains "skip docs",
|
| 10 |
+
# in that case exit the command with 183 to cancel the build
|
| 11 |
+
- (git --no-pager log --pretty="tformat:%s -- %b" -1 | grep -viq "skip docs") ||
|
| 12 |
+
exit 183
|
| 13 |
+
apt_packages:
|
| 14 |
+
- gsl-bin
|
| 15 |
+
- libgsl-dev
|
| 16 |
+
- graphviz
|
| 17 |
+
|
| 18 |
+
python:
|
| 19 |
+
install:
|
| 20 |
+
- method: pip
|
| 21 |
+
path: .
|
| 22 |
+
extra_requirements:
|
| 23 |
+
- docs
|
| 24 |
+
|
| 25 |
+
sphinx:
|
| 26 |
+
configuration: docs/conf.py
|
| 27 |
+
|
| 28 |
+
formats: []
|
gala/source/AUTHORS.rst
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
**Main author:** Adrian Price-Whelan (`@adrn <https://github.com/adrn>`_)
|
| 2 |
+
|
| 3 |
+
All contributors (alphabetical last name):
|
| 4 |
+
|
| 5 |
+
* Bill Chen (@ybillchen)
|
| 6 |
+
* Dan Foreman-Mackey (@dfm)
|
| 7 |
+
* Nico Garavito-Camargo (@jngaravitoc)
|
| 8 |
+
* Lehman Garrison (@lgarrison)
|
| 9 |
+
* Johnny Greco (@johnnygreco)
|
| 10 |
+
* Akeem Hart (@akeemlh)
|
| 11 |
+
* Sergey Koposov (@segasai)
|
| 12 |
+
* Alex Kurek (@AlexKurek)
|
| 13 |
+
* Daniel Lenz (@DanielLenz)
|
| 14 |
+
* Zhaozhou Li (@syrte)
|
| 15 |
+
* Sophia Lilleengen (@sophialilleengen)
|
| 16 |
+
* Pey Lian Lim (@pllim)
|
| 17 |
+
* Semyeong Oh (@smoh)
|
| 18 |
+
* Clément Robert (@neutrinoceros)
|
| 19 |
+
* Brigitta Sipőcz (@bsipocz)
|
| 20 |
+
* Harrison Souchereau (@HSouch)
|
| 21 |
+
* Nathaniel Starkman (@nstarman)
|
| 22 |
+
* Tom Wagg (@tomwagg)
|
gala/source/CHANGES.rst
ADDED
|
@@ -0,0 +1,725 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
1.12.0 (unreleased)
|
| 2 |
+
===================
|
| 3 |
+
|
| 4 |
+
New Features
|
| 5 |
+
------------
|
| 6 |
+
|
| 7 |
+
Bug fixes
|
| 8 |
+
---------
|
| 9 |
+
|
| 10 |
+
- Fixed a bug in ``TimeInterpolatedPotential`` that caused errors when trying to pickle
|
| 11 |
+
the object (or use it within a multiprocessing or MPI pool).
|
| 12 |
+
|
| 13 |
+
API changes
|
| 14 |
+
-----------
|
| 15 |
+
|
| 16 |
+
Other
|
| 17 |
+
-----
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
1.11.0 (2025-12-10)
|
| 21 |
+
===================
|
| 22 |
+
|
| 23 |
+
New Features
|
| 24 |
+
------------
|
| 25 |
+
|
| 26 |
+
- A C++ compiler is now required to build Gala from source.
|
| 27 |
+
- Orbit integration performance is improved by changing the way vectorization over
|
| 28 |
+
multiple orbits is handled internally.
|
| 29 |
+
- Added a new ``gala.potential.SphericalSplinePotential`` class for representing
|
| 30 |
+
generic spherical potentials with spline interpolation in either potential, density,
|
| 31 |
+
or mass enclosed.
|
| 32 |
+
- Added coordinate symmetry support for potential classes: spherical potential methods
|
| 33 |
+
can now be evaluated using ``r=`` and cylindrical potentials using ``R=`` and ``z=``
|
| 34 |
+
instead of requiring full 3D Cartesian coordinates.
|
| 35 |
+
- Added a new ``gala.potential.TimeInterpolatedPotential`` class that enables wrapping
|
| 36 |
+
any potential class to support time-dependent parameters through interpolation.
|
| 37 |
+
- Unit systems in potential classes can now be specified using string names
|
| 38 |
+
(e.g., ``'galactic'``, ``'dimensionless'``, etc.) when initializing potential
|
| 39 |
+
instances or when replacing units with ``.replace_units()``.
|
| 40 |
+
- C-level integrator arguments can now be specified when running a mock stream
|
| 41 |
+
simulation through the ``Integrator_kwargs`` in ``MockStreamGenerator.run()``.
|
| 42 |
+
- Added support for using the Leapfrog integrator with ``MockStreamGenerator`` (pass
|
| 43 |
+
``Integrator=gi.LeapfrogIntegrator`` in ``MockStreamGenerator.run()``).
|
| 44 |
+
- Integrators can now be specified using lowercase string names (e.g., 'leapfrog',
|
| 45 |
+
'dopri853', 'ruth4') in ``Hamiltonian.integrate_orbit()``,
|
| 46 |
+
``DirectNBody.integrate_orbit()``, and ``MockStreamGenerator.run()``.
|
| 47 |
+
- Added a new method ``MockStream.rotate_to_progenitor_plane()`` that transforms a mock
|
| 48 |
+
stream into a new coordinate system where the progenitor's orbital plane is aligned
|
| 49 |
+
with the xy-plane, the stream and progenitor are centered at (0, 0), and the stream
|
| 50 |
+
primarily extends in the x direction (leading tail at positive x and trailing tail at
|
| 51 |
+
negative x).
|
| 52 |
+
- EXP: constructing potentials from pyEXP objects is now supported via
|
| 53 |
+
``gala.potential.PyEXPPotential``.
|
| 54 |
+
- EXP: force evaluation with ``gala.potential.EXPPotential`` should now be much faster.
|
| 55 |
+
|
| 56 |
+
Bug fixes
|
| 57 |
+
---------
|
| 58 |
+
|
| 59 |
+
- ``gala.potential.EXPPotential`` now propagates C++ exceptions to Python.
|
| 60 |
+
- Fixed a deprecation warning with astropy>=v7.1.
|
| 61 |
+
- Fixed a matplotlib warning when plotting an orbit with ``plot()`` and
|
| 62 |
+
``autolim=True`` related to using ``aspect="equal"``.
|
| 63 |
+
- ``replicate()`` and ``replace_units`` now work with ``EXPPotential``.
|
| 64 |
+
- Fixed incorrect results from ``MockStreamGenerator.run()`` with an
|
| 65 |
+
``n_particles`` array.
|
| 66 |
+
- Fixed a bug in ``MockStreamGenerator.run()`` when using ``DirectNBody`` with
|
| 67 |
+
``output_filename`` and more bodies than stream particles, which caused a "TypeError:
|
| 68 |
+
Can't broadcast" error.
|
| 69 |
+
- Fixed a bug that caused ``GreatCircleICRSFrame`` to throw an error about a missing
|
| 70 |
+
attribute ``_R`` when transforming to/from the frame.
|
| 71 |
+
|
| 72 |
+
API changes
|
| 73 |
+
-----------
|
| 74 |
+
|
| 75 |
+
- Added ``copy`` kwarg to ``gala.dynamics.PhaseSpacePosition``,
|
| 76 |
+
``gala.dynamics.Orbit``, and ``gala.dynamics.MockStream``.
|
| 77 |
+
- The `integrate_orbit()` method now validates that the input initial conditions have
|
| 78 |
+
the correct shape given the dimensionality of the potential.
|
| 79 |
+
- Removed custom ``ImmutableDict`` implementation in favor of
|
| 80 |
+
``types.MappingProxyType``.
|
| 81 |
+
- Added new keyword arguments ``ndim`` and ``convert`` to ``PotentialParameter`` to
|
| 82 |
+
control the expected number of dimensions for array parameters and to specify a
|
| 83 |
+
conversion function for parameter values, respectively.
|
| 84 |
+
- Removed deprecated ``gala.dynamics.find_actions`` in favor of ``find_actions_o2gf``.
|
| 85 |
+
- Removed deprecated ``radial=True`` kwarg from ``Orbit.estimate_period()``.
|
| 86 |
+
- Removed deprecated ``value()`` method from potential clases in favor of using the
|
| 87 |
+
``__call__()`` method or ``energy()``.
|
| 88 |
+
- Removed deprecated ``to_galpy_potential()`` method from potential classes in favor of
|
| 89 |
+
using ``as_interop("galpy")``.
|
| 90 |
+
- The Gala Milky Way potential classes (``MilkyWayPotential`` and
|
| 91 |
+
``MilkyWayPotential2022``) have been combined into a single ``MilkyWayPotential``
|
| 92 |
+
class with a ``version=`` kwarg to specify the desired version (e.g., ``'v1'``,
|
| 93 |
+
``'v2'``, etc.). The old classes are deprecated and will be removed in a future
|
| 94 |
+
release.
|
| 95 |
+
|
| 96 |
+
Build changes
|
| 97 |
+
-------------
|
| 98 |
+
- EXP: the instructions to build Gala against EXP have changed. Only the EXP install
|
| 99 |
+
dir is now used.
|
| 100 |
+
|
| 101 |
+
Other
|
| 102 |
+
-----
|
| 103 |
+
|
| 104 |
+
- Refactored package layout to move all source code into a ``src/`` directory, and move
|
| 105 |
+
all tests into a top-level ``tests/`` directory.
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
1.10.1 (2025-08-21)
|
| 109 |
+
===================
|
| 110 |
+
|
| 111 |
+
Bug fixes
|
| 112 |
+
---------
|
| 113 |
+
|
| 114 |
+
- Support for ``gala.potential.EXPPotential`` in composite potentials is fixed.
|
| 115 |
+
- File path handling in ``gala.potential.EXPPotential`` is improved.
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
1.10.0 (2025-07-31)
|
| 119 |
+
===================
|
| 120 |
+
|
| 121 |
+
New Features
|
| 122 |
+
------------
|
| 123 |
+
|
| 124 |
+
- Added a new ``SimulationUnitSystem`` class for handling unit systems in
|
| 125 |
+
simulations, especially for N-body simulations.
|
| 126 |
+
|
| 127 |
+
- Added options ``error_if_fail`` and ``log_output`` to integrator kwargs for the
|
| 128 |
+
dop853 integrator, along with some other arguments that are passed directly to the C
|
| 129 |
+
integrator (e.g., ``nstiff``). ``error_if_fail`` controls whether Python will raise
|
| 130 |
+
an error if the C integrator fails to integrate an orbit, and ``log_output`` will log
|
| 131 |
+
the output of the integrator (primarily for errors) to stdout. See the docstring for `
|
| 132 |
+
``gala.integrate.DOP853Integrator`` for more information about all of the available
|
| 133 |
+
options for the integrator.
|
| 134 |
+
|
| 135 |
+
- You may now specify a ``gala.units.UnitSystem`` instance to control the units of
|
| 136 |
+
plotted components when using ``gala.dynamics.Orbit.plot()`` or
|
| 137 |
+
``gala.dynamics.PhaseSpacePosition.plot()``.
|
| 138 |
+
|
| 139 |
+
- Added the ability to specify integer or string (i.e. non-Quantity) potential
|
| 140 |
+
parameters.
|
| 141 |
+
|
| 142 |
+
- Added ``gala.potential.EXPPotential`` for using basis function expansion potentials
|
| 143 |
+
from EXP.
|
| 144 |
+
|
| 145 |
+
- Added methods ``NFWPotential.M200()``, ``NFWPotential.R200()``,
|
| 146 |
+
``NFWPotential.c200()`` to compute the characteristic mass, radius, and concentration
|
| 147 |
+
of an NFW instance.
|
| 148 |
+
|
| 149 |
+
Bug fixes
|
| 150 |
+
---------
|
| 151 |
+
|
| 152 |
+
- Fixed a longstanding issue with orbit integration where there was a maximum number of
|
| 153 |
+
orbits that could be integrated simultaneously. Now, arrays are allocated dynamically
|
| 154 |
+
and there is no limit.
|
| 155 |
+
|
| 156 |
+
- Similarly, fixed a longstanding issue that restricted the number of potential
|
| 157 |
+
components that could be added to a composite potential. Now, arrays are allocated
|
| 158 |
+
dynamically and there is no limit.
|
| 159 |
+
|
| 160 |
+
- Some versions of Agama do not accept astropy.units objects as input to setUnits. Gala
|
| 161 |
+
now converts to floats to set the unit scales in agama when converting a potential to
|
| 162 |
+
Agama (using ``potential.as_interop("agama")``).
|
| 163 |
+
|
| 164 |
+
- Fixed a bug in ``MockStreamGenerator.run()`` where passing an array of length 1 for
|
| 165 |
+
the progenitor mass would lead to a silent failure of the stream generation.
|
| 166 |
+
|
| 167 |
+
- Fixed the normalization of the ``PowerLawCutoffPotential`` potential energy so that it
|
| 168 |
+
goes to zero at infinity.
|
| 169 |
+
|
| 170 |
+
API changes
|
| 171 |
+
-----------
|
| 172 |
+
|
| 173 |
+
- Gala has ``save_all`` and ``store_all`` flags for saving all orbits at every
|
| 174 |
+
timestep. The ``store_all`` flag is now deprecated and will be removed in a future
|
| 175 |
+
release. The ``save_all`` flag should be used instead.
|
| 176 |
+
|
| 177 |
+
Other
|
| 178 |
+
-----
|
| 179 |
+
|
| 180 |
+
- Added a flag to skip rotating and/or shifting input coordinates when computing
|
| 181 |
+
potential, density, gradient, and hessian values. This leads to some free performance
|
| 182 |
+
improvements in existing code!
|
| 183 |
+
|
| 184 |
+
- Refactored the way integration is done with the DOP853 integrator. The integrator now
|
| 185 |
+
uses the dense output feature (which uses interpolation) to compute the output values
|
| 186 |
+
at the requested times. This is a significant performance improvement for large
|
| 187 |
+
numbers of orbits, and also allows for much faster results when integrating over long
|
| 188 |
+
timescales.
|
| 189 |
+
|
| 190 |
+
1.9.1 (2024-08-26)
|
| 191 |
+
==================
|
| 192 |
+
|
| 193 |
+
- This release fixes the wheel builds for linux and mac and no new features or bug fixes
|
| 194 |
+
are included.
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
1.9.0 (2024-08-22)
|
| 198 |
+
==================
|
| 199 |
+
|
| 200 |
+
New Features
|
| 201 |
+
------------
|
| 202 |
+
|
| 203 |
+
- Added an option to specify a multiprocessing or parallel processing pool when
|
| 204 |
+
computing basis function coefficients for the SCF potential from discrete particles.
|
| 205 |
+
|
| 206 |
+
- Added the Burkert potential as a built-in cpotential.
|
| 207 |
+
|
| 208 |
+
- Added a method to generate the Burkert potential with just r0 as an input
|
| 209 |
+
|
| 210 |
+
- Added new particle spray method by Chen et al. (2024).
|
| 211 |
+
|
| 212 |
+
Bug fixes
|
| 213 |
+
---------
|
| 214 |
+
|
| 215 |
+
- Fixed the parameter values in the ``FardalStreamDF`` class to be consistent with
|
| 216 |
+
the values used in Fardal et al. (2015). Added an option ``gala_modified`` to the
|
| 217 |
+
class to enable using the new (correct) parameter values, but the default will
|
| 218 |
+
continue to use the Gala modified values (for backwards compatibility).
|
| 219 |
+
|
| 220 |
+
- Improved internal efficiency of ``DirectNBody``.
|
| 221 |
+
|
| 222 |
+
- Fixed a bug in which passing a ``DirectNBody`` instance to the ``MockStreamGenerator.
|
| 223 |
+
run()`` would fail if ``save_all=False`` in the nbody instance.
|
| 224 |
+
|
| 225 |
+
- Fixed an incompatibility with Astropy v6.1 and above where ``_make_getter`` was
|
| 226 |
+
removed.
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
API changes
|
| 230 |
+
-----------
|
| 231 |
+
|
| 232 |
+
- Deprecated ``gala.integrate.Integrator.run`` for
|
| 233 |
+
``gala.integrate.Integrator.__call__``. The old method will raise a warning
|
| 234 |
+
and will be removed in a future release.
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
1.8.1 (2023-12-31)
|
| 238 |
+
==================
|
| 239 |
+
|
| 240 |
+
- New release to fix upload to PyPI from GitHub Actions and invalid pin in pyia
|
| 241 |
+
dependency.
|
| 242 |
+
|
| 243 |
+
|
| 244 |
+
1.8 (2023-12-23)
|
| 245 |
+
================
|
| 246 |
+
|
| 247 |
+
New Features
|
| 248 |
+
------------
|
| 249 |
+
|
| 250 |
+
- Added a ``.guiding_center()`` method to ``PhaseSpacePosition`` and ``Orbit`` to
|
| 251 |
+
compute the guiding center radius.
|
| 252 |
+
|
| 253 |
+
- Added a way to convert Gala potential instances to Agama potential instances.
|
| 254 |
+
|
| 255 |
+
Bug fixes
|
| 256 |
+
---------
|
| 257 |
+
|
| 258 |
+
- Fixed a bug with the ``plot_contours()`` and ``plot_density_contours()`` methods so
|
| 259 |
+
that times specified are now passed through correctly to the potential methods.
|
| 260 |
+
|
| 261 |
+
- Fixed the YAML output to use ``default_flow_style=None`` for serializing potential
|
| 262 |
+
objects, which leads to a more efficient array output.
|
| 263 |
+
|
| 264 |
+
- ``scf.compute_coeffs_discrete`` now raises an error if GSL is not enabled rather than
|
| 265 |
+
silently returning zeros
|
| 266 |
+
|
| 267 |
+
- ``SCFPotential`` will now work with IO functions (``save`` & ``load``)
|
| 268 |
+
|
| 269 |
+
- Fixes compatibility with Astropy v6.0
|
| 270 |
+
|
| 271 |
+
API changes
|
| 272 |
+
-----------
|
| 273 |
+
|
| 274 |
+
- Changed the way potential interoperability is done with other Galactic dynamics
|
| 275 |
+
packages (Agama, galpy, etc.). It is now handled by the ``Potential.as_interop()``
|
| 276 |
+
method on all potential class instances.
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
1.7.1 (2023-08-05)
|
| 280 |
+
==================
|
| 281 |
+
|
| 282 |
+
- Switched build system to use pyproject.toml instead of setup.cfg
|
| 283 |
+
|
| 284 |
+
1.7 (2023-08-05)
|
| 285 |
+
================
|
| 286 |
+
|
| 287 |
+
New Features
|
| 288 |
+
------------
|
| 289 |
+
|
| 290 |
+
- Added a method to export the internal components of an
|
| 291 |
+
``MN3ExponentialDiskPotential()`` to three ``MiyamotoNagaiPotential`` instances.
|
| 292 |
+
|
| 293 |
+
- Added a new Milky Way potential model: ``MilkyWayPotential2022``, which is based on
|
| 294 |
+
updated measurements of the disk structure and circular velocity curve of the disk.
|
| 295 |
+
|
| 296 |
+
- Added the ability to use leapfrog integration within the ``DirectNBody`` integrator.
|
| 297 |
+
|
| 298 |
+
- Added a new coordinate frame for the Vasiliev+2021 Sagittarius stream coordinate
|
| 299 |
+
system, ``SagittariusVasiliev21``.
|
| 300 |
+
|
| 301 |
+
Bug fixes
|
| 302 |
+
---------
|
| 303 |
+
|
| 304 |
+
- Fixed a bug with the ``OrphanKoposov19()`` coordinate frame that caused the wrong
|
| 305 |
+
rotation matrix to be returned.
|
| 306 |
+
|
| 307 |
+
- Fixed an ``AstropyDeprecationWarning`` resulting from the use of ``override__dir__``.
|
| 308 |
+
|
| 309 |
+
- Fixed a bug in ``Orbit.estimate_period()`` that would cause the method to fail with a
|
| 310 |
+
``UnitsError`` if one orbit returned a nan value for the period.
|
| 311 |
+
|
| 312 |
+
- Fixed a bug when compiling the ``dop853`` integrator.
|
| 313 |
+
|
| 314 |
+
API changes
|
| 315 |
+
-----------
|
| 316 |
+
|
| 317 |
+
- Refactored the way ``GreatCircleICRSFrame()`` works to be more consistent and
|
| 318 |
+
unambiguous with coordinate frame definitions. The frame now requires an input pole
|
| 319 |
+
and origin, but can be initialized in old ways using the ``from_*()`` class methods
|
| 320 |
+
(e.g., with ``pole`` and ``ra0`` values).
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
1.6.1 (2022-11-07)
|
| 324 |
+
==================
|
| 325 |
+
|
| 326 |
+
Bug fixes
|
| 327 |
+
---------
|
| 328 |
+
|
| 329 |
+
- Properly incorporate commits related to ``SCFInterpolatedPotential``.
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
1.6 (2022-11-07)
|
| 333 |
+
================
|
| 334 |
+
|
| 335 |
+
New Features
|
| 336 |
+
------------
|
| 337 |
+
|
| 338 |
+
- Added a ``.replicate()`` method to Potential classes to enable copying
|
| 339 |
+
potential objects but modifying some parameter values.
|
| 340 |
+
|
| 341 |
+
- Added a new potential class ``MN3ExponentialDiskPotential`` based on Smith et
|
| 342 |
+
al. (2015): an approximation of the potential generated by a double
|
| 343 |
+
exponential disk using a sum of three Miyamoto-Nagai disks.
|
| 344 |
+
|
| 345 |
+
- The ``Orbit.estimate_period()`` method now returns period estimates in all
|
| 346 |
+
phase-space components instead of just the radial period.
|
| 347 |
+
|
| 348 |
+
- Added a ``store_all`` flag to the integrators to control whether to save
|
| 349 |
+
phase-space information for all timesteps or only the final timestep.
|
| 350 |
+
|
| 351 |
+
- Added a ``plot_rotation_curve()`` method to all potential objects to make a 1D plot
|
| 352 |
+
of the circular velocity curve.
|
| 353 |
+
|
| 354 |
+
- Added a new potential for representing multipole expansions ``MultipolePotential``.
|
| 355 |
+
|
| 356 |
+
- Added a new potential ``CylSplinePotential`` for flexible representation of
|
| 357 |
+
axisymmetric potentials by allowing passing in grids of potential values
|
| 358 |
+
evaluated grids of R, z values (like the ``CylSpline`` potential in Agama).
|
| 359 |
+
|
| 360 |
+
- Added a ``show_time`` flag to ``Orbit.animate()`` to control whether to show the
|
| 361 |
+
current timestep.
|
| 362 |
+
|
| 363 |
+
- Changed ``Orbit.animate()`` to allow for different ``marker_style`` and
|
| 364 |
+
``segment_style`` options for individual orbits by passing a list of dicts instead
|
| 365 |
+
of just a dict.
|
| 366 |
+
|
| 367 |
+
- Added an experimental new class ``SCFInterpolatedPotential`` that accepts a time
|
| 368 |
+
series of coefficients and interpolates the coefficient values to any evaluation time.
|
| 369 |
+
|
| 370 |
+
Bug fixes
|
| 371 |
+
---------
|
| 372 |
+
|
| 373 |
+
- Fixed a bug where the ``NFWPotential`` energy was nan when evaluating at the
|
| 374 |
+
origin, and added tests for all potentials to check for a finite value of the
|
| 375 |
+
potential at the origin (when expected).
|
| 376 |
+
|
| 377 |
+
- Fixed a bug in ``NFWPotential.from_M200_c()`` where the incorrect scale radius
|
| 378 |
+
was computed (Cython does not always use Python 3 division rules for dividing
|
| 379 |
+
integers!).
|
| 380 |
+
|
| 381 |
+
- Fixed a bug in the (C-level/internal) estimation of the 2nd derivative of the
|
| 382 |
+
potential, used to generate mock streams, that affects non-conservative force
|
| 383 |
+
fields.
|
| 384 |
+
|
| 385 |
+
API changes
|
| 386 |
+
-----------
|
| 387 |
+
|
| 388 |
+
- The ``Orbit.estimate_period()`` method now returns period estimates in all
|
| 389 |
+
phase-space components instead of just the radial period.
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
1.5 (2022-03-03)
|
| 393 |
+
================
|
| 394 |
+
|
| 395 |
+
New Features
|
| 396 |
+
------------
|
| 397 |
+
|
| 398 |
+
- Implemented a basic progress bar for integrating orbits and mock streams. Pass
|
| 399 |
+
``progress=True`` with ``Integrator_kwargs`` when calling
|
| 400 |
+
``.integrate_orbit()``, or pass ``progress=True`` to
|
| 401 |
+
``MockStreamGenerator.run()``.
|
| 402 |
+
|
| 403 |
+
- Added a new symplectic integrator: The Ruth 4th-order integrator, implemented
|
| 404 |
+
with the class ``Ruth4Integrator``.
|
| 405 |
+
|
| 406 |
+
- Added a ``Orbit.animate()`` method to make ``matplotlib`` animations of
|
| 407 |
+
orbits.
|
| 408 |
+
|
| 409 |
+
- Modified ``Orbit._max_helper()`` to use a parabola instead of interpolation
|
| 410 |
+
|
| 411 |
+
- Added functionality to transform from action-angle coordinates to Cartesian
|
| 412 |
+
position velocity coordinates in the Isochrone potential:
|
| 413 |
+
``gala.dynamics.actionangle.isochrone_aa_to_xv()``.
|
| 414 |
+
|
| 415 |
+
- Added a new method on ``DirectNBody`` to enable computing the instantaneous,
|
| 416 |
+
mutual, N-body acceleration vectors ``DirectNBody.acceleration()``.
|
| 417 |
+
|
| 418 |
+
Bug fixes
|
| 419 |
+
---------
|
| 420 |
+
|
| 421 |
+
- Fixed ``find_actions()`` to accept an ``Orbit`` instance with multiple orbits.
|
| 422 |
+
|
| 423 |
+
- Fixed a bug that appeared when trying to release all mock stream particles at
|
| 424 |
+
the same timestep (e.g., pericenter).
|
| 425 |
+
|
| 426 |
+
- Fixed a bug where time arrays returned from ``parse_time_specification``
|
| 427 |
+
could come back with a non-float64 dtype.
|
| 428 |
+
|
| 429 |
+
- Fixed a bug with ``DirectNBody`` with composite potentials where only the
|
| 430 |
+
first potential component would move as a body / particle.
|
| 431 |
+
|
| 432 |
+
- Fixed a bug with the Python implementation of Leapfrog integration
|
| 433 |
+
``LeapfrogIntegrator`` that led to incorrect orbits for non-conservative
|
| 434 |
+
systems that were integrated backwards (i.e. with ``dt<<0``).
|
| 435 |
+
|
| 436 |
+
- Fixed a bug with the ``FlattenedNFW`` potential class in which the energy and
|
| 437 |
+
gradient functions were not using the inputted flattening (``c`` value) and
|
| 438 |
+
were instead defaulting to the spherical NFW model.
|
| 439 |
+
|
| 440 |
+
- Enabled pickling ``Frame`` instances and therefore now ``Hamiltonian``
|
| 441 |
+
instances.
|
| 442 |
+
|
| 443 |
+
- Fixed a bug with ``autolim=True`` during Orbit plotting where the axes limits
|
| 444 |
+
were only dependent on the most recent Orbit rather than all that were present
|
| 445 |
+
on the axis
|
| 446 |
+
|
| 447 |
+
API changes
|
| 448 |
+
-----------
|
| 449 |
+
|
| 450 |
+
- Renamed ``gala.dynamics.actionangle.isochrone_to_aa()`` to
|
| 451 |
+
``gala.dynamics.actionangle.isochrone_xv_to_aa()``
|
| 452 |
+
|
| 453 |
+
- Renamed ``gala.dynamics.actionangle.find_actions()`` to
|
| 454 |
+
``gala.dynamics.actionangle.find_actions_o2gf()``
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
1.4.1 (2021-07-01)
|
| 458 |
+
==================
|
| 459 |
+
|
| 460 |
+
- Fixed a RST bug that caused the README to fail to render.
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
1.4 (2021-07-01)
|
| 464 |
+
================
|
| 465 |
+
|
| 466 |
+
New Features
|
| 467 |
+
------------
|
| 468 |
+
|
| 469 |
+
- ``UnitSystem`` objects can now be created with custom units passed in as
|
| 470 |
+
Astropy ``Quantity`` objects.
|
| 471 |
+
|
| 472 |
+
- Added functionality to convert Gala potential objects to Galpy potential
|
| 473 |
+
objects, or to create Gala potential objects from a pre-existing Galpy
|
| 474 |
+
potential.
|
| 475 |
+
|
| 476 |
+
- Added a ``plot_3d()`` method for ``Orbit`` objects to make 3D plots of the
|
| 477 |
+
orbital trajectories.
|
| 478 |
+
|
| 479 |
+
Bug fixes
|
| 480 |
+
---------
|
| 481 |
+
|
| 482 |
+
- Fixed a bug when calling ``orbit.norbits`` when the representation is not
|
| 483 |
+
cartesian.
|
| 484 |
+
|
| 485 |
+
- Fixed a bug with ``GreatCircleICRSFrame.from_endpoints()`` that caused an
|
| 486 |
+
error when the input coordinates had associated velocity data.
|
| 487 |
+
|
| 488 |
+
- Fixed a bug with the ``JaffePotential`` density evaluation, which was too low
|
| 489 |
+
by a factor of two.
|
| 490 |
+
|
| 491 |
+
- Implemented a density function for ``LogarithmicPotential``, which was
|
| 492 |
+
missing previously.
|
| 493 |
+
|
| 494 |
+
- The analytic action-angle and ``find_actions()`` utilities now correctly
|
| 495 |
+
return frequencies with angular frequency units rather than frequency.
|
| 496 |
+
|
| 497 |
+
API changes
|
| 498 |
+
-----------
|
| 499 |
+
|
| 500 |
+
- Removed the deprecated ``gala.coordinates.get_galactocentric2019()`` function.
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
1.3 (2020-10-27)
|
| 504 |
+
================
|
| 505 |
+
|
| 506 |
+
New Features
|
| 507 |
+
------------
|
| 508 |
+
|
| 509 |
+
- Added a new ``.to_sympy()`` classmethod for the ``Potential`` classes to
|
| 510 |
+
return a sympy expression and variables.
|
| 511 |
+
|
| 512 |
+
- Added a method, ``.to_galpy_orbit()``, to convert Gala ``Orbit`` instances to
|
| 513 |
+
Galpy ``Orbit`` objects.
|
| 514 |
+
|
| 515 |
+
- The ``NFWPotential`` can now be instantiated via a new classmethod:
|
| 516 |
+
``NFWPotential.from_M200_c()``, which accepts a virial mass and a
|
| 517 |
+
concentration.
|
| 518 |
+
|
| 519 |
+
- Added a fast way of computing the Staeckel focal length, ``Delta``, using
|
| 520 |
+
Gala potential classes, ``gala.dynamics.get_staeckel_fudge_delta``
|
| 521 |
+
|
| 522 |
+
Bug fixes
|
| 523 |
+
---------
|
| 524 |
+
|
| 525 |
+
- Fixed a bug with ``Potential`` classes ``.replace_units()`` so that classes
|
| 526 |
+
with dimensionless unit systems cannot be replaced with physical unit systems,
|
| 527 |
+
and vice versa.
|
| 528 |
+
|
| 529 |
+
- Implemented Hessian functions for most potentials.
|
| 530 |
+
|
| 531 |
+
- Fixed ``.to_latex()`` to properly return a latex representation of the
|
| 532 |
+
potential. This uses the new ``.to_sympy()`` method under the hood.
|
| 533 |
+
|
| 534 |
+
- Potential classes now validate that input positions have dimensionality that
|
| 535 |
+
matches what is expected for each potential.
|
| 536 |
+
|
| 537 |
+
API changes
|
| 538 |
+
-----------
|
| 539 |
+
|
| 540 |
+
- Changed the way new ``Potential`` classes are defined: they now rely on
|
| 541 |
+
defining class-level ``PotentialParameter`` objects, which reduces a
|
| 542 |
+
significant amount of boilerplate code in the built-in potentials.
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
1.2 (2020-07-13)
|
| 546 |
+
================
|
| 547 |
+
|
| 548 |
+
- Gala now builds on Windows!
|
| 549 |
+
|
| 550 |
+
New Features
|
| 551 |
+
------------
|
| 552 |
+
|
| 553 |
+
- Added a coordinate frame for the Pal 13 stream, ``Pal13Shipp20``.
|
| 554 |
+
|
| 555 |
+
Bug fixes
|
| 556 |
+
---------
|
| 557 |
+
|
| 558 |
+
- Fixed a bug with the mock stream machinery in which the stream would not
|
| 559 |
+
integrate for the specified number of timesteps if an array of
|
| 560 |
+
``n_particles`` was passed in with 0's near the end of the array.
|
| 561 |
+
|
| 562 |
+
|
| 563 |
+
1.1 (2020-03-08)
|
| 564 |
+
================
|
| 565 |
+
|
| 566 |
+
New Features
|
| 567 |
+
------------
|
| 568 |
+
- Potential objects now support replacing the unit system with the
|
| 569 |
+
``.replace_units()`` method, or by updating the ``.units`` attribute on an
|
| 570 |
+
existing instance.
|
| 571 |
+
- Added a ``DirectNBody`` class that supports direct N-body orbit integration in
|
| 572 |
+
(optional) external background potentials.
|
| 573 |
+
- Added a coordinate frame for the Jhelum stream, ``JhelumBonaca19``.
|
| 574 |
+
- Added a function for retrieving a more modern Galactocentric reference frame,
|
| 575 |
+
``gala.coordinates.get_galactocentric2019()``.
|
| 576 |
+
- Added a classmethod to allow initializing a ``GreatCircleICRSFrame`` from a
|
| 577 |
+
rotation matrix that specifies the transformation from ``ICRS`` to the great
|
| 578 |
+
circle frame.
|
| 579 |
+
|
| 580 |
+
Bug fixes
|
| 581 |
+
---------
|
| 582 |
+
- Fixed an issue that led to incorrect ``GreatCircleICRSFrame`` transformations
|
| 583 |
+
when no ``ra0`` was provided.
|
| 584 |
+
- Fixed a bug in the ``OrphanKoposov19`` transformation.
|
| 585 |
+
|
| 586 |
+
API changes
|
| 587 |
+
-----------
|
| 588 |
+
- Overhauled the mock stellar stream generation methodology to allow for more
|
| 589 |
+
general stream generation. See ``MockStreamGenerator`` and the stream
|
| 590 |
+
distribution function classes, e.g., ``FardalStreamDF``.
|
| 591 |
+
- Removed deprecated ``CartesianPhaseSpacePosition`` class.
|
| 592 |
+
- Removed long deprecated ``Quaternion`` class.
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
1.0 (2019-04-12)
|
| 596 |
+
================
|
| 597 |
+
|
| 598 |
+
New Features
|
| 599 |
+
------------
|
| 600 |
+
- Added a new coordinate frame for great circle coordinate systems defined by a
|
| 601 |
+
pole. This frame can be created with a pole and origin, a pole and longitude
|
| 602 |
+
zero point, by two points along a great circle, or by specifying the cartesian
|
| 603 |
+
basis vectors of the new frame.
|
| 604 |
+
- Added a function to transform a proper motion covariance matrix to a new
|
| 605 |
+
coordinate frame.
|
| 606 |
+
- Added support for compiling Gala with or without the GNU Scientific Library
|
| 607 |
+
(GSL), which is needed for the new potential classes indicated below.
|
| 608 |
+
- Added a new ``PowerLawCutoffPotential`` class for a power-law density
|
| 609 |
+
distribution with an exponential cutoff *(requires GSL)*.
|
| 610 |
+
- Added an implementation of the ``MWPotential2014`` from ``galpy`` (called
|
| 611 |
+
``BovyMWPotential2014`` in ``gala``) *(requires GSL)*.
|
| 612 |
+
- Added an implementation of the Self-Consistent Field (SCF) basis function
|
| 613 |
+
expansion method for representing potential-density pairs *(requires GSL)*.
|
| 614 |
+
- Most Potential classes now support rotations and origin shifts through the
|
| 615 |
+
``R`` and ``origin`` arguments.
|
| 616 |
+
- Added a ``progress`` argument to the Python integrators to display a progress
|
| 617 |
+
bar when stepping the integrators.
|
| 618 |
+
- When generating mock stellar streams and storing snapshots (rather than just
|
| 619 |
+
the final phase-space positions of the particles) now supports specifying the
|
| 620 |
+
snapshot frequency with the ``output_every`` argument.
|
| 621 |
+
|
| 622 |
+
Bug fixes
|
| 623 |
+
---------
|
| 624 |
+
- Stream frames now properly wrap the longitude (``phi1``) components to the
|
| 625 |
+
range (-180, 180) deg.
|
| 626 |
+
|
| 627 |
+
API changes
|
| 628 |
+
-----------
|
| 629 |
+
- Stream classes have been renamed to reflect the author that defined them.
|
| 630 |
+
- Proper motion and coordinate velocity transformations have now been removed in
|
| 631 |
+
favor of the implementations in Astropy.
|
| 632 |
+
- Added a ``.data`` attribute to ``PhaseSpacePosition`` objects that returns a
|
| 633 |
+
``Representation`` or ``NDRepresentation`` instance with velocity data
|
| 634 |
+
(differentials) attached.
|
| 635 |
+
|
| 636 |
+
0.3 (2018-04-23)
|
| 637 |
+
================
|
| 638 |
+
|
| 639 |
+
New Features
|
| 640 |
+
------------
|
| 641 |
+
|
| 642 |
+
- Added a ``NullPotential`` class that has 0 mass and serves as a placeholder.
|
| 643 |
+
- Added a new ``zmax()`` method on the ``Orbit`` class to compute the maximum z
|
| 644 |
+
heights and times, or the mean maximum z height. Similar to ``apocenter()``
|
| 645 |
+
and ``pericenter()``.
|
| 646 |
+
- Added a new generator method on the ``Orbit`` class for easy iteration over
|
| 647 |
+
orbits.
|
| 648 |
+
|
| 649 |
+
Bug fixes
|
| 650 |
+
---------
|
| 651 |
+
|
| 652 |
+
- ``Orbit.norbits`` now works...oops.
|
| 653 |
+
- ``apocenter()`` and ``pericenter()`` now work when more than one orbit is
|
| 654 |
+
stored in an ``Orbit`` class.
|
| 655 |
+
|
| 656 |
+
0.2.2 (2017-10-07)
|
| 657 |
+
==================
|
| 658 |
+
|
| 659 |
+
New features
|
| 660 |
+
------------
|
| 661 |
+
- Added a new coordinate frame aligned with the Palomar 5 stream.
|
| 662 |
+
- Added a function ``gala.dynamics.combine`` to combine ``PhaseSpacePosition``
|
| 663 |
+
or ``Orbit`` objects.
|
| 664 |
+
|
| 665 |
+
Bug fixes
|
| 666 |
+
---------
|
| 667 |
+
- Added a density function for the Kepler potential.
|
| 668 |
+
- Added a density function for the Long & Murali bar potential
|
| 669 |
+
|
| 670 |
+
Other changes
|
| 671 |
+
-------------
|
| 672 |
+
- Added JOSS paper.
|
| 673 |
+
- Cleaned up some tests and documentation to use the ``Hamiltonian`` object.
|
| 674 |
+
|
| 675 |
+
0.2.1 (2017-07-19)
|
| 676 |
+
==================
|
| 677 |
+
|
| 678 |
+
Bug fixes
|
| 679 |
+
---------
|
| 680 |
+
- Array parameters are now numpy.ravel'd before being passed to the
|
| 681 |
+
``CPotentialWrapper`` class methods.
|
| 682 |
+
- Added attribution to Bovy 2015 for disk potential in MilkyWayPotential
|
| 683 |
+
|
| 684 |
+
0.2 (2017-07-15)
|
| 685 |
+
================
|
| 686 |
+
|
| 687 |
+
New Features
|
| 688 |
+
------------
|
| 689 |
+
- Added a new potential class for the Satoh density (Satoh 1980).
|
| 690 |
+
- Added support for Leapfrog integration when generating mock stellar streams.
|
| 691 |
+
- Added new colormaps and defaults for the matplotlib style.
|
| 692 |
+
- Added support for non-inertial reference frames and implemented a constant
|
| 693 |
+
rotating reference frame.
|
| 694 |
+
- Added a new class - ``Hamiltonian`` - for storing potentials with reference
|
| 695 |
+
frames. This should be used for easy orbit integration instead of the
|
| 696 |
+
potential classes.
|
| 697 |
+
- Added a new argument to the mock stream generation functions t output orbits
|
| 698 |
+
of all of the mock stream star particles to an HDF5 file.
|
| 699 |
+
- Cleaned up and simplified the process of subclassing a C-implemented
|
| 700 |
+
gravitational potential.
|
| 701 |
+
- Gravitational potential class instances can now be composed by just adding the
|
| 702 |
+
instances.
|
| 703 |
+
- Added a ``MilkyWayPotential`` class.
|
| 704 |
+
|
| 705 |
+
API-breaking changes
|
| 706 |
+
--------------------
|
| 707 |
+
- ``CartesianPhaseSpacePosition`` and ``CartesianOrbit`` are deprecated. Use
|
| 708 |
+
``PhaseSpacePosition`` and ``Orbit`` with a Cartesian representation instead.
|
| 709 |
+
- Overhauled the storage of position and velocity information on
|
| 710 |
+
``PhaseSpacePosition`` and ``Orbit`` classes. This uses new features in
|
| 711 |
+
Astropy 2.0 that allow attaching "differential" classes to representation
|
| 712 |
+
classes for storing velocity information. ``.pos`` and ``.vel`` no longer
|
| 713 |
+
point to arrays of Cartesian coordinates, but now instead point to
|
| 714 |
+
astropy.coordinates representation and differential objects, respectively.
|
| 715 |
+
|
| 716 |
+
0.1.1 (2016-05-20)
|
| 717 |
+
==================
|
| 718 |
+
|
| 719 |
+
- Removed debug statement.
|
| 720 |
+
- Added 'Why' page to documentation.
|
| 721 |
+
|
| 722 |
+
0.1.0 (2016-05-19)
|
| 723 |
+
==================
|
| 724 |
+
|
| 725 |
+
- Initial release.
|
gala/source/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
All `gala` community members are expected to abide by the
|
| 2 |
+
[Astropy Project Code of Conduct](http://www.astropy.org/code_of_conduct.html).
|
gala/source/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
The MIT License (MIT)
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2012-2024 Adrian M. Price-Whelan
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
gala/source/README.rst
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|logo|
|
| 2 |
+
|
| 3 |
+
Gala is a Python package for Galactic and gravitational dynamics.
|
| 4 |
+
|
| 5 |
+
|Affiliated package| |Coverage Status| |Build status|
|
| 6 |
+
|
| 7 |
+
Documentation
|
| 8 |
+
-------------
|
| 9 |
+
|
| 10 |
+
|Documentation Status|
|
| 11 |
+
|
| 12 |
+
The documentation for ``Gala`` is hosted on `Read the docs
|
| 13 |
+
<http://gala.adrian.pw>`__.
|
| 14 |
+
|
| 15 |
+
Installation and Dependencies
|
| 16 |
+
-----------------------------
|
| 17 |
+
|
| 18 |
+
|PyPI|
|
| 19 |
+
|
| 20 |
+
The easiest way to get Gala is to install with pip or uv.
|
| 21 |
+
|
| 22 |
+
Gala can be installed with ``pip`` (with ``uv`` or standalone)::
|
| 23 |
+
|
| 24 |
+
pip install gala
|
| 25 |
+
|
| 26 |
+
You can also add ``gala`` as a dependency to your environment with::
|
| 27 |
+
|
| 28 |
+
uv add gala
|
| 29 |
+
|
| 30 |
+
See the `installation
|
| 31 |
+
instructions <http://gala.adrian.pw/en/latest/install.html>`_ in the
|
| 32 |
+
`documentation <http://gala.adrian.pw>`__ for more information.
|
| 33 |
+
|
| 34 |
+
Attribution
|
| 35 |
+
-----------
|
| 36 |
+
|
| 37 |
+
|JOSS| |DOI|
|
| 38 |
+
|
| 39 |
+
If you make use of this code, please cite the `JOSS <http://joss.theoj.org>`_
|
| 40 |
+
paper::
|
| 41 |
+
|
| 42 |
+
@article{gala,
|
| 43 |
+
doi = {10.21105/joss.00388},
|
| 44 |
+
url = {https://doi.org/10.21105%2Fjoss.00388},
|
| 45 |
+
year = 2017,
|
| 46 |
+
month = {oct},
|
| 47 |
+
publisher = {The Open Journal},
|
| 48 |
+
volume = {2},
|
| 49 |
+
number = {18},
|
| 50 |
+
author = {Adrian M. Price-Whelan},
|
| 51 |
+
title = {Gala: A Python package for galactic dynamics},
|
| 52 |
+
journal = {The Journal of Open Source Software}
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
Please also cite the Zenodo DOI |DOI| as a software citation - see the
|
| 56 |
+
`documentation
|
| 57 |
+
<http://gala.adrian.pw/en/latest/index.html#citation-and-attribution>`_ for up
|
| 58 |
+
to date citation information.
|
| 59 |
+
|
| 60 |
+
License
|
| 61 |
+
-------
|
| 62 |
+
|
| 63 |
+
|License|
|
| 64 |
+
|
| 65 |
+
Copyright 2013-2025 Adrian Price-Whelan and contributors.
|
| 66 |
+
|
| 67 |
+
``Gala`` is free software made available under the MIT License. For details see
|
| 68 |
+
the `LICENSE <https://github.com/adrn/gala/blob/main/LICENSE>`_ file.
|
| 69 |
+
|
| 70 |
+
.. |Coverage Status| image:: https://codecov.io/gh/adrn/gala/branch/main/graph/badge.svg
|
| 71 |
+
:target: https://codecov.io/gh/adrn/gala
|
| 72 |
+
.. |Build status| image:: https://github.com/adrn/gala/actions/workflows/tests.yml/badge.svg
|
| 73 |
+
:target: https://github.com/adrn/gala/actions/workflows/tests.yml
|
| 74 |
+
.. |License| image:: http://img.shields.io/badge/license-MIT-blue.svg?style=flat
|
| 75 |
+
:target: https://github.com/adrn/gala/blob/main/LICENSE
|
| 76 |
+
.. |PyPI| image:: https://badge.fury.io/py/gala.svg
|
| 77 |
+
:target: https://badge.fury.io/py/gala
|
| 78 |
+
.. |conda| image:: https://anaconda.org/conda-forge/gala/badges/version.svg
|
| 79 |
+
:target: https://anaconda.org/conda-forge/gala
|
| 80 |
+
.. |Documentation Status| image:: https://readthedocs.org/projects/gala-astro/badge/?version=latest
|
| 81 |
+
:target: http://gala-astro.readthedocs.io/en/latest/?badge=latest
|
| 82 |
+
.. |Affiliated package| image:: https://img.shields.io/badge/astropy-affiliated%20package-orange.svg
|
| 83 |
+
:target: http://astropy.org/affiliated
|
| 84 |
+
.. |JOSS| image:: http://joss.theoj.org/papers/10.21105/joss.00388/status.svg
|
| 85 |
+
:target: http://joss.theoj.org/papers/10.21105/joss.00388
|
| 86 |
+
.. |DOI| image:: https://zenodo.org/badge/17577779.svg
|
| 87 |
+
:target: https://zenodo.org/badge/latestdoi/17577779
|
| 88 |
+
.. |ASCL| image:: https://img.shields.io/badge/ascl-1707.006-blue.svg?colorB=262255
|
| 89 |
+
:target: http://ascl.net/1707.006
|
| 90 |
+
.. |logo| image:: https://gala.adrian.pw/en/latest/_static/Gala_Logo_RGB.png
|
| 91 |
+
:target: https://github.com/adrn/gala
|
| 92 |
+
:width: 400
|
| 93 |
+
|
| 94 |
+
Contributors
|
| 95 |
+
------------
|
| 96 |
+
|
| 97 |
+
See the `AUTHORS.rst <https://github.com/adrn/gala/blob/main/AUTHORS.rst>`_
|
| 98 |
+
file for a complete list of contributors to the project.
|
gala/source/__init__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""
|
| 3 |
+
gala Project Package Initialization File
|
| 4 |
+
"""
|
gala/source/codemeta.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"@context": "https://doi.org/10.5063/schema/codemeta-2.0",
|
| 3 |
+
"@type": "SoftwareSourceCode",
|
| 4 |
+
"name": "Gala: Galactic astronomy and gravitational dynamics",
|
| 5 |
+
"description": "Gala is an Astropy-affiliated Python package for galactic dynamics. Python enables wrapping low-level languages (e.g., C) for speed without losing flexibility or ease-of-use in the user-interface. The API for Gala was designed to provide a class-based and user-friendly interface to fast (C or Cython-optimized) implementations of common operations such as gravitational potential and force evaluation, orbit integration, dynamical transformations, and chaos indicators for nonlinear dynamics. Gala also relies heavily on and interfaces well with the implementations of physical units and astronomical coordinate systems in the Astropy package (astropy.units and astropy.coordinates).",
|
| 6 |
+
"identifier": "https://dx.doi.org/10.21105/joss.00388",
|
| 7 |
+
"author": [
|
| 8 |
+
{
|
| 9 |
+
"@type": "Person",
|
| 10 |
+
"givenName": "Adrian",
|
| 11 |
+
"familyName": "Price-Whelan",
|
| 12 |
+
"@id": "http://orcid.org/0000-0003-0872-7098"
|
| 13 |
+
}
|
| 14 |
+
],
|
| 15 |
+
"citation": "https://ui.adsabs.harvard.edu/abs/2017JOSS....2..388P/abstract",
|
| 16 |
+
"relatedLink": [
|
| 17 |
+
"https://gala.adrian.pw/",
|
| 18 |
+
"https://zenodo.org/record/4159870"
|
| 19 |
+
],
|
| 20 |
+
"codeRepository": ["https://github.com/adrn/gala"],
|
| 21 |
+
"version": "v1.3",
|
| 22 |
+
"license": "https://github.com/adrn/gala/blob/main/LICENSE"
|
| 23 |
+
}
|
gala/source/conftest.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import sys
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
|
| 5 |
+
from pytest_astropy_header.display import (
|
| 6 |
+
PYTEST_HEADER_MODULES,
|
| 7 |
+
TESTED_VERSIONS,
|
| 8 |
+
)
|
| 9 |
+
|
| 10 |
+
# Add test helpers to path so they can be imported
|
| 11 |
+
tests_dir = Path(__file__).parent
|
| 12 |
+
sys.path.insert(0, str(tests_dir))
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def pytest_configure(config):
|
| 16 |
+
config.option.astropy_header = True
|
| 17 |
+
PYTEST_HEADER_MODULES.pop("Pandas", None)
|
| 18 |
+
PYTEST_HEADER_MODULES["astropy"] = "astropy"
|
| 19 |
+
|
| 20 |
+
from gala import __version__
|
| 21 |
+
|
| 22 |
+
packagename = os.path.basename(os.path.dirname(__file__))
|
| 23 |
+
TESTED_VERSIONS[packagename] = __version__
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def pytest_report_header(config):
|
| 27 |
+
from gala._cconfig import EXP_ENABLED, GSL_ENABLED
|
| 28 |
+
|
| 29 |
+
hdr = []
|
| 30 |
+
if GSL_ENABLED:
|
| 31 |
+
hdr.append(" +++ Gala compiled with GSL +++")
|
| 32 |
+
else:
|
| 33 |
+
hdr.append(" --- Gala compiled without GSL ---")
|
| 34 |
+
|
| 35 |
+
if EXP_ENABLED:
|
| 36 |
+
hdr.append(" +++ Gala compiled with EXP +++")
|
| 37 |
+
else:
|
| 38 |
+
hdr.append(" --- Gala compiled without EXP ---")
|
| 39 |
+
hdr.append("")
|
| 40 |
+
|
| 41 |
+
return "\n".join(hdr)
|
gala/source/docs/Makefile
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Makefile for Sphinx documentation
|
| 2 |
+
#
|
| 3 |
+
|
| 4 |
+
# You can set these variables from the command line.
|
| 5 |
+
SPHINXOPTS =
|
| 6 |
+
SPHINXBUILD = sphinx-build
|
| 7 |
+
PAPER =
|
| 8 |
+
BUILDDIR = _build
|
| 9 |
+
|
| 10 |
+
# Internal variables.
|
| 11 |
+
PAPEROPT_a4 = -D latex_paper_size=a4
|
| 12 |
+
PAPEROPT_letter = -D latex_paper_size=letter
|
| 13 |
+
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
| 14 |
+
|
| 15 |
+
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
| 16 |
+
|
| 17 |
+
#This is needed with git because git doesn't create a dir if it's empty
|
| 18 |
+
$(shell [ -d "_static" ] || mkdir -p _static)
|
| 19 |
+
|
| 20 |
+
help:
|
| 21 |
+
@echo "Please use \`make <target>' where <target> is one of"
|
| 22 |
+
@echo " html to make standalone HTML files"
|
| 23 |
+
@echo " dirhtml to make HTML files named index.html in directories"
|
| 24 |
+
@echo " singlehtml to make a single large HTML file"
|
| 25 |
+
@echo " pickle to make pickle files"
|
| 26 |
+
@echo " json to make JSON files"
|
| 27 |
+
@echo " htmlhelp to make HTML files and a HTML help project"
|
| 28 |
+
@echo " qthelp to make HTML files and a qthelp project"
|
| 29 |
+
@echo " devhelp to make HTML files and a Devhelp project"
|
| 30 |
+
@echo " epub to make an epub"
|
| 31 |
+
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
| 32 |
+
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
| 33 |
+
@echo " text to make text files"
|
| 34 |
+
@echo " man to make manual pages"
|
| 35 |
+
@echo " changes to make an overview of all changed/added/deprecated items"
|
| 36 |
+
@echo " linkcheck to check all external links for integrity"
|
| 37 |
+
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
| 38 |
+
|
| 39 |
+
clean:
|
| 40 |
+
-rm -rf $(BUILDDIR)
|
| 41 |
+
-rm -rf api
|
| 42 |
+
-rm -rf tutorials/*.ipynb
|
| 43 |
+
|
| 44 |
+
html:
|
| 45 |
+
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
| 46 |
+
@echo
|
| 47 |
+
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
| 48 |
+
|
| 49 |
+
dirhtml:
|
| 50 |
+
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
| 51 |
+
@echo
|
| 52 |
+
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
| 53 |
+
|
| 54 |
+
singlehtml:
|
| 55 |
+
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
| 56 |
+
@echo
|
| 57 |
+
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
| 58 |
+
|
| 59 |
+
pickle:
|
| 60 |
+
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
| 61 |
+
@echo
|
| 62 |
+
@echo "Build finished; now you can process the pickle files."
|
| 63 |
+
|
| 64 |
+
json:
|
| 65 |
+
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
| 66 |
+
@echo
|
| 67 |
+
@echo "Build finished; now you can process the JSON files."
|
| 68 |
+
|
| 69 |
+
htmlhelp:
|
| 70 |
+
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
| 71 |
+
@echo
|
| 72 |
+
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
| 73 |
+
".hhp project file in $(BUILDDIR)/htmlhelp."
|
| 74 |
+
|
| 75 |
+
qthelp:
|
| 76 |
+
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
| 77 |
+
@echo
|
| 78 |
+
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
| 79 |
+
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
| 80 |
+
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Astropy.qhcp"
|
| 81 |
+
@echo "To view the help file:"
|
| 82 |
+
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Astropy.qhc"
|
| 83 |
+
|
| 84 |
+
devhelp:
|
| 85 |
+
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
| 86 |
+
@echo
|
| 87 |
+
@echo "Build finished."
|
| 88 |
+
@echo "To view the help file:"
|
| 89 |
+
@echo "# mkdir -p $$HOME/.local/share/devhelp/Astropy"
|
| 90 |
+
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Astropy"
|
| 91 |
+
@echo "# devhelp"
|
| 92 |
+
|
| 93 |
+
epub:
|
| 94 |
+
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
| 95 |
+
@echo
|
| 96 |
+
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
| 97 |
+
|
| 98 |
+
latex:
|
| 99 |
+
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
| 100 |
+
@echo
|
| 101 |
+
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
| 102 |
+
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
| 103 |
+
"(use \`make latexpdf' here to do that automatically)."
|
| 104 |
+
|
| 105 |
+
latexpdf:
|
| 106 |
+
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
| 107 |
+
@echo "Running LaTeX files through pdflatex..."
|
| 108 |
+
make -C $(BUILDDIR)/latex all-pdf
|
| 109 |
+
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
| 110 |
+
|
| 111 |
+
text:
|
| 112 |
+
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
| 113 |
+
@echo
|
| 114 |
+
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
| 115 |
+
|
| 116 |
+
man:
|
| 117 |
+
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
| 118 |
+
@echo
|
| 119 |
+
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
| 120 |
+
|
| 121 |
+
changes:
|
| 122 |
+
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
| 123 |
+
@echo
|
| 124 |
+
@echo "The overview file is in $(BUILDDIR)/changes."
|
| 125 |
+
|
| 126 |
+
linkcheck:
|
| 127 |
+
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
| 128 |
+
@echo
|
| 129 |
+
@echo "Link check complete; look for any errors in the above output " \
|
| 130 |
+
"or in $(BUILDDIR)/linkcheck/output.txt."
|
| 131 |
+
|
| 132 |
+
doctest:
|
| 133 |
+
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
| 134 |
+
@echo "Testing of doctests in the sources finished, look at the " \
|
| 135 |
+
"results in $(BUILDDIR)/doctest/output.txt."
|
| 136 |
+
|
| 137 |
+
exectutorials:
|
| 138 |
+
jupytext --to ipynb --execute tutorials/*.py
|
| 139 |
+
@echo "Finished executing tutorial notebooks. Look at the executed " \
|
| 140 |
+
"notebooks in tutorials/"
|
| 141 |
+
|
| 142 |
+
execsupporting:
|
| 143 |
+
jupytext --to ipynb --execute supporting/*.py
|
| 144 |
+
@echo "Finished executing tutorial notebooks. Look at the executed " \
|
| 145 |
+
"notebooks in supporting/"
|
| 146 |
+
|
| 147 |
+
animations:
|
| 148 |
+
python _static_animations.py
|
| 149 |
+
@echo "Finished generating animation files"
|
gala/source/docs/_static/Gala_Logo_RGB.png
ADDED
|
gala/source/docs/_static/anim-prof.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:55fdb6e46d3637fe2264f55310905faeef1b10dd4117b3ab917d7e5d728d7642
|
| 3 |
+
size 184605
|
gala/source/docs/_static/gala.css
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
span#logotext2 {
|
| 2 |
+
color: #764099;
|
| 3 |
+
}
|
| 4 |
+
|
| 5 |
+
/* Taken from NumPy */
|
| 6 |
+
|
| 7 |
+
@import url("https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;0,900;1,400;1,700;1,900&family=Open+Sans:ital,wght@0,400;0,600;1,400;1,600&display=swap");
|
| 8 |
+
|
| 9 |
+
.navbar-brand img {
|
| 10 |
+
height: 60px;
|
| 11 |
+
}
|
| 12 |
+
.navbar-brand {
|
| 13 |
+
height: 75px;
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
body {
|
| 17 |
+
font-family: "Open Sans", sans-serif;
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
pre,
|
| 21 |
+
code {
|
| 22 |
+
font-size: 100%;
|
| 23 |
+
line-height: 155%;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
div.output_area div[class*="highlight"] pre {
|
| 27 |
+
white-space: pre-wrap;
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
/* Make output lighter gray and no italics for the love of all that is holy! */
|
| 31 |
+
html[data-theme="light"] .highlight .go {
|
| 32 |
+
color: #555555;
|
| 33 |
+
font-style: normal;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
html[data-theme="dark"] .highlight .go {
|
| 37 |
+
font-style: normal;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
/* OMG why would you bold numbers */
|
| 41 |
+
.highlight .mf,
|
| 42 |
+
.highlight .mi {
|
| 43 |
+
font-weight: 300;
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
/* Override some aspects of the pydata-sphinx-theme: taken from Pandas */
|
| 47 |
+
|
| 48 |
+
:root {
|
| 49 |
+
/* Use softer blue from bootstrap's default info color */
|
| 50 |
+
/* --pst-color-info: 23, 162, 184; */
|
| 51 |
+
--pst-color-primary: 118, 63, 152;
|
| 52 |
+
--pst-color-success: 40, 167, 69;
|
| 53 |
+
--pst-color-info: 0, 123, 255;
|
| 54 |
+
--pst-color-warning: 255, 193, 7;
|
| 55 |
+
--pst-color-danger: 220, 53, 69;
|
| 56 |
+
--pst-color-text-base: 51, 51, 51;
|
| 57 |
+
|
| 58 |
+
--pst-font-size-base: 15px;
|
| 59 |
+
|
| 60 |
+
--pst-color-link: 118, 63, 152;
|
| 61 |
+
--pst-color-headerlink-hover: 118, 63, 152;
|
| 62 |
+
|
| 63 |
+
/* heading font sizes */
|
| 64 |
+
--pst-font-size-h1: 28px;
|
| 65 |
+
--pst-font-size-h2: 25px;
|
| 66 |
+
--pst-font-size-h3: 20px;
|
| 67 |
+
--pst-font-size-h4: 18px;
|
| 68 |
+
--pst-font-size-h5: 16px;
|
| 69 |
+
--pst-font-size-h6: 15px;
|
| 70 |
+
}
|
gala/source/docs/_static/m104.ico
ADDED
|
|
gala/source/docs/_static/orbit-anim1.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:13306d74201810520823f51478e8724c4d5cbc7b960ad9b1d939872c9a38319f
|
| 3 |
+
size 364400
|
gala/source/docs/_static/orbit-anim2.mp4
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ac8b5bbb111753d6b13d5918b889c9bf6a03a0dbeba75a1297f22cfc93260f8d
|
| 3 |
+
size 257682
|
gala/source/docs/_static_animations.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def make_orbit_animations(static_path):
|
| 2 |
+
# orbits-in-derail.rst
|
| 3 |
+
import astropy.units as u
|
| 4 |
+
|
| 5 |
+
import gala.dynamics as gd
|
| 6 |
+
import gala.potential as gp
|
| 7 |
+
from gala.units import galactic
|
| 8 |
+
|
| 9 |
+
file1 = static_path / "orbit-anim1.mp4"
|
| 10 |
+
file2 = static_path / "orbit-anim2.mp4"
|
| 11 |
+
|
| 12 |
+
if file1.exists() and file2.exists():
|
| 13 |
+
return
|
| 14 |
+
|
| 15 |
+
pot = gp.PlummerPotential(m=1e10 * u.Msun, b=1.0 * u.kpc, units=galactic)
|
| 16 |
+
w0 = gd.PhaseSpacePosition(pos=[2.0, 0, 0] * u.kpc, vel=[0.0, 75, 15] * u.km / u.s)
|
| 17 |
+
orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=1.0, n_steps=5000)
|
| 18 |
+
|
| 19 |
+
# animation 1:
|
| 20 |
+
fig, anim = orbit[:1000].animate(stride=10)
|
| 21 |
+
anim.save(file1)
|
| 22 |
+
|
| 23 |
+
# animation 2:
|
| 24 |
+
_fig, anim = orbit[:1000].cylindrical.animate(components=["rho", "z"], stride=10)
|
| 25 |
+
anim.save(file2)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
if __name__ == "__main__":
|
| 29 |
+
import pathlib
|
| 30 |
+
|
| 31 |
+
make_orbit_animations(pathlib.Path("./_static").resolve().absolute())
|
gala/source/docs/_templates/autosummary/base.rst
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% if referencefile %}
|
| 2 |
+
.. include:: {{ referencefile }}
|
| 3 |
+
{% endif %}
|
| 4 |
+
|
| 5 |
+
{{ objname }}
|
| 6 |
+
{{ underline }}
|
| 7 |
+
|
| 8 |
+
.. currentmodule:: {{ module }}
|
| 9 |
+
|
| 10 |
+
.. auto{{ objtype }}:: {{ objname }}
|
gala/source/docs/_templates/autosummary/class.rst
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% if referencefile %}
|
| 2 |
+
.. include:: {{ referencefile }}
|
| 3 |
+
{% endif %}
|
| 4 |
+
|
| 5 |
+
{{ objname }}
|
| 6 |
+
{{ underline }}
|
| 7 |
+
|
| 8 |
+
.. currentmodule:: {{ module }}
|
| 9 |
+
|
| 10 |
+
.. autoclass:: {{ objname }}
|
| 11 |
+
:show-inheritance:
|
| 12 |
+
|
| 13 |
+
{% if '__init__' in methods %}
|
| 14 |
+
{% set caught_result = methods.remove('__init__') %}
|
| 15 |
+
{% endif %}
|
| 16 |
+
|
| 17 |
+
{% block attributes_summary %}
|
| 18 |
+
{% if attributes %}
|
| 19 |
+
|
| 20 |
+
.. rubric:: Attributes Summary
|
| 21 |
+
|
| 22 |
+
.. autosummary::
|
| 23 |
+
{% for item in attributes %}
|
| 24 |
+
~{{ name }}.{{ item }}
|
| 25 |
+
{%- endfor %}
|
| 26 |
+
|
| 27 |
+
{% endif %}
|
| 28 |
+
{% endblock %}
|
| 29 |
+
|
| 30 |
+
{% block methods_summary %}
|
| 31 |
+
{% if methods %}
|
| 32 |
+
|
| 33 |
+
.. rubric:: Methods Summary
|
| 34 |
+
|
| 35 |
+
.. autosummary::
|
| 36 |
+
{% for item in methods %}
|
| 37 |
+
~{{ name }}.{{ item }}
|
| 38 |
+
{%- endfor %}
|
| 39 |
+
|
| 40 |
+
{% endif %}
|
| 41 |
+
{% endblock %}
|
| 42 |
+
|
| 43 |
+
{% block attributes_documentation %}
|
| 44 |
+
{% if attributes %}
|
| 45 |
+
|
| 46 |
+
.. rubric:: Attributes Documentation
|
| 47 |
+
|
| 48 |
+
{% for item in attributes %}
|
| 49 |
+
.. autoattribute:: {{ item }}
|
| 50 |
+
{%- endfor %}
|
| 51 |
+
|
| 52 |
+
{% endif %}
|
| 53 |
+
{% endblock %}
|
| 54 |
+
|
| 55 |
+
{% block methods_documentation %}
|
| 56 |
+
{% if methods %}
|
| 57 |
+
|
| 58 |
+
.. rubric:: Methods Documentation
|
| 59 |
+
|
| 60 |
+
{% for item in methods %}
|
| 61 |
+
.. automethod:: {{ item }}
|
| 62 |
+
{%- endfor %}
|
| 63 |
+
|
| 64 |
+
{% endif %}
|
| 65 |
+
{% endblock %}
|
gala/source/docs/_templates/autosummary/module.rst
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% if referencefile %}
|
| 2 |
+
.. include:: {{ referencefile }}
|
| 3 |
+
{% endif %}
|
| 4 |
+
|
| 5 |
+
{{ objname }}
|
| 6 |
+
{{ underline }}
|
| 7 |
+
|
| 8 |
+
.. automodule:: {{ fullname }}
|
| 9 |
+
|
| 10 |
+
{% block functions %}
|
| 11 |
+
{% if functions %}
|
| 12 |
+
.. rubric:: Functions
|
| 13 |
+
|
| 14 |
+
.. autosummary::
|
| 15 |
+
{% for item in functions %}
|
| 16 |
+
{{ item }}
|
| 17 |
+
{%- endfor %}
|
| 18 |
+
{% endif %}
|
| 19 |
+
{% endblock %}
|
| 20 |
+
|
| 21 |
+
{% block classes %}
|
| 22 |
+
{% if classes %}
|
| 23 |
+
.. rubric:: Classes
|
| 24 |
+
|
| 25 |
+
.. autosummary::
|
| 26 |
+
{% for item in classes %}
|
| 27 |
+
{{ item }}
|
| 28 |
+
{%- endfor %}
|
| 29 |
+
{% endif %}
|
| 30 |
+
{% endblock %}
|
| 31 |
+
|
| 32 |
+
{% block exceptions %}
|
| 33 |
+
{% if exceptions %}
|
| 34 |
+
.. rubric:: Exceptions
|
| 35 |
+
|
| 36 |
+
.. autosummary::
|
| 37 |
+
{% for item in exceptions %}
|
| 38 |
+
{{ item }}
|
| 39 |
+
{%- endfor %}
|
| 40 |
+
{% endif %}
|
| 41 |
+
{% endblock %}
|
gala/source/docs/conf.py
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import datetime
|
| 2 |
+
import os
|
| 3 |
+
import pathlib
|
| 4 |
+
import re
|
| 5 |
+
import sys
|
| 6 |
+
import warnings
|
| 7 |
+
from importlib import import_module
|
| 8 |
+
|
| 9 |
+
# Load all of the global Astropy configuration
|
| 10 |
+
try:
|
| 11 |
+
from sphinx_astropy.conf.v1 import * # noqa: F403
|
| 12 |
+
except ImportError:
|
| 13 |
+
print(
|
| 14 |
+
"ERROR: Building the documentation for Gala requires the "
|
| 15 |
+
"sphinx-astropy package to be installed"
|
| 16 |
+
)
|
| 17 |
+
sys.exit(1)
|
| 18 |
+
|
| 19 |
+
# Get configuration information from setup.cfg
|
| 20 |
+
from configparser import ConfigParser
|
| 21 |
+
|
| 22 |
+
conf = ConfigParser()
|
| 23 |
+
|
| 24 |
+
docs_root = pathlib.Path(__file__).parent.resolve()
|
| 25 |
+
|
| 26 |
+
# -- General configuration ----------------------------------------------------
|
| 27 |
+
|
| 28 |
+
# By default, highlight as Python 3.
|
| 29 |
+
highlight_language = "python3"
|
| 30 |
+
|
| 31 |
+
# List of patterns, relative to source directory, that match files and
|
| 32 |
+
# directories to ignore when looking for source files.
|
| 33 |
+
exclude_patterns = ["_build", "**.ipynb_checkpoints"]
|
| 34 |
+
|
| 35 |
+
# Add any paths that contain templates here, relative to this directory.
|
| 36 |
+
templates_path = ["_templates"]
|
| 37 |
+
|
| 38 |
+
# The suffix(es) of source filenames.
|
| 39 |
+
# You can specify multiple suffix as a list of string:
|
| 40 |
+
# source_suffix = ['.rst', '.md']
|
| 41 |
+
source_suffix = ".rst"
|
| 42 |
+
|
| 43 |
+
# Don't show summaries of the members in each class along with the
|
| 44 |
+
# class' docstring
|
| 45 |
+
numpydoc_show_class_members = False
|
| 46 |
+
|
| 47 |
+
# Whether to create cross-references for the parameter types in the
|
| 48 |
+
# Parameters, Other Parameters, Returns and Yields sections of the docstring.
|
| 49 |
+
numpydoc_xref_param_type = True
|
| 50 |
+
|
| 51 |
+
autosummary_generate = True
|
| 52 |
+
|
| 53 |
+
automodapi_toctreedirnm = "api"
|
| 54 |
+
|
| 55 |
+
# The reST default role (used for this markup: `text`) to use for all
|
| 56 |
+
# documents. Set to the "smart" one.
|
| 57 |
+
default_role = "obj"
|
| 58 |
+
|
| 59 |
+
# Class documentation should contain *both* the class docstring and
|
| 60 |
+
# the __init__ docstring
|
| 61 |
+
autoclass_content = "both"
|
| 62 |
+
|
| 63 |
+
# This is added to the end of RST files - a good place to put substitutions to
|
| 64 |
+
# be used globally.
|
| 65 |
+
rst_epilog = """
|
| 66 |
+
"""
|
| 67 |
+
|
| 68 |
+
# intersphinx
|
| 69 |
+
intersphinx_mapping = {
|
| 70 |
+
"python": ("https://docs.python.org/3/", None),
|
| 71 |
+
"numpy": ("https://numpy.org/doc/stable/", None),
|
| 72 |
+
"scipy": ("https://docs.scipy.org/doc/scipy/", None),
|
| 73 |
+
"matplotlib": ("https://matplotlib.org/stable/", None),
|
| 74 |
+
"astropy": ("https://docs.astropy.org/en/stable/", None),
|
| 75 |
+
"h5py": ("https://docs.h5py.org/en/stable/", None),
|
| 76 |
+
"sympy": ("https://docs.sympy.org/latest/", None),
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
# Show / hide TODO blocks
|
| 80 |
+
todo_include_todos = True
|
| 81 |
+
|
| 82 |
+
# -- Project information ------------------------------------------------------
|
| 83 |
+
|
| 84 |
+
# This does not *have* to match the package name, but typically does
|
| 85 |
+
project = "gala"
|
| 86 |
+
author = "Adrian Price-Whelan"
|
| 87 |
+
copyright = f"{datetime.datetime.now().year}, {author}"
|
| 88 |
+
|
| 89 |
+
package_name = "gala"
|
| 90 |
+
import_module(package_name)
|
| 91 |
+
package = sys.modules[package_name]
|
| 92 |
+
|
| 93 |
+
plot_formats = [("png", 200), ("pdf", 200)]
|
| 94 |
+
plot_apply_rcparams = True
|
| 95 |
+
# NOTE: if you update these, also update docs/tutorials/nb_setup
|
| 96 |
+
plot_rcparams = {
|
| 97 |
+
"image.cmap": "magma",
|
| 98 |
+
# Fonts:
|
| 99 |
+
"font.size": 16,
|
| 100 |
+
"figure.titlesize": "x-large",
|
| 101 |
+
"axes.titlesize": "large",
|
| 102 |
+
"axes.labelsize": "large",
|
| 103 |
+
"xtick.labelsize": "medium",
|
| 104 |
+
"ytick.labelsize": "medium",
|
| 105 |
+
# Axes:
|
| 106 |
+
"axes.labelcolor": "k",
|
| 107 |
+
"axes.axisbelow": True,
|
| 108 |
+
# Ticks
|
| 109 |
+
"xtick.color": "#333333",
|
| 110 |
+
"xtick.direction": "in",
|
| 111 |
+
"ytick.color": "#333333",
|
| 112 |
+
"ytick.direction": "in",
|
| 113 |
+
"xtick.top": True,
|
| 114 |
+
"ytick.right": True,
|
| 115 |
+
"figure.dpi": 300,
|
| 116 |
+
"savefig.dpi": 300,
|
| 117 |
+
}
|
| 118 |
+
plot_include_source = False
|
| 119 |
+
|
| 120 |
+
# The short X.Y version.
|
| 121 |
+
version = package.__version__.split("-", 1)[0]
|
| 122 |
+
# The full version, including alpha/beta/rc tags.
|
| 123 |
+
release = package.__version__
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
# -- Options for HTML output ---------------------------------------------------
|
| 127 |
+
|
| 128 |
+
html_theme = "pydata_sphinx_theme"
|
| 129 |
+
html_logo = "_static/Gala_Logo_RGB.png"
|
| 130 |
+
|
| 131 |
+
html_theme_options = {
|
| 132 |
+
"logo": {
|
| 133 |
+
"image_light": "Gala_Logo_RGB.png",
|
| 134 |
+
"image_dark": "Gala_Logo_RGB.png",
|
| 135 |
+
},
|
| 136 |
+
"icon_links": [
|
| 137 |
+
{
|
| 138 |
+
"name": "GitHub",
|
| 139 |
+
"url": "https://github.com/adrn/gala",
|
| 140 |
+
"icon": "fab fa-github-square",
|
| 141 |
+
},
|
| 142 |
+
{
|
| 143 |
+
"name": "Twitter",
|
| 144 |
+
"url": "https://twitter.com/adrianprw",
|
| 145 |
+
"icon": "fab fa-twitter-square",
|
| 146 |
+
},
|
| 147 |
+
],
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
# Add any paths that contain custom themes here, relative to this directory.
|
| 151 |
+
# To use a different custom theme, add the directory containing the theme.
|
| 152 |
+
# html_theme_path = ['_themes/sphinx_rtd_theme']
|
| 153 |
+
|
| 154 |
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
| 155 |
+
# a list of builtin themes. To override the custom theme, set this to the
|
| 156 |
+
# name of a builtin theme or the name of a custom theme in html_theme_path.
|
| 157 |
+
# html_theme = "sphinx_rtd_theme"
|
| 158 |
+
|
| 159 |
+
# Custom sidebar templates, maps document names to template names.
|
| 160 |
+
html_sidebars = {"**": ["search-field.html", "sidebar-nav-bs.html"]}
|
| 161 |
+
|
| 162 |
+
# The name of an image file (within the static path) to use as favicon of the
|
| 163 |
+
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
| 164 |
+
# pixels large.
|
| 165 |
+
html_favicon = str(docs_root / "_static" / "m104.ico")
|
| 166 |
+
|
| 167 |
+
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
| 168 |
+
# using the given strftime format.
|
| 169 |
+
# html_last_updated_fmt = ''
|
| 170 |
+
|
| 171 |
+
# The name for this set of Sphinx documents. If None, it defaults to
|
| 172 |
+
# "<project> v<release> documentation".
|
| 173 |
+
html_title = f"{project} v{release}"
|
| 174 |
+
|
| 175 |
+
# Output file base name for HTML help builder.
|
| 176 |
+
htmlhelp_basename = project + "doc"
|
| 177 |
+
|
| 178 |
+
# Static files to copy after template files
|
| 179 |
+
html_static_path = ["_static"]
|
| 180 |
+
html_css_files = ["gala.css"]
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
# -- Options for LaTeX output --------------------------------------------------
|
| 184 |
+
|
| 185 |
+
# Grouping the document tree into LaTeX files. List of tuples
|
| 186 |
+
# (source start file, target name, title, author, documentclass [howto/manual]).
|
| 187 |
+
latex_documents = [
|
| 188 |
+
("index", project + ".tex", project + " Documentation", author, "manual")
|
| 189 |
+
]
|
| 190 |
+
|
| 191 |
+
# show inherited members for classes
|
| 192 |
+
automodsumm_inherited_members = True
|
| 193 |
+
|
| 194 |
+
# Add nbsphinx
|
| 195 |
+
extensions += [ # noqa: F405
|
| 196 |
+
"nbsphinx",
|
| 197 |
+
"IPython.sphinxext.ipython_console_highlighting",
|
| 198 |
+
"sphinxcontrib.bibtex",
|
| 199 |
+
"rtds_action",
|
| 200 |
+
]
|
| 201 |
+
|
| 202 |
+
# Bibliography:
|
| 203 |
+
bibtex_bibfiles = ["refs.bib"]
|
| 204 |
+
bibtex_reference_style = "author_year"
|
| 205 |
+
|
| 206 |
+
# Custom setting for nbsphinx - timeout for executing one cell
|
| 207 |
+
nbsphinx_timeout = 300
|
| 208 |
+
nbsphinx_kernel_name = os.environ.get("NBSPHINX_KERNEL_NAME", "python3")
|
| 209 |
+
|
| 210 |
+
# nbsphinx hacks (thanks exoplanet)
|
| 211 |
+
import nbsphinx # noqa: E402
|
| 212 |
+
from nbsphinx import markdown2rst as original_markdown2rst # noqa: E402
|
| 213 |
+
|
| 214 |
+
nbsphinx.RST_TEMPLATE = nbsphinx.RST_TEMPLATE.replace(
|
| 215 |
+
"{%- if width %}", "{%- if 0 %}"
|
| 216 |
+
).replace("{%- if height %}", "{%- if 0 %}")
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def subber(m):
|
| 220 |
+
return m.group(0).replace("``", "`")
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
prog = re.compile(r":(.+):``(.+)``")
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def markdown2rst(text):
|
| 227 |
+
return prog.sub(subber, original_markdown2rst(text))
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
nbsphinx.markdown2rst = markdown2rst
|
| 231 |
+
|
| 232 |
+
# rtds-action
|
| 233 |
+
if "GITHUB_TOKEN" in os.environ:
|
| 234 |
+
print("GitHub Token found: retrieving artifact")
|
| 235 |
+
|
| 236 |
+
# The name of your GitHub repository
|
| 237 |
+
rtds_action_github_repo = "adrn/gala"
|
| 238 |
+
|
| 239 |
+
# The path where the artifact should be extracted
|
| 240 |
+
# Note: this is relative to the conf.py file!
|
| 241 |
+
rtds_action_path = "."
|
| 242 |
+
|
| 243 |
+
# The "prefix" used in the `upload-artifact` step of the action
|
| 244 |
+
rtds_action_artifact_prefix = "notebooks-for-"
|
| 245 |
+
|
| 246 |
+
# A GitHub personal access token is required, more info below
|
| 247 |
+
rtds_action_github_token = os.environ["GITHUB_TOKEN"]
|
| 248 |
+
|
| 249 |
+
# Whether or not to raise an error on ReadTheDocs if the
|
| 250 |
+
# artifact containing the notebooks can't be downloaded (optional)
|
| 251 |
+
rtds_action_error_if_missing = True
|
| 252 |
+
|
| 253 |
+
else:
|
| 254 |
+
rtds_action_github_repo = ""
|
| 255 |
+
rtds_action_github_token = ""
|
| 256 |
+
rtds_action_path = ""
|
| 257 |
+
|
| 258 |
+
## -- Retrieve Zenodo record for most recent version of Gala:
|
| 259 |
+
zenodo_path = docs_root / "ZENODO.rst"
|
| 260 |
+
if not zenodo_path.exists():
|
| 261 |
+
import textwrap
|
| 262 |
+
|
| 263 |
+
try:
|
| 264 |
+
import requests
|
| 265 |
+
|
| 266 |
+
headers = {"accept": "application/x-bibtex"}
|
| 267 |
+
response = requests.get(
|
| 268 |
+
"https://zenodo.org/api/records/16923466", headers=headers
|
| 269 |
+
)
|
| 270 |
+
response.encoding = "utf-8"
|
| 271 |
+
zenodo_record = ".. code-block:: bibtex\n\n" + textwrap.indent(
|
| 272 |
+
response.text, " " * 4
|
| 273 |
+
)
|
| 274 |
+
except Exception as e:
|
| 275 |
+
warnings.warn(f"Failed to retrieve Zenodo record for Gala: {e!s}", stacklevel=1)
|
| 276 |
+
zenodo_record = (
|
| 277 |
+
"`Retrieve the Zenodo record here <https://zenodo.org/record/16923466>`_"
|
| 278 |
+
)
|
| 279 |
+
|
| 280 |
+
with open(zenodo_path, "w", encoding="utf-8") as f:
|
| 281 |
+
f.write(zenodo_record)
|
| 282 |
+
|
| 283 |
+
## -- Check for executed tutorials and only add to toctree if they exist:
|
| 284 |
+
|
| 285 |
+
# Note: for jupytext tutorials (the .py files), put the expected (generated) .ipynb name
|
| 286 |
+
tutorial_files = [
|
| 287 |
+
"tutorials/Milky-Way-model.ipynb",
|
| 288 |
+
"tutorials/integrate-potential-example.rst",
|
| 289 |
+
"tutorials/pyia-gala-orbit.ipynb",
|
| 290 |
+
"tutorials/integrate-barred-potential.ipynb",
|
| 291 |
+
"tutorials/mock-stream-heliocentric.rst",
|
| 292 |
+
"tutorials/circ-restricted-3body.rst",
|
| 293 |
+
"tutorials/spherical-spline-tutorial.ipynb",
|
| 294 |
+
"tutorials/Arbitrary-density-SCF.ipynb",
|
| 295 |
+
"tutorials/exp.rst",
|
| 296 |
+
"tutorials/stream-mass-loss.ipynb",
|
| 297 |
+
"tutorials/time-evolving-potential.ipynb",
|
| 298 |
+
"tutorials/v1_11_new_features.ipynb",
|
| 299 |
+
# Supporting documents:
|
| 300 |
+
"supporting/define-milky-way-model.ipynb",
|
| 301 |
+
]
|
| 302 |
+
|
| 303 |
+
_not_executed = []
|
| 304 |
+
_tutorial_toctree_items = []
|
| 305 |
+
_supporting_toctree_items = []
|
| 306 |
+
for fn in tutorial_files:
|
| 307 |
+
if not pathlib.Path(fn).exists() and "GITHUB_TOKEN" not in os.environ:
|
| 308 |
+
_not_executed.append(fn)
|
| 309 |
+
continue
|
| 310 |
+
|
| 311 |
+
if fn.startswith("supporting/"):
|
| 312 |
+
_supporting_toctree_items.append(fn)
|
| 313 |
+
elif fn.startswith("tutorials/"):
|
| 314 |
+
_tutorial_toctree_items.append(fn)
|
| 315 |
+
|
| 316 |
+
if _tutorial_toctree_items:
|
| 317 |
+
_tutorial_toctree_items = "\n ".join(_tutorial_toctree_items)
|
| 318 |
+
_tutorial_toctree = f"""
|
| 319 |
+
.. toctree::
|
| 320 |
+
:maxdepth: 1
|
| 321 |
+
:glob:
|
| 322 |
+
|
| 323 |
+
{_tutorial_toctree_items}
|
| 324 |
+
"""
|
| 325 |
+
|
| 326 |
+
else:
|
| 327 |
+
_tutorial_toctree = "No tutorials found!"
|
| 328 |
+
|
| 329 |
+
if _supporting_toctree_items:
|
| 330 |
+
_supporting_toctree_items = "\n ".join(_supporting_toctree_items)
|
| 331 |
+
_supporting_toctree = f"""
|
| 332 |
+
.. toctree::
|
| 333 |
+
:maxdepth: 1
|
| 334 |
+
:glob:
|
| 335 |
+
|
| 336 |
+
{_supporting_toctree_items}
|
| 337 |
+
"""
|
| 338 |
+
|
| 339 |
+
else:
|
| 340 |
+
_supporting_toctree = "No supporting documents found!"
|
| 341 |
+
|
| 342 |
+
if _not_executed:
|
| 343 |
+
print(
|
| 344 |
+
"\n-------- Gala warning --------\n"
|
| 345 |
+
"Some tutorial notebooks could not be found! This is likely because "
|
| 346 |
+
"the tutorial notebooks have not been executed. If you are building "
|
| 347 |
+
"the documentation locally, you may want to run 'make exectutorials' "
|
| 348 |
+
"before running the sphinx build."
|
| 349 |
+
)
|
| 350 |
+
print(f"Missing tutorials: {', '.join(_not_executed)}\n")
|
| 351 |
+
|
| 352 |
+
with open("_tutorials.rst", "w", encoding="utf-8") as f:
|
| 353 |
+
f.write(_tutorial_toctree)
|
| 354 |
+
|
| 355 |
+
with open("_supporting.rst", "w", encoding="utf-8") as f:
|
| 356 |
+
f.write(_supporting_toctree)
|
gala/source/docs/contributing.rst
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. include:: references.txt
|
| 2 |
+
|
| 3 |
+
*****************
|
| 4 |
+
How to contribute
|
| 5 |
+
*****************
|
| 6 |
+
|
| 7 |
+
We welcome contributions from anyone via pull requests on `GitHub
|
| 8 |
+
<https://github.com/adrn/gala>`_. If you don't feel comfortable modifying or
|
| 9 |
+
adding functionality, we also welcome feature requests and bug reports as
|
| 10 |
+
`GitHub issues <https://github.com/adrn/gala/issues>`_.
|
| 11 |
+
|
| 12 |
+
Developer documentation
|
| 13 |
+
=======================
|
| 14 |
+
|
| 15 |
+
.. toctree::
|
| 16 |
+
:maxdepth: 1
|
| 17 |
+
|
| 18 |
+
testing
|
| 19 |
+
docs
|
gala/source/docs/conventions.rst
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
.. _conventions:
|
| 3 |
+
|
| 4 |
+
***********
|
| 5 |
+
Conventions
|
| 6 |
+
***********
|
| 7 |
+
|
| 8 |
+
.. _name-conventions:
|
| 9 |
+
|
| 10 |
+
Common variable names
|
| 11 |
+
=====================
|
| 12 |
+
|
| 13 |
+
This package uses standard variable names throughout for consistency:
|
| 14 |
+
|
| 15 |
+
- ``w`` represents phase-space coordinates (positions and velocities)
|
| 16 |
+
- ``q`` represents positions only
|
| 17 |
+
- ``p`` or ``v`` represent velocities or momenta
|
| 18 |
+
- ``t`` represents time arrays
|
| 19 |
+
|
| 20 |
+
.. _shape-conventions:
|
| 21 |
+
|
| 22 |
+
Array shapes
|
| 23 |
+
============
|
| 24 |
+
|
| 25 |
+
Arrays and :class:`~astropy.units.Quantity` objects in ``Gala`` follow
|
| 26 |
+
consistent shape conventions:
|
| 27 |
+
|
| 28 |
+
**Coordinate arrays**: ``axis=0`` is the coordinate dimension. For example,
|
| 29 |
+
128 different 3D Cartesian positions have shape ``(3, 128)``.
|
| 30 |
+
|
| 31 |
+
**Orbit collections**: Arrays have three axes:
|
| 32 |
+
- ``axis=0``: coordinate dimension
|
| 33 |
+
- ``axis=1``: time axis
|
| 34 |
+
- ``axis=2``: different orbits
|
| 35 |
+
|
| 36 |
+
.. _energy-momentum:
|
| 37 |
+
|
| 38 |
+
Energy and momentum
|
| 39 |
+
===================
|
| 40 |
+
|
| 41 |
+
In `gala`, energy and angular momentum quantities are *per unit mass* unless
|
| 42 |
+
otherwise specified. This applies to:
|
| 43 |
+
|
| 44 |
+
- Potential energy
|
| 45 |
+
- Kinetic energy
|
| 46 |
+
- Total energy
|
| 47 |
+
- Angular momentum
|
| 48 |
+
- Linear momentum
|
| 49 |
+
- Conjugate momenta
|
gala/source/docs/coordinates/greatcircle.rst
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
For the examples below, we assume the following imports have already been
|
| 2 |
+
executed::
|
| 3 |
+
|
| 4 |
+
>>> import astropy.units as u
|
| 5 |
+
>>> import astropy.coordinates as coord
|
| 6 |
+
>>> import numpy as np
|
| 7 |
+
>>> import gala.coordinates as gc
|
| 8 |
+
|
| 9 |
+
.. _greatcircle:
|
| 10 |
+
|
| 11 |
+
*************************************************
|
| 12 |
+
Great circle and stellar stream coordinate frames
|
| 13 |
+
*************************************************
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
Introduction
|
| 17 |
+
============
|
| 18 |
+
|
| 19 |
+
Great circle coordinate systems are defined as a rotation from another spherical
|
| 20 |
+
coordinate system, such as the ICRS. The great circle system is defined by a specified
|
| 21 |
+
(north) pole and spherical origin -- i.e. a specification of the new coordinate system x
|
| 22 |
+
and z axes in components of the old coordinate system.
|
| 23 |
+
|
| 24 |
+
`gala` currently supports great circle frames that are defined as a rotation away from
|
| 25 |
+
the ICRS (RA, Dec) through the `~gala.coordinates.GreatCircleICRSFrame` class. To create
|
| 26 |
+
a new great circle frame with the default initializer, you must specify a pole using the
|
| 27 |
+
``pole`` keyword argument and the spherical origin with the ``origin`` argument.
|
| 28 |
+
However, this frame also supports other initialization paths through the ``from_``
|
| 29 |
+
classmethods (see API below). These classmethods are the most useful initialization
|
| 30 |
+
methods. For example, to define a great circle system with the pole at (RA, Dec) =
|
| 31 |
+
(32.5, 19.8)º and a longitude 0 at RA=100º, we first have to create a coordinate object
|
| 32 |
+
for the pole::
|
| 33 |
+
|
| 34 |
+
>>> pole = coord.SkyCoord(ra=32.5*u.deg, dec=19.8*u.deg)
|
| 35 |
+
|
| 36 |
+
We can then pass this pole to the `~gala.coordinates.GreatCircleICRSFrame.from_pole_ra0`
|
| 37 |
+
classmethod to define our coordinate frame::
|
| 38 |
+
|
| 39 |
+
>>> frame = gc.GreatCircleICRSFrame.from_pole_ra0(pole=pole, ra0=100*u.deg)
|
| 40 |
+
|
| 41 |
+
This frame instance acts like any other Astropy coordinate frame. For example, we can
|
| 42 |
+
transform other coordinates to this new coordinate system using::
|
| 43 |
+
|
| 44 |
+
>>> c = coord.SkyCoord(ra=[160, 53]*u.deg, dec=[-11, 9]*u.deg)
|
| 45 |
+
>>> c_fr = c.transform_to(frame)
|
| 46 |
+
>>> c_fr # doctest: +FLOAT_CMP
|
| 47 |
+
<SkyCoord (GreatCircleICRSFrame: pole=<ICRS Coordinate: (ra, dec) in deg
|
| 48 |
+
(32.5, 19.8)>, origin=<ICRS Coordinate: (ra, dec) in deg
|
| 49 |
+
(280., 46.74765478)>, priority=origin): (phi1, phi2) in deg
|
| 50 |
+
[(-127.59199268, -38.82050866), (-154.93887946, 67.43382209)]>
|
| 51 |
+
|
| 52 |
+
The spherical coordinate components of the resulting great circle frame are
|
| 53 |
+
always named ``phi1`` and ``phi2``, so to access the longitude and latitude in
|
| 54 |
+
the new system, we use::
|
| 55 |
+
|
| 56 |
+
>>> c_fr.phi1 # doctest: +FLOAT_CMP
|
| 57 |
+
<Longitude [-127.59199268, -154.93887946] deg>
|
| 58 |
+
>>> c_fr.phi2 # doctest: +FLOAT_CMP
|
| 59 |
+
<Latitude [-38.82050866, 67.43382209] deg>
|
| 60 |
+
|
| 61 |
+
The transformation also works for velocity components. For example, if we have a
|
| 62 |
+
sky position and proper motions, we can transform to the great circle frame in
|
| 63 |
+
the same way::
|
| 64 |
+
|
| 65 |
+
>>> c2 = coord.SkyCoord(
|
| 66 |
+
... ra=160*u.deg,
|
| 67 |
+
... dec=-11*u.deg,
|
| 68 |
+
... pm_ra_cosdec=5*u.mas/u.yr,
|
| 69 |
+
... pm_dec=0.3*u.mas/u.yr
|
| 70 |
+
... )
|
| 71 |
+
>>> c2_fr = c2.transform_to(frame)
|
| 72 |
+
>>> c2_fr.phi1 # doctest: +FLOAT_CMP
|
| 73 |
+
<Longitude -127.59199268 deg>
|
| 74 |
+
>>> c2_fr.pm_phi1_cosphi2 # doctest: +FLOAT_CMP
|
| 75 |
+
<Quantity 1.71997614 mas / yr>
|
| 76 |
+
>>> c2_fr.pm_phi2 # doctest: +FLOAT_CMP
|
| 77 |
+
<Quantity -4.70443217 mas / yr>
|
| 78 |
+
|
| 79 |
+
The generic great circle frame can also handle transforming from great circle
|
| 80 |
+
coordinates to other coordinate frames. For example, to transform a grid of points along
|
| 81 |
+
a great circle to the ICRS system, we would define a frame with positional data and a
|
| 82 |
+
specified pole::
|
| 83 |
+
|
| 84 |
+
>>> c3_fr = gc.GreatCircleICRSFrame(
|
| 85 |
+
... phi1=np.linspace(0, 360, 8)*u.deg,
|
| 86 |
+
... phi2=0*u.deg,
|
| 87 |
+
... pole=frame.pole,
|
| 88 |
+
... origin=frame.origin
|
| 89 |
+
... )
|
| 90 |
+
>>> c3 = c3_fr.transform_to(coord.ICRS())
|
| 91 |
+
>>> c3.ra # doctest: +FLOAT_CMP
|
| 92 |
+
<Longitude [280. , 302.73861084, 326.04009238, 67.95460569,
|
| 93 |
+
113.51995793, 132.05271289, 180.05477998, 280. ] deg>
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
Creating a coordinate frame from two points along a great circle
|
| 97 |
+
================================================================
|
| 98 |
+
|
| 99 |
+
It is sometimes convenient to define a great circle coordinate frame by specifying two
|
| 100 |
+
endpoints of an arc segment along a great circle (instead of the pole). For these use
|
| 101 |
+
cases, the `~gala.coordinates.GreatCircleICRSFrame.from_endpoints` provides a
|
| 102 |
+
convenience classmethod for creating a great circle frame with endpoints::
|
| 103 |
+
|
| 104 |
+
>>> endpoints = coord.SkyCoord(
|
| 105 |
+
... ra=[-38.8, 4.7]*u.deg,
|
| 106 |
+
... dec=[-45.1, -51.7]*u.deg
|
| 107 |
+
... )
|
| 108 |
+
>>> frame2 = gc.GreatCircleICRSFrame.from_endpoints(endpoints[0], endpoints[1])
|
| 109 |
+
>>> frame2
|
| 110 |
+
<GreatCircleICRSFrame Frame (pole=<ICRS Coordinate: (ra, dec) in deg
|
| 111 |
+
(359.1291976, 38.16814051)>, origin=<ICRS Coordinate: (ra, dec) in deg
|
| 112 |
+
(341.46580563, -50.48035324)>, priority=origin)>
|
| 113 |
+
|
| 114 |
+
Without specifying a longitude zeropoint, the default behavior of the above classmethod
|
| 115 |
+
is to take the spherical midpoint of the two endpoints as the longitude zeropoint.
|
| 116 |
+
However, a custom zeropoint can be specified using the ``ra0`` keyword argument. For
|
| 117 |
+
example::
|
| 118 |
+
|
| 119 |
+
>>> frame3 = gc.GreatCircleICRSFrame.from_endpoints(
|
| 120 |
+
... endpoints[0], endpoints[1], ra0=150*u.deg
|
| 121 |
+
... )
|
| 122 |
+
>>> frame3
|
| 123 |
+
<GreatCircleICRSFrame Frame (pole=<ICRS Coordinate: (ra, dec) in deg
|
| 124 |
+
(359.1291976, 38.16814051)>, origin=<ICRS Coordinate: (ra, dec) in deg
|
| 125 |
+
(330., -48.01820335)>, priority=origin)>
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
Creating a coordinate frame from endpoints and an origin
|
| 129 |
+
========================================================
|
| 130 |
+
|
| 131 |
+
When working with stellar streams, it is sometimes useful to create a stream-aligned
|
| 132 |
+
coordinate frame by specifying an exact origin for the new great circle coordinate frame
|
| 133 |
+
(e.g., set to the progenitor system) along with the endpoints of the stream (which are
|
| 134 |
+
often close to defining a great circle). In these cases, the great circle defined by the
|
| 135 |
+
endpoints and the great circle defined by the origin may not be orthogonal. You can
|
| 136 |
+
still use these to create a `~gala.coordinates.GreatCircleICRSFrame`, but by default the
|
| 137 |
+
pole location will be adjusted to be orthogonal to the input origin::
|
| 138 |
+
|
| 139 |
+
>>> endpoints = coord.SkyCoord(
|
| 140 |
+
... ra=[-38.8, 4.7]*u.deg,
|
| 141 |
+
... dec=[-45.1, -51.7]*u.deg
|
| 142 |
+
... )
|
| 143 |
+
>>> origin = coord.SkyCoord(330., -48., unit=u.deg)
|
| 144 |
+
>>> frame4 = gc.GreatCircleICRSFrame.from_endpoints( # doctest: +IGNORE_WARNINGS
|
| 145 |
+
... endpoints[0], endpoints[1], origin=origin
|
| 146 |
+
... )
|
| 147 |
+
>>> frame4
|
| 148 |
+
<GreatCircleICRSFrame Frame (pole=<ICRS Coordinate: (ra, dec) in deg
|
| 149 |
+
(359.13616655, 38.18404071)>, origin=<ICRS Coordinate: (ra, dec) in deg
|
| 150 |
+
(330., -48.)>, priority=origin)>
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
Creating a coordinate frame from a pole and longitude zero point
|
| 154 |
+
================================================================
|
| 155 |
+
|
| 156 |
+
Another common way of initializing great circle coordinate systems is with a pole and a
|
| 157 |
+
longitude zero point (as was previously — prior to v1.7 — allowed in the initializer
|
| 158 |
+
`~gala.coordinates.GreatCircleICRSFrame`). This can now be done with the
|
| 159 |
+
`~gala.coordinates.GreatCircleICRSFrame.from_pole_ra0` classmethod::
|
| 160 |
+
|
| 161 |
+
>>> frame5 = gc.GreatCircleICRSFrame.from_pole_ra0(
|
| 162 |
+
... pole=pole, ra0=100*u.deg
|
| 163 |
+
... )
|
| 164 |
+
>>> frame5
|
| 165 |
+
<GreatCircleICRSFrame Frame (pole=<ICRS Coordinate: (ra, dec) in deg
|
| 166 |
+
(32.5, 19.8)>, origin=<ICRS Coordinate: (ra, dec) in deg
|
| 167 |
+
(280., 46.74765478)>, priority=origin)>
|
| 168 |
+
|
| 169 |
+
With just these inputs, there is an ambiguity in the definition of the coordinate frame
|
| 170 |
+
because the great circles defined by the pole and longitude zero point intersect at two
|
| 171 |
+
locations (so there are two possible origins, one being the negative of the other). The
|
| 172 |
+
convention here is to pick the origin closest to (0, 0). To have finer control over
|
| 173 |
+
which origin is picked, you can also pass in a sky coordinate object with the
|
| 174 |
+
``origin_disambiguate`` argument, and the origin closest to this coordinate will be used
|
| 175 |
+
to define the coordinate frame.
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
.. _greatcircle-api:
|
| 179 |
+
|
| 180 |
+
API
|
| 181 |
+
===
|
| 182 |
+
|
| 183 |
+
.. automodapi:: gala.coordinates.greatcircle
|
| 184 |
+
:no-inheritance-diagram:
|
gala/source/docs/coordinates/index.rst
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. module:: gala.coordinates
|
| 2 |
+
|
| 3 |
+
.. _gala-coordinates:
|
| 4 |
+
|
| 5 |
+
*********************************************
|
| 6 |
+
Coordinate Systems (`gala.coordinates`)
|
| 7 |
+
*********************************************
|
| 8 |
+
|
| 9 |
+
Introduction
|
| 10 |
+
============
|
| 11 |
+
|
| 12 |
+
The `~gala.coordinates` subpackage primarily provides specialty
|
| 13 |
+
:mod:`astropy.coordinates` frame classes for coordinate systems defined by the
|
| 14 |
+
stellar streams, and for other common Galactic dynamics tasks like removing
|
| 15 |
+
solar reflex motion from proper motions or radial velocities, and transforming
|
| 16 |
+
a proper motion covariance matrix from one frame to another.
|
| 17 |
+
|
| 18 |
+
For the examples below the following imports have already been executed::
|
| 19 |
+
|
| 20 |
+
>>> import numpy as np
|
| 21 |
+
>>> import astropy.coordinates as coord
|
| 22 |
+
>>> import astropy.units as u
|
| 23 |
+
>>> import gala.coordinates as gc
|
| 24 |
+
|
| 25 |
+
We will also set the default Astropy Galactocentric frame parameters to the
|
| 26 |
+
values adopted in Astropy v4.0:
|
| 27 |
+
|
| 28 |
+
>>> _ = coord.galactocentric_frame_defaults.set('v4.0')
|
| 29 |
+
|
| 30 |
+
Stellar stream coordinate frames
|
| 31 |
+
================================
|
| 32 |
+
|
| 33 |
+
`gala` provides Astropy coordinate frame classes for transforming to several
|
| 34 |
+
built-in stellar stream stream coordinate frames (as defined in the references
|
| 35 |
+
below), and for transforming positions and velocities to and from coordinate
|
| 36 |
+
systems defined by great circles or poles. These classes behave like the
|
| 37 |
+
built-in astropy coordinates frames (e.g., :class:`~astropy.coordinates.ICRS` or
|
| 38 |
+
:class:`~astropy.coordinates.Galactic`) and can be transformed to and from other
|
| 39 |
+
astropy coordinate frames. For example, to convert a set of
|
| 40 |
+
`~astropy.coordinates.ICRS` (RA, Dec) coordinates to a coordinate system aligned
|
| 41 |
+
with the Sagittarius stream with the `~gala.coordinates.SagittariusLaw10`
|
| 42 |
+
frame::
|
| 43 |
+
|
| 44 |
+
>>> c = coord.ICRS(ra=100.68458*u.degree, dec=41.26917*u.degree)
|
| 45 |
+
>>> sgr = c.transform_to(gc.SagittariusLaw10())
|
| 46 |
+
>>> (sgr.Lambda, sgr.Beta) # doctest: +FLOAT_CMP
|
| 47 |
+
(<Longitude 179.58511053544734 deg>, <Latitude -12.558450192162654 deg>)
|
| 48 |
+
|
| 49 |
+
Or, to transform from `~gala.coordinates.SagittariusLaw10` coordinates to the
|
| 50 |
+
`~astropy.coordinates.Galactic` frame::
|
| 51 |
+
|
| 52 |
+
>>> sgr = gc.SagittariusLaw10(Lambda=156.342*u.degree, Beta=1.1*u.degree)
|
| 53 |
+
>>> c = sgr.transform_to(coord.Galactic())
|
| 54 |
+
>>> (c.l, c.b) # doctest: +FLOAT_CMP
|
| 55 |
+
(<Longitude 182.5922090437946 deg>, <Latitude -9.539692094685893 deg>)
|
| 56 |
+
|
| 57 |
+
These transformations also handle velocities so that proper motion components
|
| 58 |
+
can be transformed between the systems. For example, to transform from
|
| 59 |
+
`~gala.coordinates.GD1Koposov10` proper motions to
|
| 60 |
+
`~astropy.coordinates.Galactic` proper motions::
|
| 61 |
+
|
| 62 |
+
>>> gd1 = gc.GD1Koposov10(phi1=-35*u.degree, phi2=0*u.degree,
|
| 63 |
+
... pm_phi1_cosphi2=-12.20*u.mas/u.yr,
|
| 64 |
+
... pm_phi2=-3.10*u.mas/u.yr)
|
| 65 |
+
>>> gd1.transform_to(coord.Galactic()) # doctest: +FLOAT_CMP
|
| 66 |
+
<Galactic Coordinate: (l, b) in deg
|
| 67 |
+
(181.28968151, 54.84972806)
|
| 68 |
+
(pm_l_cosb, pm_b) in mas / yr
|
| 69 |
+
(12.03209393, -3.69847479)>
|
| 70 |
+
|
| 71 |
+
As with the other Astropy coordinate frames, with a full specification of the 3D
|
| 72 |
+
position and velocity, we can transform to a
|
| 73 |
+
`~astropy.coordinates.Galactocentric` frame::
|
| 74 |
+
|
| 75 |
+
>>> gd1 = gc.GD1Koposov10(phi1=-35.00*u.degree, phi2=0.04*u.degree,
|
| 76 |
+
... distance=7.83*u.kpc,
|
| 77 |
+
... pm_phi1_cosphi2=-12.20*u.mas/u.yr,
|
| 78 |
+
... pm_phi2=-3.10*u.mas/u.yr,
|
| 79 |
+
... radial_velocity=-32*u.km/u.s)
|
| 80 |
+
>>> gd1.transform_to(coord.Galactocentric()) # doctest: +FLOAT_CMP
|
| 81 |
+
<Galactocentric Coordinate (galcen_coord=<ICRS Coordinate: (ra, dec) in deg
|
| 82 |
+
(266.4051, -28.936175)>, galcen_distance=8.122 kpc, galcen_v_sun=(12.9, 245.6, 7.78) km / s, z_sun=20.8 pc, roll=0.0 deg): (x, y, z) in kpc
|
| 83 |
+
(-12.61622659, -0.09870921, 6.43179403)
|
| 84 |
+
(v_x, v_y, v_z) in km / s
|
| 85 |
+
(-71.14675268, -203.01648654, -97.12884319)>
|
| 86 |
+
|
| 87 |
+
For custom great circle coordinate systems, and for more information about the
|
| 88 |
+
stellar stream frames, see :ref:`greatcircle`.
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
Correcting velocities for solar reflex motion
|
| 92 |
+
---------------------------------------------
|
| 93 |
+
|
| 94 |
+
The `~gala.coordinates.reflex_correct` function accepts an Astropy
|
| 95 |
+
`~astropy.coordinates.SkyCoord` object with position and velocity information,
|
| 96 |
+
and returns a coordinate object with the solar motion added back in to the
|
| 97 |
+
velocity components. This is useful for computing velocities in a Galactocentric
|
| 98 |
+
reference frame, rather than a solar system barycentric frame.
|
| 99 |
+
|
| 100 |
+
The `~gala.coordinates.reflex_correct` function accepts a coordinate object with
|
| 101 |
+
scalar or array values::
|
| 102 |
+
|
| 103 |
+
>>> c = coord.SkyCoord(ra=[180.323, 1.523]*u.deg,
|
| 104 |
+
... dec=[-17, 29]*u.deg,
|
| 105 |
+
... distance=[172, 412]*u.pc,
|
| 106 |
+
... pm_ra_cosdec=[-11, 3]*u.mas/u.yr,
|
| 107 |
+
... pm_dec=[4, 8]*u.mas/u.yr,
|
| 108 |
+
... radial_velocity=[114, -21]*u.km/u.s)
|
| 109 |
+
>>> gc.reflex_correct(c) # doctest: +FLOAT_CMP
|
| 110 |
+
<SkyCoord (ICRS): (ra, dec, distance) in (deg, deg, pc)
|
| 111 |
+
[(180.323, -17., 172.), ( 1.523, 29., 412.)]
|
| 112 |
+
(pm_ra_cosdec, pm_dec, radial_velocity) in (mas / yr, mas / yr, km / s)
|
| 113 |
+
[(139.47001884, 175.45769809, -47.09032586),
|
| 114 |
+
(-61.01738781, 61.51055793, 163.36721898)]>
|
| 115 |
+
|
| 116 |
+
By default, this uses the solar location and velocity from the
|
| 117 |
+
`astropy.coordinates.Galactocentric` frame class. To modify these parameters,
|
| 118 |
+
for example, to change the solar velocity, or the sun's height above the
|
| 119 |
+
Galactic midplane, use the arguments of the `astropy.coordinates.Galactocentric`
|
| 120 |
+
class and pass in an instance of the `astropy.coordinates.Galactocentric`
|
| 121 |
+
frame::
|
| 122 |
+
|
| 123 |
+
>>> vsun = coord.CartesianDifferential([11., 245., 7.]*u.km/u.s)
|
| 124 |
+
>>> gc_frame = coord.Galactocentric(galcen_v_sun=vsun, z_sun=0*u.pc)
|
| 125 |
+
>>> gc.reflex_correct(c, gc_frame) # doctest: +FLOAT_CMP
|
| 126 |
+
<SkyCoord (ICRS): (ra, dec, distance) in (deg, deg, pc)
|
| 127 |
+
[(180.323, -17., 172.), ( 1.523, 29., 412.)]
|
| 128 |
+
(pm_ra_cosdec, pm_dec, radial_velocity) in (mas / yr, mas / yr, km / s)
|
| 129 |
+
[(136.93481249, 175.37627916, -47.6177433 ),
|
| 130 |
+
(-59.96484921, 61.41044742, 163.90707073)]>
|
| 131 |
+
|
| 132 |
+
If you don't have radial velocity information and want to correct the proper
|
| 133 |
+
motions, pass in zeros for the radial velocity (and ignore the output value of
|
| 134 |
+
the radial velocity)::
|
| 135 |
+
|
| 136 |
+
>>> c = coord.SkyCoord(ra=162*u.deg,
|
| 137 |
+
... dec=-17*u.deg,
|
| 138 |
+
... distance=172*u.pc,
|
| 139 |
+
... pm_ra_cosdec=-11*u.mas/u.yr,
|
| 140 |
+
... pm_dec=4*u.mas/u.yr,
|
| 141 |
+
... radial_velocity=0*u.km/u.s)
|
| 142 |
+
>>> gc.reflex_correct(c) # doctest: +FLOAT_CMP
|
| 143 |
+
<SkyCoord (ICRS): (ra, dec, distance) in (deg, deg, pc)
|
| 144 |
+
(162., -17., 172.)
|
| 145 |
+
(pm_ra_cosdec, pm_dec, radial_velocity) in (mas / yr, mas / yr, km / s)
|
| 146 |
+
(88.20380175, 163.68500525, -192.48721942)>
|
| 147 |
+
|
| 148 |
+
Similarly, if you don't have proper motion information and want to correct the
|
| 149 |
+
proper motions, pass in zeros for the proper motions (and ignore the output
|
| 150 |
+
values of the proper motions) -- this is sometimes called "v_GSR"::
|
| 151 |
+
|
| 152 |
+
>>> c = coord.SkyCoord(ra=162*u.deg,
|
| 153 |
+
... dec=-17*u.deg,
|
| 154 |
+
... distance=172*u.pc,
|
| 155 |
+
... pm_ra_cosdec=0*u.mas/u.yr,
|
| 156 |
+
... pm_dec=0*u.mas/u.yr,
|
| 157 |
+
... radial_velocity=127*u.km/u.s)
|
| 158 |
+
>>> gc.reflex_correct(c) # doctest: +FLOAT_CMP
|
| 159 |
+
<SkyCoord (ICRS): (ra, dec, distance) in (deg, deg, pc)
|
| 160 |
+
(162., -17., 172.)
|
| 161 |
+
(pm_ra_cosdec, pm_dec, radial_velocity) in (mas / yr, mas / yr, km / s)
|
| 162 |
+
(99.20380175, 159.68500525, -65.48721942)>
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
Transforming a proper motion covariance matrix to a new coordinate frame
|
| 166 |
+
------------------------------------------------------------------------
|
| 167 |
+
|
| 168 |
+
When working with Gaia or other astrometric data sets, you may need to transform
|
| 169 |
+
the reported covariance matrix between proper motion components into a new
|
| 170 |
+
coordinate system. For example, Gaia data are provided in the
|
| 171 |
+
`~astropy.coordinates.ICRS` (equatorial) coordinate frame, but for Galactic
|
| 172 |
+
science, we often want to instead work in the `~astropy.coordinates.Galactic`
|
| 173 |
+
coordinate system. For this and other transformations that only require a
|
| 174 |
+
rotation (i.e. the origin doesn't change), the astrometric covariance matrix can
|
| 175 |
+
be transformed exactly through a projection of the rotation onto the tangent
|
| 176 |
+
plane at a given location. The details of this procedure are explained in `this
|
| 177 |
+
document from the Gaia data processing team
|
| 178 |
+
<https://gea.esac.esa.int/archive/documentation/GDR2/Data_processing/chap_cu3ast/sec_cu3ast_intro/ssec_cu3ast_intro_tansforms.html>`_,
|
| 179 |
+
and this functionality is implemented in `gala`. Let's first create a coordinate
|
| 180 |
+
object to transform::
|
| 181 |
+
|
| 182 |
+
>>> c = coord.SkyCoord(ra=62*u.deg,
|
| 183 |
+
... dec=17*u.deg,
|
| 184 |
+
... pm_ra_cosdec=1*u.mas/u.yr,
|
| 185 |
+
... pm_dec=3*u.mas/u.yr)
|
| 186 |
+
|
| 187 |
+
and a covariance matrix for the proper motion components, for example, as would
|
| 188 |
+
be constructed from a single row from a Gaia data release source catalog::
|
| 189 |
+
|
| 190 |
+
>>> cov = np.array([[0.53510132, 0.16637034],
|
| 191 |
+
... [0.16637034, 1.1235292 ]])
|
| 192 |
+
|
| 193 |
+
This matrix specifies the 2D error distribution for the proper motion
|
| 194 |
+
measurement *in the ICRS frame*. To transform this matrix to, e.g., the Galactic
|
| 195 |
+
coordinate system, use the function
|
| 196 |
+
`~gala.coordinates.transform_pm_cov`::
|
| 197 |
+
|
| 198 |
+
>>> gc.transform_pm_cov(c, cov, coord.Galactic()) # doctest: +FLOAT_CMP
|
| 199 |
+
array([[ 0.69450047, -0.309945 ],
|
| 200 |
+
[-0.309945 , 0.96413005]])
|
| 201 |
+
|
| 202 |
+
Note that this also works for all of the great circle or stellar stream
|
| 203 |
+
coordinate frames implemented in `gala`::
|
| 204 |
+
|
| 205 |
+
>>> gc.transform_pm_cov(c, cov, gc.GD1Koposov10()) # doctest: +FLOAT_CMP
|
| 206 |
+
array([[1.10838914, 0.19067958],
|
| 207 |
+
[0.19067958, 0.55024138]])
|
| 208 |
+
|
| 209 |
+
This works for array-valued coordinates as well, so try to avoid looping over
|
| 210 |
+
this function and instead apply it to array-valued coordinate objects.
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
References
|
| 214 |
+
----------
|
| 215 |
+
|
| 216 |
+
* `A 2MASS All-Sky View of the Sagittarius Dwarf Galaxy: I. Morphology of the
|
| 217 |
+
Sagittarius Core and Tidal Arms <http://arxiv.org/abs/astro-ph/0304198>`_
|
| 218 |
+
* `The Orbit of the Orphan Stream <http://arxiv.org/abs/1001.0576>`_
|
| 219 |
+
* `Constraining the Milky Way potential with a 6-D phase-space map of the GD-1
|
| 220 |
+
stellar stream <https://arxiv.org/abs/0907.1085>`_
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
Using gala.coordinates
|
| 224 |
+
======================
|
| 225 |
+
|
| 226 |
+
More details are provided in the linked pages below:
|
| 227 |
+
|
| 228 |
+
.. toctree::
|
| 229 |
+
:maxdepth: 1
|
| 230 |
+
|
| 231 |
+
greatcircle
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
.. _gala-coordinates-api:
|
| 235 |
+
|
| 236 |
+
API
|
| 237 |
+
===
|
| 238 |
+
|
| 239 |
+
.. automodapi:: gala.coordinates
|
| 240 |
+
:no-inheritance-diagram:
|
| 241 |
+
:no-main-docstr:
|
gala/source/docs/docs.rst
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _gala-docs:
|
| 2 |
+
|
| 3 |
+
=================
|
| 4 |
+
Building the docs
|
| 5 |
+
=================
|
| 6 |
+
|
| 7 |
+
The documentation is built by Sphinx. To start, make sure you install all of the docs dependencies::
|
| 8 |
+
|
| 9 |
+
pip install -e ".[docs]"
|
| 10 |
+
|
| 11 |
+
Then change directory into the ``docs/`` path. You now have to execute the
|
| 12 |
+
tutorials, make animations needed by the documentation, and run the docs build::
|
| 13 |
+
|
| 14 |
+
make exectutorials
|
| 15 |
+
make animations
|
| 16 |
+
make html
|
gala/source/docs/dynamics/actionangle.rst
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _gala-actionangle:
|
| 2 |
+
|
| 3 |
+
************************************************
|
| 4 |
+
Transforming to actions, angles, and frequencies
|
| 5 |
+
************************************************
|
| 6 |
+
|
| 7 |
+
Introduction
|
| 8 |
+
============
|
| 9 |
+
|
| 10 |
+
Regular orbits permit a (local) transformation to a set of canonical coordinates
|
| 11 |
+
such that the momenta are independent, isolating integrals of motion (the
|
| 12 |
+
actions, :math:`\boldsymbol{J}`) and the conjugate coordinate variables (the
|
| 13 |
+
angles, :math:`\boldsymbol{\theta}`) linearly increase with time. Action-angle
|
| 14 |
+
coordinates are useful for a number of applications because the equations of motion are very simple:
|
| 15 |
+
|
| 16 |
+
.. math::
|
| 17 |
+
|
| 18 |
+
H &= H(\boldsymbol{J})\\
|
| 19 |
+
\dot{\boldsymbol{J}} &= -\frac{\partial H}{\partial \boldsymbol{\theta}} = 0\\
|
| 20 |
+
\dot{\boldsymbol{\theta}} &= \frac{\partial H}{\partial \boldsymbol{J}} = \boldsymbol{\Omega}(\boldsymbol{J}) = {\rm constant}
|
| 21 |
+
|
| 22 |
+
Analytic transformations from phase-space to action-angle coordinates are only
|
| 23 |
+
known for a few simple cases where the gravitational potential is separable or
|
| 24 |
+
has many symmetries. However, astronomical systems can often be approximately axisymmetric or triaxial, or have complex radial profiles that are not captured by these simple gravitational potentials where the transformations are known.
|
| 25 |
+
|
| 26 |
+
Several numerical methods have been developed over recent years to enable
|
| 27 |
+
approximate transformations between ordinary position and velocity to
|
| 28 |
+
action-angle coordinates -- see [sanders16]_ for a summary of these methods.
|
| 29 |
+
In Gala, we have implemented the method described in [sanders14]_ -- later in
|
| 30 |
+
[sanders16]_ named the "O2GF" method -- for computing actions and angles from
|
| 31 |
+
numerically integrated orbits. Gala also provides an interface to the `galpy
|
| 32 |
+
<https://github.com/jobovy/galpy>`_ implementation of the "Staeckel Fudge"
|
| 33 |
+
method, which is much faster but only useful for axisymmetric or spherical
|
| 34 |
+
potentials.
|
| 35 |
+
|
| 36 |
+
The O2GF action solver
|
| 37 |
+
======================
|
| 38 |
+
|
| 39 |
+
As mentioned above, this method was first introduced in [sanders14]_ and later
|
| 40 |
+
described in [sanders16]_. This method is very general in that it works with any
|
| 41 |
+
numerically-integrated orbital time series. However, it is slower than other
|
| 42 |
+
approximate methods: If your system is spherical or axisymmetric, other methods
|
| 43 |
+
will perform much better. If your system is triaxial, this method is your best
|
| 44 |
+
option. We demonstrate this method below with two qualitatively different
|
| 45 |
+
orbits:
|
| 46 |
+
|
| 47 |
+
* :ref:`tube-axisymmetric`
|
| 48 |
+
* :ref:`tube-triaxial`
|
| 49 |
+
|
| 50 |
+
(see also [binneytremaine]_ and [mcgill90]_ for more context). For the examples
|
| 51 |
+
below, we will use the `~gala.units.galactic` unit system and assume the
|
| 52 |
+
following imports have been executed::
|
| 53 |
+
|
| 54 |
+
>>> import astropy.coordinates as coord
|
| 55 |
+
>>> import astropy.units as u
|
| 56 |
+
>>> import matplotlib.pyplot as plt
|
| 57 |
+
>>> import numpy as np
|
| 58 |
+
>>> import gala.dynamics as gd
|
| 59 |
+
>>> import gala.integrate as gi
|
| 60 |
+
>>> import gala.potential as gp
|
| 61 |
+
>>> from gala.units import galactic
|
| 62 |
+
|
| 63 |
+
For many more options for action calculation, see
|
| 64 |
+
`tact <https://github.com/jls713/tact>`_.
|
| 65 |
+
|
| 66 |
+
.. _tube-axisymmetric:
|
| 67 |
+
|
| 68 |
+
A tube orbit in an axisymmetric potential
|
| 69 |
+
-----------------------------------------
|
| 70 |
+
|
| 71 |
+
For an example of an axisymmetric potential, we use a flattened logarithmic
|
| 72 |
+
potential:
|
| 73 |
+
|
| 74 |
+
.. math::
|
| 75 |
+
|
| 76 |
+
\Phi(x,y,z) = \frac{1}{2}v_{\rm c}^2\ln (x^2 + y^2 + (z/q)^2 + r_h^2)
|
| 77 |
+
|
| 78 |
+
with parameters
|
| 79 |
+
|
| 80 |
+
.. math::
|
| 81 |
+
|
| 82 |
+
v_{\rm c} &= 150~{\rm km}~{\rm s}^{-1}\\
|
| 83 |
+
q &= 0.9\\
|
| 84 |
+
r_h &= 0
|
| 85 |
+
|
| 86 |
+
For the orbit, we use initial conditions
|
| 87 |
+
|
| 88 |
+
.. math::
|
| 89 |
+
|
| 90 |
+
\boldsymbol{r} &= (8, 0, 0)~{\rm kpc}\\
|
| 91 |
+
\boldsymbol{v} &= (75, 150, 50)~{\rm km}~{\rm s}^{-1}
|
| 92 |
+
|
| 93 |
+
We first create a potential and set up our initial conditions::
|
| 94 |
+
|
| 95 |
+
>>> pot = gp.LogarithmicPotential(
|
| 96 |
+
... v_c=150*u.km/u.s, q1=1., q2=1., q3=0.9, r_h=0,
|
| 97 |
+
... units=galactic)
|
| 98 |
+
>>> w0 = gd.PhaseSpacePosition(pos=[8, 0, 0.]*u.kpc,
|
| 99 |
+
... vel=[75, 150, 50.]*u.km/u.s)
|
| 100 |
+
|
| 101 |
+
We will now integrate the orbit and plot it in the meridional plane::
|
| 102 |
+
|
| 103 |
+
>>> w = gp.Hamiltonian(pot).integrate_orbit(w0, dt=0.5, n_steps=10000)
|
| 104 |
+
>>> cyl = w.represent_as('cylindrical')
|
| 105 |
+
>>> fig = cyl.plot(['rho', 'z'], linestyle='-') # doctest: +SKIP
|
| 106 |
+
|
| 107 |
+
.. plot::
|
| 108 |
+
:align: center
|
| 109 |
+
:context: close-figs
|
| 110 |
+
:width: 60%
|
| 111 |
+
|
| 112 |
+
import astropy.coordinates as coord
|
| 113 |
+
import astropy.units as u
|
| 114 |
+
import matplotlib.pyplot as plt
|
| 115 |
+
import numpy as np
|
| 116 |
+
import gala.potential as gp
|
| 117 |
+
import gala.dynamics as gd
|
| 118 |
+
from gala.units import galactic
|
| 119 |
+
|
| 120 |
+
pot = gp.LogarithmicPotential(v_c=150*u.km/u.s, q1=1., q2=1., q3=0.9, r_h=0,
|
| 121 |
+
units=galactic)
|
| 122 |
+
w0 = gd.PhaseSpacePosition(pos=[8, 0, 0.]*u.kpc,
|
| 123 |
+
vel=[75, 150, 50.]*u.km/u.s)
|
| 124 |
+
|
| 125 |
+
w = gp.Hamiltonian(pot).integrate_orbit(w0, dt=0.5, n_steps=10000)
|
| 126 |
+
cyl = w.represent_as('cylindrical')
|
| 127 |
+
cyl.plot(['rho', 'z'], linestyle='-')
|
| 128 |
+
|
| 129 |
+
To solve for the actions in the true potential, we first compute the actions in
|
| 130 |
+
a "toy" potential -- a potential in which we can compute the actions and angles
|
| 131 |
+
analytically. The two simplest potentials for which this is possible are the
|
| 132 |
+
`~gala.potential.potential.IsochronePotential` and
|
| 133 |
+
`~gala.potential.potential.HarmonicOscillatorPotential`. We will use the
|
| 134 |
+
Isochrone potential as our toy potential for tube orbits and the harmonic
|
| 135 |
+
oscillator for box orbits.
|
| 136 |
+
|
| 137 |
+
We start by finding the parameters of the toy potential (Isochrone in this case)
|
| 138 |
+
by minimizing the dispersion in energy for the orbit::
|
| 139 |
+
|
| 140 |
+
>>> toy_potential = gd.fit_isochrone(w)
|
| 141 |
+
>>> toy_potential
|
| 142 |
+
<IsochronePotential: m=1.24e+11, b=4.02 (kpc,Myr,solMass,rad)>
|
| 143 |
+
|
| 144 |
+
The actions and angles in this potential are not the true actions, but will only
|
| 145 |
+
serve as an approximation. This can be seen in the angles: the orbit in the true
|
| 146 |
+
angles would be perfectly straight lines with slope equal to the frequencies.
|
| 147 |
+
Instead, the orbit is wobbly in the toy potential angles::
|
| 148 |
+
|
| 149 |
+
>>> toy_actions,toy_angles,toy_freqs = toy_potential.action_angle(w)
|
| 150 |
+
>>> fig,ax = plt.subplots(1,1,figsize=(5,5))
|
| 151 |
+
>>> ax.plot(toy_angles[0], toy_angles[2], linestyle='none', marker=',') # doctest: +SKIP
|
| 152 |
+
>>> ax.set_xlim(0,2*np.pi) # doctest: +SKIP
|
| 153 |
+
>>> ax.set_ylim(0,2*np.pi) # doctest: +SKIP
|
| 154 |
+
>>> ax.set_xlabel(r"$\theta_1$ [rad]") # doctest: +SKIP
|
| 155 |
+
>>> ax.set_ylabel(r"$\theta_3$ [rad]") # doctest: +SKIP
|
| 156 |
+
|
| 157 |
+
.. plot::
|
| 158 |
+
:align: center
|
| 159 |
+
:context: close-figs
|
| 160 |
+
:width: 60%
|
| 161 |
+
|
| 162 |
+
toy_potential = gd.fit_isochrone(w)
|
| 163 |
+
toy_actions,toy_angles,toy_freqs = toy_potential.action_angle(w)
|
| 164 |
+
fig,ax = plt.subplots(1,1,figsize=(5,5))
|
| 165 |
+
ax.plot(toy_angles[0], toy_angles[2], linestyle='none', marker=',')
|
| 166 |
+
ax.set_xlim(0,2*np.pi)
|
| 167 |
+
ax.set_ylim(0,2*np.pi)
|
| 168 |
+
ax.set_xlabel(r"$\theta_1$ [rad]")
|
| 169 |
+
ax.set_ylabel(r"$\theta_3$ [rad]")
|
| 170 |
+
fig.tight_layout()
|
| 171 |
+
|
| 172 |
+
This can also be seen in the value of the action variables, which are not
|
| 173 |
+
time-independent in the toy potential::
|
| 174 |
+
|
| 175 |
+
>>> fig,ax = plt.subplots(1,1)
|
| 176 |
+
>>> ax.plot(w.t, toy_actions[0], marker='') # doctest: +SKIP
|
| 177 |
+
>>> ax.set_xlabel(r"$t$ [Myr]") # doctest: +SKIP
|
| 178 |
+
>>> ax.set_ylabel(r"$J_1$ [rad]") # doctest: +SKIP
|
| 179 |
+
|
| 180 |
+
.. plot::
|
| 181 |
+
:align: center
|
| 182 |
+
:context: close-figs
|
| 183 |
+
:width: 60%
|
| 184 |
+
|
| 185 |
+
fig,ax = plt.subplots(1,1)
|
| 186 |
+
ax.plot(w.t, toy_actions[0].to(u.km/u.s*u.kpc), marker='')
|
| 187 |
+
ax.set_xlabel(r"$t$ [Myr]")
|
| 188 |
+
ax.set_ylabel(r"$J_1$ [kpc km/s]")
|
| 189 |
+
fig.tight_layout()
|
| 190 |
+
|
| 191 |
+
We can now find approximations to the actions in the true potential. We have to
|
| 192 |
+
choose the maximum integer vector norm, `N_max`, which here we arbitrarily set
|
| 193 |
+
to 8. This will change depending on the convergence of the action correction
|
| 194 |
+
(the properties of the orbit and potential) and the accuracy desired::
|
| 195 |
+
|
| 196 |
+
>>> result = gd.find_actions_o2gf(w, N_max=8, toy_potential=toy_potential) # doctest: +SKIP
|
| 197 |
+
>>> result.keys() # doctest: +SKIP
|
| 198 |
+
dict_keys(['Sn', 'nvecs', 'freqs', 'dSn_dJ', 'angles', 'actions'])
|
| 199 |
+
|
| 200 |
+
The value of the actions, frequencies, and the angles at t=0 are returned in
|
| 201 |
+
the result dictionary::
|
| 202 |
+
|
| 203 |
+
>>> result['actions'] # doctest: +SKIP
|
| 204 |
+
<Quantity [ 0.12472277, 1.22725461, 0.05847431] kpc2 solMass / Myr>
|
| 205 |
+
|
| 206 |
+
To visualize how the actions are computed, we again plot the actions in the
|
| 207 |
+
toy potential and then plot the "corrected" actions -- the approximation to the
|
| 208 |
+
actions computed using this machinery::
|
| 209 |
+
|
| 210 |
+
>>> nvecs = gd.generate_n_vectors(8, dx=1, dy=2, dz=2) # doctest: +SKIP
|
| 211 |
+
>>> act_correction = nvecs.T[...,None] * result['Sn'][None,:,None] * np.cos(nvecs.dot(toy_angles))[None] # doctest: +SKIP
|
| 212 |
+
>>> action_approx = toy_actions - 2*np.sum(act_correction, axis=1)*u.kpc**2/u.Myr # doctest: +SKIP
|
| 213 |
+
>>>
|
| 214 |
+
>>> fig,ax = plt.subplots(1,1) # doctest: +SKIP
|
| 215 |
+
>>> ax.plot(w.t, toy_actions[0].to(u.km/u.s*u.kpc), marker='', label='$J_1$') # doctest: +SKIP
|
| 216 |
+
>>> ax.plot(w.t, action_approx[0].to(u.km/u.s*u.kpc), marker='', label="$J_1'$") # doctest: +SKIP
|
| 217 |
+
>>> ax.set_xlabel(r"$t$ [Myr]") # doctest: +SKIP
|
| 218 |
+
>>> ax.set_ylabel(r"[kpc ${\rm M}_\odot$ km/s]") # doctest: +SKIP
|
| 219 |
+
>>> ax.legend() # doctest: +SKIP
|
| 220 |
+
|
| 221 |
+
.. plot::
|
| 222 |
+
:align: center
|
| 223 |
+
:context: close-figs
|
| 224 |
+
:width: 60%
|
| 225 |
+
|
| 226 |
+
import warnings
|
| 227 |
+
with warnings.catch_warnings(record=True):
|
| 228 |
+
warnings.simplefilter("ignore")
|
| 229 |
+
result = gd.find_actions_o2gf(w, N_max=8, toy_potential=toy_potential)
|
| 230 |
+
|
| 231 |
+
nvecs = gd.generate_n_vectors(8, dx=1, dy=2, dz=2)
|
| 232 |
+
act_correction = nvecs.T[...,None] * result['Sn'][0][None,:,None] * np.cos(nvecs.dot(toy_angles))[None]
|
| 233 |
+
action_approx = toy_actions - 2*np.sum(act_correction, axis=1)*u.kpc**2/u.Myr
|
| 234 |
+
fig,ax = plt.subplots(1,1)
|
| 235 |
+
ax.plot(w.t, toy_actions[0].to(u.km/u.s*u.kpc), marker='', label='$J_1$')
|
| 236 |
+
ax.plot(w.t, action_approx[0].to(u.km/u.s*u.kpc), marker='', label="$J_1'$")
|
| 237 |
+
ax.set_xlabel(r"$t$ [Myr]")
|
| 238 |
+
ax.set_ylabel(r"[kpc ${\rm M}_\odot$ km/s]")
|
| 239 |
+
ax.legend()
|
| 240 |
+
|
| 241 |
+
Above the blue line represents the approximation of the actions in the true
|
| 242 |
+
potential.
|
| 243 |
+
|
| 244 |
+
.. _tube-triaxial:
|
| 245 |
+
|
| 246 |
+
A tube orbit in a triaxial potential
|
| 247 |
+
------------------------------------
|
| 248 |
+
|
| 249 |
+
The same procedure works for regular orbits in more complex potentials. We
|
| 250 |
+
demonstrate this below by repeating the above in a triaxial potential. We again
|
| 251 |
+
use a logarithmic potential, but with flattening along two dimensions:
|
| 252 |
+
|
| 253 |
+
.. math::
|
| 254 |
+
|
| 255 |
+
\Phi(x,y,z) = \frac{1}{2}v_{\rm c}^2\ln ((x/q_1)^2 + (y/q_2)^2 + (z/q_3)^2)
|
| 256 |
+
|
| 257 |
+
with parameter values:
|
| 258 |
+
|
| 259 |
+
.. math::
|
| 260 |
+
|
| 261 |
+
v_{\rm c} &= 150~{\rm km}~{\rm s}^{-1}\\
|
| 262 |
+
q_1 &= 1\\
|
| 263 |
+
q_2 &= 0.9\\
|
| 264 |
+
q_3 &= 0.8\\
|
| 265 |
+
r_h &= 0
|
| 266 |
+
|
| 267 |
+
and the same initial conditions as above:
|
| 268 |
+
|
| 269 |
+
.. math::
|
| 270 |
+
|
| 271 |
+
\boldsymbol{r} &= (8, 0, 0)~{\rm kpc}\\
|
| 272 |
+
\boldsymbol{v} &= (75, 150, 50)~{\rm km}~{\rm s}^{-1}
|
| 273 |
+
|
| 274 |
+
.. plot::
|
| 275 |
+
:align: center
|
| 276 |
+
:include-source:
|
| 277 |
+
:width: 60%
|
| 278 |
+
|
| 279 |
+
import astropy.coordinates as coord
|
| 280 |
+
import astropy.units as u
|
| 281 |
+
import matplotlib.pyplot as plt
|
| 282 |
+
import numpy as np
|
| 283 |
+
import gala.potential as gp
|
| 284 |
+
import gala.dynamics as gd
|
| 285 |
+
from gala.units import galactic
|
| 286 |
+
|
| 287 |
+
# define potential
|
| 288 |
+
pot = gp.LogarithmicPotential(v_c=150*u.km/u.s, q1=1., q2=0.9, q3=0.8, r_h=0,
|
| 289 |
+
units=galactic)
|
| 290 |
+
|
| 291 |
+
# define initial conditions
|
| 292 |
+
w0 = gd.PhaseSpacePosition(pos=[8, 0, 0.]*u.kpc,
|
| 293 |
+
vel=[75, 150, 50.]*u.km/u.s)
|
| 294 |
+
|
| 295 |
+
# integrate orbit
|
| 296 |
+
w = gp.Hamiltonian(pot).integrate_orbit(w0, dt=0.5, n_steps=10000)
|
| 297 |
+
|
| 298 |
+
# solve for toy potential parameters
|
| 299 |
+
toy_potential = gd.fit_isochrone(w)
|
| 300 |
+
|
| 301 |
+
# compute the actions,angles in the toy potential
|
| 302 |
+
toy_actions,toy_angles,toy_freqs = toy_potential.action_angle(w)
|
| 303 |
+
|
| 304 |
+
# find approximations to the actions in the true potential
|
| 305 |
+
import warnings
|
| 306 |
+
with warnings.catch_warnings(record=True):
|
| 307 |
+
warnings.simplefilter("ignore")
|
| 308 |
+
result = gd.find_actions_o2gf(w, N_max=8, toy_potential=toy_potential)
|
| 309 |
+
|
| 310 |
+
# for visualization, compute the action correction used to transform the
|
| 311 |
+
# toy potential actions to the approximate true potential actions
|
| 312 |
+
nvecs = gd.generate_n_vectors(8, dx=1, dy=2, dz=2)
|
| 313 |
+
act_correction = nvecs.T[...,None] * result['Sn'][0][None,:,None] * np.cos(nvecs.dot(toy_angles))[None]
|
| 314 |
+
action_approx = toy_actions - 2*np.sum(act_correction, axis=1)*u.kpc**2/u.Myr
|
| 315 |
+
|
| 316 |
+
fig,axes = plt.subplots(3,1,figsize=(6,14))
|
| 317 |
+
|
| 318 |
+
for i,ax in enumerate(axes):
|
| 319 |
+
ax.plot(w.t, toy_actions[i].to(u.km/u.s*u.kpc), marker='', label='$J_{}$'.format(i+1))
|
| 320 |
+
ax.plot(w.t, action_approx[i].to(u.km/u.s*u.kpc), marker='', label="$J_{}'$".format(i+1))
|
| 321 |
+
ax.set_ylabel(r"[kpc ${\rm M}_\odot$ km/s]")
|
| 322 |
+
ax.legend(loc='upper left')
|
| 323 |
+
|
| 324 |
+
ax.set_xlabel(r"$t$ [Myr]")
|
| 325 |
+
fig.tight_layout()
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
Using the Staeckel Fudge in Galpy
|
| 329 |
+
=================================
|
| 330 |
+
|
| 331 |
+
Gala can transform its ``Orbit`` and ``Potential`` objects into `Galpy <https://github.com/jobovy/galpy>`_ ``Orbit`` and ``Potential`` objects, making it possible to easily use the "Staeckel Fudge" [binney12]_ implementation in Galpy. This method, as
|
| 332 |
+
implemented, is only applicable for axisymmetric systems, but is *much* faster
|
| 333 |
+
than the O2GF method for estimating actions, angles, and frequencies from
|
| 334 |
+
phase-space positions. As an example of this functionality, below we will
|
| 335 |
+
compute the vertical frequency as a function of action for a grid of orbits in a
|
| 336 |
+
two-component model for a galactic potential (a disk + halo model).
|
| 337 |
+
|
| 338 |
+
We will start by defining the potential model::
|
| 339 |
+
|
| 340 |
+
>>> halo = gp.NFWPotential.from_M200_c(
|
| 341 |
+
... M200=1e12*u.Msun, c=15,
|
| 342 |
+
... units=galactic
|
| 343 |
+
... )
|
| 344 |
+
>>> disk = gp.MN3ExponentialDiskPotential(
|
| 345 |
+
... m=8e10*u.Msun, h_R=3.5*u.kpc, h_z=0.4*u.kpc,
|
| 346 |
+
... units=galactic
|
| 347 |
+
... )
|
| 348 |
+
>>> pot = halo + disk
|
| 349 |
+
|
| 350 |
+
We next define a grid of orbital initial conditions with close to the circular
|
| 351 |
+
velocity but varying vertical velocities::
|
| 352 |
+
|
| 353 |
+
>>> vcirc = pot.circular_velocity([8, 0, 0])
|
| 354 |
+
>>> vz_grid = np.linspace(0.5, 200, 64) * u.km/u.s
|
| 355 |
+
>>> xyz = np.repeat([[8., 0, 0]], len(vz_grid), axis=0).T * u.kpc
|
| 356 |
+
>>> vxyz = np.repeat([[0, 1.1, 0]], len(vz_grid), axis=0).T * vcirc
|
| 357 |
+
>>> vxyz[2] = vz_grid
|
| 358 |
+
>>> w0 = gd.PhaseSpacePosition(xyz, vxyz)
|
| 359 |
+
|
| 360 |
+
We can now integrate these orbits in the total potential. Note that we can
|
| 361 |
+
specify the integrator using a string name::
|
| 362 |
+
|
| 363 |
+
>>> orbits = pot.integrate_orbit(
|
| 364 |
+
... w0, dt=1, t1=0, t2=4*u.Gyr,
|
| 365 |
+
... Integrator='dopri853'
|
| 366 |
+
... )
|
| 367 |
+
>>> orbits.cylindrical.plot(['rho', 'z'], alpha=0.5, marker=',') # doctest: +SKIP
|
| 368 |
+
|
| 369 |
+
.. plot::
|
| 370 |
+
:align: center
|
| 371 |
+
:width: 60%
|
| 372 |
+
:context: close-figs
|
| 373 |
+
|
| 374 |
+
import astropy.coordinates as coord
|
| 375 |
+
import astropy.units as u
|
| 376 |
+
import matplotlib.pyplot as plt
|
| 377 |
+
import numpy as np
|
| 378 |
+
import gala.potential as gp
|
| 379 |
+
import gala.integrate as gi
|
| 380 |
+
import gala.dynamics as gd
|
| 381 |
+
from gala.units import galactic
|
| 382 |
+
|
| 383 |
+
halo = gp.NFWPotential.from_M200_c(
|
| 384 |
+
M200=1e12*u.Msun, c=15,
|
| 385 |
+
units=galactic
|
| 386 |
+
)
|
| 387 |
+
disk = gp.MN3ExponentialDiskPotential(
|
| 388 |
+
m=8e10*u.Msun, h_R=3.5*u.kpc, h_z=0.4*u.kpc,
|
| 389 |
+
units=galactic
|
| 390 |
+
)
|
| 391 |
+
pot = halo + disk
|
| 392 |
+
|
| 393 |
+
vcirc = pot.circular_velocity([8, 0, 0])
|
| 394 |
+
vz_grid = np.linspace(0.5, 200, 64) * u.km/u.s
|
| 395 |
+
xyz = np.repeat([[8., 0, 0]], len(vz_grid), axis=0).T * u.kpc
|
| 396 |
+
vxyz = np.repeat([[0, 1.1, 0]], len(vz_grid), axis=0).T * vcirc
|
| 397 |
+
vxyz[2] = vz_grid
|
| 398 |
+
w0 = gd.PhaseSpacePosition(xyz, vxyz)
|
| 399 |
+
|
| 400 |
+
orbits = pot.integrate_orbit(
|
| 401 |
+
w0, dt=1, t1=0, t2=4*u.Gyr,
|
| 402 |
+
Integrator='dopri853'
|
| 403 |
+
)
|
| 404 |
+
orbits.cylindrical.plot(['rho', 'z'], alpha=0.5, marker=',')
|
| 405 |
+
|
| 406 |
+
|
| 407 |
+
With the orbits in hand, we can compute the approximate actions, angles, and
|
| 408 |
+
frequencies with the Staeckel Fudge using Galpy (for more information, see the
|
| 409 |
+
`Galpy documentation <https://docs.galpy.org/en/v1.7.2/actionAngle.html>`_):
|
| 410 |
+
|
| 411 |
+
.. doctest-requires:: galpy
|
| 412 |
+
|
| 413 |
+
>>> from gala.dynamics.actionangle import get_staeckel_fudge_delta
|
| 414 |
+
>>> from galpy.actionAngle import actionAngleStaeckel
|
| 415 |
+
>>> galpy_potential = pot.as_interop("galpy")
|
| 416 |
+
>>> J = np.zeros((3, orbits.norbits))
|
| 417 |
+
>>> Omega = np.zeros((3, orbits.norbits))
|
| 418 |
+
>>> for n, orbit in enumerate(orbits.orbit_gen()): # doctest: +SKIP
|
| 419 |
+
... o = orbit.to_galpy_orbit()
|
| 420 |
+
... delta = get_staeckel_fudge_delta(pot, orbit)
|
| 421 |
+
... staeckel = actionAngleStaeckel(pot=galpy_potential, delta=delta)
|
| 422 |
+
... af = staeckel.actionsFreqs(o)
|
| 423 |
+
... af = np.mean(np.stack(af), axis=1)
|
| 424 |
+
... J[:3, n] = af[:3]
|
| 425 |
+
... Omega[:3, n] = af[3:]
|
| 426 |
+
|
| 427 |
+
Let's visualize the dependence of the vertical action on the value of the
|
| 428 |
+
vertical velocity we used as initial conditions:
|
| 429 |
+
|
| 430 |
+
.. doctest-requires:: galpy
|
| 431 |
+
|
| 432 |
+
>>> plt.plot(w0.v_z, J[2]) # doctest: +SKIP
|
| 433 |
+
|
| 434 |
+
.. plot::
|
| 435 |
+
:align: center
|
| 436 |
+
:width: 60%
|
| 437 |
+
:context: close-figs
|
| 438 |
+
|
| 439 |
+
from gala.dynamics.actionangle import get_staeckel_fudge_delta
|
| 440 |
+
from galpy.actionAngle import actionAngleStaeckel
|
| 441 |
+
|
| 442 |
+
galpy_potential = pot.as_interop("galpy")
|
| 443 |
+
J = np.zeros((3, orbits.norbits))
|
| 444 |
+
Omega = np.zeros((3, orbits.norbits))
|
| 445 |
+
for n, orbit in enumerate(orbits.orbit_gen()):
|
| 446 |
+
o = orbit.to_galpy_orbit()
|
| 447 |
+
delta = get_staeckel_fudge_delta(pot, orbit)
|
| 448 |
+
staeckel = actionAngleStaeckel(pot=galpy_potential, delta=delta)
|
| 449 |
+
af = staeckel.actionsFreqs(o)
|
| 450 |
+
af = np.mean(np.stack(af), axis=1)
|
| 451 |
+
|
| 452 |
+
J[:3, n] = af[:3]
|
| 453 |
+
Omega[:3, n] = af[3:]
|
| 454 |
+
|
| 455 |
+
fig, ax = plt.subplots(figsize=(6, 6), constrained_layout=True)
|
| 456 |
+
ax.plot(w0.v_z, J[2])
|
| 457 |
+
ax.set_xlabel(f"$v_z$ [{w0.v_z.unit:latex_inline}]")
|
| 458 |
+
ax.set_ylabel(rf"$J_z$")
|
| 459 |
+
|
| 460 |
+
|
| 461 |
+
The overall trend looks right, but what is that weird break that occurs around
|
| 462 |
+
:math:`v_z` ~ 120 km/s? Let's visualize orbits with initial conditions just next to and
|
| 463 |
+
within this region:
|
| 464 |
+
|
| 465 |
+
.. doctest-requires:: galpy
|
| 466 |
+
|
| 467 |
+
>>> i1 = np.abs(w0.v_z.value - 120).argmin()
|
| 468 |
+
>>> i2 = np.abs(w0.v_z.value - 100).argmin()
|
| 469 |
+
>>> orbits[:, i1].cylindrical.plot(['rho', 'z'], alpha=0.5, marker=',') # doctest: +SKIP
|
| 470 |
+
>>> orbits[:, i2].cylindrical.plot(['rho', 'z'], alpha=0.5, marker=',') # doctest: +SKIP
|
| 471 |
+
|
| 472 |
+
.. plot::
|
| 473 |
+
:align: center
|
| 474 |
+
:width: 90%
|
| 475 |
+
:context: close-figs
|
| 476 |
+
|
| 477 |
+
fig, axes = plt.subplots(1, 2, figsize=(10, 5),
|
| 478 |
+
sharex=True, sharey=True,
|
| 479 |
+
constrained_layout=True)
|
| 480 |
+
|
| 481 |
+
i1 = np.abs(w0.v_z.value - 120).argmin()
|
| 482 |
+
i2 = np.abs(w0.v_z.value - 100).argmin()
|
| 483 |
+
orbits[:, i1].cylindrical.plot(['rho', 'z'], alpha=0.5, marker=',', axes=[axes[0]]);
|
| 484 |
+
orbits[:, i2].cylindrical.plot(['rho', 'z'], alpha=0.5, marker=',', axes=[axes[1]]);
|
| 485 |
+
|
| 486 |
+
Aha! This region is special: it is a resonance in the potential. Orbits in this region
|
| 487 |
+
of phase-space have qualitatively different behavior than those outside of this region
|
| 488 |
+
because they are trapped by the resonance. For these orbits, where strong potential
|
| 489 |
+
resonances occur, the Staeckel Fudge approximation will return incorrect and potentially
|
| 490 |
+
misleading action, angle, and frequency values.
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
References
|
| 494 |
+
==========
|
| 495 |
+
|
| 496 |
+
.. [binney12] Binney (2012) `Actions for axisymmetric potentials
|
| 497 |
+
<https://ui.adsabs.harvard.edu/abs/2012MNRAS.426.1324B/abstract>`_
|
| 498 |
+
.. [sanders14] Sanders & Binney (2014) `Actions, angles and frequencies for numerically integrated orbits <http://arxiv.org/abs/1401.3600>`_
|
| 499 |
+
.. [sanders16] Sanders & Binney (2016) `A review of action estimation methods for galactic dynamics <https://ui.adsabs.harvard.edu/abs/2016MNRAS.457.2107S/abstract>`_
|
| 500 |
+
.. [binneytremaine] Binney & Tremaine (2008) `Galactic Dynamics <http://press.princeton.edu/titles/8697.html>`_
|
| 501 |
+
.. [mcgill90] McGill & Binney (1990) `Torus construction in general gravitational potentials <http://articles.adsabs.harvard.edu/cgi-bin/nph-iarticle_query?1990MNRAS.244..634M&data_type=PDF_HIGH&whole_paper=YES&type=PRINTER&filetype=.pdf>`_
|
gala/source/docs/dynamics/index.rst
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. include:: references.txt
|
| 2 |
+
|
| 3 |
+
.. _gala-dynamics:
|
| 4 |
+
|
| 5 |
+
********************************
|
| 6 |
+
Dynamics (`gala.dynamics`)
|
| 7 |
+
********************************
|
| 8 |
+
|
| 9 |
+
For the examples below the following imports have already been executed::
|
| 10 |
+
|
| 11 |
+
>>> import astropy.units as u
|
| 12 |
+
>>> import numpy as np
|
| 13 |
+
>>> import gala.potential as gp
|
| 14 |
+
>>> import gala.dynamics as gd
|
| 15 |
+
>>> from gala.units import galactic
|
| 16 |
+
|
| 17 |
+
Introduction
|
| 18 |
+
============
|
| 19 |
+
|
| 20 |
+
This subpackage contains functions and classes useful for gravitational
|
| 21 |
+
dynamics. There are utilities for transforming orbits in phase-space to
|
| 22 |
+
action-angle coordinates, tools for visualizing and computing dynamical
|
| 23 |
+
quantities from orbits, tools to generate mock stellar streams, and tools useful
|
| 24 |
+
for nonlinear dynamics such as Lyapunov exponent estimation.
|
| 25 |
+
|
| 26 |
+
The fundamental objects used by many of the functions and utilities in this and
|
| 27 |
+
other subpackages are the |psp| and |orb| classes.
|
| 28 |
+
|
| 29 |
+
Getting started: Working with orbits
|
| 30 |
+
====================================
|
| 31 |
+
|
| 32 |
+
We'll demonstrate the |psp| and |orb| objects by first integrating an orbit::
|
| 33 |
+
|
| 34 |
+
>>> pot = gp.MiyamotoNagaiPotential(
|
| 35 |
+
... m=2.5e11 * u.Msun, a=6.5 * u.kpc, b=0.26 * u.kpc, units=galactic
|
| 36 |
+
... )
|
| 37 |
+
>>> w0 = gd.PhaseSpacePosition(
|
| 38 |
+
... pos=[11.0, 0.0, 0.2] * u.kpc, vel=[0.0, 200, 100] * u.km / u.s
|
| 39 |
+
... )
|
| 40 |
+
>>> orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=1.0, n_steps=1000)
|
| 41 |
+
|
| 42 |
+
This numerically integrates an orbit from the specified initial conditions,
|
| 43 |
+
``w0``, and returns an |orb| object. By default, this uses the Leapfrog
|
| 44 |
+
integrator, but you can specify a different integrator using the ``Integrator``
|
| 45 |
+
keyword argument. For example, to use a higher-order adaptive Runge-Kutta
|
| 46 |
+
method::
|
| 47 |
+
|
| 48 |
+
>>> orbit = gp.Hamiltonian(pot).integrate_orbit(
|
| 49 |
+
... w0, dt=1.0, n_steps=1000, Integrator='dopri853'
|
| 50 |
+
... )
|
| 51 |
+
|
| 52 |
+
Valid integrator names include ``'leapfrog'``, ``'dopri853'``, ``'ruth4'``, and
|
| 53 |
+
``'rk5'``. You can also pass an integrator class directly (see
|
| 54 |
+
:ref:`gala-integrate` for more information).
|
| 55 |
+
|
| 56 |
+
By default, the position and velocity are
|
| 57 |
+
assumed to be Cartesian coordinates but other coordinate systems are supported
|
| 58 |
+
(see the :ref:`orbits-in-detail` and :ref:`nd-representations` pages for more
|
| 59 |
+
information).
|
| 60 |
+
|
| 61 |
+
The |orb| object that is returned contains many useful methods, and can be
|
| 62 |
+
passed to many of the analysis functions implemented in Gala. For example, we
|
| 63 |
+
can easily visualize the orbit by plotting the time series in all Cartesian
|
| 64 |
+
projections using the :meth:`~gala.dynamics.Orbit.plot` method::
|
| 65 |
+
|
| 66 |
+
>>> fig = orbit.plot()
|
| 67 |
+
|
| 68 |
+
.. plot::
|
| 69 |
+
:align: center
|
| 70 |
+
|
| 71 |
+
import astropy.units as u
|
| 72 |
+
import gala.potential as gp
|
| 73 |
+
import gala.dynamics as gd
|
| 74 |
+
from gala.units import galactic
|
| 75 |
+
pot = gp.MiyamotoNagaiPotential(m=2.5e11, a=6.5, b=0.26, units=galactic)
|
| 76 |
+
w0 = gd.PhaseSpacePosition(
|
| 77 |
+
pos=[11.0, 0.0, 0.2] * u.kpc, vel=[0.0, 200, 100] * u.km / u.s
|
| 78 |
+
)
|
| 79 |
+
orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=1.0, n_steps=1000)
|
| 80 |
+
fig = orbit.plot()
|
| 81 |
+
|
| 82 |
+
We can also visualize the orbit in transformed coordinates, for example,
|
| 83 |
+
cylindrical radius :math:`\rho` and :math:`z`::
|
| 84 |
+
|
| 85 |
+
>>> fig = orbit.represent_as("cylindrical").plot(["rho", "z"])
|
| 86 |
+
|
| 87 |
+
.. plot::
|
| 88 |
+
:align: center
|
| 89 |
+
:width: 60%
|
| 90 |
+
|
| 91 |
+
import astropy.units as u
|
| 92 |
+
import gala.potential as gp
|
| 93 |
+
import gala.dynamics as gd
|
| 94 |
+
from gala.units import galactic
|
| 95 |
+
pot = gp.MiyamotoNagaiPotential(m=2.5e11, a=6.5, b=0.26, units=galactic)
|
| 96 |
+
w0 = gd.PhaseSpacePosition(
|
| 97 |
+
pos=[11.0, 0.0, 0.2] * u.kpc, vel=[0.0, 200, 100] * u.km / u.s
|
| 98 |
+
)
|
| 99 |
+
orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=1.0, n_steps=1000)
|
| 100 |
+
_ = orbit.represent_as("cylindrical").plot(["rho", "z"])
|
| 101 |
+
|
| 102 |
+
The |orb| object also enables computing dynamical quantities such as
|
| 103 |
+
energy or angular momentum::
|
| 104 |
+
|
| 105 |
+
>>> E = orbit.energy()
|
| 106 |
+
>>> E[0] # doctest: +SKIP
|
| 107 |
+
<Quantity −0.060740198 kpc2 / Myr2>
|
| 108 |
+
|
| 109 |
+
Let's check how well the integrator conserves energy and the ``z`` component of
|
| 110 |
+
angular momentum::
|
| 111 |
+
|
| 112 |
+
>>> Lz = orbit.angular_momentum()[2]
|
| 113 |
+
>>> np.std(E), np.std(Lz) # doctest: +FLOAT_CMP
|
| 114 |
+
(<Quantity 4.654233175716351e-06 kpc2 / Myr2>,
|
| 115 |
+
<Quantity 9.675900603446092e-16 kpc2 / Myr>)
|
| 116 |
+
|
| 117 |
+
We can access the position and velocity components of the orbit separately using
|
| 118 |
+
attributes that map to the underlying `~astropy.coordinates.BaseRepresentation`
|
| 119 |
+
and `~astropy.coordinates.BaseDifferential` subclass instances that store the
|
| 120 |
+
position and velocity data. The attribute names depend on the representation.
|
| 121 |
+
For example, for a Cartesian representation, the position components are ``["x",
|
| 122 |
+
"y", "z"]`` and the velocity components are ``["v_x", "v_y", "v_z"]``. With a
|
| 123 |
+
|orb| or |psp| instance, you can check the valid compnent names using the
|
| 124 |
+
attributes ``.pos_components`` and ``.vel_components``::
|
| 125 |
+
|
| 126 |
+
>>> orbit.pos_components.keys() # doctest: +SKIP
|
| 127 |
+
odict_keys(["x", "y", "z"])
|
| 128 |
+
>>> orbit.vel_components.keys() # doctest: +SKIP
|
| 129 |
+
odict_keys(["v_x", "v_y", "v_z"])
|
| 130 |
+
|
| 131 |
+
Meaning, we can access these components by doing, e.g.::
|
| 132 |
+
|
| 133 |
+
>>> orbit.v_x # doctest: +FLOAT_CMP
|
| 134 |
+
<Quantity [ 0. , -0.00567589, -0.01129934, ..., 0.18751756,
|
| 135 |
+
0.18286687, 0.17812762] kpc / Myr>
|
| 136 |
+
|
| 137 |
+
For a Cylindrical representation, these are instead::
|
| 138 |
+
|
| 139 |
+
>>> cyl_orbit = orbit.represent_as("cylindrical")
|
| 140 |
+
>>> cyl_orbit.pos_components.keys() # doctest: +SKIP
|
| 141 |
+
odict_keys(["rho", "phi", "z"])
|
| 142 |
+
>>> cyl_orbit.vel_components.keys() # doctest: +SKIP
|
| 143 |
+
odict_keys(["v_rho", "pm_phi", "v_z"])
|
| 144 |
+
>>> cyl_orbit.v_rho # doctest: +FLOAT_CMP
|
| 145 |
+
<Quantity [ 0. , -0.00187214, -0.00369183, ..., 0.01699321,
|
| 146 |
+
0.01930216, 0.02159477] kpc / Myr>
|
| 147 |
+
|
| 148 |
+
Continue to the :ref:`orbits-in-detail` page for more information.
|
| 149 |
+
|
| 150 |
+
Using gala.dynamics
|
| 151 |
+
===================
|
| 152 |
+
|
| 153 |
+
More details are provided in the linked pages below:
|
| 154 |
+
|
| 155 |
+
.. toctree::
|
| 156 |
+
:maxdepth: 2
|
| 157 |
+
|
| 158 |
+
orbits-in-detail
|
| 159 |
+
nd-representations
|
| 160 |
+
actionangle
|
| 161 |
+
mockstreams
|
| 162 |
+
nonlinear
|
| 163 |
+
nbody
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
API
|
| 167 |
+
===
|
| 168 |
+
|
| 169 |
+
.. automodapi:: gala.dynamics
|
| 170 |
+
:include: PhaseSpacePosition
|
| 171 |
+
:include: Orbit
|
| 172 |
+
:no-inheritance-diagram:
|
gala/source/docs/dynamics/mockstreams.rst
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _gala-mockstreams:
|
| 2 |
+
|
| 3 |
+
*******************************
|
| 4 |
+
Generating mock stellar streams
|
| 5 |
+
*******************************
|
| 6 |
+
|
| 7 |
+
Introduction
|
| 8 |
+
============
|
| 9 |
+
|
| 10 |
+
This module contains functions for generating mock stellar streams using a
|
| 11 |
+
variety of methods that approximate the formation of streams in N-body
|
| 12 |
+
simulations. Mock streams are generated by specifying time-stepping and release
|
| 13 |
+
time information (i.e., when should stream particles be generated), and by
|
| 14 |
+
specifying the stream distribution function (DF) to use to generate initial
|
| 15 |
+
conditions for the stream particles. The former is customizable, and a number of
|
| 16 |
+
popular stream DFs are implemented.
|
| 17 |
+
|
| 18 |
+
Some imports needed for the code below::
|
| 19 |
+
|
| 20 |
+
>>> import astropy.units as u
|
| 21 |
+
>>> import numpy as np
|
| 22 |
+
>>> import gala.potential as gp
|
| 23 |
+
>>> import gala.dynamics as gd
|
| 24 |
+
>>> from gala.dynamics import mockstream as ms
|
| 25 |
+
>>> from gala.units import galactic
|
| 26 |
+
|
| 27 |
+
We will also set the default Astropy Galactocentric frame parameters to the
|
| 28 |
+
values adopted in Astropy v4.0:
|
| 29 |
+
|
| 30 |
+
>>> import astropy.coordinates as coord
|
| 31 |
+
>>> _ = coord.galactocentric_frame_defaults.set('v4.0')
|
| 32 |
+
|
| 33 |
+
Getting started
|
| 34 |
+
===============
|
| 35 |
+
|
| 36 |
+
All mock stream generation done using the built-in gravitational potential
|
| 37 |
+
models implemented in `gala.potential`, so we must first specify a gravitational
|
| 38 |
+
potential to integrate orbits in. For the examples below, we will use a
|
| 39 |
+
spherical NFW potential with a circular velocity at the scale radius of 220
|
| 40 |
+
km/s, and a scale radius of 15 kpc::
|
| 41 |
+
|
| 42 |
+
>>> pot = gp.NFWPotential.from_circular_velocity(v_c=220*u.km/u.s,
|
| 43 |
+
... r_s=15*u.kpc,
|
| 44 |
+
... units=galactic)
|
| 45 |
+
|
| 46 |
+
The mock stream generation supports any of the reference frames implemented in
|
| 47 |
+
``gala`` (e.g., non-static / rotating reference frames), so we must create a
|
| 48 |
+
`~gala.potential.hamiltonian.Hamiltonian` object to use when generating streams.
|
| 49 |
+
By default, this will use a static reference frame::
|
| 50 |
+
|
| 51 |
+
>>> H = gp.Hamiltonian(pot)
|
| 52 |
+
|
| 53 |
+
Next, we will create initial conditions for the progenitor system. In this case,
|
| 54 |
+
we will generate the mock stream starting from this position going forward in
|
| 55 |
+
time. However, this is customizable: if you instead have the *final* position of
|
| 56 |
+
the progenitor system, there is a convenient way of doing this described below
|
| 57 |
+
(see :ref:`gala-mockstream-final-conditions`). Let's specify a position and
|
| 58 |
+
velocity that we think will produce a mildly eccentric orbit in the x-y plane of
|
| 59 |
+
our coordinate system::
|
| 60 |
+
|
| 61 |
+
>>> prog_w0 = gd.PhaseSpacePosition(pos=[10, 0, 0.] * u.kpc,
|
| 62 |
+
... vel=[0, 170, 0.] * u.km/u.s)
|
| 63 |
+
|
| 64 |
+
We now have to specify the method for generating stream particles, i.e., the
|
| 65 |
+
stream distribution function (DF). For this example, we will use the method
|
| 66 |
+
implemented in [fardal15]_, incuded in ``gala`` as
|
| 67 |
+
`~gala.dynamics.mockstream.FardalStreamDF`. Other methods of note are
|
| 68 |
+
`~gala.dynamics.mockstream.StreaklineStreamDF` from [kuepper12]_,
|
| 69 |
+
`~gala.dynamics.mockstream.LagrangeCloudStreamDF` based on [gibbons14]_, and
|
| 70 |
+
`~gala.dynamics.mockstream.ChenStreamDF` based on [chen24]_. Each of
|
| 71 |
+
the ``StreamDF`` classes take a few common arguments, such as ``lead`` and
|
| 72 |
+
``trail``, which are boolean arguments that control whether to generate both
|
| 73 |
+
leading and trailing tails, or just one or the other. By default, both are set
|
| 74 |
+
to True (i.e., both leading and trailing tails are generated by default). Some
|
| 75 |
+
other ``StreamDF`` classes may require other parameters. Let's create a
|
| 76 |
+
`~gala.dynamics.mockstream.ChenStreamDF` instance and accept the default
|
| 77 |
+
argument values. We will also need to specify the progenitor mass, which is
|
| 78 |
+
passed in to any ``StreamDF`` and is used to scale the particle release
|
| 79 |
+
distribution::
|
| 80 |
+
|
| 81 |
+
>>> df = ms.ChenStreamDF()
|
| 82 |
+
>>> prog_mass = 2.5E4 * u.Msun
|
| 83 |
+
|
| 84 |
+
.. warning::
|
| 85 |
+
|
| 86 |
+
The parameter values of the FardalStreamDF have been updated (fixed) in v1.9 to
|
| 87 |
+
match the parameter values in the final published version of [fardal15]_. For now,
|
| 88 |
+
this class uses the Gala modified parameter values that have been adopted over the
|
| 89 |
+
last several years in Gala. In the future, the default behavior of this class will
|
| 90 |
+
use the [fardal15]_ parameter values instead, breaking backwards compatibility for
|
| 91 |
+
mock stream simulations. To use the [fardal15]_ parameters now, set
|
| 92 |
+
``gala_modified=False``. To continue to use the Gala modified parameter values, set
|
| 93 |
+
``gala_modified=True``.
|
| 94 |
+
|
| 95 |
+
The final step before actually generating the stream is to create a
|
| 96 |
+
`~gala.dynamics.mockstream.MockStreamGenerator` instance, which we will use to
|
| 97 |
+
actually generate the stream. This takes the ``StreamDF`` and the external
|
| 98 |
+
potential (Hamiltonian) as arguments::
|
| 99 |
+
|
| 100 |
+
>>> gen = ms.MockStreamGenerator(df, H)
|
| 101 |
+
|
| 102 |
+
We are now ready to run the generator and create a mock stream. To do this, we use the `~gala.dynamics.mockstream.MockStreamGenerator.run()` method. This accepts the progenitor orbit initial conditions (we defined above as ``prog_w0``), the progenitor mass (we defined as ``prog_mass``), and time-stepping information. We will integrate the progenitor orbit for 1000 steps with a timestep of 1 Myr::
|
| 103 |
+
|
| 104 |
+
>>> stream, prog = gen.run(prog_w0, prog_mass,
|
| 105 |
+
... dt=1 * u.Myr, n_steps=1000)
|
| 106 |
+
|
| 107 |
+
Let's plot the stream::
|
| 108 |
+
|
| 109 |
+
>>> stream.plot(['x', 'y']) # doctest: +SKIP
|
| 110 |
+
|
| 111 |
+
.. plot::
|
| 112 |
+
:align: center
|
| 113 |
+
:context: close-figs
|
| 114 |
+
:width: 60%
|
| 115 |
+
|
| 116 |
+
import astropy.units as u
|
| 117 |
+
import numpy as np
|
| 118 |
+
import gala.potential as gp
|
| 119 |
+
import gala.dynamics as gd
|
| 120 |
+
from gala.dynamics import mockstream as ms
|
| 121 |
+
from gala.units import galactic
|
| 122 |
+
|
| 123 |
+
pot = gp.NFWPotential.from_circular_velocity(v_c=220*u.km/u.s,
|
| 124 |
+
r_s=15*u.kpc,
|
| 125 |
+
units=galactic)
|
| 126 |
+
H = gp.Hamiltonian(pot)
|
| 127 |
+
prog_w0 = gd.PhaseSpacePosition(pos=[10, 0, 0.] * u.kpc,
|
| 128 |
+
vel=[0, 170, 0.] * u.km/u.s)
|
| 129 |
+
|
| 130 |
+
df = ms.ChenStreamDF()
|
| 131 |
+
prog_mass = 2.5E4 * u.Msun
|
| 132 |
+
|
| 133 |
+
gen = ms.MockStreamGenerator(df, H)
|
| 134 |
+
|
| 135 |
+
stream, prog = gen.run(prog_w0, prog_mass,
|
| 136 |
+
dt=1 * u.Myr, n_steps=1000)
|
| 137 |
+
|
| 138 |
+
stream.plot(['x', 'y'], marker='o', s=4, color='k', alpha=0.1, linewidth=0)
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
By default, two stream particles are generated at every timestep in the
|
| 142 |
+
integration of the progenitor orbit (specified above by the timestep ``dt`` and
|
| 143 |
+
number of steps ``n_steps``). We can control the frequency of releasing
|
| 144 |
+
particles, and the number of particles released using the ``release_every`` and
|
| 145 |
+
``n_particles`` arguments. For example, setting ``release_every=8`` and
|
| 146 |
+
``n_particles=2`` will instead release 4 particles (2 for each tail) every 8th
|
| 147 |
+
timestep.
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
Self-gravity of the progenitor
|
| 151 |
+
==============================
|
| 152 |
+
|
| 153 |
+
Also by default, the progenitor system is assumed to be massless, and the stream
|
| 154 |
+
particles are treated as test particles in the specified external potential or
|
| 155 |
+
Hamiltonian. It is possible to include a potential object for the progenitor
|
| 156 |
+
system to account for the self-gravity of the progenitor as stream star
|
| 157 |
+
particles are released. We can use any of the ``gala.potential`` potential
|
| 158 |
+
objects to represent the progenitor system, but here we will use a simple
|
| 159 |
+
`~gala.potential.potential.PlummerPotential`. We pass this in to the
|
| 160 |
+
`~gala.dynamics.mockstream.MockStreamGenerator` - let's see what the stream
|
| 161 |
+
looks like when generated including self-gravity::
|
| 162 |
+
|
| 163 |
+
>>> prog_pot = gp.PlummerPotential(m=prog_mass, b=4*u.pc, units=galactic)
|
| 164 |
+
>>> gen2 = ms.MockStreamGenerator(df, H, progenitor_potential=prog_pot)
|
| 165 |
+
>>> stream2, prog = gen2.run(prog_w0, prog_mass,
|
| 166 |
+
... dt=1 * u.Myr, n_steps=1000)
|
| 167 |
+
>>> stream2.plot(['x', 'y']) # doctest: +SKIP
|
| 168 |
+
|
| 169 |
+
.. plot::
|
| 170 |
+
:align: center
|
| 171 |
+
:context: close-figs
|
| 172 |
+
:width: 60%
|
| 173 |
+
|
| 174 |
+
prog_pot = gp.PlummerPotential(m=prog_mass, b=2*u.pc, units=galactic)
|
| 175 |
+
gen2 = ms.MockStreamGenerator(df, H, progenitor_potential=prog_pot)
|
| 176 |
+
stream2, prog = gen2.run(prog_w0, prog_mass,
|
| 177 |
+
dt=1 * u.Myr, n_steps=1000)
|
| 178 |
+
stream2.plot(['x', 'y'], marker='o', s=4,
|
| 179 |
+
color='k', alpha=0.1, linewidth=0)
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
Integration methods and options
|
| 183 |
+
================================
|
| 184 |
+
|
| 185 |
+
Choosing an integrator
|
| 186 |
+
-----------------------
|
| 187 |
+
|
| 188 |
+
By default, mock stream generation uses the DOPRI853 integrator, which is an
|
| 189 |
+
8th-order adaptive Runge-Kutta method. This integrator is robust and
|
| 190 |
+
automatically adjusts the timestep to maintain a specified error tolerance.
|
| 191 |
+
However, ``gala`` also supports using the Leapfrog integrator, which is a
|
| 192 |
+
symplectic integrator that uses a fixed timestep and conserves energy better
|
| 193 |
+
over long integrations.
|
| 194 |
+
|
| 195 |
+
To use the Leapfrog integrator instead of DOPRI853, pass the ``Integrator``
|
| 196 |
+
argument to the `~gala.dynamics.mockstream.MockStreamGenerator.run()` method.
|
| 197 |
+
You can pass the integrator class directly::
|
| 198 |
+
|
| 199 |
+
>>> import gala.integrate as gi
|
| 200 |
+
>>> stream_lf, prog = gen.run(prog_w0, prog_mass,
|
| 201 |
+
... dt=1 * u.Myr, n_steps=1000,
|
| 202 |
+
... Integrator=gi.LeapfrogIntegrator)
|
| 203 |
+
|
| 204 |
+
or more conveniently, use a string name::
|
| 205 |
+
|
| 206 |
+
>>> stream_lf, prog = gen.run(prog_w0, prog_mass,
|
| 207 |
+
... dt=1 * u.Myr, n_steps=1000,
|
| 208 |
+
... Integrator='leapfrog')
|
| 209 |
+
|
| 210 |
+
The Leapfrog integrator is particularly useful for long-term integrations where energy conservation is critical, situations where a fixed timestep is faster or acceptable, or
|
| 211 |
+
when symplectic integration properties are important (e.g., preserving phase space volume).
|
| 212 |
+
|
| 213 |
+
The DOPRI853 integrator (the default) is better suited for N-body integrations or cases with time-varying potentials.
|
| 214 |
+
|
| 215 |
+
Customizing integrator tolerances
|
| 216 |
+
----------------------------------
|
| 217 |
+
|
| 218 |
+
For the DOPRI853 integrator, you can customize the absolute and relative error
|
| 219 |
+
tolerances using the ``Integrator_kwargs`` argument. This can be useful for
|
| 220 |
+
balancing accuracy and computational speed::
|
| 221 |
+
|
| 222 |
+
>>> # Higher accuracy (slower)
|
| 223 |
+
>>> stream_hi, prog = gen.run(prog_w0, prog_mass,
|
| 224 |
+
... dt=1 * u.Myr, n_steps=1000,
|
| 225 |
+
... Integrator_kwargs={'atol': 1e-12, 'rtol': 1e-12})
|
| 226 |
+
|
| 227 |
+
>>> # Lower accuracy (faster)
|
| 228 |
+
>>> stream_lo, prog = gen.run(prog_w0, prog_mass,
|
| 229 |
+
... dt=1 * u.Myr, n_steps=1000,
|
| 230 |
+
... Integrator_kwargs={'atol': 1e-8, 'rtol': 1e-8})
|
| 231 |
+
|
| 232 |
+
The default values are ``atol=1e-10`` and ``rtol=1e-10``, which provide a good
|
| 233 |
+
balance between accuracy and speed for most applications. Other DOPRI853 options
|
| 234 |
+
include ``nmax`` (maximum number of steps) and ``dt_max`` (maximum timestep).
|
| 235 |
+
|
| 236 |
+
Note that the Leapfrog integrator uses a fixed timestep (specified by ``dt``),
|
| 237 |
+
so tolerance parameters do not apply.
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
.. _gala-mockstream-final-conditions:
|
| 241 |
+
|
| 242 |
+
Generating a stream from the present-day progenitor location
|
| 243 |
+
============================================================
|
| 244 |
+
|
| 245 |
+
In the examples above, we pass in initial conditions for the progenitor and
|
| 246 |
+
generate the mock stream going forward in time. However, we often may want to
|
| 247 |
+
generate a stream such that the final progenitor location ends up at some
|
| 248 |
+
specified phase-space position. By convention, when a negative timestep is
|
| 249 |
+
passed in to `~gala.dynamics.mockstream.MockStreamGenerator.run()`, this is
|
| 250 |
+
interpreted to mean that the input progenitor phase-space position should be the
|
| 251 |
+
*final* position. Internally, this position is integrated backwards to the
|
| 252 |
+
earliest time, then a stream is generated forward from the past time. This is particularly useful when trying to reproduce observed streams, such as the Pal 5 stream::
|
| 253 |
+
|
| 254 |
+
>>> import astropy.coordinates as coord
|
| 255 |
+
>>> pal5_c = coord.SkyCoord(ra=229.018*u.degree, dec=-0.124*u.degree,
|
| 256 |
+
... distance=22.9*u.kpc,
|
| 257 |
+
... pm_ra_cosdec=-2.296*u.mas/u.yr,
|
| 258 |
+
... pm_dec=-2.257*u.mas/u.yr,
|
| 259 |
+
... radial_velocity=-58.7*u.km/u.s)
|
| 260 |
+
>>> rep = pal5_c.transform_to(coord.Galactocentric()).data
|
| 261 |
+
>>> pal5_w0 = gd.PhaseSpacePosition(rep)
|
| 262 |
+
>>> pal5_mass = 2.5e4 * u.Msun
|
| 263 |
+
>>> pal5_pot = gp.PlummerPotential(m=pal5_mass, b=4*u.pc, units=galactic)
|
| 264 |
+
>>> mw = gp.MilkyWayPotential(version="latest")
|
| 265 |
+
>>> gen_pal5 = ms.MockStreamGenerator(df, mw, progenitor_potential=pal5_pot)
|
| 266 |
+
>>> pal5_stream, _ = gen_pal5.run(pal5_w0, pal5_mass,
|
| 267 |
+
... dt=-1 * u.Myr, n_steps=4000)
|
| 268 |
+
>>> pal5_stream_c = pal5_stream.to_coord_frame(coord.ICRS())
|
| 269 |
+
|
| 270 |
+
.. plot::
|
| 271 |
+
:align: center
|
| 272 |
+
:context: close-figs
|
| 273 |
+
|
| 274 |
+
import astropy.coordinates as coord
|
| 275 |
+
coord.galactocentric_frame_defaults.set('v4.0')
|
| 276 |
+
pal5_c = coord.SkyCoord(ra=229.018*u.degree, dec=-0.124*u.degree,
|
| 277 |
+
distance=22.9*u.kpc,
|
| 278 |
+
pm_ra_cosdec=-2.296*u.mas/u.yr,
|
| 279 |
+
pm_dec=-2.257*u.mas/u.yr,
|
| 280 |
+
radial_velocity=-58.7*u.km/u.s)
|
| 281 |
+
rep = pal5_c.transform_to(coord.Galactocentric()).data
|
| 282 |
+
pal5_w0 = gd.PhaseSpacePosition(rep)
|
| 283 |
+
pal5_mass = 2.5e4 * u.Msun
|
| 284 |
+
pal5_pot = gp.PlummerPotential(m=pal5_mass, b=4*u.pc, units=galactic)
|
| 285 |
+
mw = gp.MilkyWayPotential(version="latest")
|
| 286 |
+
gen_pal5 = ms.MockStreamGenerator(df, mw, progenitor_potential=pal5_pot)
|
| 287 |
+
pal5_stream, _ = gen_pal5.run(pal5_w0, pal5_mass,
|
| 288 |
+
dt=-1 * u.Myr, n_steps=4000)
|
| 289 |
+
pal5_stream_c = pal5_stream.to_coord_frame(coord.ICRS())
|
| 290 |
+
|
| 291 |
+
fig, ax = plt.subplots(1, 1, figsize=(6, 4))
|
| 292 |
+
ax.scatter(pal5_stream_c.ra.degree, pal5_stream_c.dec.degree,
|
| 293 |
+
marker='o', s=4, color='k', lw=0, alpha=0.1)
|
| 294 |
+
ax.set_xlim(250, 215)
|
| 295 |
+
ax.set_ylim(-10, 10)
|
| 296 |
+
ax.set_xlabel('RA [deg]')
|
| 297 |
+
ax.set_ylabel('Dec [deg]')
|
| 298 |
+
fig.tight_layout()
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
References
|
| 302 |
+
==========
|
| 303 |
+
|
| 304 |
+
.. [fardal15] `Fardal, Huang, Weinberg (2015) <http://arxiv.org/abs/1410.1861>`_
|
| 305 |
+
.. [kuepper12] `Küpper, Lane, Heggie (2012) <http://arxiv.org/abs/1111.5013>`_
|
| 306 |
+
.. [gibbons14] `Gibbons et al. (2014) <https://arxiv.org/abs/1406.2243>`_
|
| 307 |
+
.. [chen24] `Chen et al. (2024) <https://arxiv.org/abs/2408.01496>`_
|
| 308 |
+
|
| 309 |
+
API
|
| 310 |
+
---
|
| 311 |
+
.. automodapi:: gala.dynamics.mockstream
|
| 312 |
+
:no-heading:
|
| 313 |
+
:headings: ^^
|
gala/source/docs/dynamics/nbody.rst
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _gala-nbody:
|
| 2 |
+
|
| 3 |
+
******************************
|
| 4 |
+
N-body (`gala.dynamics.nbody`)
|
| 5 |
+
******************************
|
| 6 |
+
|
| 7 |
+
Introduction
|
| 8 |
+
============
|
| 9 |
+
|
| 10 |
+
With the `~gala.potential.hamiltonian.Hamiltonian` and potential classes
|
| 11 |
+
(:ref:`potential`), Gala contains functionality for integrating test particle
|
| 12 |
+
orbits in background gravitational fields. To supplement this, Gala also now
|
| 13 |
+
contains some limited functionality for performing N-body orbit integrations
|
| 14 |
+
through direct N-body force calculations between particles. With the
|
| 15 |
+
`gala.dynamics.nbody` subpackage, gravitational fields (i.e., any potential
|
| 16 |
+
class from :mod:`gala.potential`) can be sourced by particles that interact, and
|
| 17 |
+
optionally feel a background/external potential. To use this functionality, the
|
| 18 |
+
core class is `~gala.dynamics.nbody.DirectNBody`. Below, we'll go through a few
|
| 19 |
+
examples of using this class to perform orbit integrations
|
| 20 |
+
|
| 21 |
+
For the examples below the following imports have already been executed::
|
| 22 |
+
|
| 23 |
+
>>> import astropy.units as u
|
| 24 |
+
>>> import numpy as np
|
| 25 |
+
>>> import gala.potential as gp
|
| 26 |
+
>>> import gala.dynamics as gd
|
| 27 |
+
>>> from gala.dynamics.nbody import DirectNBody
|
| 28 |
+
>>> from gala.units import galactic, UnitSystem
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
Getting started
|
| 32 |
+
===============
|
| 33 |
+
|
| 34 |
+
The `~gala.dynamics.nbody.DirectNBody`, at minimum, must be instantiated with a
|
| 35 |
+
set of particle orbital initial conditions along with a specification of the
|
| 36 |
+
gravitational fields sourced by each particle --- that is, the number of initial
|
| 37 |
+
conditions must match the input list of gravitational potential objects that
|
| 38 |
+
specify the particle mass distributions. Other optional arguments to
|
| 39 |
+
`~gala.dynamics.nbody.DirectNBody` allow you to set the unit system (i.e., to
|
| 40 |
+
improve numerical precision when time-stepping the orbit integration), or to
|
| 41 |
+
specify a background gravitational potential. Let's now go through a few
|
| 42 |
+
examples of using this class in practice.
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
Example: Mixed test particle and massive particle orbit integration
|
| 46 |
+
===================================================================
|
| 47 |
+
|
| 48 |
+
Like with `~gala.potential.hamiltonian.Hamiltonian` orbit integration, orbital
|
| 49 |
+
initial conditions are passed in to `~gala.dynamics.nbody.DirectNBody` by
|
| 50 |
+
passing in a single `~gala.dynamics.PhaseSpacePosition` object. Let's create two
|
| 51 |
+
initial conditions by specifying the position and velocity of two particles,
|
| 52 |
+
then combine them into a single `~gala.dynamics.PhaseSpacePosition` object::
|
| 53 |
+
|
| 54 |
+
>>> w0_1 = gd.PhaseSpacePosition(pos=[0, 0, 0] * u.pc,
|
| 55 |
+
... vel=[0, 1.5, 0] * u.km/u.s)
|
| 56 |
+
>>> w0_2 = gd.PhaseSpacePosition(pos=w0_1.xyz + [100., 0, 0] * u.pc,
|
| 57 |
+
... vel=w0_1.v_xyz + [0, 5, 0] * u.km/u.s)
|
| 58 |
+
>>> w0 = gd.combine((w0_1, w0_2))
|
| 59 |
+
>>> w0.shape
|
| 60 |
+
(2,)
|
| 61 |
+
|
| 62 |
+
We'll then treat particle 1 as a massive object by sourcing a
|
| 63 |
+
`~gala.potential.potential.HernquistPotential` at the location of the particle,
|
| 64 |
+
and particle 2 as a test particle: To treat some particles as test particles,
|
| 65 |
+
you can pass ``None`` or a `~gala.potential.potential.NullPotential` instance
|
| 66 |
+
for the corresponding particle potential::
|
| 67 |
+
|
| 68 |
+
>>> pot1 = gp.HernquistPotential(m=1e7*u.Msun, c=0.5*u.kpc, units=galactic)
|
| 69 |
+
>>> particle_pot = [pot1, None]
|
| 70 |
+
|
| 71 |
+
Let's now create an N-body instance and try integrating the orbits of the two
|
| 72 |
+
particles. Here, there is no external potential, so particle 1 (the massive
|
| 73 |
+
particle) will move off in a straight line. We've created the initial conditions
|
| 74 |
+
for particle 2 so that it will remain bound to the potential sourced by particle
|
| 75 |
+
1, and so will orbit it as it moves. Let's create the object and integrate the
|
| 76 |
+
orbits::
|
| 77 |
+
|
| 78 |
+
>>> nbody = DirectNBody(w0, particle_pot)
|
| 79 |
+
>>> orbits = nbody.integrate_orbit(dt=1e-2*u.Myr, t1=0, t2=1*u.Gyr)
|
| 80 |
+
>>> fig, ax = plt.subplots(1, 1, figsize=(5, 5)) # doctest: +SKIP
|
| 81 |
+
>>> _ = orbits[:, 0].plot(['x', 'y'], axes=[ax]) # doctest: +SKIP
|
| 82 |
+
>>> _ = orbits[:, 1].plot(['x', 'y'], axes=[ax]) # doctest: +SKIP
|
| 83 |
+
|
| 84 |
+
.. plot::
|
| 85 |
+
:align: center
|
| 86 |
+
:context: close-figs
|
| 87 |
+
:width: 60%
|
| 88 |
+
|
| 89 |
+
import astropy.units as u
|
| 90 |
+
import numpy as np
|
| 91 |
+
import gala.potential as gp
|
| 92 |
+
import gala.dynamics as gd
|
| 93 |
+
from gala.dynamics.nbody import DirectNBody
|
| 94 |
+
from gala.units import galactic, UnitSystem
|
| 95 |
+
import matplotlib.pyplot as plt
|
| 96 |
+
|
| 97 |
+
w0_1 = gd.PhaseSpacePosition(pos=[0, 0, 0] * u.pc,
|
| 98 |
+
vel=[0, 1.5, 0] * u.km/u.s)
|
| 99 |
+
w0_2 = gd.PhaseSpacePosition(pos=w0_1.xyz + [100., 0, 0] * u.pc,
|
| 100 |
+
vel=w0_1.v_xyz + [0, 5, 0] * u.km/u.s)
|
| 101 |
+
w0 = gd.combine((w0_1, w0_2))
|
| 102 |
+
|
| 103 |
+
pot1 = gp.HernquistPotential(m=1e7*u.Msun, c=0.5*u.kpc, units=galactic)
|
| 104 |
+
particle_pot = [pot1, None]
|
| 105 |
+
|
| 106 |
+
nbody = DirectNBody(w0, particle_pot)
|
| 107 |
+
orbits = nbody.integrate_orbit(dt=1e-2*u.Myr, t1=0, t2=1*u.Gyr)
|
| 108 |
+
fig, ax = plt.subplots(1, 1, figsize=(5, 5))
|
| 109 |
+
_ = orbits[:, 0].plot(['x', 'y'], axes=[ax])
|
| 110 |
+
_ = orbits[:, 1].plot(['x', 'y'], axes=[ax])
|
| 111 |
+
fig.tight_layout()
|
| 112 |
+
|
| 113 |
+
Example: N-body integration with a background potential
|
| 114 |
+
=======================================================
|
| 115 |
+
|
| 116 |
+
With `~gala.dynamics.nbody.DirectNBody`, we can also specify a background or
|
| 117 |
+
external potential to integrate all orbits in. To do this, you can optionally
|
| 118 |
+
pass in an external potential as a potential object to
|
| 119 |
+
`~gala.dynamics.nbody.DirectNBody`. Here, as an example, we'll repeat a similar
|
| 120 |
+
integration as above, but (1) add a positional offset of the initial conditions
|
| 121 |
+
from the origin, and (2) specify an external potential using the
|
| 122 |
+
`~gala.potential.potential.MilkyWayPotential` class as an external potential::
|
| 123 |
+
|
| 124 |
+
>>> external_pot = gp.MilkyWayPotential(version="latest")
|
| 125 |
+
>>> w0_1 = gd.PhaseSpacePosition(pos=[10, 0, 0] * u.kpc,
|
| 126 |
+
... vel=[0, 200, 0] * u.km/u.s)
|
| 127 |
+
>>> w0_2 = gd.PhaseSpacePosition(pos=w0_1.xyz + [10., 0, 0] * u.pc,
|
| 128 |
+
... vel=w0_1.v_xyz + [0, 5, 0] * u.km/u.s)
|
| 129 |
+
>>> w0 = gd.combine((w0_1, w0_2))
|
| 130 |
+
>>> pot1 = gp.HernquistPotential(m=1e7*u.Msun, c=0.5*u.kpc, units=galactic)
|
| 131 |
+
>>> particle_pot = [pot1, None]
|
| 132 |
+
>>> nbody = DirectNBody(w0, particle_pot, external_potential=external_pot)
|
| 133 |
+
>>> orbits = nbody.integrate_orbit(dt=1e-2*u.Myr, t1=0, t2=1*u.Gyr)
|
| 134 |
+
>>> fig, ax = plt.subplots(1, 1, figsize=(5, 5)) # doctest: +SKIP
|
| 135 |
+
>>> _ = orbits[:, 0].plot(['x', 'y'], axes=[ax]) # doctest: +SKIP
|
| 136 |
+
>>> _ = orbits[:, 1].plot(['x', 'y'], axes=[ax]) # doctest: +SKIP
|
| 137 |
+
|
| 138 |
+
.. plot::
|
| 139 |
+
:align: center
|
| 140 |
+
:context: close-figs
|
| 141 |
+
:width: 60%
|
| 142 |
+
|
| 143 |
+
external_pot = gp.MilkyWayPotential(version="latest")
|
| 144 |
+
w0_1 = gd.PhaseSpacePosition(pos=[10, 0, 0] * u.kpc,
|
| 145 |
+
vel=[0, 200, 0] * u.km/u.s)
|
| 146 |
+
w0_2 = gd.PhaseSpacePosition(pos=w0_1.xyz + [10., 0, 0] * u.pc,
|
| 147 |
+
vel=w0_1.v_xyz + [0, 5, 0] * u.km/u.s)
|
| 148 |
+
w0 = gd.combine((w0_1, w0_2))
|
| 149 |
+
pot1 = gp.HernquistPotential(m=1e7*u.Msun, c=0.5*u.kpc, units=galactic)
|
| 150 |
+
particle_pot = [pot1, None]
|
| 151 |
+
nbody = DirectNBody(w0, particle_pot, external_potential=external_pot)
|
| 152 |
+
orbits = nbody.integrate_orbit(dt=1e-2*u.Myr, t1=0, t2=1*u.Gyr)
|
| 153 |
+
|
| 154 |
+
fig, ax = plt.subplots(1, 1, figsize=(5, 5)) # doctest: +SKIP
|
| 155 |
+
_ = orbits[:, 0].plot(['x', 'y'], axes=[ax]) # doctest: +SKIP
|
| 156 |
+
_ = orbits[:, 1].plot(['x', 'y'], axes=[ax]) # doctest: +SKIP
|
| 157 |
+
|
| 158 |
+
In this case, the orbits are so similar that it is hard to tell whether the test
|
| 159 |
+
particle is actually bound to the secondary mass. Let's instead now plot the
|
| 160 |
+
position in the x-y plane of particle 2 relative to particle 1. This will look
|
| 161 |
+
strange because we have not transformed to the frame of particle 1, but it
|
| 162 |
+
should give us a sense of whether particle 2 is bound or unbound to this mass::
|
| 163 |
+
|
| 164 |
+
>>> dxyz = orbits[:, 0].xyz - orbits[:, 1].xyz
|
| 165 |
+
>>> fig, ax = plt.subplots(1, 1, figsize=(5, 5)) # doctest: +SKIP
|
| 166 |
+
>>> ax.plot(dxyz[0], dxyz[1]) # doctest: +SKIP
|
| 167 |
+
|
| 168 |
+
.. plot::
|
| 169 |
+
:align: center
|
| 170 |
+
:context: close-figs
|
| 171 |
+
:width: 60%
|
| 172 |
+
|
| 173 |
+
dxyz = orbits[:, 0].xyz - orbits[:, 1].xyz
|
| 174 |
+
|
| 175 |
+
fig, ax = plt.subplots(1, 1, figsize=(5, 5)) # doctest: +SKIP
|
| 176 |
+
ax.plot(dxyz[0], dxyz[1]) # doctest: +SKIP
|
| 177 |
+
ax.set_xlim(-0.1, 0.1)
|
| 178 |
+
ax.set_ylim(-0.1, 0.1)
|
| 179 |
+
|
| 180 |
+
From this, it looks like particle 2 is indeed still bound to particle 1 as they
|
| 181 |
+
both orbit within the external potential.
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
.. automodapi:: gala.dynamics.nbody
|
gala/source/docs/dynamics/nd-representations.rst
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. include:: references.txt
|
| 2 |
+
|
| 3 |
+
.. _nd-representations:
|
| 4 |
+
|
| 5 |
+
************************************
|
| 6 |
+
N-dimensional representation classes
|
| 7 |
+
************************************
|
| 8 |
+
|
| 9 |
+
For the examples below the following imports have already been executed::
|
| 10 |
+
|
| 11 |
+
>>> import astropy.units as u
|
| 12 |
+
>>> import numpy as np
|
| 13 |
+
>>> import gala.dynamics as gd
|
| 14 |
+
|
| 15 |
+
Introduction
|
| 16 |
+
============
|
| 17 |
+
|
| 18 |
+
The Astropy |astropyrep|_ presently only support 3D positions and differential
|
| 19 |
+
objects. The `~gala.dynamics.representation_nd.NDCartesianRepresentation` and
|
| 20 |
+
`~gala.dynamics.representation_nd.NDCartesianDifferential` classes add Cartesian
|
| 21 |
+
representation classes that can handle arbitrary numbers of dimensions. For
|
| 22 |
+
example, 2D coordinates::
|
| 23 |
+
|
| 24 |
+
>>> xy = np.arange(16).reshape(2, 8) * u.kpc
|
| 25 |
+
>>> rep = gd.NDCartesianRepresentation(xy)
|
| 26 |
+
>>> rep
|
| 27 |
+
<NDCartesianRepresentation (x1, x2) in kpc
|
| 28 |
+
[(0., 8.), (1., 9.), (2., 10.), (3., 11.), (4., 12.), (5., 13.),
|
| 29 |
+
(6., 14.), (7., 15.)]>
|
| 30 |
+
|
| 31 |
+
4D coordinates::
|
| 32 |
+
|
| 33 |
+
>>> x = np.arange(16).reshape(4, 4) * u.kpc
|
| 34 |
+
>>> rep = gd.NDCartesianRepresentation(x)
|
| 35 |
+
>>> rep
|
| 36 |
+
<NDCartesianRepresentation (x1, x2, x3, x4) in kpc
|
| 37 |
+
[(0., 4., 8., 12.), (1., 5., 9., 13.), (2., 6., 10., 14.),
|
| 38 |
+
(3., 7., 11., 15.)]>
|
| 39 |
+
|
| 40 |
+
These can be passed in to the |psp| or |orb| classes as with any of the Astropy
|
| 41 |
+
core representation objects::
|
| 42 |
+
|
| 43 |
+
>>> xy = np.arange(16).reshape(2, 8) * u.kpc
|
| 44 |
+
>>> vxy = np.arange(16).reshape(2, 8) / 10. * u.kpc / u.Myr
|
| 45 |
+
>>> w = gd.PhaseSpacePosition(pos=xy, vel=vxy)
|
| 46 |
+
>>> fig = w.plot()
|
| 47 |
+
|
| 48 |
+
.. plot::
|
| 49 |
+
:align: center
|
| 50 |
+
:width: 60%
|
| 51 |
+
|
| 52 |
+
import astropy.units as u
|
| 53 |
+
import numpy as np
|
| 54 |
+
import gala.dynamics as gd
|
| 55 |
+
xy = np.arange(16).reshape(2, 8) * u.kpc
|
| 56 |
+
vxy = np.arange(16).reshape(2, 8) / 10. * u.kpc / u.Myr
|
| 57 |
+
w = gd.PhaseSpacePosition(pos=xy, vel=vxy)
|
| 58 |
+
fig = w.plot()
|
| 59 |
+
|
| 60 |
+
However, certain functionality such as representation transformations, dynamical
|
| 61 |
+
quantity calculation, and coordinate frame transformations are disabled when the
|
| 62 |
+
number of dimensions is not 3 (i.e. when not using the Astropy core
|
| 63 |
+
representation classes).
|
| 64 |
+
|
| 65 |
+
N-dimensional representations API
|
| 66 |
+
---------------------------------
|
| 67 |
+
.. automodapi:: gala.dynamics.representation_nd
|
| 68 |
+
:no-heading:
|
| 69 |
+
:headings: ^^
|
gala/source/docs/dynamics/nonlinear.rst
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. _gala-nonlinear-dynamics:
|
| 2 |
+
|
| 3 |
+
******************
|
| 4 |
+
Nonlinear Dynamics
|
| 5 |
+
******************
|
| 6 |
+
|
| 7 |
+
Introduction
|
| 8 |
+
============
|
| 9 |
+
|
| 10 |
+
This module contains utilities for nonlinear dynamics. Currently, the only
|
| 11 |
+
implemented features enable you to compute estimates of the maximum
|
| 12 |
+
Lyapunov exponent for an orbit. In future releases, there will be features
|
| 13 |
+
for creating surface of sections and computing the full Lyapunov spectrum.
|
| 14 |
+
|
| 15 |
+
Some imports needed for the code below::
|
| 16 |
+
|
| 17 |
+
>>> import astropy.units as u
|
| 18 |
+
>>> import numpy as np
|
| 19 |
+
>>> import gala.potential as gp
|
| 20 |
+
>>> import gala.dynamics as gd
|
| 21 |
+
>>> from gala.units import galactic
|
| 22 |
+
|
| 23 |
+
Computing Lyapunov exponents
|
| 24 |
+
============================
|
| 25 |
+
|
| 26 |
+
Chaotic orbit
|
| 27 |
+
-------------
|
| 28 |
+
|
| 29 |
+
There are two ways to compute Lyapunov exponents implemented in `gala.dynamics`.
|
| 30 |
+
In most cases, you'll want to use the
|
| 31 |
+
`~gala.dynamics.nonlinear.fast_lyapunov_max` function because the integration is
|
| 32 |
+
implemented in C and is quite fast. This function only works if the potential
|
| 33 |
+
you are working with is implemented in C (e.g., it is a
|
| 34 |
+
`~gala.potential.potential.CPotentialBase` subclass). With a potential object
|
| 35 |
+
and a set of initial conditions::
|
| 36 |
+
|
| 37 |
+
>>> pot = gp.LogarithmicPotential(v_c=150*u.km/u.s, r_h=0.1*u.kpc,
|
| 38 |
+
... q1=1., q2=0.8, q3=0.6, units=galactic)
|
| 39 |
+
>>> w0 = gd.PhaseSpacePosition(pos=[5.5,0.,5.5]*u.kpc,
|
| 40 |
+
... vel=[0.,100.,0]*u.km/u.s)
|
| 41 |
+
>>> lyap,orbit = gd.fast_lyapunov_max(w0, pot, dt=2., n_steps=100000) # doctest: +SKIP
|
| 42 |
+
|
| 43 |
+
This returns two objects: an `~astropy.units.Quantity` object that
|
| 44 |
+
contains the maximum Lyapunov exponent estimate for each offset orbit,
|
| 45 |
+
(we can control the number of offset orbits with the ``noffset_orbits``
|
| 46 |
+
argument) and an `~gala.dynamics.Orbit` object that contains
|
| 47 |
+
the parent orbit and each offset orbit. Let's plot the parent orbit::
|
| 48 |
+
|
| 49 |
+
>>> fig = orbit[:,0].plot(marker=',', alpha=0.25, linestyle='none') # doctest: +SKIP
|
| 50 |
+
|
| 51 |
+
.. plot::
|
| 52 |
+
:align: center
|
| 53 |
+
|
| 54 |
+
import astropy.units as u
|
| 55 |
+
import matplotlib.pyplot as plt
|
| 56 |
+
import gala.potential as gp
|
| 57 |
+
import gala.dynamics as gd
|
| 58 |
+
from gala.units import galactic
|
| 59 |
+
|
| 60 |
+
pot = gp.LogarithmicPotential(v_c=150*u.km/u.s, r_h=0.1*u.kpc,
|
| 61 |
+
q1=1., q2=0.8, q3=0.6, units=galactic)
|
| 62 |
+
w0 = gd.PhaseSpacePosition(pos=[5.5,0.,5.5]*u.kpc,
|
| 63 |
+
vel=[0.,100.,0]*u.km/u.s)
|
| 64 |
+
lyap,orbit = gd.fast_lyapunov_max(w0, pot, dt=2., n_steps=100000)
|
| 65 |
+
fig = orbit[:,0].plot(marker=',', linestyle='none', alpha=0.25)
|
| 66 |
+
|
| 67 |
+
Visually, this looks like a chaotic orbit. This means the Lyapunov exponent
|
| 68 |
+
should saturate to some value. We'll now plot the estimate of the Lyapunov
|
| 69 |
+
exponent as a function of time -- because the algorithm re-normalizes every
|
| 70 |
+
several time-steps (controllable with the ``n_steps_per_pullback`` argument),
|
| 71 |
+
we have to down-sample the time array to align it with the Lyapunov exponent
|
| 72 |
+
array. This plots one line per offset orbit::
|
| 73 |
+
|
| 74 |
+
>>> plt.figure() # doctest: +SKIP
|
| 75 |
+
>>> plt.loglog(orbit.t[11::10], lyap, marker='') # doctest: +SKIP
|
| 76 |
+
>>> plt.xlabel("Time [{}]".format(orbit.t.unit)) # doctest: +SKIP
|
| 77 |
+
>>> plt.ylabel(r"$\lambda_{{\rm max}}$ [{}]".format(lyap.unit)) # doctest: +SKIP
|
| 78 |
+
>>> plt.tight_layout() # doctest: +SKIP
|
| 79 |
+
|
| 80 |
+
.. plot::
|
| 81 |
+
:align: center
|
| 82 |
+
:width: 60%
|
| 83 |
+
|
| 84 |
+
import astropy.units as u
|
| 85 |
+
import matplotlib.pyplot as plt
|
| 86 |
+
import gala.potential as gp
|
| 87 |
+
import gala.dynamics as gd
|
| 88 |
+
from gala.units import galactic
|
| 89 |
+
|
| 90 |
+
pot = gp.LogarithmicPotential(v_c=150*u.km/u.s, r_h=0.1*u.kpc,
|
| 91 |
+
q1=1., q2=0.8, q3=0.6, units=galactic)
|
| 92 |
+
w0 = gd.PhaseSpacePosition(pos=[5.5,0.,5.5]*u.kpc,
|
| 93 |
+
vel=[0.,100.,0]*u.km/u.s)
|
| 94 |
+
lyap,orbit = gd.fast_lyapunov_max(w0, pot, dt=2., n_steps=100000)
|
| 95 |
+
|
| 96 |
+
plt.figure()
|
| 97 |
+
plt.loglog(orbit.t[11::10], lyap, marker='')
|
| 98 |
+
plt.xlabel("Time [{}]".format(orbit.t.unit))
|
| 99 |
+
plt.ylabel(r"$\lambda_{{\rm max}}$ [{}]".format(lyap.unit))
|
| 100 |
+
plt.tight_layout()
|
| 101 |
+
|
| 102 |
+
The estimate is clearly starting to diverge from a simple power law decay.
|
| 103 |
+
|
| 104 |
+
Regular orbit
|
| 105 |
+
-------------
|
| 106 |
+
|
| 107 |
+
To compare, we will compute the estimate for a regular orbit as well::
|
| 108 |
+
|
| 109 |
+
>>> w0 = gd.PhaseSpacePosition(pos=[5.5,0.,0.]*u.kpc,
|
| 110 |
+
... vel=[0.,140.,25]*u.km/u.s)
|
| 111 |
+
>>> lyap,orbit = gd.fast_lyapunov_max(w0, pot, dt=2., n_steps=100000) # doctest: +SKIP
|
| 112 |
+
>>> fig = orbit[:,0].plot(marker=',', alpha=0.1, linestyle='none') # doctest: +SKIP
|
| 113 |
+
|
| 114 |
+
.. plot::
|
| 115 |
+
:align: center
|
| 116 |
+
|
| 117 |
+
import astropy.units as u
|
| 118 |
+
import gala.potential as gp
|
| 119 |
+
import gala.dynamics as gd
|
| 120 |
+
from gala.units import galactic
|
| 121 |
+
|
| 122 |
+
pot = gp.LogarithmicPotential(v_c=150*u.km/u.s, r_h=0.1*u.kpc,
|
| 123 |
+
q1=1., q2=0.8, q3=0.6, units=galactic)
|
| 124 |
+
w0 = gd.PhaseSpacePosition(pos=[5.5,0.,0.]*u.kpc,
|
| 125 |
+
vel=[0.,140.,25]*u.km/u.s)
|
| 126 |
+
lyap,orbit = gd.fast_lyapunov_max(w0, pot, dt=1., n_steps=200000)
|
| 127 |
+
fig = orbit[:,0].plot(marker=',', linestyle='none', alpha=0.1)
|
| 128 |
+
|
| 129 |
+
Because this is a regular orbit, the estimate continues decreasing,
|
| 130 |
+
following a characteristic power-law (a straight line in a log-log plot)::
|
| 131 |
+
|
| 132 |
+
>>> pl.figure() # doctest: +SKIP
|
| 133 |
+
>>> pl.loglog(orbit.t[11::10], lyap, marker='') # doctest: +SKIP
|
| 134 |
+
>>> pl.xlabel("Time [{}]".format(orbit.t.unit)) # doctest: +SKIP
|
| 135 |
+
>>> pl.ylabel(r"$\lambda_{{\rm max}}$ [{}]".format(lyap.unit)) # doctest: +SKIP
|
| 136 |
+
>>> pl.tight_layout() # doctest: +SKIP
|
| 137 |
+
|
| 138 |
+
.. plot::
|
| 139 |
+
:align: center
|
| 140 |
+
:width: 60%
|
| 141 |
+
|
| 142 |
+
import astropy.units as u
|
| 143 |
+
import matplotlib.pyplot as pl
|
| 144 |
+
import gala.potential as gp
|
| 145 |
+
import gala.dynamics as gd
|
| 146 |
+
from gala.units import galactic
|
| 147 |
+
|
| 148 |
+
pot = gp.LogarithmicPotential(v_c=150*u.km/u.s, r_h=0.1*u.kpc,
|
| 149 |
+
q1=1., q2=0.8, q3=0.6, units=galactic)
|
| 150 |
+
w0 = gd.PhaseSpacePosition(pos=[5.5,0.,0.]*u.kpc,
|
| 151 |
+
vel=[0.,140.,25]*u.km/u.s)
|
| 152 |
+
lyap,orbit = gd.fast_lyapunov_max(w0, pot, dt=1., n_steps=200000)
|
| 153 |
+
|
| 154 |
+
pl.figure()
|
| 155 |
+
pl.loglog(orbit.t[11::10], lyap, marker='')
|
| 156 |
+
pl.xlabel("Time [{}]".format(orbit.t.unit))
|
| 157 |
+
pl.ylabel(r"$\lambda_{{\rm max}}$ [{}]".format(lyap.unit))
|
| 158 |
+
pl.tight_layout()
|
| 159 |
+
|
| 160 |
+
API
|
| 161 |
+
---
|
| 162 |
+
.. automodapi:: gala.dynamics.nonlinear
|
| 163 |
+
:no-heading:
|
| 164 |
+
:headings: ^^
|
gala/source/docs/dynamics/orbits-in-detail.rst
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.. include:: references.txt
|
| 2 |
+
|
| 3 |
+
.. _orbits-in-detail:
|
| 4 |
+
|
| 5 |
+
*****************************************************
|
| 6 |
+
Orbit and phase-space position objects in more detail
|
| 7 |
+
*****************************************************
|
| 8 |
+
|
| 9 |
+
For the examples below the following imports have already been executed::
|
| 10 |
+
|
| 11 |
+
>>> import astropy.units as u
|
| 12 |
+
>>> import numpy as np
|
| 13 |
+
>>> import gala.potential as gp
|
| 14 |
+
>>> import gala.dynamics as gd
|
| 15 |
+
>>> from astropy.coordinates import (CylindricalRepresentation,
|
| 16 |
+
... CylindricalDifferential)
|
| 17 |
+
>>> from gala.units import galactic
|
| 18 |
+
>>> np.random.seed(42)
|
| 19 |
+
|
| 20 |
+
We will also set the default Astropy Galactocentric frame parameters to the
|
| 21 |
+
values adopted in Astropy v4.0:
|
| 22 |
+
|
| 23 |
+
>>> import astropy.coordinates as coord
|
| 24 |
+
>>> _ = coord.galactocentric_frame_defaults.set('v4.0')
|
| 25 |
+
|
| 26 |
+
Introduction
|
| 27 |
+
============
|
| 28 |
+
|
| 29 |
+
The `astropy.units` subpackage is excellent for working with numbers and
|
| 30 |
+
associated units, but dynamical quantities often contain many quantities with
|
| 31 |
+
mixed units. An example is a position in phase-space, which may contain some
|
| 32 |
+
quantities with length units and some quantities with velocity or momentum
|
| 33 |
+
units. The |psp| and |orb| classes are designed to work with these data
|
| 34 |
+
structures and provide a consistent API for visualizing and computing further
|
| 35 |
+
dynamical quantities. Click these shortcuts to jump to a section below, or start
|
| 36 |
+
reading below:
|
| 37 |
+
|
| 38 |
+
* :ref:`phase-space-position`
|
| 39 |
+
* :ref:`orbit`
|
| 40 |
+
|
| 41 |
+
.. _phase-space-position:
|
| 42 |
+
|
| 43 |
+
Phase-space Positions
|
| 44 |
+
=====================
|
| 45 |
+
|
| 46 |
+
The |psp| class provides an interface for representing full phase-space
|
| 47 |
+
positions--coordinate positions and momenta (velocities). This class is useful
|
| 48 |
+
as a container for initial conditions and for transforming phase-space positions
|
| 49 |
+
to new coordinate representations or reference frames.
|
| 50 |
+
|
| 51 |
+
The easiest way to create a |psp| object is to pass in a pair of
|
| 52 |
+
`~astropy.units.Quantity` objects that represent the Cartesian position and
|
| 53 |
+
velocity vectors::
|
| 54 |
+
|
| 55 |
+
>>> gd.PhaseSpacePosition(pos=[4., 8., 15.] * u.kpc,
|
| 56 |
+
... vel=[-150., 50., 15.] * u.km/u.s)
|
| 57 |
+
<PhaseSpacePosition cartesian, dim=3, shape=()>
|
| 58 |
+
|
| 59 |
+
By default, passing in `~astropy.units.Quantity`'s are interpreted as Cartesian
|
| 60 |
+
coordinates and velocities. This works with arrays of positions and velocities
|
| 61 |
+
as well::
|
| 62 |
+
|
| 63 |
+
>>> x = np.arange(24).reshape(3, 8)
|
| 64 |
+
>>> v = np.arange(24).reshape(3, 8)
|
| 65 |
+
>>> w = gd.PhaseSpacePosition(pos=x * u.kpc,
|
| 66 |
+
... vel=v * u.km/u.s)
|
| 67 |
+
>>> w
|
| 68 |
+
<PhaseSpacePosition cartesian, dim=3, shape=(8,)>
|
| 69 |
+
|
| 70 |
+
This is interpreted as 8, 6-dimensional phase-space positions.
|
| 71 |
+
|
| 72 |
+
The class internally stores the positions and velocities as
|
| 73 |
+
`~astropy.coordinates.BaseRepresentation` and
|
| 74 |
+
`~astropy.coordinates.BaseDifferential` subclasses; in this case,
|
| 75 |
+
`~astropy.coordinates.CartesianRepresentation` and
|
| 76 |
+
`~astropy.coordinates.CartesianDifferential`::
|
| 77 |
+
|
| 78 |
+
>>> w.pos
|
| 79 |
+
<CartesianRepresentation (x, y, z) in kpc
|
| 80 |
+
[(0., 8., 16.), (1., 9., 17.), (2., 10., 18.), (3., 11., 19.),
|
| 81 |
+
(4., 12., 20.), (5., 13., 21.), (6., 14., 22.), (7., 15., 23.)]>
|
| 82 |
+
>>> w.vel
|
| 83 |
+
<CartesianDifferential (d_x, d_y, d_z) in km / s
|
| 84 |
+
[(0., 8., 16.), (1., 9., 17.), (2., 10., 18.), (3., 11., 19.),
|
| 85 |
+
(4., 12., 20.), (5., 13., 21.), (6., 14., 22.), (7., 15., 23.)]>
|
| 86 |
+
|
| 87 |
+
All of the components of these classes are mapped to attributes of the
|
| 88 |
+
phase-space position class for convenience, but with more user-friendly names.
|
| 89 |
+
These mappings are defined in the class definition of
|
| 90 |
+
`~gala.dynamics.PhaseSpacePosition`. For example, to access the ``x`` component
|
| 91 |
+
of the position and the ``v_x`` component of the velocity::
|
| 92 |
+
|
| 93 |
+
>>> w.x # doctest: +FLOAT_CMP
|
| 94 |
+
<Quantity [0.,1.,2.,3.,4.,5.,6.,7.] kpc>
|
| 95 |
+
>>> w.v_x # doctest: +FLOAT_CMP
|
| 96 |
+
<Quantity [0.,1.,2.,3.,4.,5.,6.,7.] km / s>
|
| 97 |
+
|
| 98 |
+
The default representation is Cartesian, but the class can also be instantiated
|
| 99 |
+
with representation objects instead of `~astropy.units.Quantity`'s -- this is
|
| 100 |
+
useful for creating |psp| or |orb| instances from non-Cartesian
|
| 101 |
+
representations of the position and velocity::
|
| 102 |
+
|
| 103 |
+
>>> pos = CylindricalRepresentation(rho=np.linspace(1., 4, 4) * u.kpc,
|
| 104 |
+
... phi=np.linspace(0, np.pi, 4) * u.rad,
|
| 105 |
+
... z=np.linspace(-1, 1., 4) * u.kpc)
|
| 106 |
+
>>> vel = CylindricalDifferential(d_rho=np.linspace(100, 150, 4) * u.km/u.s,
|
| 107 |
+
... d_phi=np.linspace(-1, 1, 4) * u.rad/u.Myr,
|
| 108 |
+
... d_z=np.linspace(-15, 15., 4) * u.km/u.s)
|
| 109 |
+
>>> w = gd.PhaseSpacePosition(pos=pos, vel=vel)
|
| 110 |
+
>>> w
|
| 111 |
+
<PhaseSpacePosition cylindrical, dim=3, shape=(4,)>
|
| 112 |
+
>>> w.rho
|
| 113 |
+
<Quantity [1., 2., 3., 4.] kpc>
|
| 114 |
+
|
| 115 |
+
We can easily transform the full phase-space vector to new representations or
|
| 116 |
+
coordinate frames. These transformations use the :mod:`astropy.coordinates`
|
| 117 |
+
|astropyrep|_::
|
| 118 |
+
|
| 119 |
+
>>> cart = w.represent_as('cartesian')
|
| 120 |
+
>>> cart.x
|
| 121 |
+
<Quantity [ 1. , 1. , -1.5, -4. ] kpc>
|
| 122 |
+
>>> sph = w.represent_as('spherical')
|
| 123 |
+
>>> sph.distance
|
| 124 |
+
<Distance [1.41421356, 2.02758751, 3.01846171, 4.12310563] kpc>
|
| 125 |
+
|
| 126 |
+
There is also support for transforming the positions and velocities (assumed to
|
| 127 |
+
be in a `~astropy.coordinates.Galactocentric` frame) to any of the other
|
| 128 |
+
coordinate frames. For example, to transform to
|
| 129 |
+
:class:`~astropy.coordinates.Galactic` coordinates::
|
| 130 |
+
|
| 131 |
+
>>> from astropy.coordinates import Galactic
|
| 132 |
+
>>> gal_c = w.to_coord_frame(Galactic())
|
| 133 |
+
>>> gal_c # doctest: +FLOAT_CMP
|
| 134 |
+
<Galactic Coordinate: (l, b, distance) in (deg, deg, kpc)
|
| 135 |
+
[(4.40971301e-05, -6.23850462, 9.17891228),
|
| 136 |
+
(1.07501936e+01, -2.04017409, 9.29170644),
|
| 137 |
+
(2.14246214e+01, 2.65220588, 7.12026744),
|
| 138 |
+
(7.35169893e-05, 13.50991169, 4.23668468)]
|
| 139 |
+
(pm_l_cosb, pm_b, radial_velocity) in (mas / yr, mas / yr, km / s)
|
| 140 |
+
[( -28.11596908, -0.297625 , 89.093095 ),
|
| 141 |
+
( -13.077309 , 0.15891073, 511.60269726),
|
| 142 |
+
( -7.04751509, 1.33976418, -1087.52574084),
|
| 143 |
+
(-206.97042166, 2.22471526, -156.82064814)]>
|
| 144 |
+
|
| 145 |
+
We can easily plot projections of the phase-space positions using the
|
| 146 |
+
`~gala.dynamics.PhaseSpacePosition.plot` method::
|
| 147 |
+
|
| 148 |
+
>>> np.random.seed(42)
|
| 149 |
+
>>> x = np.random.uniform(-10, 10, size=(3,128))
|
| 150 |
+
>>> v = np.random.uniform(-200, 200, size=(3,128))
|
| 151 |
+
>>> w = gd.PhaseSpacePosition(pos=x * u.kpc,
|
| 152 |
+
... vel=v * u.km/u.s)
|
| 153 |
+
>>> fig = w.plot() # doctest: +SKIP
|
| 154 |
+
|
| 155 |
+
.. plot::
|
| 156 |
+
:align: center
|
| 157 |
+
:context: close-figs
|
| 158 |
+
|
| 159 |
+
import astropy.units as u
|
| 160 |
+
import numpy as np
|
| 161 |
+
import gala.dynamics as gd
|
| 162 |
+
|
| 163 |
+
np.random.seed(42)
|
| 164 |
+
x = np.random.uniform(-10,10,size=(3,128))
|
| 165 |
+
v = np.random.uniform(-200,200,size=(3,128))
|
| 166 |
+
w = gd.PhaseSpacePosition(pos=x*u.kpc,
|
| 167 |
+
vel=v*u.km/u.s)
|
| 168 |
+
fig = w.plot()
|
| 169 |
+
|
| 170 |
+
This is a thin wrapper around the `~gala.dynamics.plot_projections`
|
| 171 |
+
function and any keyword arguments are passed through to that function::
|
| 172 |
+
|
| 173 |
+
>>> fig = w.plot(components=['x', 'v_z'], color='r',
|
| 174 |
+
... facecolor='none', marker='o', s=20, alpha=0.5) # doctest: +SKIP
|
| 175 |
+
|
| 176 |
+
.. plot::
|
| 177 |
+
:align: center
|
| 178 |
+
:context: close-figs
|
| 179 |
+
:width: 60%
|
| 180 |
+
|
| 181 |
+
fig = w.plot(components=['x', 'v_z'], color='r',
|
| 182 |
+
facecolor='none', marker='o', s=20, alpha=0.5)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
.. _orbit:
|
| 186 |
+
|
| 187 |
+
Orbits
|
| 188 |
+
======
|
| 189 |
+
|
| 190 |
+
The |orb| class inherits much of the functionality from |psp| (described above)
|
| 191 |
+
and adds some additional features that are useful for time-series orbits.
|
| 192 |
+
|
| 193 |
+
An |orb| instance is initialized like the |psp|--with arrays of positions and
|
| 194 |
+
velocities-- but usually also requires specifying a time array as well. Also,
|
| 195 |
+
the extra axes in these arrays hold special meaning for the |orb| class. The
|
| 196 |
+
position and velocity arrays passed to |psp| can have arbitrary numbers of
|
| 197 |
+
dimensions as long as the 0th axis specifies the dimensionality. For the |orb|
|
| 198 |
+
class, the 0th axis remains the axis of dimensionality, but the 1st axis now is
|
| 199 |
+
always assumed to be the time axis. For example, an input position with shape
|
| 200 |
+
``(2,128)`` to a |psp| represents 128 independent 2D positions, but to a |orb|
|
| 201 |
+
it represents a single orbit's positions at 128 times::
|
| 202 |
+
|
| 203 |
+
>>> t = np.linspace(0, 100, 128) * u.Myr
|
| 204 |
+
>>> Om = 1E-1 * u.rad / u.Myr
|
| 205 |
+
>>> pos = np.vstack((5*np.cos(Om*t), np.sin(Om*t))).value * u.kpc
|
| 206 |
+
>>> vel = np.vstack((-5*np.sin(Om*t), np.cos(Om*t))).value * u.kpc/u.Myr
|
| 207 |
+
>>> orbit = gd.Orbit(pos=pos, vel=vel)
|
| 208 |
+
>>> orbit
|
| 209 |
+
<Orbit ndcartesian, dim=2, shape=(128,)>
|
| 210 |
+
|
| 211 |
+
To create a single object that contains multiple orbits, the input position
|
| 212 |
+
object should have 3 axes. The last axis (``axis=2``) specifies the number of
|
| 213 |
+
orbits. So, an input position with shape ``(2,128,16)`` would represent 16, 2D
|
| 214 |
+
orbits, each with the same 128 times::
|
| 215 |
+
|
| 216 |
+
>>> t = np.linspace(0, 100, 128) * u.Myr
|
| 217 |
+
>>> Om = np.random.uniform(size=16) * u.rad / u.Myr
|
| 218 |
+
>>> angle = Om[None] * t[:, None]
|
| 219 |
+
>>> pos = np.stack((5*np.cos(angle), np.sin(angle))).value * u.kpc
|
| 220 |
+
>>> vel = np.stack((-5*np.sin(angle), np.cos(angle))).value * u.kpc/u.Myr
|
| 221 |
+
>>> orbit = gd.Orbit(pos=pos, vel=vel)
|
| 222 |
+
>>> orbit
|
| 223 |
+
<Orbit ndcartesian, dim=2, shape=(128, 16)>
|
| 224 |
+
|
| 225 |
+
To make full use of the orbit functionality, you must also pass in an array with
|
| 226 |
+
the time values and an instance of a `~gala.potential.potential.PotentialBase`
|
| 227 |
+
subclass that represents the potential that the orbit was integrated in::
|
| 228 |
+
|
| 229 |
+
>>> pot = gp.PlummerPotential(m=1E10, b=1., units=galactic)
|
| 230 |
+
>>> orbit = gd.Orbit(pos=pos*u.kpc, vel=vel*u.km/u.s,
|
| 231 |
+
... t=t*u.Myr, potential=pot)
|
| 232 |
+
|
| 233 |
+
(note, in this case ``pos`` and ``vel`` were not generated from integrating
|
| 234 |
+
an orbit in the potential ``pot``!). However, most of the time you won't need to
|
| 235 |
+
create |orb| objects from scratch! They are returned from any of the numerical
|
| 236 |
+
integration routines provided in `gala`. For example, they are returned by the
|
| 237 |
+
`~gala.potential.potential.PotentialBase.integrate_orbit` method of potential
|
| 238 |
+
objects and will automatically contain the ``time`` array and ``potential``
|
| 239 |
+
object. For example::
|
| 240 |
+
|
| 241 |
+
>>> pot = gp.PlummerPotential(m=1E10 * u.Msun, b=1. * u.kpc, units=galactic)
|
| 242 |
+
>>> w0 = gd.PhaseSpacePosition(pos=[10.,0,0] * u.kpc,
|
| 243 |
+
... vel=[0.,75,0] * u.km/u.s)
|
| 244 |
+
>>> orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=1., n_steps=5000)
|
| 245 |
+
>>> orbit
|
| 246 |
+
<Orbit cartesian, dim=3, shape=(5001,)>
|
| 247 |
+
>>> orbit.t
|
| 248 |
+
<Quantity [0.000e+00, 1.000e+00, 2.000e+00, ..., 4.998e+03, 4.999e+03,
|
| 249 |
+
5.000e+03] Myr>
|
| 250 |
+
>>> orbit.potential
|
| 251 |
+
<PlummerPotential: m=1.00e+10, b=1.00 (kpc,Myr,solMass,rad)>
|
| 252 |
+
|
| 253 |
+
Just like for |psp|, we can quickly visualize an orbit using the
|
| 254 |
+
`~gala.dynamics.Orbit.plot` method::
|
| 255 |
+
|
| 256 |
+
>>> fig = orbit.plot() # doctest: +SKIP
|
| 257 |
+
|
| 258 |
+
.. plot::
|
| 259 |
+
:align: center
|
| 260 |
+
:context: close-figs
|
| 261 |
+
|
| 262 |
+
import astropy.units as u
|
| 263 |
+
import gala.dynamics as gd
|
| 264 |
+
import gala.potential as gp
|
| 265 |
+
from gala.units import galactic
|
| 266 |
+
|
| 267 |
+
pot = gp.PlummerPotential(m=1E10 * u.Msun, b=1. * u.kpc, units=galactic)
|
| 268 |
+
w0 = gd.PhaseSpacePosition(pos=[2.,0,0] * u.kpc,
|
| 269 |
+
vel=[0.,75,15] * u.km/u.s)
|
| 270 |
+
orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=1., n_steps=5000)
|
| 271 |
+
fig = orbit.plot()
|
| 272 |
+
|
| 273 |
+
Again, this is a thin wrapper around the `~gala.dynamics.plot_projections`
|
| 274 |
+
function and any keyword arguments are passed through to that function::
|
| 275 |
+
|
| 276 |
+
>>> fig = orbit.plot(linewidth=4., alpha=0.5, color='r') # doctest: +SKIP
|
| 277 |
+
|
| 278 |
+
.. plot::
|
| 279 |
+
:align: center
|
| 280 |
+
:context: close-figs
|
| 281 |
+
|
| 282 |
+
fig = orbit.plot(linewidth=4., alpha=0.5, color='r')
|
| 283 |
+
|
| 284 |
+
Alternatively, for three-dimensional orbits, we can visualize the orbit using
|
| 285 |
+
the 3D projection capabilities in `matplotlib`::
|
| 286 |
+
|
| 287 |
+
>>> fig = orbit.plot_3d(alpha=0.5, color='k') # doctest: +SKIP
|
| 288 |
+
|
| 289 |
+
.. plot::
|
| 290 |
+
:align: center
|
| 291 |
+
:context: close-figs
|
| 292 |
+
:width: 60%
|
| 293 |
+
|
| 294 |
+
fig = orbit.plot_3d(alpha=0.5, color='k')
|
| 295 |
+
|
| 296 |
+
We can also quickly create an animation of the progression of an orbit using the
|
| 297 |
+
`~gala.dynamics.Orbit.animate` method, which animated projections of the orbit::
|
| 298 |
+
|
| 299 |
+
>>> fig, anim = orbit[:1000].animate(stride=10) # doctest: +SKIP
|
| 300 |
+
|
| 301 |
+
.. raw:: html
|
| 302 |
+
|
| 303 |
+
<video controls src="../_static/orbit-anim1.mp4" width=650 autoplay loop></video>
|
| 304 |
+
|
| 305 |
+
The animate method acts like `~gala.dynamics.Orbit.plot`, in that it works for
|
| 306 |
+
any coordinate representation (Cartesian, cylindrical, etc.) and supports only
|
| 307 |
+
animating subsets of the phase-space components. For example, to make an
|
| 308 |
+
animation of an orbit in cylindrical coordinates, showing the orbit proress in
|
| 309 |
+
the R,z meridional plane::
|
| 310 |
+
|
| 311 |
+
>>> fig, anim = orbit[:1000].cylindrical.animate(components=['rho', 'z'], # doctest: +SKIP
|
| 312 |
+
... stride=10)
|
| 313 |
+
|
| 314 |
+
.. raw:: html
|
| 315 |
+
|
| 316 |
+
<video controls src="../_static/orbit-anim2.mp4" width=450 autoplay loop></video>
|
| 317 |
+
|
| 318 |
+
We can also quickly compute quantities like the angular momentum, and estimates
|
| 319 |
+
for the pericenter, apocenter, eccentricity of the orbit. Estimates for the
|
| 320 |
+
latter few get better with smaller timesteps::
|
| 321 |
+
|
| 322 |
+
>>> orbit = gp.Hamiltonian(pot).integrate_orbit(w0, dt=0.1, n_steps=100000)
|
| 323 |
+
>>> np.mean(orbit.angular_momentum(), axis=1) # doctest: +FLOAT_CMP
|
| 324 |
+
<Quantity [0. ,0. ,0.76703412] kpc2 / Myr>
|
| 325 |
+
>>> orbit.eccentricity() # doctest: +FLOAT_CMP
|
| 326 |
+
<Quantity 0.31951765618193967>
|
| 327 |
+
>>> orbit.pericenter() # doctest: +FLOAT_CMP
|
| 328 |
+
<Quantity 10.00000005952518 kpc>
|
| 329 |
+
>>> orbit.apocenter() # doctest: +FLOAT_CMP
|
| 330 |
+
<Quantity 19.390916871970223 kpc>
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
More information
|
| 334 |
+
================
|
| 335 |
+
|
| 336 |
+
Internally, both of the above classes rely on the Astropy representation
|
| 337 |
+
transformation framework (i.e. the subclasses of
|
| 338 |
+
`~astropy.coordinates.BaseRepresentation` and
|
| 339 |
+
`~astropy.coordinates.BaseDifferential`). However, at present these classes only
|
| 340 |
+
support 3D positions and differentials (velocities). The |psp| and |orb| classes
|
| 341 |
+
both support arbitrary numbers of dimensions and, when relevant, rely on custom
|
| 342 |
+
subclasses of the representation classes to handle such cases. See the
|
| 343 |
+
:ref:`nd-representations` page for more information about these classes.
|