├── .gitattributes ├── .github └── workflows │ ├── deploy.yml │ ├── docs.yml │ ├── issues.yml │ └── test_and_build.yml ├── .gitignore ├── .readthedocs.yml ├── .travis.yml ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── README.md ├── astromodels ├── __init__.py ├── _version.py ├── core │ ├── __init__.py │ ├── cpickle_compatibility_layer.py │ ├── memoization.py │ ├── model.py │ ├── model_parser.py │ ├── my_yaml.py │ ├── node_type.py │ ├── parameter.py │ ├── parameter_transformation.py │ ├── polarization.py │ ├── property.py │ ├── serialization.py │ ├── sky_direction.py │ ├── spectral_component.py │ ├── thread_safe_unit_format.py │ ├── tree.py │ └── units.py ├── data │ ├── dark_matter │ │ ├── dmSpecTab.npy │ │ └── gammamc_dif.dat │ ├── log_theme.ini │ ├── past_1D_values.h5 │ ├── tests │ │ ├── black_hole.yaml │ │ ├── old_table.h5 │ │ ├── simple_source.yaml │ │ ├── test.yaml │ │ └── test_xspec_table_model.fits │ └── xsect │ │ ├── xsect_phabs_angr.fits │ │ ├── xsect_phabs_aspl.fits │ │ ├── xsect_tbabs_angr.fits │ │ ├── xsect_tbabs_aspl.fits │ │ ├── xsect_tbabs_wilm.fits │ │ └── xsect_wabs_angr.fits ├── functions │ ├── __init__.py │ ├── dark_matter │ │ ├── __init__.py │ │ └── dm_models.py │ ├── function.py │ ├── functions_1D │ │ ├── __init__.py │ │ ├── absorption.py │ │ ├── apec.py │ │ ├── blackbody.py │ │ ├── extinction.py │ │ ├── functions.py │ │ ├── polynomials.py │ │ └── powerlaws.py │ ├── functions_2D.py │ ├── functions_3D.py │ ├── numba_functions.py │ ├── priors.py │ └── template_model.py ├── sources │ ├── __init__.py │ ├── extended_source.py │ ├── particle_source.py │ ├── point_source.py │ └── source.py ├── tests │ ├── test_1D_function_values.py │ ├── test_extended_source.py │ ├── test_functions.py │ ├── test_load_xspec_models.py │ ├── test_memoizer.py │ ├── test_model.py │ ├── test_parameter.py │ ├── test_point_source.py │ ├── test_polarization.py │ ├── test_template_model.py │ └── test_tree.py ├── utils │ ├── __init__.py │ ├── angular_distance.py │ ├── config_structure.py │ ├── configuration.py │ ├── data_files.py │ ├── disk_usage.py │ ├── file_utils.py │ ├── io.py │ ├── logging.py │ ├── long_path_formatter.py │ ├── pretty_list.py │ ├── table.py │ ├── valid_variable.py │ └── vincenty.py └── xspec │ ├── LICENSE │ ├── README │ ├── __init__.py │ ├── factory.py │ ├── include │ └── sherpa │ │ ├── array.hh │ │ ├── astro │ │ └── xspec_extension.hh │ │ ├── constants.hh │ │ ├── extension.hh │ │ └── fcmp.hh │ ├── src │ └── _xspec.cc │ └── xspec_settings.py ├── build_local.sh ├── ci ├── build_and_test.sh ├── environment.yml ├── environment_noxspec.yml └── set_minor_version.py ├── codecov.yml ├── conda-dist ├── no_xspec_recipe │ └── meta.yaml └── recipe │ ├── conda_build_config.yaml │ └── meta.yaml ├── docs ├── api │ ├── API.rst │ └── modules.rst ├── conf.py ├── function_docs │ ├── functions_1d.rst │ └── functions_2d.rst ├── index.rst ├── md │ ├── Additional_features_for_scripts_and_applications.md │ ├── Configuration.md │ ├── Extended_sources_tutorial.md │ ├── Functions_tutorial.md │ ├── Model_tutorial.md │ ├── Multi_component_sources.md │ ├── Point_source_tutorial.md │ ├── Priors_for_Bayesian_analysis.md │ ├── Quick_start.md │ └── my_model.yml ├── media │ ├── favicon.ico │ ├── large_logo.png │ ├── solid_logo.png │ └── transp_logo.png ├── notebooks │ └── _stub ├── release_notes.rst └── requirements.txt ├── examples ├── Additional_features_for_scripts_and_applications.ipynb ├── EBL_attenuation_example.ipynb ├── Functions_tutorials.ipynb ├── Introduction.ipynb ├── Model_tutorial.ipynb ├── Point_source_tutorial.ipynb ├── Priors_for_Bayesian_analysis.ipynb ├── Quick_start.ipynb ├── Untitled.ipynb ├── convert_to_rst.py ├── createDMSpectra.py ├── createExtendedSourceFromTemplate.py ├── exampleDMtemplate.fits ├── showcase │ ├── .ipynb_checkpoints │ │ └── Time-varying_synchrotron_spectrum-checkpoint.ipynb │ ├── Time-varying_synchrotron_spectrum.ipynb │ └── naima_test.yml ├── testGalprop3DFunction.py └── testICModel.fits ├── scripts ├── doc_gen_1d.md ├── doc_gen_2d.md ├── doc_gen_function_list.md ├── doc_gen_priors.md ├── generate_func_docs.py ├── generate_func_rst.py ├── generate_test_data.py └── past_1D_values.h5 ├── setup.cfg ├── setup.py ├── test_inside_docker.sh └── versioneer.py /.gitattributes: -------------------------------------------------------------------------------- 1 | astromodels/_version.py export-subst 2 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Docs 2 | on: [push, release] 3 | 4 | jobs: 5 | notebooks: 6 | name: "Build the notebooks for the docs" 7 | runs-on: macos-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | with: 12 | persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token 13 | fetch-depth: 0 # otherwise, you will failed to push refs to dest repo 14 | 15 | - name: Set up Python 16 | uses: actions/setup-python@v5 17 | with: 18 | python-version: 3.9 19 | #- name: Checkout 20 | # uses: actions/checkout@v4 21 | - name: Init Environment 22 | #shell: bash -l {0} 23 | run: | 24 | 25 | brew update 26 | brew install --cask basictex 27 | 28 | eval "$(/usr/libexec/path_helper)" 29 | 30 | - name: Setup base pkgs 31 | #shell: bash -l {0} 32 | run: | 33 | brew install hdf5 34 | 35 | python -m pip install --upgrade pip wheel 36 | 37 | python -m pip install numpy numba tempita jupytext jupyterthemes jupyter_latex_envs papermill "matplotlib<3.9" astropy pandas tables healpy 38 | python -m pip install ebltable black cython pkgconfig h5py 39 | 40 | 41 | - name: Install the package 42 | #shell: bash -l {0} 43 | run: | 44 | 45 | #python setup.py develop 46 | pip3 install -e . 47 | 48 | - name: Execute the notebooks 49 | #shell: bash -l {0} 50 | run: | 51 | # Make sure we fail in case of error 52 | set -e 53 | # Download an example file 54 | 55 | wget https://www.astropy.org/astropy-data/photometry/spitzer_example_image.fits 56 | 57 | mv spitzer_example_image.fits docs/md/ 58 | 59 | jupytext --to ipynb --execute docs/md/*.md 60 | mv docs/md/*.ipynb docs/notebooks/ 61 | ls docs/notebooks 62 | env: 63 | OMP_NUM_THREADS: 1 64 | MKL_NUM_THREADS: 1 65 | NUMEXPR_NUM_THREADS: 1 66 | MPLBACKEND: "Agg" 67 | - name: Create function gallery 68 | #shell: bash -l {0} 69 | run: | 70 | # move to the scripts directory 71 | cd scripts 72 | 73 | 74 | # now generate the function docs 75 | python generate_func_docs.py 76 | 77 | cd .. 78 | 79 | ls docs/notebooks 80 | 81 | - uses: actions/upload-artifact@v4 82 | with: 83 | name: notebooks-for-${{ github.sha }} 84 | path: docs/notebooks 85 | 86 | 87 | - name: Sleep for 10 min 88 | uses: juliangruber/sleep-action@v1 89 | with: 90 | time: 10m 91 | 92 | - name: Trigger RTDs build 93 | uses: dfm/rtds-action@main 94 | with: 95 | webhook_url: ${{ secrets.RTDS_WEBHOOK_URL }} 96 | webhook_token: ${{ secrets.RTDS_WEBHOOK_TOKEN }} 97 | commit_ref: ${{ github.ref }} 98 | 99 | api_doc: 100 | name: "Create the API stubs" 101 | runs-on: macos-latest 102 | steps: 103 | - uses: actions/checkout@v4 104 | with: 105 | persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token 106 | fetch-depth: 0 # otherwise, you will failed to push refs to dest repo 107 | 108 | - name: Set up Python 109 | uses: actions/setup-python@v5 110 | with: 111 | python-version: 3.9 112 | 113 | - name: Build the API doc 114 | run: | 115 | 116 | brew install c-blosc 117 | brew install hdf5 118 | 119 | pip3 install tempita tables 120 | pip3 install cython blosc2 h5py 121 | pip3 install numpy scipy numba astropy pandas 122 | pip3 install wheel pkgconfig 123 | 124 | #python setup.py develop 125 | pip3 install -e . 126 | 127 | brew install sphinx-doc pandoc 128 | 129 | pip3 install mock recommonmark 130 | pip3 install sphinx-rtd-dark-mode 131 | pip3 install -U sphinx 132 | 133 | 134 | sphinx-apidoc -f -o docs/api/ astromodels 135 | 136 | 137 | - uses: actions/upload-artifact@v4 138 | with: 139 | name: api-stubs-for-${{ github.sha }} 140 | path: docs/api 141 | 142 | 143 | build_docs: 144 | name: "Build the Documentation" 145 | runs-on: macos-latest 146 | needs: [notebooks, api_doc] 147 | steps: 148 | 149 | 150 | - uses: actions/checkout@v4 151 | with: 152 | persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token 153 | fetch-depth: 0 # otherwise, you will failed to push refs to dest repo 154 | 155 | - name: Set up Python 156 | uses: actions/setup-python@v5 157 | with: 158 | python-version: 3.9 159 | 160 | - name: Install package 161 | run: | 162 | 163 | brew install c-blosc 164 | brew install hdf5 165 | 166 | pip3 install tempita 167 | pip3 install cython blosc2 h5py 168 | pip3 install numpy scipy numba astropy 169 | 170 | brew install sphinx-doc pandoc 171 | 172 | pip3 install wheel pkgconfig 173 | pip3 install mock recommonmark 174 | pip3 install sphinx-rtd-dark-mode sphinx-math-dollar 175 | pip3 install -r docs/requirements.txt 176 | 177 | #python setup.py develop 178 | pip3 install -e . 179 | 180 | rm -rf docs/md/* 181 | 182 | 183 | 184 | - uses: actions/download-artifact@master 185 | with: 186 | name: notebooks-for-${{ github.sha }} 187 | path: docs/notebooks 188 | 189 | 190 | - uses: actions/download-artifact@master 191 | with: 192 | name: api-stubs-for-${{ github.sha }} 193 | path: docs/notebooks/api 194 | 195 | - name: Build and Commit 196 | uses: sphinx-notes/pages@v2 197 | with: 198 | documentation_path: docs 199 | sphinx_version: 5.1.1 200 | requirements_path: docs/requirements.txt 201 | 202 | 203 | - name: Push changes 204 | if: github.event_name == 'push' #&& startsWith(github.event.ref, 'refs/tags') 205 | uses: ad-m/github-push-action@master 206 | with: 207 | github_token: ${{ secrets.GITHUB_TOKEN }} 208 | branch: gh-pages 209 | -------------------------------------------------------------------------------- /.github/workflows/issues.yml: -------------------------------------------------------------------------------- 1 | name: Close stale issues and PRs 2 | on: 3 | schedule: 4 | - cron: 30 1 * * * 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v3 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | stale-issue-message: 'This issue has become stale. Is there an update? We will close in 15 days' 14 | stale-pr-message: 'This PR has become stale. We will close in 60 days' 15 | stale-issue-label: 'no-issue-activity' 16 | stale-pr-label: 'no-pr-activity' 17 | days-before-pr-stale: 90 18 | days-before-issue-stale: 30 19 | days-before-issue-close: 15 20 | days-before-pr-close: 60 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | # PyCharm 60 | .idea 61 | 62 | examples/.ipynb_checkpoints/ 63 | generate_for_pip 64 | *.py.bak 65 | /.DS_Store 66 | /doc/.DS_Store 67 | /.dir-locals.el 68 | /__test.fits 69 | /doc/*.ipynb 70 | /doc/notebooks/*.ipynb 71 | /doc/notebooks/.ipynb_checkpoints/ 72 | /doc/notebooks/my_model.yml 73 | /doc/notebooks/time_dependent.yml 74 | /doc/Makefile 75 | /docs/Makefile 76 | /docs/.DS_Store 77 | /docs/notebooks/.DS_Store 78 | yapf*.py 79 | /scripts/*.ipynb 80 | /docs/notebooks/*.ipynb 81 | /astromodels/core/.ipynb_checkpoints/ 82 | /__test.yml 83 | /scripts/.ipynb_checkpoints/ 84 | /scripts/.ipynb_checkpoints/ 85 | /docs/md/.ipynb_checkpoints/ 86 | /docs/md/*.ipynb 87 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: "3.9" 7 | 8 | python: 9 | install: 10 | - requirements: docs/requirements.txt 11 | - method: pip 12 | path: . 13 | extra_requirements: 14 | - docs 15 | 16 | 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | 3 | jobs: 4 | include: 5 | # - os: linux 6 | # python: 2.7 7 | # env: TRAVIS_PYTHON_VERSION=2.7 TEST_WITH_XSPEC=false CONDA_UPLOAD=false 8 | # - os: osx 9 | # python: 2.7 10 | # env: TRAVIS_PYTHON_VERSION=2.7 TEST_WITH_XSPEC=false CONDA_UPLOAD=false 11 | - os: linux 12 | python: 2.7 13 | env: TRAVIS_PYTHON_VERSION=2.7 TEST_WITH_XSPEC=true CONDA_UPLOAD=true 14 | - os: osx 15 | python: 2.7 16 | env: TRAVIS_PYTHON_VERSION=2.7 TEST_WITH_XSPEC=true CONDA_UPLOAD=true 17 | # - os: linux 18 | # python: 3.5 19 | # env: TRAVIS_PYTHON_VERSION=3.5 TEST_WITH_XSPEC=false CONDA_UPLOAD=false 20 | # - os: osx 21 | # python: 3.5 22 | # env: TRAVIS_PYTHON_VERSION=3.5 TEST_WITH_XSPEC=false CONDA_UPLOAD=false 23 | # - os: linux 24 | # python: 3.6 25 | # env: TRAVIS_PYTHON_VERSION=3.6 TEST_WITH_XSPEC=false CONDA_UPLOAD=false 26 | # - os: osx 27 | # python: 3.6 28 | # env: TRAVIS_PYTHON_VERSION=3.6 TEST_WITH_XSPEC=false CONDA_UPLOAD=false 29 | # - os: linux 30 | # python: 3.7 31 | # env: TRAVIS_PYTHON_VERSION=3.7 TEST_WITH_XSPEC=false CONDA_UPLOAD=false 32 | # - os: osx 33 | # python: 3.7 34 | # env: TRAVIS_PYTHON_VERSION=3.7 TEST_WITH_XSPEC=false CONDA_UPLOAD=false 35 | # - os: linux 36 | # python: 3.5 37 | # env: TRAVIS_PYTHON_VERSION=3.5 TEST_WITH_XSPEC=true CONDA_UPLOAD=true 38 | # - os: osx 39 | # python: 3.5 40 | # env: TRAVIS_PYTHON_VERSION=3.5 TEST_WITH_XSPEC=true CONDA_UPLOAD=true 41 | # - os: linux 42 | # python: 3.6 43 | # env: TRAVIS_PYTHON_VERSION=3.6 TEST_WITH_XSPEC=true CONDA_UPLOAD=true 44 | # - os: osx 45 | # python: 3.6 46 | # env: TRAVIS_PYTHON_VERSION=3.6 TEST_WITH_XSPEC=true CONDA_UPLOAD=true 47 | - os: linux 48 | python: 3.7 49 | env: TRAVIS_PYTHON_VERSION=3.7 TEST_WITH_XSPEC=true CONDA_UPLOAD=true 50 | - os: osx 51 | python: 3.7 52 | env: TRAVIS_PYTHON_VERSION=3.7 TEST_WITH_XSPEC=true CONDA_UPLOAD=true 53 | 54 | before_install: 55 | - | 56 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 57 | #docker pull condaforge/linux-anvil 58 | 59 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh 60 | bash miniconda.sh -b -p $HOME/miniconda 61 | export PATH="$HOME/miniconda/bin:$PATH" 62 | conda install -y conda-build 63 | conda config --add channels conda-forge 64 | else 65 | 66 | # Install Miniconda 67 | wget https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -O miniconda.sh 68 | bash miniconda.sh -b -p $HOME/miniconda 69 | export PATH="$HOME/miniconda/bin:$PATH" 70 | conda install -y conda-build 71 | conda config --add channels conda-forge 72 | 73 | fi 74 | 75 | script: 76 | - chmod u+x ci/build_and_test.sh 77 | - | 78 | while sleep 500; do 79 | echo "====[ $SECONDS seconds still running ]====" 80 | done & 81 | - "./ci/build_and_test.sh" 82 | 83 | deploy: 84 | provider: pypi 85 | user: __token__ 86 | password: 87 | secure: 2I14ixrWgiOiE2ha9sai44mJC8stU9pB2sv/51AMIU6E1TK09RCzRd7Hk6KeuBk7bdF6+qnBcA8khicEkwamvxmtUre2RBOjO3PE9qBdHmPHfMF19fZWSxWHHBL76I6O212MkvzQWpmWEksPcLYf2wgeOv7wMNTzjFJjd39AQE+zZUpA38OOSjHn1FJe+sf/ru8f99vTu4tCEjiNvf3THbw3s22bEQcquhehXM+UB7ZNU4s1iGgMgJHHrIHhPF/1t67iddOeKbYFzkAtRaanNf5r2Z2m0w67Nx5hJSXxbfJN3RrWGj8/SdMj0IxT4gK88WzM5BY2fKhyvewRRP5ph7a/9EA3NsabhSKor6R5Q/pJQUJR7Ln1t0Sw3oziUmcLOYna6ijfBML1cJtn24jNR8mt73/h7Wj6iPnx8yTpKL1oUGKDQIjdEdsMGSiyTRPdTv6ynV67UybbENQtIoNNrGa0l2TguF3AV+FoZGlPy1uYaM3LZ/FKqMPsGRtxmzn8W8bldZXZnzYfMNS3VculTEZ5v3KoF9CbFvE5udEvB558yhB8PV3oR+oajpEGy9E1U6pskMOVpATG0s3ne7U83kgmRcT2ArfTzOWYv3MYZBNZm4vBlQLH37+KXO2rZoVVygoYVS6g1mCz9d8yWZdoo9zb+epNX57xAUz2ewZboyk= 88 | distributions: sdist 89 | skip_existing: true 90 | on: 91 | tags: true 92 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | MAINTAINER Giacomo Vianello 4 | 5 | # Override the default shell (sh) with bash 6 | RUN rm /bin/sh && ln -s /bin/bash /bin/sh 7 | 8 | # Update repositories and install needed packages 9 | # I use one long line so that I can remove all .deb files at the end 10 | # before Docker writes out this layer 11 | 12 | RUN apt-get update && apt-get install -y python2.7 python2.7-dev python-dev wget python-pip git python-tk libreadline6-dev libncurses5-dev xorg-dev gcc g++ gfortran perl-modules && apt-get clean 13 | 14 | RUN mkdir -p /heasoft/src 15 | 16 | COPY heasoft-6.19src.tar.gz /heasoft/src 17 | 18 | RUN cd /heasoft && mkdir build && cd src && tar zxvf heasoft-6.19src.tar.gz && cd heasoft-6.19/BUILD_DIR/ && ./configure --prefix=/heasoft/build && make && make install && rm -rf /heasoft/src 19 | 20 | RUN pip install virtualenv 21 | 22 | RUN apt-get install sudo 23 | 24 | WORKDIR / 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Giacomo Vianello 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of astromodels nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft astromodels/xspec/ 2 | graft astromodels/data/tests 3 | graft astromodels/xspec/include 4 | graft astromodels/xspec/include/sherpa/ 5 | graft astromodels/xspec/include/sherpa/astro/ 6 | graft astromodels/data/dark_matter/ 7 | graft astromodels/data/xsect 8 | include versioneer.py 9 | include astromodels/_version.py 10 | include astromodels/data/log_theme.ini -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # astromodels 2 | 3 | ![CI](https://github.com/threeML/astromodels/workflows/CI/badge.svg) 4 | [![codecov](https://codecov.io/gh/threeML/astromodels/branch/master/graph/badge.svg)](https://codecov.io/gh/threeML/astromodels) 5 | [![Documentation Status](https://readthedocs.org/projects/astromodels/badge/?version=latest)](http://astromodels.readthedocs.io/en/latest/?badge=latest) 6 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) 7 | ![GitHub contributors](https://img.shields.io/github/contributors/threeML/astromodels) 8 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5646925.svg)](https://doi.org/10.5281/zenodo.5646925) 9 | 10 | ![GitHub pull requests](https://img.shields.io/github/issues-pr/threeML/astromodels) 11 | ![GitHub issues](https://img.shields.io/github/issues/threeML/astromodels) 12 | ## PyPi 13 | 14 | [![PyPI version fury.io](https://badge.fury.io/py/astromodels.svg)](https://pypi.python.org/pypi/astromodels/) 15 | ![PyPI - Downloads](https://img.shields.io/pypi/dm/astromodels) 16 | 17 | ## Conda 18 | ![Conda](https://img.shields.io/conda/pn/threeml/astromodels) 19 | ![Conda](https://img.shields.io/conda/dn/threeml/astromodels) 20 | 21 | 22 | ![alt text](https://raw.githubusercontent.com/threeml/astromodels/master/docs/media/large_logo.png) 23 | 24 | Astromodels is a very flexible framework to define models for likelihood or Bayesian analysis of astrophysical data. 25 | 26 | Even though it has been designed having in mind analysis in the spectral domain, it can be used also as a toolbox containing functions of any variable. 27 | 28 | Astromodels is not a modeling package, it only gives you the tools to build a model as complex as you need. You then need a separate package (such as [3ML](github.com/threeML/threeML)) to fit that model to the data. 29 | 30 | Some of the features which distinguish astromodels from other similar packages are: * a model can contain an arbitrary number of sources at different positions in the sky * parameters can be linked through any function (not only identity) * parameters can vary with auxiliary variables such as time. For example, you can build a model where some parameters vary with time, and you can fit the parameters of the function which describe this variability. Similary you can build models where parameters vary with the phase of a pulsar, and so on. * models can be saved in and loaded from YAML file (a human-readable format) * physical units are fully supported in input, but they are handled so that they don’t slow down the actualy computation of the models. 31 | 32 | Astromodels has been designed with performance as priority, and is considerably faster than other python-based solution for the same problem, such as astropy.modeling and the modeling part of sherpa. 33 | Documentation: http://astromodels.readthedocs.org/en/latest/ 34 | -------------------------------------------------------------------------------- /astromodels/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | 5 | from ._version import get_versions 6 | 7 | # Import the version 8 | 9 | # 10 | # 11 | 12 | if os.environ.get("ASTROMODELS_DEBUG", None) is None: 13 | 14 | from .utils.configuration import astromodels_config, show_configuration 15 | from .core.memoization import use_astromodels_memoization 16 | from .core.model import Model 17 | from .core.model_parser import clone_model, load_model 18 | from .core.parameter import ( 19 | IndependentVariable, 20 | Parameter, 21 | SettingOutOfBounds, 22 | turn_off_parameter_transforms, 23 | ) 24 | from .core.polarization import LinearPolarization, StokesPolarization 25 | from .core.serialization import * 26 | from .core.spectral_component import SpectralComponent 27 | from .core.units import get_units 28 | from .functions import ( 29 | Asymm_Gaussian_on_sphere, 30 | Band, 31 | Band_Calderone, 32 | Band_grbm, 33 | Blackbody, 34 | Broken_powerlaw, 35 | Cauchy, 36 | Constant, 37 | Continuous_injection_diffusion, 38 | Continuous_injection_diffusion_ellipse, 39 | Continuous_injection_diffusion_legacy, 40 | Cosine_Prior, 41 | Cubic, 42 | Cutoff_powerlaw, 43 | Cutoff_powerlaw_Ep, 44 | DiracDelta, 45 | Disk_on_sphere, 46 | DMFitFunction, 47 | DMSpectra, 48 | Ellipse_on_sphere, 49 | Exponential_cutoff, 50 | Function1D, 51 | Function2D, 52 | Function3D, 53 | FunctionMeta, 54 | GalPropTemplate_3D, 55 | Gaussian, 56 | Gaussian_on_sphere, 57 | Inverse_cutoff_powerlaw, 58 | Latitude_galactic_diffuse, 59 | Line, 60 | Log_normal, 61 | Log_parabola, 62 | Log_uniform_prior, 63 | MissingDataFile, 64 | ModelAssertionViolation, 65 | ModifiedBlackbody, 66 | NonDissipativePhotosphere, 67 | NonDissipativePhotosphere_Deep, 68 | PhAbs, 69 | Power_law_on_sphere, 70 | Powerlaw, 71 | Powerlaw_Eflux, 72 | Powerlaw_flux, 73 | Quadratic, 74 | Quartic, 75 | Sin, 76 | SmoothlyBrokenPowerLaw, 77 | DoubleSmoothlyBrokenPowerlaw, 78 | SpatialTemplate_2D, 79 | Standard_Rv, 80 | StepFunction, 81 | StepFunctionUpper, 82 | GenericFunction, 83 | Super_cutoff_powerlaw, 84 | TbAbs, 85 | TemplateModel, 86 | TemplateModelFactory, 87 | Truncated_gaussian, 88 | Uniform_prior, 89 | WAbs, 90 | XSPECTableModel, 91 | ZDust, 92 | get_polynomial, 93 | has_ebltable, 94 | has_gsl, 95 | has_naima, 96 | has_atomdb, 97 | ) 98 | 99 | if has_ebltable: 100 | 101 | from .functions import EBLattenuation 102 | 103 | if has_gsl: 104 | 105 | from .functions import Cutoff_powerlaw_flux 106 | 107 | if has_naima: 108 | 109 | from .functions import Synchrotron 110 | 111 | if has_atomdb: 112 | 113 | from .functions import APEC, VAPEC 114 | 115 | from .functions.function import get_function_class, list_functions 116 | from .sources import ExtendedSource, ParticleSource, PointSource 117 | 118 | astromodels_units = get_units() 119 | from astromodels.utils.logging import ( 120 | setup_logger, 121 | update_logging_level, 122 | silence_warnings, 123 | activate_warnings, 124 | ) 125 | 126 | import astropy.units as u 127 | 128 | log = setup_logger(__name__) 129 | 130 | __version__ = get_versions()["version"] 131 | del get_versions 132 | -------------------------------------------------------------------------------- /astromodels/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/core/__init__.py -------------------------------------------------------------------------------- /astromodels/core/cpickle_compatibility_layer.py: -------------------------------------------------------------------------------- 1 | try: 2 | 3 | import cPickle 4 | 5 | except ImportError: 6 | 7 | import pickle as cPickle 8 | -------------------------------------------------------------------------------- /astromodels/core/memoization.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import functools 3 | import contextlib 4 | import astropy.units as u 5 | 6 | _WITH_MEMOIZATION = False 7 | _CACHE_SIZE = 20 8 | 9 | 10 | @contextlib.contextmanager 11 | def use_astromodels_memoization(switch, cache_size=_CACHE_SIZE): 12 | """ 13 | Activate/deactivate memoization temporarily 14 | 15 | :param switch: True (memoization on) or False (memoization off) 16 | :param cache_size: number of previous evaluation of functions to keep in memory. Default: 100 17 | :return: 18 | """ 19 | 20 | global _WITH_MEMOIZATION 21 | global _CACHE_SIZE 22 | 23 | old_status = bool(_WITH_MEMOIZATION) 24 | old_cache_size = int(_CACHE_SIZE) 25 | 26 | _WITH_MEMOIZATION = bool(switch) 27 | _CACHE_SIZE = int(cache_size) 28 | 29 | yield 30 | 31 | _WITH_MEMOIZATION = old_status 32 | _CACHE_SIZE = old_cache_size 33 | 34 | 35 | def memoize(method): 36 | """ 37 | A decorator for functions of sources which memoize the results of the last _CACHE_SIZE calls, 38 | 39 | :param method: method to be memoized 40 | :return: the decorated method 41 | """ 42 | 43 | cache = method.cache = collections.OrderedDict() 44 | 45 | # Put these two methods in the local space (faster) 46 | _get = cache.get 47 | _popitem = cache.popitem 48 | 49 | @functools.wraps(method) 50 | def memoizer(instance, x, *args, **kwargs): 51 | 52 | if not _WITH_MEMOIZATION or isinstance(x, u.Quantity): 53 | 54 | # Memoization is not active or using units, do not use memoization 55 | 56 | return method(instance, x, *args, **kwargs) 57 | 58 | # Create a tuple because a tuple is hashable 59 | 60 | unique_id = tuple( 61 | float(yy.value) for yy in list(instance.parameters.values()) 62 | ) + (x.size, x.min(), x.max()) 63 | 64 | # Create a unique identifier for this combination of inputs 65 | 66 | key = hash(unique_id) 67 | 68 | # Let's do it this way so we only look into the dictionary once 69 | 70 | result = _get(key) 71 | 72 | if result is not None: 73 | 74 | return result 75 | 76 | else: 77 | 78 | result = method(instance, x, *args, **kwargs) 79 | 80 | cache[key] = result 81 | 82 | if len(cache) > _CACHE_SIZE: 83 | # Remove half of the element (but at least 1, even if _CACHE_SIZE=1, which would be pretty idiotic ;-) ) 84 | [_popitem(False) for i in range(max(_CACHE_SIZE // 2, 1))] 85 | 86 | return result 87 | 88 | # Add the function as a "attribute" so we can access it 89 | memoizer.input_object = method 90 | 91 | return memoizer 92 | -------------------------------------------------------------------------------- /astromodels/core/my_yaml.py: -------------------------------------------------------------------------------- 1 | __author__ = "giacomov" 2 | 3 | # The purpose of this module is to customize yaml so that it will load ordered dictionaries instead of normal 4 | # ones. This way the order in which things are expressed in the file is maintained. 5 | 6 | import yaml as my_yaml 7 | 8 | import collections 9 | 10 | _mapping_tag = my_yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG 11 | 12 | 13 | def dict_representer(dumper, data): 14 | return dumper.represent_dict(list(data.items())) 15 | 16 | 17 | def dict_constructor(loader, node): 18 | return collections.OrderedDict(loader.construct_pairs(node)) 19 | 20 | 21 | my_yaml.add_representer(collections.OrderedDict, dict_representer) 22 | my_yaml.add_constructor(_mapping_tag, dict_constructor) 23 | -------------------------------------------------------------------------------- /astromodels/core/parameter_transformation.py: -------------------------------------------------------------------------------- 1 | import math 2 | from builtins import object 3 | 4 | import numpy as np 5 | import numba as nb 6 | 7 | 8 | @nb.vectorize 9 | def _pow(x, y): 10 | return math.pow(x, y) 11 | 12 | 13 | @nb.vectorize 14 | def _log10(x): 15 | return math.log10(x) 16 | 17 | 18 | class ParameterTransformation(object): 19 | def __init__(self, is_positive=False): 20 | 21 | self._is_positive = is_positive 22 | 23 | @property 24 | def is_positive(self): 25 | return self._is_positive 26 | 27 | def forward(self, external_value): 28 | 29 | raise NotImplementedError("You have to implement this") 30 | 31 | def backward(self, internal_value): 32 | 33 | raise NotImplementedError("You have to implement this") 34 | 35 | 36 | class LogarithmicTransformation(ParameterTransformation): 37 | def __init__(self): 38 | 39 | super(LogarithmicTransformation, self).__init__(is_positive=True) 40 | 41 | def forward(self, external_value, vector=False): 42 | 43 | # Throw an error if taking the logarithm of a negative number (or nan) 44 | 45 | with np.errstate(invalid="raise"): 46 | 47 | # math is 4 times faster than numpy here 48 | res = _log10(external_value) 49 | 50 | return res 51 | 52 | def backward(self, internal_value): 53 | 54 | # math is 10x faster than numpy or numba 55 | 56 | return _pow(10.0, internal_value) 57 | 58 | 59 | _known_transformations = {"log10": LogarithmicTransformation} 60 | 61 | 62 | def get_transformation(transformation_name): 63 | """ 64 | Returns an instance of a transformation by name 65 | 66 | :param transformation_name: 67 | :return: instance of transformation with provided name 68 | """ 69 | 70 | if transformation_name not in _known_transformations: 71 | 72 | raise ValueError("Transformation %s is not known" % transformation_name) 73 | 74 | else: 75 | 76 | return _known_transformations[transformation_name]() 77 | -------------------------------------------------------------------------------- /astromodels/core/polarization.py: -------------------------------------------------------------------------------- 1 | __author__ = "giacomov" 2 | 3 | 4 | from astromodels.core.tree import Node 5 | from astromodels.core.parameter import Parameter 6 | import numpy as np 7 | 8 | 9 | class Polarization(Node): 10 | def __init__(self, polarization_type="linear"): 11 | 12 | assert polarization_type in [ 13 | "linear", 14 | "stokes", 15 | ], "polarization must be linear or stokes" 16 | 17 | self._polarization_type = polarization_type 18 | 19 | Node.__init__(self, "polarization") 20 | 21 | @staticmethod 22 | def _get_parameter_from_input( 23 | number_or_parameter, minimum, maximum, what, desc, unit 24 | ): 25 | 26 | # Try to transform it to float, if it works than we transform it to a parameter 27 | 28 | try: 29 | 30 | number_or_parameter = float(number_or_parameter) 31 | 32 | except TypeError: 33 | 34 | assert isinstance(number_or_parameter, Parameter), ( 35 | "%s must be either a number or a " "parameter instance" % what 36 | ) 37 | 38 | # So this is a Parameter instance already. Enforce that it has the right maximum and minimum 39 | 40 | parameter = number_or_parameter 41 | 42 | assert parameter.min_value == minimum, "%s must have a minimum of %s" % ( 43 | what, 44 | minimum, 45 | ) 46 | assert parameter.max_value == maximum, "%s must have a maximum of %s" % ( 47 | what, 48 | maximum, 49 | ) 50 | 51 | else: 52 | 53 | # This was a float. Enforce that it has a legal value 54 | 55 | assert ( 56 | minimum <= number_or_parameter <= maximum 57 | ), "%s cannot have a value of %s, " "it must be %s <= %s <= %s" % ( 58 | what, 59 | number_or_parameter, 60 | minimum, 61 | what, 62 | maximum, 63 | ) 64 | 65 | parameter = Parameter( 66 | what, 67 | number_or_parameter, 68 | desc=desc, 69 | min_value=minimum, 70 | max_value=maximum, 71 | unit=unit, 72 | free=True, 73 | ) 74 | 75 | return parameter 76 | 77 | 78 | # TODO: add transform between polarizations 79 | 80 | class LinearPolarization(Polarization): 81 | def __init__(self, degree, angle): 82 | """ 83 | Linear parameterization of polarization 84 | 85 | :param degree: The polarization degree 86 | :param angle: The polarization angle 87 | """ 88 | super(LinearPolarization, self).__init__(polarization_type="linear") 89 | 90 | if callable(degree): 91 | self.degree = LinearParameter('degree', degree) 92 | else: 93 | self.degree = self._get_parameter_from_input(degree, 0, 100, 'degree', 'Polarization degree', 'dimensionless_unscaled') 94 | 95 | if callable(angle): 96 | self.angle = LinearParameter('angle', angle) 97 | else: 98 | self.angle = self._get_parameter_from_input(angle, 0, 180, 'angle', 'Polarization angle', 'deg') 99 | 100 | self._add_child(self.degree) 101 | self._add_child(self.angle) 102 | 103 | def __call__(self, energies, stokes): 104 | 105 | if stokes == 'Q': 106 | return self.degree(energies) * np.cos(2.0 * np.radians(self.angle(energies))) 107 | elif stokes == 'U': 108 | return self.degree(energies) * np.sin(2.0 * np.radians(self.angle(energies))) 109 | return 1 110 | 111 | 112 | class StokesPolarization(Polarization): 113 | def __init__(self, I=None, Q=None, U=None, V=None): 114 | """ 115 | Stokes parameterization of polarization 116 | """ 117 | super(StokesPolarization, self).__init__(polarization_type='stokes') 118 | self._Q = StokesParameter('Q', Q) 119 | self._add_child(self._Q) 120 | self._U = StokesParameter('U', U) 121 | self._add_child(self._U) 122 | 123 | def __call__(self, energies, stokes): 124 | if stokes == 'Q': 125 | return self._Q(energies) 126 | elif stokes == 'U': 127 | return self._U(energies) 128 | return 1 129 | 130 | # def to_linear_polarization(self): 131 | # # polarization angle 132 | # # psi = 0.5 * np.arctan2(U_bin, Q_bin) 133 | # 134 | # # polarization fraction 135 | # # frac = np.sqrt(Q_bin ** 2 + U_bin ** 2) / I_bin 136 | # 137 | # pass 138 | # 139 | # #angle = 0.5 * np.arctan2(se) 140 | # 141 | # 142 | 143 | 144 | class StokesParameter(Node): 145 | 146 | def __init__(self, name, value): 147 | 148 | assert name in ['I', 'Q', 'U', 'V'] 149 | Node.__init__(self, name) 150 | self._add_child(value) 151 | self.value = value 152 | 153 | def __call__(self, energies): 154 | return self.value(energies) 155 | 156 | 157 | class LinearParameter(Node): 158 | def __init__(self, name, value): 159 | assert name in ['degree', 'angle'] 160 | Node.__init__(self, name) 161 | self._add_child(value) 162 | self.value = value 163 | 164 | def __call__(self, energies): 165 | return self.value(energies) 166 | -------------------------------------------------------------------------------- /astromodels/core/serialization.py: -------------------------------------------------------------------------------- 1 | from future import standard_library 2 | 3 | standard_library.install_aliases() 4 | from astromodels.core.model import Model 5 | from astromodels.core.model_parser import ModelParser 6 | 7 | # We do not want to actually import anything from this module 8 | __all__ = [] 9 | 10 | # copyreg is called copy_reg in python2 11 | try: 12 | 13 | import copyreg # py3 14 | 15 | except ImportError: 16 | 17 | import copyreg as copyreg # py2 18 | 19 | 20 | # Model serializer 21 | def serialize_model(model): 22 | 23 | dict_with_types = model.to_dict_with_types() 24 | 25 | return unserialize_model, (dict_with_types,) 26 | 27 | 28 | def unserialize_model(dict_with_types): 29 | 30 | return ModelParser(model_dict=dict_with_types).get_model() 31 | 32 | 33 | # Register serialization/unserialization for Model 34 | copyreg.constructor(unserialize_model) 35 | copyreg.pickle(Model, serialize_model, unserialize_model) 36 | -------------------------------------------------------------------------------- /astromodels/core/spectral_component.py: -------------------------------------------------------------------------------- 1 | __author__ = "giacomov" 2 | 3 | from astromodels.core.polarization import Polarization 4 | from astromodels.core.tree import Node 5 | 6 | 7 | class SpectralComponent(Node): 8 | 9 | # This is needed to avoid problems when constructing the class, due to the fact that we are overriding 10 | # the __setattr__ and __getattr__ attributes 11 | 12 | _spectral_shape = None 13 | 14 | def __init__(self, name, shape, polarization=None): 15 | 16 | # Check that we can call the shape (i.e., it is a function) 17 | 18 | assert hasattr( 19 | shape, "__call__" 20 | ), "The shape must be callable (i.e., behave like a function)" 21 | 22 | self._spectral_shape = shape 23 | 24 | # Store the polarization 25 | 26 | if polarization is None: 27 | 28 | self._polarization = Polarization() 29 | 30 | else: 31 | 32 | self._polarization = polarization 33 | 34 | # Add shape and polarization as children 35 | 36 | Node.__init__(self, name) 37 | 38 | try: 39 | self._add_children([self._spectral_shape, self._polarization]) 40 | 41 | except TypeError: 42 | 43 | raise TypeError( 44 | "Couldn't instance the spectral component. Please verify that you are using an " 45 | "*instance* of a function, and not a class. For example, you need to use " 46 | "'%s()', and not '%s'." % (shape.__name__, shape.__name__) 47 | ) 48 | 49 | def _repr__base(self, rich_output): 50 | 51 | representation = "Spectral component %s\n" % self.name 52 | representation += " -shape: %s\n" % self._spectral_shape.name 53 | 54 | # Print the polarization only if it's not None 55 | 56 | if self._polarization is not None: 57 | representation += " -polarization: %s\n" % self._polarization.name 58 | 59 | return representation 60 | 61 | @property 62 | def shape(self): 63 | """ 64 | A generic name for the spectral shape. 65 | 66 | :return: the spectral shape instance 67 | """ 68 | return self._spectral_shape 69 | 70 | def __call__(self, energies, stokes=None): 71 | spec = self.shape(energies) 72 | if stokes == 'Q' or stokes == 'U': 73 | spec *= self._polarization(energies, stokes) 74 | return spec 75 | -------------------------------------------------------------------------------- /astromodels/core/thread_safe_unit_format.py: -------------------------------------------------------------------------------- 1 | # NOTE: the astropy.units.format pre-defined formatters use ply.yacc 2 | # for parsing, which unfortunately is not thread safe. This causes 3 | # all sort of weird problems when trying to use ipyparallel features. 4 | # Here we implement a format which is very simple, does not use 5 | # ply.yacc and is thread safe 6 | 7 | from builtins import str 8 | from builtins import map 9 | from builtins import zip 10 | import re 11 | from astropy.units.format.base import Base 12 | import astropy.units as u 13 | from functools import reduce 14 | 15 | 16 | # NOTE: the metaclass in Base will take care of registering 17 | # this format, which will be available in the u.Unit 18 | # constructor and in the .to_string method of the Unit and 19 | # Quantity classes as format='threadsafe' (case insensitive) 20 | 21 | 22 | def _format_one(xxx_todo_changeme): 23 | # This is for example 'cm-2' if base=cm and power=-2, 24 | # but only 'cm' if base=cm and power=1 25 | 26 | (base, power) = xxx_todo_changeme 27 | return "%s%s" % (base.to_string(), power if power != 1 else "") 28 | 29 | 30 | class ThreadSafe(Base): 31 | @classmethod 32 | def parse(cls, s): 33 | 34 | # Since often we get these strings from a YAML file, 35 | # we want to make sure there is no new line within 36 | # the string. 37 | assert "\n" not in s 38 | 39 | # We assume that the string is something like 40 | # [unit1]{power} [unit2]{power} ..., like for example: 41 | # "m s-1" (a speed) or "keV-1 cm-2 s-1" 42 | # (a differential flux in 1 / (keV cm**2 s)) 43 | # This is of course the format that is the output of 44 | # our to_string method(). See there for details 45 | 46 | tokens = re.findall("([a-zA-z]+)(-?\+?[0-9]+)?", s) 47 | 48 | # tokens is a list of tuples of the type [(unit name, power), ...] 49 | # Here we build a list like [u.m, u.s**(-1), ...] 50 | r = [] 51 | 52 | for (unit, power) in tokens: 53 | 54 | # Get the primitive unit from the units module 55 | thisr = getattr(u, unit) 56 | 57 | # If needed, raise it to the power 58 | if power != "": 59 | 60 | thisr = thisr**power 61 | 62 | r.append(thisr) 63 | 64 | # Now we multiply the units 65 | # so that we get something like u.m * u.s**(-1) 66 | 67 | return reduce(lambda x, y: x * y, r) 68 | 69 | @classmethod 70 | def to_string(cls, unit): 71 | 72 | # The Unit class has two lists: bases (which are primitive units) 73 | # and powers (the correspondent powers). Thus we just need to use 74 | # those and encode them in a string like [unit1]{power} [unit2]{power} ... 75 | 76 | tokens = list(map(_format_one, list(zip(unit.bases, unit.powers)))) 77 | 78 | return str(" ".join(tokens)) 79 | -------------------------------------------------------------------------------- /astromodels/core/tree.py: -------------------------------------------------------------------------------- 1 | import collections 2 | from typing import Any, Dict 3 | 4 | from future import standard_library 5 | 6 | from astromodels.core.node_type import NodeBase 7 | from astromodels.utils.io import display 8 | from astromodels.utils.logging import setup_logger 9 | from astromodels.utils.valid_variable import is_valid_variable_name 10 | 11 | standard_library.install_aliases() 12 | 13 | 14 | class DuplicatedNode(Exception): 15 | pass 16 | 17 | 18 | class ProtectedAttribute(RuntimeError): 19 | pass 20 | 21 | 22 | class NonExistingAttribute(RuntimeWarning): 23 | pass 24 | 25 | 26 | log = setup_logger(__name__) 27 | 28 | 29 | class Node(NodeBase): 30 | 31 | # This apparently dumb constructor is needed otherwise pickle will fail 32 | 33 | def __init__(self, name: str): 34 | 35 | if not is_valid_variable_name(name): 36 | 37 | log.error( 38 | "Illegal characters in name %s. You can only use letters and numbers, " 39 | "and _" % name 40 | ) 41 | 42 | raise AssertionError() 43 | 44 | if not name != "name": 45 | 46 | log.error("You cannot call a node 'name', it is reserved.") 47 | 48 | raise AssertionError() 49 | 50 | NodeBase.__init__(self, name) 51 | 52 | ######################################################################### 53 | 54 | def __hash__(self): 55 | 56 | # return the hash of the path 57 | # anytime we need a hash should 58 | # be after a model is finalized 59 | # so it should be static 60 | 61 | return hash(self._get_path()) 62 | 63 | def to_dict(self, minimal: bool = False) -> Dict[str, Any]: 64 | """ """ 65 | this_dict: Dict[str, Any] = collections.OrderedDict() 66 | 67 | for child in self._get_children(): 68 | 69 | this_dict[child.name] = child.to_dict(minimal) 70 | 71 | return this_dict 72 | 73 | # This is used by dir() and by the autocompletion in Ipython 74 | 75 | def __dir__(self): 76 | 77 | # Get the names of the attributes of the class 78 | l = list(self.__class__.__dict__.keys()) 79 | 80 | # Get all the children 81 | l.extend([child.name for child in self._get_children()]) 82 | 83 | return l 84 | 85 | def _repr__base(self, rich_output): 86 | 87 | return f"Node with name {self.name}" 88 | 89 | def __repr__(self): 90 | """ 91 | Textual representation for console 92 | 93 | :return: representation 94 | """ 95 | 96 | return self._repr__base(rich_output=False) 97 | 98 | def _repr_html_(self): 99 | """ 100 | HTML representation for the IPython notebook 101 | 102 | :return: HTML representation 103 | """ 104 | 105 | return self._repr__base(rich_output=True) 106 | 107 | def display(self): 108 | """ 109 | Display information about the point source. 110 | 111 | :return: (none) 112 | """ 113 | 114 | # This will automatically choose the best representation among repr and repr_html 115 | 116 | display(self) 117 | -------------------------------------------------------------------------------- /astromodels/core/units.py: -------------------------------------------------------------------------------- 1 | from builtins import object 2 | 3 | __author__ = "giacomov" 4 | 5 | import collections 6 | 7 | import astropy.units as u 8 | 9 | from astromodels.utils.pretty_list import dict_to_list 10 | 11 | # This module keeps the configuration of the units used in astromodels 12 | 13 | # Pre-defined values 14 | 15 | _ENERGY = u.keV 16 | _TIME = u.s 17 | _ANGLE = u.deg 18 | _AREA = u.cm**2 19 | 20 | 21 | class UnknownUnit(Exception): 22 | pass 23 | 24 | 25 | class UnitMismatch(Exception): 26 | pass 27 | 28 | 29 | def _check_unit(new_unit, old_unit): 30 | """ 31 | Check that the new unit is compatible with the old unit for the quantity described by variable_name 32 | 33 | :param new_unit: instance of astropy.units.Unit 34 | :param old_unit: instance of astropy.units.Unit 35 | :return: nothin 36 | """ 37 | 38 | try: 39 | 40 | new_unit.physical_type 41 | 42 | except AttributeError: 43 | 44 | raise UnitMismatch( 45 | "The provided unit (%s) has no physical type. Was expecting a unit for %s" 46 | % (new_unit, old_unit.physical_type) 47 | ) 48 | 49 | if new_unit.physical_type != old_unit.physical_type: 50 | 51 | raise UnitMismatch( 52 | "Physical type mismatch: you provided a unit for %s instead of a unit for %s" 53 | % (new_unit.physical_type, old_unit.physical_type) 54 | ) 55 | 56 | 57 | class _AstromodelsUnits(object): 58 | """ 59 | Store the fundamental units of time, energy, angle and area to be used in astromodels. 60 | """ 61 | 62 | def __init__( 63 | self, energy_unit=None, time_unit=None, angle_unit=None, area_unit=None 64 | ): 65 | 66 | if energy_unit is None: 67 | energy_unit = _ENERGY 68 | if time_unit is None: 69 | time_unit = _TIME 70 | if angle_unit is None: 71 | angle_unit = _ANGLE 72 | if area_unit is None: 73 | area_unit = _AREA 74 | 75 | self._units = collections.OrderedDict() 76 | 77 | self._units["energy"] = energy_unit 78 | self._units["time"] = time_unit 79 | self._units["angle"] = angle_unit 80 | self._units["area"] = area_unit 81 | 82 | # This __new__ method add the properties to the class. We could have achieved the same with a metaclass, 83 | # but this method is more clearer, for a tiny performance penalty. Consider also that under normal circumstances 84 | # this class will be only created once per session 85 | 86 | def __new__(cls, *args, **kwargs): 87 | 88 | cls.energy = property(*(cls._create_property("energy"))) 89 | cls.time = property(*(cls._create_property("time"))) 90 | cls.angle = property(*(cls._create_property("angle"))) 91 | cls.area = property(*(cls._create_property("area"))) 92 | 93 | obj = super(_AstromodelsUnits, cls).__new__(cls) 94 | 95 | return obj 96 | 97 | def get(self, what): 98 | 99 | return self._get_unit(what) 100 | 101 | def _set_unit(self, what, new_unit): 102 | 103 | try: 104 | 105 | old_unit = self._units[what] 106 | 107 | except KeyError: 108 | 109 | raise UnknownUnit( 110 | "You can only assign units for energy, time, angle and area. Don't know " 111 | "anything about %s" % what 112 | ) 113 | 114 | # This allows to use strings in place of Unit instances as new_unit 115 | 116 | new_unit = u.Unit(new_unit) 117 | 118 | # Check that old and new unit are for the appropriate quantity 119 | 120 | _check_unit(new_unit, old_unit) 121 | 122 | # set the new unit 123 | self._units[what] = new_unit 124 | 125 | def _get_unit(self, what): 126 | 127 | try: 128 | 129 | return self._units[what] 130 | 131 | except KeyError: 132 | 133 | raise UnknownUnit("%s is not a fundamental unit" % what) 134 | 135 | # This is a function which generates the elements needed to make a property.photon 136 | # Just a trick to avoid having to duplicate the same code for each unit. 137 | # It is called in __new__ 138 | 139 | @staticmethod 140 | def _create_property(what): 141 | 142 | return ( 143 | lambda self: self._get_unit(what), 144 | lambda self, new_unit: self._set_unit(what, new_unit), 145 | "Sets or gets the unit for %s" % what, 146 | ) 147 | 148 | # Add the == and != operators 149 | def __eq__(self, other): 150 | 151 | return other.to_dict() == self.to_dict() 152 | 153 | def __ne__(self, other): 154 | 155 | return other.to_dict() != self.to_dict() 156 | 157 | def _repr__base(self, rich_output): 158 | 159 | return dict_to_list(self._units, html=rich_output) 160 | 161 | def to_dict(self, minimal=False): 162 | 163 | return self._units 164 | 165 | 166 | # This is a factory which will always return the same instance of the _AstromodelsUnits class 167 | 168 | 169 | class _AstromodelsUnitsFactory(object): 170 | 171 | _instance = None 172 | 173 | def __call__(self, *args, **kwds): 174 | 175 | if self._instance is None: 176 | 177 | # Create and return a new instance 178 | 179 | self._instance = _AstromodelsUnits(*args, **kwds) 180 | 181 | return self._instance 182 | 183 | else: 184 | 185 | # Use the instance already created 186 | 187 | return self._instance 188 | 189 | 190 | # Create the factory to be used in the program 191 | 192 | get_units = _AstromodelsUnitsFactory() 193 | -------------------------------------------------------------------------------- /astromodels/data/dark_matter/dmSpecTab.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/dark_matter/dmSpecTab.npy -------------------------------------------------------------------------------- /astromodels/data/log_theme.ini: -------------------------------------------------------------------------------- 1 | [styles] 2 | 3 | # Banner 4 | h1 = deep_sky_blue3 5 | 6 | # Status 7 | status.spinner = cyan2 8 | status.text = deep_sky_blue4 9 | 10 | # Console 11 | repr.filename = blue 12 | repr.number = white 13 | repr.path = grey37 14 | repr.str = grey37 15 | repr.tag_name = white 16 | repr.url = not bold not italic underline grey84 17 | log.time = green1 18 | log.message = bold grey78 19 | 20 | # Logging 21 | logging.level.debug = blue_violet 22 | logging.level.error = blink bold bright_red 23 | logging.level.info = medium_spring_green 24 | logging.level.warning = medium_orchid 25 | logging.level.debug_node = light_goldenrod1 26 | -------------------------------------------------------------------------------- /astromodels/data/past_1D_values.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/past_1D_values.h5 -------------------------------------------------------------------------------- /astromodels/data/tests/black_hole.yaml: -------------------------------------------------------------------------------- 1 | black hole: 2 | 3 | point source: 4 | 5 | RA : 6 | Dec : 7 | 8 | spectrum : 9 | 10 | main : 11 | 12 | powerlaw: 13 | 14 | logK : 15 | index : 16 | 17 | polarization : 18 | 19 | linear : 20 | 21 | degree : 22 | 23 | poly3 : 24 | 25 | function of: 26 | 27 | - energy 28 | 29 | p0 : 30 | p1 : 31 | p2 : 32 | 33 | angle : 34 | 35 | poly1 : 36 | 37 | function of: 38 | 39 | - energy 40 | 41 | p0 : 42 | p1 : 43 | 44 | pulsar : 45 | 46 | point source : 47 | 48 | RA : 49 | Dec : 50 | 51 | spectrum : 52 | 53 | powerlaw_expcut : 54 | 55 | logK : 56 | 57 | template : 58 | 59 | file : $TEMPLATE/flux.fits 60 | 61 | function of : 62 | 63 | time 64 | 65 | norm : 66 | 67 | index : 68 | 69 | template : 70 | 71 | file : $TEMPLATE/index.fits 72 | 73 | function of : 74 | 75 | time 76 | 77 | norm : 78 | 79 | 80 | Ec : 81 | 82 | template : 83 | 84 | file : $TEMPLATE/Ec.fits 85 | 86 | function of : 87 | 88 | time 89 | 90 | norm : 91 | 92 | polarization : 93 | 94 | linear : 95 | 96 | degree : 97 | 98 | template2d : 99 | 100 | file : $TEMPLATE/t2d_degree.fits 101 | 102 | function of : 103 | 104 | - time 105 | - energy 106 | 107 | norm : 108 | 109 | angle : 110 | 111 | template2d : 112 | 113 | file : $TEMPLATE/t2d_angle.fits 114 | 115 | function of : 116 | 117 | - time 118 | - energy 119 | 120 | norm : 121 | 122 | -------------------------------------------------------------------------------- /astromodels/data/tests/old_table.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/tests/old_table.h5 -------------------------------------------------------------------------------- /astromodels/data/tests/simple_source.yaml: -------------------------------------------------------------------------------- 1 | test_source : 2 | 3 | point source: 4 | 5 | position: 6 | 7 | ra : {value: 0.0} 8 | dec : {value: 0.0} 9 | 10 | spectrum : 11 | 12 | main : 13 | 14 | shape: 15 | 16 | powerlaw : 17 | 18 | logK : {value: 1} 19 | index : {value: -2} 20 | piv : {value: 100} 21 | 22 | polarization : 23 | 24 | linear : 25 | 26 | degree : 27 | angle : 28 | 29 | phase: 30 | 31 | p : 32 | p_dot : 33 | p0 : 34 | 35 | other : 36 | 37 | shape: 38 | 39 | powerlaw : 40 | 41 | logK : {value: 1} 42 | index : {value: -2} 43 | piv : {value: 200} 44 | 45 | polarization : 46 | 47 | stokes: 48 | 49 | I : 50 | Q : 51 | U : 52 | V : 53 | -------------------------------------------------------------------------------- /astromodels/data/tests/test.yaml: -------------------------------------------------------------------------------- 1 | crab pulsar : 2 | 3 | point source : 4 | 5 | position: 6 | 7 | RA : { value : 83.633083, fix : yes} 8 | Dec : { value : 22.014500, free : no} 9 | equinox: J2000 10 | 11 | spectrum : 12 | 13 | synchrotron : 14 | 15 | shape: 16 | 17 | powerlaw : 18 | 19 | logK : { value : 1, min : -40, max : 40, unit : photons/keV/cm2 } 20 | index : { value : -2, min : -10, max : 10 } 21 | 22 | polarization : 23 | 24 | linear : 25 | 26 | degree : { value : 0.5, min : 0, max : 1 } 27 | angle : { value : 0.5, min : 0, max : 360, unit : deg } 28 | 29 | 30 | inverse compton : 31 | 32 | powerlaw : 33 | 34 | logK : { value : 1, min : -40, max : 40, unit : photons/keV/cm2 } 35 | index : { value : -2, min : -10, max : 10 } 36 | 37 | polarization : 38 | 39 | linear : 40 | 41 | degree : 42 | 43 | weird_2d : 44 | 45 | function of: 46 | 47 | - energy 48 | - time 49 | 50 | p1 : { value : 1.2, min : -40, max : 40 } 51 | p2 : { value : -0.5, min : -40, max : 40 } 52 | 53 | angle : { value : 0.5, min : 0, max : 360, unit : deg } 54 | 55 | 56 | crab nebula : 57 | 58 | extended source : 59 | 60 | template : 61 | 62 | filename : $TEMPLATES/crab_nebula.fits 63 | 64 | normalization : { value : 1.0, min : 0.8, max : 1.2 } 65 | 66 | isotropic background: 67 | 68 | extended source: 69 | 70 | isotropic : 71 | 72 | spectrum : 73 | 74 | powerlaw : 75 | 76 | logK : { value : 1, min : -40, max : 40, unit : photons/keV/cm2 } 77 | index : { value : -2, min : -10, max : 10 } 78 | 79 | fake source : 80 | 81 | extended source : 82 | 83 | gaussian_2d : 84 | 85 | RA : { value : 80.1, min : 0, max : 360, fix : yes, unit : deg } 86 | Dec : { value : 23.25, min : 0, max : 180, fix : yes, unit : deg } 87 | 88 | sigma_x : { value : 1, min : 0, max : 40 } 89 | sigma_y : 2 * sigma_x 90 | 91 | angle : { value : 0, min : 0, max : 180 } 92 | 93 | spectrum : 94 | 95 | powerlaw : 96 | 97 | logK : 98 | 99 | powerlaw : 100 | 101 | function of: 102 | 103 | - time 104 | 105 | logK : { value : 1, min : 0, max : 40 } 106 | index : { value : -1, min : 0, max : 40 } 107 | 108 | index : { value : -2, min : 0, max : 40 } 109 | -------------------------------------------------------------------------------- /astromodels/data/tests/test_xspec_table_model.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/tests/test_xspec_table_model.fits -------------------------------------------------------------------------------- /astromodels/data/xsect/xsect_phabs_angr.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/xsect/xsect_phabs_angr.fits -------------------------------------------------------------------------------- /astromodels/data/xsect/xsect_phabs_aspl.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/xsect/xsect_phabs_aspl.fits -------------------------------------------------------------------------------- /astromodels/data/xsect/xsect_tbabs_angr.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/xsect/xsect_tbabs_angr.fits -------------------------------------------------------------------------------- /astromodels/data/xsect/xsect_tbabs_aspl.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/xsect/xsect_tbabs_aspl.fits -------------------------------------------------------------------------------- /astromodels/data/xsect/xsect_tbabs_wilm.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/xsect/xsect_tbabs_wilm.fits -------------------------------------------------------------------------------- /astromodels/data/xsect/xsect_wabs_angr.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/data/xsect/xsect_wabs_angr.fits -------------------------------------------------------------------------------- /astromodels/functions/__init__.py: -------------------------------------------------------------------------------- 1 | from .functions_1D import ( 2 | Band, 3 | Band_Calderone, 4 | Band_grbm, 5 | Blackbody, 6 | Broken_powerlaw, 7 | Constant, 8 | Cubic, 9 | Cutoff_powerlaw, 10 | Cutoff_powerlaw_Ep, 11 | DiracDelta, 12 | Exponential_cutoff, 13 | Inverse_cutoff_powerlaw, 14 | Line, 15 | Log_parabola, 16 | ModifiedBlackbody, 17 | NonDissipativePhotosphere, 18 | NonDissipativePhotosphere_Deep, 19 | PhAbs, 20 | Powerlaw, 21 | Powerlaw_Eflux, 22 | Powerlaw_flux, 23 | Quadratic, 24 | Quartic, 25 | Sin, 26 | SmoothlyBrokenPowerLaw, 27 | DoubleSmoothlyBrokenPowerlaw, 28 | Standard_Rv, 29 | StepFunction, 30 | StepFunctionUpper, 31 | GenericFunction, 32 | Super_cutoff_powerlaw, 33 | TbAbs, 34 | WAbs, 35 | ZDust, 36 | get_polynomial, 37 | has_atomdb, 38 | has_ebltable, 39 | has_gsl, 40 | has_naima, 41 | ) 42 | 43 | if has_naima: 44 | from .functions_1D import Synchrotron 45 | 46 | if has_gsl: 47 | from .functions_1D import Cutoff_powerlaw_flux 48 | 49 | if has_ebltable: 50 | from .functions_1D import EBLattenuation 51 | 52 | if has_atomdb: 53 | 54 | from .functions_1D import APEC, VAPEC 55 | 56 | from .dark_matter.dm_models import DMFitFunction, DMSpectra 57 | from .function import ( 58 | Function1D, 59 | Function2D, 60 | Function3D, 61 | FunctionMeta, 62 | ModelAssertionViolation, 63 | ) 64 | from .functions_2D import ( 65 | Asymm_Gaussian_on_sphere, 66 | Disk_on_sphere, 67 | Ellipse_on_sphere, 68 | Gaussian_on_sphere, 69 | Latitude_galactic_diffuse, 70 | Power_law_on_sphere, 71 | SpatialTemplate_2D, 72 | ) 73 | from .functions_3D import ( 74 | Continuous_injection_diffusion, 75 | Continuous_injection_diffusion_ellipse, 76 | Continuous_injection_diffusion_legacy, 77 | GalPropTemplate_3D, 78 | ) 79 | from .priors import ( 80 | Cauchy, 81 | Cosine_Prior, 82 | Gaussian, 83 | Log_normal, 84 | Log_uniform_prior, 85 | Truncated_gaussian, 86 | Uniform_prior, 87 | Beta, 88 | Gamma, 89 | Exponential, 90 | Powerlaw_Prior, 91 | ) 92 | from .template_model import ( 93 | MissingDataFile, 94 | TemplateModel, 95 | TemplateModelFactory, 96 | XSPECTableModel, 97 | ) 98 | 99 | __all__ = [ 100 | "Band", 101 | "Band_Calderone", 102 | "Band_grbm", 103 | "Broken_powerlaw", 104 | "Cutoff_powerlaw", 105 | "Cutoff_powerlaw_Ep", 106 | "Inverse_cutoff_powerlaw", 107 | "Powerlaw", 108 | "Powerlaw_Eflux", 109 | "Powerlaw_flux", 110 | "SmoothlyBrokenPowerLaw", 111 | "DoubleSmoothlyBrokenPowerlaw", 112 | "Super_cutoff_powerlaw", 113 | "Constant", 114 | "Cubic", 115 | "DiracDelta", 116 | "Exponential_cutoff", 117 | "Line", 118 | "Quadratic", 119 | "Sin", 120 | "StepFunction", 121 | "StepFunctionUpper", 122 | "GenericFunction", 123 | "PhAbs", 124 | "TbAbs", 125 | "WAbs", 126 | "Asymm_Gaussian_on_sphere", 127 | "Disk_on_sphere", 128 | "Ellipse_on_sphere", 129 | "Gaussian_on_sphere", 130 | "Latitude_galactic_diffuse", 131 | "Power_law_on_sphere", 132 | "SpatialTemplate_2D", 133 | "Continuous_injection_diffusion", 134 | "Continuous_injection_diffusion_ellipse", 135 | "Continuous_injection_diffusion_legacy", 136 | "GalPropTemplate_3D", 137 | "DMSpectra", 138 | "DMFitFunction", 139 | "Cauchy", 140 | "Cosine_Prior", 141 | "Gaussian", 142 | "Log_normal", 143 | "Log_uniform_prior", 144 | "Truncated_gaussian", 145 | "Uniform_prior", 146 | "Beta", 147 | "Gamma", 148 | "Exponential", 149 | "Powerlaw_Prior", 150 | "TemplateModel", 151 | "TemplateModelFactory", 152 | "XSPECTableModel", 153 | "MissingDataFile", 154 | "Log_parabola", 155 | "Blackbody", 156 | "Function1D", 157 | "Function2D", 158 | "Function3D", 159 | "FunctionMeta", 160 | "ModelAssertionViolation", 161 | "Quartic", 162 | "get_polynomial", 163 | "ZDust", 164 | "Standard_Rv", 165 | "ModifiedBlackbody", 166 | "NonDissipativePhotosphere", 167 | "NonDissipativePhotosphere_Deep", 168 | ] 169 | 170 | if has_atomdb: 171 | __all__.extend(["APEC", "VAPEC"]) 172 | 173 | if has_gsl: 174 | 175 | __all__.extend(["Cutoff_powerlaw_flux"]) 176 | 177 | if has_naima: 178 | 179 | __all__.extend(["Synchrotron"]) 180 | 181 | if has_ebltable: 182 | 183 | __all__.extend(["EBLattenuation"]) 184 | -------------------------------------------------------------------------------- /astromodels/functions/dark_matter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/astromodels/functions/dark_matter/__init__.py -------------------------------------------------------------------------------- /astromodels/functions/functions_1D/__init__.py: -------------------------------------------------------------------------------- 1 | from .blackbody import ( 2 | Blackbody, 3 | ModifiedBlackbody, 4 | NonDissipativePhotosphere, 5 | NonDissipativePhotosphere_Deep, 6 | ) 7 | from .functions import ( 8 | DiracDelta, 9 | Exponential_cutoff, 10 | Log_parabola, 11 | Sin, 12 | StepFunction, 13 | StepFunctionUpper, 14 | has_gsl, 15 | has_naima, 16 | GenericFunction 17 | ) 18 | 19 | if has_naima: 20 | from .functions import Synchrotron 21 | 22 | if has_gsl: 23 | 24 | from .functions import Cutoff_powerlaw_flux 25 | 26 | from .absorption import PhAbs, TbAbs, WAbs, has_ebltable 27 | from .apec import has_atomdb 28 | 29 | if has_ebltable: 30 | from .absorption import EBLattenuation 31 | 32 | 33 | from .extinction import Standard_Rv, ZDust 34 | from .polynomials import ( 35 | Constant, 36 | Cubic, 37 | Line, 38 | Quadratic, 39 | Quartic, 40 | get_polynomial, 41 | ) 42 | from .powerlaws import ( 43 | Band, 44 | Band_Calderone, 45 | Band_grbm, 46 | Broken_powerlaw, 47 | Cutoff_powerlaw, 48 | Cutoff_powerlaw_Ep, 49 | Inverse_cutoff_powerlaw, 50 | Powerlaw, 51 | Powerlaw_Eflux, 52 | Powerlaw_flux, 53 | SmoothlyBrokenPowerLaw, 54 | Super_cutoff_powerlaw, 55 | DoubleSmoothlyBrokenPowerlaw, 56 | ) 57 | 58 | if has_atomdb: 59 | 60 | from .apec import APEC, VAPEC 61 | 62 | 63 | __all__ = [ 64 | "Band", 65 | "Band_Calderone", 66 | "Band_grbm", 67 | "Broken_powerlaw", 68 | "Cutoff_powerlaw", 69 | "Cutoff_powerlaw_Ep", 70 | "Inverse_cutoff_powerlaw", 71 | "Powerlaw", 72 | "Powerlaw_Eflux", 73 | "Powerlaw_flux", 74 | "SmoothlyBrokenPowerLaw", 75 | "DoubleSmoothlyBrokenPowerlaw", 76 | "Super_cutoff_powerlaw", 77 | "Constant", 78 | "Cubic", 79 | "DiracDelta", 80 | "Exponential_cutoff", 81 | "Line", 82 | "Quadratic", 83 | "Sin", 84 | "StepFunction", 85 | "StepFunctionUpper", 86 | "GenericFunction", 87 | "PhAbs", 88 | "TbAbs", 89 | "WAbs", 90 | "Log_parabola", 91 | "Blackbody", 92 | "ModifiedBlackbody", 93 | "NonDissipativePhotosphere", 94 | "NonDissipativePhotosphere_Deep", 95 | "Quartic", 96 | "get_polynomial", 97 | "ZDust", 98 | "Standard_Rv", 99 | ] 100 | 101 | if has_atomdb: 102 | __all__.extend(["APEC", "VAPEC"]) 103 | 104 | 105 | if has_gsl: 106 | 107 | __all__.extend(["Cutoff_powerlaw_flux"]) 108 | 109 | 110 | if has_naima: 111 | 112 | __all__.extend(["Synchrotron"]) 113 | 114 | 115 | if has_ebltable: 116 | 117 | __all__.extend(["EBLattenuation"]) 118 | -------------------------------------------------------------------------------- /astromodels/functions/functions_1D/blackbody.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import astropy.units as astropy_units 4 | from past.utils import old_div 5 | 6 | import astromodels.functions.numba_functions as nb_func 7 | from astromodels.functions.function import ( 8 | Function1D, 9 | FunctionMeta, 10 | ) 11 | 12 | 13 | class Blackbody(Function1D, metaclass=FunctionMeta): 14 | r""" 15 | 16 | description : 17 | A blackbody function 18 | 19 | latex : $f(x) = K \frac{x^2}{\exp(\frac{x}{kT}) -1} $ 20 | 21 | parameters : 22 | K : 23 | desc : 24 | initial value : 1e-4 25 | min : 0. 26 | is_normalization : True 27 | 28 | kT : 29 | desc : temperature of the blackbody 30 | initial value : 30.0 31 | min: 0. 32 | 33 | """ 34 | 35 | def _set_units(self, x_unit, y_unit): 36 | # The normalization has the same units as y 37 | self.K.unit = old_div(y_unit, (x_unit**2)) 38 | 39 | # The break point has always the same dimension as the x variable 40 | self.kT.unit = x_unit 41 | 42 | def evaluate(self, x, K, kT): 43 | 44 | if isinstance(x, astropy_units.Quantity): 45 | 46 | K_ = K.value 47 | kT_ = kT.value 48 | 49 | x_ = x.value 50 | 51 | unit_ = self.y_unit 52 | 53 | else: 54 | unit_ = 1.0 55 | K_, kT_, x_, = ( 56 | K, 57 | kT, 58 | x, 59 | ) 60 | 61 | result = nb_func.bb_eval(x_, K_, kT_) 62 | 63 | return result * unit_ 64 | 65 | 66 | class ModifiedBlackbody(Function1D, metaclass=FunctionMeta): 67 | r""" 68 | 69 | description : 70 | A blackbody function 71 | 72 | latex : $f(x) = K \frac{x^2}{\exp(\frac{x}{kT}) -1} $ 73 | 74 | parameters : 75 | K : 76 | desc : 77 | initial value : 1e-4 78 | min : 0. 79 | is_normalization : True 80 | 81 | kT : 82 | desc : temperature of the blackbody 83 | initial value : 30.0 84 | min: 0. 85 | 86 | """ 87 | 88 | def _set_units(self, x_unit, y_unit): 89 | # The normalization has the same units as y 90 | self.K.unit = y_unit / x_unit 91 | 92 | # The break point has always the same dimension as the x variable 93 | self.kT.unit = x_unit 94 | 95 | def evaluate(self, x, K, kT): 96 | 97 | if isinstance(x, astropy_units.Quantity): 98 | 99 | K_ = K.value 100 | kT_ = kT.value 101 | 102 | x_ = x.value 103 | 104 | unit_ = self.y_unit 105 | 106 | else: 107 | 108 | unit_ = 1.0 109 | K_, kT_, x_, = ( 110 | K, 111 | kT, 112 | x, 113 | ) 114 | 115 | result = nb_func.mbb_eval(x_, K_, kT_) 116 | 117 | return result * unit_ 118 | 119 | 120 | class NonDissipativePhotosphere(Function1D, metaclass=FunctionMeta): 121 | r""" 122 | 123 | description : 124 | Non-dissipative photosphere of a GRB occuring above the saturation radius 125 | Acuner, Z., Ryde, F. & Yu, H.-F. Mon Not R Astron Soc 487, 5508–5519 (2019). 126 | latex : $N_{\mathrm{E}}=K\left(\frac{E}{E_{\mathrm{pivot}}}\right)^{0.4} e^{-\left(\frac{E}{E_{c}}\right)^{0.65}}$ 127 | 128 | parameters : 129 | K : 130 | desc : 131 | initial value : 1e-4 132 | min : 0. 133 | is_normalization : True 134 | 135 | ec : 136 | desc : peak energy 137 | initial value : 200.0 138 | min: 0. 139 | 140 | piv : 141 | desc : the pivot energy 142 | initial value: 100. 143 | fix: True 144 | 145 | 146 | """ 147 | 148 | def _set_units(self, x_unit, y_unit): 149 | # The normalization has the same units as y 150 | self.K.unit = y_unit 151 | 152 | # The break point has always the same dimension as the x variable 153 | self.ec.unit = x_unit 154 | 155 | self.piv.unit = x_unit 156 | 157 | def evaluate(self, x, K, ec, piv): 158 | 159 | if isinstance(x, astropy_units.Quantity): 160 | 161 | K_ = K.value 162 | ec_ = ec.value 163 | piv_ = piv.value 164 | 165 | x_ = x.value 166 | 167 | unit_ = self.y_unit 168 | 169 | else: 170 | unit_ = 1.0 171 | K_, ec_, x_, piv_ = (K, ec, x, piv) 172 | 173 | result = nb_func.non_diss_photoshere_generic(x_, K_, ec_, piv_, 0.4, 0.65) 174 | 175 | return result * unit_ 176 | 177 | 178 | class NonDissipativePhotosphere_Deep(Function1D, metaclass=FunctionMeta): 179 | r""" 180 | 181 | description : 182 | Non-dissipative photosphere of a GRB occuring BELOW the saturation radius. 183 | Acuner, Z., Ryde, F. & Yu, H.-F. Mon Not R Astron Soc 487, 5508–5519 (2019). 184 | 185 | 186 | latex : $N_{\mathrm{E}}=K\left(\frac{E}{E_{\mathrm{pivot}}}\right)^{0.66} e^{-\left(\frac{E}{E_{c}}\right)}$ 187 | 188 | parameters : 189 | K : 190 | desc : 191 | initial value : 1e-4 192 | min : 0. 193 | is_normalization : True 194 | 195 | ec : 196 | desc : peak energy 197 | initial value : 200.0 198 | min: 0. 199 | 200 | piv : 201 | desc : the pivot energy 202 | initial value: 100. 203 | fix: True 204 | 205 | 206 | """ 207 | 208 | def _set_units(self, x_unit, y_unit): 209 | # The normalization has the same units as y 210 | self.K.unit = y_unit 211 | 212 | # The break point has always the same dimension as the x variable 213 | self.ec.unit = x_unit 214 | 215 | self.piv.unit = x_unit 216 | 217 | def evaluate(self, x, K, ec, piv): 218 | 219 | if isinstance(x, astropy_units.Quantity): 220 | 221 | K_ = K.value 222 | ec_ = ec.value 223 | piv_ = piv.value 224 | 225 | x_ = x.value 226 | 227 | unit_ = self.y_unit 228 | 229 | else: 230 | unit_ = 1.0 231 | K_, ec_, x_, piv_ = (K, ec, x, piv) 232 | 233 | result = nb_func.non_diss_photoshere_generic(x_, K_, ec_, piv_, 0.66, 1.0) 234 | 235 | return result * unit_ 236 | -------------------------------------------------------------------------------- /astromodels/functions/functions_1D/polynomials.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | from astromodels.core.units import get_units 4 | from astromodels.functions.function import ( 5 | Function1D, 6 | FunctionMeta, 7 | ) 8 | 9 | 10 | def get_polynomial(order: int) -> Function1D: 11 | """ 12 | get a polynomial function of order 13 | 14 | :param order: the order of the polynomical 15 | :type order: int 16 | :returns: 17 | 18 | """ 19 | return [Constant(), Line(), Quadratic(), Cubic(), Quartic()][order] 20 | 21 | 22 | class Constant(Function1D, metaclass=FunctionMeta): 23 | r""" 24 | description : 25 | 26 | Return k 27 | 28 | latex : $ k $ 29 | 30 | parameters : 31 | 32 | k : 33 | 34 | desc : Constant value 35 | initial value : 0 36 | 37 | """ 38 | 39 | def _set_units(self, x_unit, y_unit): 40 | self.k.unit = y_unit 41 | 42 | def evaluate(self, x, k): 43 | 44 | return k * np.ones(np.shape(x)) 45 | 46 | 47 | class Line(Function1D, metaclass=FunctionMeta): 48 | r""" 49 | description : 50 | 51 | A linear function 52 | 53 | latex : $ b * x + a $ 54 | 55 | parameters : 56 | 57 | a : 58 | 59 | desc : intercept 60 | initial value : 0 61 | 62 | b : 63 | 64 | desc : coeff 65 | initial value : 1 66 | 67 | """ 68 | 69 | def _set_units(self, x_unit, y_unit): 70 | # a has units of y_unit / x_unit, so that a*x has units of y_unit 71 | self.a.unit = y_unit 72 | 73 | # b has units of y 74 | self.b.unit = y_unit / x_unit 75 | 76 | def evaluate(self, x, a, b): 77 | return b * x + a 78 | 79 | 80 | class Quadratic(Function1D, metaclass=FunctionMeta): 81 | r""" 82 | description : 83 | 84 | A Quadratic function 85 | 86 | latex : $ a + b \cdot x + c \cdot x^2 $ 87 | 88 | parameters : 89 | 90 | a : 91 | 92 | desc : coefficient 93 | initial value : 1 94 | 95 | b : 96 | 97 | desc : coefficient 98 | initial value : 1 99 | 100 | c : 101 | 102 | desc : coefficient 103 | initial value : 1 104 | 105 | 106 | """ 107 | 108 | def _set_units(self, x_unit, y_unit): 109 | # a has units of y_unit / x_unit, so that a*x has units of y_unit 110 | self.a.unit = y_unit 111 | 112 | # b has units of y 113 | self.b.unit = y_unit / x_unit 114 | 115 | self.c.unit = y_unit / (x_unit) ** 2 116 | 117 | def evaluate(self, x, a, b, c): 118 | return a + b * x + c * x * x 119 | 120 | 121 | class Cubic(Function1D, metaclass=FunctionMeta): 122 | r""" 123 | description : 124 | 125 | A cubic function 126 | 127 | latex : $ a + b \cdot x + c \cdot x^2 + d \cdot x^3$ 128 | 129 | parameters : 130 | 131 | a : 132 | 133 | desc : coefficient 134 | initial value : 1 135 | 136 | b : 137 | 138 | desc : coefficient 139 | initial value : 1 140 | 141 | c : 142 | 143 | desc : coefficient 144 | initial value : 1 145 | 146 | d : 147 | 148 | desc : coefficient 149 | initial value : 1 150 | 151 | 152 | """ 153 | 154 | def _set_units(self, x_unit, y_unit): 155 | # a has units of y_unit / x_unit, so that a*x has units of y_unit 156 | self.a.unit = y_unit 157 | 158 | # b has units of y 159 | self.b.unit = y_unit / x_unit 160 | 161 | self.c.unit = y_unit / (x_unit) ** 2 162 | 163 | self.d.unit = y_unit / (x_unit) ** 3 164 | 165 | def evaluate(self, x, a, b, c, d): 166 | 167 | x2 = x * x 168 | 169 | x3 = x2 * x 170 | 171 | return a + b * x + c * x2 + d * x3 172 | 173 | 174 | class Quartic(Function1D, metaclass=FunctionMeta): 175 | r""" 176 | description : 177 | 178 | A quartic function 179 | 180 | latex : $ a + b \cdot x + c \cdot x^2 + d \cdot x^3 + e \cdot x^4$ 181 | 182 | parameters : 183 | 184 | a : 185 | 186 | desc : coefficient 187 | initial value : 1 188 | 189 | b : 190 | 191 | desc : coefficient 192 | initial value : 1 193 | 194 | c : 195 | 196 | desc : coefficient 197 | initial value : 1 198 | 199 | d : 200 | 201 | desc : coefficient 202 | initial value : 1 203 | 204 | e : 205 | 206 | desc : coefficient 207 | initial value : 1 208 | 209 | 210 | """ 211 | 212 | def _set_units(self, x_unit, y_unit): 213 | # a has units of y_unit / x_unit, so that a*x has units of y_unit 214 | self.a.unit = y_unit 215 | 216 | # b has units of y 217 | self.b.unit = y_unit / x_unit 218 | 219 | self.c.unit = y_unit / (x_unit) ** 2 220 | 221 | self.d.unit = y_unit / (x_unit) ** 3 222 | 223 | self.e.unit = y_unit / (x_unit) ** 4 224 | 225 | def evaluate(self, x, a, b, c, d, e): 226 | 227 | x2 = x * x 228 | 229 | x3 = x2 * x 230 | 231 | x4 = x3 * x 232 | 233 | return a + b * x + c * x2 + d * x3 + e * x4 234 | -------------------------------------------------------------------------------- /astromodels/sources/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = "giacomov" 2 | 3 | from .point_source import PointSource 4 | from .extended_source import ExtendedSource 5 | from .particle_source import ParticleSource 6 | from .source import Source, SourceType 7 | 8 | __all__ = ["PointSource", "ExtendedSource", "ParticleSource"] 9 | -------------------------------------------------------------------------------- /astromodels/sources/particle_source.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from past.utils import old_div 3 | 4 | __author__ = "giacomov" 5 | 6 | import collections 7 | 8 | import numpy 9 | 10 | from astromodels.core.spectral_component import SpectralComponent 11 | from astromodels.core.tree import Node 12 | from astromodels.core.units import get_units 13 | from astromodels.sources.source import Source, SourceType 14 | from astromodels.utils.pretty_list import dict_to_list 15 | from astromodels.utils.logging import setup_logger 16 | 17 | 18 | log = setup_logger(__name__) 19 | 20 | 21 | class ParticleSource(Source, Node): 22 | """ 23 | A source of particles with a certain distribution. This source does not produce any electromagnetic signal, 24 | but it is useful if used in conjuction with spectral shapes which need a particle distribution as input, like 25 | for example a Synchrotron kernel. 26 | 27 | :param name: name for the source 28 | :param distribution_shape: a function describing the energy distribution of the particles 29 | :param components: a list of SpectralComponents instances 30 | :return: 31 | 32 | """ 33 | 34 | def __init__(self, name, distribution_shape=None, components=None): 35 | 36 | Node.__init__(self, name) 37 | 38 | if components is None: 39 | 40 | if distribution_shape is None: 41 | 42 | log.error( 43 | "You have to either provied a list of components, or a " 44 | "distribution shape" 45 | ) 46 | 47 | raise AssertionError() 48 | 49 | components = [SpectralComponent("main", distribution_shape)] 50 | 51 | Source.__init__(self, components, SourceType.PARTICLE_SOURCE) 52 | 53 | # Add a node called 'spectrum' 54 | 55 | spectrum_node = Node("spectrum") 56 | spectrum_node._add_children(list(self._components.values())) 57 | 58 | self._add_child(spectrum_node) 59 | 60 | type(self).__call__ = type(self).get_flux 61 | 62 | # Set the units 63 | # Now sets the units of the parameters for the energy domain 64 | 65 | current_units = get_units() 66 | 67 | # energy as x and particle flux as y 68 | x_unit = current_units.energy 69 | y_unit = old_div(1, current_units.energy) 70 | 71 | # Now set the units of the components 72 | for component in list(self._components.values()): 73 | component.shape.set_units(x_unit, y_unit) 74 | 75 | def get_flux(self, energies): 76 | 77 | """Get the total flux of this particle source at the given energies (summed over the components)""" 78 | 79 | results = [ 80 | component.shape(energies) for component in list(self.components.values()) 81 | ] 82 | 83 | return numpy.sum(results, 0) 84 | 85 | def _repr__base(self, rich_output=False): 86 | """ 87 | Representation of the object 88 | 89 | :param rich_output: if True, generates HTML, otherwise text 90 | :return: the representation 91 | """ 92 | 93 | # Make a dictionary which will then be transformed in a list 94 | 95 | repr_dict = collections.OrderedDict() 96 | 97 | key = "%s (particle source)" % self.name 98 | 99 | repr_dict[key] = collections.OrderedDict() 100 | repr_dict[key]["spectrum"] = collections.OrderedDict() 101 | 102 | for component_name, component in list(self.components.items()): 103 | 104 | repr_dict[key]["spectrum"][component_name] = component.to_dict(minimal=True) 105 | 106 | return dict_to_list(repr_dict, rich_output) 107 | 108 | @property 109 | def free_parameters(self): 110 | """ 111 | Returns a dictionary of free parameters for this source. 112 | We use the parameter path as the key because it's 113 | guaranteed to be unique, unlike the parameter name. 114 | 115 | :return: 116 | """ 117 | free_parameters = collections.OrderedDict() 118 | 119 | for component in self._components.values(): 120 | 121 | for par in component.shape.parameters.values(): 122 | 123 | if par.free: 124 | 125 | free_parameters[par.path] = par 126 | 127 | return free_parameters 128 | 129 | @property 130 | def parameters(self): 131 | """ 132 | Returns a dictionary of all parameters for this source. 133 | We use the parameter path as the key because it's 134 | guaranteed to be unique, unlike the parameter name. 135 | 136 | :return: 137 | """ 138 | all_parameters = collections.OrderedDict() 139 | 140 | for component in self._components.values(): 141 | 142 | for par in component.shape.parameters.values(): 143 | 144 | all_parameters[par.path] = par 145 | 146 | return all_parameters 147 | -------------------------------------------------------------------------------- /astromodels/sources/source.py: -------------------------------------------------------------------------------- 1 | __author__ = "giacomov" 2 | 3 | from enum import Enum, unique 4 | from astromodels.utils.logging import setup_logger 5 | from typing import Dict, List, Any 6 | 7 | from astromodels.core.parameter import Parameter 8 | 9 | import collections 10 | 11 | log = setup_logger(__name__) 12 | 13 | 14 | @unique 15 | class SourceType(Enum): 16 | PARTICLE_SOURCE = "particle source" 17 | POINT_SOURCE = "point source" 18 | EXTENDED_SOURCE = "extended source" 19 | 20 | def __str__(self): 21 | return f"{self.value}" 22 | 23 | 24 | class UnknownSourceType(Exception): 25 | pass 26 | 27 | 28 | class Source(object): 29 | def __init__( 30 | self, list_of_components: List[Any], src_type: str, spatial_shape=None 31 | ): 32 | 33 | # Make the dictionary of components 34 | self._components: Dict[str, Any] = collections.OrderedDict() 35 | 36 | for component in list_of_components: 37 | 38 | self._components[component.name] = component 39 | 40 | if src_type not in SourceType: 41 | 42 | log.error(f"Source of type {src_type} is unknown") 43 | 44 | raise UnknownSourceType() 45 | 46 | else: 47 | 48 | # Store the type string 49 | self._src_type = src_type 50 | 51 | @property 52 | def has_free_parameters(self) -> bool: 53 | 54 | raise NotImplementedError("You need to override this") 55 | 56 | @property 57 | def free_parameters(self) -> Dict[str, Parameter]: 58 | """ 59 | Returns a dictionary of free parameters for this source 60 | 61 | :return: 62 | """ 63 | 64 | raise NotImplementedError("You need to override this") 65 | 66 | @property 67 | def components(self) -> Dict[str, Any]: 68 | """ 69 | Return the dictionary of components 70 | 71 | :return: dictionary of components 72 | """ 73 | 74 | return self._components 75 | 76 | @property 77 | def source_type(self) -> str: 78 | """ 79 | Return the type of the source ('point source' or 'extended source') 80 | 81 | :return: type of the source 82 | """ 83 | 84 | return self._src_type 85 | -------------------------------------------------------------------------------- /astromodels/tests/test_1D_function_values.py: -------------------------------------------------------------------------------- 1 | import h5py 2 | import numpy as np 3 | import numpy.testing as npt 4 | 5 | from astromodels.functions.function import _known_functions 6 | from astromodels.functions.priors import * 7 | from astromodels.utils import _get_data_file_path 8 | 9 | _multiplicative_models = [ 10 | "PhAbs", 11 | "TbAbs", 12 | "WAbs", 13 | "APEC", 14 | "VAPEC", 15 | "EBLattenuation", 16 | ] 17 | 18 | 19 | def test_function_values_have_not_changed(): 20 | 21 | with h5py.File(_get_data_file_path("past_1D_values.h5"), "r") as f: 22 | 23 | eval_x = f["eval_values"][()] 24 | 25 | for key in _known_functions: 26 | 27 | this_function = _known_functions[key] 28 | 29 | # Test only the power law of XSpec, which is the only one we know we can test at 1 keV 30 | 31 | if key.find("XS") == 0 or (key in _multiplicative_models): 32 | 33 | # An XSpec model OR EBLattenuation function. Test it only if it's a power law (the others might need other parameters during 34 | # initialization) 35 | 36 | continue 37 | 38 | if key.find("TemplateModel") == 0: 39 | 40 | # The TemplateModel function has its own test 41 | 42 | continue 43 | 44 | if key.find("Synchrotron") == 0: 45 | 46 | # Naima Synchtron function should have its own test 47 | 48 | continue 49 | 50 | if key.find("_ComplexTestFunction") == 0: 51 | 52 | # Naima Synchtron function should have its own test 53 | 54 | continue 55 | 56 | 57 | if this_function._n_dim == 1: 58 | 59 | print("testing %s ..." % key) 60 | 61 | func = this_function() 62 | if key == "GenericFunction": 63 | 64 | def _f(x): 65 | return x**2 66 | 67 | func.set_function(_f) 68 | 69 | new_values = np.atleast_1d(func(eval_x)) 70 | 71 | with h5py.File(_get_data_file_path("past_1D_values.h5"), "r") as f: 72 | if key not in f.keys(): 73 | 74 | print( 75 | "the function %s does not exist in the past data. You must run a script to add it" 76 | % key 77 | ) 78 | 79 | else: 80 | 81 | old_values = f[key][()] 82 | 83 | npt.assert_almost_equal(new_values, old_values) 84 | -------------------------------------------------------------------------------- /astromodels/tests/test_load_xspec_models.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import division 3 | import pytest 4 | import astropy.units as u 5 | from astromodels import clone_model, PointSource, Model, load_model 6 | from pathlib import Path 7 | 8 | try: 9 | 10 | from astromodels.xspec import * 11 | 12 | except: 13 | 14 | has_XSPEC = False 15 | 16 | else: 17 | 18 | has_XSPEC = True 19 | 20 | 21 | # This defines a decorator which can be applied to single tests to 22 | # skip them if the condition is not met 23 | skip_if_xspec_is_not_available = pytest.mark.skipif( 24 | not has_XSPEC, reason="XSPEC not available" 25 | ) 26 | 27 | 28 | @skip_if_xspec_is_not_available 29 | def test_xspec_load(): 30 | 31 | # no need to do anything really 32 | s = XS_phabs() * XS_powerlaw() + XS_bbody() 33 | print(s(1.0)) 34 | s.set_units(u.keV, 1 / (u.keV * u.cm**2 * u.s)) 35 | print(s(1.0 * u.keV)) 36 | 37 | 38 | @skip_if_xspec_is_not_available 39 | def test_xspec_saving(): 40 | 41 | s = XS_powerlaw() + XS_bbody() 42 | 43 | ps = PointSource("test", 0, 0, spectral_shape=s) 44 | 45 | model = Model(ps) 46 | 47 | cloned_model = clone_model(model) 48 | 49 | filename = "_test_xspec_model.yml" 50 | 51 | model.save(filename) 52 | 53 | reloaded_model = load_model(filename) 54 | 55 | p = Path(filename) 56 | 57 | p.unlink() 58 | -------------------------------------------------------------------------------- /astromodels/tests/test_memoizer.py: -------------------------------------------------------------------------------- 1 | from builtins import zip 2 | from astromodels.functions import Powerlaw 3 | import numpy as np 4 | 5 | 6 | def test_memoizer(): 7 | 8 | po = Powerlaw() 9 | 10 | a = np.random.uniform(-3, -1, 2000) 11 | b = np.random.uniform(0.1, 10, 2000) 12 | 13 | for aa, bb in zip(a, b): 14 | 15 | po.index = aa 16 | po.K = bb 17 | 18 | po(1.0) 19 | -------------------------------------------------------------------------------- /astromodels/tests/test_polarization.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import os 4 | import math 5 | 6 | from astromodels import update_logging_level 7 | from astromodels.core.polarization import * 8 | from astromodels.core.model import Model 9 | from astromodels.core.model_parser import * 10 | from astromodels.functions import Constant,Powerlaw 11 | from astromodels.sources.point_source import PointSource 12 | 13 | update_logging_level("DEBUG") 14 | 15 | def test_linear_polarization_parameters(): 16 | degree = 50.0 17 | angle = 30.0 18 | ps = PointSource('PS',0,0,spectral_shape=Powerlaw(),polarization=LinearPolarization(degree=degree, angle=angle)) 19 | m1 = Model(ps) 20 | m1.display() 21 | 22 | m1.save("__test.yml",overwrite=True) 23 | 24 | mp = load_model("__test.yml") 25 | assert math.isclose(mp.sources["PS"].spectrum.main.polarization.degree.value, degree, rel_tol=0.02) 26 | assert math.isclose(mp.sources["PS"].spectrum.main.polarization.angle.value, angle, rel_tol=0.02) 27 | mp.display() 28 | 29 | os.remove("__test.yml") 30 | 31 | def test_linear_polarization_functions(): 32 | degree = Constant() 33 | angle = Constant() 34 | degree.k = 50 35 | angle.k = 30 36 | ps = PointSource('PS',0,0,spectral_shape=Powerlaw(),polarization=LinearPolarization(degree=degree, angle=angle)) 37 | m1 = Model(ps) 38 | m1.display() 39 | 40 | m1.save("__test.yml",overwrite=True) 41 | 42 | mp = load_model("__test.yml") 43 | assert math.isclose(mp.sources["PS"].spectrum.main.polarization.degree.Constant.k.value, degree.k.value, rel_tol=0.02) 44 | assert math.isclose(mp.sources["PS"].spectrum.main.polarization.angle.Constant.k.value, angle.k.value, rel_tol=0.02) 45 | mp.display() 46 | 47 | os.remove("__test.yml") 48 | 49 | def test_Stokes_polarization_functions(): 50 | u = Constant() 51 | q = Constant() 52 | u.k = 0.5 53 | q.k = 0.5 54 | 55 | ps = PointSource('PS',0,0,spectral_shape=Powerlaw(),polarization=StokesPolarization(Q=q, U=u)) 56 | m1 = Model(ps) 57 | m1.display() 58 | 59 | m1.save("__test.yml",overwrite=True) 60 | 61 | mp = load_model("__test.yml") 62 | assert math.isclose(mp.sources["PS"].spectrum.main.polarization.Q.Constant.k.value, q.k.value, rel_tol=0.02) 63 | assert math.isclose(mp.sources["PS"].spectrum.main.polarization.Q.Constant.k.value, u.k.value, rel_tol=0.02) 64 | mp.display() 65 | 66 | os.remove("__test.yml") -------------------------------------------------------------------------------- /astromodels/utils/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = "giacomov" 2 | 3 | from .file_utils import ( 4 | _get_data_file_path, 5 | get_path_of_user_config, 6 | get_user_data_path, 7 | get_user_path, 8 | ) 9 | -------------------------------------------------------------------------------- /astromodels/utils/angular_distance.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | from past.utils import old_div 3 | import numpy as np 4 | 5 | 6 | def angular_distance_fast(ra1, dec1, ra2, dec2): 7 | """ 8 | Compute angular distance using the Haversine formula. Use this one when you know you will never ask for points at 9 | their antipodes. If this is not the case, use the angular_distance function which is slower, but works also for 10 | antipodes. 11 | 12 | :param lon1: 13 | :param lat1: 14 | :param lon2: 15 | :param lat2: 16 | :return: 17 | """ 18 | 19 | lon1 = np.deg2rad(ra1) 20 | lat1 = np.deg2rad(dec1) 21 | lon2 = np.deg2rad(ra2) 22 | lat2 = np.deg2rad(dec2) 23 | dlon = lon2 - lon1 24 | dlat = lat2 - lat1 25 | 26 | a = ( 27 | np.sin(old_div(dlat, 2.0)) ** 2 28 | + np.cos(lat1) * np.cos(lat2) * np.sin(old_div(dlon, 2.0)) ** 2 29 | ) 30 | c = 2 * np.arcsin(np.sqrt(a)) 31 | return np.rad2deg(c) 32 | 33 | 34 | def angular_distance(ra1, dec1, ra2, dec2): 35 | """ 36 | Returns the angular distance between two points, two sets of points, or a set of points and one point. 37 | 38 | :param ra1: array or float, longitude of first point(s) 39 | :param dec1: array or float, latitude of first point(s) 40 | :param ra2: array or float, longitude of second point(s) 41 | :param dec2: array or float, latitude of second point(s) 42 | :return: angular distance(s) in degrees 43 | """ 44 | 45 | # Vincenty formula, slower than the Haversine formula in some cases, but stable also at antipodes 46 | 47 | lon1 = np.deg2rad(ra1) 48 | lat1 = np.deg2rad(dec1) 49 | lon2 = np.deg2rad(ra2) 50 | lat2 = np.deg2rad(dec2) 51 | 52 | sdlon = np.sin(lon2 - lon1) 53 | cdlon = np.cos(lon2 - lon1) 54 | slat1 = np.sin(lat1) 55 | slat2 = np.sin(lat2) 56 | clat1 = np.cos(lat1) 57 | clat2 = np.cos(lat2) 58 | 59 | num1 = clat2 * sdlon 60 | num2 = clat1 * slat2 - slat1 * clat2 * cdlon 61 | denominator = slat1 * slat2 + clat1 * clat2 * cdlon 62 | 63 | return np.rad2deg(np.arctan2(np.sqrt(num1**2 + num2**2), denominator)) 64 | 65 | 66 | def spherical_angle(ra0, dec0, ra1, dec1, ra2, dec2): 67 | """ 68 | Returns the spherical angle distance between two sets of great circles defined by (ra0, dec0), (ra1, dec1) and (ra0, dec0), (ra2, dec2) 69 | 70 | :param ra0: array or float, longitude of intersection point(s) 71 | :param dec0: array or float, latitude of intersection point(s) 72 | :param ra1: array or float, longitude of first point(s) 73 | :param dec1: array or float, latitude of first point(s) 74 | :param ra2: array or float, longitude of second point(s) 75 | :param dec2: array or float, latitude of second point(s) 76 | :return: spherical angle in degrees 77 | """ 78 | 79 | a = np.deg2rad(angular_distance(ra0, dec0, ra1, dec1)) 80 | b = np.deg2rad(angular_distance(ra0, dec0, ra2, dec2)) 81 | c = np.deg2rad(angular_distance(ra2, dec2, ra1, dec1)) 82 | 83 | # use the spherical law of cosines: https://en.wikipedia.org/wiki/Spherical_law_of_cosines#Rearrangements 84 | 85 | numerator = np.atleast_1d(np.cos(c) - np.cos(a) * np.cos(b)) 86 | denominator = np.atleast_1d(np.sin(a) * np.sin(b)) 87 | 88 | return np.where( 89 | denominator == 0, 90 | np.zeros(len(denominator)), 91 | np.rad2deg(np.arccos(old_div(numerator, denominator))), 92 | ) 93 | -------------------------------------------------------------------------------- /astromodels/utils/config_structure.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from dataclasses import dataclass, field 3 | from enum import IntEnum, Enum 4 | 5 | 6 | # logging 7 | class LoggingLevel(IntEnum): 8 | DEBUG = logging.DEBUG 9 | INFO = logging.INFO 10 | WARNING = logging.WARNING 11 | ERROR = logging.ERROR 12 | CRITICAL = logging.CRITICAL 13 | 14 | 15 | @dataclass 16 | class Logging: 17 | path: str = "~/.astromodels/log" 18 | developer: bool = "off" 19 | usr: bool = "on" 20 | console: bool = "on" 21 | level: LoggingLevel = LoggingLevel.INFO 22 | startup_warnings: bool = "on" 23 | info_style: str = "medium_spring_green" 24 | warn_style: str = "medium_orchid" 25 | error_style: str = "blink bold bright_red" 26 | debug_style: str = "blue_violet" 27 | message_style: str = "bold grey78" 28 | 29 | 30 | class AbsTables(Enum): 31 | WILM = "WILM" 32 | ASPL = "ASPL" 33 | AG89 = "AG89" 34 | 35 | 36 | class EBLTable(Enum): 37 | franceschini = "franceschini" 38 | kneiske = "kneiske" 39 | dominguez = "dominguez" 40 | inuoe = "inuoe" 41 | gilmore = "gilmore" 42 | 43 | 44 | @dataclass 45 | class AbsorptionModels: 46 | tbabs_table: AbsTables = AbsTables.WILM 47 | phabs_table: AbsTables = AbsTables.AG89 48 | ebl_table: EBLTable = EBLTable.dominguez 49 | 50 | 51 | @dataclass 52 | class Modeling: 53 | use_memoization: bool = True 54 | use_parameter_transforms: bool = True 55 | ignore_parameter_bounds: bool = False 56 | 57 | 58 | @dataclass 59 | class Config: 60 | logging: Logging = field(default_factory=Logging) 61 | absorption_models: AbsorptionModels = field( 62 | default_factory=AbsorptionModels 63 | ) 64 | modeling: Modeling = field(default_factory=Modeling) 65 | -------------------------------------------------------------------------------- /astromodels/utils/configuration.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | from omegaconf import OmegaConf 4 | from omegaconf.dictconfig import DictConfig 5 | from rich.tree import Tree 6 | 7 | from astromodels.utils import get_path_of_user_config 8 | 9 | from .config_structure import Config 10 | 11 | # Read the default Config 12 | astromodels_config: Config = OmegaConf.structured(Config) 13 | 14 | # now glob the config directory 15 | 16 | for user_config_file in get_path_of_user_config().glob("*.yml"): 17 | 18 | _partial_conf = OmegaConf.load(user_config_file) 19 | 20 | astromodels_config: Config = OmegaConf.merge(astromodels_config, _partial_conf) 21 | 22 | 23 | def recurse_dict(d, tree): 24 | 25 | for k, v in d.items(): 26 | 27 | if (type(v) is dict) or isinstance(v, DictConfig): 28 | 29 | branch = tree.add( 30 | k, guide_style="bold medium_orchid", style="bold medium_orchid" 31 | ) 32 | 33 | recurse_dict(v, branch) 34 | 35 | else: 36 | 37 | tree.add( 38 | f"{k}: [blink cornflower_blue]{v}", 39 | guide_style="medium_spring_green", 40 | style="medium_spring_green", 41 | ) 42 | 43 | return 44 | 45 | 46 | def show_configuration(sub_menu: Optional[str] = None): 47 | """ 48 | display the current configuration or a sub menu if 49 | provided 50 | """ 51 | 52 | if sub_menu is None: 53 | 54 | tree = Tree( 55 | "config", 56 | guide_style="bold medium_orchid", 57 | style="bold medium_orchid", 58 | ) 59 | 60 | recurse_dict(astromodels_config, tree) 61 | 62 | else: 63 | 64 | if sub_menu in astromodels_config: 65 | 66 | tree = Tree( 67 | "config", 68 | guide_style="bold medium_orchid", 69 | style="bold medium_orchid", 70 | ) 71 | 72 | recurse_dict(astromodels_config[sub_menu], tree) 73 | 74 | else: 75 | 76 | msg = f"{sub_menu} is not in the astromodels configuration" 77 | 78 | raise AssertionError(msg) 79 | 80 | return tree 81 | -------------------------------------------------------------------------------- /astromodels/utils/data_files.py: -------------------------------------------------------------------------------- 1 | import pkg_resources 2 | import os 3 | 4 | 5 | def _get_data_file_path(data_file): 6 | """ 7 | Returns the absolute path to the required data files. 8 | 9 | :param data_file: relative path to the data file, relative to the astromodels/data path. 10 | So to get the path to data/dark_matter/gammamc_dif.dat you need to use data_file="dark_matter/gammamc_dif.dat" 11 | :return: absolute path of the data file 12 | """ 13 | 14 | try: 15 | 16 | file_path = pkg_resources.resource_filename( 17 | "astromodels", "data/%s" % data_file 18 | ) 19 | 20 | except KeyError: 21 | 22 | raise IOError( 23 | "Could not read or find data file %s. Try reinstalling astromodels. If this does not fix your " 24 | "problem, open an issue on github." % (data_file) 25 | ) 26 | 27 | else: 28 | 29 | return os.path.abspath(file_path) 30 | -------------------------------------------------------------------------------- /astromodels/utils/disk_usage.py: -------------------------------------------------------------------------------- 1 | __author__ = "Giampaolo Rodola " 2 | 3 | """ 4 | Return disk usage statistics about the given path as a (total, used, free) 5 | namedtuple. Values are expressed in bytes. 6 | """ 7 | # Author: Giampaolo Rodola' 8 | # License: MIT 9 | 10 | import os 11 | import collections 12 | 13 | _ntuple_diskusage = collections.namedtuple("usage", "total used free") 14 | 15 | if hasattr(os, "statvfs"): # POSIX 16 | 17 | def disk_usage(path): 18 | st = os.statvfs(path) 19 | free = st.f_bavail * st.f_frsize 20 | total = st.f_blocks * st.f_frsize 21 | used = (st.f_blocks - st.f_bfree) * st.f_frsize 22 | return _ntuple_diskusage(total, used, free) 23 | 24 | elif os.name == "nt": # Windows 25 | import ctypes 26 | import sys 27 | 28 | def disk_usage(path): 29 | _, total, free = ( 30 | ctypes.c_ulonglong(), 31 | ctypes.c_ulonglong(), 32 | ctypes.c_ulonglong(), 33 | ) 34 | if sys.version_info >= (3,) or isinstance(path, str): 35 | fun = ctypes.windll.kernel32.GetDiskFreeSpaceExW 36 | else: 37 | fun = ctypes.windll.kernel32.GetDiskFreeSpaceExA 38 | ret = fun(path, ctypes.byref(_), ctypes.byref(total), ctypes.byref(free)) 39 | if ret == 0: 40 | raise ctypes.WinError() 41 | used = total.value - free.value 42 | return _ntuple_diskusage(total.value, used, free.value) 43 | 44 | else: 45 | raise NotImplementedError("platform not supported") 46 | 47 | disk_usage.__doc__ = __doc__ 48 | -------------------------------------------------------------------------------- /astromodels/utils/file_utils.py: -------------------------------------------------------------------------------- 1 | # This file contains some defaults, like locations of files, which should not 2 | # change much but benefits anyway of being in one central location 3 | 4 | import os 5 | from pathlib import Path 6 | from typing import Optional 7 | import numpy as np 8 | import pkg_resources 9 | 10 | _custom_config_path = os.environ.get("ASTROMODELS_CONFIG") 11 | 12 | copy_if_needed: Optional[bool] 13 | 14 | if np.lib.NumpyVersion(np.__version__) >= "2.0.0": 15 | copy_if_needed = None 16 | else: 17 | copy_if_needed = False 18 | 19 | 20 | def _get_data_file_path(data_file: str) -> Path: 21 | """ 22 | Returns the absolute path to the required data files. 23 | 24 | :param data_file: relative path to the data file, relative to the astromodels/data path. 25 | So to get the path to data/dark_matter/gammamc_dif.dat you need to use data_file="dark_matter/gammamc_dif.dat" 26 | :return: absolute path of the data file 27 | """ 28 | 29 | try: 30 | 31 | file_path: str = pkg_resources.resource_filename( 32 | "astromodels", "data/%s" % data_file 33 | ) 34 | 35 | except KeyError: 36 | 37 | raise IOError( 38 | "Could not read or find data file %s. Try reinstalling astromodels. If this does not fix your " 39 | "problem, open an issue on github." % (data_file) 40 | ) 41 | 42 | else: 43 | 44 | return Path(file_path).absolute() 45 | 46 | 47 | def get_user_path() -> Path: 48 | 49 | user_path: Path = Path.home() / ".astromodels" 50 | 51 | if not user_path.exists(): 52 | 53 | user_path.mkdir(parents=True) 54 | 55 | return user_path 56 | 57 | 58 | def get_user_data_path() -> Path: 59 | 60 | user_data: Path = get_user_path() / "data" 61 | 62 | # Create it if doesn't exist 63 | if not user_data.exists(): 64 | 65 | user_data.mkdir(parents=True) 66 | 67 | return user_data 68 | 69 | 70 | def get_path_of_user_config() -> Path: 71 | 72 | if _custom_config_path is not None: 73 | 74 | config_path: Path = Path(_custom_config_path) 75 | 76 | config_path: Path = Path().home() / ".config" / "astromodels" 77 | 78 | if not config_path.exists(): 79 | 80 | config_path.mkdir(parents=True) 81 | 82 | return config_path 83 | -------------------------------------------------------------------------------- /astromodels/utils/io.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | __author__ = "giacomov" 4 | 5 | # Import IPython display facility, if available. Otherwise, 6 | # create a wrapper which just uses print 7 | 8 | try: 9 | from IPython.display import display 10 | 11 | except ImportError: 12 | def display(*args): 13 | """ 14 | Mock version of display, used if there is no ipython installed 15 | """ 16 | print(args) 17 | 18 | 19 | try: 20 | from IPython.display import Latex 21 | 22 | except ImportError: 23 | class Latex(object): 24 | """ 25 | Mock version of the IPython Latex object, used if there is no ipython installed 26 | """ 27 | 28 | def __init__(self, *args, **kwargs): 29 | 30 | pass 31 | 32 | def __repr__(self, *args, **kwargs): 33 | 34 | print("[you need to install IPython to see the Latex representation]") 35 | -------------------------------------------------------------------------------- /astromodels/utils/logging.py: -------------------------------------------------------------------------------- 1 | # If threeml is not installed, we create our own log, 2 | # otherwise, just print to the 3ML one! 3 | 4 | import logging 5 | import logging.handlers as handlers 6 | from contextlib import contextmanager 7 | from pathlib import Path 8 | 9 | from rich.console import Console 10 | from rich.logging import RichHandler 11 | from rich.theme import Theme 12 | 13 | from astromodels.utils.configuration import astromodels_config 14 | 15 | try: 16 | 17 | from threeML.config.config import threeML_config 18 | 19 | has_threeml = True 20 | 21 | except ImportError: 22 | 23 | has_threeml = False 24 | 25 | 26 | DEBUG_NODE_LEVEL = 9 27 | logging.addLevelName(DEBUG_NODE_LEVEL, "DEBUG_NODE") 28 | 29 | 30 | def debug_node(self, message, *args, **kws): 31 | if self.isEnabledFor(DEBUG_NODE_LEVEL): 32 | # Yes, logger takes its '*args' as 'args'. 33 | self._log(DEBUG_NODE_LEVEL, message, args, **kws) 34 | 35 | 36 | logging.Logger.debug_node = debug_node 37 | 38 | 39 | def get_path_of_log_dir(): 40 | 41 | # we use the 3ML log path to simplify things 42 | # a more clever solution could be found 43 | 44 | if has_threeml: 45 | 46 | user_log: Path = Path(threeML_config.logging.path).expanduser() 47 | 48 | else: 49 | 50 | user_log: Path = Path(astromodels_config.logging.path).expanduser() 51 | 52 | # Create it if doesn't exist 53 | if not user_log.exists(): 54 | 55 | user_log.mkdir(parents=True) 56 | 57 | return user_log 58 | 59 | 60 | _log_file_names = ["usr.log", "dev.log"] 61 | 62 | 63 | def get_path_of_log_file(log_file: str) -> Path: 64 | """ 65 | returns the path of the log files 66 | """ 67 | assert log_file in _log_file_names, f"{log_file} is not one of {_log_file_names}" 68 | 69 | return get_path_of_log_dir() / log_file 70 | 71 | 72 | class LogFilter(object): 73 | def __init__(self, level): 74 | self.__level = level 75 | 76 | def filter(self, logRecord): 77 | return logRecord.levelno != self.__level 78 | 79 | 80 | # now create the developer handler that rotates every day and keeps 81 | # 10 days worth of backup 82 | astromodels_dev_log_handler = handlers.TimedRotatingFileHandler( 83 | get_path_of_log_file("dev.log"), when="D", interval=1, backupCount=10 84 | ) 85 | 86 | # lots of info written out 87 | 88 | _dev_formatter = logging.Formatter( 89 | "%(asctime)s | %(name)s | %(levelname)s| %(funcName)s | %(lineno)d | %(message)s", 90 | datefmt="%Y-%m-%d %H:%M:%S", 91 | ) 92 | 93 | astromodels_dev_log_handler.setFormatter(_dev_formatter) 94 | astromodels_dev_log_handler.setLevel(logging.DEBUG) 95 | # now set up the usr log which will save the info 96 | 97 | astromodels_usr_log_handler = handlers.TimedRotatingFileHandler( 98 | get_path_of_log_file("usr.log"), when="D", interval=1, backupCount=10 99 | ) 100 | 101 | astromodels_usr_log_handler.setLevel(logging.INFO) 102 | 103 | # lots of info written out 104 | _usr_formatter = logging.Formatter( 105 | "%(asctime)s | %(levelname)s | %(message)s", datefmt="%Y-%m-%d %H:%M:%S" 106 | ) 107 | 108 | astromodels_usr_log_handler.setFormatter(_usr_formatter) 109 | 110 | 111 | _console_formatter = logging.Formatter( 112 | " %(message)s", 113 | datefmt="%H:%M:%S", 114 | ) 115 | 116 | 117 | _theme = {} 118 | 119 | # Banner 120 | _theme["h1"] = "deep_sky_blue3" 121 | _theme["status.spinner"] = "cyan2" 122 | _theme["status.text"] = "deep_sky_blue4" 123 | _theme["repr.filename"] = "blue" 124 | _theme["repr.number"] = "white" 125 | _theme["repr.path"] = "grey37" 126 | _theme["repr.str"] = "grey37" 127 | _theme["repr.tag_name"] = "white" 128 | _theme["repr.url"] = "not bold not italic underline grey84" 129 | _theme["log.time"] = "green1" 130 | _theme["log.message"] = f"{astromodels_config.logging.message_style}" 131 | _theme["logging.level.debug"] = f"{astromodels_config.logging.debug_style}" 132 | _theme["logging.level.error"] = f"{astromodels_config.logging.error_style}" 133 | _theme["logging.level.info"] = f"{astromodels_config.logging.info_style}" 134 | _theme["logging.level.warning"] = f"{astromodels_config.logging.warn_style}" 135 | _theme["logging.level.degub_node"] = "light_goldenrod1" 136 | 137 | 138 | # mytheme = Theme().read(_get_data_file_path("log_theme.ini")) 139 | mytheme = Theme(_theme) 140 | console = Console(theme=mytheme) 141 | 142 | 143 | astromodels_console_log_handler = RichHandler( 144 | level="INFO", rich_tracebacks=True, markup=True, console=console 145 | ) 146 | astromodels_console_log_handler.setFormatter(_console_formatter) 147 | astromodels_console_log_handler.setLevel("INFO") 148 | 149 | warning_filter = LogFilter(logging.WARNING) 150 | 151 | 152 | @contextmanager 153 | def silence_console_log(): 154 | """ 155 | temporarily silence the console and progress bars 156 | """ 157 | current_console_logging_level = astromodels_console_log_handler.level 158 | current_usr_logging_level = astromodels_usr_log_handler.level 159 | 160 | astromodels_console_log_handler.setLevel(logging.CRITICAL) 161 | astromodels_usr_log_handler.setLevel(logging.CRITICAL) 162 | 163 | try: 164 | 165 | yield 166 | 167 | finally: 168 | 169 | astromodels_console_log_handler.setLevel(current_console_logging_level) 170 | astromodels_usr_log_handler.setLevel(current_usr_logging_level) 171 | 172 | 173 | def silence_warnings(): 174 | """ 175 | supress warning messages in console and file usr logs 176 | """ 177 | 178 | astromodels_usr_log_handler.addFilter(warning_filter) 179 | astromodels_console_log_handler.addFilter(warning_filter) 180 | 181 | 182 | def activate_warnings(): 183 | """ 184 | supress warning messages in console and file usr logs 185 | """ 186 | 187 | astromodels_usr_log_handler.removeFilter(warning_filter) 188 | astromodels_console_log_handler.removeFilter(warning_filter) 189 | 190 | 191 | def update_logging_level(level): 192 | 193 | astromodels_console_log_handler.setLevel(level) 194 | 195 | 196 | def setup_logger(name): 197 | 198 | # A logger with name name will be created 199 | # and then add it to the print stream 200 | log = logging.getLogger(name) 201 | 202 | # this must be set to allow debug messages through 203 | log.setLevel(DEBUG_NODE_LEVEL) 204 | 205 | # add the handlers 206 | 207 | log.addHandler(astromodels_dev_log_handler) 208 | 209 | log.addHandler(astromodels_console_log_handler) 210 | 211 | log.addHandler(astromodels_usr_log_handler) 212 | 213 | # we do not want to duplicate teh messages in the parents 214 | log.propagate = False 215 | 216 | return log 217 | -------------------------------------------------------------------------------- /astromodels/utils/long_path_formatter.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | 4 | def long_path_formatter(line, max_width=pd.get_option("max_colwidth")): 5 | """ 6 | If a path is longer than max_width, it substitute it with the first and last element, 7 | joined by "...". For example 'this.is.a.long.path.which.we.want.to.shorten' becomes 8 | 'this...shorten' 9 | 10 | :param line: 11 | :param max_width: 12 | :return: 13 | """ 14 | 15 | if len(line) > max_width: 16 | 17 | tokens = line.split(".") 18 | trial1 = "%s...%s" % (tokens[0], tokens[-1]) 19 | 20 | if len(trial1) > max_width: 21 | 22 | return "...%s" % (tokens[-1][-1 : -(max_width - 3)]) 23 | 24 | else: 25 | 26 | return trial1 27 | 28 | else: 29 | 30 | return line 31 | -------------------------------------------------------------------------------- /astromodels/utils/pretty_list.py: -------------------------------------------------------------------------------- 1 | from builtins import str 2 | 3 | __author__ = "giacomov" 4 | 5 | import yaml 6 | import re 7 | 8 | 9 | def _process_html(dictionary): 10 | 11 | list_start = "
    \n" 12 | list_stop = "
