diff --git a/.gitignore b/.gitignore index 30010a7..51b262d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,11 @@ +# docs +docs/_build +docs/api +!docs/_static/*.png + +pyvisgen/_version_cache.py +pyvisgen/_version.py + #slurm *.slurm *.log diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..5b16ce9 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,18 @@ +version: 2 + +build: + os: ubuntu-24.04 + apt_packages: + - graphviz + tools: + python: "3.10" + +python: + install: + - method: pip + path: . + extra_requirements: + - docs + +sphinx: + configuration: docs/conf.py diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/_static/favicon/favicon.ico b/docs/_static/favicon/favicon.ico new file mode 100755 index 0000000..efcd29d Binary files /dev/null and b/docs/_static/favicon/favicon.ico differ diff --git a/docs/_static/pyvisgen.css b/docs/_static/pyvisgen.css new file mode 100644 index 0000000..eadf40b --- /dev/null +++ b/docs/_static/pyvisgen.css @@ -0,0 +1,221 @@ +@import url('https://fonts.googleapis.com/css?family=Open+Sans:700|Open+Sans:400'); +@import url('https://fonts.googleapis.com/css2?family=Fira+Mono:wght@400;500;700&display=swap'); + + +:root[data-theme="light"] { + --pst-color-text: #090704; + --pst-color-background: #f5f5f5; + --pst-color-on-background: #ffffff; + --pst-color-primary: #52ba66; + --pst-color-secondary: #325e8d; + --pst-color-primary-highlight: #6EC87F; + --pst-color-secondary-highlight: #547cae; + --pst-color-inline-code: var(--pst-color-primary-highlight); + --bd-header-announcement-color: var(--pst-color-background); + --bd-header-announcement-background: var(--pst-color-primary); + --table-hover-background: var(--pst-color-primary-highlight); + --hl-pre-code-background: var(--pst-color-background); +} +:root[data-theme="dark"] { + --pst-color-text: #faf8f4; + --pst-color-background: #181f28; + --pst-color-on-background: #222832; + --pst-color-primary: #52ba66; + --pst-color-secondary: #b8dc2e; + --pst-color-primary-highlight: #83c39c; + --pst-color-secondary-highlight: #ece520; + --pst-color-inline-code: var(--pst-color-primary-highlight); + --bd-header-announcement-color: var(--pst-color-background); + --bd-header-announcement-background: var(--pst-color-primary-highlight); + --table-hover-background: #279544; + --hl-pre-code-background: var(--pst-color-background); +} + + +body { + font-family: 'Open Sans'; + font-weight: 400; +} + +:root { + --pst-font-family-monospace: "Fira Mono"; +} + + +h1, h2, h3, h4, h5 { +font-family: 'Open Sans'; + font-weight: 700; +} + +html {font-size: 100%;} /* 16px */ + +h1 { + font-size: 2.489rem; + background-image: -webkit-gradient( + linear, + left top, + right top, + color-stop(0.03, var(--pst-color-primary)), + color-stop(0.40, var(--pst-color-secondary)), + color-stop(0.97, var(--pst-color-primary-highlight)) +); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} + +h1 .docutils { + -webkit-text-fill-color: var(--pst-color-inline-code); +} + +h2 {font-size: 2.074rem; /* 33.12px */} + +h3 {font-size: 1.728rem; /* 27.68px */} + +h4 {font-size: 1.440rem; /* 23.04px */} + +h5 {font-size: 1.200rem; /* 19.2px */} + +small {font-size: 0.833rem; /* 13.28px */} + +dt:target { + background-color: var(--pst-color-surface); + border-radius: 10px; + padding: 5px 5px 5px 5px; +} + +/* code blocks */ +div.highlight { + background-color: var(--hl-pre-code-background) !important; +} +.highlight pre { + border-radius: 20px; + background-color: var(--pst-color-on-background); +} +.highlight pre .gp { + color: var(--pst-color-primary); +} + +.bd-header-announcement { + color: var(--bd-header-announcement-color); + background-color: var(--bd-header-announcement-background); +} + +.admonition { + border-radius: 10px !important; +} + +/* sphinx-design */ +.sd-card { + background-color: var(--pst-color-on-background); + border-radius: 10px; + padding: 30px 10px 20px 10px; + margin: 10px 0px; +} + +.sd-card .sd-card-header .sd-card-text { + margin: 0px; +} + +.sd-card .sd-card-header { + border: none; + text-align: center; + font-size: var(--pst-font-size-h4); + font-weight: bold; + padding: 0.5rem 0rem 0.5rem 0rem; +} + +.sd-card .sd-card-footer { + border: none; +} + +.sd-card .sd-card-footer .sd-card-text { + max-width: 220px; + margin-left: auto; + margin-right: auto; +} + +.sd-card .sd-btn { + border-radius: 20px; +} + +.sd-card:hover { + border-color: var(--pst-color-secondary); + transform: scale(1.05); + -webkit-transition: all .2s; + -moz-transition: all .2s; + transition: all .2s; +} + +.sd-card .sd-btn:hover { + -webkit-animation: pulse 2s infinite; + animation: pulse512 1.5s infinite; + background-size: 200% auto; +} + +.search-button-field:hover { + border-color: var(--pst-color-primary-highlight); + border-width: 2pt; + -webkit-animation: pulse 2s infinite; + animation: pulse512 1.5s infinite; + background-size: 200% auto; +} + +@keyframes pulse512 { + 0% { + box-shadow: 0 0 0 0 var(--pst-color-primary-highlight); + } + + 70% { + box-shadow: 0 0 0 10px rgb(255 255 255 / 0%); + } + + 100% { + box-shadow: 0 0 0 0 rgb(255 255 255 / 0%); + } +} + +/* sphinx gallery */ +.sphx-glr-thumbcontainer { + border-radius: 10px; + transition: 0.3s; + border-color: var(--pst-color-primary); +} + +.sphx-glr-thumbcontainer:hover { + border-color: var(--pst-color-secondary); + transform: scale(1.05); +} + +.sphx-glr-thumbcontainer::after { + border-radius: 10px !important; +} + +.reference.download.internal { + --pst-color-inline-code-links: white; + background-color: var(--pst-color-primary); + background-image: none !important; + border-radius: 40px; +} + +.reference.download.internal::before { + color: white; + text-decoration: none; +} + +.reference.download.internal:hover { + background-color: var(--pst-color-secondary) !important; + transition: 0.3s; + -webkit-animation: pulse 2s infinite; + animation: pulse512 1.5s infinite; + background-size: 200% auto; +} + +/* tables */ +.table tbody tr:hover td, .table tbody tr:hover th { + background: var(--table-hover-background) !important; +} + +/* buttons */ +#pst-back-to-top: { + background-color: var(--pst-color-secondary-highlight); +} diff --git a/docs/_static/pyvisgen.webp b/docs/_static/pyvisgen.webp new file mode 100755 index 0000000..58582e6 Binary files /dev/null and b/docs/_static/pyvisgen.webp differ diff --git a/docs/_static/pyvisgen_dark.webp b/docs/_static/pyvisgen_dark.webp new file mode 100755 index 0000000..f95e7e4 Binary files /dev/null and b/docs/_static/pyvisgen_dark.webp differ diff --git a/docs/_templates/base.rst b/docs/_templates/base.rst new file mode 100644 index 0000000..1621719 --- /dev/null +++ b/docs/_templates/base.rst @@ -0,0 +1,2 @@ +{% extends "autosummary_core/base.rst" %} +{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} diff --git a/docs/_templates/class.rst b/docs/_templates/class.rst new file mode 100644 index 0000000..0fa59f2 --- /dev/null +++ b/docs/_templates/class.rst @@ -0,0 +1,2 @@ +{% extends "autosummary_core/class.rst" %} +{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} diff --git a/docs/_templates/module.rst b/docs/_templates/module.rst new file mode 100644 index 0000000..230cd6e --- /dev/null +++ b/docs/_templates/module.rst @@ -0,0 +1,2 @@ +{% extends "autosummary_core/module.rst" %} +{# The template this is inherited from is in astropy/sphinx/ext/templates/autosummary_core. If you want to modify this template, it is strongly recommended that you still inherit from the astropy template. #} diff --git a/docs/api-reference/fits/data.rst b/docs/api-reference/fits/data.rst new file mode 100644 index 0000000..6d9ae7a --- /dev/null +++ b/docs/api-reference/fits/data.rst @@ -0,0 +1,16 @@ +.. _data: + +******************************** +Data (:mod:`pyvisgen.fits.data`) +******************************** + +.. currentmodule:: pyvisgen.fits.data + +Data submodule of :mod:`pyvisgen.fits`. + + +Reference/API +============= + +.. automodapi:: pyvisgen.fits.data + :inherited-members: diff --git a/docs/api-reference/fits/index.rst b/docs/api-reference/fits/index.rst new file mode 100644 index 0000000..de2e08e --- /dev/null +++ b/docs/api-reference/fits/index.rst @@ -0,0 +1,31 @@ +.. _fits: + +*********************************** +FITS Handler (:mod:`pyvisgen.fits`) +*********************************** + +.. currentmodule:: pyvisgen.fits + + +Introduction +============ + +This module include all the functions and classes needed for handling FITS files. + + +Submodules +========== + +.. toctree:: + :maxdepth: 1 + :glob: + + data + writer + + +Reference/API +============= + +.. automodapi:: pyvisgen.fits + :no-inheritance-diagram: diff --git a/docs/api-reference/fits/writer.rst b/docs/api-reference/fits/writer.rst new file mode 100644 index 0000000..931673d --- /dev/null +++ b/docs/api-reference/fits/writer.rst @@ -0,0 +1,16 @@ +.. _writer: + +***************************************** +FITS Writer (:mod:`pyvisgen.fits.writer`) +***************************************** + +.. currentmodule:: pyvisgen.fits.writer + +FITS writer submodule of :mod:`pyvisgen.fits`. + + +Reference/API +============= + +.. automodapi:: pyvisgen.fits.writer + :inherited-members: diff --git a/docs/api-reference/gridding/index.rst b/docs/api-reference/gridding/index.rst new file mode 100644 index 0000000..ba07831 --- /dev/null +++ b/docs/api-reference/gridding/index.rst @@ -0,0 +1,16 @@ +.. _gridding: + +*********************************** +Gridding (:mod:`pyvisgen.gridding`) +*********************************** + +.. currentmodule:: pyvisgen.gridding + +Gridding module of pyvisgen. + + +Reference/API +============= + +.. automodapi:: pyvisgen.gridding + :inherited-members: diff --git a/docs/api-reference/index.rst b/docs/api-reference/index.rst new file mode 100644 index 0000000..f22811f --- /dev/null +++ b/docs/api-reference/index.rst @@ -0,0 +1,9 @@ +************* +API Reference +************* + +.. toctree:: + :maxdepth: 1 + :glob: + + */index diff --git a/docs/api-reference/layouts/index.rst b/docs/api-reference/layouts/index.rst new file mode 100644 index 0000000..3fc719b --- /dev/null +++ b/docs/api-reference/layouts/index.rst @@ -0,0 +1,16 @@ +.. _layouts: + +********************************* +Layouts (:mod:`pyvisgen.layouts`) +********************************* + +.. currentmodule:: pyvisgen.layouts + +Layouts module of pyvisgen. + + +Reference/API +============= + +.. automodapi:: pyvisgen.layouts + :inherited-members: diff --git a/docs/api-reference/simulation/array.rst b/docs/api-reference/simulation/array.rst new file mode 100644 index 0000000..d71723b --- /dev/null +++ b/docs/api-reference/simulation/array.rst @@ -0,0 +1,16 @@ +.. array: + +**************************************** +Array (:mod:`pyvisgen.simulation.array`) +**************************************** + +.. currentmodule:: pyvisgen.simulation.array + +Scan submodule of :mod:`pyvisgen.simulation`. + + +Reference/API +============= + +.. automodapi:: pyvisgen.simulation.array + :inherited-members: diff --git a/docs/api-reference/simulation/data_set.rst b/docs/api-reference/simulation/data_set.rst new file mode 100644 index 0000000..741a63f --- /dev/null +++ b/docs/api-reference/simulation/data_set.rst @@ -0,0 +1,16 @@ +.. _data_set: + +********************************************** +Data Set (:mod:`pyvisgen.simulation.data_set`) +********************************************** + +.. currentmodule:: pyvisgen.simulation.visibility + +Data set submodule of :mod:`pyvisgen.simulation`. + + +Reference/API +============= + +.. automodapi:: pyvisgen.simulation.data_set + :inherited-members: diff --git a/docs/api-reference/simulation/index.rst b/docs/api-reference/simulation/index.rst new file mode 100644 index 0000000..837daf6 --- /dev/null +++ b/docs/api-reference/simulation/index.rst @@ -0,0 +1,34 @@ +.. _simulation: + +*************************************** +Simulation (:mod:`pyvisgen.simulation`) +*************************************** + +.. currentmodule:: pyvisgen.simulation + + +Introduction +============ + +This module include all the functions and classes needed for the simulation of an observation. + + +Submodules +========== + +.. toctree:: + :maxdepth: 1 + :glob: + + array + data_set + observation + scan + visibility + + +Reference/API +============= + +.. automodapi:: pyvisgen.simulation + :no-inheritance-diagram: diff --git a/docs/api-reference/simulation/observation.rst b/docs/api-reference/simulation/observation.rst new file mode 100644 index 0000000..bda3eed --- /dev/null +++ b/docs/api-reference/simulation/observation.rst @@ -0,0 +1,16 @@ +.. _observation: + +**************************************************** +Observation (:mod:`pyvisgen.simulation.observation`) +**************************************************** + +.. currentmodule:: pyvisgen.simulation.observation + +Observation submodule of pyvisgen. + + +Reference/API +============= + +.. automodapi:: pyvisgen.simulation.observation + :inherited-members: diff --git a/docs/api-reference/simulation/scan.rst b/docs/api-reference/simulation/scan.rst new file mode 100644 index 0000000..97ad498 --- /dev/null +++ b/docs/api-reference/simulation/scan.rst @@ -0,0 +1,16 @@ +.. scan: + +************************************** +Scan (:mod:`pyvisgen.simulation.scan`) +************************************** + +.. currentmodule:: pyvisgen.simulation.scan + +Scan submodule of :mod:`pyvisgen.simulation`. + + +Reference/API +============= + +.. automodapi:: pyvisgen.simulation.scan + :inherited-members: diff --git a/docs/api-reference/simulation/visibility.rst b/docs/api-reference/simulation/visibility.rst new file mode 100644 index 0000000..25a0474 --- /dev/null +++ b/docs/api-reference/simulation/visibility.rst @@ -0,0 +1,16 @@ +.. _visibility: + +************************************************** +Visibility (:mod:`pyvisgen.simulation.visibility`) +************************************************** + +.. currentmodule:: pyvisgen.simulation.visibility + +Visibility simulation submodule of :mod:`pyvisgen.simulation`. + + +Reference/API +============= + +.. automodapi:: pyvisgen.simulation.visibility + :inherited-members: diff --git a/docs/api-reference/utils/config.rst b/docs/api-reference/utils/config.rst new file mode 100644 index 0000000..4d66dc4 --- /dev/null +++ b/docs/api-reference/utils/config.rst @@ -0,0 +1,16 @@ +.. _utils_config: + +************************************* +Config (:mod:`pyvisgen.utils.config`) +************************************* + +.. currentmodule:: pyvisgen.utils.config + +Config utility submodule of :mod:`pyvisgen.utils`. + + +Reference/API +============= + +.. automodapi:: pyvisgen.utils.config + :inherited-members: diff --git a/docs/api-reference/utils/data.rst b/docs/api-reference/utils/data.rst new file mode 100644 index 0000000..a7bf249 --- /dev/null +++ b/docs/api-reference/utils/data.rst @@ -0,0 +1,16 @@ +.. _utils_data: + +********************************* +Data (:mod:`pyvisgen.utils.data`) +********************************* + +.. currentmodule:: pyvisgen.utils.data + +Data utility submodule of :mod:`pyvisgen.utils`. + + +Reference/API +============= + +.. automodapi:: pyvisgen.utils.data + :inherited-members: diff --git a/docs/api-reference/utils/index.rst b/docs/api-reference/utils/index.rst new file mode 100644 index 0000000..915bbdb --- /dev/null +++ b/docs/api-reference/utils/index.rst @@ -0,0 +1,31 @@ +.. _utils: + +***************************** +Utils (:mod:`pyvisgen.utils`) +***************************** + +.. currentmodule:: pyvisgen.utils + + +Introduction +============ + +This module contains utility functions. + + +Submodules +========== + +.. toctree:: + :maxdepth: 1 + :glob: + + config + data + + +Reference/API +============= + +.. automodapi:: pyvisgen.utils + :no-inheritance-diagram: diff --git a/docs/changelog.rst b/docs/changelog.rst new file mode 100644 index 0000000..2120f07 --- /dev/null +++ b/docs/changelog.rst @@ -0,0 +1,5 @@ +********** +Change Log +********** + +.. include:: ../CHANGES.rst diff --git a/docs/changes/47.feature.rst b/docs/changes/47.feature.rst new file mode 100644 index 0000000..cf47768 --- /dev/null +++ b/docs/changes/47.feature.rst @@ -0,0 +1 @@ +- Add docs diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..2c0f945 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +import datetime +import sys +from pathlib import Path + +import pyvisgen + +if sys.version_info < (3, 11): + import tomli as tomllib +else: + import tomllib + +pyproject_path = Path(__file__).parent.parent / "pyproject.toml" +pyproject = tomllib.loads(pyproject_path.read_text()) + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.intersphinx", + "sphinx.ext.todo", + "sphinx.ext.coverage", + "sphinx.ext.viewcode", + "sphinx_automodapi.automodapi", + "sphinx_automodapi.smart_resolver", + "matplotlib.sphinxext.plot_directive", + "numpydoc", + "sphinx_design", + "IPython.sphinxext.ipython_console_highlighting", + "sphinx_copybutton", +] + +numpydoc_show_class_members = False +numpydoc_class_members_toctree = False + +templates_path = ["_templates"] +exclude_patterns = [ + "_build", + "Thumbs.db", + ".DS_Store", + "changes", + "*.log", +] + +source_suffix = ".rst" +master_doc = "index" + + +project = pyproject["project"]["name"] +author = pyproject["project"]["authors"][0]["name"] +copyright = "{}. Last updated {}".format( + author, datetime.datetime.now().strftime("%d %b %Y %H:%M") +) +python_requires = pyproject["project"]["requires-python"] + +# make some variables available to each page +rst_epilog = f""" +.. |python_requires| replace:: {python_requires} +""" + + +version = pyvisgen.__version__ +# The full version, including alpha/beta/rc tags. +release = version + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = "pydata_sphinx_theme" +html_static_path = ["_static"] + +html_file_suffix = ".html" + +html_css_files = ["pyvisgen.css"] + +html_favicon = "_static/favicon/favicon.ico" + +html_theme_options = { + "github_url": "https://github.com/radionets-project/pyvisgen", + "header_links_before_dropdown": 5, + "navbar_start": ["navbar-logo"], + "navigation_with_keys": False, + # "use_edit_page_button": True, + "icon_links_label": "Quick Links", + "icon_links": [ + { + "name": "Radionets Project", + "url": "https://github.com/radionets-project", + "type": "url", + "icon": "https://avatars.githubusercontent.com/u/77392854?s=200&v=4", # noqa: E501 + }, + ], + "logo": { + "image_light": "_static/pyvisgen.webp", + "image_dark": "_static/pyvisgen_dark.webp", + "alt_text": "pyvisgen", + }, + "announcement": """ +

