├── .circleci ├── config.yml └── early_exit.sh ├── .codecov.yaml ├── .codespellrc ├── .coveragerc ├── .cruft.json ├── .flake8 ├── .github └── workflows │ ├── ci.yml │ ├── label_sync.yml │ └── sub_package_update.yml ├── .gitignore ├── .isort.cfg ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── .rtd-environment.yml ├── .ruff.toml ├── CHANGELOG.rst ├── LICENSE.rst ├── MANIFEST.in ├── README.rst ├── changelog └── README.rst ├── docs ├── Makefile ├── conf.py ├── index.rst ├── make.bat ├── reference │ └── index.rst └── whatsnew │ ├── changelog.rst │ └── index.rst ├── licenses ├── LICENSE.rst ├── README.rst ├── TEMPLATE_LICENCE.rst └── TEMPLATE_LICENSE.rst ├── pyproject.toml ├── pytest.ini ├── setup.py ├── sunkit_dem ├── __init__.py ├── _dev │ ├── __init__.py │ └── scm_version.py ├── base_model.py ├── data │ └── README.rst ├── model_factory.py ├── models │ └── __init__.py ├── reference │ └── index.rst ├── tests │ ├── __init__.py │ └── test_util.py ├── util.py └── version.py └── tox.ini /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | no-backports: &no-backports 2 | name: Skip any branches called backport* 3 | command: | 4 | if [[ "${CIRCLE_BRANCH}" = *"backport"* ]]; then 5 | circleci step halt 6 | fi 7 | 8 | skip-check: &skip-check 9 | name: Check for [ci skip] 10 | command: bash .circleci/early_exit.sh 11 | 12 | merge-check: &merge-check 13 | name: Check if we need to merge upstream master 14 | command: | 15 | if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then 16 | git fetch origin --tags 17 | git fetch origin +refs/pull/$CIRCLE_PR_NUMBER/merge:pr/$CIRCLE_PR_NUMBER/merge 18 | git checkout -qf pr/$CIRCLE_PR_NUMBER/merge 19 | fi 20 | 21 | apt-run: &apt-install 22 | name: Install apt packages 23 | command: | 24 | apt update 25 | apt install -y graphviz build-essential 26 | 27 | version: 2 28 | jobs: 29 | twine-check: 30 | docker: 31 | - image: continuumio/miniconda3 32 | steps: 33 | - checkout 34 | - run: *no-backports 35 | - run: *skip-check 36 | - run: *merge-check 37 | - run: pip install -U pep517 38 | - run: python -m pep517.build --source . 39 | - run: python -m pip install -U --user --force-reinstall twine 40 | - run: python -m twine check dist/* 41 | 42 | html-docs: 43 | docker: 44 | - image: continuumio/miniconda3 45 | steps: 46 | - checkout 47 | - run: *no-backports 48 | - run: *skip-check 49 | - run: *merge-check 50 | - run: *apt-install 51 | - run: pip install -U tox 52 | - run: tox -e build_docs 53 | - run: 54 | name: Prepare for upload 55 | command: | 56 | # If it's not a PR, don't upload 57 | if [ -z "${CIRCLE_PULL_REQUEST}" ]; then 58 | rm -r docs/_build/html/* 59 | else 60 | # If it is a PR, delete sources, because it's a lot of files 61 | # which we don't really need to upload 62 | rm -r docs/_build/html/_sources 63 | fi 64 | - store_artifacts: 65 | path: docs/_build/html 66 | 67 | workflows: 68 | version: 2 69 | 70 | twine-check: 71 | jobs: 72 | - twine-check 73 | 74 | test-documentation: 75 | jobs: 76 | - html-docs 77 | 78 | notify: 79 | webhooks: 80 | - url: https://giles.cadair.dev/circleci 81 | -------------------------------------------------------------------------------- /.circleci/early_exit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | commitmessage=$(git log --pretty=%B -n 1) 4 | if [[ $commitmessage = *"[ci skip]"* ]] || [[ $commitmessage = *"[skip ci]"* ]]; then 5 | echo "Skipping build because [ci skip] found in commit message" 6 | circleci step halt 7 | fi 8 | -------------------------------------------------------------------------------- /.codecov.yaml: -------------------------------------------------------------------------------- 1 | comment: off 2 | coverage: 3 | status: 4 | project: 5 | default: 6 | threshold: 0.2% 7 | 8 | codecov: 9 | require_ci_to_pass: false 10 | notify: 11 | wait_for_ci: true 12 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | skip = *.asdf,*.fits,*.fts,*.header,*.json,*.xsh,*cache*,*egg*,*extern*,.git,.idea,.tox,_build,*truncated,*.svg,.asv_env,.history 3 | ignore-words-list = 4 | alog, 5 | nd, 6 | nin, 7 | observ, 8 | ot, 9 | te, 10 | upto, 11 | afile, 12 | precessed, 13 | precess 14 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | sunkit_dem/conftest.py 4 | sunkit_dem/*setup_package* 5 | sunkit_dem/extern/* 6 | sunkit_dem/version* 7 | */sunkit_dem/conftest.py 8 | */sunkit_dem/*setup_package* 9 | */sunkit_dem/extern/* 10 | */sunkit_dem/version* 11 | 12 | [report] 13 | exclude_lines = 14 | # Have to re-enable the standard pragma 15 | pragma: no cover 16 | # Don't complain about packages we have installed 17 | except ImportError 18 | # Don't complain if tests don't hit assertions 19 | raise AssertionError 20 | raise NotImplementedError 21 | # Don't complain about script hooks 22 | def main(.*): 23 | # Ignore branches that don't pertain to this version of Python 24 | pragma: py{ignore_python_version} 25 | # Don't complain about IPython completion helper 26 | def _ipython_key_completions_ 27 | # typing.TYPE_CHECKING is False at runtime 28 | if TYPE_CHECKING: 29 | # Ignore typing overloads 30 | @overload 31 | -------------------------------------------------------------------------------- /.cruft.json: -------------------------------------------------------------------------------- 1 | { 2 | "template": "git@github.com:sunpy/package-template.git", 3 | "commit": "15fdf534198e4f67f0a667af7d2367e93de181c5", 4 | "checkout": null, 5 | "context": { 6 | "cookiecutter": { 7 | "package_name": "sunkit-dem", 8 | "module_name": "sunkit_dem", 9 | "short_description": "A package for computing differential emission measures using multiple methods with a common API", 10 | "author_name": "SunPy Developers", 11 | "author_email": "sunpy@googlegroups.com", 12 | "project_url": "https://sunpy.org", 13 | "github_repo": "sunpy/sunkit-dem", 14 | "sourcecode_url": "https://github.com/sunpy/sunkit-dem", 15 | "download_url": "https://pypi.org/project/sunkit-dem", 16 | "documentation_url": "https://docs.sunpy.org/projects/sunkit-dem", 17 | "changelog_url": "https://docs.sunpy.org/projects/sunkit-dem/en/stable/whatsnew/changelog.html", 18 | "issue_tracker_url": "https://github.com/sunpy/sunkit-dem/issues", 19 | "license": "BSD 3-Clause", 20 | "minimum_python_version": "3.10", 21 | "use_compiled_extensions": "n", 22 | "enable_dynamic_dev_versions": "y", 23 | "include_example_code": "n", 24 | "include_cruft_update_github_workflow": "y", 25 | "use_extended_ruff_linting": "n", 26 | "_sphinx_theme": "sunpy", 27 | "_parent_project": "", 28 | "_install_requires": "", 29 | "_copy_without_render": [ 30 | "docs/_templates", 31 | "docs/_static", 32 | ".github/workflows/sub_package_update.yml" 33 | ], 34 | "_template": "git@github.com:sunpy/package-template.git", 35 | "_commit": "15fdf534198e4f67f0a667af7d2367e93de181c5" 36 | } 37 | }, 38 | "directory": null 39 | } 40 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | # missing-whitespace-around-operator 4 | E225 5 | # missing-whitespace-around-arithmetic-operator 6 | E226 7 | # line-too-long 8 | E501 9 | # unused-import 10 | F401 11 | # undefined-local-with-import-star 12 | F403 13 | # redefined-while-unused 14 | F811 15 | # Line break occurred before a binary operator 16 | W503, 17 | # Line break occurred after a binary operator 18 | W504 19 | max-line-length = 110 20 | exclude = 21 | .git 22 | __pycache__ 23 | docs/conf.py 24 | build 25 | sunkit-dem/__init__.py 26 | rst-directives = 27 | plot 28 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Main CI Workflow 2 | name: CI 3 | 4 | on: 5 | push: 6 | branches: 7 | - 'main' 8 | - '*.*' 9 | - '!*backport*' 10 | tags: 11 | - 'v*' 12 | - '!*dev*' 13 | - '!*pre*' 14 | - '!*post*' 15 | pull_request: 16 | # Allow manual runs through the web UI 17 | workflow_dispatch: 18 | schedule: 19 | # ┌───────── minute (0 - 59) 20 | # │ ┌───────── hour (0 - 23) 21 | # │ │ ┌───────── day of the month (1 - 31) 22 | # │ │ │ ┌───────── month (1 - 12 or JAN-DEC) 23 | # │ │ │ │ ┌───────── day of the week (0 - 6 or SUN-SAT) 24 | - cron: '0 7 * * 3' # Every Wed at 07:00 UTC 25 | 26 | concurrency: 27 | group: ${{ github.workflow }}-${{ github.ref }} 28 | cancel-in-progress: true 29 | 30 | jobs: 31 | core: 32 | uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2 33 | with: 34 | submodules: false 35 | coverage: codecov 36 | toxdeps: tox-pypi-filter 37 | envs: | 38 | - linux: py313 39 | secrets: 40 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 41 | 42 | sdist_verify: 43 | runs-on: ubuntu-latest 44 | steps: 45 | - uses: actions/checkout@v4 46 | - uses: actions/setup-python@v5 47 | with: 48 | python-version: '3.12' 49 | - run: python -m pip install -U --user build 50 | - run: python -m build . --sdist 51 | - run: python -m pip install -U --user twine 52 | - run: python -m twine check dist/* 53 | 54 | test: 55 | needs: [core, sdist_verify] 56 | uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2 57 | with: 58 | submodules: false 59 | coverage: codecov 60 | toxdeps: tox-pypi-filter 61 | posargs: -n auto 62 | envs: | 63 | - windows: py311 64 | - macos: py312 65 | - linux: py310-oldestdeps 66 | - linux: py313-devdeps 67 | secrets: 68 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 69 | 70 | docs: 71 | needs: [core] 72 | uses: OpenAstronomy/github-actions-workflows/.github/workflows/tox.yml@v2 73 | with: 74 | default_python: '3.12' 75 | submodules: false 76 | pytest: false 77 | toxdeps: tox-pypi-filter 78 | libraries: | 79 | apt: 80 | - graphviz 81 | envs: | 82 | - linux: build_docs 83 | 84 | publish: 85 | # Build wheels on PRs only when labelled. Releases will only be published if tagged ^v.* 86 | # see https://github-actions-workflows.openastronomy.org/en/latest/publish.html#upload-to-pypi 87 | if: | 88 | github.event_name != 'pull_request' || 89 | ( 90 | github.event_name == 'pull_request' && 91 | contains(github.event.pull_request.labels.*.name, 'Run publish') 92 | ) 93 | needs: [test, docs] 94 | uses: OpenAstronomy/github-actions-workflows/.github/workflows/publish_pure_python.yml@v2 95 | with: 96 | python-version: '3.12' 97 | test_extras: 'tests' 98 | test_command: 'pytest -p no:warnings --doctest-rst --pyargs sunkit_dem' 99 | submodules: false 100 | secrets: 101 | pypi_token: ${{ secrets.pypi_token }} 102 | -------------------------------------------------------------------------------- /.github/workflows/label_sync.yml: -------------------------------------------------------------------------------- 1 | name: Label Sync 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | # ┌───────── minute (0 - 59) 6 | # │ ┌───────── hour (0 - 23) 7 | # │ │ ┌───────── day of the month (1 - 31) 8 | # │ │ │ ┌───────── month (1 - 12 or JAN-DEC) 9 | # │ │ │ │ ┌───────── day of the week (0 - 6 or SUN-SAT) 10 | - cron: '0 0 * * *' # run every day at midnight UTC 11 | 12 | # Give permissions to write issue labels 13 | permissions: 14 | issues: write 15 | 16 | jobs: 17 | label_sync: 18 | runs-on: ubuntu-latest 19 | name: Label Sync 20 | steps: 21 | - uses: srealmoreno/label-sync-action@850ba5cef2b25e56c6c420c4feed0319294682fd 22 | with: 23 | config-file: https://raw.githubusercontent.com/sunpy/.github/main/labels.yml 24 | -------------------------------------------------------------------------------- /.github/workflows/sub_package_update.yml: -------------------------------------------------------------------------------- 1 | # This template is taken from the cruft example code, for further information please see: 2 | # https://cruft.github.io/cruft/#automating-updates-with-github-actions 3 | name: Automatic Update from package template 4 | permissions: 5 | contents: write 6 | pull-requests: write 7 | 8 | on: 9 | # Allow manual runs through the web UI 10 | workflow_dispatch: 11 | schedule: 12 | # ┌───────── minute (0 - 59) 13 | # │ ┌───────── hour (0 - 23) 14 | # │ │ ┌───────── day of the month (1 - 31) 15 | # │ │ │ ┌───────── month (1 - 12 or JAN-DEC) 16 | # │ │ │ │ ┌───────── day of the week (0 - 6 or SUN-SAT) 17 | - cron: '0 7 * * 1' # Every Monday at 7am UTC 18 | 19 | jobs: 20 | update: 21 | runs-on: ubuntu-latest 22 | strategy: 23 | fail-fast: true 24 | steps: 25 | - uses: actions/checkout@v4 26 | 27 | - uses: actions/setup-python@v5 28 | with: 29 | python-version: "3.11" 30 | 31 | - name: Install Cruft 32 | run: python -m pip install git+https://github.com/Cadair/cruft@patch-p1 33 | 34 | - name: Check if update is available 35 | continue-on-error: false 36 | id: check 37 | run: | 38 | CHANGES=0 39 | if [ -f .cruft.json ]; then 40 | if ! cruft check; then 41 | CHANGES=1 42 | fi 43 | else 44 | echo "No .cruft.json file" 45 | fi 46 | 47 | echo "has_changes=$CHANGES" >> "$GITHUB_OUTPUT" 48 | 49 | - name: Run update if available 50 | id: cruft_update 51 | if: steps.check.outputs.has_changes == '1' 52 | run: | 53 | git config --global user.email "${{ github.actor }}@users.noreply.github.com" 54 | git config --global user.name "${{ github.actor }}" 55 | 56 | cruft_output=$(cruft update --skip-apply-ask --refresh-private-variables) 57 | echo $cruft_output 58 | git restore --staged . 59 | 60 | if [[ "$cruft_output" == *"Failed to cleanly apply the update, there may be merge conflicts."* ]]; then 61 | echo merge_conflicts=1 >> $GITHUB_OUTPUT 62 | else 63 | echo merge_conflicts=0 >> $GITHUB_OUTPUT 64 | fi 65 | 66 | - name: Check if only .cruft.json is modified 67 | id: cruft_json 68 | if: steps.check.outputs.has_changes == '1' 69 | run: | 70 | git status --porcelain=1 71 | if [[ "$(git status --porcelain=1)" == " M .cruft.json" ]]; then 72 | echo "Only .cruft.json is modified. Exiting workflow early." 73 | echo "has_changes=0" >> "$GITHUB_OUTPUT" 74 | else 75 | echo "has_changes=1" >> "$GITHUB_OUTPUT" 76 | fi 77 | 78 | - name: Create pull request 79 | if: steps.cruft_json.outputs.has_changes == '1' 80 | uses: peter-evans/create-pull-request@v7 81 | with: 82 | token: ${{ secrets.GITHUB_TOKEN }} 83 | add-paths: "." 84 | commit-message: "Automatic package template update" 85 | branch: "cruft/update" 86 | delete-branch: true 87 | draft: ${{ steps.cruft_update.outputs.merge_conflicts == '1' }} 88 | title: "Updates from the package template" 89 | labels: | 90 | No Changelog Entry Needed 91 | body: | 92 | This is an autogenerated PR, which will applies the latest changes from the [SunPy Package Template](https://github.com/sunpy/package-template). 93 | If this pull request has been opened as a draft there are conflicts which need fixing. 94 | 95 | **To run the CI on this pull request you will need to close it and reopen it.** 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python: https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | tmp/ 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | pip-wheel-metadata/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | sunkit_dem/_version.py 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | # automodapi 78 | docs/api 79 | docs/sg_execution_times.rst 80 | 81 | # PyBuilder 82 | .pybuilder/ 83 | target/ 84 | 85 | # Jupyter Notebook 86 | .ipynb_checkpoints 87 | 88 | # IPython 89 | profile_default/ 90 | ipython_config.py 91 | 92 | # pyenv 93 | # For a library or package, you might want to ignore these files since the code is 94 | # intended to run in multiple environments; otherwise, check them in: 95 | # .python-version 96 | 97 | # pipenv 98 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 99 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 100 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 101 | # install all needed dependencies. 102 | #Pipfile.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/#use-with-ide 110 | .pdm.toml 111 | 112 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 113 | __pypackages__/ 114 | 115 | # Celery stuff 116 | celerybeat-schedule 117 | celerybeat.pid 118 | 119 | # SageMath parsed files 120 | *.sage.py 121 | 122 | # Environments 123 | .env 124 | .venv 125 | env/ 126 | venv/ 127 | ENV/ 128 | env.bak/ 129 | venv.bak/ 130 | 131 | # Rope project settings 132 | .ropeproject 133 | 134 | # mkdocs documentation 135 | /site 136 | 137 | # mypy 138 | .mypy_cache/ 139 | 140 | # Pyre type checker 141 | .pyre/ 142 | 143 | # IDE 144 | # PyCharm 145 | .idea 146 | 147 | # Spyder project settings 148 | .spyderproject 149 | .spyproject 150 | 151 | ### VScode: https://raw.githubusercontent.com/github/gitignore/master/Global/VisualStudioCode.gitignore 152 | .vscode/* 153 | .vs/* 154 | 155 | ### https://raw.github.com/github/gitignore/master/Global/OSX.gitignore 156 | .DS_Store 157 | .AppleDouble 158 | .LSOverride 159 | 160 | # Icon must ends with two \r. 161 | Icon 162 | 163 | # Thumbnails 164 | ._* 165 | 166 | # Files that might appear on external disk 167 | .Spotlight-V100 168 | .Trashes 169 | 170 | ### Linux: https://raw.githubusercontent.com/github/gitignore/master/Global/Linux.gitignore 171 | *~ 172 | 173 | # temporary files which can be created if a process still has a handle open of a deleted file 174 | .fuse_hidden* 175 | 176 | # KDE directory preferences 177 | .directory 178 | 179 | # Linux trash folder which might appear on any partition or disk 180 | .Trash-* 181 | 182 | # .nfs files are created when an open file is removed but is still being accessed 183 | .nfs* 184 | 185 | # pytype static type analyzer 186 | .pytype/ 187 | 188 | # General 189 | .DS_Store 190 | .AppleDouble 191 | .LSOverride 192 | 193 | # Icon must end with two \r 194 | Icon 195 | 196 | 197 | # Thumbnails 198 | ._* 199 | 200 | # Files that might appear in the root of a volume 201 | .DocumentRevisions-V100 202 | .fseventsd 203 | .Spotlight-V100 204 | .TemporaryItems 205 | .Trashes 206 | .VolumeIcon.icns 207 | .com.apple.timemachine.donotpresent 208 | 209 | # Directories potentially created on remote AFP share 210 | .AppleDB 211 | .AppleDesktop 212 | Network Trash Folder 213 | Temporary Items 214 | .apdisk 215 | 216 | ### Windows: https://raw.githubusercontent.com/github/gitignore/master/Global/Windows.gitignore 217 | 218 | # Windows thumbnail cache files 219 | Thumbs.db 220 | ehthumbs.db 221 | ehthumbs_vista.db 222 | 223 | # Dump file 224 | *.stackdump 225 | 226 | # Folder config file 227 | [Dd]esktop.ini 228 | 229 | # Recycle Bin used on file shares 230 | $RECYCLE.BIN/ 231 | 232 | # Windows Installer files 233 | *.cab 234 | *.msi 235 | *.msix 236 | *.msm 237 | *.msp 238 | 239 | # Windows shortcuts 240 | *.lnk 241 | 242 | ### Extra Python Items and SunPy Specific 243 | docs/whatsnew/latest_changelog.txt 244 | examples/**/*.csv 245 | figure_test_images* 246 | tags 247 | baseline 248 | 249 | # Release script 250 | .github_cache 251 | 252 | # Misc Stuff 253 | .history 254 | *.orig 255 | .tmp 256 | node_modules/ 257 | package-lock.json 258 | package.json 259 | .prettierrc 260 | 261 | # Log files generated by 'vagrant up' 262 | *.log 263 | -------------------------------------------------------------------------------- /.isort.cfg: -------------------------------------------------------------------------------- 1 | [settings] 2 | balanced_wrapping = true 3 | skip = 4 | docs/conf.py 5 | sunkit_dem/__init__.py 6 | default_section = THIRDPARTY 7 | include_trailing_comma = true 8 | known_astropy = astropy, asdf 9 | known_sunpy = sunpy 10 | known_first_party = sunkit_dem 11 | length_sort = false 12 | length_sort_sections = stdlib 13 | line_length = 110 14 | multi_line_output = 3 15 | no_lines_before = LOCALFOLDER 16 | sections = STDLIB, THIRDPARTY, ASTROPY, SUNPY, FIRSTPARTY, LOCALFOLDER 17 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | # This should be before any formatting hooks like isort 3 | - repo: https://github.com/astral-sh/ruff-pre-commit 4 | rev: "v0.11.12" 5 | hooks: 6 | - id: ruff 7 | args: ["--fix"] 8 | - repo: https://github.com/PyCQA/isort 9 | rev: 6.0.1 10 | hooks: 11 | - id: isort 12 | exclude: ".*(.fits|.fts|.fit|.header|.txt|tca.*|extern.*|sunkit_dem/extern)$" 13 | - repo: https://github.com/pre-commit/pre-commit-hooks 14 | rev: v5.0.0 15 | hooks: 16 | - id: check-ast 17 | - id: check-case-conflict 18 | - id: trailing-whitespace 19 | exclude: ".*(.fits|.fts|.fit|.header|.txt)$" 20 | - id: check-yaml 21 | - id: debug-statements 22 | - id: check-added-large-files 23 | args: ["--enforce-all", "--maxkb=1054"] 24 | - id: end-of-file-fixer 25 | exclude: ".*(.fits|.fts|.fit|.header|.txt|tca.*|.json)$|^CITATION.rst$" 26 | - id: mixed-line-ending 27 | exclude: ".*(.fits|.fts|.fit|.header|.txt|tca.*)$" 28 | - repo: https://github.com/codespell-project/codespell 29 | rev: v2.4.1 30 | hooks: 31 | - id: codespell 32 | args: [ "--write-changes" ] 33 | ci: 34 | autofix_prs: false 35 | autoupdate_schedule: "quarterly" 36 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-lts-latest 5 | tools: 6 | python: "mambaforge-latest" 7 | jobs: 8 | post_checkout: 9 | - git fetch --unshallow || true 10 | pre_install: 11 | - git update-index --assume-unchanged .rtd-environment.yml docs/conf.py 12 | 13 | conda: 14 | environment: .rtd-environment.yml 15 | 16 | sphinx: 17 | builder: html 18 | configuration: docs/conf.py 19 | fail_on_warning: false 20 | 21 | formats: 22 | - htmlzip 23 | 24 | python: 25 | install: 26 | - method: pip 27 | extra_requirements: 28 | - docs 29 | path: . 30 | -------------------------------------------------------------------------------- /.rtd-environment.yml: -------------------------------------------------------------------------------- 1 | name: sunkit-dem 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - python=3.12 6 | - pip 7 | - graphviz!=2.42.*,!=2.43.* 8 | -------------------------------------------------------------------------------- /.ruff.toml: -------------------------------------------------------------------------------- 1 | target-version = "py310" 2 | line-length = 120 3 | exclude = [ 4 | ".git,", 5 | "__pycache__", 6 | "build", 7 | "sunkit-dem/version.py", 8 | ] 9 | 10 | [lint] 11 | select = [ 12 | "E", 13 | "F", 14 | "W", 15 | "UP", 16 | "PT", 17 | ] 18 | extend-ignore = [ 19 | # pycodestyle (E, W) 20 | "E501", # ignore line length will use a formatter instead 21 | # pyupgrade (UP) 22 | "UP038", # Use | in isinstance - not compatible with models and is slower 23 | # pytest (PT) 24 | "PT001", # Always use pytest.fixture() 25 | "PT023", # Always use () on pytest decorators 26 | # flake8-pie (PIE) 27 | "PIE808", # Disallow passing 0 as the first argument to range 28 | # flake8-use-pathlib (PTH) 29 | "PTH123", # open() should be replaced by Path.open() 30 | # Ruff (RUF) 31 | "RUF003", # Ignore ambiguous quote marks, doesn't allow ' in comments 32 | "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` 33 | "RUF013", # PEP 484 prohibits implicit `Optional` 34 | "RUF015", # Prefer `next(iter(...))` over single element slice 35 | ] 36 | 37 | [lint.per-file-ignores] 38 | "setup.py" = [ 39 | "INP001", # File is part of an implicit namespace package. 40 | ] 41 | "conftest.py" = [ 42 | "INP001", # File is part of an implicit namespace package. 43 | ] 44 | "docs/conf.py" = [ 45 | "E402" # Module imports not at top of file 46 | ] 47 | "docs/*.py" = [ 48 | "INP001", # File is part of an implicit namespace package. 49 | ] 50 | "examples/**.py" = [ 51 | "T201", # allow use of print in examples 52 | "INP001", # File is part of an implicit namespace package. 53 | ] 54 | "__init__.py" = [ 55 | "E402", # Module level import not at top of cell 56 | "F401", # Unused import 57 | "F403", # from {name} import * used; unable to detect undefined names 58 | "F405", # {name} may be undefined, or defined from star imports 59 | ] 60 | "test_*.py" = [ 61 | "E402", # Module level import not at top of cell 62 | ] 63 | 64 | [lint.pydocstyle] 65 | convention = "numpy" 66 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunpy/sunkit-dem/30b9a0c31437e3ed6a23d999db4b1ab8f293c855/CHANGELOG.rst -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2024, SunPy Developers 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | 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 | * Redistributions in binary form must reproduce the above copyright notice, this 10 | list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | * Neither the name of the SunPy Team nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # Exclude specific files 2 | # All files which are tracked by git and not explicitly excluded here are included by setuptools_scm 3 | # Prune folders 4 | prune build 5 | prune docs/_build 6 | prune docs/api 7 | global-exclude *.pyc *.o 8 | 9 | # This subpackage is only used in development checkouts 10 | # and should not be included in built tarballs 11 | prune sunkit_dem/_dev 12 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ************** 2 | ``sunkit-Dem`` 3 | ************** 4 | 5 | A package for computing differential emission measures using multiple methods with a common API. 6 | 7 | Usage of Generative AI 8 | ---------------------- 9 | 10 | We expect authentic engagement in our community. 11 | Be wary of posting output from Large Language Models or similar generative AI as comments on GitHub or any other platform, as such comments tend to be formulaic and low quality content. 12 | If you use generative AI tools as an aid in developing code or documentation changes, ensure that you fully understand the proposed changes and can explain why they are the correct approach and an improvement to the current state. 13 | 14 | License 15 | ------- 16 | 17 | This project is Copyright (c) SunPy Developers and licensed under 18 | the terms of the BSD 3-Clause license. This package is based upon 19 | the `Openastronomy packaging guide `_ 20 | which is licensed under the BSD 3-clause licence. See the licenses folder for 21 | more information. 22 | 23 | Contributing 24 | ------------ 25 | 26 | We love contributions! sunkit-dem is open source, 27 | built on open source, and we'd love to have you hang out in our community. 28 | 29 | **Imposter syndrome disclaimer**: We want your help. No, really. 30 | 31 | There may be a little voice inside your head that is telling you that you're not 32 | ready to be an open source contributor; that your skills aren't nearly good 33 | enough to contribute. What could you possibly offer a project like this one? 34 | 35 | We assure you - the little voice in your head is wrong. If you can write code at 36 | all, you can contribute code to open source. Contributing to open source 37 | projects is a fantastic way to advance one's coding skills. Writing perfect code 38 | isn't the measure of a good developer (that would disqualify all of us!); it's 39 | trying to create something, making mistakes, and learning from those 40 | mistakes. That's how we all improve, and we are happy to help others learn. 41 | 42 | Being an open source contributor doesn't just mean writing code, either. You can 43 | help out by writing documentation, tests, or even giving feedback about the 44 | project (and yes - that includes giving feedback about the contribution 45 | process). Some of these contributions may be the most valuable to the project as 46 | a whole, because you're coming to the project with fresh eyes, so you can see 47 | the errors and assumptions that seasoned contributors have glossed over. 48 | 49 | Note: This disclaimer was originally written by 50 | `Adrienne Lowe `_ for a 51 | `PyCon talk `_, and was adapted by 52 | sunkit-dem based on its use in the README file for the 53 | `MetPy project `_. 54 | -------------------------------------------------------------------------------- /changelog/README.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Changelog 3 | ========= 4 | 5 | .. note:: 6 | 7 | This README was adapted from the pytest changelog readme under the terms of the MIT licence. 8 | 9 | This directory contains "news fragments" which are short files that contain a small **ReST**-formatted text that will be added to the next ``CHANGELOG``. 10 | 11 | The ``CHANGELOG`` will be read by users, so this description should be aimed at SunPy users instead of describing internal changes which are only relevant to the developers. 12 | 13 | Make sure to use full sentences with correct case and punctuation, for example:: 14 | 15 | Add support for Helioprojective coordinates in `sunpy.coordinates.frames`. 16 | 17 | Please try to use Sphinx intersphinx using backticks. 18 | 19 | Each file should be named like ``.[.].rst``, where ```` is a pull request number, ``COUNTER`` is an optional number if a PR needs multiple entries with the same type and ```` is one of: 20 | 21 | * ``breaking``: A change which requires users to change code and is not backwards compatible. (Not to be used for removal of deprecated features.) 22 | * ``feature``: New user facing features and any new behavior. 23 | * ``bugfix``: Fixes a reported bug. 24 | * ``doc``: Documentation addition or improvement, like rewording an entire session or adding missing docs. 25 | * ``deprecation``: Feature deprecation 26 | * ``removal``: Feature removal. 27 | * ``trivial``: A change which has no user facing effect or is tiny change. 28 | 29 | So for example: ``123.feature.rst``, ``456.bugfix.rst``. 30 | 31 | If you are unsure what pull request type to use, don't hesitate to ask in your PR. 32 | 33 | Note that the ``towncrier`` tool will automatically reflow your text, so it will work best if you stick to a single paragraph, but multiple sentences and links are OK and encouraged. 34 | You can install ``towncrier`` and then run ``towncrier --draft`` if you want to get a preview of how your change will look in the final release notes. 35 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file does only contain a selection of the most common options. For a 4 | # full list see the documentation: 5 | # http://www.sphinx-doc.org/en/master/config 6 | 7 | import datetime 8 | 9 | from packaging.version import Version 10 | 11 | # -- Project information ----------------------------------------------------- 12 | 13 | # The full version, including alpha/beta/rc tags 14 | from sunkit_dem import __version__ 15 | 16 | _version = Version(__version__) 17 | version = release = str(_version) 18 | # Avoid "post" appearing in version string in rendered docs 19 | if _version.is_postrelease: 20 | version = release = _version.base_version 21 | # Avoid long githashes in rendered Sphinx docs 22 | elif _version.is_devrelease: 23 | version = release = f"{_version.base_version}.dev{_version.dev}" 24 | is_development = _version.is_devrelease 25 | is_release = not(_version.is_prerelease or _version.is_devrelease) 26 | 27 | project = "sunkit-dem" 28 | author = "SunPy Developers" 29 | copyright = f"{datetime.datetime.now().year}, {author}" # noqa: A001 30 | 31 | # -- General configuration --------------------------------------------------- 32 | 33 | # Wrap large function/method signatures 34 | maximum_signature_line_length = 80 35 | 36 | # Add any Sphinx extension module names here, as strings. They can be 37 | # extensions coming with Sphinx (named "sphinx.ext.*") or your custom 38 | # ones. 39 | extensions = [ 40 | "sphinx.ext.autodoc", 41 | "sphinx.ext.intersphinx", 42 | "sphinx.ext.todo", 43 | "sphinx.ext.coverage", 44 | "sphinx.ext.inheritance_diagram", 45 | "sphinx.ext.viewcode", 46 | "sphinx.ext.napoleon", 47 | "sphinx.ext.doctest", 48 | "sphinx.ext.mathjax", 49 | "sphinx_automodapi.automodapi", 50 | "sphinx_automodapi.smart_resolver", 51 | "sphinx_changelog", 52 | ] 53 | 54 | # Add any paths that contain templates here, relative to this directory. 55 | # templates_path = ["_templates"] 56 | 57 | # List of patterns, relative to source directory, that match files and 58 | # directories to ignore when looking for source files. 59 | # This pattern also affects html_static_path and html_extra_path. 60 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 61 | 62 | # The suffix(es) of source filenames. 63 | # You can specify multiple suffix as a list of string: 64 | source_suffix = ".rst" 65 | 66 | # The master toctree document. 67 | master_doc = "index" 68 | 69 | # Treat everything in single ` as a Python reference. 70 | default_role = "py:obj" 71 | 72 | # -- Options for intersphinx extension --------------------------------------- 73 | 74 | # Example configuration for intersphinx: refer to the Python standard library. 75 | intersphinx_mapping = {"python": ("https://docs.python.org/", None)} 76 | 77 | # -- Options for HTML output ------------------------------------------------- 78 | 79 | # The theme to use for HTML and HTML Help pages. See the documentation for 80 | # a list of builtin themes. 81 | html_theme = "sunpy" 82 | 83 | # Render inheritance diagrams in SVG 84 | graphviz_output_format = "svg" 85 | 86 | graphviz_dot_args = [ 87 | "-Nfontsize=10", 88 | "-Nfontname=Helvetica Neue, Helvetica, Arial, sans-serif", 89 | "-Efontsize=10", 90 | "-Efontname=Helvetica Neue, Helvetica, Arial, sans-serif", 91 | "-Gfontsize=10", 92 | "-Gfontname=Helvetica Neue, Helvetica, Arial, sans-serif", 93 | ] 94 | 95 | # Add any paths that contain custom static files (such as style sheets) here, 96 | # relative to this directory. They are copied after the builtin static files, 97 | # so a file named "default.css" will overwrite the builtin "default.css". 98 | # html_static_path = ["_static"] 99 | 100 | # By default, when rendering docstrings for classes, sphinx.ext.autodoc will 101 | # make docs with the class-level docstring and the class-method docstrings, 102 | # but not the __init__ docstring, which often contains the parameters to 103 | # class constructors across the scientific Python ecosystem. The option below 104 | # will append the __init__ docstring to the class-level docstring when rendering 105 | # the docs. For more options, see: 106 | # https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autoclass_content 107 | autoclass_content = "both" 108 | 109 | # -- Other options ---------------------------------------------------------- 110 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ************************ 2 | sunkit-dem Documentation 3 | ************************ 4 | 5 | This is the documentation for sunkit-dem. 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | :caption: Contents: 10 | 11 | whatsnew/index 12 | reference/index 13 | 14 | Installation 15 | ============ 16 | 17 | If you want to help develop sunkit-image, or just want to try out the package, you will need to install it from GitHub for the time being. 18 | The best way to do this is to create a new python virtual environment (either with ``pipenv`` or ``conda``). 19 | Once you have that virtual environment: 20 | 21 | .. code:: bash 22 | 23 | $ git clone https://github.com/sunpy/sunkit-image.git 24 | $ cd sunkit-dem 25 | $ pip install -e . 26 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/reference/index.rst: -------------------------------------------------------------------------------- 1 | .. automodapi:: sunkit_dem 2 | -------------------------------------------------------------------------------- /docs/whatsnew/changelog.rst: -------------------------------------------------------------------------------- 1 | .. _changelog: 2 | 3 | ************** 4 | Full Changelog 5 | ************** 6 | 7 | .. changelog:: 8 | :towncrier: ../../ 9 | :towncrier-skip-if-empty: 10 | :changelog_file: ../../CHANGELOG.rst 11 | -------------------------------------------------------------------------------- /docs/whatsnew/index.rst: -------------------------------------------------------------------------------- 1 | *************** 2 | Release History 3 | *************** 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | changelog 9 | -------------------------------------------------------------------------------- /licenses/LICENSE.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024, SunPy Developers 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | 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 | * Redistributions in binary form must reproduce the above copyright notice, this 10 | list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | * Neither the name of the sunkit-dem team nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /licenses/README.rst: -------------------------------------------------------------------------------- 1 | Licenses 2 | ======== 3 | 4 | This directory holds license and credit information for the package, 5 | works the package is derived from, and/or datasets. 6 | 7 | Ensure that you pick a package licence which is in this folder and it matches 8 | the one mentioned in the top level README.rst file. If you are using the 9 | pre-rendered version of this template check for the word 'Other' in the README. 10 | -------------------------------------------------------------------------------- /licenses/TEMPLATE_LICENCE.rst: -------------------------------------------------------------------------------- 1 | This project is based upon the Astropy package template 2 | (https://github.com/astropy/package-template/) which is licenced under the terms 3 | of the following licence. 4 | 5 | --- 6 | 7 | Copyright (c) 2018, Astropy Developers 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without modification, 11 | are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, this 16 | list of conditions and the following disclaimer in the documentation and/or 17 | other materials provided with the distribution. 18 | * Neither the name of the Astropy Team nor the names of its contributors may be 19 | used to endorse or promote products derived from this software without 20 | specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 26 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 29 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /licenses/TEMPLATE_LICENSE.rst: -------------------------------------------------------------------------------- 1 | This project is based upon the OpenAstronomy package template 2 | (https://github.com/OpenAstronomy/package-template/) which is licensed under the terms 3 | of the following licence. 4 | 5 | --- 6 | 7 | Copyright (c) 2018, OpenAstronomy Developers 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without modification, 11 | are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, this 16 | list of conditions and the following disclaimer in the documentation and/or 17 | other materials provided with the distribution. 18 | * Neither the name of the Astropy Team nor the names of its contributors may be 19 | used to endorse or promote products derived from this software without 20 | specific prior written permission. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 23 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 26 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 29 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "setuptools>=62.1", 4 | "setuptools_scm[toml]>=8.0.0", 5 | "wheel", 6 | ] 7 | build-backend = "setuptools.build_meta" 8 | 9 | [project] 10 | name = "sunkit_dem" 11 | description = "A package for computing differential emission measures using multiple methods with a common API" 12 | requires-python = ">=3.10" 13 | readme = { file = "README.rst", content-type = "text/x-rst" } 14 | license = { file = "licenses/LICENSE.rst" } 15 | authors = [ 16 | { name = "SunPy Developers", email = "sunpy@googlegroups.com" }, 17 | ] 18 | dependencies = [ 19 | "ndcube>=2.3.0", 20 | "sunpy>=6.0.0", 21 | ] 22 | dynamic = ["version"] 23 | 24 | [project.scripts] 25 | open_astronomy_package_template_example = "sunkit_dem.example_mod:main" 26 | 27 | [project.optional-dependencies] 28 | tests = [ 29 | "pytest", 30 | "pytest-astropy", 31 | "pytest-xdist", 32 | ] 33 | docs = [ 34 | "sphinx", 35 | "sphinx-automodapi", 36 | "sphinx-changelog", 37 | "sunpy-sphinx-theme", 38 | "packaging", 39 | "sphinx_changelog", 40 | ] 41 | 42 | [project.urls] 43 | Homepage = "https://sunpy.org" 44 | "Source Code" = "https://github.com/sunpy/sunkit-dem" 45 | Download = "https://pypi.org/project/sunkit-dem" 46 | Documentation = "https://docs.sunpy.org/projects/sunkit-dem" 47 | Changelog = "https://docs.sunpy.org/projects/sunkit-dem/en/stable/whatsnew/changelog.html" 48 | "Issue Tracker" = "https://github.com/sunpy/sunkit-dem/issues" 49 | 50 | [tool.setuptools] 51 | zip-safe = false 52 | include-package-data = true 53 | 54 | [tool.setuptools.packages.find] 55 | include = ["sunkit_dem*"] 56 | exclude = ["sunkit_dem._dev*"] 57 | 58 | [tool.setuptools_scm] 59 | version_file = "sunkit_dem/_version.py" 60 | 61 | [tool.gilesbot] 62 | [tool.gilesbot.pull_requests] 63 | enabled = true 64 | 65 | [tool.gilesbot.towncrier_changelog] 66 | enabled = true 67 | verify_pr_number = true 68 | changelog_skip_label = "No Changelog Entry Needed" 69 | help_url = "https://github.com/sunpy/sunkit-dem/blob/main/changelog/README.rst" 70 | 71 | changelog_missing_long = "There isn't a changelog file in this pull request. Please add a changelog file to the `changelog/` directory following the instructions in the changelog [README](https://github.com/sunpy/sunkit-dem/blob/main/changelog/README.rst)." 72 | 73 | type_incorrect_long = "The changelog file you added is not one of the allowed types. Please use one of the types described in the changelog [README](https://github.com/sunpy/sunkit-dem/blob/main/changelog/README.rst)" 74 | 75 | number_incorrect_long = "The number in the changelog file you added does not match the number of this pull request. Please rename the file." 76 | 77 | # TODO: This should be in towncrier.toml but Giles currently only works looks in 78 | # pyproject.toml we should move this back when it's fixed. 79 | [tool.towncrier] 80 | package = "sunkit_dem" 81 | filename = "CHANGELOG.rst" 82 | directory = "changelog/" 83 | issue_format = "`#{issue} `__" 84 | title_format = "{version} ({project_date})" 85 | 86 | [[tool.towncrier.type]] 87 | directory = "breaking" 88 | name = "Breaking Changes" 89 | showcontent = true 90 | 91 | [[tool.towncrier.type]] 92 | directory = "deprecation" 93 | name = "Deprecations" 94 | showcontent = true 95 | 96 | [[tool.towncrier.type]] 97 | directory = "removal" 98 | name = "Removals" 99 | showcontent = true 100 | 101 | [[tool.towncrier.type]] 102 | directory = "feature" 103 | name = "New Features" 104 | showcontent = true 105 | 106 | [[tool.towncrier.type]] 107 | directory = "bugfix" 108 | name = "Bug Fixes" 109 | showcontent = true 110 | 111 | [[tool.towncrier.type]] 112 | directory = "doc" 113 | name = "Documentation" 114 | showcontent = true 115 | 116 | [[tool.towncrier.type]] 117 | directory = "trivial" 118 | name = "Internal Changes" 119 | showcontent = true 120 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | minversion = 7.0 3 | testpaths = 4 | sunkit_dem 5 | docs 6 | norecursedirs = 7 | .tox 8 | build 9 | docs/_build 10 | docs/generated 11 | *.egg-info 12 | examples 13 | sunkit_dem/_dev 14 | .history 15 | sunkit_dem/extern 16 | doctest_plus = enabled 17 | doctest_optionflags = 18 | NORMALIZE_WHITESPACE 19 | FLOAT_CMP 20 | ELLIPSIS 21 | text_file_format = rst 22 | addopts = 23 | --doctest-rst 24 | -p no:unraisableexception 25 | -p no:threadexception 26 | filterwarnings = 27 | # Turn all warnings into errors so they do not pass silently. 28 | error 29 | # Do not fail on pytest config issues (i.e. missing plugins) but do show them 30 | always::pytest.PytestConfigWarning 31 | # A list of warnings to ignore follows. If you add to this list, you MUST 32 | # add a comment or ideally a link to an issue that explains why the warning 33 | # is being ignored 34 | ignore:Please use astropy.wcs.wcsapi.high_level_api.values_to_high_level_objects:DeprecationWarning 35 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | setup() 5 | -------------------------------------------------------------------------------- /sunkit_dem/__init__.py: -------------------------------------------------------------------------------- 1 | from .version import version as __version__ 2 | from .base_model import * 3 | from .model_factory import * 4 | -------------------------------------------------------------------------------- /sunkit_dem/_dev/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This package contains utilities that are only used when developing in a 3 | copy of the source repository. 4 | These files are not installed, and should not be assumed to exist at 5 | runtime. 6 | """ 7 | -------------------------------------------------------------------------------- /sunkit_dem/_dev/scm_version.py: -------------------------------------------------------------------------------- 1 | # Try to use setuptools_scm to get the current version; this is only used 2 | # in development installations from the git repository. 3 | from pathlib import Path 4 | 5 | try: 6 | from setuptools_scm import get_version 7 | 8 | version = get_version(root=Path('../..'), relative_to=__file__) 9 | except ImportError: 10 | raise 11 | except Exception as e: 12 | raise ValueError('setuptools_scm can not determine version.') from e 13 | -------------------------------------------------------------------------------- /sunkit_dem/base_model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Base model class for DEM models 3 | """ 4 | from abc import ABC, abstractmethod 5 | 6 | import ndcube 7 | import numpy as np 8 | from ndcube.extra_coords.table_coord import MultipleTableCoordinate, QuantityTableCoordinate 9 | from ndcube.wcs.wrappers import CompoundLowLevelWCS 10 | 11 | import astropy.units as u 12 | from astropy.nddata import StdDevUncertainty 13 | 14 | __all__ = ["GenericModel"] 15 | 16 | 17 | class BaseModel(ABC): 18 | 19 | @abstractmethod 20 | def _model(self): 21 | raise NotImplementedError 22 | 23 | def defines_model_for(self): 24 | raise NotImplementedError 25 | 26 | 27 | class GenericModel(BaseModel): 28 | """ 29 | Base class for implementing a differential emission measure model 30 | 31 | Parameters 32 | ---------- 33 | data : `~ndcube.NDCollection` 34 | kernel : `dict` 35 | `~astropy.units.Quantity` objects containing the kernels 36 | of each response. The keys should correspond to those in ``data``. 37 | temperature_bin_edges : `~astropy.units.Quantity` 38 | Edges of the temperature bins in which the DEM is computed. The 39 | rightmost edge is included. The kernel is evaluated at the bin centers. 40 | The bin widths must be equal in log10. 41 | """ 42 | 43 | _registry = dict() 44 | 45 | def __init_subclass__(cls, **kwargs): 46 | """ 47 | An __init_subclass__ hook initializes all of the subclasses of a given 48 | class. So for each subclass, it will call this block of code on import. 49 | This replicates some metaclass magic without the need to be aware of 50 | metaclasses. Here we use this to register each subclass in a dict that 51 | has the `defines_model_for` attribute. This is then passed into the Map 52 | Factory so we can register them. 53 | """ 54 | super().__init_subclass__(**kwargs) 55 | if hasattr(cls, 'defines_model_for'): 56 | cls._registry[cls] = cls.defines_model_for 57 | 58 | @u.quantity_input 59 | def __init__(self, data, kernel, temperature_bin_edges: u.K, kernel_temperatures=None, **kwargs): 60 | self.temperature_bin_edges = temperature_bin_edges 61 | self.data = data 62 | self.kernel_temperatures = kernel_temperatures 63 | if self.kernel_temperatures is None: 64 | self.kernel_temperatures = self.temperature_bin_centers 65 | self.kernel = kernel 66 | 67 | @property 68 | def _keys(self): 69 | # Internal reference for entries in kernel and data 70 | # This ensures consistent ordering in kernel and data matrices 71 | return sorted(list(self.kernel.keys())) 72 | 73 | @property 74 | @u.quantity_input 75 | def temperature_bin_centers(self) -> u.K: 76 | return (self.temperature_bin_edges[1:] + self.temperature_bin_edges[:-1])/2 77 | 78 | @property 79 | @u.quantity_input 80 | def temperature_bin_widths(self) -> u.K: 81 | return np.diff(self.temperature_bin_edges) 82 | 83 | @property 84 | def data(self) -> ndcube.NDCollection: 85 | return self._data 86 | 87 | @data.setter 88 | def data(self, data): 89 | """ 90 | Check that input data is correctly formatted as an 91 | `ndcube.NDCollection` 92 | """ 93 | if not isinstance(data, ndcube.NDCollection): 94 | raise ValueError('Input data must be an NDCollection') 95 | if not all([hasattr(data[k], 'unit') for k in data]): 96 | raise u.UnitsError('Each NDCube in NDCollection must have units') 97 | self._data = data 98 | 99 | @property 100 | def combined_mask(self): 101 | """ 102 | Combined mask of all members of ``data``. Will be True if any member is masked. 103 | This is propagated to the final DEM result 104 | """ 105 | combined_mask = [] 106 | for k in self._keys: 107 | if self.data[k].mask is not None: 108 | combined_mask.append(self.data[k].mask) 109 | else: 110 | combined_mask.append(np.full(self.data[k].data.shape, False)) 111 | return np.any(combined_mask, axis=0) 112 | 113 | @property 114 | def kernel(self): 115 | return self._kernel 116 | 117 | @kernel.setter 118 | def kernel(self, kernel): 119 | if len(kernel) != len(self.data): 120 | raise ValueError('Number of kernels must be equal to length of wavelength dimension.') 121 | if not all([v.shape == self.kernel_temperatures.shape for _, v in kernel.items()]): 122 | raise ValueError('Temperature bin centers and kernels must have the same shape.') 123 | self._kernel = kernel 124 | 125 | @property 126 | def data_matrix(self): 127 | return np.stack([self.data[k].data for k in self._keys]) 128 | 129 | @property 130 | def uncertainty_matrix(self): 131 | uncertainties = [self.data[k].uncertainty for k in self._keys] 132 | if any([_u is None for _u in uncertainties]): 133 | return None 134 | return np.stack([_u.array for _u in uncertainties]) 135 | 136 | @property 137 | def kernel_matrix(self): 138 | return np.stack([self.kernel[k].value for k in self._keys]) 139 | 140 | def fit(self, *args, **kwargs): 141 | r""" 142 | Apply inversion procedure to data. 143 | 144 | Returns 145 | ------- 146 | dem : `~ndcube.NDCube` 147 | Differential emission measure as a function of temperature. The 148 | temperature axis is evenly spaced in :math:`\log{T}`. The number 149 | of dimensions depend on the input data. 150 | """ 151 | dem_dict = self._model(*args, **kwargs) 152 | wcs = self._make_dem_wcs() 153 | meta = self._make_dem_meta() 154 | dem_data = dem_dict.pop('dem') 155 | mask = np.full(dem_data.shape, False) 156 | mask[:,...] = self.combined_mask 157 | uncertainty = dem_dict.pop('uncertainty', None) 158 | if uncertainty is not None: 159 | uncertainty = StdDevUncertainty(uncertainty) 160 | dem = ndcube.NDCube(dem_data, 161 | wcs, 162 | meta=meta, 163 | mask=mask, 164 | uncertainty=uncertainty) 165 | cubes = [('dem', dem),] 166 | for k in dem_dict: 167 | cubes += [(k, ndcube.NDCube(dem_dict[k], wcs, meta=meta))] 168 | return ndcube.NDCollection(cubes, ) 169 | 170 | def _make_dem_wcs(self): 171 | data_wcs = self.data[self._keys[0]].wcs 172 | temp_table = QuantityTableCoordinate(self.temperature_bin_centers, 173 | names='temperature', 174 | physical_types='phys.temperature') 175 | temp_table_coord = MultipleTableCoordinate(temp_table) 176 | mapping = list(range(data_wcs.pixel_n_dim)) 177 | mapping.extend([data_wcs.pixel_n_dim] * temp_table_coord.wcs.pixel_n_dim) 178 | compound_wcs = CompoundLowLevelWCS(data_wcs, temp_table_coord.wcs, mapping=mapping) 179 | return compound_wcs 180 | 181 | def _make_dem_meta(self): 182 | # Individual classes should override this if they want specific metadata 183 | return {} 184 | -------------------------------------------------------------------------------- /sunkit_dem/data/README.rst: -------------------------------------------------------------------------------- 1 | Data directory 2 | ============== 3 | 4 | This directory contains data files included with the package source 5 | code distribution. Note that this is intended only for relatively small files 6 | - large files should be externally hosted and downloaded as needed. 7 | -------------------------------------------------------------------------------- /sunkit_dem/model_factory.py: -------------------------------------------------------------------------------- 1 | """ 2 | Factory for creating model classes 3 | """ 4 | from sunpy.util.datatype_factory_base import BasicRegistrationFactory 5 | 6 | from .base_model import GenericModel 7 | 8 | __all__ = ["Model"] 9 | 10 | 11 | class ModelFactory(BasicRegistrationFactory): 12 | 13 | def __call__(self, *args, **kwargs): 14 | # TODO: account for duplicates? 15 | WidgetType = None 16 | for key in self.registry: 17 | if self.registry[key](*args, **kwargs): 18 | WidgetType = key 19 | # If no matches, return the default model 20 | WidgetType = self.default_widget_type if WidgetType is None else WidgetType 21 | return WidgetType(*args, **kwargs) 22 | 23 | 24 | Model = ModelFactory(registry=GenericModel._registry, 25 | default_widget_type=GenericModel, 26 | additional_validation_functions=['defines_model_for']) 27 | -------------------------------------------------------------------------------- /sunkit_dem/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Models for computing the differential emission measure 3 | """ 4 | -------------------------------------------------------------------------------- /sunkit_dem/reference/index.rst: -------------------------------------------------------------------------------- 1 | .. _sunkit_dem_reference: 2 | 3 | ********* 4 | Reference 5 | ********* 6 | 7 | .. automodapi:: sunkit_dem 8 | :include-all-objects: 9 | -------------------------------------------------------------------------------- /sunkit_dem/tests/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains package tests. 3 | """ 4 | -------------------------------------------------------------------------------- /sunkit_dem/tests/test_util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tests for utilities 3 | """ 4 | import ndcube 5 | import numpy as np 6 | import pytest 7 | 8 | import astropy.units as u 9 | 10 | from sunkit_dem.util import quantity_1d_to_collection 11 | 12 | wavelengths = np.linspace(0, 1, 10) * u.angstrom 13 | intensities = np.random.rand(wavelengths.shape[0]) * u.photon 14 | 15 | 16 | @pytest.fixture 17 | def collection1d(): 18 | return quantity_1d_to_collection(intensities, wavelengths) 19 | 20 | 21 | def test_is_collection(collection1d): 22 | assert isinstance(collection1d, ndcube.NDCollection) 23 | 24 | 25 | def test_wavelengths(collection1d): 26 | wavelength_axis = u.Quantity([collection1d[k].axis_world_coords()[0] for k in collection1d.keys()]).squeeze() 27 | assert u.allclose(wavelength_axis, wavelengths) 28 | 29 | 30 | def test_axis_type(collection1d): 31 | assert all([collection1d[k].array_axis_physical_types == [('em.wl',)] for k in collection1d.keys()]) 32 | 33 | def test_data(collection1d): 34 | collection_data = u.Quantity([collection1d[k].quantity for k in collection1d.keys()]).squeeze() 35 | assert u.allclose(intensities, collection_data) 36 | -------------------------------------------------------------------------------- /sunkit_dem/util.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions 3 | """ 4 | import ndcube 5 | import numpy as np 6 | 7 | import astropy.units as u 8 | from astropy.nddata import StdDevUncertainty 9 | from astropy.wcs import WCS 10 | 11 | __all__ = ["quantity_1d_to_collection"] 12 | 13 | 14 | @u.quantity_input 15 | def quantity_1d_to_collection(intensity, wavelength: u.angstrom, uncertainty=None, meta=None): 16 | """ 17 | Transform 1D `~astropy.units.Quantity` of intensities to a single-axis 18 | `~ndcube.NDCubeSequence`. 19 | 20 | This is a function for easily converting a 1D array of intensity values into a 21 | 1D `~ndcube.NDCubeSequence` that can be passed to `sunkit_dem.Model` 22 | 23 | Parameters 24 | ---------- 25 | intensities : `~astropy.units.Quantity` 26 | wavelengths : `~astropy.units.Quantity` 27 | uncertainty : `~astrpoy.units.Quantity`, optional 28 | Uncertainties on intensities 29 | meta : `dict` or `dict`-like, optional 30 | 31 | Returns 32 | ------- 33 | `~ndcube.NDCollection` 34 | Collection of intensities with keys corresponding to ``wavelengths`` 35 | """ 36 | cubes = [] 37 | for j, (i, w) in enumerate(zip(intensity, wavelength)): 38 | if uncertainty is not None: 39 | _uncertainty = StdDevUncertainty(uncertainty.value[j, np.newaxis]) 40 | else: 41 | _uncertainty = None 42 | wcs = {'CTYPE1': 'WAVE', 43 | 'CUNIT1': w.unit.to_string(), 44 | 'CDELT1': 1, 45 | 'CRPIX1': 1, 46 | 'CRVAL1': w.value, 47 | 'NAXIS1': 1} 48 | cube = ndcube.NDCube( 49 | i[np.newaxis], 50 | WCS(wcs), 51 | meta=meta, 52 | uncertainty=_uncertainty, 53 | ) 54 | cubes.append((str(w), cube)) 55 | return ndcube.NDCollection(cubes) 56 | -------------------------------------------------------------------------------- /sunkit_dem/version.py: -------------------------------------------------------------------------------- 1 | # NOTE: First try _dev.scm_version if it exists and setuptools_scm is installed 2 | # This file is not included in wheels/tarballs, so otherwise it will 3 | # fall back on the generated _version module. 4 | try: 5 | try: 6 | from ._dev.scm_version import version 7 | except ImportError: 8 | from ._version import version 9 | except Exception: 10 | import warnings 11 | 12 | warnings.warn( 13 | f'could not determine {__name__.split(".")[0]} package version; this indicates a broken installation' 14 | ) 15 | del warnings 16 | 17 | version = '0.0.0' 18 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | min_version = 4.0 3 | requires = 4 | tox-pypi-filter>=0.14 5 | envlist = 6 | py{310,311,312} 7 | py312-devdeps 8 | py310-oldestdeps 9 | codestyle 10 | build_docs 11 | 12 | [testenv] 13 | pypi_filter = https://raw.githubusercontent.com/sunpy/sunpy/main/.test_package_pins.txt 14 | # Run the tests in a temporary directory to make sure that we don't import 15 | # the package from the source tree 16 | change_dir = .tmp/{envname} 17 | description = 18 | run tests 19 | oldestdeps: with the oldest supported version of key dependencies 20 | devdeps: with the latest developer version of key dependencies 21 | pass_env = 22 | # A variable to tell tests we are on a CI system 23 | CI 24 | # Custom compiler locations (such as ccache) 25 | CC 26 | # Location of locales (needed by sphinx on some systems) 27 | LOCALE_ARCHIVE 28 | # If the user has set a LC override we should follow it 29 | LC_ALL 30 | set_env = 31 | MPLBACKEND = agg 32 | devdeps: PIP_EXTRA_INDEX_URL = https://pypi.anaconda.org/astropy/simple https://pypi.anaconda.org/scientific-python-nightly-wheels/simple 33 | deps = 34 | # For packages which publish nightly wheels this will pull the latest nightly 35 | devdeps: sunpy>=0.0.dev0 36 | # Packages without nightly wheels will be built from source like this 37 | # devdeps: git+https://github.com/ndcube/ndcube 38 | oldestdeps: minimum_dependencies 39 | # The following indicates which extras_require will be installed 40 | extras = 41 | tests 42 | commands_pre = 43 | oldestdeps: minimum_dependencies sunkit_dem --filename requirements-min.txt 44 | oldestdeps: pip install -r requirements-min.txt 45 | pip freeze --all --no-input 46 | commands = 47 | # To amend the pytest command for different factors you can add a line 48 | # which starts with a factor like `online: --remote-data=any \` 49 | # If you have no factors which require different commands this is all you need: 50 | pytest \ 51 | -vvv \ 52 | -r fEs \ 53 | --pyargs sunkit_dem \ 54 | --cov-report=xml \ 55 | --cov=sunkit_dem \ 56 | --cov-config={toxinidir}/.coveragerc \ 57 | {toxinidir}/docs \ 58 | {posargs} 59 | 60 | [testenv:codestyle] 61 | pypi_filter = 62 | skip_install = true 63 | description = Run all style and file checks with pre-commit 64 | deps = 65 | pre-commit 66 | commands = 67 | pre-commit install-hooks 68 | pre-commit run --color always --all-files --show-diff-on-failure 69 | 70 | [testenv:build_docs] 71 | description = invoke sphinx-build to build the HTML docs 72 | change_dir = 73 | docs 74 | extras = 75 | docs 76 | commands = 77 | sphinx-build --color -W --keep-going -b html -d _build/.doctrees . _build/html {posargs} 78 | --------------------------------------------------------------------------------