\n" 13 | entry_start = "
  • " 14 | entry_stop = "
  • \n" 15 | 16 | output = [list_start] 17 | 18 | for key, value in list(dictionary.items()): 19 | 20 | if isinstance(value, dict): 21 | 22 | # Check whether the dictionary is empty. In that case, don't print anything 23 | if len(value) == 0: 24 | 25 | continue 26 | 27 | if len(value) > 1 or isinstance(list(value.values())[0], dict): 28 | 29 | output.append(entry_start + str(key) + ": ") 30 | output.append(_process_html(value)) 31 | output.append(entry_stop) 32 | 33 | else: 34 | 35 | output.append( 36 | entry_start 37 | + str(key) 38 | + ": " 39 | + str(list(value.values())[0]) 40 | + entry_stop 41 | ) 42 | 43 | else: 44 | 45 | output.append(entry_start + str(key) + ": " + str(value) + entry_stop) 46 | 47 | output.append(list_stop) 48 | 49 | final_output = "\n".join(output) 50 | 51 | return final_output 52 | 53 | 54 | def _process_text(dictionary): 55 | 56 | # Obtain YAML representation 57 | 58 | string_repr = yaml.dump(dictionary, default_flow_style=False) 59 | 60 | # Add a '*' for each point in the list and indent appropriately 61 | 62 | final_output = re.sub("(\s*)(.+)", "\\1 * \\2", string_repr) 63 | 64 | return final_output 65 | 66 | 67 | def dict_to_list(dictionary, html=False): 68 | """ 69 | Convert a dictionary into a unordered list. 70 | 71 | :param dictionary: a dictionary 72 | :param html: whether to output HTML or simple text (True or False) 73 | :return: the list 74 | """ 75 | 76 | if html: 77 | 78 | return _process_html(dictionary) 79 | 80 | else: 81 | 82 | return _process_text(dictionary) 83 | -------------------------------------------------------------------------------- /astromodels/utils/table.py: -------------------------------------------------------------------------------- 1 | __author__ = "giacomov" 2 | 3 | import astropy.table 4 | 5 | 6 | def dict_to_table(dictionary, list_of_keys=None): 7 | """ 8 | Return a table representing the dictionary. 9 | 10 | :param dictionary: the dictionary to represent 11 | :param list_of_keys: optionally, only the keys in this list will be inserted in the table 12 | :return: a Table instance 13 | """ 14 | 15 | # assert len(dictionary.values()) > 0, "Dictionary cannot be empty" 16 | 17 | # Create an empty table 18 | 19 | table = Table() 20 | 21 | # If the dictionary is not empty, fill the table 22 | 23 | if len(dictionary) > 0: 24 | 25 | # Add the names as first column 26 | 27 | table["name"] = list(dictionary.keys()) 28 | 29 | # Now add all other properties 30 | 31 | # Use the first parameter as prototype 32 | 33 | prototype = list(dictionary.values())[0] 34 | 35 | column_names = list(prototype.keys()) 36 | 37 | # If we have a white list for the columns, use it 38 | 39 | if list_of_keys is not None: 40 | 41 | column_names = [key for key in column_names if key in list_of_keys] 42 | 43 | # Fill the table 44 | 45 | for column_name in column_names: 46 | 47 | table[column_name] = [x[column_name] for x in list(dictionary.values())] 48 | 49 | return table 50 | 51 | 52 | # A hack on the astropy Table class to make its output 53 | # more appealing, especially when in the Ipython notebook 54 | 55 | 56 | class Table(astropy.table.Table): 57 | """ 58 | Wrapper around the astropy table to remove some useless clutter (like the format of each column) 59 | """ 60 | 61 | def _base_repr_(self, html=False, show_name=True, **kwargs): 62 | """ 63 | Override the method in the astropy.Table class 64 | to avoid displaying the description, and the format 65 | of the columns 66 | """ 67 | 68 | table_id = "table{id}".format(id=id(self)) 69 | 70 | data_lines, outs = self.formatter._pformat_table( 71 | self, 72 | tableid=table_id, 73 | html=html, 74 | max_width=(-1 if html else None), 75 | show_name=show_name, 76 | show_unit=None, 77 | show_dtype=False, 78 | ) 79 | 80 | out = "\n".join(data_lines) 81 | 82 | # if astropy.table.six.PY2 and isinstance(out, astropy.table.six.text_type): 83 | # out = out.encode('utf-8') 84 | 85 | return out 86 | 87 | 88 | class NumericMatrix(Table): 89 | def _base_repr_(self, html=False, show_name=True, **kwargs): 90 | return super(NumericMatrix, self)._base_repr_(html, False) 91 | -------------------------------------------------------------------------------- /astromodels/utils/valid_variable.py: -------------------------------------------------------------------------------- 1 | from ast import parse 2 | 3 | 4 | def is_valid_variable_name(string_to_check): 5 | """ 6 | Returns whether the provided name is a valid variable name in Python 7 | 8 | :param string_to_check: the string to be checked 9 | :return: True or False 10 | """ 11 | 12 | try: 13 | 14 | parse("{} = None".format(string_to_check)) 15 | return True 16 | 17 | except (SyntaxError, ValueError, TypeError): 18 | 19 | return False 20 | -------------------------------------------------------------------------------- /astromodels/utils/vincenty.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def vincenty(lon0, lat0, a1, s): 5 | """ 6 | Returns the coordinates of a new point that is a given angular distance s away from a starting point (lon0, lat0) at bearing (angle from north) a1), to within a given precision 7 | 8 | Note that this calculation is a simplified version of the full vincenty problem, which solves for the coordinates on the surface on an arbitrary ellipsoid. Here we only care about the surface of a sphere. 9 | 10 | Note: All parameters are assumed to be given in DEGREES 11 | :param lon0: float, longitude of starting point 12 | :param lat0: float, latitude of starting point 13 | :param a1: float, bearing to second point, i.e. angle between due north and line connecting 2 points 14 | :param s: float, angular distance between the two points 15 | :return: coordinates of second point in degrees 16 | """ 17 | 18 | lon0 = np.deg2rad(lon0) 19 | lat0 = np.deg2rad(lat0) 20 | a1 = np.deg2rad(a1) 21 | s = np.deg2rad(s) 22 | 23 | sina = np.cos(lat0) * np.sin(a1) 24 | 25 | num1 = np.sin(lat0) * np.cos(s) + np.cos(lat0) * np.sin(s) * np.cos(a1) 26 | den1 = np.sqrt( 27 | sina**2 + (np.sin(lat0) * np.sin(s) - np.cos(lat0) * np.cos(a1)) ** 2 28 | ) 29 | lat = np.rad2deg(np.arctan2(num1, den1)) 30 | 31 | num2 = np.sin(s) * np.sin(a1) 32 | den2 = np.cos(lat0) * np.cos(s) - np.sin(lat0) * np.sin(s) * np.cos(a1) 33 | L = np.arctan2(num2, den2) 34 | lon = np.rad2deg(lon0 + L) 35 | 36 | return lon, lat 37 | -------------------------------------------------------------------------------- /astromodels/xspec/README: -------------------------------------------------------------------------------- 1 | This subpackage has been extracted from Sherpa and the __init__.py modified 2 | to fit the purposes of astromodels. 3 | 4 | The GPL license applies here (see the LICENSE file) 5 | -------------------------------------------------------------------------------- /astromodels/xspec/__init__.py: -------------------------------------------------------------------------------- 1 | from .factory import * 2 | 3 | # from .xspec_settings import x 4 | -------------------------------------------------------------------------------- /astromodels/xspec/include/sherpa/constants.hh: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2008 Smithsonian Astrophysical Observatory 3 | // 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License along 16 | // with this program; if not, write to the Free Software Foundation, Inc., 17 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | // 19 | 20 | 21 | #ifndef __sherpa_constants_hh__ 22 | #define __sherpa_constants_hh__ 23 | 24 | #include 25 | #include 26 | 27 | // 28 | // The following constants come from GNU's math.h. They provide 29 | // enough digits for the 128-bit IEEE quad. 30 | // 31 | #ifndef M_LN2l 32 | # define M_LN2l 0.6931471805599453094172321214581766L /* log_e 2 */ 33 | #endif 34 | #ifndef M_LN10l 35 | # define M_LN10l 2.3025850929940456840179914546843642L /* log_e 10 */ 36 | #endif 37 | #ifndef M_PIl 38 | # define M_PIl 3.1415926535897932384626433832795029L /* pi */ 39 | #endif 40 | 41 | 42 | namespace sherpa { namespace constants { 43 | 44 | 45 | // 46 | // Min/max values to be used in place of DBL_MIN/DBL_MAX. ("smp" stands 47 | // for "Sherpa model parameter", but obviously they can be used other 48 | // places.) 49 | // 50 | 51 | template 52 | inline Type smp_min() { return 1.0E-120; } 53 | 54 | template 55 | inline Type smp_max() { return 1.0E+120; } 56 | 57 | // 58 | // Mathematical constants 59 | // 60 | 61 | template 62 | inline Type logten() { return M_LN10l; } 63 | 64 | template 65 | inline Type pi() { return M_PIl; } 66 | 67 | template 68 | inline Type twopi() { return 2.0L * M_PIl; } 69 | 70 | template 71 | inline Type sqrt_pi() { return std::sqrt( M_PIl ); } 72 | 73 | // The formula for gfactor is derived towards the end of the file 74 | template 75 | inline Type gfactor() { return 4.0L * M_LN2l; } 76 | 77 | template 78 | inline Type sqrt_gfactor() 79 | { 80 | return std::sqrt( gfactor< long double >() ); 81 | } 82 | 83 | // 84 | // Physical constants 85 | // 86 | 87 | // nist Electron volt [J] 88 | template 89 | inline Type e_ev() { return 1.60217653e-9L; } 90 | 91 | // nist Planck constant [erg * s] 92 | template 93 | inline Type h_erg() { return 6.6260693e-27L; } 94 | 95 | // nist Planck constant [KeV * s] 96 | template 97 | inline Type h_kev() { return h_erg< Type >() / e_ev< Type >(); } 98 | 99 | // gnu Speed of light vacuum [km/s] 100 | template 101 | inline Type c_km() { return 2.99792458e+5L; } 102 | 103 | // gnu Speed of light vacuum [cm/s] 104 | template 105 | inline Type c_cm() { return 2.99792458e+10L; } 106 | 107 | // gnu Speed of light vacuum [ang/s] 108 | template 109 | inline Type c_ang() { return 2.99792458e+18L; } 110 | 111 | // nist Boltzmann constant [erg / K] 112 | template 113 | inline Type k() { return 1.3806505e-16L; } 114 | 115 | template 116 | inline Type two_h_over_c_squared() 117 | { 118 | return 2.0L * h_erg< Type >() / c_cm< Type >() / c_cm< Type >(); 119 | } 120 | 121 | template 122 | inline Type h_over_k() { return h_erg< Type >() / k< Type >(); } 123 | 124 | 125 | } } /* namespace constants, namespace sherpa */ 126 | 127 | 128 | #ifndef _SHERPA_CONST 129 | #define _SHERPA_CONST(name) sherpa::constants::name< DataType >() 130 | #endif 131 | 132 | #define SMP_MIN _SHERPA_CONST(smp_min) 133 | #define SMP_MAX _SHERPA_CONST(smp_max) 134 | #define LOGTEN _SHERPA_CONST(logten) 135 | #define PI _SHERPA_CONST(pi) 136 | #define TWOPI _SHERPA_CONST(twopi) 137 | #define H_ERG _SHERPA_CONST(h_erg) 138 | #define E_EV _SHERPA_CONST(e_ev) 139 | #define H_KEV _SHERPA_CONST(h_kev) 140 | #define C_KM _SHERPA_CONST(c_km) 141 | #define C_CM _SHERPA_CONST(c_cm) 142 | #define C_ANG _SHERPA_CONST(c_ang) 143 | #define K _SHERPA_CONST(k) 144 | #define TWO_H_OVER_C_SQUARED _SHERPA_CONST(two_h_over_c_squared) 145 | #define H_OVER_K _SHERPA_CONST(h_over_k) 146 | #define GFACTOR _SHERPA_CONST(gfactor) 147 | #define SQRT_GFACTOR _SHERPA_CONST(sqrt_gfactor) 148 | #define SQRT_PI _SHERPA_CONST(sqrt_pi) 149 | 150 | 151 | /* 152 | dumbo-357: maple 153 | |\^/| Maple V Release 2 (Harvard University) 154 | ._|\| |/|_. Copyright (c) 1981-1992 by the University of Waterloo. 155 | \ MAPLE / All rights reserved. MAPLE is a registered trademark of 156 | <____ ____> Waterloo Maple Software. 157 | | Type ? for help. 158 | > f:= x-> exp( - 1/2 * ( (x-mu)/sigma)^2 ); 159 | 160 | 2 161 | (x - mu) 162 | f := x -> exp(- 1/2 ---------) 163 | 2 164 | sigma 165 | 166 | > f( mu - fwhm/2) = f(mu)/2; 167 | 168 | 2 169 | fwhm 170 | exp(- 1/8 ------) = 1/2 171 | 2 172 | sigma 173 | 174 | > solve(",fwhm); 175 | 176 | 1/2 1/2 1/2 1/2 177 | 2 sigma 2 ln(2) , - 2 sigma 2 ln(2) 178 | 179 | 180 | gfactor is defined to be : 181 | fwhm = 2 * sqrt( 2 * ln( 2 ) ) 182 | gfactor = fwhm^2 / 2 183 | gfactor = ( 4 * 2 * ln( 2 ) ) / 2 184 | gfactor = 4 * ln( 2 ) 185 | */ 186 | 187 | 188 | #endif /* __sherpa_constants_hh__ */ 189 | -------------------------------------------------------------------------------- /astromodels/xspec/include/sherpa/extension.hh: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2007, 2016, 2017 Smithsonian Astrophysical Observatory 3 | // 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License along 16 | // with this program; if not, write to the Free Software Foundation, Inc., 17 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | // 19 | 20 | #ifndef __sherpa_extension_hh__ 21 | #define __sherpa_extension_hh__ 22 | 23 | #include 24 | 25 | typedef unsigned int SherpaUInt; 26 | typedef sherpa::Array< unsigned int, NPY_UINT > SherpaUIntArray; 27 | 28 | typedef double SherpaFloat; 29 | typedef sherpa::Array< double, NPY_DOUBLE > DoubleArray; 30 | typedef DoubleArray SherpaFloatArray; 31 | 32 | typedef sherpa::Array< int, NPY_INT > IntArray; 33 | 34 | typedef int (*converter)( PyObject*, void* ); 35 | 36 | #define CONVERTME(arg) ((converter) sherpa::convert_to_contig_array) 37 | 38 | #if PY_MAJOR_VERSION >= 3 39 | #define PY3 40 | #endif 41 | 42 | #ifdef PY3 43 | 44 | #define SHERPAMOD(name, fctlist) \ 45 | static struct PyModuleDef module##name = {\ 46 | PyModuleDef_HEAD_INIT, \ 47 | #name, \ 48 | NULL, \ 49 | -1, \ 50 | fctlist \ 51 | }; \ 52 | \ 53 | PyMODINIT_FUNC PyInit_##name(void) { \ 54 | import_array(); \ 55 | return PyModule_Create(&module##name); \ 56 | } 57 | 58 | #else 59 | 60 | #define SHERPAMOD(name, fctlist) \ 61 | PyMODINIT_FUNC init##name(void);\ 62 | PyMODINIT_FUNC \ 63 | init##name(void) \ 64 | { \ 65 | import_array(); \ 66 | Py_InitModule( (char*)#name, fctlist ); \ 67 | } 68 | 69 | #endif 70 | 71 | 72 | #define FCTSPEC(name, func) \ 73 | { (char*)#name, (PyCFunction)func, METH_VARARGS, NULL } 74 | 75 | 76 | #endif /* __sherpa_extension_hh__ */ 77 | -------------------------------------------------------------------------------- /astromodels/xspec/include/sherpa/fcmp.hh: -------------------------------------------------------------------------------- 1 | #ifndef fcmp_hh 2 | #define fcmp_hh 3 | 4 | #include 5 | 6 | /* 7 | fcmp 8 | Copyright (c) 1998-2000 Theodore C. Belding 9 | University of Michigan Center for the Study of Complex Systems 10 | 11 | 12 | 13 | This file is part of the fcmp distribution. fcmp is free software; 14 | you can redistribute and modify it under the terms of the GNU Library 15 | General Public License (LGPL), version 2 or later. This software 16 | comes with absolutely no warranty. See the file COPYING for details 17 | and terms of copying. 18 | 19 | File: fcmp.c 20 | 21 | Description: see fcmp.h and README files. 22 | */ 23 | template< typename Real1, typename Real2, typename Real3 > 24 | int gsl_fcmp( Real1 x1, Real2 x2, Real3 epsilon ) { 25 | int exponent; 26 | Real3 delta; 27 | Real1 difference; 28 | 29 | /* Get exponent(max(fabs(x1), fabs(x2))) and store it in exponent. */ 30 | 31 | /* If neither x1 nor x2 is 0, */ 32 | /* this is equivalent to max(exponent(x1), exponent(x2)). */ 33 | 34 | /* If either x1 or x2 is 0, its exponent returned by frexp would be 0, */ 35 | /* which is much larger than the exponents of numbers close to 0 in */ 36 | /* magnitude. But the exponent of 0 should be less than any number */ 37 | /* whose magnitude is greater than 0. */ 38 | 39 | /* So we only want to set exponent to 0 if both x1 and */ 40 | /* x2 are 0. Hence, the following works for all x1 and x2. */ 41 | 42 | frexp(fabs(x1) > fabs(x2) ? x1 : x2, &exponent); 43 | 44 | /* Do the comparison. */ 45 | 46 | /* delta = epsilon * pow(2, exponent) */ 47 | 48 | /* Form a neighborhood around x2 of size delta in either direction. */ 49 | /* If x1 is within this delta neighborhood of x2, x1 == x2. */ 50 | /* Otherwise x1 > x2 or x1 < x2, depending on which side of */ 51 | /* the neighborhood x1 is on. */ 52 | 53 | delta = ldexp(epsilon, exponent); 54 | 55 | difference = x1 - x2; 56 | 57 | if (difference > delta) 58 | return 1; /* x1 > x2 */ 59 | else if (difference < -delta) 60 | return -1; /* x1 < x2 */ 61 | else /* -delta <= difference <= delta */ 62 | return 0; /* x1 == x2 */ 63 | } 64 | 65 | template< typename Real1, typename Real2, typename Real3 > 66 | int sao_fcmp( const Real1 x1, const Real2 x2, const Real3 epsilon ) { 67 | 68 | if ( x1 == x2 ) { 69 | // might as well start at the simplest comparison... 70 | return 0; 71 | } else if ( static_cast< Real1 >( 0 ) == x1 || 72 | static_cast< Real2 >( 0 ) == x2 ) { 73 | 74 | // x1 || x2 is 0 so the following substraction is kosher 75 | if ( fabs( x1 - x2 ) < epsilon ) 76 | return 0; 77 | else { 78 | if ( x1 > x2 ) 79 | return 1; 80 | else 81 | return -1; 82 | } 83 | 84 | } else { 85 | 86 | return gsl_fcmp( x1, x2, epsilon ); 87 | 88 | } 89 | 90 | } 91 | 92 | 93 | // template< typename Real > 94 | // bool approximately_equal( Real a, Real b, Real epsilon ) { 95 | // return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); 96 | // } 97 | 98 | // template< typename Real > 99 | // bool essentially_equal( Real a, Real b, Real epsilon ) { 100 | // return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon ); 101 | // } 102 | 103 | // template< typename Real > 104 | // bool essentially_greater_than( Real a, Real b, Real epsilon ) { 105 | // return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); 106 | // } 107 | 108 | // template< typename Real > 109 | // bool essentially_less_than( Real a, Real b, Real epsilon ) { 110 | // return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); 111 | // } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /astromodels/xspec/xspec_settings.py: -------------------------------------------------------------------------------- 1 | from astromodels.xspec import _xspec 2 | 3 | 4 | def xspec_abund(command_string=None): 5 | """ 6 | Change/Report the solar abundance table in use. See XSpec manual for help: 7 | 8 | http://heasarc.nasa.gov/xanadu/xspec/manual/XSabund.html 9 | 10 | :param command_string : the command string. If None, returns the current settings, otherwise set to the provided 11 | settings 12 | :return: Either none or the current setting 13 | """ 14 | 15 | if command_string is None: 16 | 17 | return _xspec.get_xsabund() 18 | 19 | else: 20 | 21 | _xspec.set_xsabund(command_string) 22 | 23 | 24 | def xspec_cosmo(H0=None, q0=None, lambda_0=None): 25 | """ 26 | Define the Cosmology in use within the XSpec models. See Xspec manual for help: 27 | 28 | http://heasarc.nasa.gov/xanadu/xspec/manual/XScosmo.html 29 | 30 | All parameters can be modified or just a single parameter 31 | 32 | :param H0: the hubble constant 33 | :param q0: 34 | :param lambda_0: 35 | :return: Either none or the current setting (H_0, q_0, lambda_0) 36 | """ 37 | 38 | current_settings = _xspec.get_xscosmo() 39 | 40 | if (H0 is None) and (q0 is None) and (lambda_0 is None): 41 | 42 | return current_settings 43 | 44 | else: 45 | 46 | # ok, we will see what was changed by the used 47 | 48 | user_inputs = [H0, q0, lambda_0] 49 | 50 | for i, current_setting in enumerate(current_settings): 51 | 52 | if user_inputs[i] is None: 53 | 54 | # the user didn't modify this, 55 | # so lets keep what was already set 56 | 57 | user_inputs[i] = current_setting 58 | 59 | # pass this to xspec 60 | 61 | _xspec.set_xscosmo(*user_inputs) 62 | 63 | 64 | def xspec_xsect(command_string=None): 65 | """ 66 | Change/Report the photoionization cross sections in use for XSpec models. See Xspec manual for help: 67 | 68 | http://heasarc.nasa.gov/xanadu/xspec/manual/XSxsect.html 69 | 70 | :param command_string : the command string. If None, returns the current settings, otherwise set to the provided 71 | settings 72 | :return: Either none or the current setting 73 | """ 74 | 75 | if command_string is None: 76 | 77 | return _xspec.get_xsxsect() 78 | 79 | else: 80 | 81 | _xspec.set_xsxsect(command_string) 82 | -------------------------------------------------------------------------------- /build_local.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Make sure we fail in case of errors 3 | set -e 4 | 5 | TRAVIS_OS_NAME="unknown" 6 | 7 | if [[ "$OSTYPE" == "linux-gnu" ]]; then 8 | 9 | # Linux 10 | 11 | TRAVIS_OS_NAME="linux" 12 | 13 | 14 | elif [[ "$OSTYPE" == darwin* ]]; then 15 | 16 | # Mac OSX 17 | 18 | TRAVIS_OS_NAME="osx" 19 | 20 | 21 | elif [[ "$OSTYPE" == "cygwin" ]]; then 22 | 23 | # POSIX compatibility layer and Linux environment emulation for Windows 24 | 25 | TRAVIS_OS_NAME="linux" 26 | 27 | else 28 | 29 | # Unknown. 30 | 31 | echo "Could not guess your OS. Exiting." 32 | 33 | exit 1 34 | 35 | fi 36 | 37 | echo " ===> Running on ${TRAVIS_OS_NAME}" 38 | 39 | TEST_WITH_XSPEC=true 40 | USE_LOCAL=false 41 | TRAVIS_PYTHON_VERSION=3.9 42 | ENVNAME=astromodels_test_$TRAVIS_PYTHON_VERSION 43 | UPDATE_CONDA=false 44 | 45 | if [[ ${TRAVIS_OS_NAME} == linux ]]; 46 | then 47 | miniconda_os=Linux 48 | compilers="gcc_linux-64 gxx_linux-64 gfortran_linux-64" 49 | else # osx 50 | miniconda_os=MacOSX 51 | compilers="clang_osx-64 clangxx_osx-64 gfortran_osx-64" 52 | 53 | # On macOS we also need the conda libx11 libraries used to build xspec 54 | # We also need to pin down ncurses, for now only on macos. 55 | xorg="xorg-libx11" 56 | fi 57 | 58 | # export PKG_VERSION=$(cd astromodels && python -c "import version;print(version.__version__)") 59 | 60 | 61 | export PKG_VERSION=$(python -c "import versioneer;print(versioneer.get_version())") 62 | 63 | echo "Building ${PKG_VERSION} ..." 64 | echo "Python version: ${TRAVIS_PYTHON_VERSION}" 65 | echo "Testing with XSPEC: ${TEST_WITH_XSPEC} ..." 66 | echo "Use local is: ${USE_LOCAL}" 67 | 68 | if ${TEST_WITH_XSPEC}; then 69 | XSPECVER="6.30.1" 70 | export ASTRO_XSPEC_VERSION="12.12.1" 71 | export XSPEC="xspec-modelsonly=${XSPECVER} ${xorg} cfitsio=4.1.0 ccfits wcslib=7.7" 72 | xspec_channel=xspecmodels 73 | 74 | if ${USE_LOCAL}; then 75 | #conda config --remove channels ${xspec_channel} 76 | use_local="--use-local" 77 | else 78 | conda config --add channels ${xspec_channel} 79 | fi 80 | fi 81 | 82 | if $UPDATE_CONDA ; then 83 | # Update conda 84 | echo "Update conda..." 85 | conda update --yes -q conda conda-build 86 | fi 87 | 88 | echo "dependencies: ${XSPEC}" 89 | 90 | # Answer yes to all questions (non-interactive) 91 | conda config --set always_yes true 92 | 93 | # We will upload explicitly at the end, if successful 94 | conda config --set anaconda_upload no 95 | 96 | # Create test environment 97 | echo "Create test environment..." 98 | 99 | conda create --yes --name $ENVNAME -c conda-forge ${use_local} python=$TRAVIS_PYTHON_VERSION "pytest>=3.6" codecov pytest-cov git ${XSPEC} astropy ${compilers} libgfortran scipy pytables ${READLINE} future numba h5py 100 | 101 | # Make sure conda-forge is the first channel 102 | conda config --add channels defaults 103 | 104 | conda config --add channels conda-forge 105 | 106 | # Activate test environment 107 | echo "Activate test environment..." 108 | 109 | #source $CONDA_PREFIX/etc/profile.d/conda.sh 110 | #source /home/ndilalla/work/miniconda3/etc/profile.d/conda.sh 111 | #source /Users/omodei/miniconda3/etc/profile.d/conda.sh 112 | source /opt/miniconda3/etc/profile.d/conda.sh 113 | conda activate $ENVNAME 114 | 115 | # Build package 116 | echo "Build package..." 117 | conda config --show channels 118 | 119 | conda install -c conda-forge boa -n base 120 | 121 | if $TEST_WITH_XSPEC ; then 122 | echo " ====> Building WITH xspec" 123 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 124 | conda mambabuild --python=$TRAVIS_PYTHON_VERSION conda-dist/recipe 125 | else 126 | # there is some strange error about the prefix length 127 | conda mambabuild --python=$TRAVIS_PYTHON_VERSION conda-dist/recipe 128 | #conda install -c conda-forge/label/cf201901 ccfits=2.5 129 | fi 130 | else 131 | echo " ====> Building WITHOUT xspec" 132 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 133 | conda mambabuild --python=$TRAVIS_PYTHON_VERSION conda-dist/no_xspec_recipe 134 | else 135 | # there is some strange error about the prefix length 136 | conda mambabuild --python=$TRAVIS_PYTHON_VERSION conda-dist/no_xspec_recipe 137 | fi 138 | fi 139 | 140 | echo "======> installing..." 141 | conda install --use-local -c conda-forge astromodels 142 | 143 | echo "======> Run tests..." 144 | 145 | #cd astromodels/tests 146 | #python -m pytest -vv --cov=astromodels # -k "not slow" 147 | 148 | # Codecov needs to run in the main git repo 149 | 150 | # Upload coverage measurements if we are on Linux 151 | #if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 152 | # 153 | # echo "********************************** COVERAGE ******************************" 154 | # codecov -t 493c9a2d-42fc-40d6-8e65-24e681efaa1e 155 | # 156 | #fi 157 | -------------------------------------------------------------------------------- /ci/environment.yml: -------------------------------------------------------------------------------- 1 | name: test_env 2 | 3 | channels: 4 | - conda-forge 5 | - xspecmodels 6 | - defaults 7 | 8 | dependencies: 9 | - python 10 | - mkl 11 | - numpy 12 | - pyyaml>=5.1 13 | - astropy 14 | - scipy 15 | - numdifftools 16 | - hdf5 17 | - pytables 18 | - pandas>=0.23 19 | - dill 20 | - cfitsio 21 | - ccfits 22 | - wcslib 23 | - future 24 | - xspec-modelsonly==6.30.1 25 | - numba 26 | - h5py 27 | - interpolation>=2.2.3 28 | - libgfortran 29 | - omegaconf 30 | - colorama 31 | - rich 32 | - joblib 33 | -------------------------------------------------------------------------------- /ci/environment_noxspec.yml: -------------------------------------------------------------------------------- 1 | name: test_env 2 | 3 | channels: 4 | - conda-forge 5 | - defaults 6 | 7 | dependencies: 8 | - python 9 | - numpy 10 | - pyyaml>=5.1 11 | - astropy 12 | - scipy 13 | - numdifftools 14 | - hdf5 15 | - pytables 16 | - pandas>=0.23 17 | - dill 18 | - wcslib 19 | - future 20 | - numba 21 | - h5py 22 | - interpolation>=2.2.3 23 | - libgfortran 24 | - omegaconf 25 | - colorama 26 | - rich 27 | - joblib 28 | -------------------------------------------------------------------------------- /ci/set_minor_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This sets the minor version 4 | 5 | import sys 6 | import re 7 | import os 8 | import argparse 9 | 10 | if __name__ == "__main__": 11 | 12 | parser = argparse.ArgumentParser(description="Set the patch number in the version file") 13 | parser.add_argument("--patch", help="New patch number", required=True, type=int) 14 | parser.add_argument("--version_file", help="Path of the version file", required=True, type=str) 15 | 16 | args = parser.parse_args() 17 | 18 | # Sanitize file name 19 | file_path = os.path.abspath(os.path.expandvars(os.path.expanduser(args.version_file))) 20 | 21 | # Read current content 22 | 23 | assert os.path.exists(file_path), "File %s does not exist!" % file_path 24 | 25 | with open(file_path, "r") as f: 26 | 27 | lines = f.readlines() 28 | 29 | major = None 30 | minor = None 31 | patch = None 32 | 33 | line_number = None 34 | 35 | for i, line in enumerate(lines): 36 | 37 | # Look for the version. A typical line is: 38 | # __version__ = '0.3.2' 39 | 40 | match = re.match("__version__.*=.*([0-9]+)\.([0-9]+)\.([0-9]+).*", line) 41 | 42 | if match is not None: 43 | 44 | groups = match.groups() 45 | 46 | assert len(groups) == 3 47 | 48 | major, minor, patch = match.groups() 49 | 50 | line_number = int(i) 51 | 52 | if line_number is None: 53 | 54 | raise RuntimeError("Could not understand version in file %s" % file_path) 55 | 56 | # Update patch version 57 | 58 | lines[line_number] = "__version__ = '%s.%s.%s'\n" % (major, minor, args.patch) 59 | 60 | # Overwrite the file 61 | with open(file_path, "w+") as f: 62 | 63 | f.writelines(lines) 64 | 65 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This codecov.yml is the default configuration for 3 | # all repositories on Codecov. You may adjust the settings 4 | # below in your own codecov.yml in your repository. 5 | # 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 70...100 10 | 11 | status: 12 | # Learn more at https://codecov.io/docs#yaml_default_commit_status 13 | project: true 14 | patch: true 15 | changes: false 16 | ignore: 17 | - astromodels/test/.* 18 | - astromodels/_version.py 19 | - **/__init__.py 20 | comment: 21 | layout: "header, diff" 22 | behavior: default # update if exists else create new 23 | 24 | 25 | -------------------------------------------------------------------------------- /conda-dist/no_xspec_recipe/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set name = "astromodels" %} 2 | 3 | package: 4 | name: {{ name }} 5 | version: {{ os.environ.get('PKG_VERSION') }} 6 | 7 | source: 8 | # git_url: https://github.com/threeml/{{ name }}.git 9 | path: ../../ 10 | 11 | requirements: 12 | build: 13 | #- {{ compiler('c') }} # [linux] 14 | #- {{ compiler('cxx') }} # [linux] 15 | #- {{ compiler('fortran') }} # [linux] 16 | #- krb5==1.14.6 17 | 18 | 19 | host: 20 | - python<=2.7.16 # [py2k] 21 | - python # [py3k] 22 | - setuptools 23 | - pip 24 | - numpy 25 | - pyyaml>=5.1 26 | - astropy 27 | #- scipy 28 | - numdifftools 29 | #- hdf5 30 | - pytables 31 | - pandas>=0.23 32 | - future 33 | #- krb5==1.14.6 34 | - numba 35 | - h5py 36 | 37 | run: 38 | - python 39 | - numpy 40 | - pyyaml==5.3 41 | - astropy 42 | #- scipy 43 | - numdifftools 44 | #- hdf5 45 | - pytables 46 | - pandas>=0.23 47 | - dill 48 | - future 49 | #- krb5==1.14.6 50 | - numba>=0.54 51 | - interpolation>=2.2.2 # [py3k] 52 | - h5py 53 | - rich 54 | 55 | build: 56 | script: python -m pip install --verbose --no-deps --ignore-installed . 57 | skip: true # [win] 58 | #skip: true # [py3k] 59 | 60 | test: 61 | # Python imports 62 | imports: 63 | - astromodels 64 | - astromodels.core 65 | - astromodels.functions 66 | - astromodels.functions.dark_matter 67 | - astromodels.sources 68 | - astromodels.utils 69 | 70 | 71 | # commands: 72 | # - pytest -vv --pyargs {{ name }} 73 | 74 | # You can also put a file called run_test.py in the recipe that will be run 75 | # at test time. 76 | 77 | 78 | 79 | about: 80 | home: https://github.com/threeml/astromodels 81 | license: UNKNOWN 82 | summary: 'Astromodels contains models to be used in likelihood or Bayesian analysis in astronomy' 83 | license_family: OTHER 84 | 85 | # See 86 | # http://docs.continuum.io/conda/build.html for 87 | # more information about meta.yaml 88 | -------------------------------------------------------------------------------- /conda-dist/recipe/conda_build_config.yaml: -------------------------------------------------------------------------------- 1 | numpy: 2 | - 1.20 3 | -------------------------------------------------------------------------------- /conda-dist/recipe/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set name = "astromodels" %} 2 | 3 | package: 4 | name: {{ name }} 5 | version: {{ os.environ.get('PKG_VERSION') }} 6 | 7 | source: 8 | # git_url: https://github.com/threeml/{{ name }}.git 9 | path: ../../ 10 | 11 | requirements: 12 | build: 13 | - {{ compiler('c') }} # [linux] 14 | - {{ compiler('cxx') }} # [linux] 15 | - {{ compiler('fortran') }} # [linux] 16 | - xspec-modelsonly #[not arm64] 17 | - cfitsio # [not arm64] 18 | - ccfits # [not arm64] 19 | - wcslib # [not arm64] 20 | 21 | host: 22 | - python 23 | - setuptools 24 | - pip 25 | - numpy 26 | - pyyaml>=5.1 27 | - pandas>=0.23 28 | - cfitsio # [not arm64] 29 | - ccfits # [not arm64] 30 | - wcslib # [not arm64] 31 | - xspec-modelsonly # [not arm64] 32 | - future 33 | - numba>=0.54 34 | - h5py 35 | - interpolation>=2.2.2 36 | - colorama 37 | - omegaconf 38 | - rich 39 | - joblib 40 | 41 | run: 42 | - python 43 | - numpy 44 | - pyyaml>=5.1 45 | - astropy 46 | - pytables 47 | - pandas>=0.23 48 | - dill 49 | - cfitsio # [not arm64] 50 | - ccfits # [not arm64] 51 | - wcslib # [not arm64] 52 | - future 53 | - numba>=0.54 54 | - h5py 55 | - interpolation>=2.2.2 56 | - colorama 57 | - omegaconf 58 | - rich 59 | - joblib 60 | 61 | build: 62 | script: python -m pip install --verbose --no-deps --ignore-installed . 63 | skip: true # [win] 64 | # skip: true # [py3k] 65 | 66 | #test: 67 | # # Python imports 68 | # imports: 69 | # - astromodels 70 | # - astromodels.core 71 | # - astromodels.functions 72 | # - astromodels.functions.dark_matter 73 | # - astromodels.sources 74 | # - astromodels.utils 75 | # - astromodels.xspec 76 | 77 | # commands: 78 | # - pytest -vv --pyargs {{ name }} 79 | 80 | # You can also put a file called run_test.py in the recipe that will be run 81 | # at test time. 82 | 83 | # requires: 84 | # - xspec-modelsonly 85 | 86 | 87 | about: 88 | home: https://github.com/threeml/astromodels 89 | license: UNKNOWN 90 | summary: 'Astromodels contains models to be used in likelihood or Bayesian analysis in astronomy' 91 | license_family: OTHER 92 | 93 | # See 94 | # http://docs.continuum.io/conda/build.html for 95 | # more information about meta.yaml 96 | -------------------------------------------------------------------------------- /docs/api/API.rst: -------------------------------------------------------------------------------- 1 | API 2 | === 3 | 4 | Here you can find the documentation of all classes and methods: 5 | 6 | .. include:: modules.rst 7 | .. include:: astromodels.rst 8 | -------------------------------------------------------------------------------- /docs/api/modules.rst: -------------------------------------------------------------------------------- 1 | astromodels 2 | =========== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | import os 19 | import sys 20 | from pathlib import Path 21 | 22 | import mock 23 | 24 | # If extensions (or modules to document with autodoc) are in another directory, 25 | # add these directories to sys.path here. If the directory is relative to the 26 | # documentation root, use os.path.abspath to make it absolute, like shown here. 27 | sys.path.insert(0, os.path.abspath("..")) 28 | # sys.path.insert(0, os.path.abspath('../threeML/classicMLE')) 29 | 30 | print(f" current dir {os.getcwd()}") 31 | 32 | files = [f for f in os.listdir(".") if os.path.isfile(f)] 33 | for f in files: 34 | print(f) 35 | 36 | 37 | DOCS = Path(__file__).parent 38 | 39 | # -- Generate API documentation ------------------------------------------------ 40 | 41 | 42 | def run_apidoc(app): 43 | """Generage API documentation""" 44 | import better_apidoc 45 | import pkgutil 46 | import sys 47 | import os 48 | 49 | astro_path = os.path.dirname( 50 | pkgutil.get_loader("astromodels").get_filename() 51 | ) 52 | 53 | sys.path.insert(0, os.path.abspath("..")) 54 | sys.path.insert(1, os.path.abspath("../astromodels")) 55 | 56 | # Add the path to the C extension 57 | # lib_path = os.path.abspath('%s/core' % astromodels.__path__[0]) 58 | # lib_path = os.path.abspath('%s/core' % astro_path) 59 | # sys.path.insert(2, lib_path) 60 | # This must work now 61 | # import node_ctype 62 | 63 | better_apidoc.APP = app 64 | better_apidoc.main( 65 | [ 66 | "better-apidoc", 67 | # "-t", 68 | # str(docs / "_templates"), 69 | "--force", 70 | "--no-toc", 71 | "--separate", 72 | "-o", 73 | str(DOCS / "api"), 74 | str(DOCS / ".." / "astromodels"), 75 | ] 76 | ) 77 | 78 | 79 | # #import astromodels 80 | # import pkgutil 81 | # astro_path = os.path.dirname(pkgutil.get_loader("astromodels").get_filename()) 82 | 83 | # sys.path.insert(1, os.path.abspath('../astromodels')) 84 | 85 | # # Add the path to the C extension 86 | # #lib_path = os.path.abspath('%s/core' % astromodels.__path__[0]) 87 | # lib_path = os.path.abspath('%s/core' % astro_path) 88 | 89 | # sys.path.insert(2, lib_path) 90 | 91 | 92 | # #this must work now 93 | # import node_ctype 94 | 95 | # print(f" current dir {os.getcwd()}") 96 | # files = [f for f in os.listdir('.') if os.path.isfile(f)] 97 | # for f in files: 98 | # print(f) 99 | 100 | # -- General configuration --------------------------------------------------- 101 | 102 | # Add any Sphinx extension module names here, as strings. They can be 103 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 104 | # ones. 105 | extensions = [ 106 | "nbsphinx", 107 | "recommonmark", 108 | "sphinx.ext.autodoc", 109 | "sphinx.ext.mathjax", 110 | "sphinx.ext.viewcode", 111 | "sphinx.ext.githubpages", 112 | "sphinx.ext.napoleon", 113 | "sphinx_gallery.load_style", 114 | # "sphinx_math_dollar", 115 | "sphinx_rtd_dark_mode", 116 | ] 117 | 118 | # mathjax_config = { 119 | # "tex2jax": { 120 | # "inlineMath": [["\\(", "\\)"]], 121 | # "displayMath": [["\\[", "\\]"]], 122 | # }, 123 | # } 124 | 125 | # mathjax3_config = { 126 | # "tex": { 127 | # "inlineMath": [["\\(", "\\)"]], 128 | # "displayMath": [["\\[", "\\]"]], 129 | # } 130 | # } 131 | # from sphinx_math_dollar import NODE_BLACKLIST 132 | 133 | 134 | napoleon_google_docstring = True 135 | napoleon_use_param = False 136 | 137 | default_dark_mode = True 138 | 139 | 140 | 141 | 142 | # SPHINX gallery 143 | 144 | 145 | 146 | 147 | # The path where the artifact should be extracted 148 | # Note: this is relative to the conf.py file! 149 | if "GITHUB_TOKEN" in os.environ: 150 | 151 | extensions.append("rtds_action") 152 | 153 | rtds_action_path = "notebooks" 154 | 155 | # # The "prefix" used in the `upload-artifact` step of the action 156 | rtds_action_artifact_prefix = "notebooks-for-" 157 | 158 | rtds_action_github_repo = "threeML/astromodels" 159 | 160 | # # A GitHub personal access token is required, more info below 161 | rtds_action_github_token = os.environ["GITHUB_TOKEN"] 162 | 163 | rtds_action_error_if_missing = True 164 | 165 | 166 | # Add any paths that contain templates here, relative to this directory. 167 | # templates_path = ['_templates'] 168 | 169 | 170 | # see https://github.com/spatialaudio/nbsphinx/issues/595 171 | source_suffix = [".rst"] 172 | 173 | # The master toctree document. 174 | master_doc = "index" 175 | 176 | 177 | # -- Project information ----------------------------------------------------- 178 | 179 | project = "Astromodels" 180 | copyright = "2016--2022, G.Vianello, J. M. Burgess, N. Di Lalla, N. Omodei, H. Fleischhack" 181 | author = "G.Vianello" 182 | 183 | 184 | # -- Options for HTML output ------------------------------------------------- 185 | 186 | # The theme to use for HTML and HTML Help pages. See the documentation for 187 | # a list of builtin themes. 188 | # 189 | language = None 190 | 191 | 192 | 193 | # List of patterns, relative to source directory, that match files and 194 | # directories to ignore when looking for source files. 195 | # This pattern also affects html_static_path and html_extra_path. 196 | exclude_patterns = ["_build", "**.ipynb_checkpoints", "md/*.md"] 197 | 198 | html_theme = "sphinx_rtd_dark_mode" 199 | 200 | 201 | html_theme_options = { 202 | "logo_only": False, 203 | "display_version": False, 204 | "collapse_navigation": True, 205 | "navigation_depth": 4, 206 | "prev_next_buttons_location": "bottom", # top and bottom 207 | } 208 | 209 | 210 | html_logo = "media/transp_logo.png" 211 | html_show_sourcelink = False 212 | html_favicon = "media/favicon.ico" 213 | 214 | autosectionlabel_prefix_document = True 215 | 216 | version = "latest" 217 | # The full version, including alpha/beta/rc tags. 218 | release = "latest" 219 | 220 | print("Done.") 221 | 222 | 223 | def setup(app): 224 | app.connect("builder-inited", run_apidoc) 225 | -------------------------------------------------------------------------------- /docs/function_docs/functions_1d.rst: -------------------------------------------------------------------------------- 1 | One-dimensional Functions 2 | ========================= 3 | 4 | Here are the one dimensional functions builtin to astromodels 5 | 6 | 7 | .. nbgallery:: 8 | ../notebooks/Blackbody.ipynb 9 | ../notebooks/ModifiedBlackbody.ipynb 10 | ../notebooks/NonDissipativePhotosphere.ipynb 11 | ../notebooks/NonDissipativePhotosphere_Deep.ipynb 12 | ../notebooks/StepFunction.ipynb 13 | ../notebooks/StepFunctionUpper.ipynb 14 | ../notebooks/Sin.ipynb 15 | ../notebooks/DiracDelta.ipynb 16 | ../notebooks/Log_parabola.ipynb 17 | ../notebooks/Exponential_cutoff.ipynb 18 | ../notebooks/PhAbs.ipynb 19 | ../notebooks/TbAbs.ipynb 20 | ../notebooks/WAbs.ipynb 21 | ../notebooks/ZDust.ipynb 22 | ../notebooks/Constant.ipynb 23 | ../notebooks/Line.ipynb 24 | ../notebooks/Quadratic.ipynb 25 | ../notebooks/Cubic.ipynb 26 | ../notebooks/Quartic.ipynb 27 | ../notebooks/Powerlaw.ipynb 28 | ../notebooks/Powerlaw_flux.ipynb 29 | ../notebooks/Powerlaw_Eflux.ipynb 30 | ../notebooks/Cutoff_powerlaw.ipynb 31 | ../notebooks/Cutoff_powerlaw_Ep.ipynb 32 | ../notebooks/Inverse_cutoff_powerlaw.ipynb 33 | ../notebooks/Super_cutoff_powerlaw.ipynb 34 | ../notebooks/SmoothlyBrokenPowerLaw.ipynb 35 | ../notebooks/Broken_powerlaw.ipynb 36 | ../notebooks/Band.ipynb 37 | ../notebooks/Band_grbm.ipynb 38 | ../notebooks/Band_Calderone.ipynb 39 | ../notebooks/DoubleSmoothlyBrokenPowerlaw.ipynb 40 | ../notebooks/DMFitFunction.ipynb 41 | ../notebooks/DMSpectra.ipynb 42 | ../notebooks/Gaussian.ipynb 43 | ../notebooks/Truncated_gaussian.ipynb 44 | ../notebooks/Cauchy.ipynb 45 | ../notebooks/Cosine_Prior.ipynb 46 | ../notebooks/Log_normal.ipynb 47 | ../notebooks/Uniform_prior.ipynb 48 | ../notebooks/Log_uniform_prior.ipynb 49 | ../notebooks/Beta.ipynb 50 | ../notebooks/Gamma.ipynb 51 | ../notebooks/Exponential.ipynb 52 | ../notebooks/Powerlaw_Prior.ipynb 53 | -------------------------------------------------------------------------------- /docs/function_docs/functions_2d.rst: -------------------------------------------------------------------------------- 1 | Two-dimensional Functions 2 | ========================= 3 | 4 | Here are the two dimensional functions builtin to astromodels 5 | 6 | 7 | .. nbgallery:: 8 | ../notebooks/Latitude_galactic_diffuse.ipynb 9 | ../notebooks/Gaussian_on_sphere.ipynb 10 | ../notebooks/Asymm_Gaussian_on_sphere.ipynb 11 | ../notebooks/Disk_on_sphere.ipynb 12 | ../notebooks/Ellipse_on_sphere.ipynb 13 | ../notebooks/Power_law_on_sphere.ipynb 14 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Astromodels documentation master file, created by 2 | sphinx-quickstart on Mon Nov 23 22:10:27 2015. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to Astromodels's documentation! 7 | ======================================= 8 | 9 | About astromodels 10 | ----------------- 11 | 12 | .. image:: media/large_logo.png 13 | 14 | 15 | Astromodels is a very flexible framework to define models for likelihood or 16 | Bayesian analysis of astrophysical data. 17 | 18 | Even though it has been designed having in mind analysis in the spectral domain, 19 | it can be used also as a toolbox containing functions of any variable. 20 | 21 | Astromodels is *not* a modeling package, it only gives you the tools to build a 22 | model as complex as you need. You then need a separate package (such as 23 | `3ML `) to fit that model to the data. 24 | 25 | Some of the features which distinguish astromodels from other similar packages 26 | are: 27 | 28 | * a model can contain an arbitrary number of sources at different positions in 29 | the sky 30 | * parameters can be linked through any function (not only identity) 31 | * parameters can vary with *auxiliary variables* such as time. For example, you 32 | can build a model where some parameters vary with time, and you can fit the 33 | parameters of the function which describe this variability. Similary you can 34 | build models where parameters vary with the phase of a pulsar, and so on. 35 | * models can be saved in and loaded from YAML file (a human-readable format) 36 | * physical units are fully supported in input, but they are handled so that they 37 | don't slow down the actual computation of the models. 38 | 39 | Astromodels has been designed with performance as priority, and is considerably 40 | faster than other python-based solutions for the same problem, such as 41 | astropy.modeling and the modeling part of sherpa. 42 | 43 | Contents: 44 | ========= 45 | 46 | .. toctree:: 47 | :maxdepth: 3 48 | 49 | notebooks/Quick_start.ipynb 50 | notebooks/Configuration.ipynb 51 | function_docs/functions.rst 52 | notebooks/function_list.ipynb 53 | notebooks/Functions_tutorial.ipynb 54 | notebooks/Point_source_tutorial.ipynb 55 | notebooks/Extended_sources_tutorial.ipynb 56 | notebooks/Multi_component_sources.ipynb 57 | notebooks/Model_tutorial.ipynb 58 | notebooks/Priors_for_Bayesian_analysis.ipynb 59 | notebooks/Additional_features_for_scripts_and_applications.ipynb 60 | api/API 61 | release_notes.rst 62 | -------------------------------------------------------------------------------- /docs/md/Configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupyter: 3 | jupytext: 4 | text_representation: 5 | extension: .md 6 | format_name: markdown 7 | format_version: '1.3' 8 | jupytext_version: 1.14.1 9 | kernelspec: 10 | display_name: Python 3 (ipykernel) 11 | language: python 12 | name: python3 13 | --- 14 | 15 | # Configuration 16 | 17 | `astromodels` includes a configuration that allows users to set certain variables from the beginning 18 | 19 | ```python 20 | from astromodels import astromodels_config, show_configuration 21 | ``` 22 | 23 | ```python 24 | show_configuration() 25 | ``` 26 | 27 | The configuration can be accessed and altered during runtime: 28 | 29 | ```python 30 | astromodels_config.modeling.use_memoization = False 31 | ``` 32 | 33 | ```python 34 | show_configuration() 35 | ``` 36 | 37 | 38 | The user can create a configuration YAML file with any name and the extension `.yml` and place it in the `~/.config/astromodels/` folder. An example file: 39 | 40 | 41 | ```yaml 42 | logging: 43 | developer: False # do not store debug log file 44 | usr: True # store a log file 45 | console: True # print logs to screen 46 | level: DEBUG # turn on debug message 47 | 48 | 49 | modeling: 50 | use_parameter_transforms: no # turn off parameter transforms 51 | ignore_parameter_bounds: yes # ignore parameter bounds 52 | 53 | ``` 54 | 55 | Not all options are required to be set and the defaults will be applied to anything not set. 56 | 57 | 58 | ## Configuration options 59 | 60 | There are a few special configuration options 61 | 62 | ### use_memoization 63 | 64 | By default, astromodels functions *memoize* or cache their output. This is useful for various processes like optimization as speeds of the evaluation of repeated function calls with the same values. However, there is a slight overhead when caching values and when performing Bayesian fits, this can slow down the evaluation as chance of hitting the exact same values more than once should be low. Thus, it is possible to turn of memoization directly in the configuration. 65 | 66 | ### use\_parameter_transforms 67 | 68 | Parameters can have transforms assigned to them. These transforms are used during optimization to transform the parameter into a different space, such as log10. However, this may not be desirable and is not needed (or used) during Bayesian fits. There is also a small overhead in computing these transforms. Thus, this can be turned off via the configuration. 69 | 70 | ### ignore\_parameter_bounds 71 | 72 | The bounds of parameters can be used in during optimization but are not used during Bayesian fits (*the prior on a parameter controls its bounds if any*). Thus, it is possible to turn off errors occuring from trying to set parameters outside of thier bounds in the configuration. 73 | 74 | 75 | -------------------------------------------------------------------------------- /docs/md/Multi_component_sources.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupyter: 3 | jupytext: 4 | formats: ipynb,md 5 | text_representation: 6 | extension: .md 7 | format_name: markdown 8 | format_version: '1.2' 9 | jupytext_version: 1.8.0 10 | kernelspec: 11 | display_name: Python 3 12 | language: python 13 | name: python3 14 | --- 15 | 16 | # Multi-component sources 17 | 18 | 19 | A multi-component source is a point source (or extended source) which has different spectral 20 | components. For example, in a Gamma-Ray Bursts you can have a 21 | Synchrotron component and an Inverse Compton component, which come from 22 | different zones and are described by different spectra. Depending on the 23 | needs of your analysis, you might model this situation using a single 24 | component constituted by the sum of the two spectra, or you might want 25 | to model them independently. Each component has its own 26 | polarization, which can be useful when studying polarized sources (to be 27 | implemented). The latter choice allows you to measure for instance the 28 | fluxes from the two components independently. See below for an example of how to create a source with two named spectral components (of course, the spectral shapes can be arbitrary). 29 | 30 | **Sources with multiple *spatial* components are not currently supported. As a workaround, you can create two functions with the same spectral shape and link any relevant parameters. There is no restriction against overlapping extended sources.** 31 | 32 | ```python 33 | from astromodels import * 34 | component1 = SpectralComponent('synchrotron',shape=Powerlaw()) 35 | component2 = SpectralComponent('IC',shape=Powerlaw()) 36 | multicomp_source = PointSource('multicomp_source', ra=123.2, dec=-13.2, components=[component1,component2]) 37 | 38 | multicomp_source.display() 39 | 40 | ``` 41 | 42 | ## Modifying features of the source and modify parameters of its spectrum 43 | 44 | Starting from the source instance you can modify any of its components, 45 | or its position, in a straightforward way. 46 | 47 | Changing position: 48 | 49 | ```python 50 | multicomp_source.position.ra = 124.5 51 | multicomp_source.position.dec = -11.5 52 | ``` 53 | 54 | Change values for the parameters: 55 | 56 | ```python 57 | multicomp_source.spectrum.synchrotron.shape.K = 1e-2 58 | multicomp_source.spectrum.IC.shape.index = -1.0 59 | multicomp_source.display() 60 | ``` 61 | 62 | Spectral components can be assigned to python variables to modify several parameters without too much repetition: 63 | 64 | ```python 65 | po = multicomp_source.spectrum.synchrotron.shape 66 | po.K = 0.1/u.keV/u.cm**2/u.s 67 | po.K.min_value = 1e-10/u.keV/u.cm**2/u.s 68 | multicomp_source.display() 69 | ``` 70 | 71 | Be careful when creating a shortcut directly to a parameter. 72 | 73 | ```python 74 | p1 = multicomp_source.spectrum.synchrotron.shape.K 75 | print (type(p1), p1) 76 | p1 = 0.3 77 | print (type(p1), p1) 78 | 79 | ``` 80 | 81 | This did **not** change the value of K, but instead assigned the float 0.3 to p1 (i.e., destroy the shortcut). 82 | 83 | However you can change the value of p1 like this: 84 | 85 | ```python 86 | p1 = multicomp_source.spectrum.synchrotron.shape.K 87 | p1.value = 0.5 88 | multicomp_source.display() 89 | ``` 90 | 91 | ```python 92 | 93 | ``` 94 | 95 | ```python 96 | 97 | ``` 98 | -------------------------------------------------------------------------------- /docs/md/Priors_for_Bayesian_analysis.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupyter: 3 | jupytext: 4 | formats: ipynb,md 5 | text_representation: 6 | extension: .md 7 | format_name: markdown 8 | format_version: '1.2' 9 | jupytext_version: 1.8.0 10 | kernelspec: 11 | display_name: Python 3 12 | language: python 13 | name: python3 14 | --- 15 | 16 | # Priors for Bayesian analysis 17 | 18 | Astromodels supports the definition of priors for all parameters in 19 | your model. You can use as prior any function (although of course not 20 | all functions should be used this way, but the choice is up to you). 21 | 22 | First let’s define a simple model containing one point source (see the 23 | “Model tutorial” for more info): 24 | 25 | ```python 26 | %%capture 27 | from astromodels import * 28 | 29 | # Create a point source named "pts1" 30 | pts1 = PointSource('pts1',ra=125.23, dec=17.98, spectral_shape=Powerlaw()) 31 | 32 | # Create the model 33 | my_model = Model(pts1) 34 | ``` 35 | 36 | 37 | Now let’s assign uniform priors to the parameters of the powerlaw 38 | function. The function uniform_prior is defined like this: 39 | 40 | 41 | ```python 42 | Uniform_prior.info() 43 | ``` 44 | 45 | We can use it as such: 46 | 47 | ```python 48 | # Set 'lower_bound' to 0, 'upper bound' to 10, and leave the 'value' parameter 49 | # to the default value 50 | pts1.spectrum.main.Powerlaw.K.prior = Uniform_prior(lower_bound = 0, upper_bound=10) 51 | 52 | # Display it 53 | pts1.spectrum.main.Powerlaw.K.display() 54 | 55 | ``` 56 | 57 | Now, lets's set a Gaussian prior on the spectral index 58 | 59 | ```python 60 | 61 | pts1.spectrum.main.Powerlaw.index.prior = Gaussian(mu=-2, sigma=1) 62 | 63 | pts1.spectrum.main.Powerlaw.index.display() 64 | ``` 65 | 66 | ```python 67 | # Let's get 500 points uniformly distributed between -20 and 20 68 | import numpy as np 69 | import matplotlib.pyplot as plt 70 | %matplotlib inline 71 | from jupyterthemes import jtplot 72 | jtplot.style(context="talk", fscale=1, ticks=True, grid=False) 73 | 74 | 75 | 76 | 77 | 78 | random_points = np.random.uniform(-10,20,100) 79 | 80 | fig, ax = plt.subplots() 81 | 82 | ax.plot(random_points,pts1.spectrum.main.Powerlaw.K.prior(random_points), '.' ) 83 | 84 | ax.set_ylim([-0.1,1.2]) 85 | ax.set_xlabel("value of K") 86 | ax.set_ylabel("Prior") 87 | ``` 88 | 89 | ```python 90 | random_points = np.random.uniform(-4,0,100) 91 | 92 | fig, ax = plt.subplots() 93 | 94 | ax.plot(random_points,pts1.spectrum.main.Powerlaw.index.prior(random_points), 'r.' ) 95 | 96 | ax.set_ylim([-0.1,0.6]) 97 | ax.set_xlabel("value of K") 98 | ax.set_ylabel("Prior") 99 | ``` 100 | 101 | ```python 102 | 103 | ``` 104 | -------------------------------------------------------------------------------- /docs/md/Quick_start.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupyter: 3 | jupytext: 4 | formats: ipynb,md 5 | text_representation: 6 | extension: .md 7 | format_name: markdown 8 | format_version: '1.3' 9 | jupytext_version: 1.14.1 10 | kernelspec: 11 | display_name: Python 3 (ipykernel) 12 | language: python 13 | name: python3 14 | --- 15 | 16 | # Quick start 17 | 18 | In this quick tutorial we will learn how to create a simple model with one point source, with a power law spectrum. You can of course use any function instead of the power law. Use ```list_models()``` to obtain a list of available models. 19 | 20 | Let’s define the model: 21 | 22 | ```python 23 | %%capture 24 | from astromodels import * 25 | ``` 26 | 27 | ```python 28 | test_source = PointSource('test_source',ra=123.22, dec=-13.56, spectral_shape=Powerlaw_flux()) 29 | 30 | my_model = Model(test_source) 31 | ``` 32 | 33 | Now let’s use it: 34 | 35 | 36 | 37 | ```python 38 | # Get and print the differential flux at 1 keV: 39 | 40 | differential_flux_at_1_keV = my_model.test_source(1.0) 41 | 42 | print("Differential flux @ 1 keV : %.3f photons / ( cm2 s keV)" % differential_flux_at_1_keV) 43 | 44 | ``` 45 | 46 | Evaluate the model on an array of 100 energies logarithmically distributed between 1 and 100 keV and plot it 47 | 48 | ```python 49 | import numpy as np 50 | import matplotlib.pyplot as plt 51 | %matplotlib inline 52 | from jupyterthemes import jtplot 53 | jtplot.style(context="talk", fscale=1, ticks=True, grid=False) 54 | 55 | # Set up the energies 56 | 57 | energies = np.logspace(0,2,100) 58 | 59 | # Get the differential flux 60 | 61 | differential_flux = my_model.test_source(energies) 62 | 63 | 64 | fig, ax = plt.subplots() 65 | 66 | ax.loglog(energies, differential_flux) 67 | 68 | ax.set_xlabel("Energy (keV)") 69 | ax.set_ylabel("Differential flux (ph./cm2/s/keV)") 70 | ``` 71 | -------------------------------------------------------------------------------- /docs/md/my_model.yml: -------------------------------------------------------------------------------- 1 | source_1 (point source): 2 | 3 | position: 4 | 5 | ra: 6 | 7 | value: 125.6 8 | 9 | desc: Right Ascension 10 | 11 | min_value: 0.0 12 | 13 | max_value: 360.0 14 | 15 | unit: deg 16 | 17 | is_normalization: false 18 | 19 | delta: 12.56 20 | 21 | free: false 22 | 23 | dec: 24 | 25 | value: -75.3 26 | 27 | desc: Declination 28 | 29 | min_value: -90.0 30 | 31 | max_value: 90.0 32 | 33 | unit: deg 34 | 35 | is_normalization: false 36 | 37 | delta: 7.53 38 | 39 | free: false 40 | 41 | equinox: J2000 42 | 43 | spectrum: 44 | 45 | main: 46 | 47 | Powerlaw: 48 | 49 | K: 50 | 51 | value: 1.2 52 | 53 | desc: Normalization (differential flux at the pivot value) 54 | 55 | min_value: 1.0e-30 56 | 57 | max_value: 1000.0 58 | 59 | unit: keV-1 s-1 cm-2 60 | 61 | is_normalization: true 62 | 63 | delta: 0.1 64 | 65 | free: true 66 | 67 | piv: 68 | 69 | value: 1.0 70 | 71 | desc: Pivot value 72 | 73 | min_value: null 74 | 75 | max_value: null 76 | 77 | unit: keV 78 | 79 | is_normalization: false 80 | 81 | delta: 0.1 82 | 83 | free: false 84 | 85 | index: 86 | 87 | value: -1.3 88 | 89 | desc: Photon index 90 | 91 | min_value: -10.0 92 | 93 | max_value: 10.0 94 | 95 | unit: '' 96 | 97 | is_normalization: false 98 | 99 | delta: 0.2 100 | 101 | free: true 102 | 103 | polarization: {} 104 | 105 | source_2 (point source): 106 | 107 | position: 108 | 109 | l: 110 | 111 | value: 11.25 112 | 113 | desc: Galactic longitude 114 | 115 | min_value: 0.0 116 | 117 | max_value: 360.0 118 | 119 | unit: deg 120 | 121 | is_normalization: false 122 | 123 | delta: 1.125 124 | 125 | free: false 126 | 127 | b: 128 | 129 | value: -22.5 130 | 131 | desc: Galactic latitude 132 | 133 | min_value: -90.0 134 | 135 | max_value: 90.0 136 | 137 | unit: deg 138 | 139 | is_normalization: false 140 | 141 | delta: 2.25 142 | 143 | free: false 144 | 145 | equinox: J2000 146 | 147 | spectrum: 148 | 149 | synchrotron: 150 | 151 | Powerlaw: 152 | 153 | K: 154 | 155 | value: 0.20000000000000004 156 | 157 | desc: Normalization (differential flux at the pivot value) 158 | 159 | min_value: 1.0e-30 160 | 161 | max_value: 1000.0 162 | 163 | unit: keV-1 s-1 cm-2 164 | 165 | is_normalization: true 166 | 167 | delta: 0.1 168 | 169 | free: true 170 | 171 | piv: 172 | 173 | value: 1.0 174 | 175 | desc: Pivot value 176 | 177 | min_value: null 178 | 179 | max_value: null 180 | 181 | unit: keV 182 | 183 | is_normalization: false 184 | 185 | delta: 0.1 186 | 187 | free: false 188 | 189 | index: 190 | 191 | value: -0.75 192 | 193 | desc: Photon index 194 | 195 | min_value: -10.0 196 | 197 | max_value: 10.0 198 | 199 | unit: '' 200 | 201 | is_normalization: false 202 | 203 | delta: 0.2 204 | 205 | free: true 206 | 207 | polarization: {} 208 | 209 | IC: 210 | 211 | Powerlaw: 212 | 213 | K: 214 | 215 | value: 0.8 216 | 217 | desc: Normalization (differential flux at the pivot value) 218 | 219 | min_value: 1.0e-30 220 | 221 | max_value: 1000.0 222 | 223 | unit: keV-1 s-1 cm-2 224 | 225 | is_normalization: true 226 | 227 | delta: 0.1 228 | 229 | free: true 230 | 231 | piv: 232 | 233 | value: 1.0 234 | 235 | desc: Pivot value 236 | 237 | min_value: null 238 | 239 | max_value: null 240 | 241 | unit: keV 242 | 243 | is_normalization: false 244 | 245 | delta: 0.1 246 | 247 | free: false 248 | 249 | index: 250 | 251 | value: -1.0 252 | 253 | desc: Photon index 254 | 255 | min_value: -10.0 256 | 257 | max_value: 10.0 258 | 259 | unit: '' 260 | 261 | is_normalization: false 262 | 263 | delta: 0.2 264 | 265 | free: true 266 | 267 | polarization: {} 268 | 269 | -------------------------------------------------------------------------------- /docs/media/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/docs/media/favicon.ico -------------------------------------------------------------------------------- /docs/media/large_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/docs/media/large_logo.png -------------------------------------------------------------------------------- /docs/media/solid_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/docs/media/solid_logo.png -------------------------------------------------------------------------------- /docs/media/transp_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/docs/media/transp_logo.png -------------------------------------------------------------------------------- /docs/notebooks/_stub: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/docs/notebooks/_stub -------------------------------------------------------------------------------- /docs/release_notes.rst: -------------------------------------------------------------------------------- 1 | Release Notes 2 | ============= 3 | 4 | 5 | Version 2.2 6 | ----------- 7 | 8 | 9 | v2.2.2 10 | ^^^^^^^^ 11 | *Tue, 17 Aug 2020 12:30:08 + 0000* 12 | 13 | * update XSPEC models to handle different versions up to 14 | 12.12.0, Now setting an environment variable: ASTRO_XSPEC_VERSION 15 | to the required version is needed, i.e., 12.10.1. A default of 16 | 12.10.1 is assumed otherwise 17 | 18 | 19 | v2.2.1 20 | ^^^^^^^^ 21 | *Tue, 17 Aug 2020 06:30:08 + 0000* 22 | 23 | * New documentation style 24 | * Added astromodels configuration: 25 | https://github.com/threeML/astromodels/pull/161 26 | * Added sources as enum objects: 27 | https://github.com/threeML/astromodels/pull/160 28 | * Added a cutoff power law with epeak parameterization: 29 | https://github.com/threeML/astromodels/pull/159 30 | * Created internal HDF5 table model instead of using 31 | `pandas` format: https://github.com/threeML/astromodels/pull/155 32 | * Refactor of function code for clarity: 33 | https://github.com/threeML/astromodels/pull/153 34 | * Fixed bug in XSPEC models due to new `astropy` units 35 | 36 | 37 | v2.2.0 38 | ^^^^^^^^ 39 | *Tue, 06 April 2021 00:02:29 + 0000* 40 | 41 | * Added logging 42 | * More models for x-ray analysis (APEC, absorption models) 43 | * Added power law with normalization of energy flux 44 | * Issue(s) closed: 45 | 46 | * 47 | 48 | 49 | Version 2.0 50 | ----------- 51 | 52 | 53 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | ipython 2 | rtds_action 3 | better_apidoc 4 | sphinx-gallery<0.11 5 | numpy 6 | nbsphinx 7 | cython 8 | numpy 9 | pandas 10 | numba<0.59 11 | sphinx_rtd_dark_mode 12 | joblib 13 | mock 14 | recommonmark -------------------------------------------------------------------------------- /examples/Introduction.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Astromodels is a very flexible framework to define models for likelihood or Bayesian analysis of astrophysical data. \n", 15 | "\n", 16 | "Even though it has been designed having in mind analysis in the spectral domain, it can be used also as a toolbox containing functions of any variable.\n", 17 | "\n", 18 | "Astromodels is *not* a modeling package, it only gives you the tools to build a model as complex as you need. You then need a separate package (such as 3ML, github.com/giacomov/3ML) to fit that model to the data.\n", 19 | "\n", 20 | "Some of the features which distinguish astromodels from other similar packages are:\n", 21 | "* a model can contain an arbitrary number of sources at different positions in the sky\n", 22 | "* parameters can be linked through any function (not only identity)\n", 23 | "* parameters can vary with *auxiliary variables* such as time. For example, you can build a model where some parameters vary with time, and you can fit the parameters of the function which describe this variability. Similary you can build models where parameters vary with the phase of a pulsar, and so on.\n", 24 | "* models can be saved in and loaded from YAML file (a human-readable format)\n", 25 | "* physical units are fully supported in input, but they are handled so that they don't slow down the actualy computation of the models.\n", 26 | "\n", 27 | "Astromodels has been designed with performance as priority, and is considerably faster than other python-based solution for the same problem, such as astropy.modeling and the modeling part of sherpa." 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": { 34 | "collapsed": true 35 | }, 36 | "outputs": [], 37 | "source": [] 38 | } 39 | ], 40 | "metadata": { 41 | "kernelspec": { 42 | "display_name": "Python 2", 43 | "language": "python", 44 | "name": "python2" 45 | }, 46 | "language_info": { 47 | "codemirror_mode": { 48 | "name": "ipython", 49 | "version": 2 50 | }, 51 | "file_extension": ".py", 52 | "mimetype": "text/x-python", 53 | "name": "python", 54 | "nbconvert_exporter": "python", 55 | "pygments_lexer": "ipython2", 56 | "version": "2.7.6" 57 | } 58 | }, 59 | "nbformat": 4, 60 | "nbformat_minor": 0 61 | } 62 | -------------------------------------------------------------------------------- /examples/convert_to_rst.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # COnvert all notebooks to .rst format and save them 4 | # in the ../doc folder 5 | 6 | from __future__ import print_function 7 | import glob 8 | import subprocess 9 | import shutil 10 | import os 11 | 12 | notebooks = glob.glob("*.ipynb") 13 | 14 | for nb in notebooks: 15 | 16 | root = nb.split(".")[0] 17 | 18 | cmd_line = 'ipython nbconvert --to rst %s' % (nb) 19 | 20 | print(cmd_line) 21 | 22 | subprocess.check_call(cmd_line,shell=True) 23 | 24 | # Now move the .rst file and the directory with the data 25 | # under ../doc 26 | 27 | try: 28 | 29 | os.remove("../doc/%s.rst" % root) 30 | 31 | except: 32 | 33 | pass 34 | 35 | files_dir = "%s_files" % root 36 | 37 | try: 38 | 39 | shutil.rmtree("../doc/%s" % files_dir) 40 | 41 | except: 42 | 43 | pass 44 | 45 | shutil.move("%s.rst" % root,"../doc") 46 | 47 | if os.path.exists(files_dir): 48 | 49 | shutil.move(files_dir, "../doc") 50 | -------------------------------------------------------------------------------- /examples/createDMSpectra.py: -------------------------------------------------------------------------------- 1 | # code to demonstrate how to create a spectra object for dark matter models 2 | # author: Andrea Albert (aalbert@slac.stanford.edu) 3 | # date: Oct 26, 2016 4 | 5 | from threeML import * 6 | 7 | # DMFitFunction uses the Pythia-generated table from the standard Fermi Science Tools which is appropriate for 2 GeV < mass < 10 TeV 8 | # DMSpectra combines the Pythia-generated table from the standard Fermi Science Tools (for 2 GeV < mass < 10 TeV) and the one used by the HAWC Collaboration (for 10 TeV < mass < 1 PeV). 9 | 10 | spec = DMFitFunction() 11 | spec2 = DMSpectra() 12 | 13 | channels = spec.print_channel_mapping() 14 | 15 | spec.mass = 1000. # mass needs to be in GeV 16 | spec.channel = 4 # bb 17 | spec.sigmav = 3.e-26 # sigmav must be in cm^3 / s 18 | spec.J = 1.e20 # J-factor must be in GeV^2 / cm^5 19 | 20 | spec2.mass = 50000. # mass needs to be in GeV 21 | spec2.channel = 4 # bb 22 | spec2.sigmav = 3.e-26 # sigmav must be in cm^3 / s 23 | spec2.J = 1.e20 # J-factor must be in GeV^2 / cm^5 24 | 25 | en = np.logspace(6.,11.,100) # en is in keV. so from 1 GeV to 100 TeV 26 | 27 | DMspec = spec.evaluate(en,spec.mass.value,spec.channel.value,spec.sigmav.value,spec.J.value) 28 | DMspec2 = spec.evaluate(en,spec2.mass.value,spec2.channel.value,spec2.sigmav.value,spec2.J.value) -------------------------------------------------------------------------------- /examples/createExtendedSourceFromTemplate.py: -------------------------------------------------------------------------------- 1 | # code to show how to create an extended source via uploading a FITs image template 2 | # author: Andrea Albert (aalbert@slac.stanford.edu) 3 | # date: Oct 26, 2016 4 | 5 | from threeML import * 6 | 7 | # the class SpatialTemplate_2D expects a FITs file that contains a header with the following info: reference pixels (e.g. 'CRPIX1'), pixels step in degrees (e.g. 'CDELT1'), RA and DEC values at reference pixel (e.g. 'CRVAL1') 8 | 9 | # initialize shape object 10 | shape = SpatialTemplate_2D() 11 | # load in template file 12 | # by default the extension number is set to zero (ihdu = 0) 13 | shape.load_file("exampleDMtemplate.fits",ihdu=0) 14 | 15 | # just for example let's assume a powerlaw spectrum 16 | spectrum = Powerlaw() 17 | 18 | source = ExtendedSource("M31",spatial_shape=shape,spectral_shape=spectrum) 19 | 20 | # The code assumes the template is normalized to 1 sr. If it isn't be default then you should set the optional normalization (K) appropriately. The example template is already normalized to 1 sr so we'll keep K set to 1. Note K is set to 1 and fixed by default, we include the following commands as an example of how to manipulate K 21 | shape.K = 1. 22 | shape.K.fix = True 23 | 24 | # The following are example commands that get called during fitting 25 | 26 | # get the edges of the template 27 | (min_ra,max_ra),(min_dec,max_dec) = shape.get_boundaries() 28 | 29 | # return the values at various pixels at locations (x,y). Note the code assumes x=RA (degrees) and y=DEC(degrees). Note the code will return a value of 0 is the pixel is outside the template ROI...in this example only the 2nd pixel will have a non-zero value 30 | val = shape.evaluate(x=[1.,10.,10.],y=[1.,40.,89.],K=1) -------------------------------------------------------------------------------- /examples/exampleDMtemplate.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/examples/exampleDMtemplate.fits -------------------------------------------------------------------------------- /examples/showcase/naima_test.yml: -------------------------------------------------------------------------------- 1 | electrons (particle source): 2 | 3 | spectrum: 4 | 5 | main: 6 | 7 | log_parabola: 8 | 9 | K: {value: 1.0000000000000002e+38, desc: Normalization, min_value: null, max_value: null, 10 | 11 | unit: 1 / keV, delta: 0.3, free: true} 12 | 13 | piv: {value: 1.0, desc: Pivot (keep this fixed), min_value: null, max_value: null, 14 | 15 | unit: keV, delta: 0.3, free: false} 16 | 17 | alpha: {value: -0.75, desc: index, min_value: null, max_value: null, unit: '', 18 | 19 | delta: 0.6, free: true} 20 | 21 | beta: 22 | 23 | value: f(time) 24 | 25 | desc: curvature (negative is concave, positive is convex) 26 | 27 | min_value: null 28 | 29 | max_value: null 30 | 31 | unit: '' 32 | 33 | law: 34 | 35 | line: 36 | 37 | a: {value: -0.0032, desc: linear coefficient, min_value: null, max_value: null, 38 | 39 | unit: 1 / s, delta: 0.3, free: true} 40 | 41 | b: {value: 0.0, desc: intercept, min_value: null, max_value: null, unit: '', 42 | 43 | delta: 0.1, free: true} 44 | 45 | delta: 0.3 46 | 47 | free: false 48 | 49 | polarization: {} 50 | 51 | synch_source (point source): 52 | 53 | position: 54 | 55 | ra: {value: 12.6, desc: Right Ascension, min_value: 0.0, max_value: 360.0, unit: deg, 56 | 57 | delta: 3.78, free: false} 58 | 59 | dec: {value: -13.5, desc: Declination, min_value: -90.0, max_value: 90.0, unit: deg, 60 | 61 | delta: 4.05, free: false} 62 | 63 | equinox: J2000 64 | 65 | spectrum: 66 | 67 | main: 68 | 69 | synchrotron: 70 | 71 | B: 72 | 73 | value: f(time) 74 | 75 | desc: magnetic field 76 | 77 | min_value: null 78 | 79 | max_value: null 80 | 81 | unit: G 82 | 83 | law: 84 | 85 | powerlaw: 86 | 87 | K: {value: 0.000125, desc: Normalization (differential flux at the pivot 88 | 89 | value), min_value: null, max_value: null, unit: G, delta: 0.3, free: true} 90 | 91 | piv: {value: 1.0, desc: Pivot value, min_value: null, max_value: null, 92 | 93 | unit: s, delta: 0.3, free: false} 94 | 95 | index: {value: -1.0, desc: Photon index, min_value: -10.0, max_value: 10.0, 96 | 97 | unit: '', delta: 0.6, free: true} 98 | 99 | delta: 9.72e-07 100 | 101 | free: false 102 | 103 | distance: {value: 1.0, desc: distance of the source, min_value: null, max_value: null, 104 | 105 | unit: kpc, delta: 0.3, free: true} 106 | 107 | emin: {value: 1.0, desc: minimum energy for the particle distribution, min_value: null, 108 | 109 | max_value: null, unit: GeV, delta: 0.3, free: false} 110 | 111 | emax: {value: 510000.0, desc: maximum energy for the particle distribution, 112 | 113 | min_value: null, max_value: null, unit: GeV, delta: 153000.0, free: false} 114 | 115 | need: {value: 10.0, desc: number of points per decade in which to evaluate 116 | 117 | the function, min_value: 2.0, max_value: 100.0, unit: '', delta: 3.0, 118 | 119 | free: false} 120 | 121 | extra_setup: {particle_distribution: electrons.spectrum.main.log_parabola} 122 | 123 | polarization: {} 124 | 125 | time (IndependentVariable): {value: 100.0, desc: None, min_value: null, max_value: null, 126 | 127 | unit: s} 128 | 129 | -------------------------------------------------------------------------------- /examples/testGalprop3DFunction.py: -------------------------------------------------------------------------------- 1 | # code to show how to use the GalPropTemplate_3D() function 2 | # author: Hugo Ayala (hgayala@psu.edu) 3 | # date: Apr 22, 2019 4 | import os 5 | 6 | import numpy as np 7 | from threeML import * 8 | 9 | MODELMAP = 'testICModel.fits' 10 | 11 | # Create and initialze shape object 12 | shape = GalPropTemplate_3D() 13 | 14 | # Load template file. Information that is needed is: 15 | # Filename, RA(Long) low, RA(Long) high, 16 | # Dec(Lat) low, Dec(Lat), high, and bool variable to tell if coordinates 17 | # are in galactic or celestial. 18 | # The galprop map was generated with flux information in units of Mev / (cm^2 s sr) 19 | shape.load_file(MODELMAP, 65, 75, -5, 5, True) 20 | 21 | # The templates are cubes that contain spatial, energy and flux information. 22 | 23 | # Functions that are called during the fitting 24 | print( "Function get boundaries()") 25 | (ramin, ramax), (decmin, decmax) = shape.get_boundaries() 26 | print ( "RA: {:0.2f},{:0.2f} Dec: {:0.2f},{:0.2f}".format(ramin, ramax, decmin, decmax)) 27 | 28 | print("\nFunction evaluate()") 29 | 30 | x = np.linspace(ramin, ramax, 10) 31 | y = np.linspace(decmin, decmax, 10) 32 | e = np.array([100000, 1000000, 10000000, 100000000, 1000000000]) 33 | res = shape.evaluate(x, y, e, 1, 1) 34 | print(res) 35 | -------------------------------------------------------------------------------- /examples/testICModel.fits: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/examples/testICModel.fits -------------------------------------------------------------------------------- /scripts/doc_gen_1d.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupyter: 3 | jupytext: 4 | formats: ipynb,md 5 | text_representation: 6 | extension: .md 7 | format_name: markdown 8 | format_version: '1.3' 9 | jupytext_version: 1.11.2 10 | kernelspec: 11 | display_name: Python 3 12 | language: python 13 | name: python3 14 | --- 15 | 16 | # func_title 17 | 18 | ```python nbsphinx="hidden" tags=[] 19 | %%capture 20 | 21 | import numpy as np 22 | 23 | import matplotlib.pyplot as plt 24 | 25 | import warnings 26 | warnings.simplefilter("ignore") 27 | 28 | from astromodels.functions.function import _known_functions 29 | 30 | 31 | from jupyterthemes import jtplot 32 | jtplot.style(context="talk", fscale=1, ticks=True, grid=False) 33 | %matplotlib inline 34 | ``` 35 | 36 | ```python nbsphinx="hidden" tags=["parameters"] 37 | func_name = "TbAbs" 38 | 39 | x_scale="log" 40 | y_scale="log" 41 | 42 | linear_range = False 43 | 44 | wide_energy_range = False 45 | ``` 46 | 47 | ```python nbsphinx="hidden" tags=[] 48 | func = _known_functions[func_name]() 49 | 50 | if wide_energy_range: 51 | 52 | energy_grid = np.geomspace(1e2,1e4,500) 53 | 54 | else: 55 | 56 | energy_grid = np.geomspace(2e-1,1e1,1000) 57 | 58 | if linear_range: 59 | 60 | energy_grid = np.linspace(-5,5,1000) 61 | 62 | 63 | blue = "#4152E3" 64 | red = "#E3414B" 65 | green = "#41E39E" 66 | ``` 67 | ## Description 68 | ```python 69 | func.display() 70 | ``` 71 | 72 | ## Shape 73 | 74 | The shape of the function. 75 | 76 | *If this is not a photon model but a prior or linear function then ignore the units as these docs are auto-generated* 77 | 78 | ```python tags=["nbsphinx-thumbnail"] 79 | fig, ax = plt.subplots() 80 | 81 | 82 | ax.plot(energy_grid, func(energy_grid), color=blue) 83 | 84 | ax.set_xlabel("energy (keV)") 85 | ax.set_ylabel("photon flux") 86 | ax.set_xscale(x_scale) 87 | ax.set_yscale(y_scale) 88 | 89 | ``` 90 | 91 | ## F$_{\nu}$ 92 | 93 | The F$_{\nu}$ shape of the photon model 94 | *if this is not a photon model, please ignore this auto-generated plot* 95 | ```python 96 | fig, ax = plt.subplots() 97 | 98 | ax.plot(energy_grid, energy_grid * func(energy_grid), red) 99 | 100 | 101 | ax.set_xlabel("energy (keV)") 102 | ax.set_ylabel(r"energy flux (F$_{\nu}$)") 103 | ax.set_xscale(x_scale) 104 | ax.set_yscale(y_scale) 105 | 106 | 107 | ``` 108 | 109 | ## $\nu$F$_{\nu}$ 110 | 111 | The $\nu$F$_{\nu}$ shape of the photon model 112 | *if this is not a photon model, please ignore this auto-generated plot* 113 | 114 | ```python 115 | fig, ax = plt.subplots() 116 | 117 | ax.plot(energy_grid, energy_grid**2 * func(energy_grid), color=green) 118 | 119 | 120 | ax.set_xlabel("energy (keV)") 121 | ax.set_ylabel(r"$\nu$F$_{\nu}$") 122 | ax.set_xscale(x_scale) 123 | ax.set_yscale(y_scale) 124 | 125 | ``` 126 | -------------------------------------------------------------------------------- /scripts/doc_gen_2d.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupyter: 3 | jupytext: 4 | formats: ipynb,md 5 | text_representation: 6 | extension: .md 7 | format_name: markdown 8 | format_version: '1.3' 9 | jupytext_version: 1.11.2 10 | kernelspec: 11 | display_name: Python 3 12 | language: python 13 | name: python3 14 | --- 15 | 16 | # func_title 17 | 18 | ```python nbsphinx="hidden" tags=[] 19 | %%capture 20 | 21 | import numpy as np 22 | 23 | import matplotlib.pyplot as plt 24 | 25 | import warnings 26 | warnings.simplefilter("ignore") 27 | 28 | from astromodels.functions.function import _known_functions 29 | 30 | import healpy as hp 31 | 32 | 33 | from astropy.coordinates import ICRS, Galactic 34 | 35 | 36 | from jupyterthemes import jtplot 37 | jtplot.style(context="talk", fscale=1, ticks=False, grid=False) 38 | %matplotlib inline 39 | 40 | 41 | 42 | 43 | ``` 44 | 45 | ```python nbsphinx="hidden" tags=["parameters"] 46 | func_name = "Latitude_galactic_diffuse" 47 | 48 | ``` 49 | 50 | ```python nbsphinx="hidden" tags=[] 51 | func = _known_functions[func_name]() 52 | 53 | NSIDE = 2**7 54 | NPIX = hp.nside2npix(NSIDE) 55 | 56 | ra, dec = hp.pix2ang(nside=NSIDE, ipix=np.arange(NPIX), lonlat=True) 57 | 58 | ``` 59 | ## Description 60 | ```python 61 | func.display() 62 | ``` 63 | 64 | ## Shape 65 | 66 | The shape of the function on the sky. 67 | ```python tags=["nbsphinx-thumbnail"] 68 | 69 | 70 | m=func(ra, dec) 71 | hp.mollview(m, title=func_name, cmap="magma") 72 | hp.graticule(color="grey", lw=2) 73 | 74 | 75 | 76 | ``` 77 | -------------------------------------------------------------------------------- /scripts/doc_gen_function_list.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupyter: 3 | jupytext: 4 | text_representation: 5 | extension: .md 6 | format_name: markdown 7 | format_version: '1.3' 8 | jupytext_version: 1.14.1 9 | kernelspec: 10 | display_name: Python 3 (ipykernel) 11 | language: python 12 | name: python3 13 | --- 14 | 15 | # Available Functions 16 | 17 | The galleries below display the available functions which are included in `astromodels`. 18 | More functions can be included by either making your own or installing optional packages. 19 | 20 | ## Included 1D Functions 21 | 22 | 23 | ## 1D Functions 24 | 25 | 26 | ## Included 2D Functions 27 | 28 | 29 | ## 2D Functions 30 | 31 | 32 | ## Included Prior Functions 33 | Priors can be used as regular functions but have special properties that enable 34 | them to be used with various sampling algorithms. 35 | 36 | 37 | ## Priors 38 | 39 | 40 | -------------------------------------------------------------------------------- /scripts/doc_gen_priors.md: -------------------------------------------------------------------------------- 1 | --- 2 | jupyter: 3 | jupytext: 4 | formats: ipynb,md 5 | text_representation: 6 | extension: .md 7 | format_name: markdown 8 | format_version: '1.3' 9 | jupytext_version: 1.14.1 10 | kernelspec: 11 | display_name: Python 3 (ipykernel) 12 | language: python 13 | name: python3 14 | --- 15 | 16 | # func_title 17 | 18 | ```python nbsphinx="hidden" tags=[] 19 | %%capture 20 | 21 | import numpy as np 22 | 23 | import matplotlib.pyplot as plt 24 | 25 | import warnings 26 | warnings.simplefilter("ignore") 27 | 28 | from astromodels.functions.function import _known_functions 29 | 30 | 31 | from jupyterthemes import jtplot 32 | jtplot.style(context="talk", fscale=1, ticks=True, grid=False) 33 | %matplotlib inline 34 | ``` 35 | 36 | ```python nbsphinx="hidden" tags=["parameters"] 37 | func_name = "TbAbs" 38 | 39 | positive_prior = False 40 | 41 | ``` 42 | 43 | ```python nbsphinx="hidden" tags=[] 44 | func = _known_functions[func_name]() 45 | 46 | if not positive_prior: 47 | 48 | energy_grid = np.linspace(-5,5,1000) 49 | 50 | else: 51 | 52 | energy_grid = np.linspace(0,1,1000) 53 | 54 | 55 | 56 | 57 | blue = "#4152E3" 58 | red = "#E3414B" 59 | green = "#41E39E" 60 | ``` 61 | ## Description 62 | ```python 63 | func.display() 64 | ``` 65 | 66 | ## Shape 67 | 68 | The shape of the function. 69 | 70 | *If this is not a photon model but a prior or linear function then ignore the units as these docs are auto-generated* 71 | 72 | ```python tags=["nbsphinx-thumbnail"] 73 | fig, ax = plt.subplots() 74 | 75 | 76 | ax.plot(energy_grid, func(energy_grid), color=blue, lw=3) 77 | 78 | ax.set_xlabel("x") 79 | ax.set_ylabel("probability") 80 | 81 | ``` 82 | 83 | ## Random Number Generation 84 | 85 | This is how we can generate random numbers from the prior. 86 | 87 | 88 | ```python 89 | 90 | 91 | u = np.random.uniform(0,1, size=5000) 92 | 93 | draws = [func.from_unit_cube(x) for x in u] 94 | 95 | 96 | fig, ax = plt.subplots() 97 | 98 | 99 | ax.hist(draws, color=green, bins=50) 100 | 101 | ax.set_xlabel("value") 102 | ax.set_ylabel("N") 103 | 104 | 105 | ``` 106 | -------------------------------------------------------------------------------- /scripts/generate_func_docs.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import jupytext 4 | import papermill as pm 5 | 6 | from astromodels.functions.function import _known_functions 7 | 8 | narrow_energy_funcs = ["PhAbs", "TbAbs", "WAbs"] 9 | 10 | models_to_exclude = [ 11 | "_ComplexTestFunction", 12 | "TemplateModel", 13 | "SpatialTemplate_2D", 14 | "GenericFunction" 15 | ] 16 | 17 | positive_priors = [ 18 | "Log_uniform_prior", 19 | "Log_normal", 20 | "Gamma", 21 | "Beta", 22 | "Exponential", 23 | "Powerlaw_Prior", 24 | ] 25 | 26 | linear_models = [ 27 | "Constant", 28 | "Cubic", 29 | "DiracDelta", 30 | "Line", 31 | "Quadratic", 32 | "Quartic", 33 | "StepFunction", 34 | "StepFunctionUpper", 35 | "Sin", 36 | ] 37 | 38 | one_d_func_list = [] 39 | two_d_func_list = [] 40 | prior_func_list = [] 41 | 42 | with open("doc_gen_1d.md") as f: 43 | 44 | base_1d_md_str = f.read() 45 | 46 | with open("doc_gen_2d.md") as f: 47 | 48 | base_2d_md_str = f.read() 49 | 50 | with open("doc_gen_priors.md") as f: 51 | 52 | base_prior_md_str = f.read() 53 | 54 | 55 | base_path = Path("../docs/notebooks").resolve() 56 | 57 | # we will loop through all the functions and generate docs for them 58 | 59 | for k, v in _known_functions.items(): 60 | 61 | if k in models_to_exclude: 62 | 63 | continue 64 | 65 | instance = v() 66 | 67 | ntbk_file_name = f"{k}.ipynb" 68 | 69 | if instance.n_dim == 1: 70 | 71 | if not instance.is_prior: 72 | 73 | print(f"generating {k}") 74 | 75 | one_d_func_list.append(k) 76 | 77 | # inject the func name into the markdown 78 | 79 | this_md_str = base_1d_md_str.replace( 80 | "func_title", k.replace("_", " ") 81 | ) 82 | 83 | # create 84 | 85 | ntbk = jupytext.reads(this_md_str, fmt="md") 86 | 87 | wide_energy_range = True 88 | 89 | if k in narrow_energy_funcs: 90 | 91 | wide_energy_range = False 92 | 93 | x_scale = "log" 94 | y_scale = "log" 95 | 96 | linear_range = False 97 | 98 | if k in linear_models or instance.is_prior: 99 | 100 | linear_range = True 101 | 102 | x_scale = "linear" 103 | y_scale = "linear" 104 | 105 | out = jupytext.write(ntbk, ntbk_file_name) 106 | 107 | print(f"excecuting {ntbk_file_name}") 108 | 109 | nb = pm.execute_notebook( 110 | ntbk_file_name, 111 | f"{base_path / ntbk_file_name}", 112 | parameters=dict( 113 | func_name=k, 114 | wide_energy_range=wide_energy_range, 115 | x_scale=x_scale, 116 | y_scale=y_scale, 117 | linear_range=linear_range, 118 | ), 119 | ) 120 | 121 | else: 122 | 123 | prior_func_list.append(k) 124 | 125 | print(f"generating {k}") 126 | 127 | # inject the func name into the markdown 128 | 129 | this_md_str = base_prior_md_str.replace( 130 | "func_title", k.replace("_", " ") 131 | ) 132 | 133 | # create 134 | 135 | ntbk = jupytext.reads(this_md_str, fmt="md") 136 | 137 | out = jupytext.write(ntbk, ntbk_file_name) 138 | 139 | positive_prior = False 140 | 141 | if k in positive_priors: 142 | 143 | positive_prior = True 144 | 145 | print(f"excecuting {ntbk_file_name}") 146 | 147 | nb = pm.execute_notebook( 148 | ntbk_file_name, 149 | f"{base_path / ntbk_file_name}", 150 | parameters=dict( 151 | func_name=k, 152 | positive_prior=positive_prior, 153 | ), 154 | ) 155 | 156 | if instance.n_dim == 2: 157 | 158 | print(f"generating {k}") 159 | 160 | two_d_func_list.append(k) 161 | 162 | # inject the func name into the markdown 163 | 164 | this_md_str = base_2d_md_str.replace("func_title", k.replace("_", " ")) 165 | 166 | # create 167 | 168 | ntbk = jupytext.reads(this_md_str, fmt="md") 169 | 170 | out = jupytext.write(ntbk, ntbk_file_name) 171 | 172 | print(f"excecuting {ntbk_file_name}") 173 | 174 | nb = pm.execute_notebook( 175 | ntbk_file_name, 176 | f"{base_path / ntbk_file_name}", 177 | parameters=dict( 178 | func_name=k, 179 | ), 180 | ) 181 | 182 | path = Path(ntbk_file_name) 183 | 184 | if path.exists(): 185 | 186 | path.unlink() 187 | 188 | del nb 189 | 190 | del ntbk 191 | 192 | del out 193 | 194 | # Now we generate the gallery notebook 195 | 196 | with Path("doc_gen_function_list.md").open("r") as f: 197 | 198 | func_nb = jupytext.reads(f.read(), fmt="md") 199 | 200 | 201 | cells = func_nb["cells"] 202 | 203 | 204 | for cell in cells: 205 | 206 | source = cell["source"] 207 | 208 | if source == "## 1D Functions": 209 | 210 | new_source = ["## 1D Functions", "\n"] 211 | 212 | for func_name in one_d_func_list: 213 | 214 | line = f"* [{func_name}]({func_name}.ipynb)\n" 215 | 216 | new_source.append(line) 217 | 218 | cell["source"] = new_source 219 | 220 | elif source == "## 2D Functions": 221 | 222 | new_source = ["## 2D Functions", "\n"] 223 | 224 | for func_name in two_d_func_list: 225 | 226 | line = f"* [{func_name}]({func_name}.ipynb)\n" 227 | 228 | new_source.append(line) 229 | 230 | cell["source"] = new_source 231 | 232 | elif source == "## Priors": 233 | 234 | new_source = ["## Priors", "\n"] 235 | 236 | for func_name in prior_func_list: 237 | 238 | line = f"* [{func_name}]({func_name}.ipynb)\n" 239 | 240 | new_source.append(line) 241 | 242 | cell["source"] = new_source 243 | 244 | 245 | func_list_file_name = "function_list.ipynb" 246 | 247 | 248 | jupytext.write(func_nb, f"{base_path / func_list_file_name }") 249 | -------------------------------------------------------------------------------- /scripts/generate_func_rst.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from astromodels.functions.function import _known_functions 4 | 5 | narrow_energy_funcs = ["PhAbs", "TbAbs", "WAbs"] 6 | 7 | models_to_exclude = ["_ComplexTestFunction", "TemplateModel", "SpatialTemplate_2D"] 8 | 9 | 10 | linear_models = [ 11 | "Constant", 12 | "Cubic", 13 | "DiracDelta", 14 | "Line", 15 | "Quadratic", 16 | "Quartic", 17 | "StepFunction", 18 | "StepFunctionUpper", 19 | "Sin", 20 | ] 21 | 22 | 23 | one_d_func_list = [] 24 | two_d_func_list = [] 25 | # we will loop through all the functions and generate docs for them 26 | 27 | for k, v in _known_functions.items(): 28 | 29 | if k in models_to_exclude: 30 | 31 | continue 32 | 33 | instance = v() 34 | 35 | if instance.n_dim == 1: 36 | 37 | print(f"generating {k}") 38 | 39 | one_d_func_list.append(k) 40 | 41 | if instance.n_dim == 2: 42 | 43 | print(f"generating {k}") 44 | 45 | two_d_func_list.append(k) 46 | 47 | 48 | # now we want to update the ReST galleries 49 | 50 | with open("functions_1d.rst") as f: 51 | 52 | lines = f.readlines() 53 | 54 | for func in one_d_func_list: 55 | 56 | lines.append(f" ../notebooks/{func}.ipynb\n") 57 | 58 | #p = Path("../docs/function_docs/functions_1d.rst").absolute() 59 | 60 | p = Path("../docs/function_docs/functions_1d.rst").absolute() 61 | 62 | with p.open("w") as f: 63 | 64 | for line in lines: 65 | f.write(line) 66 | 67 | 68 | # now we want to update the ReST galleries 69 | 70 | with open("functions_2d.rst") as f: 71 | 72 | lines = f.readlines() 73 | 74 | for func in two_d_func_list: 75 | 76 | lines.append(f" ../notebooks/{func}.ipynb\n") 77 | 78 | p = Path("../docs/function_docs/functions_2d.rst").absolute() 79 | 80 | with p.open("w") as f: 81 | 82 | for line in lines: 83 | f.write(line) 84 | -------------------------------------------------------------------------------- /scripts/generate_test_data.py: -------------------------------------------------------------------------------- 1 | # this generates test data to make sure changes in the 2 | # code do not destroy the values. If a new function is added 3 | # this needs to be re run 4 | # 5 | # 6 | 7 | import sys 8 | 9 | import h5py 10 | 11 | from astromodels.functions.function import _known_functions 12 | from astromodels.functions.priors import * 13 | from astromodels.utils.data_files import _get_data_file_path 14 | import numpy as np 15 | 16 | eval_x = np.logspace(-1, 3, 10) 17 | _multiplicative_models = ["PhAbs", "TbAbs", "WAbs", "APEC", "VAPEC"] 18 | 19 | input = int(sys.argv[-1]) 20 | 21 | file_path = _get_data_file_path("past_1D_values.h5") 22 | 23 | 24 | if input == 0: 25 | # do not regenerate only add 26 | with h5py.File(file_path, "r") as f: 27 | 28 | already_known_functions = list(f.keys()) 29 | 30 | flag = "a" 31 | 32 | elif input == 1: 33 | 34 | already_known_functions = [] 35 | 36 | flag = "w" 37 | 38 | else: 39 | 40 | already_known_functions = None 41 | 42 | print(input, already_known_functions) 43 | 44 | 45 | with h5py.File(file_path, flag) as f: 46 | if input == 1: 47 | 48 | f.create_dataset("eval_values", data=eval_x, compression="lzf") 49 | 50 | for key in _known_functions: 51 | 52 | if key in already_known_functions: 53 | 54 | continue 55 | 56 | this_function = _known_functions[key] 57 | 58 | # Test only the power law of XSpec, which is the only one we know we can test at 1 keV 59 | 60 | if ( 61 | key.find("XS") == 0 62 | and key != "XS_powerlaw" 63 | or (key in _multiplicative_models) 64 | ): 65 | 66 | # An XSpec model. Test it only if it's a power law (the others might need other parameters during 67 | # initialization) 68 | 69 | continue 70 | 71 | if key.find("TemplateModel") == 0: 72 | 73 | # The TemplateModel function has its own test 74 | 75 | continue 76 | 77 | # if key.find("Synchrotron")==0: 78 | 79 | # Naima Synchtron function should have its own test 80 | 81 | # continue 82 | 83 | if this_function._n_dim == 1: 84 | 85 | print("testing %s ..." % key) 86 | if key == "_ComplexTestFunction": 87 | func = this_function(file_name="lost.txt", dummy="test") 88 | elif key == "GenericFunction": 89 | 90 | def _f(x): 91 | return x**2 92 | 93 | func = this_function() 94 | func.set_function(_f) 95 | else: 96 | func = this_function() 97 | 98 | data = func(eval_x) 99 | 100 | print(data) 101 | 102 | f.create_dataset(key, data=np.atleast_1d(data), compression="lzf") 103 | -------------------------------------------------------------------------------- /scripts/past_1D_values.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threeML/astromodels/df43e68c147c36fd760a589d6223ddc35fb27cbe/scripts/past_1D_values.h5 -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [aliases] 2 | test=pytest 3 | 4 | [metadata] 5 | name = astromodels 6 | description = Astromodels contains models to be used in likelihood or Bayesian analysis in astronomy 7 | long_description = file:README.md 8 | long_description_content_type = text/markdown 9 | url = https://github.com/threeml/astromodels 10 | 11 | author_email = giacomo.vianello@gmail.com 12 | author = Giacomo Vianello 13 | requires_python = >=3.9.0 14 | 15 | 16 | project_urls = 17 | Documentation = https://astromodels.readthedocs.io 18 | Bug Tracker = https://github.com/threeML/astromodels/issues 19 | Source Code = https://github.com/threeML/astromodels 20 | 21 | classifiers = 22 | Development Status :: 5 - Production/Stable 23 | Topic :: Scientific/Engineering :: Astronomy 24 | Intended Audience :: Science/Research 25 | Operating System :: POSIX 26 | Programming Language :: Python :: 3.9 27 | Environment :: Console 28 | 29 | [options] 30 | include_package_data = True 31 | 32 | install_requires = 33 | numpy>=1.6 34 | PyYAML>=5.1 35 | scipy>=0.14 36 | astropy>=1.2 37 | dill 38 | future 39 | interpolation>=2.2.3 40 | numba 41 | h5py 42 | pandas 43 | tables 44 | colorama 45 | omegaconf 46 | rich 47 | joblib 48 | 49 | tests_require = 50 | pytest 51 | pytest-codecov 52 | 53 | 54 | [options.extras_require] 55 | tests = 56 | pytest 57 | docs = 58 | sphinx>= 1.4 59 | sphinx_rtd_theme 60 | nbsphinx==0.8.8 61 | sphinx-autoapi 62 | 63 | # [options.packages.find] 64 | # where = src 65 | # exclude = 66 | # tests 67 | 68 | 69 | [versioneer] 70 | VCS=git 71 | style=pep440 72 | versionfile_source=astromodels/_version.py 73 | versionfile_build=astromodels/_version.py 74 | tag_prefix=v 75 | parentdir_prefix=astromodels- 76 | 77 | [isort] 78 | multi_line_output = 3 79 | include_trailing_comma = true 80 | force_grid_wrap = 0 81 | use_parentheses = true 82 | ensure_newline_before_comments = true 83 | line_length = 80 84 | 85 | [flake8] 86 | ignore = E127,E201,E202,E203,E231,E252,E266,E402,E999,F841,W503,W605,F403,F401,E741 87 | select = C,E,F,W,B,B950 88 | extend-ignore = E203, E501 89 | #extend-ignore = F401 # ignore unused 90 | max-line-length = 88 91 | exclude = .eggs,.git,docs,scripts,examples,setup.py,ci,build,dist,versioneer.py,conftest.py,astromodels/tests,*/*.ipynb_checkpoints 92 | 93 | per-file-ignores = 94 | */*__init__.py:F401 95 | astromodels/xspec/factory.py:W293 96 | -------------------------------------------------------------------------------- /test_inside_docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This ensure that the script will exit if any command fails 4 | set -e 5 | 6 | # I am running as root, first let's create a new user and become that 7 | # The user_id env. variable must be specified on the docker command line using -e user_id=`id -u` 8 | adduser --system --home /home/user --shell /bin/bash --uid $user_id user --disabled-password 9 | exec sudo -i -u user /bin/bash << EOF 10 | 11 | ################################################################################### 12 | # Beginning of script run as user "user" 13 | ################################################################################### 14 | 15 | set -e 16 | cd /home/user 17 | 18 | # Setup environment 19 | echo "##########################################################" 20 | echo " Setting up HEASOFT environment" 21 | echo "##########################################################" 22 | 23 | export HEADAS=/heasoft/build/x86_64-unknown-linux-gnu-libc2.23-0 24 | source /heasoft/build/x86_64-unknown-linux-gnu-libc2.23-0/headas-init.sh 25 | 26 | # Print XSPEC version 27 | echo exit | xspec 28 | 29 | echo "##########################################################" 30 | echo " Creating python virtual environment" 31 | echo "##########################################################" 32 | virtualenv astromodels_env 33 | source astromodels_env/bin/activate 34 | 35 | echo "##########################################################" 36 | echo " Installing numpy, pytest, pytest-cov and coveralls" 37 | echo "##########################################################" 38 | 39 | pip install numpy pytest pytest-cov coveralls codecov 40 | 41 | echo "##########################################################" 42 | echo " Installing astromodels" 43 | echo "##########################################################" 44 | 45 | # This assumes that the $TRAVIS_BUILD_DIR directory has been mounted to 46 | # /astromodels using -v $TRAVIS_BUILD_DIR:/travis_build_dir 47 | cd /travis_build_dir 48 | pip install . 49 | 50 | echo "##########################################################" 51 | echo " Executing tests and coveralls" 52 | echo "##########################################################" 53 | 54 | # Execute tests 55 | # (need to move away from root directory, otherwise xspec import will fail) 56 | cd astromodels 57 | python -m pytest -vv --cov=astromodels 58 | 59 | echo "##########################################################" 60 | echo " Executing codecov" 61 | echo "##########################################################" 62 | 63 | # Execute the coverage analysis 64 | codecov -t 493c9a2d-42fc-40d6-8e65-24e681efaa1e 65 | 66 | ################################################################################### 67 | # end of script run as user "user" 68 | ################################################################################### 69 | 70 | 71 | EOF --------------------------------------------------------------------------------