├── docs
├── requirements.txt
├── utility_functions.rst
├── Images
│ ├── gsm_icrs_radec.png
│ ├── gleam_50srcs_radec.png
│ ├── gleam_50srcs_freqflux.png
│ ├── gsm_icrs_fluxcounts.png
│ ├── gleam_50srcs_fluxcounts.png
│ ├── gsm_icrs_flux_molliwede.png
│ ├── pointsource_catalog_radec.png
│ ├── fhd_catalog_specind_atfreqs.png
│ ├── gleam_50srcs_radec_raselect.png
│ ├── gsm_gal_coneselect_molliwede.png
│ ├── gsm_galactic_flux_molliwede.png
│ ├── gsm_icrs_indselect_molliwede.png
│ ├── gleam_50srcs_radec_fluxselect.png
│ ├── gleam_subband_spectra_atfreqs.png
│ ├── fhd_catalog_extended_models_radec.png
│ ├── gsm_gal_neighborselect_molliwede.png
│ ├── gsm_point_galactic_flux_molliwede.png
│ ├── pointsource_catalog_radec_concat.png
│ ├── fhd_catalog_extended_models_refspec.png
│ ├── gleam_50srcs_fluxcounts_fluxselect.png
│ ├── fhd_catalog_extended_models_fluxcounts.png
│ └── fhd_catalog_extended_models_radec_picA.png
├── references
│ ├── skyh5_memo.pdf
│ └── skyh5_memo.tex
├── developer_docs.rst
├── Makefile
├── conftest.py
├── make.bat
├── make_index.py
├── make_skymodel.py
└── conf.py
├── src
└── pyradiosky
│ ├── data
│ ├── __init__.py
│ ├── gsm_icrs.skyh5
│ ├── fhd_catalog.sav
│ ├── fhd_catalog_bad.sav
│ ├── gsm_galactic.skyh5
│ ├── healpix_disk.skyh5
│ ├── fhd_source_array.sav
│ ├── extended_source_test.sav
│ ├── fhd_catalog_no_extend.sav
│ ├── old_skyh5_point_sources.skyh5
│ ├── fhd_catalog_with_beam_values.sav
│ ├── pointsource_catalog.txt
│ ├── simple_test.vot
│ ├── mock_hera_text_2458098.27471.txt
│ └── single_source_old.vot
│ ├── __init__.py
│ ├── cli.py
│ ├── spherical_coords_transforms.py
│ └── utils.py
├── ci
├── publish.yaml
├── min_deps.yaml
├── min_versions.yaml
└── full_deps.yaml
├── MANIFEST.in
├── .coveragerc
├── environment.yaml
├── .readthedocs.yml
├── .pre-commit-config.yaml
├── tests
├── conftest.py
├── test_spherical_coords_transforms.py
└── test_utils.py
├── setup.py
├── .github
├── original_pr_templates
│ ├── documentation.md
│ ├── version_change.md
│ ├── build_ci_change.md
│ ├── bug_fix.md
│ ├── new_feature.md
│ └── other.md
├── workflows
│ ├── externaltests.yaml
│ ├── publish-to-test-pypi.yaml
│ └── testsuite.yaml
├── PULL_REQUEST_TEMPLATE.md
└── CONTRIBUTING.md
├── LICENSE
├── .gitignore
├── pyproject.toml
├── CODE_OF_CONDUCT.md
├── paper.md
├── README.md
├── paper.bib
└── CHANGELOG.md
/docs/requirements.txt:
--------------------------------------------------------------------------------
1 | sphinx>=6.2
2 | sphinx_rtd_theme==1.2.2
3 |
--------------------------------------------------------------------------------
/src/pyradiosky/data/__init__.py:
--------------------------------------------------------------------------------
1 | """Init file for data directory."""
2 |
3 | DATA_PATH = __path__[0]
4 |
--------------------------------------------------------------------------------
/docs/utility_functions.rst:
--------------------------------------------------------------------------------
1 | Utility Functions
2 | =================
3 |
4 | .. automodule:: pyradiosky.utils
5 | :members:
6 |
--------------------------------------------------------------------------------
/docs/Images/gsm_icrs_radec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gsm_icrs_radec.png
--------------------------------------------------------------------------------
/docs/references/skyh5_memo.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/references/skyh5_memo.pdf
--------------------------------------------------------------------------------
/docs/Images/gleam_50srcs_radec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gleam_50srcs_radec.png
--------------------------------------------------------------------------------
/src/pyradiosky/data/gsm_icrs.skyh5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/gsm_icrs.skyh5
--------------------------------------------------------------------------------
/docs/Images/gleam_50srcs_freqflux.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gleam_50srcs_freqflux.png
--------------------------------------------------------------------------------
/docs/Images/gsm_icrs_fluxcounts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gsm_icrs_fluxcounts.png
--------------------------------------------------------------------------------
/src/pyradiosky/data/fhd_catalog.sav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/fhd_catalog.sav
--------------------------------------------------------------------------------
/docs/Images/gleam_50srcs_fluxcounts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gleam_50srcs_fluxcounts.png
--------------------------------------------------------------------------------
/docs/Images/gsm_icrs_flux_molliwede.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gsm_icrs_flux_molliwede.png
--------------------------------------------------------------------------------
/src/pyradiosky/data/fhd_catalog_bad.sav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/fhd_catalog_bad.sav
--------------------------------------------------------------------------------
/src/pyradiosky/data/gsm_galactic.skyh5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/gsm_galactic.skyh5
--------------------------------------------------------------------------------
/src/pyradiosky/data/healpix_disk.skyh5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/healpix_disk.skyh5
--------------------------------------------------------------------------------
/docs/Images/pointsource_catalog_radec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/pointsource_catalog_radec.png
--------------------------------------------------------------------------------
/src/pyradiosky/data/fhd_source_array.sav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/fhd_source_array.sav
--------------------------------------------------------------------------------
/docs/Images/fhd_catalog_specind_atfreqs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/fhd_catalog_specind_atfreqs.png
--------------------------------------------------------------------------------
/docs/Images/gleam_50srcs_radec_raselect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gleam_50srcs_radec_raselect.png
--------------------------------------------------------------------------------
/docs/Images/gsm_gal_coneselect_molliwede.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gsm_gal_coneselect_molliwede.png
--------------------------------------------------------------------------------
/docs/Images/gsm_galactic_flux_molliwede.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gsm_galactic_flux_molliwede.png
--------------------------------------------------------------------------------
/docs/Images/gsm_icrs_indselect_molliwede.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gsm_icrs_indselect_molliwede.png
--------------------------------------------------------------------------------
/src/pyradiosky/data/extended_source_test.sav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/extended_source_test.sav
--------------------------------------------------------------------------------
/docs/Images/gleam_50srcs_radec_fluxselect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gleam_50srcs_radec_fluxselect.png
--------------------------------------------------------------------------------
/docs/Images/gleam_subband_spectra_atfreqs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gleam_subband_spectra_atfreqs.png
--------------------------------------------------------------------------------
/src/pyradiosky/data/fhd_catalog_no_extend.sav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/fhd_catalog_no_extend.sav
--------------------------------------------------------------------------------
/docs/Images/fhd_catalog_extended_models_radec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/fhd_catalog_extended_models_radec.png
--------------------------------------------------------------------------------
/docs/Images/gsm_gal_neighborselect_molliwede.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gsm_gal_neighborselect_molliwede.png
--------------------------------------------------------------------------------
/docs/Images/gsm_point_galactic_flux_molliwede.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gsm_point_galactic_flux_molliwede.png
--------------------------------------------------------------------------------
/docs/Images/pointsource_catalog_radec_concat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/pointsource_catalog_radec_concat.png
--------------------------------------------------------------------------------
/src/pyradiosky/data/old_skyh5_point_sources.skyh5:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/old_skyh5_point_sources.skyh5
--------------------------------------------------------------------------------
/docs/Images/fhd_catalog_extended_models_refspec.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/fhd_catalog_extended_models_refspec.png
--------------------------------------------------------------------------------
/docs/Images/gleam_50srcs_fluxcounts_fluxselect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/gleam_50srcs_fluxcounts_fluxselect.png
--------------------------------------------------------------------------------
/src/pyradiosky/data/fhd_catalog_with_beam_values.sav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/src/pyradiosky/data/fhd_catalog_with_beam_values.sav
--------------------------------------------------------------------------------
/docs/Images/fhd_catalog_extended_models_fluxcounts.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/fhd_catalog_extended_models_fluxcounts.png
--------------------------------------------------------------------------------
/docs/Images/fhd_catalog_extended_models_radec_picA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RadioAstronomySoftwareGroup/pyradiosky/HEAD/docs/Images/fhd_catalog_extended_models_radec_picA.png
--------------------------------------------------------------------------------
/ci/publish.yaml:
--------------------------------------------------------------------------------
1 | name: publish
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - astropy>=6.0
6 | - h5py>=3.7
7 | - numpy>=1.23
8 | - pip
9 | - pyuvdata>=3.2.3
10 | - scipy>=1.9
11 | - setuptools_scm>=8.1
12 | - pip:
13 | - build
14 |
--------------------------------------------------------------------------------
/ci/min_deps.yaml:
--------------------------------------------------------------------------------
1 | name: min_deps
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - astropy>=6.0
6 | - coverage
7 | - h5py>=3.7
8 | - numpy>=1.23
9 | - pytest
10 | - pytest-cov
11 | - pyuvdata>=3.2.3
12 | - scipy>=1.9
13 | - setuptools_scm>=8.1
14 | - pip
15 |
--------------------------------------------------------------------------------
/src/pyradiosky/data/pointsource_catalog.txt:
--------------------------------------------------------------------------------
1 | Source_ID RA_J2000 Dec_J2000 Flux_Density_I_Jy Frequency
2 | src1 01.00 -30.00 0.5 1e6
3 | src2 01.30 -31.00 2.0 1e6
4 | src3 01.35 -32.00 5.0 1e6
5 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.md
2 | include LICENSE
3 | include *.bib
4 |
5 | recursive-include src/pyradiosky/data *
6 |
7 | recursive-include tests *.py
8 |
9 | recursive-include docs *
10 | exclude docs/.DS_Store
11 | prune docs/_static
12 | prune docs/_templates
13 | prune docs/build
14 | prune docs/__pycache__
15 |
--------------------------------------------------------------------------------
/docs/developer_docs.rst:
--------------------------------------------------------------------------------
1 | Developer Docs
2 | ==============
3 | Documentation for all the under-the-hood classes and functions that most users
4 | won't need to interact with.
5 |
6 | spherical_coords_transforms
7 | ___________________________
8 |
9 | .. automodule:: pyradiosky.spherical_coords_transforms
10 | :members:
11 |
--------------------------------------------------------------------------------
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | omit =
3 | */tests/*
4 | */docs/*
5 | setup.py
6 | branch = true
7 | source =
8 | pyradiosky
9 |
10 | [report]
11 | omit =
12 | */tests/*
13 | */docs/*
14 | setup.py
15 |
16 | show_missing = true
17 |
18 | [paths]
19 | source =
20 | src
21 | */site-packages"
22 | tests =
23 | tests
24 | */tests
25 |
--------------------------------------------------------------------------------
/ci/min_versions.yaml:
--------------------------------------------------------------------------------
1 | name: min_versions
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - astropy==6.0
6 | - astropy-healpix==1.0.2
7 | - astroquery==0.4.4
8 | - h5py==3.7.0
9 | - numpy==1.23.*
10 | - scipy==1.9.*
11 | - coverage
12 | - pytest-cov
13 | - setuptools_scm==8.1
14 | - pyuvdata==3.2.3
15 | - pip
16 | - pip:
17 | - lunarsky==0.2.5
18 |
--------------------------------------------------------------------------------
/ci/full_deps.yaml:
--------------------------------------------------------------------------------
1 | name: full_deps
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - astropy>=6.0
6 | - astropy-healpix>=1.0.2
7 | - astroquery>=0.4.4
8 | - coverage
9 | - h5py>=3.7
10 | - matplotlib # just for the doctests
11 | - numpy>=1.23
12 | - pip
13 | - pytest-cov
14 | - pyuvdata>=3.2.3
15 | - scipy>=1.9
16 | - setuptools_scm>=8.1
17 | - pip:
18 | - lunarsky>=0.2.5
19 |
--------------------------------------------------------------------------------
/environment.yaml:
--------------------------------------------------------------------------------
1 | name: pyradiosky
2 | channels:
3 | - conda-forge
4 | dependencies:
5 | - astropy>=6.0
6 | - astropy-healpix>=1.0.2
7 | - astroquery>=0.4.4
8 | - h5py>=3.7
9 | - matplotlib
10 | - numpy>=1.23
11 | - coverage
12 | - pip
13 | - pre-commit
14 | - pypandoc
15 | - pytest-cov
16 | - pyuvdata>=3.2.3
17 | - scipy>=1.9
18 | - setuptools_scm>=8.1
19 | - sphinx
20 | - pip:
21 | - lunarsky>=0.2.5
22 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/conftest.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Radio Astronomy Software Group
2 | # Licensed under the 2-clause BSD License
3 |
4 | """Testing environment setup and teardown for pytest."""
5 |
6 | import os
7 | import shutil
8 | from pathlib import Path
9 |
10 | import pytest
11 |
12 |
13 | @pytest.fixture(autouse=True, scope="session")
14 | def setup_and_teardown_package(tmp_path_factory):
15 | """Make data/test directory to put test output files in."""
16 | cwd = Path.cwd()
17 | tmp_path = tmp_path_factory.mktemp("skymodel_tests")
18 | try:
19 | os.chdir(tmp_path)
20 | if not os.path.exists("Images"):
21 | os.makedirs("Images")
22 | yield
23 | finally:
24 | os.chdir(cwd)
25 | shutil.rmtree(tmp_path)
26 |
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | # Build documentation in the docs/ directory with Sphinx
9 | sphinx:
10 | builder: html
11 | configuration: docs/conf.py
12 | # might want to do this in future:
13 | # fail_on_warning: true
14 |
15 | # Do not build the docs in additional formats (e.g. PDF and ePub), use default
16 | formats: []
17 |
18 | # Set the version of Python and other tools you might need
19 | build:
20 | os: ubuntu-22.04
21 | tools:
22 | python: "3.12"
23 |
24 | # Optionally install options and requirements required to build your docs
25 | python:
26 | install:
27 | - method: pip
28 | path: .
29 | extra_requirements:
30 | - doc
31 | - requirements: docs/requirements.txt
32 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | exclude: '(^docs/conf.py|^user_data/External_tables/)'
2 |
3 | repos:
4 | - repo: https://github.com/pre-commit/pre-commit-hooks
5 | rev: v6.0.0
6 | hooks:
7 | - id: trailing-whitespace
8 | - id: check-added-large-files
9 | - id: check-ast
10 | - id: check-json
11 | - id: check-merge-conflict
12 | - id: check-xml
13 | - id: check-yaml
14 | - id: debug-statements
15 | - id: end-of-file-fixer
16 | - id: mixed-line-ending
17 | args: ['--fix=no']
18 |
19 | - repo: https://github.com/astral-sh/ruff-pre-commit
20 | rev: v0.14.6
21 | hooks:
22 | - id: ruff
23 | - id: ruff-format
24 |
25 | - repo: https://github.com/PyCQA/bandit
26 | rev: 1.9.2
27 | hooks:
28 | - id: bandit
29 | args: [--skip, "B101", --recursive, pyradiosky]
30 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Radio Astronomy Software Group
2 | # Licensed under the 2-clause BSD License
3 |
4 | import pytest
5 | from astropy.time import Time
6 | from astropy.utils import iers
7 |
8 |
9 | @pytest.fixture(autouse=True, scope="session")
10 | def setup_and_teardown_package():
11 | """Handle IERS errors."""
12 | # Do a calculation that requires a current IERS table. This will trigger
13 | # automatic downloading of the IERS table if needed, including trying the
14 | # mirror site in python 3 (but won't redownload if a current one exists).
15 | # If there's not a current IERS table and it can't be downloaded, turn off
16 | # auto downloading for the tests and turn it back on once all tests are
17 | # completed (done by extending auto_max_age).
18 | try:
19 | t1 = Time.now()
20 | t1.ut1 # noqa
21 | except Exception:
22 | iers.conf.auto_max_age = None
23 |
24 | yield
25 |
26 | iers.conf.auto_max_age = 30
27 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019 Radio Astronomy Software Group
2 | # Licensed under the 2-clause BSD License
3 |
4 | from setuptools import setup
5 |
6 | # The only reason we currently need a setup.py is because we use a special
7 | # branch scheme. See https://setuptools-scm.readthedocs.io/en/stable/customizing/
8 |
9 |
10 | # define the branch scheme. Have to do it here so we don't have to modify the path
11 | def branch_scheme(version):
12 | """
13 | Local version scheme that adds the branch name for absolute reproducibility.
14 |
15 | If and when this is added to setuptools_scm this function can be removed.
16 | """
17 | if version.exact or version.node is None:
18 | return version.format_choice("", "+d{time:{time_format}}", time_format="%Y%m%d")
19 | else:
20 | if version.branch == "main":
21 | return version.format_choice("+{node}", "+{node}.dirty")
22 | else:
23 | version_str = version.format_choice(
24 | "+{node}.{branch}", "+{node}.{branch}.dirty"
25 | )
26 | version_str = version_str.replace("/", ".")
27 | return version_str
28 |
29 |
30 | setup(use_scm_version={"local_scheme": branch_scheme})
31 |
--------------------------------------------------------------------------------
/.github/original_pr_templates/documentation.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Documentation
3 | about: Update or improve the documentation.
4 | labels: documentation
5 | ---
6 |
7 |
8 | ## Description
9 |
10 |
11 | ## Motivation and Context
12 |
13 |
14 |
15 | ## Checklist:
16 |
17 |
18 |
19 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyuvdata/blob/main/.github/CONTRIBUTING.md).
20 | - [ ] My code follows the code style of this project.
21 | Documentation change checklist:
22 | - [ ] Any updated docstrings use the [numpy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html).
23 |
--------------------------------------------------------------------------------
/.github/original_pr_templates/version_change.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Version Change
3 | about: Update the version.
4 | ---
5 |
6 |
7 | ## Description
8 |
9 |
10 | ## Motivation and Context
11 |
12 |
13 |
14 | ## Checklist:
15 |
16 |
17 |
18 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyuvdata/blob/main/.github/CONTRIBUTING.md).
19 | - [ ] My code follows the code style of this project.
20 | - [ ] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyuvdata/blob/main/CHANGELOG.md) to put all the unreleased changes under the new version (leaving the unreleased section empty).
21 |
--------------------------------------------------------------------------------
/src/pyradiosky/data/simple_test.vot:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | | TEST |
15 | 0.00000000 |
16 | -30.00000000 |
17 | 1.000000 |
18 |
19 |
20 | | TEST2 |
21 | 0.00000000 |
22 | 0.00000000 |
23 | 0.000000 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.github/original_pr_templates/build_ci_change.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Build or Continuous Integration Change
3 | about: Modify a continuous integration setup or the package building.
4 | ---
5 |
6 |
7 | ## Description
8 |
9 |
10 | ## Motivation and Context
11 |
12 |
13 |
14 | ## Checklist:
15 |
16 |
17 |
18 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyuvdata/blob/main/.github/CONTRIBUTING.md).
19 | - [ ] My code follows the code style of this project.
20 | - [ ] If required or optional dependencies have changed (including version numbers), I have updated the readme to reflect this.
21 | - [ ] If this is a new CI setup, I have added the associated badge to the readme
22 |
--------------------------------------------------------------------------------
/.github/original_pr_templates/bug_fix.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Fix
3 | about: Fix a bug
4 | labels: bug
5 | ---
6 |
7 |
8 | ## Description
9 |
10 |
11 | ## Motivation and Context
12 |
13 |
14 |
15 | ## Checklist:
16 |
17 |
18 |
19 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
20 | - [ ] My code follows the code style of this project.
21 | - [ ] My fix includes a new test that breaks as a result of the bug (if possible).
22 | - [ ] My change includes a breaking change
23 | - [ ] My change includes backwards compatibility and deprecation warnings (if possible).
24 | - [ ] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/CHANGELOG.md).
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 2-Clause License
2 |
3 | Copyright (c) 2019, Radio Astronomy Software Group
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | 1. Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | 2. Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 |
--------------------------------------------------------------------------------
/.github/original_pr_templates/new_feature.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: New Feature
3 | about: Add a new feature
4 | ---
5 |
6 |
7 | ## Description
8 |
9 |
10 | ## Motivation and Context
11 |
12 |
13 |
14 | ## Checklist:
15 |
16 |
17 |
18 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
19 | - [ ] My code follows the code style of this project.
20 | - [ ] I have added or updated the docstrings associated with my feature using the [numpy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html).
21 | - [ ] I have added tests to cover my new feature.
22 | - [ ] My change includes a breaking change
23 | - [ ] My change includes backwards compatibility and deprecation warnings (if possible).
24 | - [ ] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/CHANGELOG.md).
25 |
--------------------------------------------------------------------------------
/.github/original_pr_templates/other.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Other
3 | about: Anything that doesn't fit in any of the other types of pull requests.
4 | ---
5 |
6 |
7 | ## Description
8 |
9 |
10 | ## Motivation and Context
11 |
12 |
13 |
14 | ## Checklist:
15 |
16 |
17 |
18 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
19 | - [ ] My code follows the code style of this project.
20 | - [ ] Any new or updated docstrings use the [numpy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html).
21 | - [ ] I have added tests to cover any changes.
22 | - [ ] My change includes a breaking change
23 | - [ ] My change includes backwards compatibility and deprecation warnings (if possible).
24 | - [ ] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/CHANGELOG.md) if appropriate.
25 |
--------------------------------------------------------------------------------
/src/pyradiosky/__init__.py:
--------------------------------------------------------------------------------
1 | """Define namespace."""
2 |
3 | import contextlib
4 | from importlib.metadata import PackageNotFoundError, version
5 | from pathlib import Path
6 |
7 | from setuptools_scm import get_version
8 |
9 |
10 | # copy this function here from setup.py.
11 | # Copying code is terrible, but it's better than altering the python path in setup.py.
12 | def branch_scheme(version): # pragma: nocover
13 | """
14 | Local version scheme that adds the branch name for absolute reproducibility.
15 |
16 | If and when this is added to setuptools_scm this function can be removed.
17 | """
18 | if version.exact or version.node is None:
19 | return version.format_choice("", "+d{time:{time_format}}", time_format="%Y%m%d")
20 | else:
21 | if version.branch == "main":
22 | return version.format_choice("+{node}", "+{node}.dirty")
23 | else:
24 | version_str = version.format_choice(
25 | "+{node}.{branch}", "+{node}.{branch}.dirty"
26 | )
27 | version_str = version_str.replace("/", ".")
28 | return version_str
29 |
30 |
31 | try:
32 | # get accurate version for developer installs
33 | version_str = get_version(
34 | Path(__file__).parent.parent.parent, local_scheme=branch_scheme
35 | )
36 |
37 | __version__ = version_str
38 |
39 | except (LookupError, ImportError):
40 | with contextlib.suppress(PackageNotFoundError):
41 | # Set the version automatically from the package details.
42 | __version__ = version("pyradiosky")
43 |
44 | from .skymodel import SkyModel # noqa
45 |
--------------------------------------------------------------------------------
/docs/make_index.py:
--------------------------------------------------------------------------------
1 | """
2 | Format the readme.md file into the sphinx index.rst file.
3 |
4 | """
5 |
6 | import inspect
7 | import os
8 |
9 | import pypandoc
10 | from astropy.time import Time
11 |
12 |
13 | def write_index_rst(readme_file=None, write_file=None):
14 | t = Time.now()
15 | t.format = "iso"
16 | t.out_subfmt = "date"
17 | out = (
18 | ".. pyradiosky documentation index file, created by\n"
19 | f" make_index.py on {t.iso}\n\n"
20 | )
21 |
22 | if readme_file is None:
23 | main_path = os.path.dirname(
24 | os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
25 | )
26 | readme_file = os.path.join(main_path, "README.md")
27 |
28 | readme_text = pypandoc.convert_file(readme_file, "rst")
29 |
30 | # convert relative links in readme to explicit links
31 | readme_text = readme_text.replace(
32 | "> $GITHUB_ENV
49 |
50 | - name: Check environment variable
51 | run: echo $SETUPTOOLS_SCM_PRETEND_VERSION
52 |
53 | - name: Build a binary wheel and a source tarball
54 | run: |
55 | python3 -m build
56 |
57 | - name: Publish to PyPI
58 | if: startsWith(github.ref, 'refs/tags')
59 | uses: pypa/gh-action-pypi-publish@release/v1
60 | with:
61 | password: ${{ secrets.pypi_password }}
62 |
--------------------------------------------------------------------------------
/src/pyradiosky/data/mock_hera_text_2458098.27471.txt:
--------------------------------------------------------------------------------
1 | SOURCE_ID RA_J2000 [deg] Dec_J2000 [deg] Flux [Jy] Frequency [Hz]
2 | src0 28.103507 -32.541308 1.00 150000000.00
3 | src1 25.734946 -32.672235 1.00 150000000.00
4 | src2 23.361369 -32.760516 1.00 150000000.00
5 | src3 22.172001 -32.787771 1.00 150000000.00
6 | src4 20.983198 -32.804964 1.00 150000000.00
7 | src5 18.604394 -32.806366 1.00 150000000.00
8 | src6 16.226085 -32.764718 1.00 150000000.00
9 | src7 13.852232 -32.679226 1.00 150000000.00
10 | src8 11.483261 -32.551071 1.00 150000000.00
11 | src9 28.013547 -31.546839 1.00 150000000.00
12 | src10 25.670235 -31.675179 1.00 150000000.00
13 | src11 23.321217 -31.761294 1.00 150000000.00
14 | src12 18.616585 -31.806457 1.00 150000000.00
15 | src13 17.440155 -31.791342 1.00 150000000.00
16 | src14 13.915314 -31.682104 1.00 150000000.00
17 | src15 11.571614 -31.556511 1.00 150000000.00
18 | src16 27.927165 -30.552168 1.00 150000000.00
19 | src17 26.768313 -30.620199 1.00 150000000.00
20 | src18 25.607956 -30.677942 1.00 150000000.00
21 | src19 23.283697 -30.762426 1.00 150000000.00
22 | src20 22.120290 -30.789115 1.00 150000000.00
23 | src21 18.627961 -30.806786 1.00 150000000.00
24 | src22 17.463988 -30.791865 1.00 150000000.00
25 | src23 16.300503 -30.766550 1.00 150000000.00
26 | src24 13.975997 -30.684803 1.00 150000000.00
27 | src25 12.815469 -30.628424 1.00 150000000.00
28 | src26 11.656418 -30.561752 1.00 150000000.00
29 | src27 27.844011 -29.557348 1.00 150000000.00
30 | src28 25.548066 -29.680626 1.00 150000000.00
31 | src29 23.247039 -29.763546 1.00 150000000.00
32 | src30 18.639216 -29.807115 1.00 150000000.00
33 | src31 16.335582 -29.767633 1.00 150000000.00
34 | src32 14.034319 -29.687427 1.00 150000000.00
35 | src33 11.738024 -29.566848 1.00 150000000.00
36 | src34 27.764133 -28.562410 1.00 150000000.00
37 | src35 25.490401 -28.683338 1.00 150000000.00
38 | src36 23.212815 -28.764195 1.00 150000000.00
39 | src37 22.071845 -28.790420 1.00 150000000.00
40 | src38 20.931473 -28.805845 1.00 150000000.00
41 | src39 18.649710 -28.807197 1.00 150000000.00
42 | src40 17.509296 -28.793122 1.00 150000000.00
43 | src41 16.368255 -28.768247 1.00 150000000.00
44 | src42 12.952166 -28.635639 1.00 150000000.00
45 |
--------------------------------------------------------------------------------
/docs/make_skymodel.py:
--------------------------------------------------------------------------------
1 | """Format the SkyModel object parameters into a sphinx rst file."""
2 |
3 | import inspect
4 | import os
5 |
6 | from astropy.time import Time
7 |
8 | from pyradiosky import SkyModel
9 |
10 |
11 | def write_skymodel_rst(write_file=None):
12 | UV = SkyModel()
13 | out = "SkyModel\n========\n"
14 | out += (
15 | "SkyModel is the main user class for point source and diffuse models.\n"
16 | "It provides import and export functionality to and from supported file\n"
17 | "formats as well as methods for transforming the data (combining, selecting,\n"
18 | "changing coordinate frames) and can be interacted with directly.\n\n"
19 | "Attributes\n----------\n"
20 | "The attributes on SkyModel hold all of the metadata and data required to\n"
21 | "work with radio sky models. Under the hood, the attributes are\n"
22 | "implemented as properties based on :class:`pyuvdata.parameter.UVParameter`\n"
23 | "objects but this is fairly transparent to users.\n\n"
24 | "SkyModel objects can be initialized from a file using the\n"
25 | ":meth:`pyradiosky.SkyModel.from_file` class method\n"
26 | "(as ``sky = SkyModel.from_file()``) or be initialized by passing\n"
27 | "in all the information to the constructor. SkyModel objects can also be\n"
28 | "initialized as an empty object (as ``sky = SkyModel()``). When an empty\n"
29 | "SkyModel object is initialized, it has all of these attributes defined but\n"
30 | "set to ``None``. The attributes can be set by reading in a data file using\n"
31 | "the :meth:`pyradiosky.SkyModel.read` method or by setting them directly on\n"
32 | "the object. Some of these attributes are `required`_ to be set to have a\n"
33 | "fully defined data set while others are `optional`_. The\n"
34 | ":meth:`pyradiosky.SkyModel.check` method can be called on the object to\n"
35 | "verify that all of the required attributes have been set in a consistent\n"
36 | "way.\n\n"
37 | )
38 | out += "Required\n********\n"
39 | out += (
40 | "These parameters are required to have a sensible SkyModel object and \n"
41 | "are required for most kinds of catalog files."
42 | )
43 | out += "\n\n"
44 | for thing in UV.required():
45 | obj = getattr(UV, thing)
46 | out += f"**{obj.name}**\n"
47 | out += f" {obj.description}\n"
48 | out += "\n"
49 |
50 | out += "Optional\n********\n"
51 | out += (
52 | "These parameters are defined by one or more file standard but are not "
53 | "always required.\nSome of them are required depending on the "
54 | "spectral_type or component_type (as noted below)."
55 | )
56 | out += "\n\n"
57 | for thing in UV.extra():
58 | obj = getattr(UV, thing)
59 | out += f"**{obj.name}**\n"
60 | out += f" {obj.description}\n"
61 | out += "\n"
62 |
63 | out += "Methods\n-------\n.. autoclass:: pyradiosky.SkyModel\n :members:\n\n"
64 |
65 | t = Time.now()
66 | t.format = "iso"
67 | t.out_subfmt = "date"
68 | out += f"last updated: {t.iso}"
69 | if write_file is None:
70 | write_path = os.path.dirname(os.path.abspath(inspect.stack()[0][1]))
71 | write_file = os.path.join(write_path, "skymodel.rst")
72 | with open(write_file, "w") as F:
73 | F.write(out)
74 | print("wrote " + write_file)
75 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=64", "wheel", "setuptools_scm>=8.1"]
3 | build-backend = "setuptools.build_meta"
4 |
5 | [project]
6 | name = "pyradiosky"
7 | authors = [
8 | {name = "Bryna Hazelton", email = "brynah@phys.washington.edu"},
9 | {name = "Matthew Kolopanis", email = "mjkolopa@asu.edu"},
10 | {name = "Jonathan Pober", email = "jonathan_pober@brown.edu"},
11 | {name = "Adam Lanman", email = "alanman@mit.edu"},
12 | ]
13 | maintainers = [
14 | {name = "Bryna Hazelton", email = "brynah@phys.washington.edu"},
15 | {name = "Matthew Kolopanis", email = "mjkolopa@asu.edu"}
16 | ]
17 | description = "Python objects and interfaces for representing diffuse, extended and compact astrophysical radio sources"
18 | readme = "README.md"
19 | dynamic = ["version"]
20 | dependencies = [
21 | "astropy>=6.0",
22 | "h5py>=3.7",
23 | "numpy>=1.23",
24 | "pyuvdata>=3.2.3",
25 | "scipy>=1.9",
26 | "setuptools>=64",
27 | "setuptools_scm>=8.1",
28 | ]
29 | requires-python = ">=3.11"
30 | keywords = ["radio astronomy"]
31 | classifiers = [
32 | "License :: OSI Approved :: BSD License",
33 | "Development Status :: 5 - Production/Stable",
34 | "Intended Audience :: Science/Research",
35 | "License :: OSI Approved :: BSD License",
36 | "Programming Language :: Python :: 3.11",
37 | "Programming Language :: Python :: 3.12",
38 | "Programming Language :: Python :: 3.13",
39 | "Topic :: Scientific/Engineering :: Astronomy",
40 | ]
41 |
42 | [project.optional-dependencies]
43 | healpix = ["astropy-healpix>=1.0.2"]
44 | astroquery = ["astroquery>=0.4.4"]
45 | lunarsky = ["lunarsky>=0.2.5"]
46 | all = ["pyradiosky[healpix,astroquery,lunarsky]"]
47 | test = ["coverage", "pre-commit", "pytest", "pytest-cov"]
48 | doc = ["matplotlib", "pypandoc", "sphinx"]
49 | dev = ["pyradiosky[all,test,doc]"]
50 |
51 | [project.urls]
52 | Repository = "https://github.com/RadioAstronomySoftwareGroup/pyradiosky"
53 | Documentation = "https://pyradiosky.readthedocs.io/"
54 |
55 | [project.scripts]
56 | download_gleam = "pyradiosky.cli:download_gleam"
57 | make_flat_spectrum_eor = "pyradiosky.cli:make_flat_spectrum_eor"
58 |
59 | [tool.setuptools_scm]
60 |
61 | [tool.ruff.lint]
62 | select = [
63 | "E", # pycodestyle
64 | "W", # pycodestyle warnings
65 | "F", # Pyflakes
66 | "D", # pydocstyle
67 | "UP", # pyupgrade
68 | "B", # flake8-bugbear
69 | "A", # flake8-builtins
70 | "C4", # flake8-comprehensions
71 | "N", # pep8-naming
72 | "SIM", # flake8-simplify
73 | "I", # isort
74 | # "C90", # McCabe complexity. Consider for the future
75 | ]
76 | ignore = [
77 | "N806", # non-lowercase variable (we use N* for axes lengths)
78 | "B028", # no-explicit-stacklevel for warnings
79 | "SIM108", # prefer ternary opperators. I find them difficult to read.
80 | "D203", # one-blank-line-before-class. we use two.
81 | "D212", # multi-line-summary-first-line. We put it on the second line.
82 | ]
83 |
84 | [tool.ruff.lint.per-file-ignores]
85 | "tests/*" = ["D"] # Don't require docstrings for tests
86 | "docs/*.py" = ["D", "A"] # Don't require docstrings or worry about builtins for docs
87 | "setup.py" = ["D"] # Don't require docstrings for setup.py
88 |
89 | [tool.ruff.format]
90 | skip-magic-trailing-comma = true
91 |
92 | [tool.ruff.lint.pycodestyle]
93 | max-line-length = 88
94 |
95 | # consider setting this in the future
96 | # [tool.ruff.lint.mccabe]
97 | # max-complexity = 30
98 |
99 | [tool.ruff.lint.isort]
100 | combine-as-imports = true
101 | known-first-party = ["pyradiosky"]
102 | split-on-trailing-comma = false
103 |
104 | [tool.ruff.lint.pydocstyle]
105 | convention = "numpy"
106 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the RASG managers at [rasgmanagers@gmail.com](mailto:rasgmanagers@gmail.com).
59 | The list of managers who will receive the email is in the [readme](README.md).
60 | To report an issue involving any of the managers, you can email any subset of the managers directly.
61 |
62 | All complaints will be reviewed and investigated and will result in a response that
63 | is deemed necessary and appropriate to the circumstances. The RASG managers are
64 | obligated to maintain confidentiality with regard to the reporter of an incident.
65 |
66 | Project maintainers or contributors who do not follow or enforce the Code of
67 | Conduct in good faith may be temporary or permanently removed from the project
68 | team or blocked from RASG repositories, Slack channels or telecons.
69 |
70 | ## Attribution
71 |
72 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
73 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
74 |
75 | [homepage]: https://www.contributor-covenant.org
76 |
77 | For answers to common questions about this code of conduct, see
78 | https://www.contributor-covenant.org/faq
79 |
--------------------------------------------------------------------------------
/paper.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'pyradiosky: A Python package for Radio Sky Models'
3 | tags:
4 | - Python
5 | - astronomy
6 | - radio astronomy
7 | - 21cm cosmology
8 | authors:
9 | - name: Bryna Hazelton
10 | orcid: 0000-0001-7532-645X
11 | equal-contrib: true
12 | affiliation: "1, 2"
13 | - name: Matthew Kolopanis
14 | orcid: 0000-0002-2950-2974
15 | equal-contrib: true
16 | affiliation: 3
17 | - name: Adam Lanman
18 | orcid: 0000-0003-2116-3573
19 | equal-contrib: true
20 | affiliation: 4
21 | - name: Jonathan Pober
22 | orcid: 0000-0002-3492-0433
23 | equal-contrib: true
24 | affiliation: 5
25 | affiliations:
26 | - name: Physics Department, University of Washington, USA
27 | index: 1
28 | - name: eScience Institute University of Washington, USA
29 | index: 2
30 | - name: School of Earth and Space Exploration, Arizona State University, USA
31 | index: 3
32 | - name: Kavli Institute of Astrophysics and Space Research, Massachusetts Institute of Technology, USA
33 | index: 4
34 | - name: Department of Physics, Brown University, USA
35 | index: 5
36 | date: 8 February 2024
37 | bibliography: paper.bib
38 | ---
39 |
40 | # Summary
41 |
42 | Pyradiosky is a package to fully and generally describe models of compact,
43 | extended and diffuse radio sources with full polarization support. It is designed
44 | to support simulations and calibrations of radio interferometry data.
45 | It emphasizes the provenance and metadata required to understand the errors and
46 | covariances associated with sky models built from interferometric data.
47 |
48 | # Statement of need
49 |
50 | The original motivation for the development of pyradiosky was to support
51 | high-precision simulation of interferometric data for 21 cm cosmology, but the
52 | package was deliberately developed to support a broad range of radio astronomy
53 | applications. Sky models are key to the future of 21 cm analyses because
54 | high-precision foreground subtraction has the potential to dramatically increase
55 | the sensitivity of 21 cm instruments. High-quality sky models are also extremely
56 | important for calibration of all radio astronomy data.
57 |
58 | Pyradiosky supports reading in catalogs in the widely used VOTable format as
59 | well as an HDF5 based format we developed. It also supports some formats used by
60 | 21 cm cosmology codes and is easily extensible to other formats. It provides an
61 | object interface and useful methods for downselecting and combining multiple
62 | catalogs, coordinate transformations (with polarization support) and calculating
63 | fluxes at specific frequencies. Pyradiosky uses astropy [@astropy:2013;
64 | @astropy:2018; @astropy:2022] for most coordinate transforms and for VOTable
65 | support, it also interfaces with the lunarsky [@lunarsky] package to support
66 | moon-based coordinate systems. Pyradiosky uses astropy-healpix [@astropy-healpix]
67 | for interfacing with HEALPix [@healpix] maps.
68 |
69 | Pyradiosky is different than other commonly used code to handle catalogs, like
70 | TOPCAT [@topcat] and astropy's VOTable sub-package, which are primarily table
71 | interfaces that support table operations but not the more complicated
72 | astronomy-aware operations (e.g. polarization coordinate transformations)
73 | supported by pyradiosky. Pyradiosky also provides a unified interface for
74 | catalogs and HEALPix sky maps.
75 |
76 | As part of the Radio Astronomy Software Group suite, along with pyuvdata
77 | [@pyuvdata2017] and pyuvsim [@pyuvsim2019], pyradiosky provides software
78 | infrastructure for a broad range of radio astronomy applications including
79 | enabling rigorous, seamless testing of 21 cm cosmology analysis developments.
80 |
81 | # Acknowledgements
82 |
83 | Support for pyradiosky was provided by NSF awards #1835421 and #1835120.
84 |
85 | # References
86 |
--------------------------------------------------------------------------------
/src/pyradiosky/cli.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2025 Radio Astronomy Software Group
2 | # Licensed under the 2-clause BSD License
3 | """Command line scripts."""
4 |
5 | import argparse
6 |
7 | import numpy as np
8 |
9 | import pyradiosky.utils as utils
10 |
11 |
12 | def download_gleam(argv=None):
13 | """Download the GLEAM vot table from Vizier."""
14 | parser = argparse.ArgumentParser(
15 | description="A command-line script to download the GLEAM vot table from Vizier."
16 | )
17 | parser.add_argument(
18 | "--path", type=str, help="Folder location to save catalog to.", default="."
19 | )
20 | parser.add_argument(
21 | "--filename", type=str, help="Filename to save catalog to.", default="gleam.vot"
22 | )
23 | parser.add_argument(
24 | "--overwrite",
25 | help="Download file even if it already exists",
26 | default=False,
27 | action="store_true",
28 | )
29 | parser.add_argument(
30 | "--row_limit",
31 | type=int,
32 | help="Max number of rows (sources) to download, default is to download "
33 | "all rows.",
34 | default=None,
35 | )
36 | parser.add_argument(
37 | "--for_testing",
38 | help="Download a file to use for unit tests. If True, all columns that "
39 | "are ever used are included (e.g. for different spectral types and errors), "
40 | "the rows are limited to 50, the path and filename are set to put the "
41 | "file in the correct location for the unit tests and the overwrite keyword "
42 | "is set to True.",
43 | default=False,
44 | action="store_true",
45 | )
46 |
47 | args = parser.parse_args(argv)
48 |
49 | utils.download_gleam(
50 | path=args.path,
51 | filename=args.filename,
52 | overwrite=args.overwrite,
53 | row_limit=args.row_limit,
54 | for_testing=args.for_testing,
55 | )
56 |
57 |
58 | def make_flat_spectrum_eor(argv=None):
59 | """Generate a noise-like SkyModel with a flat P(k) cosmological power spectrum."""
60 | parser = argparse.ArgumentParser(
61 | description="A command-line script to generate a noise-like EoR SkyModel "
62 | "with a flat P(k) cosmological power spectrum"
63 | )
64 | parser.add_argument(
65 | "-v",
66 | "--variance",
67 | type=float,
68 | required=True,
69 | help="Variance of the signal, in Kelvin^2, at the reference channel.",
70 | )
71 | parser.add_argument(
72 | "--nside",
73 | type=int,
74 | required=True,
75 | help="HEALPix NSIDE parameter. Must be a power of 2.",
76 | )
77 | parser.add_argument(
78 | "--ref_chan",
79 | type=int,
80 | default=0,
81 | help="Frequency channel to set as reference. Defaults to 0, meaning the "
82 | "start frequency.",
83 | )
84 | parser.add_argument(
85 | "-s",
86 | "--start_freq",
87 | type=float,
88 | required=True,
89 | help="Start frequency (in Hz), used to set up the frequency array as "
90 | "freq_array = np.linspace(start_freq, end_freq, Nfreqs).",
91 | )
92 | parser.add_argument(
93 | "-e",
94 | "--end_freq",
95 | type=float,
96 | required=True,
97 | help="End frequency (in Hz), used to set up the frequency array as "
98 | "freq_array = np.linspace(start_freq, end_freq, Nfreqs) (will be the "
99 | "last included frequency).",
100 | )
101 | parser.add_argument(
102 | "-N",
103 | "--nfreqs",
104 | type=int,
105 | required=True,
106 | help="Number of frequencies, used to set up the frequency array as "
107 | "freq_array = np.linspace(start_freq, end_freq, Nfreqs).",
108 | )
109 | parser.add_argument(
110 | "--filename", type=str, help="Output file name", default="noise_sky.hdf5"
111 | )
112 | parser.add_argument(
113 | "--frame",
114 | type=str,
115 | help="Astropy Frame for output SkyModel, default ICRS",
116 | default="icrs",
117 | )
118 |
119 | args = parser.parse_args(argv)
120 |
121 | variance = args.variance
122 | nside = args.nside
123 | frame = args.frame
124 |
125 | filename = args.filename
126 | start_freq = args.start_freq
127 | end_freq = args.end_freq
128 | Nfreqs = args.nfreqs
129 |
130 | freq_array = np.linspace(start_freq, end_freq, Nfreqs)
131 |
132 | print(
133 | f"Generating sky model, nside {nside},"
134 | f" and variance {variance} K^2 at channel {args.ref_chan}."
135 | )
136 |
137 | sky = utils.flat_spectrum_skymodel(
138 | variance=variance,
139 | nside=nside,
140 | freqs=freq_array,
141 | ref_chan=args.ref_chan,
142 | frame=frame,
143 | )
144 | sky.check()
145 | print(sky.history)
146 | print(f"Saving to {filename}.")
147 | sky.write_skyh5(filename)
148 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 | ## Motivation and Context
7 |
8 |
9 |
10 | ## Checklists:
11 |
12 |
13 | ### Bug Fix Checklist:
14 |
15 |
16 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
17 | - [ ] My code follows the code style of this project.
18 | - [ ] My fix includes a new test that breaks as a result of the bug (if possible).
19 | - [ ] My change includes a breaking change
20 | - [ ] My change includes backwards compatibility and deprecation warnings (if possible).
21 | - [ ] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/CHANGELOG.md).
22 |
23 | ### New Feature Checklist:
24 |
25 |
26 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
27 | - [ ] My code follows the code style of this project.
28 | - [ ] I have added or updated the docstrings associated with my feature using the [numpy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html).
29 | - [ ] I have updated the tutorial to highlight my new feature (if appropriate).
30 | - [ ] I have added tests to cover my new feature.
31 | - [ ] My change includes a breaking change
32 | - [ ] My change includes backwards compatibility and deprecation warnings (if possible).
33 | - [ ] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/CHANGELOG.md).
34 |
35 | ### Documentation Change Checklist:
36 |
37 |
38 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
39 | - [ ] My code follows the code style of this project.
40 | Documentation change checklist:
41 | - [ ] Any updated docstrings use the [numpy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html).
42 |
43 |
44 | ### Build/CI Change Checklist:
45 |
46 |
47 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
48 | - [ ] My code follows the code style of this project.
49 | - [ ] If required or optional dependencies have changed (including version numbers), I have updated the readme to reflect this.
50 | - [ ] If this is a new CI setup, I have added the associated badge to the readme
51 |
52 | ### Version Change Checklist
53 |
54 |
55 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
56 | - [ ] My code follows the code style of this project.
57 | - [ ] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/CHANGELOG.md) to put all the unreleased changes under the new version (leaving the unreleased section empty).
58 |
59 | ### Other Change Checklist
60 |
61 |
62 | - [ ] I have read the [contribution guide](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/.github/CONTRIBUTING.md).
63 | - [ ] My code follows the code style of this project.
64 | - [ ] Any new or updated docstrings use the [numpy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html).
65 | - [ ] I have updated the tutorial to highlight my new feature (if appropriate).
66 | - [ ] I have added tests to cover any changes.
67 | - [ ] My change includes a breaking change
68 | - [ ] My change includes backwards compatibility and deprecation warnings (if possible).
69 | - [ ] I have updated the [CHANGELOG](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/CHANGELOG.md) if appropriate.
70 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | #
2 | # Configuration file for the Sphinx documentation builder.
3 | #
4 | # This file only contains a selection of the most common options. For a full
5 | # list see the documentation:
6 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
7 |
8 | import os
9 | import sys
10 | from io import StringIO
11 |
12 | from docutils import nodes, statemachine
13 | from sphinx.util.docutils import SphinxDirective
14 |
15 | import pyradiosky
16 |
17 | # If extensions (or modules to document with autodoc) are in another directory,
18 | # add these directories to sys.path here. If the directory is relative to the
19 | # documentation root, use os.path.abspath to make it absolute, like shown here.
20 | sys.path.insert(0, os.path.abspath("../pyradiosky/"))
21 | readme_file = os.path.join(os.path.abspath("../"), "README.md")
22 | index_file = os.path.join(os.path.abspath("../docs"), "index.rst")
23 | skymodel_file = os.path.join(os.path.abspath("../docs"), "skymodel.rst")
24 |
25 |
26 | # -- Path setup --------------------------------------------------------------
27 |
28 | # If extensions (or modules to document with autodoc) are in another directory,
29 | # add these directories to sys.path here. If the directory is relative to the
30 | # documentation root, use os.path.abspath to make it absolute, like shown here.
31 | #
32 | # import os
33 | # import sys
34 | # sys.path.insert(0, os.path.abspath('.'))
35 |
36 |
37 | # -- Project information -----------------------------------------------------
38 |
39 | project = 'pyradiosky'
40 | copyright = '2019, Radio Astronomy Software Group'
41 | author = 'Radio Astronomy Software Group'
42 |
43 | # The full version, including alpha/beta/rc tags
44 | version = pyradiosky.__version__
45 | release = version
46 |
47 |
48 | # -- General configuration ---------------------------------------------------
49 |
50 | # Add any Sphinx extension module names here, as strings. They can be
51 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
52 | # ones.
53 | extensions = [
54 | "sphinx.ext.autodoc",
55 | "sphinx.ext.doctest",
56 | "sphinx.ext.todo",
57 | "sphinx.ext.coverage",
58 | "sphinx.ext.viewcode",
59 | "sphinx.ext.napoleon",
60 | "sphinx.ext.intersphinx",
61 | "sphinx_rtd_theme",
62 | ]
63 | # set this to properly handle multiple input params with the same type/shape
64 | napoleon_use_param = False
65 |
66 | # set this to handle returns section more uniformly
67 | napoleon_use_rtype = False
68 |
69 | # use this to create custom sections
70 | # currently used for the SkyModel.read method
71 | napoleon_custom_sections = [
72 | ("GLEAM", "params_style"),
73 | ("FHD", "params_style"),
74 | ("VOTable", "params_style"),
75 | ("SkyH5", "params_style")
76 | ]
77 |
78 | # turn off alphabetical ordering in autodoc
79 | autodoc_member_order = "bysource"
80 |
81 | # Add any paths that contain templates here, relative to this directory.
82 | templates_path = ['_templates']
83 |
84 | # List of patterns, relative to source directory, that match files and
85 | # directories to ignore when looking for source files.
86 | # This pattern also affects html_static_path and html_extra_path.
87 | exclude_patterns = ['build', 'Thumbs.db', '.DS_Store']
88 |
89 |
90 | # -- Options for HTML output -------------------------------------------------
91 |
92 | # The theme to use for HTML and HTML Help pages. See the documentation for
93 | # a list of builtin themes.
94 | html_theme = "sphinx_rtd_theme"
95 |
96 | # Add any paths that contain custom static files (such as style sheets) here,
97 | # relative to this directory. They are copied after the builtin static files,
98 | # so a file named "default.css" will overwrite the builtin "default.css".
99 | html_static_path = ['_static']
100 |
101 |
102 | def build_custom_docs(app):
103 | sys.path.append(os.getcwd())
104 | import make_index
105 | import make_skymodel
106 |
107 | make_index.write_index_rst(readme_file=readme_file, write_file=index_file)
108 | make_skymodel.write_skymodel_rst(write_file=skymodel_file)
109 |
110 |
111 | # this is to enable running python in the rst files.
112 | # first use case is to better format the KNOWN_TELESCOPES dict
113 | class ExecDirective(SphinxDirective):
114 | """Execute the specified python code and insert the output into the document"""
115 |
116 | has_content = True
117 |
118 | def run(self):
119 | oldStdout, sys.stdout = sys.stdout, StringIO()
120 |
121 | tab_width = self.options.get(
122 | "tab-width", self.state.document.settings.tab_width
123 | )
124 | source = self.state_machine.input_lines.source(
125 | self.lineno - self.state_machine.input_offset - 1
126 | )
127 |
128 | try:
129 | exec("\n".join(self.content))
130 | text = sys.stdout.getvalue()
131 | lines = statemachine.string2lines(text, tab_width, convert_whitespace=True)
132 | self.state_machine.insert_input(lines, source)
133 | return []
134 | except Exception:
135 | return [
136 | nodes.error(
137 | None,
138 | nodes.paragraph(
139 | text="Unable to execute python "
140 | f"code at {os.path.basename(source)}:{self.lineno}"
141 | ),
142 | nodes.paragraph(text=str(sys.exc_info()[1])),
143 | )
144 | ]
145 | finally:
146 | sys.stdout = oldStdout
147 |
148 |
149 | def setup(app):
150 | app.connect("builder-inited", build_custom_docs)
151 | app.add_directive("exec", ExecDirective)
152 |
153 | # -- Options for intersphinx extension ---------------------------------------
154 |
155 |
156 | # Example configuration for intersphinx: refer to the Python standard library.
157 | intersphinx_mapping = {
158 | "python": ("https://docs.python.org/3/", None),
159 | "pyuvdata": ("https://pyuvdata.readthedocs.io/en/latest/", None),
160 | "astropy": ("https://docs.astropy.org/en/stable/", None),
161 | "astropy_healpix": ("https://astropy-healpix.readthedocs.io/en/latest/", None),
162 | "numpy": ("https://numpy.org/doc/stable/", None),
163 | # "lunarsky": (None), lunarsky is not on RTD yet. Add it here when it is.
164 | }
165 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to pyradiosky
2 |
3 | Thank you for considering contributing to pyradiosky! It's our community of
4 | users and contributors that makes pyradiosky powerful and relevant.
5 |
6 | pyradiosky is an open source project, driven by our community of contributors.
7 | There are many ways to contribute, including writing tutorials, improving the
8 | documentation, submitting bug reports and feature requests or writing code
9 | which can be incorporated into pyradiosky itself. Following the guidelines and
10 | patterns suggested below helps us maintain a high quality code base and review
11 | your contributions faster.
12 |
13 | Please note we have a [code of conduct](../CODE_OF_CONDUCT.md), please follow
14 | it in all your interactions with the project.
15 |
16 | ## How to report a bug
17 | First check the issues to see if your bug already exists. Feel free to comment
18 | on the existing issue to provide more context or just to note that it is
19 | affecting you as well. If your bug is not in the issue list, make a new issue.
20 |
21 | When making an issue, try to provide as much context as possible including:
22 |
23 | 1. What version of python and pyradiosky are you using?
24 | 2. What operating system are you using?
25 | 3. What did you do?
26 | 4. What did you expect to see?
27 | 5. What did you see instead?
28 | 6. Any code to reproduce the bug (as minimal an example as possible)
29 |
30 | If you're really inspired, you can make a pull request adding a test that fails
31 | because of the bug. This is likely to lead to the bug being fixed more quickly.
32 |
33 | ## How to suggest a feature or enhancement
34 | First check the issues to see if your feature request already exists. Feel
35 | free to comment on the existing issue to provide more context or just to note
36 | that you would like to see the feature implemented as well. If your feature
37 | request is not in the issue list, make a new issue.
38 |
39 | When making a feature request, try to provide as much context as possible.
40 | Feel free to include suggestions for implementations.
41 |
42 | ## Guidelines for contributing to the code
43 |
44 | * Create issues for any major changes and enhancements that you wish to make.
45 | Discuss things transparently and get community feedback.
46 | * Keep pull requests as small as possible. Ideally each pull request should
47 | implement ONE feature or bugfix. If you want to add or fix more than one thing,
48 | submit more than one pull request.
49 | * Do not commit changes to files that are irrelevant to your feature or bugfix.
50 | * Be aware that the pull request review process is not immediate, and is
51 | generally proportional to the size of the pull request.
52 | * Be welcoming to newcomers and encourage diverse new contributors from all
53 | backgrounds. See our [Code of Conduct](../CODE_OF_CONDUCT.md).
54 |
55 | ### Your First Contribution
56 |
57 | Contributing for the first time can seem daunting, but we value contributions
58 | from our user community and we will do our best to help you through the process.
59 | Here’s some advice to help make your work on pyradiosky more useful and rewarding.
60 |
61 | * Use issue labels to guide you
62 | - Unsure where to begin contributing to pyradiosky?
63 | You can start by looking through issues labeled `good first issue` and
64 | `help wanted` issues.
65 |
66 | * Pick a subject area that you care about, that you are familiar with, or that
67 | you want to learn about
68 | - Some of the feature request issues involve file formats that the core
69 | developers may not be very familiar with. If you know about those formats,
70 | consider helping on those issues.
71 |
72 | * Start small
73 | - It’s easier to get feedback on a little issue or pull request than on a big one.
74 |
75 | * If you’re going to take on a big change, make sure that your idea has support first
76 | - This means getting someone else to confirm that a bug is real before you
77 | fix the issue, and ensuring that there’s consensus on a proposed feature
78 | before you work to implement it. Use the issue log to start conversations
79 | about major changes and enhancements.
80 |
81 | * Be bold! Leave feedback!
82 | - Sometimes it can be scary to make new issues or comment on existing issues
83 | or pull requests, but contributions from the wider community are what ensure
84 | that pyradiosky serves the whole community as well as possible.
85 |
86 | * Be rigorous
87 | - Our requirements on code style, testing and documentation are important.
88 | If you have questions about them or difficulty meeting them, please ask for
89 | help, we will do our best to support you. Your contributions will be
90 | reviewed and integrated much more quickly if your pull request meets the
91 | requirements.
92 |
93 | If you are new to the GitHub or the pull request process you can start by
94 | taking a look at these tutorials:
95 | http://makeapullrequest.com/ and http://www.firsttimersonly.com/. If you have
96 | more questions, feel free to ask for help, everyone is a beginner at first and
97 | all of us are still learning!
98 |
99 | ### Getting started
100 |
101 | 1. Create your own fork or branch of the code
102 | 2. Do the changes in your fork or branch
103 | 3. If you like the change and think the project could use it:
104 | - If you're fixing a bug, include a new test that breaks as a result of the bug (if possible)
105 | - Ensure that all your new code is covered by tests and that the existing
106 | tests pass. Tests can be run and coverage automatically created by running
107 | `pytest` in the top level directory. Coverage reports require the `pytest-cov`
108 | plug-in.
109 | - Ensure that your code meets the PEP8 style guidelines. You can check that
110 | your code will pass our linting tests by installing the `pre-commit` package
111 | or by running `pre-commit run -a` in the top-level pyradiosky directory.
112 | - Ensure that you fully document any new features via docstrings.
113 | - You can see the full pull request checklist [here](PULL_REQUEST_TEMPLATE.md)
114 |
115 | ### Code review process
116 |
117 | The core team looks at pull requests on a regular basis and tries to provide
118 | feedback as quickly as possible. Larger pull requests generally require more
119 | time for review and may require discussion among the core team.
120 |
121 | # Community
122 | In addition to conversations on github, we also communicate via Slack and on
123 | biweekly telecons. We welcome contributors who want to join those discussions,
124 | [contact the RASG managers](mailto:rasgmanagers@gmail.com) if you'd like to be
125 | invited to join them.
126 |
--------------------------------------------------------------------------------
/tests/test_spherical_coords_transforms.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019 Radio Astronomy Software Group
2 | # Licensed under the 2-clause BSD License
3 |
4 | import numpy as np
5 | import pytest
6 | from astropy import units
7 | from astropy.coordinates import EarthLocation, SkyCoord
8 | from astropy.time import Time
9 | from scipy.linalg import orthogonal_procrustes as ortho_procr
10 |
11 | from pyradiosky import spherical_coords_transforms as sct
12 |
13 |
14 | @pytest.mark.parametrize("func_name", ["r_hat", "theta_hat", "phi_hat"])
15 | def test_hat_errors(func_name):
16 | with pytest.raises(ValueError) as cm:
17 | getattr(sct, func_name)([0, 0], [0])
18 | assert str(cm.value).startswith("theta and phi must have the same shape")
19 |
20 |
21 | def test_rotate_points_3d():
22 | array_location = EarthLocation(lat="-30d43m17.5s", lon="21d25m41.9s", height=1073.0)
23 | time0 = Time("2018-03-01 18:00:00", scale="utc", location=array_location)
24 |
25 | ha_off = 0.5
26 | ha_delta = 0.01
27 | time_offsets = np.arange(-ha_off, ha_off + ha_delta, ha_delta)
28 | zero_indx = np.argmin(np.abs(time_offsets))
29 | # make sure we get a true zenith time
30 | time_offsets[zero_indx] = 0.0
31 | times = time0 + time_offsets * units.hr
32 | ntimes = times.size
33 |
34 | zenith = SkyCoord(
35 | alt=90.0 * units.deg,
36 | az=0 * units.deg,
37 | frame="altaz",
38 | obstime=time0,
39 | location=array_location,
40 | )
41 | zenith_icrs = zenith.transform_to("icrs")
42 |
43 | src_astropy = SkyCoord(
44 | ra=zenith_icrs.ra, dec=zenith_icrs.dec, obstime=times, location=array_location
45 | )
46 | src_astropy_altaz = src_astropy.transform_to("altaz")
47 | assert np.isclose(src_astropy_altaz.alt.rad[zero_indx], np.pi / 2)
48 |
49 | alts = np.zeros(ntimes)
50 | azs = np.zeros(ntimes)
51 | for ti, time in enumerate(times):
52 | alts[ti] = src_astropy_altaz[ti].alt.radian
53 | azs[ti] = src_astropy_altaz[ti].az.radian
54 |
55 | # unit vectors to be transformed by astropy
56 | x_c = np.array([1.0, 0, 0])
57 | y_c = np.array([0, 1.0, 0])
58 | z_c = np.array([0, 0, 1.0])
59 |
60 | # astropy 2 vs 3 use a different keyword name
61 | rep_keyword = "representation_type"
62 |
63 | rep_dict = {}
64 | rep_dict[rep_keyword] = "cartesian"
65 | axes_icrs = SkyCoord(
66 | x=x_c,
67 | y=y_c,
68 | z=z_c,
69 | obstime=time,
70 | location=array_location,
71 | frame="icrs",
72 | **rep_dict,
73 | )
74 |
75 | axes_altaz = axes_icrs.transform_to("altaz")
76 | setattr(axes_altaz, rep_keyword, "cartesian")
77 |
78 | """ This transformation matrix is generally not orthogonal
79 | to better than 10^-7, so let's fix that. """
80 |
81 | R_screwy = axes_altaz.cartesian.xyz
82 | R_avg, _ = ortho_procr(R_screwy, np.eye(3))
83 |
84 | # Note the transpose, to be consistent with calculation in sct
85 | R_avg = np.array(R_avg).T
86 |
87 | # Find mathematical points and vectors for RA/Dec
88 | theta_radec = np.pi / 2.0 - src_astropy.dec.radian
89 | phi_radec = src_astropy.ra.radian
90 | radec_vec = sct.r_hat(theta_radec, phi_radec)
91 | assert radec_vec.shape == (3,)
92 |
93 | # Find mathematical points and vectors for Alt/Az
94 | theta_altaz = np.pi / 2.0 - alts[ti]
95 | phi_altaz = azs[ti]
96 | altaz_vec = sct.r_hat(theta_altaz, phi_altaz)
97 | assert altaz_vec.shape == (3,)
98 |
99 | intermediate_vec = np.matmul(R_avg, radec_vec)
100 |
101 | R_perturb = sct.vecs2rot(r1=intermediate_vec, r2=altaz_vec)
102 |
103 | intermediate_theta, intermediate_phi = sct.rotate_points_3d(
104 | R_avg, theta_radec, phi_radec
105 | )
106 | R_perturb_pts = sct.vecs2rot(
107 | theta1=intermediate_theta,
108 | phi1=intermediate_phi,
109 | theta2=theta_altaz,
110 | phi2=phi_altaz,
111 | )
112 |
113 | assert np.allclose(R_perturb, R_perturb_pts)
114 |
115 | R_exact = np.matmul(R_perturb, R_avg)
116 |
117 | calc_theta_altaz, calc_phi_altaz = sct.rotate_points_3d(
118 | R_exact, theta_radec, phi_radec
119 | )
120 |
121 | if ti == zero_indx:
122 | assert np.isclose(calc_theta_altaz, theta_altaz, atol=1e-7)
123 | else:
124 | assert np.isclose(calc_theta_altaz, theta_altaz)
125 |
126 | if ti == zero_indx:
127 | assert np.isclose(calc_phi_altaz, phi_altaz, atol=2e-4)
128 | else:
129 | assert np.isclose(calc_phi_altaz, phi_altaz)
130 |
131 | v1 = sct.r_hat(theta_radec, phi_radec)
132 | v2 = sct.r_hat(theta_altaz, phi_altaz)
133 | Rv1 = np.matmul(R_exact, v1)
134 | assert np.allclose(Rv1, v2)
135 |
136 | coherency_rot_matrix_two_pt = sct.spherical_basis_vector_rotation_matrix(
137 | theta_radec, phi_radec, R_exact, theta_altaz, phi_altaz
138 | )
139 |
140 | coherency_rot_matrix_one_pt = sct.spherical_basis_vector_rotation_matrix(
141 | theta_radec, phi_radec, R_exact
142 | )
143 |
144 | if ti == zero_indx:
145 | assert np.allclose(
146 | coherency_rot_matrix_two_pt, coherency_rot_matrix_one_pt, atol=1e-3
147 | )
148 | else:
149 | assert np.allclose(
150 | coherency_rot_matrix_two_pt, coherency_rot_matrix_one_pt, atol=1e-14
151 | )
152 |
153 | # check errors are raised appropriately
154 | with pytest.raises(ValueError) as cm:
155 | sct.rotate_points_3d(R_exact[0:1, :], theta_radec, phi_radec)
156 | assert str(cm.value).startswith("rot_matrix must be a 3x3 array")
157 |
158 | with pytest.raises(ValueError) as cm:
159 | sct.vecs2rot(r1=intermediate_vec, theta2=theta_altaz, phi2=phi_altaz)
160 | assert str(cm.value).startswith("Either r1 and r2 must be supplied or all of")
161 |
162 | with pytest.raises(ValueError) as cm:
163 | sct.vecs2rot(r1=intermediate_vec[0:1], r2=altaz_vec)
164 | assert str(cm.value).startswith("r1 and r2 must be length 3 vectors")
165 |
166 | with pytest.raises(ValueError) as cm:
167 | sct.vecs2rot(r1=intermediate_vec * 2, r2=altaz_vec)
168 | assert str(cm.value).startswith("r1 and r2 must be unit vectors")
169 |
170 | norm = np.cross(intermediate_vec, altaz_vec)
171 | sinPsi = np.sqrt(np.dot(norm, norm))
172 | n_hat = norm / sinPsi # Trouble lurks if Psi = 0.
173 | cosPsi = np.dot(intermediate_vec, altaz_vec)
174 | Psi = np.arctan2(sinPsi, cosPsi)
175 |
176 | with pytest.raises(ValueError) as cm:
177 | sct.axis_angle_rotation_matrix(n_hat[0:1], Psi)
178 | assert str(cm.value).startswith("axis must be a must be length 3 vector")
179 |
180 | with pytest.raises(ValueError) as cm:
181 | sct.axis_angle_rotation_matrix(n_hat * 2, Psi)
182 | assert str(cm.value).startswith("axis must be a unit vector")
183 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # pyradiosky
2 | 
3 | [](https://codecov.io/gh/RadioAstronomySoftwareGroup/pyradiosky)
4 | [](https://app.readthedocs.org/projects/pyradiosky/)
5 | [](https://doi.org/10.21105/joss.06503)
6 | [](https://doi.org/10.5281/zenodo.11187469)
7 |
8 | Python objects and interfaces for representing diffuse, extended and compact
9 | astrophysical radio sources.
10 |
11 | The primary user class is `SkyModel`, which supports:
12 |
13 | - catalogs of point sources (read/write to hd5 and text files, read VOTables)
14 | - diffuse models as HEALPix maps (read/write to hd5 files)
15 | - conversion between any astropy supported coordinates (e.g. J2000 RA/Dec) and
16 | Azimuth/Elevation including calculating full polarization coherencies in Az/El.
17 |
18 | # File formats
19 |
20 | pyradiosky supports reading in catalogs from several formats, including VO Table files,
21 | text files, [FHD](https://github.com/EoRImaging/FHD) catalog files and SkyH5 files and
22 | supports writing to SkyH5 and text files. SkyH5 is an HDF5-based file format defined by
23 | the pyradiosky team, a full description is in the [SkyH5 memo](docs/references/skyh5_memo.pdf).
24 |
25 | # Community Guidelines
26 | Contributions to this package to add new file formats or address any of the
27 | issues in the [issue log](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/issues)
28 | are very welcome, as are bug reports and feature requests.
29 | Please see our [guide on contributing](.github/CONTRIBUTING.md)
30 |
31 | # Versioning
32 | We use a `generation.major.minor` version number format. We use the `generation`
33 | number for very significant improvements or major rewrites, the `major` number
34 | to indicate substantial package changes and the `minor` number to release smaller
35 | incremental updates (which do not include breaking API changes). We do our best
36 | to provide a significant period (usually 2 major generations) of deprecation
37 | warnings for all breaking changes to the API.
38 | We track all changes in our [changelog](https://github.com/RadioAstronomySoftwareGroup/pyradiosky/blob/main/CHANGELOG.md).
39 |
40 | # Documentation
41 | Developer API documentation is hosted [here](https://pyradiosky.readthedocs.io/en/latest/).
42 |
43 | # Citation
44 | Please cite pyradiosky by citing our JOSS paper:
45 |
46 | Hazelton et al., (2024). pyradiosky: A Python package for Radio Sky Models.
47 | Journal of Open Source Software, 9(97), 6503, https://doi.org/10.21105/joss.06503
48 |
49 | [ADS Link](https://ui.adsabs.harvard.edu/abs/2024JOSS....9.6503H)
50 |
51 | # Installation
52 | Simple installation via pip is available for users, developers should follow
53 | the directions under [Developer Installation](#developer-installation) below.
54 |
55 | For simple installation, the latest stable version is available via pip with
56 | `pip install pyradiosky`.
57 |
58 | There are some optional dependencies that are required for specific functionality,
59 | which will not be installed automatically by pip.
60 | See [Dependencies](#dependencies) for details on installing optional dependencies.
61 |
62 | ## Dependencies
63 |
64 | If you are using `conda` to manage your environment, you may wish to install the
65 | following packages before installing `pyradiosky`:
66 |
67 | Required:
68 |
69 | * astropy>=6.0
70 | * h5py>=3.7
71 | * numpy>=1.23
72 | * scipy>=1.9
73 | * python>=3.11
74 | * pyuvdata>=3.2.3
75 | * setuptools_scm>=8.1
76 |
77 | Optional:
78 |
79 | * astropy-healpix>=1.0.2 (for working with beams in HEALPix formats)
80 | * astroquery>=0.4.4 (for downloading GLEAM and other VizieR catalogs)
81 | * lunarsky>=0.2.5 (for supporting telescope locations on the moon)
82 |
83 | We suggest using conda to install all the dependencies. To install
84 | pyuvdata, astropy-healpix and astroquery, you'll need to add conda-forge as a channel
85 | (```conda config --add channels conda-forge```).
86 |
87 | If you do not want to use conda, the packages are also available on PyPI.
88 | You can install the optional dependencies via pip by specifying an option
89 | when you install pyradiosky, as in ```pip install .[healpix]```
90 | which will install all the required packages for using the HEALPix functionality
91 | in pyradiosky. The options that can be passed in this way are:
92 | [`healpix`, `astroquery`, `lunarsky`, `all`, `doc`, `test`, `dev`].
93 | The first three (`healpix`, `astroquery`, `lunarsky`) enable various specific
94 | functionality while `all` will install all optional dependencies.
95 | The last three (`doc`, `test` and `dev`) may be useful for developers of pyradiosky.
96 |
97 | ## Developer Installation
98 | Clone the repository using
99 | ```git clone https://github.com/RadioAstronomySoftwareGroup/pyradiosky.git```
100 |
101 | Navigate into the pyradiosky directory and run `pip install .`
102 | (note that `python setup.py install` does not work).
103 | Note that this will attempt to automatically install any missing dependencies.
104 | If you use anaconda or another package manager you might prefer to first install
105 | the dependencies as described in [Dependencies](#dependencies).
106 |
107 | To install without dependencies, run `pip install --no-deps`
108 |
109 | If you want to do development on pyradiosky, in addition to the other dependencies
110 | you will need the following packages:
111 |
112 | * pytest
113 | * pytest-cov
114 | * coverage
115 | * pre-commit
116 | * sphinx
117 | * pypandoc
118 |
119 | One way to ensure you have all the needed packages is to use the included
120 | `environment.yaml` file to create a new environment that will
121 | contain all the optional dependencies along with dependencies required for
122 | testing and development (```conda env create -f environment.yml```).
123 | Alternatively, you can specify `dev` when installing pyradiosky
124 | (as in `pip install pyradiosky[dev]`) to install the packages needed for testing
125 | and documentation development.
126 |
127 | To use pre-commit to prevent committing code that does not follow our style,
128 | you'll need to run `pre-commit install` in the top level `pyradiosky` directory.
129 |
130 | # Tests
131 | Uses the `pytest` package to execute test suite.
132 | From the pyradiosky directory run ```pytest``` or ```python -m pytest```.
133 |
134 | # Maintainers
135 | pyradiosky is maintained by the RASG Managers, which currently include:
136 |
137 | - Adam Beardsley (Arizona State University)
138 | - Bryna Hazelton (University of Washington)
139 | - Daniel Jacobs (Arizona State University)
140 | - Paul La Plante (University of California, Berkeley)
141 | - Jonathan Pober (Brown University)
142 |
143 | Please use the channels discussed in the [guide on contributing](.github/CONTRIBUTING.md)
144 | for code-related discussions. You can contact us privately if needed at
145 | [rasgmanagers@gmail.com](mailto:rasgmanagers@gmail.com).
146 |
147 | # Acknowledgments
148 | Support for pyradiosky was provided by NSF awards #1835421 and #1835120.
149 |
--------------------------------------------------------------------------------
/.github/workflows/testsuite.yaml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: [push, pull_request]
3 |
4 | concurrency:
5 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
6 | cancel-in-progress: true
7 |
8 | jobs:
9 | tests:
10 | env:
11 | ENV_NAME: full_deps
12 | PYTHON: ${{ matrix.python-version }}
13 | OS: ${{ matrix.os }}
14 | name: Testing
15 | runs-on: ${{ matrix.os }}
16 | defaults:
17 | run:
18 | # Adding -l {0} helps ensure conda can be found properly.
19 | shell: bash -l {0}
20 | strategy:
21 | fail-fast: false
22 | matrix:
23 | os: [ubuntu-latest, macos-latest, windows-latest]
24 | python-version: ["3.11", "3.12", "3.13"]
25 | steps:
26 | - uses: actions/checkout@main
27 | with:
28 | fetch-depth: 0
29 |
30 | - name: Setup Miniconda
31 | uses: conda-incubator/setup-miniconda@v3
32 | with:
33 | miniforge-version: latest
34 | python-version: ${{ env.PYTHON }}
35 | environment-file: ci/${{ env.ENV_NAME }}.yaml
36 | activate-environment: ${{ env.ENV_NAME }}
37 | conda-remove-defaults: "true"
38 |
39 | - name: Conda Info
40 | run: |
41 | conda info -a
42 | conda list
43 | PYVER=`python -c "import sys; print('{:d}.{:d}'.format(sys.version_info.major, sys.version_info.minor))"`
44 | if [[ $PYVER != ${{ env.PYTHON }} ]]; then
45 | exit 1;
46 | fi
47 |
48 | - name: Install
49 | # calling git right before the install seems to prevent a time-out within setuptools_scm on MacOS
50 | run: |
51 | git describe --tags
52 | git version
53 | SETUPTOOLS_SCM_DEBUG=1 pip install --no-deps .
54 |
55 | - name: Run Tests
56 | run: |
57 | python -m pytest --cov=pyradiosky --cov-config=.coveragerc --cov-report xml:./coverage.xml
58 |
59 | - name: check coverage report
60 | run: |
61 | ls
62 | cat coverage.xml
63 |
64 | - uses: codecov/codecov-action@v5
65 | if: success()
66 | with:
67 | token: ${{ secrets.CODECOV_TOKEN }} #required
68 | files: ./coverage.xml #optional
69 |
70 | min_deps:
71 | env:
72 | ENV_NAME: min_deps
73 | PYTHON: "3.13"
74 | name: Min Deps Testing
75 | runs-on: ubuntu-latest
76 | strategy:
77 | fail-fast: false
78 | defaults:
79 | run:
80 | # Adding -l {0} helps ensure conda can be found properly.
81 | shell: bash -l {0}
82 | steps:
83 | - uses: actions/checkout@main
84 | with:
85 | fetch-depth: 0
86 |
87 | - name: Setup Miniconda
88 | uses: conda-incubator/setup-miniconda@v3
89 | with:
90 | miniforge-version: latest
91 | python-version: ${{ env.PYTHON }}
92 | environment-file: ci/${{ env.ENV_NAME }}.yaml
93 | activate-environment: ${{ env.ENV_NAME }}
94 | conda-remove-defaults: "true"
95 |
96 | - name: Conda Info
97 | run: |
98 | conda info -a
99 | conda list
100 | PYVER=`python -c "import sys; print('{:d}.{:d}'.format(sys.version_info.major, sys.version_info.minor))"`
101 | if [[ $PYVER != ${{ env.PYTHON }} ]]; then
102 | exit 1;
103 | fi
104 |
105 | - name: Install
106 | run: |
107 | pip install --no-deps .
108 |
109 | - name: Run Tests
110 | run: |
111 | python -m pytest --cov=pyradiosky --cov-config=.coveragerc --cov-report xml:./coverage.xml
112 |
113 | - name: check coverage report
114 | run: |
115 | ls
116 | cat coverage.xml
117 |
118 | - uses: codecov/codecov-action@v5
119 | if: success()
120 | with:
121 | token: ${{secrets.CODECOV_TOKEN}} #required
122 | files: ./coverage.xml #optional
123 |
124 | min_versions:
125 | env:
126 | ENV_NAME: min_versions
127 | PYTHON: "3.11"
128 | name: Min Versions Testing
129 | runs-on: ubuntu-latest
130 | strategy:
131 | fail-fast: false
132 | defaults:
133 | run:
134 | # Adding -l {0} helps ensure conda can be found properly.
135 | shell: bash -l {0}
136 | steps:
137 | - uses: actions/checkout@main
138 | with:
139 | fetch-depth: 0
140 |
141 | - name: Setup Miniconda
142 | uses: conda-incubator/setup-miniconda@v3
143 | with:
144 | miniforge-version: latest
145 | python-version: ${{ env.PYTHON }}
146 | environment-file: ci/${{ env.ENV_NAME }}.yaml
147 | activate-environment: ${{ env.ENV_NAME }}
148 | conda-remove-defaults: "true"
149 |
150 | - name: Conda Info
151 | run: |
152 | conda info -a
153 | conda list
154 | PYVER=`python -c "import sys; print('{:d}.{:d}'.format(sys.version_info.major, sys.version_info.minor))"`
155 | if [[ $PYVER != ${{ env.PYTHON }} ]]; then
156 | exit 1;
157 | fi
158 |
159 | - name: Install
160 | run: |
161 | pip install --no-deps -e .
162 |
163 | - name: Run Tests
164 | run: |
165 | python -m pytest --cov=pyradiosky --cov-config=.coveragerc --cov-report xml:./coverage.xml
166 |
167 | - name: check coverage report
168 | run: |
169 | ls
170 | cat coverage.xml
171 |
172 | - uses: codecov/codecov-action@v5
173 | if: success()
174 | with:
175 | token: ${{secrets.CODECOV_TOKEN}} #required
176 | files: ./coverage.xml #optional
177 |
178 | # Use pip for diversity
179 | warning_test:
180 | env:
181 | PYTHON: "3.12"
182 | name: Warning Test
183 | runs-on: ubuntu-latest
184 | steps:
185 | - uses: actions/checkout@main
186 | with:
187 | fetch-depth: 0
188 |
189 | - name: Setup Python
190 | uses: actions/setup-python@v4
191 | with:
192 | python-version: ${{ env.PYTHON }}
193 |
194 | - name: Install
195 | run: pip install -e .[dev]
196 |
197 | - name: Environment Info
198 | run: |
199 | pip list
200 | PYVER=`python -c "import sys; print('{:d}.{:d}'.format(sys.version_info.major, sys.version_info.minor))"`
201 | if [[ $PYVER != ${{ env.PYTHON }} ]]; then
202 | exit 1;
203 | fi
204 |
205 | - name: Run Tests
206 | run: |
207 | python -m pytest -W error --cov=pyradiosky --cov-config=.coveragerc --cov-report xml:./coverage.xml
208 |
209 | - name: check coverage report
210 | run: |
211 | ls
212 | cat coverage.xml
213 |
214 | - uses: codecov/codecov-action@v5
215 | if: success()
216 | with:
217 | token: ${{secrets.CODECOV_TOKEN}} #required
218 | files: ./coverage.xml #optional
219 |
220 | docs_test:
221 | env:
222 | ENV_NAME: full_deps
223 | PYTHON: "3.12"
224 | name: Tutorial Testing
225 | runs-on: ubuntu-latest
226 | defaults:
227 | run:
228 | # Adding -l {0} helps ensure conda can be found properly.
229 | shell: bash -l {0}
230 | strategy:
231 | fail-fast: false
232 | steps:
233 | - uses: actions/checkout@main
234 | with:
235 | fetch-depth: 0
236 |
237 | - name: Setup Miniconda
238 | uses: conda-incubator/setup-miniconda@v3
239 | with:
240 | auto-update-conda: true
241 | miniconda-version: "latest"
242 | python-version: ${{ env.PYTHON }}
243 | environment-file: ci/${{ env.ENV_NAME }}.yaml
244 | activate-environment: ${{ env.ENV_NAME }}
245 | conda-remove-defaults: "true"
246 |
247 | - name: Conda Info
248 | run: |
249 | conda info -a
250 | conda list
251 | PYVER=`python -c "import sys; print('{:d}.{:d}'.format(sys.version_info.major, sys.version_info.minor))"`
252 | if [[ $PYVER != ${{ env.PYTHON }} ]]; then
253 | exit 1;
254 | fi
255 |
256 | - name: Install
257 | run: |
258 | pip install --no-deps .
259 |
260 | - name: Run Tests
261 | run: |
262 | cd docs
263 | python -m pytest --doctest-glob="*.rst" -W "error::DeprecationWarning"
264 |
--------------------------------------------------------------------------------
/tests/test_utils.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019 Radio Astronomy Software Group
2 | # Licensed under the 2-clause BSD License
3 | import os
4 | import subprocess # nosec
5 |
6 | import astropy.units as units
7 | import numpy as np
8 | import pytest
9 | from astropy.cosmology import Planck15
10 |
11 | from pyradiosky import SkyModel, utils as skyutils
12 |
13 |
14 | def test_stokes_tofrom_coherency():
15 | stokesI = 4.5
16 | stokesQ = -0.3
17 | stokesU = 1.2
18 | stokesV = -0.15
19 | stokes = np.array([stokesI, stokesQ, stokesU, stokesV])
20 |
21 | expected_coherency = (
22 | 0.5 * np.array([[4.2, 1.2 + 0.15j], [1.2 - 0.15j, 4.8]]) * units.Jy
23 | )
24 |
25 | with pytest.raises(ValueError, match="stokes_arr must be an astropy Quantity."):
26 | skyutils.stokes_to_coherency(stokes)
27 |
28 | coherency = skyutils.stokes_to_coherency(stokes * units.Jy)
29 |
30 | with pytest.raises(
31 | ValueError, match="coherency_matrix must be an astropy Quantity."
32 | ):
33 | skyutils.coherency_to_stokes(coherency.value)
34 |
35 | back_to_stokes = skyutils.coherency_to_stokes(coherency)
36 |
37 | assert np.allclose(stokes * units.Jy, back_to_stokes)
38 |
39 | # again, with multiple sources and a frequency axis.
40 | stokes = (
41 | np.array(
42 | [[stokesI, stokesQ, stokesU, stokesV], [stokesI, stokesQ, stokesU, stokesV]]
43 | ).T
44 | * units.Jy
45 | )
46 |
47 | stokes = stokes[:, np.newaxis, :]
48 |
49 | coherency = skyutils.stokes_to_coherency(stokes)
50 | back_to_stokes = skyutils.coherency_to_stokes(coherency)
51 |
52 | assert units.quantity.allclose(stokes, back_to_stokes)
53 |
54 | with pytest.raises(ValueError) as cm:
55 | skyutils.stokes_to_coherency(stokes[0:2, :])
56 | assert str(cm.value).startswith(
57 | "First dimension of stokes_vector must be length 4."
58 | )
59 |
60 | with pytest.raises(ValueError) as cm:
61 | skyutils.coherency_to_stokes(expected_coherency[0, :])
62 | assert str(cm.value).startswith(
63 | "First two dimensions of coherency_matrix must be length 2."
64 | )
65 |
66 |
67 | @pytest.mark.filterwarnings("ignore:Some stokes I values are negative")
68 | @pytest.mark.filterwarnings("ignore:Some spectral index values are NaN")
69 | @pytest.mark.filterwarnings("ignore:Some stokes values are NaNs")
70 | @pytest.mark.parametrize("stype", ["subband", "spectral_index", "flat"])
71 | def test_download_gleam(tmp_path, stype):
72 | pytest.importorskip("astroquery")
73 | import requests # a dependency of astroquery
74 |
75 | fname = "gleam_cat.vot"
76 | filename = os.path.join(tmp_path, fname)
77 | n_src = 50
78 |
79 | try:
80 | output = subprocess.check_output( # nosec
81 | [
82 | "download_gleam",
83 | "--path",
84 | str(tmp_path),
85 | "--filename",
86 | fname,
87 | "--row_limit",
88 | str(n_src),
89 | ]
90 | )
91 | assert output.decode("utf-8").startswith(
92 | "GLEAM catalog downloaded and saved to"
93 | )
94 | except requests.exceptions.ConnectionError:
95 | pytest.skip("Connection error w/ Vizier")
96 |
97 | sky = SkyModel()
98 | sky.read_gleam_catalog(filename, spectral_type=stype)
99 | assert sky.Ncomponents == n_src
100 |
101 | # Cannot just compare to the file we have in our data folder because there
102 | # seems to be some variation in which sources you get when you just download
103 | # some of the sources, especially on CI (the comparison works locally for me).
104 |
105 | # check there's not an error if the file exists and overwrite is False
106 | # and that the file is not replaced
107 | skyutils.download_gleam(path=tmp_path, filename=fname, row_limit=5)
108 | sky.read_gleam_catalog(filename, spectral_type=stype)
109 | assert sky.Ncomponents == n_src
110 |
111 | # check that the file is replaced if overwrite is True
112 | try:
113 | skyutils.download_gleam(
114 | path=tmp_path, filename=fname, row_limit=5, overwrite=True
115 | )
116 | except requests.exceptions.ConnectionError:
117 | pytest.skip("Connection error w/ Vizier")
118 | sky2 = SkyModel()
119 | sky2.read_gleam_catalog(filename, spectral_type=stype)
120 | assert sky2.Ncomponents == 5
121 |
122 |
123 | def test_astroquery_missing_error(tmp_path):
124 | fname = "gleam_cat.vot"
125 |
126 | try:
127 | import astroquery # noqa
128 |
129 | pass
130 | except ImportError:
131 | with pytest.raises(
132 | ImportError,
133 | match="The astroquery module is required to use the download_gleam "
134 | "function.",
135 | ):
136 | skyutils.download_gleam(path=tmp_path, filename=fname, row_limit=10)
137 |
138 |
139 | def test_jy_to_ksr():
140 | Nfreqs = 200
141 | freqs = np.linspace(100, 200, Nfreqs) * units.MHz
142 |
143 | def jy2ksr_nonastropy(freq_arr):
144 | c_cmps = 29979245800.0 # cm/s
145 | k_boltz = 1.380658e-16 # erg/K
146 | lam = c_cmps / freq_arr.to_value("Hz") # cm
147 | return 1e-23 * lam**2 / (2 * k_boltz)
148 |
149 | conv0 = skyutils.jy_to_ksr(freqs)
150 | conv1 = jy2ksr_nonastropy(freqs) * units.K * units.sr / units.Jy
151 |
152 | assert np.allclose(conv0, conv1)
153 |
154 |
155 | @pytest.mark.filterwarnings("ignore:Some stokes I values are negative")
156 | @pytest.mark.parametrize(
157 | ("fspec", "use_cli"), [("freqs", True), ("freqs", False), ("redshifts", False)]
158 | )
159 | def test_flat_spectrum_skymodel(fspec, use_cli, tmp_path):
160 | n_freq = 20
161 | freqs = np.linspace(150e6, 180e6, n_freq)
162 | nside = 256
163 | variance = 1e-6
164 |
165 | redshifts = skyutils.f21 / freqs - 1
166 | z_order = np.argsort(redshifts)
167 | redshifts = redshifts[z_order]
168 |
169 | if use_cli:
170 | # cli only accepts frequencies
171 | file_name = str(tmp_path) + "test_flat_spectrum.skyh5"
172 | output = subprocess.check_output( # nosec
173 | [
174 | "make_flat_spectrum_eor",
175 | "-v",
176 | str(variance),
177 | "--nside",
178 | str(nside),
179 | "--filename",
180 | file_name,
181 | "-s",
182 | str(freqs[0]),
183 | "-e",
184 | str(freqs[-1]),
185 | "-N",
186 | str(n_freq),
187 | ]
188 | )
189 | assert output.decode("utf-8").startswith(
190 | "Generating sky model, nside 256, and variance 1e-06 K^2 at channel 0."
191 | )
192 | assert (
193 | "Generated flat-spectrum model, with spectral amplitude 0.037 K$^2$ Mpc$^3$"
194 | in output.decode("utf-8")
195 | )
196 |
197 | sky = SkyModel.from_file(file_name)
198 | else:
199 | fspec_kwargs = {}
200 | if fspec == "freqs":
201 | fspec_kwargs = {"freqs": freqs * units.Hz}
202 | else:
203 | fspec_kwargs = {"redshifts": redshifts, "ref_zbin": n_freq - 1}
204 |
205 | sky = skyutils.flat_spectrum_skymodel(
206 | variance=variance, nside=nside, **fspec_kwargs
207 | )
208 |
209 | npix = 12 * nside**2
210 | assert sky.Ncomponents == npix
211 | np.testing.assert_allclose(sky.freq_array.value, freqs)
212 | calc_var = np.var(sky.stokes, axis=2)
213 |
214 | dz = redshifts[-1] - redshifts[-2]
215 | omega = 4 * np.pi / npix
216 | vol = (
217 | Planck15.differential_comoving_volume(redshifts[-1]).to_value("Mpc^3/sr")
218 | * dz
219 | * omega
220 | )
221 |
222 | pspec_amp = variance * vol
223 | assert f"{pspec_amp:.3f} " + r"K$^2$ Mpc$^3$" in sky.history
224 |
225 | assert np.isclose(calc_var[0, 0].value, variance)
226 |
227 |
228 | @pytest.mark.parametrize(
229 | ("kwargs", "msg"),
230 | [
231 | ({}, "Either redshifts or freqs must be set."),
232 | ({"freqs": np.linspace(180e6, 150e6, 10)}, "freqs must be in ascending order."),
233 | (
234 | {"redshifts": skyutils.f21 / np.linspace(150e6, 180e6, 10) - 1},
235 | "redshifts must be in ascending order.",
236 | ),
237 | ],
238 | )
239 | def test_flat_spectrum_skymodel_errors(kwargs, msg):
240 | with pytest.raises(ValueError, match=msg):
241 | skyutils.flat_spectrum_skymodel(variance=1e-6, nside=256, **kwargs)
242 |
--------------------------------------------------------------------------------
/src/pyradiosky/spherical_coords_transforms.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019 Radio Astronomy Software Group
2 | # Licensed under the 2-clause BSD License
3 | """Methods for doing spherical coordinate transformations on vectors."""
4 |
5 | import numpy as np
6 |
7 |
8 | def r_hat(theta, phi):
9 | """
10 | Get the r hat unit vectors in cartesian coordinates for points on a sphere.
11 |
12 | Parameters
13 | ----------
14 | theta, phi : float
15 | The co-latitude and azimuth coordinates, respectively, for a point
16 | on the sphere, in radians. Azimuth is defined with respect to the
17 | x axis, co-latitude is the angle with the positive z axis.
18 |
19 | Returns
20 | -------
21 | array of float
22 | Array of r hat vectors, shape (3, Npoints)
23 | """
24 | theta = np.array(theta)
25 | phi = np.array(phi)
26 | if theta.shape != phi.shape:
27 | raise ValueError("theta and phi must have the same shape")
28 | rhx = np.cos(phi) * np.sin(theta)
29 | rhy = np.sin(phi) * np.sin(theta)
30 | rhz = np.cos(theta)
31 | return np.stack((rhx, rhy, rhz))
32 |
33 |
34 | def theta_hat(theta, phi):
35 | """
36 | Get the theta hat unit vectors in cartesian coordinates for points on a sphere.
37 |
38 | Parameters
39 | ----------
40 | theta, phi : float
41 | The co-latitude and azimuth coordinates, respectively, for a point
42 | on the sphere, in radians. Azimuth is defined with respect to the
43 | x axis, co-latitude is the angle with the positive z axis.
44 |
45 | Returns
46 | -------
47 | array of float
48 | Array of theta hat vectors, shape (3, Npoints)
49 | """
50 | theta = np.array(theta)
51 | phi = np.array(phi)
52 | if theta.shape != phi.shape:
53 | raise ValueError("theta and phi must have the same shape")
54 | thx = np.cos(phi) * np.cos(theta)
55 | thy = np.sin(phi) * np.cos(theta)
56 | thz = -np.sin(theta)
57 | return np.stack((thx, thy, thz))
58 |
59 |
60 | def phi_hat(theta, phi):
61 | """
62 | Get the phi hat unit vectors in cartesian coordinates for points on a sphere.
63 |
64 | Parameters
65 | ----------
66 | theta, phi : float
67 | The co-latitude and azimuth coordinates, respectively, for a point
68 | on the sphere, in radians. Azimuth is defined with respect to the
69 | x axis, co-latitude is the angle with the positive z axis.
70 |
71 | Returns
72 | -------
73 | array of float
74 | Array of phi hat vectors, shape (3, Npoints)
75 | """
76 | theta = np.array(theta)
77 | phi = np.array(phi)
78 | if theta.shape != phi.shape:
79 | raise ValueError("theta and phi must have the same shape")
80 | phx = -np.sin(phi)
81 | phy = np.cos(phi)
82 | phz = np.zeros_like(phi)
83 | return np.stack((phx, phy, phz))
84 |
85 |
86 | def rotate_points_3d(rot_matrix, theta, phi):
87 | """
88 | Get the spherical coordinates of the point under a 3d rotation.
89 |
90 | Finds the spherical coordinates for point p specified by p = R . q,
91 | where q is the 3D position vector of the point specified by (theta,phi) and
92 | R is the 3D rotation matrix that relates two coordinate charts.
93 |
94 | The accuracy of this method may not be good enough near pols in either
95 | coordinate system.
96 |
97 | Parameters
98 | ----------
99 | rot_matrix : array-like of float
100 | rotation matrix to use
101 | theta, phi : float
102 | The co-latitude and azimuth coordinates, respectively, for a point
103 | on the sphere, in radians. Azimuth is defined with respect to the
104 | x axis, co-latitude is the angle with the positive z axis.
105 |
106 | Returns
107 | -------
108 | beta, alpha : float
109 | The theta, phi coordinates for the point on the sphere (using normal
110 | mathematical conventions) in the rotated frame.
111 | """
112 | # This is NOT written to be vectorized for multiple (theta, phi)
113 |
114 | rot_matrix = np.array(rot_matrix)
115 | if rot_matrix.shape != (3, 3):
116 | raise ValueError("rot_matrix must be a 3x3 array")
117 |
118 | # Replace with function call?
119 | q_hat_1 = np.cos(phi) * np.sin(theta)
120 | q_hat_2 = np.sin(phi) * np.sin(theta)
121 | q_hat_3 = np.cos(theta)
122 | q_hat = np.stack((q_hat_1, q_hat_2, q_hat_3))
123 |
124 | # Should test for shape of p_hat
125 | p_hat = np.einsum("ab...,b...->a...", rot_matrix, q_hat)
126 |
127 | # Should write a function to do this as well, i.e., pull back angles from
128 | # a vector
129 | if np.isclose(p_hat[2], 1.0, rtol=0.0, atol=1e-12):
130 | p_hat[2] = 1.0
131 | beta = np.arccos(p_hat[2])
132 | alpha = np.arctan2(p_hat[1], p_hat[0])
133 |
134 | if alpha < 0:
135 | alpha += 2.0 * np.pi
136 |
137 | return (beta, alpha)
138 |
139 |
140 | def spherical_basis_vector_rotation_matrix(
141 | theta, phi, rot_matrix, beta=None, alpha=None
142 | ):
143 | """
144 | Get the rotation matrix for vectors in theta/phi basis to a new reference frame.
145 |
146 | Given a position (`theta`, `phi`) in “standard mathematical” coordinates
147 | (0 < `theta` < pi, 0 < `phi` < 2 pi) which will typically be an ICRS RA/Dec
148 | coordinate appropriately converted, and the point to which it is transformed
149 | in another standard mathematical coordinate system (`beta`, `alpha`), which
150 | will typically be local telescope Alt/Az appropriately converted, and a
151 | 3 x 3 rotation matrix `rot_matrix` which connects those two points,
152 | calculate the rotation matrix which rotates the basis vectors associated
153 | with (`theta`, `phi`) to those associated with (`beta`, `alpha`).
154 |
155 | Parameters
156 | ----------
157 | theta, phi : float
158 | The co-latitude and azimuth coordinates, respectively, for a point
159 | on the sphere, in radians. Azimuth is defined with respect to the
160 | x axis, co-latitude is the angle with the positive z axis.
161 | rot_matrix : array-like of float
162 | Rotation matrix that takes 3-vectors from (theta, phi) to (beta, alpha)
163 | beta, alpha : float, optional
164 | The theta, phi coordinates for the point on the sphere (using normal
165 | mathematical conventions) in the rotated frame. If either is not provided,
166 | they are calculated using `rotate_points_3d`. Note these may
167 | not be as exact as values calculated from astropy.
168 |
169 | Returns
170 | -------
171 | array of float
172 | 2 x 2 rotation matrix that takes vectors in the theta/phi basis to
173 | the beta/alpha basis.
174 | """
175 | if alpha is None or beta is None:
176 | beta, alpha = rotate_points_3d(rot_matrix, theta, phi)
177 |
178 | th = theta_hat(theta, phi)
179 | ph = phi_hat(theta, phi)
180 |
181 | bh = np.einsum("ab...,b...->a...", rot_matrix.T, theta_hat(beta, alpha))
182 |
183 | cosX = np.einsum("a...,a...", bh, th)
184 | sinX = np.einsum("a...,a...", bh, ph)
185 |
186 | return np.array([[cosX, sinX], [-sinX, cosX]])
187 |
188 |
189 | def axis_angle_rotation_matrix(axis, angle):
190 | """
191 | Get the rotation matrix using Rodrigues' rotation matrix formula.
192 |
193 | Parameters
194 | ----------
195 | axis : array-like of float
196 | 3 element unit vector specifying the axis to rotate around.
197 | angle : float
198 | angle to rotate by in radians
199 |
200 | Returns
201 | -------
202 | array
203 | 3x3 rotation matrix to rotate vectors by `angle` around `axis`.
204 | """
205 | if axis.shape != (3,):
206 | raise ValueError("axis must be a must be length 3 vector")
207 | if not is_unit_vector(axis):
208 | raise ValueError("axis must be a unit vector")
209 |
210 | K_matrix = np.array(
211 | [[0.0, -axis[2], axis[1]], [axis[2], 0.0, -axis[0]], [-axis[1], axis[0], 0.0]]
212 | )
213 |
214 | I_matrix = np.identity(3)
215 |
216 | rot_matrix = (
217 | I_matrix
218 | + np.sin(angle) * K_matrix
219 | + (1.0 - np.cos(angle)) * np.dot(K_matrix, K_matrix)
220 | )
221 |
222 | return rot_matrix
223 |
224 |
225 | def is_orthogonal(matrix, tol=1e-15):
226 | """
227 | Test for matrix orthogonality.
228 |
229 | Parameters
230 | ----------
231 | matrix : array-like of float
232 | square matrix to test
233 |
234 | Returns
235 | -------
236 | bool
237 | True if `matrix` is orthogonal, False otherwise.
238 | """
239 | return np.allclose(np.matmul(matrix, matrix.T), np.eye(3), atol=tol)
240 |
241 |
242 | def is_unit_vector(vec, tol=1e-15):
243 | """
244 | Test for unit vectors.
245 |
246 | Parameters
247 | ----------
248 | vec : array-like of float
249 | vector to test
250 |
251 | Returns
252 | -------
253 | bool
254 | True if `vec` is a unit vector, False otherwise.
255 | """
256 | return np.allclose(np.dot(vec, vec), 1, atol=tol)
257 |
258 |
259 | def vecs2rot(r1=None, r2=None, theta1=None, phi1=None, theta2=None, phi2=None):
260 | """
261 | Get the rotation matrix that connects two points or unit vectors on the sphere.
262 |
263 | Parameters
264 | ----------
265 | r1, r2 : array-like of float, optional
266 | length 3 unit vectors
267 | theta1, phi1, theta2, phi2 : float, optional
268 | The co-latitude and azimuth coordinates, respectively, for a point
269 | on the sphere, in radians. Azimuth is defined with respect to the
270 | x axis, co-latitude is the angle with the positive z axis.
271 | Ignored if r1 and r2 are supplied.
272 |
273 | Returns
274 | -------
275 | array
276 | 3x3 rotation matrix that rotates the first point or vector into the other.
277 | """
278 | if r1 is None or r2 is None:
279 | if theta1 is None or phi1 is None or theta2 is None or phi2 is None:
280 | raise ValueError(
281 | "Either r1 and r2 must be supplied or all of "
282 | "theta1, phi1, theta2 and phi2 must be supplied."
283 | )
284 | r1 = r_hat(theta1, phi1)
285 | r2 = r_hat(theta2, phi2)
286 |
287 | assert is_unit_vector(r1), "r1 is not a unit vector: " + str(r1)
288 | assert is_unit_vector(r2), "r2 is not a unit vector: " + str(r2)
289 | else:
290 | r1 = np.array(r1)
291 | r2 = np.array(r2)
292 | if r1.shape != (3,) or r2.shape != (3,):
293 | raise ValueError("r1 and r2 must be length 3 vectors")
294 | if not is_unit_vector(r1) or not is_unit_vector(r2):
295 | raise ValueError("r1 and r2 must be unit vectors")
296 |
297 | norm = np.cross(r1, r2)
298 | # Note that Psi is between 0 and pi
299 | sinPsi = np.sqrt(np.dot(norm, norm))
300 | n_hat = norm / sinPsi # Trouble lurks if Psi = 0.
301 | cosPsi = np.dot(r1, r2)
302 | Psi = np.arctan2(sinPsi, cosPsi)
303 | rotation = axis_angle_rotation_matrix(n_hat, Psi)
304 |
305 | assert is_unit_vector(n_hat), "n_hat is not a unit vector: " + str(n_hat)
306 | assert is_orthogonal(rotation), "rotation matrix is not orthogonal: " + str(
307 | rotation
308 | )
309 |
310 | return rotation
311 |
--------------------------------------------------------------------------------
/src/pyradiosky/utils.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2019 Radio Astronomy Software Group
2 | # Licensed under the 2-clause BSD License
3 | """Utility methods."""
4 |
5 | import os
6 |
7 | import astropy.units as units
8 | import numpy as np
9 | from astropy.cosmology import Planck15
10 | from astropy.units import Quantity
11 |
12 | from pyradiosky.data import DATA_PATH as SKY_DATA_PATH
13 |
14 | f21 = 1.420405751e9
15 |
16 |
17 | def stokes_to_coherency(stokes_arr):
18 | """
19 | Convert Stokes array to coherency matrix.
20 |
21 | Parameters
22 | ----------
23 | stokes_arr : Quantity
24 | Array of stokes parameters in order [I, Q, U, V], shape(4,) or
25 | (4, Nfreqs, Ncomponents).
26 |
27 | Returns
28 | -------
29 | coherency matrix : array of float
30 | Array of coherencies, shape (2, 2) or (2, 2, Nfreqs, Ncomponents)
31 | """
32 | if not isinstance(stokes_arr, Quantity):
33 | raise ValueError("stokes_arr must be an astropy Quantity.")
34 |
35 | initial_shape = stokes_arr.shape
36 | if initial_shape[0] != 4:
37 | raise ValueError("First dimension of stokes_vector must be length 4.")
38 |
39 | if stokes_arr.size == 4 and len(initial_shape) == 1:
40 | stokes_arr = stokes_arr[:, np.newaxis, np.newaxis]
41 |
42 | coherency = (
43 | 0.5
44 | * np.array(
45 | [
46 | [
47 | stokes_arr[0, :, :] + stokes_arr[1, :, :],
48 | stokes_arr[2, :, :] - 1j * stokes_arr[3, :, :],
49 | ],
50 | [
51 | stokes_arr[2, :, :] + 1j * stokes_arr[3, :, :],
52 | stokes_arr[0, :, :] - stokes_arr[1, :, :],
53 | ],
54 | ]
55 | )
56 | * stokes_arr.unit
57 | )
58 |
59 | if stokes_arr.size == 4 and len(initial_shape) == 1:
60 | coherency = coherency.squeeze()
61 | return coherency
62 |
63 |
64 | def coherency_to_stokes(coherency_matrix):
65 | """
66 | Convert coherency matrix to vector of 4 Stokes parameter in order [I, Q, U, V].
67 |
68 | Parameters
69 | ----------
70 | coherency matrix : Quantity
71 | Array of coherencies, shape (2, 2) or (2, 2, Ncomponents)
72 |
73 | Returns
74 | -------
75 | stokes_arr : array of float
76 | Array of stokes parameters, shape(4,) or (4, Ncomponents)
77 | """
78 | if not isinstance(coherency_matrix, Quantity):
79 | raise ValueError("coherency_matrix must be an astropy Quantity.")
80 |
81 | initial_shape = coherency_matrix.shape
82 | if len(initial_shape) < 2 or initial_shape[0] != 2 or initial_shape[1] != 2:
83 | raise ValueError("First two dimensions of coherency_matrix must be length 2.")
84 |
85 | if coherency_matrix.size == 4 and len(initial_shape) == 2:
86 | coherency_matrix = coherency_matrix[:, :, np.newaxis]
87 |
88 | stokes = (
89 | np.array(
90 | [
91 | coherency_matrix[0, 0, :] + coherency_matrix[1, 1, :],
92 | coherency_matrix[0, 0, :] - coherency_matrix[1, 1, :],
93 | coherency_matrix[0, 1, :] + coherency_matrix[1, 0, :],
94 | -(coherency_matrix[0, 1, :] - coherency_matrix[1, 0, :]).imag,
95 | ]
96 | ).real
97 | * coherency_matrix.unit
98 | )
99 | if coherency_matrix.size == 4 and len(initial_shape) == 2:
100 | stokes = stokes.squeeze()
101 |
102 | return stokes
103 |
104 |
105 | def jy_to_ksr(freqs):
106 | """
107 | Calculate multiplicative factors to convert [Jy] to [K sr].
108 |
109 | Parameters
110 | ----------
111 | freqs : :class:`astropy.Quantity` or array_like of float (Deprecated)
112 | Frequencies, assumed to be in Hz if not a Quantity.
113 |
114 | Returns
115 | -------
116 | Quantity
117 | Conversion factor(s) to go from [Jy] to [K sr]. Shape equal to shape of freqs.
118 | """
119 | freqs = np.atleast_1d(freqs)
120 | if not isinstance(freqs, Quantity):
121 | freqs = freqs * units.Hz
122 |
123 | equiv = units.brightness_temperature(freqs, beam_area=1 * units.sr)
124 | conv_factor = (1 * units.Jy).to(units.K, equivalencies=equiv) * units.sr / units.Jy
125 |
126 | return conv_factor
127 |
128 |
129 | def download_gleam(
130 | path=".", filename="gleam.vot", overwrite=False, row_limit=None, for_testing=False
131 | ):
132 | """
133 | Download the GLEAM vot table from Vizier.
134 |
135 | Parameters
136 | ----------
137 | path : str
138 | Folder location to save catalog to.
139 | filename : str
140 | Filename to save catalog to.
141 | overwrite : bool
142 | Option to download the file even if it already exists.
143 | row_limit : int, optional
144 | Max number of rows (sources) to download, default is None meaning download
145 | all rows.
146 | for_testing : bool
147 | Download a file to use for unit tests. If True, some additional columns are
148 | included, the rows are limited to 50, the path and filename are set to put
149 | the file in the correct location and the overwrite keyword is set to True.
150 |
151 | """
152 | try:
153 | from astroquery.vizier import Vizier
154 | except ImportError as e:
155 | raise ImportError(
156 | "The astroquery module is required to use the download_gleam function."
157 | ) from e
158 |
159 | if for_testing: # pragma: no cover
160 | path = SKY_DATA_PATH
161 | filename = "gleam_50srcs.vot"
162 | overwrite = True
163 | row_limit = 50
164 | # We have frequent CI failures with messages like
165 | # "Failed to resolve 'vizier.u-strasbg.fr'"
166 | # Try using a US mirror to see if we have fewer errors
167 | Vizier.VIZIER_SERVER = "vizier.cfa.harvard.edu"
168 |
169 | opath = os.path.join(path, filename)
170 | if os.path.exists(opath) and not overwrite:
171 | print(
172 | f"GLEAM already downloaded to {opath}. Set overwrite=True to "
173 | "re-download it."
174 | )
175 | return
176 |
177 | # full download is too slow for unit tests
178 | if row_limit is None: # pragma: no cover
179 | Vizier.ROW_LIMIT = -1
180 | else:
181 | Vizier.ROW_LIMIT = row_limit
182 | desired_columns = [
183 | "GLEAM",
184 | "RAJ2000",
185 | "DEJ2000",
186 | "Fintwide",
187 | "e_Fintwide",
188 | "alpha",
189 | "e_alpha",
190 | "chi2",
191 | "Fintfit200",
192 | "e_Fintfit200",
193 | "Fint076",
194 | "Fint084",
195 | "Fint092",
196 | "Fint099",
197 | "Fint107",
198 | "Fint115",
199 | "Fint122",
200 | "Fint130",
201 | "Fint143",
202 | "Fint151",
203 | "Fint158",
204 | "Fint166",
205 | "Fint174",
206 | "Fint181",
207 | "Fint189",
208 | "Fint197",
209 | "Fint204",
210 | "Fint212",
211 | "Fint220",
212 | "Fint227",
213 | "e_Fint076",
214 | "e_Fint084",
215 | "e_Fint092",
216 | "e_Fint099",
217 | "e_Fint107",
218 | "e_Fint115",
219 | "e_Fint122",
220 | "e_Fint130",
221 | "e_Fint143",
222 | "e_Fint151",
223 | "e_Fint158",
224 | "e_Fint166",
225 | "e_Fint174",
226 | "e_Fint181",
227 | "e_Fint189",
228 | "e_Fint197",
229 | "e_Fint204",
230 | "e_Fint212",
231 | "e_Fint220",
232 | "e_Fint227",
233 | ]
234 | if for_testing: # pragma: no cover
235 | desired_columns.extend(["Fpwide", "e_Fp076"])
236 |
237 | Vizier.columns = desired_columns
238 | catname = "VIII/100/gleamegc"
239 | table = Vizier.get_catalogs(catname)[0]
240 |
241 | for col in desired_columns:
242 | assert col in table.colnames, f"column {col} not in downloaded table."
243 |
244 | table.write(opath, format="votable", overwrite=overwrite)
245 |
246 | print("GLEAM catalog downloaded and saved to " + opath)
247 |
248 |
249 | def flat_spectrum_skymodel(
250 | *, variance, nside, ref_chan=0, ref_zbin=0, redshifts=None, freqs=None, frame="icrs"
251 | ):
252 | """
253 | Generate a full-frequency SkyModel of a flat-spectrum (noiselike) EoR signal.
254 |
255 | The amplitude of this signal is variance * vol(ref_chan), where vol() gives
256 | the voxel volume and ref_chan is a chosen reference point. The generated
257 | SkyModel has healpix component type.
258 |
259 | Parameters
260 | ----------
261 | variance: float
262 | Variance of the signal, in Kelvin^2, at the reference channel.
263 | nside: int
264 | HEALPix NSIDE parameter. Must be a power of 2.
265 | ref_chan: int
266 | Frequency channel to set as reference, if using freqs.
267 | ref_zbin: int
268 | Redshift bin number to use as reference, if using redshifts.
269 | redshifts: numpy.ndarray
270 | Redshifts at which to generate maps. Ignored if freqs is provided.
271 | freqs: numpy.ndarray
272 | Frequencies in Hz, not required if redshifts is passed. Overrides
273 | redshifts if passed.
274 |
275 | Returns
276 | -------
277 | pyradiosky.SkyModel
278 | A SkyModel instance corresponding a white noise-like EoR signal.
279 |
280 | Notes
281 | -----
282 | Either redshifts or freqs must be provided.
283 | The history string of the returned SkyModel gives the expected amplitude.
284 | """
285 | # import here to avoid circular imports
286 | from pyradiosky import SkyModel
287 |
288 | if freqs is not None:
289 | if not isinstance(freqs, units.Quantity):
290 | freqs *= units.Hz
291 |
292 | if np.any(np.diff(freqs.value) < 0):
293 | raise ValueError("freqs must be in ascending order.")
294 | redshifts = f21 / freqs.to("Hz").value - 1
295 | ref_z = redshifts[ref_chan]
296 | # must sort so that redshifts go in ascending order (opposite freq order)
297 | z_order = np.argsort(redshifts)
298 | redshifts = redshifts[z_order]
299 | freqs = freqs[z_order]
300 | ref_zbin = np.where(np.isclose(redshifts, ref_z))[0][0]
301 | elif redshifts is not None:
302 | if np.any(np.diff(redshifts) < 0):
303 | raise ValueError("redshifts must be in ascending order.")
304 | freqs = (f21 / (redshifts + 1)) * units.Hz
305 | else:
306 | raise ValueError("Either redshifts or freqs must be set.")
307 |
308 | npix = 12 * nside**2
309 | nfreqs = freqs.size
310 | omega = 4 * np.pi / npix
311 |
312 | # Make some noise.
313 | stokes = np.zeros((4, npix, nfreqs))
314 | stokes[0, :, :] = np.random.normal(0.0, np.sqrt(variance), (npix, nfreqs))
315 |
316 | voxvols = np.zeros(nfreqs)
317 | for zi in range(nfreqs - 1):
318 | dz = redshifts[zi + 1] - redshifts[zi]
319 |
320 | vol = (
321 | Planck15.differential_comoving_volume(redshifts[zi]).to_value("Mpc^3/sr")
322 | * dz
323 | * omega
324 | )
325 | voxvols[zi] = vol
326 | voxvols[nfreqs - 1] = (
327 | vol # Assume the last redshift bin is the same width as the next-to-last.
328 | )
329 |
330 | scale = np.sqrt(voxvols / voxvols[ref_zbin])
331 | stokes /= scale
332 | stokes = np.swapaxes(stokes, 1, 2) # Put npix in last place again.
333 |
334 | # sort back to freq order
335 | f_order = np.argsort(freqs)
336 | freqs = freqs[f_order]
337 | stokes = stokes[:, f_order]
338 |
339 | # The true power spectrum amplitude is variance * reference voxel volume.
340 | pspec_amp = variance * voxvols[ref_zbin]
341 | history_string = (
342 | f"Generated flat-spectrum model, with spectral amplitude {pspec_amp:.3f} "
343 | )
344 | history_string += r"K$^2$ Mpc$^3$"
345 |
346 | return SkyModel(
347 | freq_array=freqs,
348 | hpx_inds=np.arange(npix),
349 | spectral_type="full",
350 | nside=nside,
351 | stokes=stokes * units.K,
352 | history=history_string,
353 | frame=frame,
354 | )
355 |
--------------------------------------------------------------------------------
/paper.bib:
--------------------------------------------------------------------------------
1 | @article{pyuvdata2017,
2 | doi = {10.21105/joss.00140},
3 | url = {https://doi.org/10.21105/joss.00140},
4 | year = {2017},
5 | publisher = {The Open Journal},
6 | volume = {2},
7 | number = {10},
8 | pages = {140},
9 | author = {Bryna J. Hazelton and Daniel C. Jacobs and Jonathan C. Pober and Adam P. Beardsley},
10 | title = {pyuvdata: an interface for astronomical interferometeric datasets in python},
11 | journal = {Journal of Open Source Software}
12 | }
13 |
14 | @article{pyuvsim2019,
15 | doi = {10.21105/joss.01234},
16 | url = {https://doi.org/10.21105/joss.01234},
17 | year = {2019},
18 | publisher = {The Open Journal},
19 | volume = {4},
20 | number = {37},
21 | pages = {1234},
22 | author = {Adam E. Lanman and Bryna J. Hazelton and Daniel C. Jacobs and
23 | Matthew J. Kolopanis and Jonathan C. Pober and James E. Aguirre
24 | and Nithyanandan Thyagarajan},
25 | title = {pyuvsim: A comprehensive simulation package for radio interferometers in python},
26 | journal = {Journal of Open Source Software}
27 | }
28 |
29 |
30 | @article{astropy:2013,
31 | Adsnote = {Provided by the SAO/NASA Astrophysics Data System},
32 | Adsurl = {http://adsabs.harvard.edu/abs/2013A%26A...558A..33A},
33 | Archiveprefix = {arXiv},
34 | Author = {{Astropy Collaboration} and {Robitaille}, T.~P. and {Tollerud}, E.~J.
35 | and {Greenfield}, P. and {Droettboom}, M. and {Bray}, E. and {Aldcroft}, T.
36 | and {Davis}, M. and {Ginsburg}, A. and {Price-Whelan}, A.~M. and {Kerzendorf}, W.~E.
37 | and {Conley}, A. and {Crighton}, N. and {Barbary}, K. and {Muna}, D.
38 | and {Ferguson}, H. and {Grollier}, F. and {Parikh}, M.~M. and {Nair}, P.~H.
39 | and {Unther}, H.~M. and {Deil}, C. and {Woillez}, J. and {Conseil}, S.
40 | and {Kramer}, R. and {Turner}, J.~E.~H. and {Singer}, L. and {Fox}, R.
41 | and {Weaver}, B.~A. and {Zabalza}, V. and {Edwards}, Z.~I.
42 | and {Azalee Bostroem}, K. and {Burke}, D.~J. and {Casey}, A.~R.
43 | and {Crawford}, S.~M. and {Dencheva}, N. and {Ely}, J. and {Jenness}, T.
44 | and {Labrie}, K. and {Lim}, P.~L. and {Pierfederici}, F. and {Pontzen}, A.
45 | and {Ptak}, A. and {Refsdal}, B. and {Servillat}, M. and {Streicher}, O.
46 | },
47 | Doi = {10.1051/0004-6361/201322068},
48 | Eid = {A33},
49 | Eprint = {1307.6212},
50 | Journal = {Astronomy and Astrophysics},
51 | Keywords = {methods: data analysis, methods: miscellaneous, virtual observatory tools},
52 | Month = oct,
53 | Pages = {A33},
54 | Primaryclass = {astro-ph.IM},
55 | Title = {{Astropy: A community Python package for astronomy}},
56 | Volume = 558,
57 | Year = 2013,
58 | Bdsk-Url-1 = {https://dx.doi.org/10.1051/0004-6361/201322068}
59 | }
60 |
61 | @article{astropy:2018,
62 | author = {{Astropy Collaboration} and {Price-Whelan}, A.~M. and
63 | {Sip{\H{o}}cz}, B.~M. and {G{\"u}nther}, H.~M. and {Lim}, P.~L. and
64 | {Crawford}, S.~M. and {Conseil}, S. and {Shupe}, D.~L. and
65 | {Craig}, M.~W. and {Dencheva}, N. and {Ginsburg}, A. and {VanderPlas}, J.~T.
66 | and {Bradley}, L.~D. and {P{\'e}rez-Su{\'a}rez}, D. and
67 | {de Val-Borro}, M. and {Aldcroft}, T.~L. and {Cruz}, K.~L. and
68 | {Robitaille}, T.~P. and {Tollerud}, E.~J. and {Ardelean}, C. and
69 | {Babej}, T. and {Bach}, Y.~P. and {Bachetti}, M. and {Bakanov}, A.~V. and
70 | {Bamford}, S.~P. and {Barentsen}, G. and {Barmby}, P. and
71 | {Baumbach}, A. and {Berry}, K.~L. and {Biscani}, F. and {Boquien}, M. and
72 | {Bostroem}, K.~A. and {Bouma}, L.~G. and {Brammer}, G.~B. and
73 | {Bray}, E.~M. and {Breytenbach}, H. and {Buddelmeijer}, H. and
74 | {Burke}, D.~J. and {Calderone}, G. and {Cano Rodr{\'\i}guez}, J.~L. and
75 | {Cara}, M. and {Cardoso}, J.~V.~M. and {Cheedella}, S. and {Copin}, Y. and
76 | {Corrales}, L. and {Crichton}, D. and {D'Avella}, D. and {Deil}, C. and
77 | {Depagne}, {\'E}. and {Dietrich}, J.~P. and {Donath}, A. and
78 | {Droettboom}, M. and {Earl}, N. and {Erben}, T. and {Fabbro}, S. and
79 | {Ferreira}, L.~A. and {Finethy}, T. and {Fox}, R.~T. and
80 | {Garrison}, L.~H. and {Gibbons}, S.~L.~J. and {Goldstein}, D.~A. and
81 | {Gommers}, R. and {Greco}, J.~P. and {Greenfield}, P. and
82 | {Groener}, A.~M. and {Grollier}, F. and {Hagen}, A. and {Hirst}, P. and
83 | {Homeier}, D. and {Horton}, A.~J. and {Hosseinzadeh}, G. and {Hu}, L. and
84 | {Hunkeler}, J.~S. and {Ivezi{\'c}}, {\v{Z}}. and {Jain}, A. and
85 | {Jenness}, T. and {Kanarek}, G. and {Kendrew}, S. and {Kern}, N.~S. and
86 | {Kerzendorf}, W.~E. and {Khvalko}, A. and {King}, J. and {Kirkby}, D. and
87 | {Kulkarni}, A.~M. and {Kumar}, A. and {Lee}, A. and {Lenz}, D. and
88 | {Littlefair}, S.~P. and {Ma}, Z. and {Macleod}, D.~M. and
89 | {Mastropietro}, M. and {McCully}, C. and {Montagnac}, S. and
90 | {Morris}, B.~M. and {Mueller}, M. and {Mumford}, S.~J. and {Muna}, D. and
91 | {Murphy}, N.~A. and {Nelson}, S. and {Nguyen}, G.~H. and
92 | {Ninan}, J.~P. and {N{\"o}the}, M. and {Ogaz}, S. and {Oh}, S. and
93 | {Parejko}, J.~K. and {Parley}, N. and {Pascual}, S. and {Patil}, R. and
94 | {Patil}, A.~A. and {Plunkett}, A.~L. and {Prochaska}, J.~X. and
95 | {Rastogi}, T. and {Reddy Janga}, V. and {Sabater}, J. and
96 | {Sakurikar}, P. and {Seifert}, M. and {Sherbert}, L.~E. and
97 | {Sherwood-Taylor}, H. and {Shih}, A.~Y. and {Sick}, J. and
98 | {Silbiger}, M.~T. and {Singanamalla}, S. and {Singer}, L.~P. and
99 | {Sladen}, P.~H. and {Sooley}, K.~A. and {Sornarajah}, S. and
100 | {Streicher}, O. and {Teuben}, P. and {Thomas}, S.~W. and
101 | {Tremblay}, G.~R. and {Turner}, J.~E.~H. and {Terr{\'o}n}, V. and
102 | {van Kerkwijk}, M.~H. and {de la Vega}, A. and {Watkins}, L.~L. and
103 | {Weaver}, B.~A. and {Whitmore}, J.~B. and {Woillez}, J. and
104 | {Zabalza}, V. and {Astropy Contributors}
105 | },
106 | title = "{The Astropy Project: Building an Open-science Project and Status of the v2.0 Core Package}",
107 | journal = {Astronomical Journal},
108 | keywords = {methods: data analysis, methods: miscellaneous, methods: statistical, reference systems, Astrophysics - Instrumentation and Methods for Astrophysics},
109 | year = 2018,
110 | month = sep,
111 | volume = {156},
112 | number = {3},
113 | eid = {123},
114 | pages = {123},
115 | doi = {10.3847/1538-3881/aabc4f},
116 | archivePrefix = {arXiv},
117 | eprint = {1801.02634},
118 | primaryClass = {astro-ph.IM},
119 | adsurl = {https://ui.adsabs.harvard.edu/abs/2018AJ....156..123A},
120 | adsnote = {Provided by the SAO/NASA Astrophysics Data System}
121 | }
122 |
123 | @article{astropy:2022,
124 | author = {{Astropy Collaboration} and {Price-Whelan}, Adrian M. and
125 | {Lim}, Pey Lian and {Earl}, Nicholas and {Starkman}, Nathaniel and
126 | {Bradley}, Larry and {Shupe}, David L. and {Patil}, Aarya A. and
127 | {Corrales}, Lia and {Brasseur}, C.~E. and {N{"o}the}, Maximilian and
128 | {Donath}, Axel and {Tollerud}, Erik and {Morris}, Brett M. and
129 | {Ginsburg}, Adam and {Vaher}, Eero and {Weaver}, Benjamin A. and
130 | {Tocknell}, James and {Jamieson}, William and {van Kerkwijk}, Marten H.
131 | and {Robitaille}, Thomas P. and {Merry}, Bruce and {Bachetti}, Matteo and
132 | {G{"u}nther}, H. Moritz and {Aldcroft}, Thomas L. and {Alvarado-Montes}, Jaime A.
133 | and {Archibald}, Anne M. and {B{'o}di}, Attila and {Bapat}, Shreyas and
134 | {Barentsen}, Geert and {Baz{'a}n}, Juanjo and {Biswas}, Manish and
135 | {Boquien}, M{'e}d{'e}ric and {Burke}, D.~J. and {Cara}, Daria and
136 | {Cara}, Mihai and {Conroy}, Kyle E. and {Conseil}, Simon and {Craig}, Matthew W.
137 | and {Cross}, Robert M. and {Cruz}, Kelle L. and {D'Eugenio}, Francesco
138 | and {Dencheva}, Nadia and {Devillepoix}, Hadrien A.~R. and {Dietrich}, J{"o}rg P.
139 | and {Eigenbrot}, Arthur Davis and {Erben}, Thomas and {Ferreira}, Leonardo
140 | and {Foreman-Mackey}, Daniel and {Fox}, Ryan and {Freij}, Nabil and
141 | {Garg}, Suyog and {Geda}, Robel and {Glattly}, Lauren and {Gondhalekar}, Yash
142 | and {Gordon}, Karl D. and {Grant}, David and {Greenfield}, Perry and
143 | {Groener}, Austen M. and {Guest}, Steve and {Gurovich}, Sebastian and
144 | {Handberg}, Rasmus and {Hart}, Akeem and {Hatfield-Dodds}, Zac and
145 | {Homeier}, Derek and {Hosseinzadeh}, Griffin and {Jenness}, Tim and
146 | {Jones}, Craig K. and {Joseph}, Prajwel and {Kalmbach}, J. Bryce and
147 | {Karamehmetoglu}, Emir and {Ka{l}uszy{'n}ski}, Miko{l}aj and
148 | {Kelley}, Michael S.~P. and {Kern}, Nicholas and {Kerzendorf}, Wolfgang E.
149 | and {Koch}, Eric W. and {Kulumani}, Shankar and {Lee}, Antony and {Ly}, Chun
150 | and {Ma}, Zhiyuan and {MacBride}, Conor and {Maljaars}, Jakob M. and
151 | {Muna}, Demitri and {Murphy}, N.~A. and {Norman}, Henrik and {O'Steen}, Richard
152 | and {Oman}, Kyle A. and {Pacifici}, Camilla and {Pascual}, Sergio and
153 | {Pascual-Granado}, J. and {Patil}, Rohit R. and {Perren}, Gabriel I. and
154 | {Pickering}, Timothy E. and {Rastogi}, Tanuj and {Roulston}, Benjamin R.
155 | and {Ryan}, Daniel F. and {Rykoff}, Eli S. and {Sabater}, Jose and
156 | {Sakurikar}, Parikshit and {Salgado}, Jes{'u}s and {Sanghi}, Aniket and
157 | {Saunders}, Nicholas and {Savchenko}, Volodymyr and {Schwardt}, Ludwig and
158 | {Seifert-Eckert}, Michael and {Shih}, Albert Y. and {Jain}, Anany Shrey
159 | and {Shukla}, Gyanendra and {Sick}, Jonathan and {Simpson}, Chris and
160 | {Singanamalla}, Sudheesh and {Singer}, Leo P. and {Singhal}, Jaladh and
161 | {Sinha}, Manodeep and {Sip{H{o}}cz}, Brigitta M. and {Spitler}, Lee R.
162 | and {Stansby}, David and {Streicher}, Ole and {{{S}}umak}, Jani and
163 | {Swinbank}, John D. and {Taranu}, Dan S. and {Tewary}, Nikita and
164 | {Tremblay}, Grant R. and {Val-Borro}, Miguel de and {Van Kooten}, Samuel J.
165 | and {Vasovi{'c}}, Zlatan and {Verma}, Shresth and {de Miranda Cardoso}, Jos{'e} Vin{'i}cius
166 | and {Williams}, Peter K.~G. and {Wilson}, Tom J. and {Winkel}, Benjamin
167 | and {Wood-Vasey}, W.~M. and {Xue}, Rui and {Yoachim}, Peter and {Zhang}, Chen
168 | and {Zonca}, Andrea and {Astropy Project Contributors}
169 | },
170 | title = "{The Astropy Project: Sustaining and Growing a Community-oriented Open-source Project and the Latest Major Release (v5.0) of the Core Package}",
171 | journal = {Astrophysical Journal},
172 | keywords = {Astronomy software, Open source software, Astronomy data analysis, 1855, 1866, 1858, Astrophysics - Instrumentation and Methods for Astrophysics},
173 | year = 2022,
174 | month = aug,
175 | volume = {935},
176 | number = {2},
177 | eid = {167},
178 | pages = {167},
179 | doi = {10.3847/1538-4357/ac7c74},
180 | archivePrefix = {arXiv},
181 | eprint = {2206.14220},
182 | primaryClass = {astro-ph.IM},
183 | adsurl = {https://ui.adsabs.harvard.edu/abs/2022ApJ...935..167A},
184 | adsnote = {Provided by the SAO/NASA Astrophysics Data System}
185 | }
186 |
187 |
188 | @misc{lunarsky,
189 | author = {Adam E. Lanman},
190 | title = {lunarsky: An astropy extension to describe observations from the surface of the Moon.},
191 | year = {2020},
192 | publisher = {GitHub},
193 | journal = {GitHub repository},
194 | url = {https://github.com/aelanman/lunarsky}
195 | }
196 |
197 |
198 | @inproceedings{topcat,
199 | author = {{Taylor}, M.~B.},
200 | title = "{TOPCAT \& STIL: Starlink Table/VOTable Processing Software}",
201 | booktitle = {Astronomical Data Analysis Software and Systems XIV},
202 | year = 2005,
203 | editor = {{Shopbell}, P. and {Britton}, M. and {Ebert}, R.},
204 | series = {Astronomical Society of the Pacific Conference Series},
205 | volume = {347},
206 | month = dec,
207 | pages = {29},
208 | adsurl = {https://ui.adsabs.harvard.edu/abs/2005ASPC..347...29T},
209 | adsnote = {Provided by the SAO/NASA Astrophysics Data System}
210 | }
211 |
212 |
213 | @ARTICLE{healpix,
214 | author = {{G{\'o}rski}, K.~M. and {Hivon}, E. and {Banday}, A.~J. and {Wandelt}, B.~D. and {Hansen}, F.~K. and {Reinecke}, M. and {Bartelmann}, M.},
215 | title = "{HEALPix: A Framework for High-Resolution Discretization and Fast Analysis of Data Distributed on the Sphere}",
216 | journal = {The Astrophysical Journal},
217 | keywords = {Cosmology: Cosmic Microwave Background, Cosmology: Observations, Methods: Statistical, Astrophysics},
218 | year = 2005,
219 | month = apr,
220 | volume = {622},
221 | number = {2},
222 | pages = {759-771},
223 | doi = {10.1086/427976},
224 | archivePrefix = {arXiv},
225 | eprint = {astro-ph/0409513},
226 | primaryClass = {astro-ph},
227 | adsurl = {https://ui.adsabs.harvard.edu/abs/2005ApJ...622..759G},
228 | adsnote = {Provided by the SAO/NASA Astrophysics Data System}
229 | }
230 |
231 | @misc{astropy-healpix,
232 | author = {{Astropy Developers}},
233 | title = {astropy healpix: BSD-licensed HEALPix for Astropy},
234 | year = {2016},
235 | publisher = {GitHub},
236 | journal = {GitHub repository},
237 | url = {https://github.com/astropy/astropy-healpix}
238 | }
239 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [Unreleased]
4 |
5 | ### Changed
6 | - Updated minimum dependency versions: pyuvdata>=3.2.3
7 | - Use `pyuvdata.UVBase._select_along_param_axis` rather than custom logic in
8 | `SkyModel.select`.
9 |
10 | ### Fixed
11 | - Entry point scripts are now properly installed.
12 |
13 | ## [1.1.0] - 2025-06-26
14 |
15 | ### Added
16 | - New option to `SkyModel.select` to select components that do not have NaN
17 | values in the `stokes` parameter at either any or all frequencies.
18 | - New option to `SkyModel.select` to select components with no negative Stokes I
19 | values.
20 | - New check warnings about NaN stokes and spectral index values and negative
21 | Stokes I values.
22 | - An option to skip optional parameters when reading in skyh5 files.
23 |
24 | ### Changed
25 | - Updated minimum dependency versions: h5py>=3.7, python>=3.11, scipy>=1.9
26 | - Converted scripts to entry points to be better aligned with the pyproject.toml
27 | approach.
28 | - Only import lunarsky if needed.
29 | - Use the new pyuvdata analytic short dipole beam for polarized source testing.
30 | - Updated minimum dependency versions: pyuvdata>=3.1.0
31 | - Updated minimum optional dependency versions: lunarsky>=0.2.5
32 |
33 | ### Fixed
34 | - A bug where running check on read was impossible to turn off.
35 |
36 | ## [1.0.1] - 2024-07-01
37 |
38 | ### Changed
39 | - Updated minimum dependency versions: setuptools_scm>=8.1, scipy>=1.8
40 | - pyuvdata utils imports to ensure compatibility with version 3.0.
41 | - `numpy.string_` calls to `numpy.bytes_`, `np.in1d` to `np.isin` and a few other
42 | changes for numpy 2.0 compatibility.
43 |
44 | ### Fixed
45 | - An error when Skymodel.concat was called serially.
46 |
47 | ## [1.0.0] - 2024-05-09
48 |
49 | ### Changed
50 | - Updated minimum dependency versions: astropy>=6.0, h5py>=3.4, numpy>=1.23,
51 | pyuvdata>=2.4.3, scipy>=1.7.3, python>=3.10
52 | - Updated minimum optional dependency versions: astropy-healpix>=1.0.2, lunarsky>=0.2.2
53 |
54 | ## [0.3.1] - 2024-02-22
55 |
56 | ### Added
57 | - A memo describing the SkyH5 format in docs/references.
58 |
59 | ### Changed
60 | - Added support for more types of FHD catalog files and better error messages.
61 | - Added support (and testing) for python 3.12, dropped support for python 3.8
62 | - Moved the optional data-sized arrays (stokes_error and beam_amp) from the Header
63 | datagroup to the Data datagroup in SkyH5 files.
64 | - Added an `extra_columns` attribute to SkyModel, a recarray to store other catalog
65 | information with a value per component.
66 | - Updated the pyuvdata requirement to >= 2.4.1
67 | - Updated the scipy requirement to >= 1.5
68 |
69 | ### Fixed
70 | - A bug where FHD frequencies were interpreted as being in Hz rather than MHz.
71 |
72 | ## [0.3.0] - 2023-04-10
73 |
74 | ### Added
75 | - A `freq_edge_array` parameter to store the top and bottom of the sub-bands with shape
76 | (2, Nfreqs). This will become required for objects with "subband" spectral types in
77 | version 0.5. A `freq_edge_array` parameter was also added to the `read_votable_file`,
78 | `read` and `from_file` methods.
79 |
80 | ### Changed
81 | - When writing to text files, "subband" spectral types are represented in the same
82 | way as "full" spectral types (losing the frequency edge array information).
83 | - Increase the minimum compatible version for lunarsky to 0.2.1 to fix astropy
84 | deprecation warnings.
85 |
86 | ### Deprecated
87 | - Not setting the `freq_edge_array` parameter for subband spectral types.
88 |
89 | ### Removed
90 | - Defaulting for the coordinate frame, `frame` is now required to be passed to
91 | the `__init__` method for Healpix maps and point-like components when passing individual
92 | components rather than a SkyCoord object. It is also required to be passed to the `read_votable_file` method.
93 | - Handling for accessing the deprecated `ra_dec_coherency`, `lon`, `lat` attributes.
94 | - Handling for accessing galactic `l` and `b` coordinates as `gl` and `gb`.
95 | - The `point_to_healpix` method, use the `assign_to_healpix` instead.
96 | - The `frame` parameter to the `assign_to_healpix` method. The frame from the object's
97 | skycoord parameter is now always used for the Healpix frame, use `transform_to` to
98 | convert to the desired frame before calling `assign_to_healpix`.
99 | - The `source_cuts` method and the associated `source_select_keywords` parameters from
100 | the various `read` methods.
101 | - The `to_recarray` and `from_recarray` methods.
102 | - Support for reading old `healvis` style Healpix hdf5 files (via the associated
103 | `read_healpix_hdf5`, `from_healpix_hdf5` and `write_healpix_hdf5` methods). skyh5
104 | files are now the only supported hdf5 file format.
105 | - The `ra_column` and `dec_column` parameters to the `read` and `from_file` methods. Use
106 | the more generic `lon_column` and `lat_column` parameters instead.
107 |
108 |
109 | ## [0.2.0] - 2023-02-01
110 |
111 | ### Added
112 | - Add support for all astropy coordinate frames with the new `skycoord` attribute (which
113 | contains an astropy SkyCoord object) on point objects and the new `hpx_frame` attribute
114 | on healpix objects. Users can interact with the SkyCoord object directly or use
115 | convenience methods on SkyModel as appropriate. References to older SkyModel attributes
116 | are handled for backwards compatibility.
117 | - Read/write methods for text and votable files now support more astropy frames (not
118 | just `icrs`).
119 | - New `lon_column`, `lat_column` and `frame` parameters to the `read` and `from_file`
120 | methods which apply when reading votable files.
121 | - Added `calc_frame_coherency` method to calculate and optionally store the
122 | frame_coherency on the object.
123 |
124 | ### Changed
125 | - Updated the astropy requirement to >= 5.2
126 | - No longer calculate frame_coherency (previously ra_dec_coherency) on SkyModel
127 | initialization to save memory.
128 | - `J2000` in ra and dec columns names of text files are now properly identified as
129 | indicating that the coordinates are in the FK5 frame rather than ICRS.
130 | - Added handling for the new `skycoord` and `hpx_frame` parameters in skyh5 read/write
131 | methods.
132 | - Updated the pyuvdata requirement to >= 2.2.10
133 | - Added support for `allowed_failures` keyword in `SkyModel.__eq__` to match
134 | `pyuvdata.UVBase`, update pyuvdata minimum version to 2.2.1 when that keyword was
135 | introduced.
136 | - Updated the astropy requirement to >= 5.0.4
137 | - Dropped support for python 3.7
138 |
139 | ### Deprecated
140 | - The `ra_dec_coherency` attribute in favor of `frame_coherency` because SkyModel
141 | objects can be in frames that do not use ra/dec coordinates.
142 | - The `lon`, `lat` and `frame` attributes, in favor of the `skycoord` attribute for
143 | point objects and the `hpx_inds` and `hpx_frame` attributes for healpix objects.
144 | - The `ra_column` and `dec_column` parameters in the `read` and `from_file` methods in
145 | favor of the `lon_column` and `lat_column` as a part of supporting more frames in
146 | votable files.
147 | - The `to_recarray` and `from_recarray` methods.
148 |
149 | ### Removed
150 | - Removed support for the older input parameter order to `SkyModel.__init__`.
151 | - Removed support for passing `freq_array`, `reference_freq` or `stokes` to
152 | `SkyModel.__init__` as anything other than appropriate Quantities.
153 | - `set_spectral_type_params` and `read_idl_catalog` methods have been
154 | removed from `SkyModel`
155 | - Removed the `read_healpix_hdf5`, `healpix_to_sky`, `write_healpix_hdf5`,
156 | `skymodel_to_array`, `array_to_skymodel`, `source_cuts`, `read_votable_catalog`,
157 | `read_gleam_catalog`, `read_text_catalog`, `read_idl_catalog`, `write_catalog_to_file`
158 | functions (many similar methods on `SkyModel` remain).
159 | - Removed support for passing telescope_location directly to the
160 | `SkyModel.coherency_calc` method.
161 | - Removed support for votable_files with tables with no name or ID.
162 | - Removed `frequency` column and alias like `flux_density_I` for flux columns in output
163 | of `SkyModel.to_recarray`,
164 |
165 | ## [0.1.3] - 2022-02-22
166 |
167 | ### Added
168 | - Generic `read` and `from_file` methods to SkyModel objects that accept any file type
169 | supported by SkyModel.
170 |
171 | ### Added
172 | - A `filename` attribute to SkyModel objects.
173 | - The `nan_handling` keyword to the `at_frequencies` method to control how NaNs in the
174 | stokes array on subband objects are handled during interpolation.
175 | - The `lat_range` and `lon_range` keywords to the `select` method to support selecting
176 | on fields.
177 | - The `min_brightness`, `max_brightness` and `brightness_freq_range` keywords to the
178 | `select` method to support selecting on brightness.
179 | - The `cut_nonrising` method to remove sources that never rise given a telescope
180 | location, replacing functionality that was in the `source_cuts` method.
181 | - The `calculate_rise_set_lsts` method to calculate and set the `_rise_lst` and
182 | `_set_lst` attributes on the object, replacing functionality that was in the
183 | `source_cuts` method.
184 | - The `get_lon_lat` method which computes the values from `hpx_inds` on healpix objects
185 | and just returns the parameter values on point objects.
186 | - The `assign_to_healpix` method to assign point component objects to a healpix grid
187 | (using the nearest neighbor approach).
188 |
189 | ### Fixed
190 | - A bug that caused the stokes array to be all NaNs after using the `at_frequencies`
191 | method on a subband spectral_type object if any NaNs appeared in the input stokes.
192 |
193 | ### Changed
194 | - `ra` and `dec` are no longer required parameters on healpix objects (since that
195 | information is encoded in the `hpx_inds` parameter). When objects are initialized as
196 | healpix objects the `ra` and `dec` parameters are no longer populated.
197 | - `point_to_healpix` has been renamed to `_point_to_healpix` because it's only intended
198 | to be used internally to undo the `healpix_to_point` method.
199 |
200 | ## Fixed
201 | - A bug in `concat` when optional spectral paramters are not None on one of the objects.
202 |
203 | ### Deprecated
204 | - The `source_cuts` method and the `source_select_kwds` keywords to the reading methods.
205 | - The `point_to_healpix` method.
206 |
207 | ## [0.1.2] - 2021-07-06
208 |
209 | ### Added
210 | - Ability to overwrite default ra/dec when importing a healpix map
211 | - `clobber` keyword to allow overwriting of skyh5 files
212 | - Support for ring / nested healpix ordering.
213 | - New `SkyModel.concat` method to support concatenating catalogs.
214 | - A new optional parameters `stokes_error` to track errors on the fluxes reported by catalogs.
215 |
216 | ### Fixed
217 | - Fix bug in writing skyh5 files with composite stokes units (e.g. Jy/sr)
218 | - Fix bugs causing healpix ordering to not be round tripped properly.
219 | - Some bugs related to writing & reading skyh5 files after converting object using `healpix_to_point` method.
220 |
221 | ## [0.1.1] - 2021-02-17
222 |
223 | ### Added
224 | - Read and write methods for skyh5 -- our newly defined hdf5 file format that fully
225 | supports all SkyModel types.
226 | - Classmethods `from_recarray`, `from_healpix_hdf5`, `from_votable_catalog`,
227 | `from_text_catalog`, `from_gleam_catalog` and `from_idl_catalog` to enable
228 | instantiation from different formats directly.
229 |
230 | ### Changed
231 | - Changed default `spectral_type` for `read_gleam_catalog` to `subband` rather than `flat`.
232 | - Changed `extended_model_group` in `read_fhd_catalog` to match the parent source name.
233 |
234 | ### Fixed
235 | - Improved handling of lists passed to `SkyModel.__init__`.
236 | - A bug in the `download_gleam` utility method that caused missing columns for the
237 | `subband` spectral type
238 | - Enabled subselecting to a given tolerance in at_frequencies (for `full` spectral type).
239 | - A bug in `Skymodel.__init__` that caused extended_model_group and beam_amps
240 | to not be added to the object.
241 | - Enabled proper handling of duplicated source IDs in `read_fhd_catalog`.
242 | - A bug in `read_fhd_catalog` did not order the `name` and `beam_amp` attributes correctly for extended sources.
243 |
244 | ## [0.1.0] - 2020-6-29
245 |
246 | ### Added
247 | - The methods `jansky_to_kelvin` and 'kelvin_to_jansky' to convert between Jy
248 | and K based units.
249 | - The methods `healpix_to_point` and `point_to_healpix` to convert between
250 | component types.
251 |
252 | ### Changed
253 | - Renamed the `read_idl_catalog` method to `read_fhd_catalog`.
254 | - The `stokes` and `coherency_radec` parameters are now Quantity objects and must
255 | have units of 'Jy' or 'K sr' if `component_type` is 'point' and 'Jy/sr' or 'K'
256 | if the `component_type` is 'healpix'.
257 | - The inputs to the utility functions `stokes_to_coherency` and `coherency_to_stokes`
258 | must now be Quantity objects.
259 | - The utility function `jy_to_ksr` now uses astropy's built in conversion methods.
260 |
261 | ### Deprecated
262 | - The `read_idl_catalog` method.
263 | - Initializing a SkyModel object with a float array rather than a Quantity for
264 | `stokes` is deprecated and support will be removed in a future version.
265 | - Passing floats rather than Quantity objects as inputs to the
266 | `stokes_to_coherency` and `coherency_to_stokes` utility functions.
267 |
268 | ## [0.0.2] - 2020-6-4
269 |
270 | ### Added
271 | - Documentation is now hosted on ReadTheDocs at https://pyradiosky.readthedocs.io/en/latest/
272 | - A utility function and a script to download the GLEAM catalog.
273 | - An `at_frequencies` function that will return a new SkyModel at requested frequencies.
274 | - A new `component_type` parameter that can be set to "healpix" or "point".
275 | - Better support for HEALPix maps, with new `nside` and `hpx_inds` parameters.
276 | - Added the following methods to the `SkyModel` class: `select`, `source_cuts`,
277 | `to_recarray`, `from_recarray`, `read_healpix_hdf5`, `read_votable_catalog`,
278 | `read_gleam_catalog`, `read_text_catalog`, `read_idl_catalog`, `write_healpix_hdf5`
279 | `write_text_catalog`.
280 |
281 | ### Fixed
282 | - A bug in `spherical_coords_transforms.rotate_points_3d` where an arccos
283 | calculation failed for a value larger than one by ~1e-12.
284 |
285 | ### Deprecated
286 | - The following functions in the `skymodel` module were deprecated: `read_healpix_hdf5`,
287 | `write_healpix_hdf5`, `healpix_to_sky`, `skymodel_to_array`, `array_to_skymodel`,
288 | `source_cuts`, `read_votable_catalog`, `read_gleam_catalog`, `read_text_catalog`,
289 | `read_idl_catalog`, `write_catalog_to_file`.
290 |
291 | ## [0.0.1] - 2020-4-6
292 |
293 | ### Added
294 | - SkyModel object inherits from UVBase.
295 | - Moved existing code from pyuvsim, got package set up.
296 |
--------------------------------------------------------------------------------
/docs/references/skyh5_memo.tex:
--------------------------------------------------------------------------------
1 | \documentclass[11pt, oneside]{article}
2 | \usepackage{geometry}
3 | \geometry{letterpaper}
4 | \usepackage{graphicx}
5 | \usepackage[titletoc,toc,title]{appendix}
6 | \usepackage{amssymb}
7 | \usepackage{physics}
8 | \usepackage{array}
9 | \usepackage{makecell}
10 |
11 | \usepackage{hyperref}
12 | \hypersetup{
13 | colorlinks = true
14 | }
15 |
16 | \usepackage{cleveref}
17 |
18 | \title{Memo: SkyH5 file format}
19 | \author{Bryna Hazelton, and the pyradiosky team}
20 | \date{October 5, 2023}
21 |
22 | \begin{document}
23 | \maketitle
24 | \tableofcontents
25 | \section{Introduction}
26 | \label{sec:intro}
27 |
28 | This memo introduces a new HDF5\footnote{\url{https://www.hdfgroup.org/}} based
29 | file format of a \texttt{SkyModel} object in
30 | \texttt{pyradiosky}\footnote{\url{https://github.com/RadioAstronomySoftwareGroup/pyradiosky}},
31 | a python package that provides objects and interfaces for representing diffuse,
32 | extended and compact astrophysical radio sources. Here, we describe the required
33 | and optional elements and the structure of this file format, called \textit{SkyH5}.
34 |
35 | We assume that the user has a working knowledge of HDF5 and the associated
36 | python bindings in the package \texttt{h5py}\footnote{\url{https://www.h5py.org/}}, as
37 | well as \texttt{SkyModel} objects in \texttt{pyradiosky}. For more information about
38 | HDF5, please visit \url{https://portal.hdfgroup.org/display/HDF5/HDF5}. For more
39 | information about the parameters present in a \texttt{SkyModel} object, please visit
40 | \url{https://pyradiosky.readthedocs.io/en/latest/skymodel.html}.
41 | Examples of how to interact with \texttt{SkyModel} objects in \texttt{pyradiosky} are
42 | available at \url{http://pyradiosky.readthedocs.io/en/latest/tutorial.html}.
43 |
44 | Note that throughout the documentation, we assume a row-major convention (i.e.,
45 | C-ordering) for the dimension specification of multi-dimensional arrays. For
46 | example, for a two-dimensional array with shape ($N$, $M$), the $M$-dimension is
47 | varying fastest, and is contiguous in memory. This convention is the same as
48 | Python and the underlying C-based HDF5 library. Users of languages with the
49 | opposite column-major convention (i.e., Fortran-ordering, seen also in MATLAB
50 | and Julia) must transpose these axes.
51 |
52 | \section{Overview}
53 | \label{sec:overview}
54 | A SkyH5 object contains data representing catalogs and maps of
55 | astrophysical radio sources, including the associated metadata necessary to interpret
56 | them. A SkyH5 file contains two primary HDF5 groups: the \texttt{Header} group,
57 | which contains the metadata, and the \texttt{Data} group, which contains the Stokes
58 | parameters representing the flux densities or brightness temperatures of the sources
59 | (as well as some optional arrays the same size as the stokes parameters data).
60 | Datasets in the \texttt{Data} group
61 | are can be passed through HDF5's compression
62 | pipeline, to reduce the amount of on-disk space required to store the data.
63 | However, because HDF5 is aware of any compression applied to a dataset, there is
64 | little that the user has to explicitly do when reading data. For users
65 | interested in creating new files, the use of compression is optional in the
66 | SkyH5 format, because the HDF5 file is self-documenting in this regard.
67 |
68 | Many of the datasets in SkyH5 files have units associated with them (represented
69 | as \texttt{astropy Quantity} objects on the \texttt{SkyModel} object).
70 | The units are stored as attributes on the datasets with the name ``unit''.
71 | Datasets that derive from other \texttt{astropy} objects (e.g. \texttt{astropy Time,
72 | astropy EarthLocation, astropy Latitude, astropy Longitude})
73 | also have an ``object\_type'' attribute indicating the object type.
74 |
75 | In the discussion below, we discuss required and optional datasets in the
76 | various groups. We note in parenthesis the corresponding attribute of a \texttt{SkyModel}
77 | object. Note that in nearly all cases, the names are coincident, to make things
78 | as transparent as possible to the user.
79 |
80 | \section{Header}
81 | \label{sec:header}
82 | The \texttt{Header} group of the file contains the metadata necessary to interpret
83 | the data. We begin with the required parameters, then continue to optional
84 | ones. Unless otherwise noted, all datasets are scalars (i.e., not arrays). The
85 | precision of the data type is also not specified as part of the format, because
86 | in general the user is free to set it according to the desired use case (and
87 | HDF5 records the precision and endianness when generating datasets). When using
88 | the standard \texttt{h5py}-based implementation in \texttt{pyradiosky}, this typically
89 | results in 32-bit integers and double precision floating point numbers. Each
90 | entry in the list contains \textbf{(1)} the exact name of the dataset in the
91 | HDF5 file, in boldface, \textbf{(2)} the expected datatype of the dataset, in
92 | italics, \textbf{(3)} a brief description of the data, and \textbf{(4)} the name
93 | of the corresponding attribute on a \texttt{SkyModel} object, italicized and in
94 | parentheses at the end of the entry.
95 |
96 | Note that string datatypes should be handled with care. See
97 | the Appendix in the UVH5 memo
98 | (\url{https://github.com/RadioAstronomySoftwareGroup/pyuvdata/blob/main/docs/references/uvh5_memo.pdf})
99 | for appropriately defining them for interoperability between different HDF5
100 | implementations.
101 |
102 | \subsection{Required Parameters}
103 | \label{sec:req_params}
104 | \begin{itemize}
105 |
106 | \item \textbf{component\_type}: \textit{string} The type of components in the sky model.
107 | The options are: ``healpix'' and ``point''. If component\_type is ``healpix'', the components
108 | are the pixels in a HEALPix map in units compatible with K or Jy/sr. If the
109 | component\_type is ``point'', the components are point-like sources, or point like
110 | components of extended sources, in units compatible with Jy or K sr. Some additional
111 | parameters are required depending on the component type. (\textit{component\_type})
112 |
113 | \item \textbf{Ncomponents}: \textit{int} The number of components in the sky model. This
114 | can be the number of individual compact sources, or it can include components of
115 | extended sources, or the number of pixels in a map. (\textit{Ncomponents})
116 |
117 | \item \textbf{spectral\_type}: \textit{string} This describes the type of spectral model for
118 | the components. The options are:
119 | \begin{enumerate}
120 | \item \textbf{spectral\_index} The convention for the spectral index is
121 | $I=I_0 \frac{f}{f_0}^{\alpha}$, where $I_0$ is the stokes parameter at the
122 | reference\_frequency parameter $f_0$ and $\alpha$ is the spectral\_index
123 | parameter. Note that the spectral index is assumed to apply in the units of the
124 | stokes parameter (i.e. there is no additive factor of 2 applied to convert between
125 | brightness temperature and flux density units). If the spectral model uses a
126 | spectral index, the reference\_frequency and spectral\_index parameters are
127 | required.
128 | \item \textbf{subband} The subband spectral model is used for catalogs with
129 | multiple flux measurements at different frequencies (i.e. GLEAM
130 | \url{https://www.mwatelescope.org/science/galactic-science/gleam/}). For subband
131 | spectral models, the freq\_array and freq\_edge\_array parameters are required
132 | to give the nominal (usually the central) frequency and the top and bottom of
133 | each subband respectively.
134 | \item \textbf{flat} The flat spectral model assumes no spectral flux dependence,
135 | which can be useful for testing. Since the flux is assumed to be the same at all
136 | frequencies it does not require any extra parameters to be defined.
137 | \item \textbf{full} The full spectral model is used for catalogs with flux values at
138 | multiple frequencies that are not expected to have flux correlations as a function
139 | of frequency, so cannot not be interpolated to frequencies not included in the
140 | catalog. This is a good representation for e.g. Epoch of Reionization signal cubes.
141 | For full spectral models, the freq\_array parameter is required to give the frequencies.
142 | \end{enumerate}
143 | (\textit{spectral\_type})
144 |
145 | \item \textbf{Nfreqs}: \textit{int}
146 | Number of frequencies if spectral\_type is ``full'' or ``subband'', 1 otherwise.
147 | (\textit{Nfreqs})
148 |
149 | \item \textbf{history}: \textit{string} The history of the catalog. (\textit{history})
150 | \end{itemize}
151 |
152 |
153 | \subsection{Optional Parameters}
154 | \label{sec:opt_params}
155 | \begin{itemize}
156 | \item \textbf{name}: \textit{string} The name for each component. This is a
157 | one-dimensional array of size (Ncomponents).
158 | Note this is \textbf{required} if the component\_type is ``point''. (\textit{name})
159 |
160 | \item \textbf{skycoord}:
161 | A nested dataset that contains the information to create an \texttt{astropy SkyCoord}
162 | object representing the component positions. Note this is \textbf{required} if the
163 | component\_type is ``point''. The keys must include:
164 | \begin{itemize}
165 | \item \textbf{frame}: \textit{string} The name of the coordinate frame
166 | (e.g. ``icrs'', ``fk5'', ``galactic''). Must be a frame supported by \texttt{astropy}.
167 | Note that only one frame is allowed, which applies to all the components.
168 | \item \textbf{representation\_type}: \textit{string} The representation type, one
169 | of ``spherical'', ``cartesian'' or ``cylindrical''. This sets what the coordinate
170 | names can be. It is most common to set this to ``spherical" and
171 | specify latitudinal and longitudinal coordinates (e.g. \textbf{ra}, \textbf{dec})
172 | and optionally distance coordinates. It is also possible to use other
173 | representations such as cartesian or cylindrical, e.g. for ``cartesian" the
174 | coordinates would be specified in x, y, and z. See the \texttt{astropy SkyCoord}
175 | docs for more details.
176 | \item Coordinate names (e.g. \textbf{ra}, \textbf{dec}, \textbf{alt}, \textbf{az}):
177 | \textit{float} Two or three such components must be present, which ones are
178 | required depend on the frame and representation\_type. These are
179 | one-dimensional arrays of size (Ncomponents).
180 | \end{itemize}
181 | And may include any other attributes accepted as input parameters for an
182 | \texttt{astropy SkyCoord} object (e.g. obstime, equinox, location).
183 | Each of these datasets may have ``unit'' and ``object\_type'' attributes and may
184 | be either a scalar or a one-dimensional array of size (Ncomponents) as
185 | appropriate.
186 | (\textit{skycoord})
187 |
188 | \item \textbf{nside}: \textit{int}
189 | The HEALPix nside parameter. Note this is \textbf{required} if the
190 | component\_type is ``healpix'' and should not be defined otherwise.
191 | (\textit{nside})
192 |
193 | \item \textbf{hpx\_order}: \textit{string}
194 | The HEALPix pixel ordering convention, either ``ring'' or ``nested''.
195 | Note this is \textbf{required} if the component\_type is ``healpix'' and should
196 | not be defined otherwise. (\textit{hpx\_order})
197 |
198 | \item \textbf{hpx\_frame}: A nested dataset that contains the information to
199 | describe an \texttt{astropy} coordinate frame giving the HEALPix coordinate
200 | frame. This is similar to the skycoord dataset described above but it does not
201 | contain the representation\_type or the coordinate names.
202 | Note this is \textbf{required} if the component\_type is ``healpix'' and should
203 | not be defined otherwise. The keys must include:
204 | \begin{itemize}
205 | \item \textbf{frame}: \textit{string} The name of the coordinate frame
206 | (e.g. ``icrs'', ``fk5'', ``galactic''). Must be a frame supported by
207 | \texttt{astropy}.
208 | \end{itemize}
209 | And may include any other scalar attributes accepted as input parameters
210 | for an \texttt{astropy SkyCoord} object (e.g. obstime, equinox, location). Each
211 | of these datasets may have ``unit'' and ``object\_type'' attributes as appropriate.
212 | (\textit{hpx\_frame})
213 |
214 | \item \textbf{hpx\_inds}: \textit{int}
215 | The HEALPix indices for the included components. Does not need to include
216 | all the HEALPix pixels in the map. This is a one-dimensional array of size
217 | (Ncomponents). Note this is \textbf{required} if the component\_type is
218 | ``healpix'' and should not be defined otherwise. (\textit{hpx\_inds})
219 |
220 | \item \textbf{freq\_array}: \textit{float}
221 | Frequency array giving the nominal (or central) frequency in a unit that can be
222 | converted to Hz. Note this is \textbf{required} if the spectral\_type is ``full'' or
223 | ``subband'' and should not be defined otherwise. (\textit{freq\_array})
224 |
225 | \item \textbf{freq\_edge\_array}: \textit{float}
226 | Array giving the frequency band edges in a unit that can be converted to Hz.
227 | This is a two dimensional array with shape (2, Nfreqs). The zeroth index in
228 | the first dimension holds the lower band edge and the first index holds the
229 | upper band edge. Note this is \textbf{required} if the spectral\_type is
230 | ``subband'' and should not be defined otherwise. (\textit{freq\_edge\_array})
231 |
232 | \item \textbf{reference\_frequency}: \textit{float}
233 | Reference frequency giving the frequency at which the flux in the stokes
234 | parameter was measured in a unit that can be converted to Hz. This is a
235 | one-dimensional array of size (Ncomponents). Note this is \textbf{required} if
236 | the spectral\_type is ``spectral\_index'' and should not be defined if the
237 | spectral\_type is ``full'' or ``subband''.
238 | (\textit{reference\_frequency})
239 |
240 | \item \textbf{spectral\_index}: \textit{float}
241 | The spectral index describing the flux evolution with frequency, see details
242 | in the spectral\_type description above. This is a one-dimensional array
243 | of size (Ncomponents). Note this is \textbf{required} if the spectral\_type is
244 | ``spectral\_index'' and should not be defined otherwise.
245 | (\textit{spectral\_index})
246 |
247 | \item \textbf{extended\_model\_group}: \textit{string}
248 | Identifier that groups components of an extended source model.
249 | This is a one-dimensional array of size (Ncomponents), with empty strings
250 | for point sources that are not components of an extended source.
251 | (\textit{extended\_model\_group})
252 | \end{itemize}
253 |
254 | \subsection{Extra Columns}
255 | \label{sec:extra-columns}
256 | \texttt{SkyModel} objects support ``extra columns'', which are additional arbitrary
257 | per-component arrays of metadata that are useful to carryaround with the data but
258 | which are not formally supported as a reserved keyword in the \texttt{Header}. In a
259 | SkyH5 file, extra columns are handled by creating a datagroup called
260 | \textbf{extra\_columns} inside the \texttt{Data} datagroup. When possible, these
261 | quantities should be HDF5 datatypes, to support interoperability between SkyH5
262 | readers. Inside of the extra\_columns datagroup, each extra columns is saved as
263 | a key-value pair using a dataset, where the name of the extra columns is the name
264 | of the dataset and its corresponding array is saved in the dataset. The ``unit'' and
265 | ``object\_type'' HDF5 attributes are used in the same way as for the other header
266 | items (see \ref{sec:overview}), but it is not recommended to use other attribute
267 | names due to the lack of support inside of \texttt{pyradiosky} for ensuring the
268 | attributes are properly saved when reading and writing SkyH5 files.
269 |
270 | \section{Data}
271 | \label{sec:data}
272 | In addition to the \texttt{Data} datagroup in the root namespace, there must be
273 | one called \texttt{Data}. This datagroup saves the Stokes parameters representing
274 | the flux densities or brightness temperatures of the sources and some optional
275 | arrays that are the same size. They are also all expected to be the same shape:
276 | (4, Nfreqs, Ncomponents) where the first dimension indexes the polarization
277 | direction, ordered (I, Q, U, V). The \textbf{stokes} dataset must be present in this
278 | datagroup and it must have a ``unit'' attribute that is equivalent to Jy or K str if the
279 | component\_type is ``point'' or equivalent to Jy/str or K if the component\_type is
280 | ``healpix''. In addition, this datagroup may also contain a \textbf{stokes\_error}
281 | dataset that gives the standard error on the stokes values and should have the
282 | same ``unit'' attribute as the stokes dataset and a \textbf{beam\_amp} dataset
283 | that gives the beam amplitude at the source position for the instrument that made
284 | the measurement.
285 |
286 | \end{document}
287 |
--------------------------------------------------------------------------------
/src/pyradiosky/data/single_source_old.vot:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 | GLEAM first year observing parameters
9 |
10 |
11 |
12 | Observation date
13 |
14 |
15 |
16 |
17 |
18 | RA range
19 |
20 |
21 |
22 |
23 | RA range
24 |
25 |
26 |
27 |
28 | Declination
29 |
30 |
31 |
32 |
33 | Number of flagged tiles out of the 128 available
34 |
35 |
36 |
37 |
38 | Calibrator (1)
39 |
40 |
41 |
42 |
43 |
44 | | 2013-08-09 |
45 | 19.5 |
46 | 3.5 |
47 | -55 |
48 | 10 |
49 | 3C444 |
50 |
51 |
52 | | 2013-08-10 |
53 | 19.5 |
54 | 3.5 |
55 | -27 |
56 | 4 |
57 | 3C444 |
58 |
59 |
60 | | 2013-08-18 |
61 | 19.5 |
62 | 3.5 |
63 | -72 |
64 | 7 |
65 | Pictor A |
66 |
67 |
68 | | 2013-08-22 |
69 | 19.5 |
70 | 3.5 |
71 | -13 |
72 | 3 |
73 | 3C444 |
74 |
75 |
76 | | 2013-08-25 |
77 | 19.5 |
78 | 3.5 |
79 | -40 |
80 | 5 |
81 | 3C444 |
82 |
83 |
84 | | 2013-11-05 |
85 | 0.0 |
86 | 8.0 |
87 | -13 |
88 | 4 |
89 | Hydra A |
90 |
91 |
92 | | 2013-11-06 |
93 | 0.0 |
94 | 8.0 |
95 | -40 |
96 | 4 |
97 | Hydra A |
98 |
99 |
100 | | 2013-11-07 |
101 | 0.0 |
102 | 8.0 |
103 | 2 |
104 | 4 |
105 | Hydra A |
106 |
107 |
108 | | 2013-11-08 |
109 | 0.0 |
110 | 8.0 |
111 | -55 |
112 | 4 |
113 | Hydra A |
114 |
115 |
116 | | 2013-11-11 |
117 | 0.0 |
118 | 8.0 |
119 | 19 |
120 | 4 |
121 | Hydra A |
122 |
123 |
124 | | 2013-11-12 |
125 | 0.0 |
126 | 8.0 |
127 | -72 |
128 | 4 |
129 | Hydra A |
130 |
131 |
132 | | 2013-11-25 |
133 | 0.0 |
134 | 8.0 |
135 | -27 |
136 | 4 |
137 | Hydra A |
138 |
139 |
140 | | 2014-03-03 |
141 | 6.0 |
142 | 16.0 |
143 | -27 |
144 | 4 |
145 | Hydra A |
146 |
147 |
148 | | 2014-03-04 |
149 | 6.0 |
150 | 16.0 |
151 | -13 |
152 | 4 |
153 | Hydra A |
154 |
155 |
156 | | 2014-03-06 |
157 | 6.0 |
158 | 16.0 |
159 | 2 |
160 | 4 |
161 | Hydra A |
162 |
163 |
164 | | 2014-03-08 |
165 | 6.0 |
166 | 16.0 |
167 | 19 |
168 | 4 |
169 | Hydra A |
170 |
171 |
172 | | 2014-03-09 |
173 | 6.0 |
174 | 16.0 |
175 | -72 |
176 | 4 |
177 | Virgo A |
178 |
179 |
180 | | 2014-03-16 |
181 | 6.0 |
182 | 16.0 |
183 | -40 |
184 | 4 |
185 | Virgo A |
186 |
187 |
188 | | 2014-03-17 |
189 | 6.0 |
190 | 16.0 |
191 | -55 |
192 | 4 |
193 | Hydra A |
194 |
195 |
196 | | 2014-06-09 |
197 | 12.0 |
198 | 22.0 |
199 | -27 |
200 | 8 |
201 | 3C444 |
202 |
203 |
204 | | 2014-06-10 |
205 | 12.0 |
206 | 22.0 |
207 | -40 |
208 | 8 |
209 | 3C444 |
210 |
211 |
212 | | 2014-06-11 |
213 | 12.0 |
214 | 22.0 |
215 | 2 |
216 | 8 |
217 | Hercules A |
218 |
219 |
220 | | 2014-06-12 |
221 | 12.0 |
222 | 18.5 |
223 | -55 |
224 | 8 |
225 | 3C444 |
226 |
227 |
228 | | 2014-06-13 |
229 | 12.0 |
230 | 19.0 |
231 | -13 |
232 | 8 |
233 | Centaurus A |
234 |
235 |
236 | | 2014-06-14 |
237 | 12.0 |
238 | 22.0 |
239 | -72 |
240 | 9 |
241 | Hercules A |
242 |
243 |
244 | | 2014-06-15 |
245 | 12.0 |
246 | 22.0 |
247 | 18 |
248 | 13 |
249 | Virgo A |
250 |
251 |
252 | | 2014-06-16 |
253 | 18.5 |
254 | 22.0 |
255 | -13 |
256 | 8 |
257 | 3C444 |
258 |
259 |
260 | | 2014-06-18 |
261 | 18.5 |
262 | 22.0 |
263 | -55 |
264 | 8 |
265 | 3C444 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 | Right ascension (FK5, Equinox=J2000.0) (computed by VizieR, not
274 | part of the original data. The format may include more digits
275 | than the original data because of internal accuracy requirements
276 | in VizieR and across other CDS services)
277 |
278 |
279 |
280 |
281 | Declination (FK5, Equinox=J2000.0) (computed by VizieR, not part
282 | of the original data. The format may include more digits than the
283 | original data because of internal accuracy requirements in VizieR
284 | and across other CDS services)
285 |
286 |
287 |
288 |
289 | GLEAM name (JHHMMSS+DDMMSS) (Name)
290 |
291 |
292 |
293 |
294 | Right Ascension J2000 (RAJ2000)
295 |
296 |
297 |
298 |
299 | Declination J2000 (DEJ2000)
300 |
301 |
302 |
303 |
304 | Peak flux in wide (170-231MHz) image (peak_flux_wide)
305 |
306 |
307 |
308 |
309 | Integrated flux in wide (170-231MHz) image (int_flux_wide)
310 |
311 |
312 |
313 |
314 | Percent error in absolute flux scale - all frequencies
315 | (err_abs_flux_pct)
316 |
317 |
318 |
319 |
320 | Percent error on internal flux scale - all frequencies
321 | (err_fit_flux_pct)
322 |
323 |
324 |
325 |
326 | ? Peak flux in 072-080MHz image (peak_flux_076)
327 |
328 |
329 |
330 |
331 |
332 | ? Integrated flux in 072-080MHz image (int_flux_076)
333 |
334 |
335 |
336 |
337 |
338 | ? Peak flux in 080-088MHz image (peak_flux_084)
339 |
340 |
341 |
342 |
343 |
344 | ? Integrated flux in 080-088MHz image (int_flux_084)
345 |
346 |
347 |
348 |
349 |
350 | ? Peak flux in 088-095MHz image (peak_flux_092)
351 |
352 |
353 |
354 |
355 |
356 | ? Integrated flux in 088-095MHz image (int_flux_092)
357 |
358 |
359 |
360 |
361 |
362 | ? Peak flux in 095-103MHz image (peak_flux_099)
363 |
364 |
365 |
366 |
367 |
368 | ? Integrated flux in 095-103MHz image (int_flux_099)
369 |
370 |
371 |
372 |
373 |
374 | ? Peak flux in 103-111MHz image (peak_flux_107)
375 |
376 |
377 |
378 |
379 |
380 | ? Integrated flux in 103-111MHz image (int_flux_107)
381 |
382 |
383 |
384 |
385 |
386 | ? Peak flux in 111-118MHz image (peak_flux_115)
387 |
388 |
389 |
390 |
391 |
392 | ? Integrated flux in 111-118MHz image (int_flux_115)
393 |
394 |
395 |
396 |
397 |
398 | ? Peak flux in 118-126MHz image (peak_flux_122)
399 |
400 |
401 |
402 |
403 |
404 | ? Integrated flux in 118-126MHz image (int_flux_122)
405 |
406 |
407 |
408 |
409 |
410 | ? Peak flux in 126-134MHz image (peak_flux_130)
411 |
412 |
413 |
414 |
415 |
416 | ? Integrated flux in 126-134MHz image (int_flux_130)
417 |
418 |
419 |
420 |
421 |
422 | ? Peak flux in 139-147MHz image (peak_flux_143)
423 |
424 |
425 |
426 |
427 |
428 | ? Integrated flux in 139-147MHz image (int_flux_143)
429 |
430 |
431 |
432 |
433 |
434 | ? Peak flux in 147-154MHz image (peak_flux_151)
435 |
436 |
437 |
438 |
439 |
440 | ? Integrated flux in 147-154MHz image (int_flux_151)
441 |
442 |
443 |
444 |
445 |
446 | ? Peak flux in 154-162MHz image (peak_flux_158)
447 |
448 |
449 |
450 |
451 |
452 | ? Integrated flux in 154-162MHz image (int_flux_158)
453 |
454 |
455 |
456 |
457 |
458 | ? Peak flux in 162-170MHz image (peak_flux_166)
459 |
460 |
461 |
462 |
463 |
464 | ? Integrated flux in 162-170MHz image (int_flux_166)
465 |
466 |
467 |
468 |
469 |
470 | ? Peak flux in 170-177MHz image (peak_flux_174)
471 |
472 |
473 |
474 |
475 |
476 | ? Integrated flux in 170-177MHz image (int_flux_174)
477 |
478 |
479 |
480 |
481 |
482 | ? Peak flux in 177-185MHz image (peak_flux_181)
483 |
484 |
485 |
486 |
487 |
488 | ? Integrated flux in 177-185MHz image (int_flux_181)
489 |
490 |
491 |
492 |
493 |
494 | ? Peak flux in 185-193MHz image (peak_flux_189)
495 |
496 |
497 |
498 |
499 |
500 | ? Integrated flux in 185-193MHz image (int_flux_189)
501 |
502 |
503 |
504 |
505 |
506 | ? Peak flux in 193-200MHz image (peak_flux_197)
507 |
508 |
509 |
510 |
511 |
512 | ? Integrated flux in 193-200MHz image (int_flux_197)
513 |
514 |
515 |
516 |
517 |
518 | ? Peak flux in 200-208MHz image (peak_flux_204)
519 |
520 |
521 |
522 |
523 |
524 | ? Integrated flux in 200-208MHz image (int_flux_204)
525 |
526 |
527 |
528 |
529 |
530 | ? Peak flux in 208-216MHz image (peak_flux_212)
531 |
532 |
533 |
534 |
535 |
536 | ? Integrated flux in 208-216MHz image (int_flux_212)
537 |
538 |
539 |
540 |
541 |
542 | ? Peak flux in 216-223MHz image (peak_flux_220)
543 |
544 |
545 |
546 |
547 |
548 | ? Integrated flux in 216-223MHz image (int_flux_220)
549 |
550 |
551 |
552 |
553 |
554 | ? Peak flux in 223-231MHz image (peak_flux_227)
555 |
556 |
557 |
558 |
559 |
560 | ? Integrated flux in 223-231MHz image (int_flux_227)
561 |
562 |
563 |
564 |
565 |
566 | ? Fitted spectral index (alpha)
567 |
568 |
569 |
570 |
571 |
572 | ? Fitted 200MHz integrated flux density (int_flux_fit_200)
573 |
574 |
575 |
576 |
577 |
578 |
579 | | 0.00000000 |
580 | 0.0000000 |
581 | TEST |
582 | 0.000000 |
583 | -30.000000 |
584 | 0.000000 |
585 | 1.000000 |
586 | 16959 |
587 | 63 |
588 | 0.000000 |
589 | 0.000000 |
590 | 0.000000 |
591 | 0.000000 |
592 | 0.000000 |
593 | 0.000000 |
594 | 0.000000 |
595 | 0.000000 |
596 | 0.000000 |
597 | 0.000000 |
598 | 0.000000 |
599 | 0.000000 |
600 | 0.000000 |
601 | 0.000000 |
602 | 0.000000 |
603 | 0.000000 |
604 | 0.000000 |
605 | 0.000000 |
606 | 0.000000 |
607 | 0.000000 |
608 | 0.000000 |
609 | 0.000000 |
610 | 0.000000 |
611 | 0.000000 |
612 | 0.000000 |
613 | 0.000000 |
614 | 0.000000 |
615 | 0.000000 |
616 | 0.000000 |
617 | 0.000000 |
618 | 0.000000 |
619 | 0.000000 |
620 | 0.000000 |
621 | 0.000000 |
622 | 0.000000 |
623 | 0.000000 |
624 | 0.000000 |
625 | 0.000000 |
626 | 0.000000 |
627 | 0.000000 |
628 | 0.000000 |
629 | 0.000000 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
--------------------------------------------------------------------------------