pyvisgen is not stable yet, so expect large and rapid + changes to structure and functionality as we explore various + design choices before the 1.0 release.

+ """, +} + +html_title = f"{project}: Visibility Simulations in Python" +htmlhelp_basename = project + "docs" + + +# Configuration for intersphinx +intersphinx_mapping = { + "astropy": ("https://docs.astropy.org/en/stable", None), + "ipywidgets": ("https://ipywidgets.readthedocs.io/en/stable", None), + "joblib": ("https://joblib.readthedocs.io/en/stable", None), + "matplotlib": ("https://matplotlib.org/stable", None), + "numba": ("https://numba.readthedocs.io/en/stable", None), + "numpy": ("https://numpy.org/doc/stable", None), + "pandas": ("https://pandas.pydata.org/pandas-docs/stable", None), + "pytest": ("https://docs.pytest.org/en/stable", None), + "python": ("https://docs.python.org/3", None), + "scipy": ("https://docs.scipy.org/doc/scipy", None), + "setuptools": ("https://setuptools.pypa.io/en/stable", None), + "sklearn": ("https://scikit-learn.org/stable", None), + "torch": ("https://pytorch.org/docs/stable/", None), +} + + +suppress_warnings = [ + "intersphinx.external", +] diff --git a/docs/developer-guide/getting-started.rst b/docs/developer-guide/getting-started.rst new file mode 100644 index 0000000..a73e4a3 --- /dev/null +++ b/docs/developer-guide/getting-started.rst @@ -0,0 +1,51 @@ +.. _getting_started_dev: + +****************************** +Getting Started for Developers +****************************** + +We strongly recommend using the `Miniforge3 conda distribution `_ +that ships the package installer ``mamba``, a C++ reimplementation of ``conda``. + +.. warning:: + + The following guide is used only if you want to *develop* the + ``pyvisgen`` package, if you just want to write code that uses it + as a dependency, you can install ``pyvisgen`` using pip. + See :ref:`getting_started_users` + + +Setting Up the Development Environment +====================================== + +We provide a conda environment with all packages needed for development of pyvisgen +that can be installed via: + +.. code-block:: console + + $ mamba env create -f environment.yml + + +Next, switch to this new virtual environment: + +.. code-block:: console + + $ mamba activate pyvisgen + +You will need to run that last command any time you open a new +terminal session to activate the conda environment. + + +Installing pyvisgen in Development Mode +======================================= + +To install pyvisgen inside the ``pyvisgen`` environment +(or any environment for that matter), just run + +.. code-block:: console + + $ pip install -e . + +This installs the package in editable mode, meaning that you won't have to rerun +the installation for code changes to take effect. For greater changes such as +adding new entry points, the command may have to be run again. diff --git a/docs/developer-guide/index.rst b/docs/developer-guide/index.rst new file mode 100644 index 0000000..e26b4d2 --- /dev/null +++ b/docs/developer-guide/index.rst @@ -0,0 +1,10 @@ +.. _dev-guide: + +*************** +Developer Guide +*************** + +.. toctree:: + :maxdepth: 2 + + getting-started diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..bd04ffd --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,128 @@ +:html_theme.sidebar_secondary.remove: true +:html_theme.sidebar_primary.remove: true + +.. _pyvisgen: + +.. show title in tab name but not on index page +.. raw:: html + +
+ +======== +Pyvisgen +======== + +.. raw:: html + +
+ +.. currentmodule:: pyvisgen + +.. image:: _static/pyvisgen.webp + :class: only-light + :align: center + :width: 90% + :alt: The pyvisgen logo. + +.. image:: _static/pyvisgen_dark.webp + :class: only-dark + :align: center + :width: 90% + :alt: The pyvisgen logo. + +| + +**Version**: |version| | **Date**: |today| + +**Useful links**: +`Source Repository `__ | +`Issue Tracker `__ | +`Pull Requests `__ + +**License**: `MIT `__ + +**Python**: |python_requires| + + + +`pyvisgen` is a python implementation of the VISGEN tool developed at `Haystack Observatory `__. +It uses the Radio Interferometer Measurement Equation (RIME) to simulate the measurement process +of a radio interferometer. A gridder is also implemented to process the resulting visibilities and +convert them to images suitable as input for the neural networks developed in the +`radionets project `__. + +.. _pyvisgen_docs: + +.. toctree:: + :maxdepth: 1 + :hidden: + + user-guide/index + developer-guide/index + api-reference/index + changelog + + + +.. grid:: 1 2 2 3 + + .. grid-item-card:: + + :octicon:`book;40px` + + User Guide + ^^^^^^^^^^ + + Learn how to get started as a user. This guide + will help you install pyvisgen. + + +++ + + .. button-ref:: user-guide/index + :expand: + :color: primary + :click-parent: + + To the user guide + + + .. grid-item-card:: + + :octicon:`person-add;40px` + + Developer Guide + ^^^^^^^^^^^^^^^ + + Learn how to get started as a developer. + This guide will help you install pyvisgen for development + and explains how to contribute. + + +++ + + .. button-ref:: developer-guide/index + :expand: + :color: primary + :click-parent: + + To the developer guide + + + .. grid-item-card:: + + :octicon:`code;40px` + + API Docs + ^^^^^^^^ + + The API docs contain detailed descriptions of + of the various modules, classes and functions + included in pyvisgen. + + +++ + + .. button-ref:: api-reference/index + :expand: + :color: primary + :click-parent: + + To the API docs diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/user-guide/about.rst b/docs/user-guide/about.rst new file mode 100644 index 0000000..8c5d02a --- /dev/null +++ b/docs/user-guide/about.rst @@ -0,0 +1,59 @@ +************** +About pyvisgen +************** + +`pyvisgen` is a python implementation of the VISGEN tool developed at `Haystack Observatory `__. +It uses the Radio Interferometer Measurement Equation (RIME) to simulate the measurement process +of a radio interferometer. A gridder is also implemented to process the resulting visibilities and +convert them to images suitable as input for the neural networks developed in the +`radionets project `__. + +Input Images +============ + +As input images for the RIME formalism, we use GAN-generated radio galaxies created by `Rustige et. al. `_ +and `Kummer et. al. `_ Below, you can see four example images consisting of FRI and FRII sources. + + +.. image:: https://github.com/radionets-project/pyvisgen/assets/23259659/285e36f6-74e7-45f1-9976-896a38217880 + :align: center + :width: 90% + :alt: Sources generated with a GAN. + +Any image can be used as input for the formalism, as long as they are stored in the h5 format, generated with |h5py|_. + +.. |h5py| replace:: ``h5py`` +.. _h5py: https://www.h5py.org/ + + +RIME +==== + +Currently, we use the following expression for the simulation process: + +.. math:: + + \mathbf{V}_{\mathrm{pq}}(l, m) = \sum_{l, m} \mathbf{E}_{\mathrm{p}}(l, m) \mathbf{K}_{\mathrm{p}}(l, m) \mathbf{B}(l, m) \mathbf{K}^{H}_{\mathrm{q}}(l, m) \mathbf{E}^{H}_{\mathrm{q}}(l, m) + +Here, :math:`\mathbf{B}(l, m)` corresponds to the source distribution, :math:`\mathbf{K}(l, m) = \exp(-2\pi\cdot i\cdot (ul + vm))` represents +the phase delay, and :math:`\mathbf{E}(l, m) = \mathrm{jinc}\left(\frac{2\pi}{\lambda}d\cdot \theta_{lm}\right)` the telescope properties, +with :math:`\mathrm{jinc(x)} = \frac{J_1(x)}{x}` and :math:`J_1(x)` as the first Bessel function. An exemplary result can be found below. + +.. image:: https://github.com/radionets-project/pyvisgen/assets/23259659/858a5d4b-893a-4216-8d33-41d33981354c + :alt: visibilities + + +Visualization of Jones matrices +=============================== + +In this section, you can see visualizations of the matrices :math:`\mathbf{E}(l, m)` and :math:`\mathbf{K}(l, m)`. + +Visualization of the :math:`\mathbf{E}` matrix +---------------------------------------------- +.. image:: https://github.com/radionets-project/pyvisgen/assets/23259659/194a321b-77cd-423b-9d01-c18c0741d6c5 + :alt: visualize_E + +Visualization of the :math:`\mathbf{K}` matrix +---------------------------------------------- +.. image:: https://github.com/radionets-project/pyvisgen/assets/23259659/501f487a-498b-4143-b54a-eb0e2f28e417 + :alt: visualize_K diff --git a/docs/user-guide/getting-started.rst b/docs/user-guide/getting-started.rst new file mode 100644 index 0000000..6da3d4f --- /dev/null +++ b/docs/user-guide/getting-started.rst @@ -0,0 +1,26 @@ +.. _getting_started_users: + + +************************* +Getting Started for Users +************************* + +.. warning:: + + The following guide is for *users*. If you want to contribute to + pyvisgen as a developer, see :ref:`getting_started_dev`. + + +Installation +============ + +Currently, pyvisgen is only available via the +`source repository `_. + +To install ``pyvisgen`` into an existing (conda) environment, use + +.. code-block:: console + + $ pip install . + +in the root directory of the source repository. diff --git a/docs/user-guide/index.rst b/docs/user-guide/index.rst new file mode 100644 index 0000000..c1bdf9a --- /dev/null +++ b/docs/user-guide/index.rst @@ -0,0 +1,11 @@ +.. _user-guide: + +********** +User Guide +********** + +.. toctree:: + :maxdepth: 2 + + getting-started + about diff --git a/pyproject.toml b/pyproject.toml index 3cbd03f..5afa926 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta" [project] name = "pyvisgen" -version = "0.2.0" +dynamic = ["version"] description = "Simulate radio interferometer observations and visibility generation with the RIME formalism." readme = "README.md" -authors = [{ name = "Kevin Schmidt, Felix Geyer, Stefan Fröse" }] +authors = [{ name = "Kevin Schmitz, Felix Geyer, Stefan Fröse, Anno Knierim, Tom Groß" }] maintainers = [ - { name = "Kevin Schmidt", email = "kevin3.schmidt@tu-dortmund.de" }, - { name = "Felix Geyer", email = "felix.geyer@tu-dortmund.de" }, + { name = "Kevin Schmitz", email = "kevin2.schmitz@tu-dortmund.de" }, + { name = "Anno Knierim", email = "anno.knierim@tu-dortmund.de" }, ] license = { text = "MIT" } classifiers = [ @@ -50,12 +50,53 @@ dependencies = [ "tqdm", ] +[project.optional-dependencies] + +tests = [ + "h5py", + "pytest >= 7.0", + "pytest-cov", + "tomli", +] + +docs = [ + "graphviz", + "ipython", + "jupyter", + "nbsphinx", + "notebook", + "numpydoc", + "pydata_sphinx_theme", + "pyvisgen[all]", + "sphinx", + "sphinx-changelog", + "sphinx-copybutton", + "sphinx-design", + "sphinx-gallery >= 0.16.0", + "sphinx_automodapi", + "sphinxcontrib-bibtex", + "tomli; python_version < '3.11'", +] + [project.scripts] pyvisgen_create_dataset = "pyvisgen.simulation.scripts.create_dataset:main" +[tool.setuptools_scm] +write_to = "pyvisgen/_version.py" + [tool.setuptools.packages.find] where = ["."] +[tool.coverage.run] +omit = [ + "pyvisgen/version.py", + "pyvisgen/_version.py", + "docs/*" +] + +[tool.coverage.xml] +output = "coverage.xml" + [tool.towncrier] package = "pyvisgen" directory = "docs/changes" diff --git a/pyvisgen/__init__.py b/pyvisgen/__init__.py index e69de29..76e6b0b 100644 --- a/pyvisgen/__init__.py +++ b/pyvisgen/__init__.py @@ -0,0 +1,9 @@ +""" +pyvisgen - Python implementation of the VISGEN tool. + +Licensed under a MIT style license - see LICENSE +""" + +from .version import __version__ + +__all__ = ["__version__"] diff --git a/pyvisgen/fits/__init__.py b/pyvisgen/fits/__init__.py index e69de29..3417d49 100644 --- a/pyvisgen/fits/__init__.py +++ b/pyvisgen/fits/__init__.py @@ -0,0 +1,17 @@ +from .data import fits_data +from .writer import ( + create_antenna_hdu, + create_frequency_hdu, + create_hdu_list, + create_time_hdu, + create_vis_hdu, +) + +__all__ = [ + "create_antenna_hdu", + "create_frequency_hdu", + "create_hdu_list", + "create_time_hdu", + "create_vis_hdu", + "fits_data", +] diff --git a/pyvisgen/gridding/__init__.py b/pyvisgen/gridding/__init__.py index e69de29..9bcde62 100644 --- a/pyvisgen/gridding/__init__.py +++ b/pyvisgen/gridding/__init__.py @@ -0,0 +1,21 @@ +from .gridder import ( + calc_truth_fft, + convert_amp_phase, + convert_real_imag, + create_gridded_data_set, + ducc0_gridding, + grid_data, + open_data, + save_fft_pair, +) + +__all__ = [ + "calc_truth_fft", + "convert_amp_phase", + "convert_real_imag", + "create_gridded_data_set", + "ducc0_gridding", + "grid_data", + "open_data", + "save_fft_pair", +] diff --git a/pyvisgen/layouts/__init__.py b/pyvisgen/layouts/__init__.py index e69de29..d906012 100644 --- a/pyvisgen/layouts/__init__.py +++ b/pyvisgen/layouts/__init__.py @@ -0,0 +1,7 @@ +from .layouts import Stations, get_array_layout, get_array_names + +__all__ = [ + "Stations", + "get_array_layout", + "get_array_names", +] diff --git a/pyvisgen/simulation/__init__.py b/pyvisgen/simulation/__init__.py index e69de29..c7fd0c7 100644 --- a/pyvisgen/simulation/__init__.py +++ b/pyvisgen/simulation/__init__.py @@ -0,0 +1,38 @@ +from .array import Array +from .data_set import ( + calc_ref_elev, + calc_time_steps, + create_observation, + create_sampling_rc, + draw_sampling_opts, + get_images, + simulate_data_set, + test_opts, +) +from .observation import Baselines, Observation, ValidBaselineSubset +from .scan import angularDistance, calc_beam, calc_fourier, integrate, jinc, rime +from .visibility import Visibilities, generate_noise, vis_loop + +__all__ = [ + "Array", + "Baselines", + "Observation", + "ValidBaselineSubset", + "Visibilities", + "angularDistance", + "calc_beam", + "calc_fourier", + "calc_ref_elev", + "calc_time_steps", + "create_observation", + "create_sampling_rc", + "draw_sampling_opts", + "generate_noise", + "get_images", + "integrate", + "jinc", + "rime", + "simulate_data_set", + "test_opts", + "vis_loop", +] diff --git a/pyvisgen/simulation/observation.py b/pyvisgen/simulation/observation.py index 2f3f673..8f4ad61 100644 --- a/pyvisgen/simulation/observation.py +++ b/pyvisgen/simulation/observation.py @@ -158,7 +158,13 @@ def get_timerange(self, t_start, t_stop): *[getattr(self, f.name).ravel() for f in fields(self)] )[(self.date >= t_start) & (self.date <= t_stop)] - def get_unique_grid(self, fov_size, ref_frequency, img_size, device): + def get_unique_grid( + self, + fov_size: float, + ref_frequency: float, + img_size: int, + device: str, + ): uv = torch.cat([self.u_valid[None], self.v_valid[None]], dim=0) fov = fov_size * pi / (3600 * 180) @@ -696,3 +702,52 @@ def create_lm_grid(self): ) * torch.sin(dec) * torch.cos(self.rd[..., 0]) return lm_grid + + def calc_direction_cosines( + self, + ha: torch.tensor, + el_st: torch.tensor, + delta_x: torch.tensor, + delta_y: torch.tensor, + delta_z: torch.tensor, + ): + """Calculates direction cosines u, v, and w for + given hour angles and relative antenna positions. + + Parameters + ---------- + ha : :func:`torch.tensor` + Tensor containing hour angles for each time step. + el_st : :func:`torch.tensor` + Tensor containing station elevations. + delta_x : :func:`torch.tensor` + Tensor containing relative antenna x-postions. + delta_y : :func:`torch.tensor` + Tensor containing relative antenna y-postions. + delta_z : :func:`torch.tensor` + Tensor containing relative antenna z-postions. + + Returns + ------- + u : :func:`torch.tensor` + Tensor containing direction cosines in u-axis direction. + v : :func:`torch.tensor` + Tensor containing direction cosines in v-axis direction. + w : :func:`torch.tensor` + Tensor containing direction cosines in w-axis direction. + """ + src_dec = torch.deg2rad(self.dec) + ha = torch.deg2rad(ha) + u = (torch.sin(ha) * delta_x + torch.cos(ha) * delta_y).reshape(-1) + v = ( + -torch.sin(src_dec) * torch.cos(ha) * delta_x + + torch.sin(src_dec) * torch.sin(ha) * delta_y + + torch.cos(src_dec) * delta_z + ).reshape(-1) + w = ( + torch.cos(src_dec) * torch.cos(ha) * delta_x + - torch.cos(src_dec) * torch.sin(ha) * delta_y + + torch.sin(src_dec) * delta_z + ).reshape(-1) + assert u.shape == v.shape == w.shape + return u, v, w diff --git a/pyvisgen/utils/__init__.py b/pyvisgen/utils/__init__.py index e69de29..b76a994 100644 --- a/pyvisgen/utils/__init__.py +++ b/pyvisgen/utils/__init__.py @@ -0,0 +1,9 @@ +from .config import read_data_set_conf +from .data import get_bundles, load_bundles, open_bundles + +__all__ = [ + "get_bundles", + "load_bundles", + "open_bundles", + "read_data_set_conf", +] diff --git a/pyvisgen/version.py b/pyvisgen/version.py new file mode 100644 index 0000000..90c7787 --- /dev/null +++ b/pyvisgen/version.py @@ -0,0 +1,19 @@ +# this is adapted from https://github.com/astropy/astropy/blob/master/astropy/version.py +# see https://github.com/astropy/astropy/pull/10774 for a discussion on why this needed. +try: + try: + from ._dev_version import version + except ImportError: + from ._version import version +except Exception: + import warnings + + warnings.warn( + "Could not determine pyvisgen version. This indicates" + " a broken installation. Please install pyvisgen from" + " the local git repository." + ) + del warnings + version = "0.0.0" + +__version__ = version diff --git a/tests/test_simulation.py b/tests/test_simulation.py index c384951..9799823 100644 --- a/tests/test_simulation.py +++ b/tests/test_simulation.py @@ -5,7 +5,6 @@ from pyvisgen.utils.config import read_data_set_conf - torch.manual_seed(1) config = "tests/test_conf.toml" conf = read_data_set_conf(config) @@ -80,6 +79,45 @@ def test_vis_loop(): hdu_list.writeto(out, overwrite=True) +def test_vis_loop_grid(): + import torch + + import pyvisgen.fits.writer as writer + from pyvisgen.simulation.data_set import create_observation + from pyvisgen.simulation.visibility import vis_loop + from pyvisgen.utils.data import load_bundles, open_bundles + + bundles = load_bundles(conf["in_path"]) + obs = create_observation(conf) + # num_active_telescopes = test_opts(samp_ops) + data = open_bundles(bundles[0]) + SI = torch.tensor(data[0])[None] + vis_data = vis_loop(obs, SI, noisy=conf["noisy"], mode="grid") + + assert (vis_data[0].V_11[0]).dtype == torch.complex128 + assert (vis_data[0].V_22[0]).dtype == torch.complex128 + assert (vis_data[0].V_12[0]).dtype == torch.complex128 + assert (vis_data[0].V_21[0]).dtype == torch.complex128 + assert (vis_data[0].num).dtype == torch.float32 + assert (vis_data[0].base_num).dtype == torch.float64 + assert torch.is_tensor(vis_data[0].u) + assert torch.is_tensor(vis_data[0].v) + assert torch.is_tensor(vis_data[0].w) + assert (vis_data[0].date).dtype == torch.float64 + + # test num vis for time step 0 + # num_vis_theory = num_active_telescopes * (num_active_telescopes - 1) / 2 + # num_vis_calc = vis_data.base_num[vis_data.date == vis_data.date[0]].shape[0] + # dunno what's going on here + # assert num_vis_theory == num_vis_calc + # + + out_path = Path(conf["out_path_fits"]) + out = out_path / Path("vis_0.fits") + hdu_list = writer.create_hdu_list(vis_data, obs) + hdu_list.writeto(out, overwrite=True) + + def test_vis_loop_batch_size_auto(): import torch