├── .coveragerc ├── .f2py_f2cmap ├── .github └── workflows │ ├── docs.yml │ ├── download_mirror.py │ ├── tests.yml │ └── wheels.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CITATION.cff ├── LICENSE ├── MANIFEST.in ├── MSIS2_LICENSE ├── README.md ├── codecov.yml ├── docs ├── Makefile ├── make.bat └── source │ ├── .nojekyll │ ├── _static │ └── pymsis-logo.png │ ├── _templates │ └── version.html │ ├── changelog │ └── index.rst │ ├── conf.py │ ├── development │ └── index.rst │ ├── index.rst │ └── reference │ └── index.rst ├── examples ├── README.txt ├── plot_altitude_profiles.py ├── plot_annual_variation.py ├── plot_diurnal_variation.py ├── plot_surface.py ├── plot_surface_animation.py ├── plot_version_diff_altitude.py └── plot_version_diff_surface.py ├── meson.build ├── pymsis ├── __init__.py ├── meson.build ├── msis.py └── utils.py ├── pyproject.toml ├── src ├── meson.build ├── msis00 │ └── .gitkeep ├── msis2.0 │ └── .gitkeep ├── msis2.1 │ └── .gitkeep └── wrappers │ ├── msis00.F90 │ ├── msis00.pyf │ ├── msis2.F90 │ ├── msis20.pyf │ └── msis21.pyf ├── tests ├── __init__.py ├── conftest.py ├── f107_ap_test_data.txt ├── meson.build ├── msis2.0_test_ref_dp.txt ├── msis2.1_test_ref_dp.txt ├── test_msis.py ├── test_regression.py └── test_utils.py └── tools ├── download_source.py └── generate_f2pymod.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = true 3 | source = pymsis 4 | omit = pymsis/__init__.py 5 | -------------------------------------------------------------------------------- /.f2py_f2cmap: -------------------------------------------------------------------------------- 1 | {'real':{'rp':'float'}} 2 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: "Documentation Build" 2 | concurrency: 3 | group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} 4 | cancel-in-progress: true 5 | on: 6 | - pull_request 7 | - release 8 | 9 | jobs: 10 | docs: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | # sets up the compiler paths automatically for us 16 | - uses: fortran-lang/setup-fortran@v1 17 | id: setup-fortran 18 | with: 19 | compiler: gcc 20 | version: 13 21 | 22 | - name: Set up Python 23 | uses: actions/setup-python@v4 24 | with: 25 | python-version: '3.13' 26 | 27 | - name: Download source files 28 | run: | 29 | python .github/workflows/download_mirror.py 30 | 31 | - name: Install dependencies 32 | run: | 33 | python -m pip install --upgrade pip 34 | pip install .[doc] 35 | 36 | - name: Build documentation 37 | run: | 38 | cd docs 39 | make html 40 | 41 | - uses: actions/upload-artifact@v4 42 | with: 43 | name: DocumentationHTML 44 | path: docs/build/html/ 45 | 46 | # Publish built docs to gh-pages branch 47 | - name: Commit documentation changes 48 | # push docs only when a GitHub Release is made 49 | if: github.event_name == 'release' && github.event.action == 'published' 50 | run: | 51 | git clone https://github.com/SWxTREC/pymsis.git --branch gh-pages --single-branch gh-pages 52 | cp -r docs/build/html/* gh-pages/ 53 | cd gh-pages 54 | git config --local user.email "action@github.com" 55 | git config --local user.name "GitHub Action" 56 | git add . 57 | git commit -m "Update documentation" -a || true 58 | # The above command will fail if no changes were present, so we ignore that. 59 | 60 | - name: Publish docs 61 | # push docs only when a GitHub Release is made 62 | if: github.event_name == 'release' && github.event.action == 'published' 63 | 64 | uses: ad-m/github-push-action@master 65 | with: 66 | branch: gh-pages 67 | directory: gh-pages 68 | github_token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/download_mirror.py: -------------------------------------------------------------------------------- 1 | """Only for CI downloading/testing.""" 2 | 3 | import tarfile 4 | import urllib.request 5 | from pathlib import Path 6 | 7 | 8 | MSIS21_FILE = ( 9 | "https://gist.github.com/greglucas/" 10 | "6a545a4ccbfdb93290a96ad13a0b6f81/" 11 | "raw/3f82de72d2ea5df7b1cfe492a6b2efee55349f95/" 12 | "nrlmsis2.1.tar.gz" 13 | ) 14 | 15 | with urllib.request.urlopen(MSIS21_FILE) as stream: 16 | tf = tarfile.open(fileobj=stream, mode="r|gz") 17 | tf.extractall(path=Path("src/msis2.1/")) 18 | 19 | # MSIS2 Tar file 20 | MSIS20_FILE = ( 21 | "https://gist.github.com/greglucas/" 22 | "6a545a4ccbfdb93290a96ad13a0b6f81/" 23 | "raw/8a853f3e1c2b324f1c4ea9e0fa1e6d073258a456/" 24 | "NRLMSIS2.0.tar.gz" 25 | ) 26 | 27 | with urllib.request.urlopen(MSIS20_FILE) as stream: 28 | tf = tarfile.open(fileobj=stream, mode="r|gz") 29 | tf.extractall(path=Path("src/msis2.0/")) 30 | 31 | # MSIS-00 fortran file 32 | MSIS00_FILE = ( 33 | "https://gist.githubusercontent.com/greglucas/" 34 | "6a545a4ccbfdb93290a96ad13a0b6f81/" 35 | "raw/2c1f5d899d7b42392a6b19a041d2cc213589a5f1/" 36 | "NRLMSISE-00.FOR" 37 | ) 38 | with urllib.request.urlopen(MSIS00_FILE) as response: 39 | with open(Path("src/msis00/NRLMSISE-00.FOR"), "wb") as f: 40 | f.write(response.read()) 41 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: tests 5 | 6 | on: 7 | push: 8 | branches: [ 'main' ] 9 | pull_request: 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | linting: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: actions/setup-python@v4 21 | with: 22 | python-version: 3.11 23 | - name: Install mypy 24 | run: | 25 | python -m pip install mypy numpy 26 | mypy pymsis 27 | test: 28 | runs-on: ${{ matrix.os }} 29 | strategy: 30 | matrix: 31 | os: [windows-latest, ubuntu-latest, macos-latest, macos-14] 32 | python-version: ['3.10', '3.11', '3.12', '3.13', '3.13t'] 33 | defaults: 34 | run: 35 | shell: bash 36 | 37 | steps: 38 | - uses: actions/checkout@v3 39 | 40 | # sets up the compiler paths automatically for us 41 | - uses: fortran-lang/setup-fortran@v1 42 | id: setup-fortran 43 | with: 44 | compiler: gcc 45 | version: 13 46 | 47 | - name: Compiler versions 48 | run: | 49 | which gcc 50 | gcc --version 51 | which gfortran 52 | gfortran --version 53 | 54 | - name: Set up Python ${{ matrix.python-version }} 55 | uses: Quansight-Labs/setup-python@b9ab292c751a42bcd2bb465b7fa202ea2c3f5796 # v5.3.1 56 | with: 57 | python-version: ${{ matrix.python-version }} 58 | 59 | - name: Download source files 60 | run: | 61 | python .github/workflows/download_mirror.py 62 | 63 | - name: Install pymsis 64 | run: | 65 | python -m pip install --upgrade pip 66 | # TODO: Remove meson installation from source once a new release 67 | # that includes https://github.com/mesonbuild/meson/pull/13851 is available 68 | python -m pip install git+https://github.com/mesonbuild/meson 69 | python -m pip install ninja meson-python setuptools_scm numpy 70 | python -m pip install --no-build-isolation -v .[test] 71 | 72 | - name: Test with pytest 73 | run: | 74 | # TODO: Get coverage for other modules without specifying pymsis.msis directly 75 | pytest --color=yes --cov=pymsis.msis --cov pymsis.utils --cov-report=xml --pyargs pymsis 76 | 77 | - name: Upload code coverage 78 | uses: codecov/codecov-action@v3 79 | -------------------------------------------------------------------------------- /.github/workflows/wheels.yml: -------------------------------------------------------------------------------- 1 | name: Build wheels 2 | 3 | # Only build on tagged releases 4 | on: 5 | release: 6 | types: [published] 7 | # Also allow running this action on PRs if requested by applying the 8 | # "Run cibuildwheel" label. 9 | pull_request: 10 | types: 11 | - opened 12 | - synchronize 13 | - reopened 14 | - labeled 15 | 16 | concurrency: 17 | group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event_name }}-${{ github.event.ref }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | build_wheels: 22 | if: | 23 | github.event_name == 'release' || 24 | (github.event_name == 'pull_request' && ( 25 | ( 26 | github.event.action == 'labeled' && 27 | github.event.label.name == 'CI: build wheels' 28 | ) || 29 | contains(github.event.pull_request.labels.*.name, 30 | 'CI: build wheels') 31 | ) 32 | ) 33 | name: Build wheels on ${{ matrix.os }} 34 | runs-on: ${{ matrix.os }} 35 | strategy: 36 | matrix: 37 | os: [ubuntu-latest, windows-latest, macos-13, macos-14] 38 | defaults: 39 | run: 40 | shell: bash 41 | steps: 42 | - uses: actions/checkout@v4 43 | with: 44 | fetch-depth: 0 45 | 46 | - name: Download source files 47 | run: | 48 | python .github/workflows/download_mirror.py 49 | 50 | - name: Set up QEMU 51 | if: runner.os == 'Linux' 52 | uses: docker/setup-qemu-action@v3 53 | with: 54 | platforms: all 55 | 56 | # sets up the compiler paths automatically for us 57 | - uses: fortran-lang/setup-fortran@v1 58 | id: setup-fortran 59 | with: 60 | compiler: gcc 61 | version: 13 62 | 63 | - name: Compiler versions 64 | run: | 65 | which gcc 66 | gcc --version 67 | which gfortran 68 | gfortran --version 69 | 70 | - name: Build wheels 71 | uses: pypa/cibuildwheel@v2.21.3 72 | env: 73 | # The brew built gfortran linked libraries have minimum macOS versions 74 | # of the macOS version they were built on. We would need to compile 75 | # from source rather than use setup-fortran if we want to support 76 | # lower macOS versions. 77 | MACOSX_DEPLOYMENT_TARGET: "${{ matrix.os == 'macos-13' && '13.0' || '14.0' }}" 78 | # TEMP don't use automated/isolated build environment, but manually 79 | # install build dependencies so we can build with meson from source 80 | CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation" 81 | CIBW_BEFORE_BUILD: 82 | python -m pip install git+https://github.com/mesonbuild/meson && 83 | python -m pip install ninja meson-python setuptools_scm numpy 84 | 85 | - uses: actions/upload-artifact@v4 86 | with: 87 | name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} 88 | path: ./wheelhouse/*.whl 89 | 90 | build_sdist: 91 | name: Build source distribution 92 | needs: build_wheels 93 | runs-on: ubuntu-latest 94 | steps: 95 | - uses: actions/checkout@v4 96 | 97 | - uses: actions/setup-python@v4 98 | name: Install Python 99 | with: 100 | python-version: '3.12' 101 | 102 | - name: Download source files 103 | run: | 104 | python .github/workflows/download_mirror.py 105 | - name: Build sdist 106 | run: | 107 | python -m pip install meson-python meson ninja build 108 | python -m build --sdist 109 | - uses: actions/upload-artifact@v3 110 | with: 111 | name: cibw-sdist 112 | path: dist/*.tar.gz 113 | 114 | upload_pypi: 115 | needs: [build_wheels, build_sdist] 116 | runs-on: ubuntu-latest 117 | environment: 118 | name: pypi 119 | url: https://pypi.org/project/pymsis 120 | permissions: 121 | id-token: write 122 | # alternatively, to publish when a GitHub Release is created, use the following rule: 123 | if: github.event_name == 'release' && github.event.action == 'published' 124 | steps: 125 | - name: Download sdist and wheels 126 | uses: actions/download-artifact@v4 127 | with: 128 | # unpacks all CIBW artifacts into dist/ 129 | pattern: 'cibw-*' 130 | path: dist 131 | merge-multiple: true 132 | 133 | - name: Publish to PyPI 134 | uses: pypa/gh-action-pypi-publish@release/v1 135 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/msis**/* 2 | !src/msis*/.gitkeep 3 | src/wrappers/* 4 | !src/wrappers/*.pyf 5 | !src/wrappers/*.F90 6 | 7 | pymsis/msis*.parm 8 | pymsis/msis20f* 9 | pymsis/msis21f* 10 | pymsis/msis00f* 11 | 12 | # Solar wind file 13 | SW-All.csv 14 | 15 | *.tar.gz 16 | docs/source/examples 17 | docs/source/reference/generated 18 | docs/source/sg_execution_times.rst 19 | junit/ 20 | 21 | # Meson build files 22 | .mesonpy* 23 | 24 | # Byte-compiled / optimized / DLL files 25 | __pycache__/ 26 | *.py[cod] 27 | *$py.class 28 | 29 | # C extensions 30 | *.so 31 | 32 | # Distribution / packaging 33 | .Python 34 | build/ 35 | develop-eggs/ 36 | dist/ 37 | downloads/ 38 | eggs/ 39 | .eggs/ 40 | lib/ 41 | lib64/ 42 | parts/ 43 | sdist/ 44 | var/ 45 | wheels/ 46 | share/python-wheels/ 47 | *.egg-info/ 48 | .installed.cfg 49 | *.egg 50 | MANIFEST 51 | 52 | # PyInstaller 53 | # Usually these files are written by a python script from a template 54 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 55 | *.manifest 56 | *.spec 57 | 58 | # Installer logs 59 | pip-log.txt 60 | pip-delete-this-directory.txt 61 | 62 | # Unit test / coverage reports 63 | htmlcov/ 64 | .tox/ 65 | .nox/ 66 | .coverage 67 | .coverage.* 68 | .cache 69 | nosetests.xml 70 | coverage.xml 71 | *.cover 72 | *.py,cover 73 | .hypothesis/ 74 | .pytest_cache/ 75 | cover/ 76 | 77 | # Translations 78 | *.mo 79 | *.pot 80 | 81 | # Django stuff: 82 | *.log 83 | local_settings.py 84 | db.sqlite3 85 | db.sqlite3-journal 86 | 87 | # Flask stuff: 88 | instance/ 89 | .webassets-cache 90 | 91 | # Scrapy stuff: 92 | .scrapy 93 | 94 | # Sphinx documentation 95 | docs/_build/ 96 | 97 | # PyBuilder 98 | .pybuilder/ 99 | target/ 100 | 101 | # Jupyter Notebook 102 | .ipynb_checkpoints 103 | 104 | # IPython 105 | profile_default/ 106 | ipython_config.py 107 | 108 | # pyenv 109 | # For a library or package, you might want to ignore these files since the code is 110 | # intended to run in multiple environments; otherwise, check them in: 111 | # .python-version 112 | 113 | # pipenv 114 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 115 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 116 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 117 | # install all needed dependencies. 118 | #Pipfile.lock 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | autofix_prs: false 3 | autoupdate_schedule: 'quarterly' 4 | repos: 5 | - repo: https://github.com/astral-sh/ruff-pre-commit 6 | rev: 'v0.11.4' 7 | hooks: 8 | - id: ruff 9 | args: [--fix] 10 | - id: ruff-format 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [v0.10.0] 2024-11-19 6 | 7 | - **ADDED** Top-level function exports to avoid submodule imports 8 | - `pymsis.calculate()` is the primrary entrypoint to running the MSIS 9 | model and calculating the atmosphere at the requested data points. 10 | - `pymsis.msis.run()` was not very descriptive and caused issues with IDL 11 | bridging into Python and wanting that name reserved. To avoid this, the 12 | function has been renamed `calculate` and is available as `pymsis.calculate()` 13 | now. The `pymsis.msis.run()` is still available as an alias for now, but 14 | may be removed in the future. 15 | - **ADDED** Variable enumeration for easier indexing into output arrays. 16 | - This can be used as `pymsis.Variable.O2` for getting the `O2` species index. 17 | For example, `output_array[..., pymsis.Variable.O2]`. 18 | - **ADDED** Python 3.13 and 3.13t support 19 | - **ADDED** Multithreaded support. 20 | - The underlying MSIS libraries are not threadsafe due 21 | to the use of many global/save variables. There is a lock around the 22 | extension modules so that only one thread will be calling the routines 23 | at a time, so the Python library is safe to use in a multi-threaded context. 24 | - **MAINTENANCE** Default `-O1` optimization level for all builds. 25 | - Previously, this 26 | was only done on Windows machines. Users can change this by updating 27 | environment variables before building with `FFLAGS=-Ofast pip install .`, 28 | but note that some machines produce invalid results when higher 29 | optimizations are used. 30 | - **PERFORMANCE** Cache options state between subsequent runs. 31 | - Avoid calling initialization switches unless they have changed between runs 32 | - **PERFORMANCE** Speed up numpy function calls. 33 | - Change some numpy broadcasting and comparisons to speed up the creation of 34 | input and output values. 35 | - **MAINTENANCE** Add dynamic versioning to the project based on git tags and commits. 36 | - This removes the need to manually bump the version numbers and metadata before 37 | releasing the project which led to some errors previously. 38 | - **DEPRECATED** Calling `msis00f.pytselec()` and `msis00f.pygtd7d` functions. 39 | - Use `msis00f.pyinitswitch` and `msis00f.pymsiscalc` instead. 40 | - This helps with standardization across the extension modules. These extension 41 | should rarely be used by external people and `pymsis.calculate()` is a better entry 42 | to using the package. 43 | 44 | ## [v0.9.0] - 2024-04-03 45 | 46 | - **MAINTENANCE** Add MacOS arm64 wheels (Apple Silicon). 47 | - **FIX** Obvious solar radio burst F10.7 data is automatically removed. 48 | - This applies to the default data and a warning is issued when running over 49 | these time periods. 50 | 51 | ## [v0.8.0] - 2023-10-03 52 | 53 | - **MAINTENANCE** Python 3.10+ required. 54 | 55 | ## [v0.7.0] - 2023-01-29 56 | 57 | ### Maintenance 58 | 59 | - **MAINTENANCE** CelesTrak is now used as the data provider for the Ap and F10.7 values. 60 | - This avoids data gaps and interpolation issues that were present in the source data. 61 | - **MAINTENANCE** Cleanup type hints throughout the codebase. 62 | - **MAINTENANCE** Updated the wrappers argument order to be the same throughout. 63 | - This helps when called with positional vs keyword arguments. 64 | 65 | ## [v0.6.0] - 2022-11-14 66 | 67 | - **ADDED** Automatic download of F10.7 and ap data for users. 68 | - This means that F10.7 and ap are optional inputs to the `pymsis.calculate()` 69 | function during historical periods and the routines will automatically 70 | fetch the proper input data. 71 | 72 | - **MAINTENANCE** We now use meson as the build system to compile the extension modules. 73 | 74 | ## [v0.5.0] - 2022-08-18 75 | 76 | - **ADDED** MSIS2.1, a new version of MSIS. 77 | - This is the first version that contains NO. 78 | - This is the new default used in `pymsis.calculate()`. 79 | - **MAINTENANCE** Added more wheels to the release and CI systems for testing. 80 | 81 | ## [v0.4.0] - 2022-02-26 82 | 83 | - **ADDED** Created a DOI for the project: . 84 | - **ADDED** Handle flattened input arrays directly (satellite flythroughs). 85 | 86 | ## [v0.3.0] - 2021-08-31 87 | 88 | - **ADDED** First release with wheels for all platforms. 89 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Lucas" 5 | given-names: "Greg" 6 | email: greg.lucas@lasp.colorado.edu 7 | affiliation: >- 8 | University of Colorado Laboratory for 9 | Atmospheric and Space Physics (LASP) 10 | orcid: "https://orcid.org/0000-0003-1331-1863" 11 | title: "pymsis" 12 | version: v0.10.0 13 | doi: 10.5281/zenodo.5348502 14 | date-released: 2024-04-04 15 | url: "https://github.com/SWxTREC/pymsis" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020, Regents of the University of Colorado 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MSIS2_LICENSE 2 | include pyproject.toml 3 | include tests/* 4 | include tools/* 5 | # Exclude external fortran files 6 | exclude src/msis**/* 7 | exclude src/wrappers/*.[fc] 8 | include src/wrappers/*.pyf 9 | exclude pymsis/*.parm 10 | -------------------------------------------------------------------------------- /MSIS2_LICENSE: -------------------------------------------------------------------------------- 1 | !############################################################################## 2 | ! MSIS (NRL-SOF-014-1) SOFTWARE 3 | ! 4 | ! MSIS is a registered trademark of the Government of the United States of 5 | ! America, as represented by the Secretary of the Navy. Unauthorized use of 6 | ! the trademark is prohibited. 7 | ! 8 | ! The MSIS Software (hereinafter Software) is property of the United States 9 | ! Government, as represented by the Secretary of the Navy. Methods performed 10 | ! by this software are covered by U.S. Patent Number 10,641,925. The Government 11 | ! of the United States of America, as represented by the Secretary of the Navy, 12 | ! herein grants a non-exclusive, non-transferable license to the Software for 13 | ! academic, non-commercial, purposes only. A user of the Software shall not: 14 | ! (i) use the Software for any non-academic, commercial purposes, (ii) make 15 | ! any modification or improvement to the Software, (iii) disseminate the 16 | ! Software or any supporting data to any other person or entity who will use 17 | ! the Software for any non-academic, commercial purposes, or (iv) copy the 18 | ! Software or any documentation related thereto except for (a) distribution 19 | ! among the users personal computer systems, archival, or emergency repair 20 | ! purposes, or (b) distribution for non-commercial, academic purposes, without 21 | ! first obtaining the written consent of IP Counsel for the Naval Research 22 | ! Laboratory. 23 | ! 24 | ! As the owner of MSIS, the United States, the United States Department of 25 | ! Defense, and their employees: (1) Disclaim any warranties, express, or 26 | ! implied, including but not limited to any implied warranties of 27 | ! merchantability, fitness for a particular purpose, title or non-infringement, 28 | ! (2) Do not assume any legal liability or responsibility for the accuracy, 29 | ! completeness, or usefulness of the software, (3) Do not represent that use of 30 | ! the software would not infringe privately owned rights, (4) Do not warrant 31 | ! that the software will function uninterrupted, that is error-free or that any 32 | ! errors will be corrected. 33 | ! 34 | ! BY USING THIS SOFTWARE YOU ARE AGREEING TO THE ABOVE TERMS AND CONDITIONS. 35 | !############################################################################## -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pymsis: A python wrapper of the NRLMSIS model 2 | 3 | ![image](https://swxtrec.github.io/pymsis/_static/pymsis-logo.png) 4 | 5 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5348502.svg)](https://doi.org/10.5281/zenodo.5348502) 6 | [![PyPi](https://badge.fury.io/py/pymsis.svg)](https://badge.fury.io/py/pymsis) 7 | [![Downloads](https://static.pepy.tech/badge/pymsis/month)](https://pepy.tech/project/pymsis) 8 | [![GitHubActions](https://github.com/SWxTREC/pymsis/actions/workflows/tests.yml/badge.svg)](https://github.com/SWxTREC/pymsis/actions?query=workflow%3Atests) 9 | [![codecov](https://codecov.io/gh/SWxTREC/pymsis/branch/main/graph/badge.svg?token=NSUGKPJ3F7)](https://codecov.io/gh/SWxTREC/pymsis) 10 | 11 | Pymsis is a minimal and fast Python wrapper of the NRLMSIS models (MSISE-00, MSIS2.0, MSIS2.1). 12 | The [MSIS model](https://www.nrl.navy.mil/Our-Work/Areas-of-Research/Space-Science/) is 13 | developed by the Naval Research Laboratory. For quick access to the model data without any code, 14 | there is a web viewer that uses pymsis: 15 | 16 | ## Quickstart 17 | 18 | - [Documentation](https://swxtrec.github.io/pymsis/) 19 | - [API Reference](https://swxtrec.github.io/pymsis/reference/index.html): Details about the various options and configurations available in the functions. 20 | - [Examples](https://swxtrec.github.io/pymsis/examples/index.html): Demo for how to access and plot the data. 21 | - [Web viewer](https://swx-trec.com/msis): An interactive website using pymsis through cloud-based serverless functions. 22 | 23 | **A few short lines of code to get started quickly with pymsis.** 24 | 25 | 1. Create a range of dates during the 2003 Halloween storm. 26 | 2. Run the model at the desired location (lon, lat) (0, 0) and 400 km altitude. 27 | 3. Plot the results to see how the mass density increased at 400 km altitude during this storm. 28 | 29 | ```python 30 | import numpy as np 31 | import pymsis 32 | 33 | dates = np.arange(np.datetime64("2003-10-28T00:00"), np.datetime64("2003-11-04T00:00"), np.timedelta64(30, "m")) 34 | # geomagnetic_activity=-1 is a storm-time run 35 | data = pymsis.calculate(dates, 0, 0, 400, geomagnetic_activity=-1) 36 | 37 | # Plot the data 38 | import matplotlib.pyplot as plt 39 | # Total mass density over time 40 | plt.plot(dates, data[:, 0, 0, 0, 0]) 41 | plt.show() 42 | ``` 43 | 44 | > **note** 45 | > 46 | > - The model will automatically download and access the F10.7 and ap data for you if you have an internet connection. 47 | > - The returned data structure has shape [ndates, nlons, nlats, nalts, 11], but for this example we only have one point with many dates [ndates, 1, 1, 1, 11]. 48 | > -s The 11 is for each of the species MSIS calculates for each input point. The first element is the Total Mass Density (kg/m3). 49 | 50 | ## NRL Mass Spectrometer, Incoherent Scatter Radar Extended Model (MSIS) 51 | 52 | The [MSIS 53 | model](https://www.nrl.navy.mil/Our-Work/Areas-of-Research/Space-Science/) 54 | is developed by the Naval Research Laboratory. 55 | 56 | Note that the MSIS2 code is not available for commercial use without 57 | contacting NRL. See the [MSIS2 license file](https://github.com/SWxTREC/pymsis/blob/main/MSIS2_LICENSE)) for explicit 58 | details. We do not repackage the MSIS source code in this 59 | repository for that reason. However, utility functions are provided to easily 60 | download and extract the original source code. By using that code you 61 | agree to their terms and conditions. 62 | 63 | ## References 64 | 65 | Please acknowledge the University of Colorado Space Weather Technology, 66 | Research and Education Center (SWx TREC) and cite the original papers if 67 | you make use of this model in a publication. 68 | 69 | ### Python Code 70 | 71 | [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.5348502.svg)](https://doi.org/10.5281/zenodo.5348502) 72 | 73 | > Lucas, G. (2022). pymsis [Computer software]. [doi:10.5281/zenodo.5348502](https://doi.org/10.5281/zenodo.5348502) 74 | 75 | ### MSIS2.1 76 | 77 | > Emmert, J. T., Jones, M., Siskind, D. E., Drob, D. P., Picone, J. M., 78 | > Stevens, M. H., et al. (2022). NRLMSIS 2.1: An empirical model of nitric 79 | > oxide incorporated into MSIS. Journal of Geophysical Research: Space 80 | > Physics, 127, e2022JA030896. [doi:10.1029/2022JA030896](https://doi.org/10.1029/2022JA030896) 81 | 82 | ### MSIS2.0 83 | 84 | > Emmert, J. T., Drob, D. P., Picone, J. M., Siskind, D. E., Jones, M., 85 | > Mlynczak, M. G., et al. (2020). NRLMSIS 2.0: A whole‐atmosphere 86 | > empirical model of temperature and neutral species densities. Earth 87 | > and Space Science, 7, e2020EA001321. 88 | > [doi:10.1029/2020EA001321](https://doi.org/10.1029/2020EA001321) 89 | 90 | ### MSISE-00 91 | 92 | > Picone, J. M., Hedin, A. E., Drob, D. P., and Aikin, A. C., 93 | > NRLMSISE‐00 empirical model of the atmosphere: Statistical comparisons 94 | > and scientific issues, J. Geophys. Res., 107( A12), 1468, 95 | > [doi:10.1029/2002JA009430](https://doi.org/10.1029/2002JA009430), 96 | > 2002. 97 | 98 | ### Geomagnetic Data 99 | 100 | If you make use of the automatic downloads of the F10.7 and ap data, 101 | please cite that data in your publication as well. The data is downloaded 102 | from CelesTrak, which has filled in missing data from the source. Both citations 103 | are given below. 104 | 105 | > CelesTrak. https://celestrak.org/SpaceData/ 106 | 107 | > Matzka, J., Stolle, C., Yamazaki, Y., Bronkalla, O. and Morschhauser, A., 108 | > 2021. The geomagnetic Kp index and derived indices of geomagnetic activity. 109 | > Space Weather, [doi:10.1029/2020SW002641](https://doi.org/10.1029/2020SW002641). 110 | 111 | ## Installation 112 | 113 | The easiest way to install pymsis is to install from PyPI. 114 | 115 | ```bash 116 | pip install pymsis 117 | ``` 118 | 119 | For the most up-to-date pymsis, you can install directly from the git 120 | repository 121 | 122 | ```bash 123 | pip install git+https://github.com/SWxTREC/pymsis.git 124 | ``` 125 | 126 | or to work on it locally, you can clone the repository and install the 127 | test dependencies. 128 | 129 | ```bash 130 | git clone https://github.com/SWxTREC/pymsis.git 131 | cd pymsis 132 | pip install .[test] 133 | ``` 134 | 135 | ### Remote installation 136 | 137 | The installation is dependent on access to the NRL source code. If the 138 | download fails, or you have no internet access you can manually install 139 | the Fortran source code as follows. A script to help with this or give 140 | ideas on how to achieve this remote installation are provided in the 141 | [tools directory](https://github.com/SWxTREC/pymsis/blob/main/tools/download_source.py)). 142 | 143 | 1. **Download the source code** 144 | The source code is hosted on NRL\'s website: 145 | 146 | Download the `NRLMSIS2.0.tar.gz` file to your local system. 147 | 148 | 2. **Extract the source files** 149 | The tar file needs to be extracted to the `src/msis2.0` directory. 150 | 151 | ```bash 152 | tar -xvzf NRLMSIS2.0.tar.gz -C src/msis2.0/ 153 | ``` 154 | 155 | 3. **Install the Python package** 156 | 157 | ```bash 158 | pip install . 159 | ``` 160 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false -------------------------------------------------------------------------------- /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 = source 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/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=source 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/source/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SWxTREC/pymsis/cdd4043395bf3485fe9295afe862e0a607a0e7fb/docs/source/.nojekyll -------------------------------------------------------------------------------- /docs/source/_static/pymsis-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SWxTREC/pymsis/cdd4043395bf3485fe9295afe862e0a607a0e7fb/docs/source/_static/pymsis-logo.png -------------------------------------------------------------------------------- /docs/source/_templates/version.html: -------------------------------------------------------------------------------- 1 | 2 | v{{ version }} -------------------------------------------------------------------------------- /docs/source/changelog/index.rst: -------------------------------------------------------------------------------- 1 | 2 | .. include:: ../../../CHANGELOG.md 3 | :parser: myst_parser.sphinx_ -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration file for the Sphinx documentation builder. 3 | 4 | This file only contains a selection of the most common options. For a full 5 | list see the documentation: 6 | https://www.sphinx-doc.org/en/master/usage/configuration.html 7 | """ 8 | 9 | # -- Path setup -------------------------------------------------------------- 10 | 11 | import importlib.metadata 12 | import sys 13 | 14 | # If extensions (or modules to document with autodoc) are in another directory, 15 | # add these directories to sys.path here. If the directory is relative to the 16 | # documentation root, use resolve() to make it absolute, like shown here. 17 | # 18 | from pathlib import Path 19 | 20 | 21 | sys.path.insert(0, Path("../../pymsis").resolve()) 22 | 23 | # -- Project information ----------------------------------------------------- 24 | 25 | project = "pymsis" 26 | copyright = "2020, Regents of the University of Colorado" 27 | author = "Greg Lucas" 28 | 29 | # The full version, including alpha/beta/rc tags 30 | version = importlib.metadata.version("pymsis") 31 | release = version 32 | 33 | 34 | # -- General configuration --------------------------------------------------- 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 | "matplotlib.sphinxext.plot_directive", 41 | "myst_parser", 42 | "sphinx.ext.autodoc", 43 | "sphinx.ext.autosummary", 44 | "sphinx.ext.githubpages", # Helpful for publishing to gh-pages 45 | "sphinx.ext.napoleon", 46 | "sphinx_gallery.gen_gallery", 47 | ] 48 | 49 | # Add any paths that contain templates here, relative to this directory. 50 | templates_path = ["_templates"] 51 | 52 | # List of patterns, relative to source directory, that match files and 53 | # directories to ignore when looking for source files. 54 | # This pattern also affects html_static_path and html_extra_path. 55 | exclude_patterns = [] 56 | 57 | 58 | # -- Options for HTML output ------------------------------------------------- 59 | 60 | # The theme to use for HTML and HTML Help pages. See the documentation for 61 | # a list of builtin themes. 62 | # 63 | html_theme = "pydata_sphinx_theme" 64 | 65 | html_logo = "_static/pymsis-logo.png" 66 | 67 | html_theme_options = { 68 | "github_url": "https://github.com/SWxTREC/pymsis", 69 | "navbar_start": ["navbar-logo", "version"], 70 | } 71 | 72 | # Add any paths that contain custom static files (such as style sheets) here, 73 | # relative to this directory. They are copied after the builtin static files, 74 | # so a file named "default.css" will overwrite the builtin "default.css". 75 | html_static_path = ["_static"] 76 | 77 | # Autosummary 78 | autosummary_generate = True 79 | autodoc_typehints = "none" 80 | 81 | # Sphinx gallery 82 | sphinx_gallery_conf = { 83 | "examples_dirs": "../../examples", # path to example scripts 84 | "gallery_dirs": "examples", # path to where to save generated output 85 | "matplotlib_animations": True, 86 | } 87 | -------------------------------------------------------------------------------- /docs/source/development/index.rst: -------------------------------------------------------------------------------- 1 | .. _development: 2 | 3 | Development 4 | =========== 5 | 6 | To contribute to pymsis, you can fork the GitHub repository, 7 | add your code, and make a pull request. If you are adding additional functionality, 8 | you should also include a test with your enhancement. 9 | 10 | Pymsis is purposefully minimal, with Numpy as the only dependency. 11 | This makes it easy for other packages to leverage the interface to the Fortran 12 | code without imposing a specific data storage or plotting framework. 13 | 14 | A typical development workflow might look like the following: 15 | 16 | .. code:: bash 17 | 18 | # Install the development dependencies 19 | pip install .[dev] 20 | 21 | # Install the pre-commit hooks 22 | pre-commit install 23 | 24 | # Update the code on a feature branch 25 | git checkout -b my-cool-feature 26 | 27 | # Run the tests 28 | pytest 29 | 30 | # Commit the changes and push to your remote repository 31 | git add my-file 32 | git commit 33 | git push -u origin my-cool-feature 34 | 35 | # Go to GitHub and open a pull request! 36 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../README.md 2 | :parser: myst_parser.sphinx_ 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | :hidden: 7 | 8 | examples/index 9 | reference/index 10 | development/index 11 | changelog/index 12 | -------------------------------------------------------------------------------- /docs/source/reference/index.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | API reference 4 | ============= 5 | .. currentmodule:: pymsis 6 | 7 | This page gives an overview of the routines within the pymsis package. 8 | To calculate atmospheric constituents at grid points, use the :func:`~.calculate` function. 9 | This is typically the only function you will need to use. You can access the output variables 10 | using the :class:`~.Variable` enumeration for easier indexing into the output data array. 11 | For example, as ``output_array[..., Variable.MASS_DENSITY]``. 12 | 13 | .. autosummary:: 14 | :toctree: generated/ 15 | :nosignatures: 16 | 17 | calculate 18 | Variable 19 | 20 | msis module 21 | ----------- 22 | 23 | For more control and help creating properly formatted inputs, one can 24 | use the :mod:`pymsis.msis` module. This module provides functions to 25 | create input data, create the options list, and run the model. 26 | 27 | .. autosummary:: 28 | :toctree: generated/ 29 | :nosignatures: 30 | 31 | msis.create_input 32 | msis.create_options 33 | msis.calculate 34 | 35 | utils module 36 | ------------ 37 | 38 | The MSIS model requires solar forcing data (F10.7 and ap) to calculate the state of the atmosphere. 39 | When running over past time periods, this data is automatically downloaded and used if not specified by the user. 40 | For more control over the F10.7 and Ap values the model is using, one can input the desired values 41 | into the :func:`~.calculate` call ``calculate(..., f107s=..., f107as=..., aps=...)``. 42 | To get the values used automatically by the model, the :mod:`pymsis.utils` module has several 43 | helper routines to download new files (will retrieve a file with data up to the present) and 44 | get the proper F10.7 and ap values to use for a specific date and time. 45 | 46 | .. autosummary:: 47 | :toctree: generated/ 48 | :nosignatures: 49 | 50 | utils.download_f107_ap 51 | utils.get_f107_ap 52 | -------------------------------------------------------------------------------- /examples/README.txt: -------------------------------------------------------------------------------- 1 | .. _examples: 2 | 3 | Examples 4 | ======== 5 | 6 | This is a collection of demos and examples of what can be done with pymsis. 7 | -------------------------------------------------------------------------------- /examples/plot_altitude_profiles.py: -------------------------------------------------------------------------------- 1 | """ 2 | Altitude profiles 3 | ----------------- 4 | 5 | This example demonstrates how to calculate the 6 | altitude variations at a single location. Additionally, 7 | we show the difference between densities at noon (solid) 8 | and midnight (dashed). 9 | 10 | """ 11 | 12 | import matplotlib.pyplot as plt 13 | import numpy as np 14 | 15 | import pymsis 16 | 17 | 18 | lon = 0 19 | lat = 70 20 | alts = np.linspace(0, 1000, 1000) 21 | f107 = 150 22 | f107a = 150 23 | ap = 7 24 | aps = [[ap] * 7] 25 | 26 | date = np.datetime64("2003-01-01T00:00") 27 | output_midnight = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps) 28 | date = np.datetime64("2003-01-01T12:00") 29 | output_noon = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps) 30 | 31 | # output is now of the shape (1, 1, 1, 1000, 11) 32 | # Get rid of the single dimensions 33 | output_midnight = np.squeeze(output_midnight) 34 | output_noon = np.squeeze(output_noon) 35 | 36 | _, ax = plt.subplots() 37 | for variable in pymsis.Variable: 38 | if variable.name in ("Total mass density", "Temperature"): 39 | # Ignore non-number densities 40 | continue 41 | (line,) = ax.plot(output_midnight[:, variable], alts, linestyle="--") 42 | ax.plot(output_noon[:, variable], alts, c=line.get_color(), label=variable.name) 43 | 44 | ax.legend( 45 | loc="upper center", bbox_to_anchor=(0.5, 1.15), fancybox=True, shadow=True, ncol=4 46 | ) 47 | ax.set_title(f"Longitude: {lon}, Latitude: {lat}") 48 | ax.set_xscale("log") 49 | ax.set_xlim(1e8, 1e18) 50 | ax.set_ylim(0, 1000) 51 | ax.set_xlabel("Number density (/m$^3$)") 52 | ax.set_ylabel("Altitude (km)") 53 | 54 | plt.show() 55 | -------------------------------------------------------------------------------- /examples/plot_annual_variation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Annual variation 3 | ---------------- 4 | 5 | This example demonstrates how to calculate the 6 | difference from the annual mean at a single location. 7 | 8 | """ 9 | 10 | import matplotlib.pyplot as plt 11 | import numpy as np 12 | 13 | import pymsis 14 | 15 | 16 | lon = 0 17 | lat = 0 18 | alt = 200 19 | f107 = 150 20 | f107a = 150 21 | ap = 7 22 | # One years worth of data at the 12th hour every day 23 | dates = np.arange("2003-01", "2004-01", dtype="datetime64[D]") + np.timedelta64(12, "h") 24 | ndates = len(dates) 25 | # (F107, F107a, ap) all need to be specified at the same length as dates 26 | f107s = [f107] * ndates 27 | f107as = [f107a] * ndates 28 | aps = [[ap] * 7] * ndates 29 | 30 | output = pymsis.calculate(dates, lon, lat, alt, f107s, f107as, aps) 31 | # output is now of the shape (ndates, 1, 1, 1, 11) 32 | # Get rid of the single dimensions 33 | output = np.squeeze(output) 34 | 35 | # Lets get the percent variation from the annual mean for each variable 36 | variation = 100 * (output / output.mean(axis=0) - 1) 37 | 38 | _, ax = plt.subplots() 39 | for variable in pymsis.Variable: 40 | if variable.name == "NO": 41 | # There is currently no NO data 42 | continue 43 | ax.plot(dates, variation[:, variable], label=variable.name) 44 | 45 | ax.legend( 46 | loc="upper center", bbox_to_anchor=(0.5, 1.15), fancybox=True, shadow=True, ncol=5 47 | ) 48 | ax.set_xlabel(f"Longitude: {lon}, Latitude: {lat}, Altitude: {alt} km") 49 | ax.set_ylabel("Difference from annual mean (%)") 50 | 51 | plt.show() 52 | -------------------------------------------------------------------------------- /examples/plot_diurnal_variation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Diurnal variation 3 | ----------------- 4 | 5 | This example demonstrates how to calculate the 6 | diurnal variation at a single location. 7 | 8 | """ 9 | 10 | import matplotlib.dates as mdates 11 | import matplotlib.pyplot as plt 12 | import numpy as np 13 | 14 | import pymsis 15 | 16 | 17 | lon = 0 18 | lat = 70 19 | alt = 200 20 | f107 = 150 21 | f107a = 150 22 | ap = 7 23 | # One years worth of data at the 12th hour every day 24 | dates = np.arange("2003-01-01", "2003-01-02", dtype="datetime64[m]") 25 | ndates = len(dates) 26 | # (F107, F107a, ap) all need to be specified at the same length as dates 27 | f107s = [f107] * ndates 28 | f107as = [f107a] * ndates 29 | aps = [[ap] * 7] * ndates 30 | 31 | output = pymsis.calculate(dates, lon, lat, alt, f107s, f107as, aps) 32 | # output is now of the shape (ndates, 1, 1, 1, 11) 33 | # Get rid of the single dimensions 34 | output = np.squeeze(output) 35 | 36 | # Lets get the percent variation from the annual mean for each variable 37 | variation = 100 * (output / output.mean(axis=0) - 1) 38 | 39 | _, ax = plt.subplots() 40 | for variable in pymsis.Variable: 41 | ax.plot(dates, variation[:, variable], label=variable.name) 42 | 43 | ax.legend( 44 | loc="upper center", bbox_to_anchor=(0.5, 1.15), fancybox=True, shadow=True, ncol=5 45 | ) 46 | ax.set_xlabel(f"Longitude: {lon}, Latitude: {lat}, Altitude: {alt} km") 47 | ax.set_ylabel("Difference from the daily mean (%)") 48 | ax.set_xlim(dates[0], dates[-1]) 49 | ax.xaxis.set_major_locator(mdates.HourLocator(interval=3)) 50 | ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M")) 51 | 52 | plt.show() 53 | -------------------------------------------------------------------------------- /examples/plot_surface.py: -------------------------------------------------------------------------------- 1 | """ 2 | Surface plot 3 | ------------ 4 | 5 | This example demonstrates how to calculate the 6 | quantities on a constant altitude plane. 7 | 8 | """ 9 | 10 | import matplotlib.pyplot as plt 11 | import numpy as np 12 | 13 | import pymsis 14 | 15 | 16 | lons = range(-180, 185, 5) 17 | lats = range(-90, 95, 5) 18 | alt = 200 19 | f107 = 150 20 | f107a = 150 21 | ap = 7 22 | # One years worth of data at the 12th hour every day 23 | date = np.datetime64("2003-01-01T12:00") 24 | aps = [[ap] * 7] 25 | 26 | output = pymsis.calculate(date, lons, lats, alt, f107, f107a, aps) 27 | # output is now of the shape (1, nlons, nlats, 1, 11) 28 | # Get rid of the single dimensions 29 | output = np.squeeze(output) 30 | 31 | _, ax = plt.subplots() 32 | xx, yy = np.meshgrid(lons, lats) 33 | mesh = ax.pcolormesh(xx, yy, output[:, :, pymsis.Variable.O2].T, shading="auto") 34 | plt.colorbar(mesh, label="Number density (/m$^3$)") 35 | 36 | ax.set_title(f"O$_2$ number density at {alt} km") 37 | ax.set_xlabel("Longitude") 38 | ax.set_ylabel("Latitude") 39 | 40 | plt.show() 41 | -------------------------------------------------------------------------------- /examples/plot_surface_animation.py: -------------------------------------------------------------------------------- 1 | """ 2 | Surface animation 3 | ----------------- 4 | 5 | This example demonstrates how to animate the surface plot. Showing 6 | the variations over the course of a year. 7 | 8 | """ 9 | 10 | import matplotlib 11 | import matplotlib.dates as mdates 12 | import matplotlib.pyplot as plt 13 | import numpy as np 14 | from matplotlib.animation import FuncAnimation 15 | 16 | import pymsis 17 | 18 | 19 | lons = range(-180, 185, 5) 20 | lats = range(-90, 95, 5) 21 | alt = 200 22 | f107 = 150 23 | f107a = 150 24 | ap = 4 25 | # Diurnal data 26 | dates = np.arange("2003-01-01", "2003-01-02", dtype="datetime64[30m]") 27 | ndates = len(dates) 28 | # (F107, F107a, ap) all need to be specified at the same length as dates 29 | f107s = [f107] * ndates 30 | f107as = [f107a] * ndates 31 | aps = [[ap] * 7] * ndates 32 | 33 | output = pymsis.calculate(dates, lons, lats, alt, f107s, f107as, aps) 34 | # output is now of the shape (ndates, nlons, nlats, 1, 11) 35 | # Get rid of the single dimensions 36 | output = np.squeeze(output) 37 | 38 | fig, (ax_time, ax_mesh) = plt.subplots( 39 | nrows=2, gridspec_kw={"height_ratios": [1, 4]}, constrained_layout=True 40 | ) 41 | xx, yy = np.meshgrid(lons, lats) 42 | vmin, vmax = 1e13, 8e13 43 | norm = matplotlib.colors.Normalize(vmin, vmax) 44 | mesh = ax_mesh.pcolormesh( 45 | xx, yy, output[0, :, :, pymsis.Variable.N].T, shading="auto", norm=norm 46 | ) 47 | plt.colorbar( 48 | mesh, label=f"N number density at {alt} km (/m$^3$)", orientation="horizontal" 49 | ) 50 | 51 | time_data = output[:, len(lons) // 2, len(lats) // 2, :] 52 | ax_time.plot(dates, time_data[:, pymsis.Variable.N], c="k") 53 | ax_time.set_xlim(dates[0], dates[-1]) 54 | ax_time.set_ylim(vmin, vmax) 55 | ax_time.xaxis.set_major_locator(mdates.HourLocator(interval=3)) 56 | ax_time.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M")) 57 | time_line = ax_time.axvline(dates[0], c="r") 58 | 59 | sun_loc = ( 60 | 180 61 | - (dates.astype("datetime64[s]") - dates.astype("datetime64[D]")).astype(float) 62 | / 86400 63 | * 360 64 | ) 65 | (sun,) = ax_mesh.plot(sun_loc[0], 0, ".", c="gold", markersize=15) 66 | 67 | date_string = dates[0].astype("O").strftime("%H:%M") 68 | title = ax_time.set_title(f"{date_string}") 69 | ax_mesh.set_xlabel("Longitude") 70 | ax_mesh.set_ylabel("Latitude") 71 | 72 | 73 | def update_surface(t): 74 | date_string = dates[t].astype("O").strftime("%H:%M") 75 | title.set_text(f"{date_string}") 76 | time_line.set_xdata([dates[t]]) 77 | mesh.set_array(output[t, :, :, pymsis.Variable.N].T) 78 | sun.set_data([sun_loc[t]], [0]) 79 | 80 | 81 | ani = FuncAnimation(fig, update_surface, frames=range(ndates), interval=25) 82 | 83 | plt.show() 84 | -------------------------------------------------------------------------------- /examples/plot_version_diff_altitude.py: -------------------------------------------------------------------------------- 1 | """ 2 | Version Differences (Altitude) 3 | ------------------------------ 4 | 5 | This example demonstrates how to calculate the 6 | percent change along an altitude profile between MSIS version 00 and 2. 7 | Additionally, we show the difference between densities at noon (solid) 8 | and midnight (dashed). 9 | 10 | """ 11 | 12 | import matplotlib.pyplot as plt 13 | import numpy as np 14 | 15 | import pymsis 16 | 17 | 18 | lon = 0 19 | lat = 70 20 | alts = np.linspace(0, 1000, 1000) 21 | f107 = 150 22 | f107a = 150 23 | ap = 7 24 | aps = [[ap] * 7] 25 | 26 | date = np.datetime64("2003-01-01T00:00") 27 | output_midnight2 = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps) 28 | output_midnight0 = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps, version=0) 29 | diff_midnight = (output_midnight2 - output_midnight0) / output_midnight0 * 100 30 | 31 | date = np.datetime64("2003-01-01T12:00") 32 | output_noon2 = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps) 33 | output_noon0 = pymsis.calculate(date, lon, lat, alts, f107, f107a, aps, version=0) 34 | diff_noon = (output_noon2 - output_noon0) / output_noon0 * 100 35 | 36 | 37 | # output is now of the shape (1, 1, 1, 1000, 11) 38 | # Get rid of the single dimensions 39 | diff_midnight = np.squeeze(diff_midnight) 40 | diff_noon = np.squeeze(diff_noon) 41 | 42 | _, ax = plt.subplots() 43 | for variable in pymsis.Variable: 44 | if variable.name in ("NO", "Total mass density", "Temperature"): 45 | # There is currently no NO data for earlier versions, 46 | # also ignore non-number densities 47 | continue 48 | (line,) = ax.plot(diff_midnight[:, variable], alts, linestyle="--") 49 | ax.plot(diff_noon[:, variable], alts, c=line.get_color(), label=variable.name) 50 | 51 | ax.legend( 52 | loc="upper center", bbox_to_anchor=(0.5, 1.15), fancybox=True, shadow=True, ncol=4 53 | ) 54 | ax.set_title(f"Longitude: {lon}, Latitude: {lat}") 55 | ax.set_xlim(-50, 50) 56 | ax.set_ylim(0, 1000) 57 | ax.set_xlabel("Change from MSIS-00 to MSIS2 (%)") 58 | ax.set_ylabel("Altitude (km)") 59 | 60 | plt.show() 61 | -------------------------------------------------------------------------------- /examples/plot_version_diff_surface.py: -------------------------------------------------------------------------------- 1 | """ 2 | Version Differences (Surface) 3 | ----------------------------- 4 | 5 | This example demonstrates how to calculate the 6 | percent change of all variables on a surface between 7 | MSIS version 00 and 2. 8 | 9 | """ 10 | 11 | import matplotlib as mpl 12 | import matplotlib.pyplot as plt 13 | import numpy as np 14 | 15 | import pymsis 16 | 17 | 18 | lons = range(-180, 185, 5) 19 | lats = range(-90, 95, 5) 20 | alt = 200 21 | f107 = 150 22 | f107a = 150 23 | ap = 7 24 | # One years worth of data at the 12th hour every day 25 | date = np.datetime64("2003-01-01T12:00") 26 | aps = [[ap] * 7] 27 | 28 | output2 = pymsis.calculate(date, lons, lats, alt, f107, f107a, aps) 29 | output0 = pymsis.calculate(date, lons, lats, alt, f107, f107a, aps, version=0) 30 | diff = (output2 - output0) / output0 * 100 31 | # diff is now of the shape (1, nlons, nlats, 1, 11) 32 | # Get rid of the single dimensions 33 | diff = np.squeeze(diff) 34 | 35 | fig, axarr = plt.subplots(nrows=3, ncols=3, constrained_layout=True, figsize=(8, 6)) 36 | xx, yy = np.meshgrid(lons, lats) 37 | norm = mpl.colors.Normalize(-50, 50) 38 | cmap = mpl.colormaps["RdBu_r"] 39 | for i, variable in enumerate(pymsis.Variable): 40 | if i > 8: 41 | break 42 | ax = axarr.flatten()[i] 43 | mesh = ax.pcolormesh( 44 | xx, yy, diff[:, :, variable].T, shading="auto", norm=norm, cmap=cmap 45 | ) 46 | ax.set_title(f"{variable.name}") 47 | 48 | plt.colorbar( 49 | mesh, ax=axarr, label="Change from MSIS-00 to MSIS2 (%)", orientation="horizontal" 50 | ) 51 | 52 | plt.show() 53 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'pymsis', 3 | 'c', 4 | # Note that the git commit hash cannot be added dynamically here 5 | version: run_command('python', '-m', 'setuptools_scm', check: true).stdout().strip(), 6 | license: 'MIT', 7 | meson_version: '>=1.2.99', 8 | default_options: [ 9 | 'fortran_std=legacy', 10 | ], 11 | ) 12 | 13 | cc = meson.get_compiler('c') 14 | 15 | # TODO: the below -Wno flags are all needed to silence warnings in 16 | # f2py-generated code. This should be fixed in f2py itself. 17 | _global_c_args = cc.get_supported_arguments( 18 | '-Wno-unused-but-set-variable', 19 | '-Wno-unused-function', 20 | '-Wno-conversion', 21 | '-Wno-misleading-indentation', 22 | '-Wno-incompatible-pointer-types', 23 | ) 24 | add_project_arguments(_global_c_args, language : 'c') 25 | 26 | 27 | # Adding at project level causes many spurious -lgfortran flags. 28 | add_languages('fortran', native: false) 29 | ff = meson.get_compiler('fortran') 30 | if ff.has_argument('-Wno-conversion') 31 | add_project_arguments('-Wno-conversion', language: 'fortran') 32 | endif 33 | 34 | # Default to optimization level 1 35 | # Some CI jobs fail with higher levels of optimization, so restrict this by default. 36 | add_project_arguments('-O1', language: 'fortran') 37 | 38 | is_windows = host_machine.system() == 'windows' 39 | # Platform detection 40 | is_mingw = is_windows and cc.get_id() == 'gcc' 41 | if is_windows 42 | # Need static libraries to get libgfortran 43 | gcc_link_args = ['-static'] 44 | if is_mingw 45 | add_project_link_arguments(gcc_link_args, language: ['c']) 46 | endif 47 | if meson.get_compiler('fortran').get_id() == 'gcc' 48 | add_project_link_arguments(gcc_link_args, language: ['fortran']) 49 | endif 50 | endif 51 | 52 | generate_f2pymod = files('tools/generate_f2pymod.py') 53 | 54 | # https://mesonbuild.com/Python-module.html 55 | py_mod = import('python') 56 | py3 = py_mod.find_installation(pure: false) 57 | py3_dep = py3.dependency() 58 | 59 | incdir_numpy = run_command(py3, 60 | ['-c', 'import os; os.chdir(".."); import numpy; print(numpy.get_include())'], 61 | check : true 62 | ).stdout().strip() 63 | 64 | inc_np = include_directories(incdir_numpy) 65 | 66 | incdir_f2py = incdir_numpy / '..' / '..' / 'f2py' / 'src' 67 | inc_f2py = include_directories(incdir_f2py) 68 | fortranobject_c = incdir_f2py / 'fortranobject.c' 69 | 70 | # Share this object across multiple modules. 71 | fortranobject_lib = static_library('_fortranobject', 72 | fortranobject_c, 73 | dependencies: py3_dep, 74 | include_directories: [inc_np, inc_f2py], 75 | ) 76 | fortranobject_dep = declare_dependency( 77 | link_with: fortranobject_lib, 78 | include_directories: [inc_np, inc_f2py], 79 | ) 80 | 81 | generate_f2pymod = files('tools/generate_f2pymod.py') 82 | 83 | # Get the external sources 84 | run_command(py3, 85 | files('tools/download_source.py'), 86 | check : true 87 | ) 88 | # Must build the src extensions first 89 | subdir('src') 90 | subdir('pymsis') 91 | subdir('tests') 92 | -------------------------------------------------------------------------------- /pymsis/__init__.py: -------------------------------------------------------------------------------- 1 | """Python interface to the MSIS codes.""" 2 | 3 | import importlib.metadata 4 | 5 | from pymsis.msis import Variable, calculate 6 | 7 | 8 | __version__ = importlib.metadata.version("pymsis") 9 | 10 | __all__ = ["Variable", "__version__", "calculate"] 11 | -------------------------------------------------------------------------------- /pymsis/meson.build: -------------------------------------------------------------------------------- 1 | py3.install_sources([ 2 | '__init__.py', 3 | 'msis.py', 4 | 'msis2.0.parm', 5 | 'msis21.parm', 6 | 'utils.py', 7 | ], 8 | subdir: 'pymsis' 9 | ) -------------------------------------------------------------------------------- /pymsis/msis.py: -------------------------------------------------------------------------------- 1 | """Interface for running and creating input for the MSIS models.""" 2 | 3 | import threading 4 | from enum import IntEnum 5 | from pathlib import Path 6 | 7 | import numpy as np 8 | import numpy.typing as npt 9 | 10 | from pymsis import msis00f, msis20f, msis21f # type: ignore 11 | from pymsis.utils import get_f107_ap 12 | 13 | 14 | # We need to point to the MSIS parameter file that was installed with the Python package 15 | _MSIS_PARAMETER_PATH = str(Path(__file__).resolve().parent) + "/" 16 | for lib in [msis00f, msis20f, msis21f]: 17 | # Store the previous options to avoid reinitializing the model 18 | # each iteration unless necessary 19 | lib._last_used_options = None 20 | # Anytime we call into the Fortran code, we need to lock 21 | # to avoid threading issues 22 | lib._lock = threading.Lock() 23 | 24 | 25 | class Variable(IntEnum): 26 | r""" 27 | Enumeration of variable data indices for the output from ``calculate()``. 28 | 29 | This can be used to access the data from the output arrays instead of having 30 | to remember the order of the output. For example, 31 | ``output_array[..., Variable.MASS_DENSITY]``. 32 | 33 | Attributes 34 | ---------- 35 | MASS_DENSITY 36 | Index of total mass density (kg/m\ :sup:`3`). 37 | N2 38 | Index of N2 number density (m\ :sup:`-3`). 39 | O2 40 | Index of O2 number density (m\ :sup:`-3`). 41 | O 42 | Index of O number density (m\ :sup:`-3`). 43 | HE 44 | Index of He number density (m\ :sup:`-3`). 45 | H 46 | Index of H number density (m\ :sup:`-3`). 47 | AR 48 | Index of Ar number density (m\ :sup:`-3`). 49 | N 50 | Index of N number density (m\ :sup:`-3`). 51 | ANOMALOUS_O 52 | Index of anomalous oxygen number density (m\ :sup:`-3`). 53 | NO 54 | Index of NO number density (m\ :sup:`-3`). 55 | TEMPERATURE 56 | Index of temperature (K). 57 | 58 | """ 59 | 60 | MASS_DENSITY = 0 61 | N2 = 1 62 | O2 = 2 63 | O = 3 # noqa: E741 (ambiguous name) 64 | HE = 4 65 | H = 5 66 | AR = 6 67 | N = 7 68 | ANOMALOUS_O = 8 69 | NO = 9 70 | TEMPERATURE = 10 71 | 72 | 73 | def calculate( 74 | dates: npt.ArrayLike, 75 | lons: npt.ArrayLike, 76 | lats: npt.ArrayLike, 77 | alts: npt.ArrayLike, 78 | f107s: npt.ArrayLike | None = None, 79 | f107as: npt.ArrayLike | None = None, 80 | aps: npt.ArrayLike | None = None, 81 | *, 82 | options: list[float] | None = None, 83 | version: float | str = 2.1, 84 | **kwargs: dict, 85 | ) -> npt.NDArray: 86 | r""" 87 | Call MSIS to calculate the atmosphere at the provided input points. 88 | 89 | **Satellite Fly-Through Mode:** 90 | If ndates is the same length as nlons, nlats, and nalts, then the 91 | input arrays are assumed to be aligned and no regridding is done. 92 | This is equivalent to a satellite fly-through, producing an output 93 | return shape of (ndates, 11). 94 | 95 | **Grid Mode:** 96 | If the input arrays have different lengths the data will be expanded 97 | in a grid-like fashion broadcasting to a larger shape than the input 98 | arrays. This is equivalent to a full atmosphere simulation where you 99 | want to calculate the data at every grid point. The output shape will 100 | be 5D (ndates, nlons, nlats, nalts, 11), with potentially single element 101 | dimensions if you have a single date, lon, lat, or alt. 102 | 103 | The output array can be indexed with the :class:`~.Variable` enum 104 | for easier access. ``output_array[..., Variable.MASS_DENSITY]`` 105 | returns the total mass density. 106 | 107 | Parameters 108 | ---------- 109 | dates : ArrayLike 110 | Dates and times of interest 111 | lons : ArrayLike 112 | Longitudes of interest 113 | lats : ArrayLike 114 | Latitudes of interest 115 | alts : ArrayLike 116 | Altitudes of interest 117 | f107s : ArrayLike, optional 118 | Daily F10.7 of the previous day for the given date(s) 119 | f107as : ArrayLike, optional 120 | F10.7 running 81-day average centered on the given date(s) 121 | aps : ArrayLike, optional 122 | | Ap for the given date(s), (1-6 only used if ``geomagnetic_activity=-1``) 123 | | (0) Daily Ap 124 | | (1) 3 hr ap index for current time 125 | | (2) 3 hr ap index for 3 hrs before current time 126 | | (3) 3 hr ap index for 6 hrs before current time 127 | | (4) 3 hr ap index for 9 hrs before current time 128 | | (5) Average of eight 3 hr ap indices from 12 to 33 hrs 129 | | prior to current time 130 | | (6) Average of eight 3 hr ap indices from 36 to 57 hrs 131 | | prior to current time 132 | options : ArrayLike[25, float], optional 133 | A list of options (switches) to the model, if options is passed 134 | all keyword arguments specifying individual options will be ignored. 135 | version : Number or string, default: 2.1 136 | MSIS version number, one of (0, 2.0, 2.1). 137 | **kwargs : dict 138 | Single options for the switches can be defined through keyword arguments. 139 | For example, calculate(..., geomagnetic_activity=-1) will set the geomagnetic 140 | activity switch to -1 (storm-time ap mode). 141 | 142 | Returns 143 | ------- 144 | ndarray (ndates, nlons, nlats, nalts, 11) or (ndates, 11) 145 | | The data calculated at each grid point: 146 | | [Total mass density (kg/m\ :sup:`3`), 147 | | N2 # density (m\ :sup:`-3`), 148 | | O2 # density (m\ :sup:`-3`), 149 | | O # density (m\ :sup:`-3`), 150 | | He # density (m\ :sup:`-3`), 151 | | H # density (m\ :sup:`-3`), 152 | | Ar # density (m\ :sup:`-3`), 153 | | N # density (m\ :sup:`-3`), 154 | | Anomalous oxygen # density (m\ :sup:`-3`), 155 | | NO # density (m\ :sup:`-3`), 156 | | Temperature (K)] 157 | 158 | Other Parameters 159 | ---------------- 160 | f107 : float 161 | Account for F10.7 variations 162 | time_independent : float 163 | Account for time variations 164 | symmetrical_annual : float 165 | Account for symmetrical annual variations 166 | symmetrical_semiannual : float 167 | Account for symmetrical semiannual variations 168 | asymmetrical_annual : float 169 | Account for asymmetrical annual variations 170 | asymmetrical_semiannual : float 171 | Account for asymmetrical semiannual variations 172 | diurnal : float 173 | Account for diurnal variations 174 | semidiurnal : float 175 | Account for semidiurnal variations 176 | geomagnetic_activity : float 177 | Account for geomagnetic activity 178 | (1 = Daily Ap mode, -1 = Storm-time Ap mode) 179 | all_ut_effects : float 180 | Account for all UT/longitudinal effects 181 | longitudinal : float 182 | Account for longitudinal effects 183 | mixed_ut_long : float 184 | Account for UT and mixed UT/longitudinal effects 185 | mixed_ap_ut_long : float 186 | Account for mixed Ap, UT, and longitudinal effects 187 | terdiurnal : float 188 | Account for terdiurnal variations 189 | 190 | Notes 191 | ----- 192 | 1. The 10.7 cm radio flux is at the Sun-Earth distance, 193 | not the radio flux at 1 AU. 194 | 2. aps[1:] are only used when ``geomagnetic_activity=-1``. 195 | 196 | """ 197 | num_options = 25 198 | if options is None: 199 | options = create_options(**kwargs) # type: ignore 200 | elif len(options) != num_options: 201 | raise ValueError(f"options needs to be a list of length {num_options}") 202 | 203 | input_shape, input_data = create_input(dates, lons, lats, alts, f107s, f107as, aps) 204 | 205 | if np.any(~np.isfinite(input_data)): 206 | raise ValueError( 207 | "Input data has non-finite values, all input data must be valid." 208 | ) 209 | 210 | # convert to string version 211 | version = str(version) 212 | # Select the underlying MSIS library based on the version 213 | match version: 214 | case "0" | "00": 215 | msis_lib = msis00f 216 | case "2.0": 217 | msis_lib = msis20f 218 | case "2.1" | "2": 219 | # generic 2 defaults to most recent available 220 | msis_lib = msis21f 221 | case _: 222 | raise ValueError( 223 | f"The MSIS version selected: {version} is not " 224 | "one of the valid version numbers: (0, 2.0, 2.1)" 225 | ) 226 | 227 | with msis_lib._lock: 228 | # Only reinitialize the model if the options have changed 229 | if msis_lib._last_used_options != options: 230 | msis_lib.pyinitswitch(options, parmpath=_MSIS_PARAMETER_PATH) 231 | msis_lib._last_used_options = options 232 | 233 | output = msis_lib.pymsiscalc( 234 | input_data[:, 0], 235 | input_data[:, 1], 236 | input_data[:, 2], 237 | input_data[:, 3], 238 | input_data[:, 4], 239 | input_data[:, 5], 240 | input_data[:, 6], 241 | input_data[:, 7:], 242 | ) 243 | 244 | # The Fortran code puts 9.9e-38 in as NaN 245 | # or 9.99e-38, or 9.999e-38, so lets just bound the 9s 246 | output[(output >= 9.9e-38) & (output < 1e-37)] = np.nan # noqa: PLR2004 247 | 248 | return output.reshape(*input_shape, 11) 249 | 250 | 251 | # For backwards compatibility export the old name here 252 | run = calculate 253 | 254 | 255 | def create_options( 256 | f107: float = 1, 257 | time_independent: float = 1, 258 | symmetrical_annual: float = 1, 259 | symmetrical_semiannual: float = 1, 260 | asymmetrical_annual: float = 1, 261 | asymmetrical_semiannual: float = 1, 262 | diurnal: float = 1, 263 | semidiurnal: float = 1, 264 | geomagnetic_activity: float = 1, 265 | all_ut_effects: float = 1, 266 | longitudinal: float = 1, 267 | mixed_ut_long: float = 1, 268 | mixed_ap_ut_long: float = 1, 269 | terdiurnal: float = 1, 270 | ) -> list[float]: 271 | """ 272 | Create the options list based on keyword argument choices. 273 | 274 | Defaults to all 1's for the input options. 275 | 276 | Parameters 277 | ---------- 278 | f107 : float 279 | Account for F10.7 variations 280 | time_independent : float 281 | Account for time variations 282 | symmetrical_annual : float 283 | Account for symmetrical annual variations 284 | symmetrical_semiannual : float 285 | Account for symmetrical semiannual variations 286 | asymmetrical_annual : float 287 | Account for asymmetrical annual variations 288 | asymmetrical_semiannual : float 289 | Account for asymmetrical semiannual variations 290 | diurnal : float 291 | Account for diurnal variations 292 | semidiurnal : float 293 | Account for semidiurnal variations 294 | geomagnetic_activity : float 295 | Account for geomagnetic activity 296 | (1 = Daily Ap mode, -1 = Storm-time Ap mode) 297 | all_ut_effects : float 298 | Account for all UT/longitudinal effects 299 | longitudinal : float 300 | Account for longitudinal effects 301 | mixed_ut_long : float 302 | Account for UT and mixed UT/longitudinal effects 303 | mixed_ap_ut_long : float 304 | Account for mixed Ap, UT, and longitudinal effects 305 | terdiurnal : float 306 | Account for terdiurnal variations 307 | 308 | Returns 309 | ------- 310 | list 311 | 25 options as a list ready for msis2 input 312 | """ 313 | options = [ 314 | f107, 315 | time_independent, 316 | symmetrical_annual, 317 | symmetrical_semiannual, 318 | asymmetrical_annual, 319 | asymmetrical_semiannual, 320 | diurnal, 321 | semidiurnal, 322 | geomagnetic_activity, 323 | all_ut_effects, 324 | longitudinal, 325 | mixed_ut_long, 326 | mixed_ap_ut_long, 327 | terdiurnal, 328 | ] + [1] * 11 329 | return options 330 | 331 | 332 | def create_input( 333 | dates: npt.ArrayLike, 334 | lons: npt.ArrayLike, 335 | lats: npt.ArrayLike, 336 | alts: npt.ArrayLike, 337 | f107s: npt.ArrayLike | None = None, 338 | f107as: npt.ArrayLike | None = None, 339 | aps: npt.ArrayLike | None = None, 340 | ) -> tuple[tuple, npt.NDArray]: 341 | """ 342 | Combine all input values into a single flattened array. 343 | 344 | Parameters 345 | ---------- 346 | dates : ArrayLike 347 | Dates and times of interest 348 | lons : ArrayLike 349 | Longitudes of interest 350 | lats : ArrayLike 351 | Latitudes of interest 352 | alts : ArrayLike 353 | Altitudes of interest 354 | f107s : ArrayLike, optional 355 | F107 values for the previous day of the given date(s) 356 | f107as : ArrayLike, optional 357 | F107 running 81-day average for the given date(s) 358 | aps : ArrayLike, optional 359 | Ap for the given date(s) 360 | 361 | Returns 362 | ------- 363 | tuple (shape, flattened_input) 364 | The shape of the data as a tuple (ndates, nlons, nlats, nalts) and 365 | the flattened version of the input data 366 | (ndates*nlons*nlats*nalts, 14). If the input array was preflattened 367 | (ndates == nlons == nlats == nalts), then the shape is (ndates,). 368 | """ 369 | # Turn everything into arrays 370 | dates_arr: npt.NDArray[np.datetime64] = np.atleast_1d( 371 | np.array(dates, dtype=np.datetime64) 372 | ) 373 | dyear: npt.NDArray[np.datetime64] = ( 374 | dates_arr.astype("datetime64[D]") - dates_arr.astype("datetime64[Y]") 375 | ).astype(float) + 1 # DOY 1-366 376 | dseconds: npt.NDArray[np.datetime64] = ( 377 | dates_arr.astype("datetime64[s]") - dates_arr.astype("datetime64[D]") 378 | ).astype(float) 379 | # TODO: Make it a continuous day of year? 380 | # The new code mentions it should be and accepts float, but the 381 | # regression tests indicate it should still be integer DOY 382 | # dyear += dseconds/86400 383 | lons = np.atleast_1d(lons) 384 | lats = np.atleast_1d(lats) 385 | alts = np.atleast_1d(alts) 386 | 387 | # If any of the geomagnetic data wasn't specified, we will default 388 | # to getting it with the utility functions. 389 | if f107s is None or f107as is None or aps is None: 390 | data = get_f107_ap(dates_arr) 391 | # Only update the values that were None 392 | if f107s is None: 393 | f107s = data[0] 394 | if f107as is None: 395 | f107as = data[1] 396 | if aps is None: 397 | aps = data[2] 398 | 399 | f107s = np.atleast_1d(f107s) 400 | f107as = np.atleast_1d(f107as) 401 | aps = np.atleast_1d(aps) 402 | 403 | ndates = len(dates_arr) 404 | nlons = len(lons) 405 | nlats = len(lats) 406 | nalts = len(alts) 407 | 408 | if not (ndates == len(f107s) == len(f107as) == len(aps)): 409 | raise ValueError( 410 | f"The length of dates ({ndates}), f107s " 411 | f"({len(f107s)}), f107as ({len(f107as)}), " 412 | f"and aps ({len(aps)}) must all be equal" 413 | ) 414 | 415 | if ndates == nlons == nlats == nalts: 416 | # This means the data came in preflattened, from a satellite 417 | # trajectory for example, where we don't want to make a grid 418 | # out of the input data, we just want to stack it together. 419 | # Create an array to hold all of the data 420 | # F-ordering so we can pass by reference to the Fortran code 421 | arr = np.empty((ndates, 14), dtype=np.float32, order="F") 422 | arr[:, 0] = dyear 423 | arr[:, 1] = dseconds 424 | arr[:, 2] = lons 425 | arr[:, 3] = lats 426 | arr[:, 4] = alts 427 | arr[:, 5] = f107s 428 | arr[:, 6] = f107as 429 | arr[:, 7:] = aps 430 | return (ndates,), arr 431 | 432 | # Use broadcasting to fill each column directly 433 | # This is much faster than creating an indices array and then 434 | # using that to fill the columns 435 | arr = np.empty((ndates * nlons * nlats * nalts, 14), dtype=np.float32, order="F") 436 | arr[:, 0] = np.repeat(dyear, nlons * nlats * nalts) # dyear 437 | arr[:, 1] = np.repeat(dseconds, nlons * nlats * nalts) # dseconds 438 | arr[:, 2] = np.tile(np.repeat(lons, nlats * nalts), ndates) # lons 439 | arr[:, 3] = np.tile(np.repeat(lats, nalts), ndates * nlons) # lats 440 | arr[:, 4] = np.tile(alts, ndates * nlons * nlats) # alts 441 | arr[:, 5] = np.repeat(f107s, nlons * nlats * nalts) # f107s 442 | arr[:, 6] = np.repeat(f107as, nlons * nlats * nalts) # f107as 443 | arr[:, 7:] = np.repeat(aps, nlons * nlats * nalts, axis=0) # aps 444 | 445 | return (ndates, nlons, nlats, nalts), arr 446 | -------------------------------------------------------------------------------- /pymsis/utils.py: -------------------------------------------------------------------------------- 1 | """Utilities for obtaining input datasets.""" 2 | 3 | import urllib.request 4 | import warnings 5 | from io import BytesIO 6 | from pathlib import Path 7 | 8 | import numpy as np 9 | import numpy.typing as npt 10 | 11 | import pymsis 12 | 13 | 14 | _DATA_FNAME: str = "SW-All.csv" 15 | _F107_AP_URL: str = f"https://celestrak.org/SpaceData/{_DATA_FNAME}" 16 | _F107_AP_PATH: Path = Path(pymsis.__file__).parent / _DATA_FNAME 17 | _DATA: dict[str, npt.NDArray] | None = None 18 | 19 | 20 | def download_f107_ap() -> None: 21 | """ 22 | Download the latest ap and F10.7 values. 23 | 24 | The file is downloaded into the installed package location, keeping 25 | the same filename as the data source: ``SW-All.csv``. 26 | This routine can be called to update the data as well if you would like to 27 | use newer data since the last time you downloaded the file. 28 | 29 | Notes 30 | ----- 31 | The default data provider is CelesTrak, which gets data from other 32 | sources and interpolates or predicts missing values to make data easier to 33 | work with. A warning is emitted when the interpolation or prediction 34 | occurs, but it is up to the user to verify the Ap and F10.7 data that is used 35 | is correct for their application. 36 | Use of this geomagnetic data should cite the references [1]_ [2]_. 37 | 38 | References 39 | ---------- 40 | .. [1] CelesTrak. https://celestrak.org/SpaceData/ 41 | .. [2] Matzka, J., Stolle, C., Yamazaki, Y., Bronkalla, O. and Morschhauser, A., 42 | 2021. The geomagnetic Kp index and derived indices of geomagnetic activity. 43 | Space Weather, https://doi.org/10.1029/2020SW002641 44 | """ 45 | warnings.warn(f"Downloading ap and F10.7 data from {_F107_AP_URL}") 46 | req = urllib.request.urlopen(_F107_AP_URL) 47 | with _F107_AP_PATH.open("wb") as f: 48 | f.write(req.read()) 49 | 50 | 51 | def _load_f107_ap_data() -> dict[str, npt.NDArray]: 52 | """Load data from disk, if it isn't present go out and download it first.""" 53 | if not _F107_AP_PATH.exists(): 54 | download_f107_ap() 55 | 56 | dtype = { 57 | "names": ( 58 | "date", 59 | "ap1", 60 | "ap2", 61 | "ap3", 62 | "ap4", 63 | "ap5", 64 | "ap6", 65 | "ap7", 66 | "ap8", 67 | "Ap", 68 | "f107", 69 | "f107-type", 70 | "f107a", 71 | ), 72 | "formats": ( 73 | "datetime64[D]", 74 | "i4", 75 | "i4", 76 | "i4", 77 | "i4", 78 | "i4", 79 | "i4", 80 | "i4", 81 | "i4", 82 | "i4", 83 | "f8", 84 | "S3", 85 | "f8", 86 | ), 87 | } 88 | usecols = (0, 12, 13, 14, 15, 16, 17, 18, 19, 20, 24, 26, 27) 89 | 90 | # Use a buffer to read in and load so we can quickly get rid of 91 | # the extra "PRD" lines at the end of the file (unknown length 92 | # so we can't just go back in line lengths) 93 | with _F107_AP_PATH.open() as fin: 94 | with BytesIO() as fout: 95 | for line in fin: 96 | if "PRM" in line: 97 | # We don't want the monthly predicted values 98 | continue 99 | if ",,,,,,,," in line: 100 | # We don't want lines with missing values 101 | continue 102 | fout.write(line.encode("utf-8")) 103 | fout.seek(0) 104 | arr = np.loadtxt( 105 | fout, delimiter=",", dtype=dtype, usecols=usecols, skiprows=1 106 | ) # type: ignore 107 | 108 | # transform each day's 8 3-hourly ap values into a single column 109 | ap = np.empty(len(arr) * 8, dtype=float) 110 | daily_ap = arr["Ap"].astype(float) 111 | dates = np.repeat(arr["date"], 8).astype("datetime64[m]") 112 | for i in range(8): 113 | ap[i::8] = arr[f"ap{i + 1}"] 114 | dates[i::8] += i * np.timedelta64(3, "h") 115 | 116 | # data file has missing values as negatives 117 | ap[ap < 0] = np.nan 118 | daily_ap[daily_ap < 0] = np.nan 119 | # There are also some non-physical f10.7 values 120 | # F10.7 > 400 is unrealistic indicating a solar radio burst 121 | solar_radio_burst = 400 122 | bad_f107 = (arr["f107"] <= 0) | (arr["f107"] > solar_radio_burst) 123 | # Set the data to the 81-day average as a temporary fix 124 | arr["f107"][bad_f107] = arr["f107a"][bad_f107] 125 | # flag it as interpolated or predicted values so we warn the user 126 | arr["f107-type"][bad_f107] = b"INT" 127 | 128 | ap_data = np.ones((len(ap), 7)) * np.nan 129 | # daily Ap 130 | # repeat each value for each 3-hourly interval 131 | ap_data[:, 0] = np.repeat(arr["Ap"], 8) 132 | # current 3-hour ap index value 133 | ap_data[:, 1] = ap 134 | # 3-hours before current time 135 | ap_data[1:, 2] = ap[:-1] 136 | # 6-hours before current time 137 | ap_data[2:, 3] = ap[:-2] 138 | # 9-hours before current time 139 | ap_data[3:, 4] = ap[:-3] 140 | # average of 12-33 hours prior to current time 141 | # calculate the rolling mean with a convolution window length of 8 142 | # we want to offset by 4 because we are starting at the 12th hour, 143 | # then adjust for the valid mode with an edge-length of (8-1) 144 | rolling_mean = np.convolve(ap, np.ones(8) / 8, mode="valid") 145 | ap_data[4 + 8 - 1 :, 5] = rolling_mean[:-4] 146 | # average of 36-57 hours prior to current time 147 | # Use the same window length as before, just shifting the fill values 148 | ap_data[4 + 16 - 1 :, 6] = rolling_mean[: -(4 + 8)] 149 | 150 | # F107 Data is needed from the previous day, F107a centered on the current day 151 | f107_data = np.ones(len(arr), dtype=float) * np.nan 152 | f107a_data = np.ones(len(arr), dtype=float) * np.nan 153 | f107_data[1:] = arr["f107"][:-1] 154 | f107a_data = arr["f107a"] 155 | # So that we can warn the user that this F107 data was interpolated or predicted 156 | interpolated = b"INT" 157 | predicted = b"PRD" 158 | warn_data = (arr["f107-type"] == interpolated) | (arr["f107-type"] == predicted) 159 | # Because we use the F107 from the day before, we need to shift the warning 160 | # to the following day when we would actually use the value 161 | warn_data[1:] = warn_data[:-1] 162 | 163 | # Set the global module-level data variable 164 | data = { 165 | "dates": dates, 166 | "ap": ap_data, 167 | "f107": f107_data, 168 | "f107a": f107a_data, 169 | "warn_data": warn_data, 170 | } 171 | globals()["_DATA"] = data 172 | return data 173 | 174 | 175 | def get_f107_ap(dates: npt.ArrayLike) -> tuple[npt.NDArray, npt.NDArray, npt.NDArray]: 176 | """ 177 | Retrieve the F10.7 and ap data needed to run msis for the given times. 178 | 179 | The MSIS model uses historical F10.7 and ap values for the calculations, 180 | and these are also derived quantities. 181 | 182 | Parameters 183 | ---------- 184 | dates : datetime-like or sequence of datetimes 185 | times of interest to get the proper ap and F10.7 values for 186 | 187 | Returns 188 | ------- 189 | tuple (f107 [n], f107a [n], ap [n x 7]) 190 | Three arrays containing the f107, f107a, and ap values 191 | corresponding to the input times of length n. 192 | 193 | f107 : np.ndarray 194 | Daily F10.7 of the previous day for the given date(s) 195 | f107a : np.ndarray 196 | F10.7 running 81-day average centered on the given date(s) 197 | ap : np.ndarray 198 | | Ap for the given date(s), (1-6 only used if ``geomagnetic_activity=-1``) 199 | | (0) Daily Ap 200 | | (1) 3 hr ap index for current time 201 | | (2) 3 hr ap index for 3 hrs before current time 202 | | (3) 3 hr ap index for 6 hrs before current time 203 | | (4) 3 hr ap index for 9 hrs before current time 204 | | (5) Average of eight 3 hr ap indices from 12 to 33 hrs 205 | | prior to current time 206 | | (6) Average of eight 3 hr ap indices from 36 to 57 hrs 207 | | prior to current time 208 | """ 209 | dates = np.asarray(dates, dtype=np.datetime64) 210 | data = _DATA or _load_f107_ap_data() 211 | 212 | data_start = data["dates"][0] 213 | data_end = data["dates"][-1] 214 | date_offsets = dates - data_start 215 | # daily index values 216 | daily_indices = date_offsets.astype("timedelta64[D]").astype(int) 217 | # 3-hourly index values 218 | ap_indices = date_offsets.astype("timedelta64[h]").astype(int) // 3 219 | 220 | if np.any((ap_indices < 0) | (ap_indices >= len(data["ap"]))): 221 | # We are requesting data outside of the valid range 222 | raise ValueError( 223 | "The geomagnetic data is not available for these dates. " 224 | f"Dates should be between {data_start} and " 225 | f"{data_end}." 226 | ) 227 | 228 | f107 = np.take(data["f107"], daily_indices) 229 | f107a = np.take(data["f107a"], daily_indices) 230 | ap = np.take(data["ap"], ap_indices, axis=0) 231 | warn_or_not = np.take(data["warn_data"], daily_indices) 232 | # TODO: Do we want to warn if any values within 81 days of a point are used? 233 | # i.e. if any of the f107a values were interpolated or predicted 234 | if np.any(warn_or_not): 235 | warnings.warn( 236 | "There is data that was either interpolated or predicted " 237 | "(not observed), use at your own risk." 238 | ) 239 | return f107, f107a, ap 240 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | build-backend = "mesonpy" 3 | requires = [ 4 | "meson-python>=0.13.1", 5 | "numpy>=2", 6 | "setuptools_scm>=7", 7 | ] 8 | 9 | [project] 10 | name = "pymsis" 11 | dynamic = ["version"] 12 | description = "A Python wrapper around the NRLMSIS model." 13 | readme = "README.md" 14 | license = {file = "LICENSE"} 15 | keyword = ["MSIS2", "NRLMSIS"] 16 | authors = [{name = "Greg Lucas", email = "greg.lucas@lasp.colorado.edu"}] 17 | classifiers = [ 18 | "Development Status :: 4 - Beta", 19 | "Intended Audience :: Developers", 20 | "License :: OSI Approved :: MIT License", 21 | "Natural Language :: English", 22 | "Programming Language :: Python :: 3", 23 | "Programming Language :: Python :: 3.10", 24 | "Programming Language :: Python :: 3.11", 25 | "Programming Language :: Python :: 3.12", 26 | "Programming Language :: Python :: 3.13", 27 | "Topic :: Software Development", 28 | "Topic :: Scientific/Engineering", 29 | "Operating System :: Microsoft :: Windows", 30 | "Operating System :: POSIX", 31 | "Operating System :: Unix", 32 | "Operating System :: MacOS", 33 | ] 34 | requires-python = ">=3.10" 35 | dependencies = ["numpy>=1.23"] 36 | 37 | [project.optional-dependencies] 38 | test = [ 39 | "pytest", 40 | "pytest-cov", 41 | ] 42 | doc = [ 43 | "matplotlib", 44 | "myst-parser", 45 | "sphinx", 46 | "sphinx_gallery", 47 | "pydata-sphinx-theme", 48 | ] 49 | dev = [ 50 | "pymsis[doc,test]", 51 | "mypy", 52 | "pre-commit", 53 | "ruff", 54 | ] 55 | 56 | [project.urls] 57 | homepage = "https://swxtrec.github.io/pymsis/" 58 | repository = "https://github.com/swxtrec/pymsis" 59 | 60 | [tool.setuptools_scm] 61 | version_scheme = "release-branch-semver" 62 | local_scheme = "node-and-date" 63 | parentdir_prefix_version = "pymsis-" 64 | fallback_version = "0.0+UNKNOWN" 65 | 66 | [tool.pytest.ini_options] 67 | junit_family = "xunit2" 68 | testpaths = [ 69 | "tests", 70 | ] 71 | addopts = [ 72 | "--import-mode=importlib", 73 | ] 74 | 75 | [tool.cibuildwheel] 76 | # skip Python <3.10 77 | # skip 32 bit windows and linux builds for lack of numpy wheels 78 | skip = "cp36* cp37* cp38* cp39* *-win32 *_i686" 79 | free-threaded-support = true 80 | test-requires = "pytest" 81 | test-command = "pytest --pyargs pymsis" 82 | 83 | [tool.ruff] 84 | target-version = "py310" 85 | lint.select = ["B", "D", "E", "F", "I", "N", "S", "W", "PL", "PT", "UP", "RUF", "ANN"] 86 | lint.ignore = ["B028", "D203", "D212", "PLR0913", "S310"] 87 | 88 | [tool.ruff.lint.per-file-ignores] 89 | "examples/*" = ["ANN", "D", "PLR2004"] 90 | "tests/*" = ["ANN", "D", "S"] 91 | "tools/*" = ["ANN", "S"] 92 | ".github/*" = ["S"] 93 | 94 | [tool.ruff.lint.pydocstyle] 95 | convention = "numpy" 96 | 97 | [tool.ruff.lint.isort] 98 | lines-after-imports = 2 99 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | # msis00 2 | msis00_module = custom_target('msis00_module', 3 | output: 'msis00fmodule.c', 4 | input: ['wrappers/msis00.pyf'], 5 | command: [py3, generate_f2pymod, '@INPUT@', '-o', '@OUTDIR@'] 6 | ) 7 | 8 | py3.extension_module('msis00f', 9 | ['msis00/NRLMSISE-00.FOR', 10 | 'wrappers/msis00.F90', 11 | msis00_module, 12 | ], 13 | include_directories: [inc_np, inc_f2py], 14 | dependencies: fortranobject_dep, 15 | install: true, 16 | link_language: 'fortran', 17 | subdir: 'pymsis' 18 | ) 19 | 20 | # msis2.0 21 | msis20_module = custom_target('msis20_module', 22 | output: 'msis20fmodule.c', 23 | input: ['wrappers/msis20.pyf'], 24 | command: [py3, generate_f2pymod, '@INPUT@', '-o', '@OUTDIR@'] 25 | ) 26 | 27 | py3.extension_module('msis20f', 28 | ['wrappers/msis2.F90', 29 | 'msis2.0/msis_constants.F90', 30 | 'msis2.0/msis_init.F90', 31 | 'msis2.0/msis_gfn.F90', 32 | 'msis2.0/msis_tfn.F90', 33 | 'msis2.0/alt2gph.F90', 34 | 'msis2.0/msis_dfn.F90', 35 | 'msis2.0/msis_calc.F90', 36 | msis20_module, 37 | ], 38 | include_directories: [inc_np, inc_f2py], 39 | dependencies: fortranobject_dep, 40 | install: true, 41 | link_language: 'fortran', 42 | subdir: 'pymsis' 43 | ) 44 | 45 | # msis2.1 46 | msis21_module = custom_target('msis21_module', 47 | output: 'msis21fmodule.c', 48 | input: ['wrappers/msis21.pyf'], 49 | command: [py3, generate_f2pymod, '@INPUT@', '-o', '@OUTDIR@'] 50 | ) 51 | 52 | py3.extension_module('msis21f', 53 | ['wrappers/msis2.F90', 54 | 'msis2.1/msis_constants.F90', 55 | 'msis2.1/msis_init.F90', 56 | 'msis2.1/msis_gfn.F90', 57 | 'msis2.1/msis_utils.F90', 58 | 'msis2.1/msis_tfn.F90', 59 | 'msis2.1/msis_dfn.F90', 60 | 'msis2.1/msis_calc.F90', 61 | msis21_module, 62 | ], 63 | include_directories: [inc_np, inc_f2py], 64 | dependencies: fortranobject_dep, 65 | install: true, 66 | link_language: 'fortran', 67 | subdir: 'pymsis' 68 | ) -------------------------------------------------------------------------------- /src/msis00/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SWxTREC/pymsis/cdd4043395bf3485fe9295afe862e0a607a0e7fb/src/msis00/.gitkeep -------------------------------------------------------------------------------- /src/msis2.0/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SWxTREC/pymsis/cdd4043395bf3485fe9295afe862e0a607a0e7fb/src/msis2.0/.gitkeep -------------------------------------------------------------------------------- /src/msis2.1/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SWxTREC/pymsis/cdd4043395bf3485fe9295afe862e0a607a0e7fb/src/msis2.1/.gitkeep -------------------------------------------------------------------------------- /src/wrappers/msis00.F90: -------------------------------------------------------------------------------- 1 | subroutine pyinitswitch(switch_legacy, parmpath) 2 | implicit none 3 | 4 | real(4), intent(in), optional :: switch_legacy(1:25) !Legacy switch array 5 | ! Here for compatibility with MSIS2 even though it is unused 6 | character(len=*), intent(in), optional :: parmpath !Path to parameter file 7 | 8 | call tselec(switch_legacy) 9 | call meters(.TRUE.) 10 | 11 | return 12 | end subroutine pyinitswitch 13 | 14 | subroutine pymsiscalc(day, utsec, lon, lat, z, sflux, sfluxavg, ap, output, n) 15 | implicit none 16 | 17 | integer, intent(in) :: n 18 | real, intent(in) :: day(n) 19 | real, intent(in) :: utsec(n) 20 | real, intent(in) :: lon(n) 21 | real, intent(in) :: lat(n) 22 | real, intent(in) :: z(n) 23 | real, intent(in) :: sflux(n) 24 | real, intent(in) :: sfluxavg(n) 25 | real, intent(in) :: ap(n, 1:7) 26 | real, intent(out) :: output(n, 1:11) 27 | 28 | integer :: i 29 | real :: t(2), d(9), lon_tmp ! Temporary to swap dimensions 30 | 31 | do i=1, n 32 | if (lon(i) < 0) then 33 | lon_tmp = lon(i) + 360 34 | else 35 | lon_tmp = lon(i) 36 | endif 37 | call gtd7d(10000 + FLOOR(day(i)), utsec(i), z(i), lat(i), lon_tmp, & 38 | utsec(i)/3600. + lon_tmp/15., sfluxavg(i), & 39 | sflux(i), ap(i, :), 48, d, t) 40 | ! O, H, and N are set to zero below 72.5 km 41 | ! Change them to NaN instead 42 | if(z(i) < 72.5) then 43 | d(2) = 9.99e-38 ! NaN 44 | d(7) = 9.99e-38 ! NaN 45 | d(8) = 9.99e-38 ! NaN 46 | endif 47 | ! These mappings are to go from MSIS00 locations to MSIS2 locations 48 | output(i, 1) = d(6) 49 | output(i, 2) = d(3) 50 | output(i, 3) = d(4) 51 | output(i, 4) = d(2) 52 | output(i, 5) = d(1) 53 | output(i, 6) = d(7) 54 | output(i, 7) = d(5) 55 | output(i, 8) = d(8) 56 | output(i, 9) = d(9) 57 | output(i, 10) = 9.99e-38 ! NaN 58 | output(i, 11) = t(2) 59 | enddo 60 | 61 | return 62 | end subroutine pymsiscalc 63 | 64 | 65 | ! Deprecated forms of the above functions 66 | subroutine pytselec(switch_legacy) 67 | implicit none 68 | 69 | real(4), intent(in), optional :: switch_legacy(1:25) !Legacy switch array 70 | 71 | WRITE(6, *) 'Warning: pytselec is deprecated and will be removed in a future version. Use pyinitswitch instead.' 72 | call tselec(switch_legacy) 73 | call meters(.TRUE.) 74 | 75 | return 76 | end subroutine pytselec 77 | 78 | subroutine pygtd7d(day, utsec, lon, lat, z, sflux, sfluxavg, ap, output, n) 79 | 80 | implicit none 81 | 82 | integer, intent(in) :: n 83 | real, intent(in) :: day(n) 84 | real, intent(in) :: utsec(n) 85 | real, intent(in) :: lon(n) 86 | real, intent(in) :: lat(n) 87 | real, intent(in) :: z(n) 88 | real, intent(in) :: sflux(n) 89 | real, intent(in) :: sfluxavg(n) 90 | real, intent(in) :: ap(n, 1:7) 91 | real, intent(out) :: output(n, 1:11) 92 | 93 | integer :: i 94 | real :: t(2), d(9), lon_tmp ! Temporary to swap dimensions 95 | 96 | WRITE(6, *) 'Warning: pygtd7d is deprecated and will be removed in a future version. Use pymsiscalc instead.' 97 | do i=1, n 98 | if (lon(i) < 0) then 99 | lon_tmp = lon(i) + 360 100 | else 101 | lon_tmp = lon(i) 102 | endif 103 | call gtd7d(10000 + FLOOR(day(i)), utsec(i), z(i), lat(i), lon_tmp, & 104 | utsec(i)/3600. + lon_tmp/15., sfluxavg(i), & 105 | sflux(i), ap(i, :), 48, d, t) 106 | ! O, H, and N are set to zero below 72.5 km 107 | ! Change them to NaN instead 108 | if(z(i) < 72.5) then 109 | d(2) = 9.99e-38 ! NaN 110 | d(7) = 9.99e-38 ! NaN 111 | d(8) = 9.99e-38 ! NaN 112 | endif 113 | ! These mappings are to go from MSIS00 locations to MSIS2 locations 114 | output(i, 1) = d(6) 115 | output(i, 2) = d(3) 116 | output(i, 3) = d(4) 117 | output(i, 4) = d(2) 118 | output(i, 5) = d(1) 119 | output(i, 6) = d(7) 120 | output(i, 7) = d(5) 121 | output(i, 8) = d(8) 122 | output(i, 9) = d(9) 123 | output(i, 10) = 9.99e-38 ! NaN 124 | output(i, 11) = t(2) 125 | enddo 126 | 127 | return 128 | end subroutine pygtd7d 129 | -------------------------------------------------------------------------------- /src/wrappers/msis00.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | 4 | python module msis00f ! in 5 | interface ! in :pymsis 6 | subroutine pyinitswitch(switch_legacy, parmpath) ! in :pymsis2:pymsis00.F90 7 | real(kind=4), optional,dimension(25),intent(in) :: switch_legacy 8 | character(len=*), intent(in), optional :: parmpath 9 | end subroutine pyinitswitch 10 | subroutine pymsiscalc(day,utsec,lon,lat,z,sflux,sfluxavg,ap,output,n) ! in :pymsis:pymsis00.F90 11 | real dimension(n),intent(in) :: day 12 | real dimension(n),intent(in),depend(n) :: utsec 13 | real dimension(n),intent(in),depend(n) :: lon 14 | real dimension(n),intent(in),depend(n) :: lat 15 | real dimension(n),intent(in),depend(n) :: z 16 | real dimension(n),intent(in),depend(n) :: sflux 17 | real dimension(n),intent(in),depend(n) :: sfluxavg 18 | real dimension(n,7),intent(in),depend(n) :: ap 19 | real dimension(n,11),intent(out),depend(n) :: output 20 | integer, optional,intent(in),check(len(day)>=n),depend(day) :: n=len(day) 21 | end subroutine pygtd7d 22 | ! The following functions are deprecated in 0.10 and will be removed in the future 23 | subroutine pytselec(switch_legacy) ! in :pymsis2:pymsis00.F90 24 | real(kind=4), optional,dimension(25),intent(in) :: switch_legacy 25 | end subroutine pyinitswitch 26 | subroutine pygtd7d(day,utsec,lon,lat,z,sflux,sfluxavg,ap,output,n) ! in :pymsis:pymsis00.F90 27 | real dimension(n),intent(in) :: day 28 | real dimension(n),intent(in),depend(n) :: utsec 29 | real dimension(n),intent(in),depend(n) :: lon 30 | real dimension(n),intent(in),depend(n) :: lat 31 | real dimension(n),intent(in),depend(n) :: z 32 | real dimension(n),intent(in),depend(n) :: sflux 33 | real dimension(n),intent(in),depend(n) :: sfluxavg 34 | real dimension(n,7),intent(in),depend(n) :: ap 35 | real dimension(n,11),intent(out),depend(n) :: output 36 | integer, optional,intent(in),check(len(day)>=n),depend(day) :: n=len(day) 37 | end subroutine pygtd7d 38 | end interface 39 | end python module msis00f 40 | -------------------------------------------------------------------------------- /src/wrappers/msis2.F90: -------------------------------------------------------------------------------- 1 | 2 | subroutine pyinitswitch(switch_legacy, parmpath) 3 | use msis_calc, only: msiscalc 4 | use msis_constants, only: rp 5 | use msis_init, only: msisinit 6 | 7 | implicit none 8 | 9 | real(4), intent(in), optional :: switch_legacy(1:25) !Legacy switch array 10 | character(len=*), intent(in), optional :: parmpath !Path to parameter file 11 | real(kind=rp) :: output = 0. 12 | real(kind=rp) :: output_arr(1:11) = 0. 13 | 14 | call msisinit(switch_legacy=switch_legacy, parmpath=parmpath) 15 | 16 | ! Artificially call msiscalc to reset the last variables as there is 17 | ! a global cache on these and the parameters won't be updated if we 18 | ! don't set them to something different. 19 | ! See issue: gh-59 20 | call msiscalc(0., 0., -999., -999., -1., 0., 0., (/1., 1., 1., 1., 1., 1., 1./), output, output_arr) 21 | 22 | return 23 | end subroutine pyinitswitch 24 | 25 | subroutine pymsiscalc(day, utsec, lon, lat, z, sflux, sfluxavg, ap, output, n) 26 | ! NOTE: pymsiscalc takes the order (lon, lat, z), but the msiscalc Fortran 27 | ! code takes the order (z, lat, lon). 28 | ! sflux also comes before sfluxavg 29 | use msis_calc, only: msiscalc 30 | use msis_constants, only: rp 31 | 32 | implicit none 33 | 34 | integer, intent(in) :: n 35 | real(kind=rp), intent(in) :: day(n) 36 | real(kind=rp), intent(in) :: utsec(n) 37 | real(kind=rp), intent(in) :: lon(n) 38 | real(kind=rp), intent(in) :: lat(n) 39 | real(kind=rp), intent(in) :: z(n) 40 | real(kind=rp), intent(in) :: sflux(n) 41 | real(kind=rp), intent(in) :: sfluxavg(n) 42 | real(kind=rp), intent(in) :: ap(n, 1:7) 43 | real(kind=rp), intent(out) :: output(n, 1:11) 44 | 45 | integer :: i 46 | real(kind=rp) :: lon_tmp 47 | 48 | do i=1, n 49 | if (lon(i) < 0) then 50 | lon_tmp = lon(i) + 360 51 | else 52 | lon_tmp = lon(i) 53 | endif 54 | call msiscalc(day(i), utsec(i), z(i), lat(i), lon(i), sfluxavg(i), & 55 | sflux(i), ap(i, :), output(i, 11), output(i, 1:10)) 56 | enddo 57 | 58 | end subroutine pymsiscalc 59 | -------------------------------------------------------------------------------- /src/wrappers/msis20.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | 4 | python module msis20f ! in 5 | interface ! in :pymsis 6 | subroutine pyinitswitch(switch_legacy, parmpath) ! in :pymsis:msis2.F90 7 | use msis_init, only: msisinit 8 | real(kind=4), optional,dimension(25),intent(in) :: switch_legacy 9 | character(len=*), intent(in), optional :: parmpath 10 | end subroutine pyinitswitch 11 | subroutine pymsiscalc(day,utsec,lon,lat,z,sfluxavg,sflux,ap,output,n) ! in :pymsis:msis2.F90 12 | use msis_calc, only: msiscalc 13 | use msis_constants, only: rp 14 | real(kind=rp) dimension(n),intent(in) :: day 15 | real(kind=rp) dimension(n),intent(in),depend(n) :: utsec 16 | real(kind=rp) dimension(n),intent(in),depend(n) :: lon 17 | real(kind=rp) dimension(n),intent(in),depend(n) :: lat 18 | real(kind=rp) dimension(n),intent(in),depend(n) :: z 19 | real(kind=rp) dimension(n),intent(in),depend(n) :: sflux 20 | real(kind=rp) dimension(n),intent(in),depend(n) :: sfluxavg 21 | real(kind=rp) dimension(n,7),intent(in),depend(n) :: ap 22 | real(kind=rp) dimension(n,11),intent(out),depend(n) :: output 23 | integer, optional,intent(in),check(len(day)>=n),depend(day) :: n=len(day) 24 | end subroutine pymsiscalc 25 | end interface 26 | end python module msis20f 27 | -------------------------------------------------------------------------------- /src/wrappers/msis21.pyf: -------------------------------------------------------------------------------- 1 | ! -*- f90 -*- 2 | ! Note: the context of this file is case sensitive. 3 | 4 | python module msis21f ! in 5 | interface ! in :pymsis 6 | subroutine pyinitswitch(switch_legacy, parmpath) ! in :pymsis:msis2.F90 7 | use msis_init, only: msisinit 8 | real(kind=4), optional,dimension(25),intent(in) :: switch_legacy 9 | character(len=*), intent(in), optional :: parmpath 10 | end subroutine pyinitswitch 11 | subroutine pymsiscalc(day,utsec,lon,lat,z,sflux,sfluxavg,ap,output,n) ! in :pymsis:msis2.F90 12 | use msis_calc, only: msiscalc 13 | use msis_constants, only: rp 14 | real(kind=rp) dimension(n),intent(in) :: day 15 | real(kind=rp) dimension(n),intent(in),depend(n) :: utsec 16 | real(kind=rp) dimension(n),intent(in),depend(n) :: lon 17 | real(kind=rp) dimension(n),intent(in),depend(n) :: lat 18 | real(kind=rp) dimension(n),intent(in),depend(n) :: z 19 | real(kind=rp) dimension(n),intent(in),depend(n) :: sflux 20 | real(kind=rp) dimension(n),intent(in),depend(n) :: sfluxavg 21 | real(kind=rp) dimension(n,7),intent(in),depend(n) :: ap 22 | real(kind=rp) dimension(n,11),intent(out),depend(n) :: output 23 | integer, optional,intent(in),check(len(day)>=n),depend(day) :: n=len(day) 24 | end subroutine pymsiscalc 25 | end interface 26 | end python module msis21f 27 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SWxTREC/pymsis/cdd4043395bf3485fe9295afe862e0a607a0e7fb/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | 5 | from pymsis import utils 6 | 7 | 8 | @pytest.fixture(autouse=True) 9 | def local_path(monkeypatch): 10 | # Update the data location to our test data 11 | test_file = Path(__file__).parent / "f107_ap_test_data.txt" 12 | # Monkeypatch the url and expected download location, so we aren't 13 | # dependent on an internet connection. 14 | monkeypatch.setattr(utils, "_F107_AP_PATH", test_file) 15 | return test_file 16 | 17 | 18 | @pytest.fixture(autouse=True) 19 | def remote_path(monkeypatch, local_path): 20 | # Update the remote URL to point to a local file system test path 21 | # by prepending file:// so that it can be opened by urlopen() 22 | test_url = local_path.absolute().as_uri() 23 | # Monkeypatch the url and expected download location, so we aren't 24 | # dependent on an internet connection. 25 | monkeypatch.setattr(utils, "_F107_AP_URL", test_url) 26 | return test_url 27 | -------------------------------------------------------------------------------- /tests/meson.build: -------------------------------------------------------------------------------- 1 | py3.install_sources( 2 | ['__init__.py', 3 | 'conftest.py', 4 | 'f107_ap_test_data.txt', 5 | 'test_msis.py', 6 | 'test_regression.py', 7 | 'test_utils.py', 8 | 'msis2.0_test_ref_dp.txt', 9 | 'msis2.1_test_ref_dp.txt', 10 | ], 11 | subdir: 'pymsis/tests' 12 | ) -------------------------------------------------------------------------------- /tests/msis2.0_test_ref_dp.txt: -------------------------------------------------------------------------------- 1 | iyd sec alt glat glong stl f107a f107 Ap He O N2 O2 Ar rho H N O* T 2 | 70178 64800 0.2 50.0 55.0 21.67 153.3 146.5 35.0 0.1255E+15 0.9999E-37 0.1884E+20 0.5052E+19 0.2252E+18 0.1160E-02 0.9999E-37 0.9999E-37 0.9999E-37 294.10 3 | 70022 64800 0.6 -15.0 35.0 20.33 163.8 182.8 5.0 0.1190E+15 0.9999E-37 0.1787E+20 0.4792E+19 0.2136E+18 0.1100E-02 0.9999E-37 0.9999E-37 0.9999E-37 296.25 4 | 70280 43200 0.8 -10.0 160.0 22.67 149.4 128.1 4.0 0.1174E+15 0.9999E-37 0.1763E+20 0.4727E+19 0.2107E+18 0.1085E-02 0.9999E-37 0.9999E-37 0.9999E-37 293.36 5 | 70305 21600 1.0 -65.0 230.0 21.33 155.9 171.1 2.0 0.1254E+15 0.9999E-37 0.1884E+20 0.5051E+19 0.2251E+18 0.1160E-02 0.9999E-37 0.9999E-37 0.9999E-37 264.78 6 | 70281 0 1.0 -70.0 5.0 0.33 150.0 133.5 2.0 0.1308E+15 0.9999E-37 0.1964E+20 0.5267E+19 0.2347E+18 0.1209E-02 0.9999E-37 0.9999E-37 0.9999E-37 252.35 7 | 70036 43200 1.0 45.0 105.0 19.00 165.1 122.9 10.0 0.1241E+15 0.9999E-37 0.1863E+20 0.4997E+19 0.2227E+18 0.1147E-02 0.9999E-37 0.9999E-37 0.9999E-37 268.25 8 | 70184 21600 1.3 75.0 305.0 2.33 149.9 182.8 24.0 0.1181E+15 0.9999E-37 0.1773E+20 0.4755E+19 0.2119E+18 0.1091E-02 0.9999E-37 0.9999E-37 0.9999E-37 271.81 9 | 70003 21600 1.8 -90.0 55.0 9.67 152.9 149.5 9.0 0.1170E+15 0.9999E-37 0.1757E+20 0.4712E+19 0.2100E+18 0.1082E-02 0.9999E-37 0.9999E-37 0.9999E-37 254.18 10 | 70239 0 2.0 25.0 230.0 15.33 142.0 132.9 12.0 0.1039E+15 0.9999E-37 0.1561E+20 0.4185E+19 0.1865E+18 0.9608E-03 0.9999E-37 0.9999E-37 0.9999E-37 287.80 11 | 70069 0 3.1 40.0 275.0 18.33 168.7 166.8 7.0 0.9564E+14 0.9999E-37 0.1436E+20 0.3851E+19 0.1716E+18 0.8841E-03 0.9999E-37 0.9999E-37 0.9999E-37 269.19 12 | 70148 43200 3.1 -20.0 100.0 18.67 159.2 151.2 45.0 0.9350E+14 0.9999E-37 0.1404E+20 0.3765E+19 0.1678E+18 0.8643E-03 0.9999E-37 0.9999E-37 0.9999E-37 279.82 13 | 70070 64800 3.2 -70.0 320.0 15.33 168.2 160.5 3.0 0.9843E+14 0.9999E-37 0.1478E+20 0.3964E+19 0.1766E+18 0.9099E-03 0.9999E-37 0.9999E-37 0.9999E-37 251.74 14 | 70084 0 3.5 45.0 155.0 10.33 166.7 170.8 3.0 0.9349E+14 0.9999E-37 0.1404E+20 0.3765E+19 0.1678E+18 0.8642E-03 0.9999E-37 0.9999E-37 0.9999E-37 258.34 15 | 70054 0 4.2 70.0 290.0 19.33 167.1 187.0 4.0 0.8809E+14 0.9999E-37 0.1323E+20 0.3547E+19 0.1581E+18 0.8143E-03 0.9999E-37 0.9999E-37 0.9999E-37 242.78 16 | 70363 43200 4.6 -65.0 225.0 3.00 156.8 121.3 12.0 0.8310E+14 0.9999E-37 0.1248E+20 0.3346E+19 0.1491E+18 0.7682E-03 0.9999E-37 0.9999E-37 0.9999E-37 249.43 17 | 70011 43200 4.9 75.0 125.0 20.33 156.3 139.1 4.0 0.8178E+14 0.9999E-37 0.1228E+20 0.3293E+19 0.1468E+18 0.7560E-03 0.9999E-37 0.9999E-37 0.9999E-37 233.91 18 | 70206 0 5.9 -5.0 300.0 20.00 148.8 158.9 92.0 0.6919E+14 0.9999E-37 0.1039E+20 0.2786E+19 0.1242E+18 0.6396E-03 0.9999E-37 0.9999E-37 0.9999E-37 268.30 19 | 70062 21600 7.4 20.0 325.0 3.67 169.1 176.1 15.0 0.5974E+14 0.9999E-37 0.8971E+19 0.2406E+19 0.1072E+18 0.5522E-03 0.9999E-37 0.9999E-37 0.9999E-37 253.08 20 | 70332 43200 7.7 50.0 115.0 19.67 157.6 138.8 8.0 0.5819E+14 0.9999E-37 0.8738E+19 0.2343E+19 0.1044E+18 0.5379E-03 0.9999E-37 0.9999E-37 0.9999E-37 229.60 21 | 70180 43200 11.8 -60.0 310.0 8.67 151.7 155.3 6.0 0.3240E+14 0.9999E-37 0.4865E+19 0.1305E+19 0.5814E+17 0.2995E-03 0.9999E-37 0.9999E-37 0.9999E-37 209.01 22 | 70363 43200 12.2 -55.0 35.0 14.33 156.8 121.3 12.0 0.3096E+14 0.9999E-37 0.4650E+19 0.1247E+19 0.5557E+17 0.2862E-03 0.9999E-37 0.9999E-37 0.9999E-37 219.78 23 | 70068 43200 12.2 -30.0 260.0 5.33 169.1 175.4 47.0 0.3456E+14 0.9999E-37 0.5190E+19 0.1392E+19 0.6203E+17 0.3195E-03 0.9999E-37 0.9999E-37 0.9999E-37 218.03 24 | 70133 64800 14.4 -5.0 145.0 3.67 162.9 176.6 7.0 0.2714E+14 0.9999E-37 0.4075E+19 0.1093E+19 0.4870E+17 0.2509E-03 0.9999E-37 0.9999E-37 0.9999E-37 203.16 25 | 70252 39617 16.4 -78.5 74.2 15.95 139.5 154.6 5.0 0.1424E+14 0.9999E-37 0.2138E+19 0.5732E+18 0.2555E+17 0.1316E-03 0.9999E-37 0.9999E-37 0.9999E-37 186.14 26 | 70285 43200 18.6 -25.0 140.0 21.33 151.7 143.3 16.0 0.1290E+14 0.9999E-37 0.1937E+19 0.5194E+18 0.2315E+17 0.1192E-03 0.9999E-37 0.9999E-37 0.9999E-37 206.54 27 | 70303 21600 23.7 -55.0 320.0 3.33 155.1 191.6 9.0 0.4852E+13 0.9999E-37 0.7285E+18 0.1954E+18 0.8707E+16 0.4485E-04 0.9999E-37 0.9999E-37 0.9999E-37 219.18 28 | 70252 64800 26.4 -15.0 145.0 3.67 139.5 154.6 5.0 0.3449E+13 0.9999E-37 0.5180E+18 0.1389E+18 0.6190E+16 0.3188E-04 0.9999E-37 0.9999E-37 0.9999E-37 219.71 29 | 70090 0 26.5 -10.0 235.0 15.67 163.5 149.8 51.0 0.3345E+13 0.9999E-37 0.5023E+18 0.1347E+18 0.6003E+16 0.3092E-04 0.9999E-37 0.9999E-37 0.9999E-37 222.71 30 | 70211 43200 34.2 50.0 45.0 15.00 147.2 156.2 7.0 0.1124E+13 0.9999E-37 0.1688E+18 0.4527E+17 0.2017E+16 0.1039E-04 0.9999E-37 0.9999E-37 0.9999E-37 242.73 31 | 70248 27142 35.3 35.7 -28.5 5.64 140.9 160.7 8.0 0.9091E+12 0.9999E-37 0.1365E+18 0.3661E+17 0.1631E+16 0.8403E-05 0.9999E-37 0.9999E-37 0.9999E-37 237.05 32 | 70230 48384 38.9 -66.1 165.5 0.47 141.4 147.9 36.0 0.3396E+12 0.9999E-37 0.5099E+17 0.1367E+17 0.6094E+15 0.3139E-05 0.9999E-37 0.9999E-37 0.9999E-37 239.15 33 | 70008 33536 39.1 56.1 54.7 12.96 154.0 116.9 6.0 0.3887E+12 0.9999E-37 0.5838E+17 0.1565E+17 0.6977E+15 0.3594E-05 0.9999E-37 0.9999E-37 0.9999E-37 245.01 34 | 70087 0 39.5 -45.0 295.0 19.67 165.2 159.9 21.0 0.4759E+12 0.9999E-37 0.7146E+17 0.1916E+17 0.8540E+15 0.4399E-05 0.9999E-37 0.9999E-37 0.9999E-37 248.07 35 | 70165 6144 41.0 -42.9 -7.2 1.22 158.1 194.2 8.0 0.3296E+12 0.9999E-37 0.4950E+17 0.1327E+17 0.5916E+15 0.3047E-05 0.9999E-37 0.9999E-37 0.9999E-37 239.29 36 | 70312 62592 41.3 67.5 -74.7 12.41 156.7 153.4 8.0 0.2890E+12 0.9999E-37 0.4340E+17 0.1164E+17 0.5186E+15 0.2671E-05 0.9999E-37 0.9999E-37 0.9999E-37 228.28 37 | 70013 0 42.3 -15.0 125.0 8.33 157.7 177.4 4.0 0.3018E+12 0.9999E-37 0.4531E+17 0.1215E+17 0.5415E+15 0.2789E-05 0.9999E-37 0.9999E-37 0.9999E-37 255.20 38 | 70297 64800 42.5 -55.0 5.0 18.33 151.5 150.5 14.0 0.2805E+12 0.9999E-37 0.4212E+17 0.1129E+17 0.5034E+15 0.2593E-05 0.9999E-37 0.9999E-37 0.9999E-37 267.26 39 | 70151 39040 43.7 -3.0 -135.9 1.78 159.6 159.3 6.0 0.2611E+12 0.9999E-37 0.3920E+17 0.1051E+17 0.4685E+15 0.2413E-05 0.9999E-37 0.9999E-37 0.9999E-37 261.94 40 | 70035 16786 44.0 13.3 30.5 6.70 165.3 127.3 13.0 0.2378E+12 0.9999E-37 0.3571E+17 0.9576E+16 0.4268E+15 0.2198E-05 0.9999E-37 0.9999E-37 0.9999E-37 258.97 41 | 70046 38400 44.2 40.0 39.3 13.28 165.5 201.2 8.0 0.2243E+12 0.9999E-37 0.3368E+17 0.9032E+16 0.4026E+15 0.2073E-05 0.9999E-37 0.9999E-37 0.9999E-37 261.12 42 | 70024 61055 44.9 -75.5 -13.5 16.06 164.6 158.3 7.0 0.2516E+12 0.9999E-37 0.3778E+17 0.1013E+17 0.4515E+15 0.2326E-05 0.9999E-37 0.9999E-37 0.9999E-37 280.78 43 | 70072 26161 45.4 -80.8 322.4 4.76 167.5 166.5 8.0 0.1993E+12 0.9999E-37 0.2993E+17 0.8027E+16 0.3578E+15 0.1843E-05 0.9999E-37 0.9999E-37 0.9999E-37 259.96 44 | 70355 49246 45.9 -57.1 207.5 3.51 159.6 160.8 4.0 0.2119E+12 0.9999E-37 0.3183E+17 0.8534E+16 0.3804E+15 0.1959E-05 0.9999E-37 0.9999E-37 0.9999E-37 272.06 45 | 70035 35265 47.1 -18.2 132.1 18.60 165.3 127.3 13.0 0.1591E+12 0.9999E-37 0.2388E+17 0.6405E+16 0.2854E+15 0.1470E-05 0.9999E-37 0.9999E-37 0.9999E-37 267.41 46 | 70365 13741 51.9 -65.4 324.5 1.45 155.2 133.4 2.0 0.1054E+12 0.3340E+10 0.1583E+17 0.4246E+16 0.1892E+15 0.9747E-06 0.9999E-37 0.9999E-37 0.9999E-37 280.96 47 | 70301 20270 52.0 -23.8 183.9 17.89 153.9 194.2 15.0 0.8959E+11 0.4934E+10 0.1345E+17 0.3607E+16 0.1608E+15 0.8281E-06 0.9999E-37 0.9999E-37 0.9999E-37 260.58 48 | 70338 71881 52.1 66.3 259.3 13.25 158.3 152.4 5.0 0.5788E+11 0.3613E+10 0.8691E+16 0.2331E+16 0.1039E+15 0.5350E-06 0.9999E-37 0.9999E-37 0.9999E-37 250.34 49 | 70225 29883 53.1 -25.3 139.5 17.60 142.7 133.3 7.0 0.7391E+11 0.4328E+10 0.1110E+17 0.2976E+16 0.1326E+15 0.6832E-06 0.9999E-37 0.9999E-37 0.9999E-37 254.87 50 | 70037 32896 53.4 7.4 -110.3 1.78 164.9 124.5 4.0 0.7137E+11 0.5163E+08 0.1072E+17 0.2874E+16 0.1281E+15 0.6597E-06 0.9999E-37 0.9999E-37 0.9999E-37 262.98 51 | 70250 16640 53.6 68.9 115.0 12.29 139.8 159.0 6.0 0.7479E+11 0.5117E+10 0.1123E+17 0.3012E+16 0.1342E+15 0.6914E-06 0.9999E-37 0.9999E-37 0.9999E-37 259.74 52 | 70293 70912 53.8 -16.3 87.8 1.55 150.5 140.1 7.0 0.7193E+11 0.5082E+08 0.1080E+17 0.2896E+16 0.1291E+15 0.6649E-06 0.9999E-37 0.9999E-37 0.9999E-37 257.37 53 | 70135 39420 54.1 -69.4 348.6 10.19 162.2 192.6 10.0 0.3717E+11 0.4207E+10 0.5582E+16 0.1497E+16 0.6671E+14 0.3436E-06 0.9999E-37 0.9999E-37 0.9999E-37 257.12 54 | 11360 44281 55.0 -46.0 208.7 2.21 136.6 144.3 1.0 0.6618E+11 0.7561E+08 0.9938E+16 0.2665E+16 0.1188E+15 0.6118E-06 0.9999E-37 0.9999E-37 0.9999E-37 261.28 55 | 70339 26112 55.2 77.8 56.0 10.99 158.3 158.6 8.0 0.3235E+11 0.2078E+09 0.4857E+16 0.1302E+16 0.5805E+14 0.2990E-06 0.9999E-37 0.9999E-37 0.9999E-37 259.93 56 | 70254 54016 56.1 -81.3 91.4 21.10 139.6 141.9 1.0 0.3185E+11 0.1706E+09 0.4783E+16 0.1283E+16 0.5716E+14 0.2944E-06 0.9999E-37 0.9999E-37 0.9999E-37 266.13 57 | 70123 60787 56.9 -6.8 348.1 16.09 160.7 158.2 10.0 0.5061E+11 0.6843E+10 0.7600E+16 0.2038E+16 0.9083E+14 0.4679E-06 0.9999E-37 0.9999E-37 0.9999E-37 252.08 58 | 10338 58684 58.5 -48.1 70.3 20.99 84.0 86.9 1.0 0.4501E+11 0.2287E+09 0.6759E+16 0.1813E+16 0.8078E+14 0.4161E-06 0.9999E-37 0.9999E-37 0.9999E-37 255.21 59 | 70333 82943 58.9 36.7 117.3 6.86 158.0 153.0 3.0 0.3356E+11 0.4720E+10 0.5039E+16 0.1351E+16 0.6022E+14 0.3102E-06 0.9999E-37 0.9999E-37 0.9999E-37 238.67 60 | 13365 68019 60.8 -81.2 42.8 21.75 154.3 142.9 5.0 0.4022E+11 0.6157E+10 0.6040E+16 0.1620E+16 0.7218E+14 0.3718E-06 0.9999E-37 0.9999E-37 0.9999E-37 271.28 61 | 70042 59285 62.0 -47.1 143.9 2.06 164.2 174.9 3.0 0.2824E+11 0.4085E+08 0.4241E+16 0.1137E+16 0.5069E+14 0.2611E-06 0.9999E-37 0.9999E-37 0.9999E-37 237.73 62 | 70016 64466 62.6 52.7 104.9 0.90 159.6 186.0 17.0 0.1889E+11 0.5651E+08 0.2837E+16 0.7608E+15 0.3391E+14 0.1746E-06 0.9999E-37 0.9999E-37 0.9999E-37 227.49 63 | 70052 38015 62.6 28.1 110.4 17.92 167.4 201.0 2.0 0.2291E+11 0.4590E+10 0.3440E+16 0.9225E+15 0.4111E+14 0.2118E-06 0.9999E-37 0.9999E-37 0.9999E-37 229.00 64 | 70114 32400 62.9 -74.9 9.2 9.62 161.3 128.7 16.0 0.1356E+11 0.8568E+10 0.2036E+16 0.5458E+15 0.2433E+14 0.1253E-06 0.9999E-37 0.9999E-37 0.9999E-37 243.58 65 | 12355 50214 65.4 35.6 168.5 1.18 121.4 113.5 8.0 0.1400E+11 0.5491E+08 0.2102E+16 0.5637E+15 0.2512E+14 0.1294E-06 0.9999E-37 0.9999E-37 0.9999E-37 224.08 66 | 70242 13960 68.1 50.3 269.5 21.85 142.3 145.8 5.0 0.1239E+11 0.7528E+08 0.1860E+16 0.4988E+15 0.2223E+14 0.1145E-06 0.9999E-37 0.9999E-37 0.9999E-37 213.34 67 | 70054 61659 69.0 -17.9 18.0 18.33 167.1 187.0 4.0 0.1002E+11 0.5316E+10 0.1505E+16 0.4037E+15 0.1799E+14 0.9267E-07 0.9999E-37 0.9999E-37 0.9999E-37 218.60 68 | 70269 70016 69.1 45.9 -93.6 13.21 145.7 154.9 6.0 0.9175E+10 0.7994E+10 0.1378E+16 0.3695E+15 0.1647E+14 0.8481E-07 0.9999E-37 0.9999E-37 0.9999E-37 211.10 69 | 12104 45507 71.5 10.5 270.6 6.68 117.3 95.3 20.0 0.7022E+10 0.6242E+10 0.1054E+16 0.2828E+15 0.1260E+14 0.6491E-07 0.9999E-37 0.9999E-37 0.9999E-37 199.16 70 | 70361 6656 71.6 64.7 16.8 2.97 158.0 126.5 8.0 0.4047E+10 0.3846E+09 0.6077E+15 0.1630E+15 0.7262E+13 0.3741E-07 0.9999E-37 0.9999E-37 0.9999E-37 234.81 71 | 70128 75684 72.2 6.2 47.0 0.16 162.9 159.5 4.0 0.6653E+10 0.1136E+09 0.9991E+15 0.2679E+15 0.1194E+14 0.6150E-07 0.9999E-37 0.9999E-37 0.9999E-37 194.11 72 | 70026 46656 72.6 39.3 274.8 7.28 165.4 154.3 3.0 0.4518E+10 0.6878E+10 0.6785E+15 0.1819E+15 0.8109E+13 0.4177E-07 0.9999E-37 0.9999E-37 0.9999E-37 217.67 73 | 11137 43521 73.0 41.7 286.1 7.16 101.2 92.2 9.0 0.5741E+10 0.5860E+10 0.8621E+15 0.2312E+15 0.1030E+14 0.5307E-07 0.9999E-37 0.9999E-37 0.9999E-37 203.13 74 | 70132 15320 73.6 -72.0 283.0 23.12 163.1 172.7 15.0 0.2809E+10 0.5839E+09 0.4218E+15 0.1131E+15 0.5040E+13 0.2596E-07 0.9999E-37 0.9999E-37 0.9999E-37 227.21 75 | 70007 66215 75.3 -17.4 312.2 15.21 153.6 121.6 5.0 0.3833E+10 0.6183E+10 0.5756E+15 0.1544E+15 0.6879E+13 0.3543E-07 0.9999E-37 0.9999E-37 0.9999E-37 210.49 76 | 70253 28950 76.3 -30.3 -70.7 3.33 139.5 155.0 6.0 0.2961E+10 0.4547E+09 0.4445E+15 0.1192E+15 0.5313E+13 0.2737E-07 0.1642E+08 0.9999E-37 0.9999E-37 201.00 77 | 11146 37212 77.4 -13.4 321.4 7.77 98.4 80.3 6.0 0.2494E+10 0.7867E+10 0.3739E+15 0.1003E+15 0.4468E+13 0.2302E-07 0.1406E+09 0.9999E-37 0.9999E-37 194.94 78 | 70301 81666 77.8 52.1 53.6 2.26 154.6 194.2 15.0 0.2156E+10 0.1033E+10 0.3227E+15 0.8652E+14 0.3856E+13 0.1986E-07 0.5184E+08 0.9999E-37 0.9999E-37 200.27 79 | 70362 38908 77.9 -20.8 94.5 17.10 157.5 123.9 18.0 0.2614E+10 0.7854E+10 0.3915E+15 0.1050E+15 0.4679E+13 0.2410E-07 0.1368E+09 0.9999E-37 0.9999E-37 200.80 80 | 70232 25110 78.2 -30.3 -70.7 2.26 141.4 142.0 4.0 0.2124E+10 0.8911E+09 0.3177E+15 0.8520E+14 0.3797E+13 0.1956E-07 0.7304E+08 0.9999E-37 0.9999E-37 198.61 81 | 70079 2581 78.2 -82.5 74.6 5.69 167.9 131.3 4.0 0.2396E+10 0.4817E+10 0.3578E+15 0.9595E+14 0.4276E+13 0.2203E-07 0.9640E+08 0.9999E-37 0.9999E-37 193.48 82 | 70090 72448 78.6 57.6 -108.2 12.91 163.1 149.8 51.0 0.2017E+10 0.1130E+11 0.3010E+15 0.8071E+14 0.3597E+13 0.1853E-07 0.1265E+09 0.9999E-37 0.9999E-37 209.59 83 | 70145 20790 78.7 -30.3 -70.7 1.06 158.4 158.5 7.0 0.1994E+10 0.1194E+10 0.2976E+15 0.7981E+14 0.3557E+13 0.1832E-07 0.9840E+08 0.9999E-37 0.9999E-37 194.19 84 | 70335 24360 79.9 40.1 -105.2 23.75 158.3 156.8 1.0 0.1571E+10 0.3066E+10 0.2329E+15 0.6245E+14 0.2783E+13 0.1434E-07 0.1186E+09 0.9999E-37 0.9999E-37 200.91 85 | 70284 2730 80.9 -30.3 -70.7 20.05 151.5 148.0 15.0 0.1524E+10 0.8263E+10 0.2242E+15 0.6013E+14 0.2680E+13 0.1380E-07 0.1624E+09 0.9999E-37 0.9999E-37 195.86 86 | 70036 82500 80.9 69.3 16.8 0.03 164.9 122.9 10.0 0.1170E+10 0.2033E+11 0.1721E+15 0.4616E+14 0.2057E+13 0.1060E-07 0.8159E+08 0.9999E-37 0.9999E-37 215.14 87 | 70010 38912 81.4 75.5 -102.5 3.98 155.5 123.7 6.0 0.9669E+09 0.4276E+11 0.1417E+15 0.3800E+14 0.1694E+13 0.8725E-08 0.6366E+08 0.9999E-37 0.9999E-37 221.12 88 | 70077 30600 81.9 40.6 -105.1 1.49 167.7 135.0 6.0 0.1282E+10 0.8586E+10 0.1869E+15 0.5011E+14 0.2233E+13 0.1150E-07 0.1621E+09 0.9999E-37 0.9999E-37 197.55 89 | 10353 32086 82.3 -55.9 137.8 18.10 83.7 80.6 2.0 0.1785E+10 0.1083E+11 0.2582E+15 0.6907E+14 0.3078E+13 0.1589E-07 0.3759E+09 0.9999E-37 0.9999E-37 168.45 90 | 70081 28740 82.6 -30.3 -70.7 3.27 167.8 150.8 2.0 0.1153E+10 0.1250E+11 0.1668E+15 0.4472E+14 0.1993E+13 0.1027E-07 0.2162E+09 0.9999E-37 0.9999E-37 194.71 91 | 70325 32728 82.8 11.8 328.4 6.99 156.8 177.9 30.0 0.1089E+10 0.1731E+11 0.1574E+15 0.4220E+14 0.1881E+13 0.9688E-08 0.1896E+09 0.9999E-37 0.9999E-37 195.15 92 | 70060 16800 83.2 69.3 15.2 5.68 168.5 177.4 22.0 0.8831E+09 0.4533E+11 0.1271E+15 0.3407E+14 0.1519E+13 0.7823E-08 0.1105E+09 0.9999E-37 0.9999E-37 217.95 93 | 70250 12570 83.6 -30.3 -70.7 22.78 139.8 159.0 6.0 0.9437E+09 0.4746E+11 0.1349E+15 0.3618E+14 0.1612E+13 0.8307E-08 0.1626E+09 0.9999E-37 0.9999E-37 196.29 94 | 9029 33427 84.1 -48.2 245.3 1.64 69.7 69.5 4.0 0.1097E+10 0.2454E+11 0.1549E+15 0.4146E+14 0.1848E+13 0.9530E-08 0.3311E+09 0.9999E-37 0.9999E-37 172.72 95 | 12339 43649 88.8 -67.7 266.6 5.90 120.4 96.7 4.0 0.6003E+09 0.9982E+11 0.7589E+14 0.1981E+14 0.8791E+12 0.4644E-08 0.2212E+09 0.9999E-37 0.9999E-37 149.16 96 | 13160 72325 89.3 41.8 136.9 5.22 120.5 103.2 7.0 0.3940E+09 0.2631E+12 0.4948E+14 0.1306E+14 0.5791E+12 0.3041E-08 0.1439E+09 0.9999E-37 0.9999E-37 173.97 97 | 11147 66312 95.0 -44.6 11.8 19.21 98.0 82.7 11.0 0.2004E+09 0.5130E+12 0.1857E+14 0.4897E+13 0.2120E+12 0.1152E-08 0.4393E+08 0.8685E+05 0.9999E-37 186.96 98 | 12139 6690 95.0 -55.0 59.8 5.84 119.9 136.3 8.0 0.1977E+09 0.5809E+12 0.1811E+14 0.4782E+13 0.2067E+12 0.1126E-08 0.4016E+08 0.2289E+05 0.9999E-37 184.66 99 | 12104 37819 96.0 50.9 210.5 0.54 117.3 95.3 20.0 0.1987E+09 0.8001E+12 0.1815E+14 0.4681E+13 0.2031E+12 0.1128E-08 0.6931E+08 0.5275E+05 0.9999E-37 191.36 100 | 13135 34150 97.5 63.4 226.0 0.56 123.6 147.9 8.0 0.1516E+09 0.6957E+12 0.1218E+14 0.3018E+13 0.1290E+12 0.7540E-09 0.6854E+08 0.9230E+05 0.9999E-37 181.81 101 | 13319 80974 102.0 -82.3 66.2 2.91 145.6 175.7 10.0 0.8703E+08 0.2131E+12 0.4486E+13 0.1024E+13 0.4243E+11 0.2715E-09 0.3175E+08 0.1761E+06 0.9999E-37 198.98 102 | 8174 36372 110.0 62.7 2.3 10.25 66.5 64.9 3.0 0.2948E+08 0.5349E+11 0.1020E+13 0.2081E+12 0.7436E+10 0.6041E-10 0.1402E+08 0.2850E+06 0.9999E-37 280.27 103 | 11154 26650 115.0 10.6 42.1 10.21 95.8 111.6 3.0 0.4479E+08 0.9400E+11 0.5959E+12 0.1122E+12 0.3433E+10 0.3641E-10 0.8157E+07 0.1084E+07 0.9999E-37 277.19 104 | 10249 6525 115.0 -70.8 117.4 9.64 80.1 82.1 8.0 0.4178E+08 0.6777E+11 0.4989E+12 0.1107E+12 0.3340E+10 0.3111E-10 0.5642E+07 0.3614E+06 0.9999E-37 291.22 105 | 10169 72244 125.0 20.9 -147.3 10.25 74.8 70.4 5.0 0.2032E+08 0.3165E+11 0.1482E+12 0.2452E+11 0.6237E+09 0.9080E-11 0.4132E+07 0.2274E+07 0.4160E-34 408.06 106 | 95300 44459 127.7 -10.9 31.2 14.43 75.4 73.9 6.0 0.2568E+08 0.3500E+11 0.1392E+12 0.1792E+11 0.4983E+09 0.8391E-11 0.2762E+07 0.3840E+07 0.7857E-33 475.70 107 | 11104 81874 135.0 61.8 173.4 10.30 107.8 117.7 4.0 0.1712E+08 0.2051E+11 0.5937E+11 0.8120E+10 0.2130E+09 0.3753E-11 0.1394E+07 0.6171E+07 0.2433E-28 555.61 108 | 76028 53610 147.6 19.7 24.5 16.53 72.4 69.3 5.0 0.1332E+08 0.1133E+11 0.2604E+11 0.3006E+10 0.5427E+08 0.1676E-11 0.9610E+06 0.1420E+08 0.6838E-24 626.17 109 | 76032 25650 148.9 18.9 143.2 16.67 72.3 71.2 24.0 0.1364E+08 0.1175E+11 0.2514E+11 0.2919E+10 0.4852E+08 0.1640E-11 0.8669E+06 0.1511E+08 0.6140E-23 646.00 110 | 6264 42650 155.0 80.8 150.2 21.86 77.7 70.9 3.0 0.1190E+08 0.8226E+10 0.1279E+11 0.1803E+10 0.3382E+08 0.9118E-12 0.8136E+06 0.1051E+08 0.2784E-20 638.85 111 | 76264 1515 155.6 15.0 97.8 6.94 74.1 72.7 51.0 0.2021E+08 0.1449E+11 0.1781E+11 0.2266E+10 0.3527E+08 0.1337E-11 0.1204E+07 0.1234E+08 0.5298E-20 614.99 112 | 11014 39689 160.0 60.6 -11.5 10.26 87.1 79.5 9.0 0.2815E+08 0.9050E+10 0.1251E+11 0.1614E+10 0.1989E+08 0.9099E-12 0.5920E+06 0.1080E+08 0.3170E-19 665.88 113 | 11114 50370 160.0 -86.6 -172.3 2.50 104.5 119.1 6.0 0.2003E+08 0.8499E+10 0.9658E+10 0.1296E+10 0.2552E+08 0.7460E-12 0.4934E+06 0.1161E+08 0.1007E-18 719.60 114 | 74274 80445 164.3 -17.1 120.5 6.38 90.2 91.2 28.0 0.1822E+08 0.1123E+11 0.1111E+11 0.1152E+10 0.2032E+08 0.8781E-12 0.6999E+06 0.1209E+08 0.2760E-17 668.95 115 | 75363 7425 166.8 9.4 107.4 9.22 75.0 73.9 15.0 0.1435E+08 0.7864E+10 0.7174E+10 0.9580E+09 0.1118E+08 0.5948E-12 0.8260E+06 0.1731E+08 0.7202E-17 655.36 116 | 75340 54408 170.1 61.8 -91.8 8.99 77.0 79.7 7.0 0.3017E+08 0.6139E+10 0.6620E+10 0.9442E+09 0.7810E+07 0.5222E-12 0.5901E+06 0.9089E+07 0.3503E-16 673.94 117 | 74043 46665 173.1 36.8 -6.9 12.50 82.0 79.6 40.0 0.1626E+08 0.6683E+10 0.6990E+10 0.8224E+09 0.9916E+07 0.5479E-12 0.3933E+06 0.3412E+08 0.6431E-15 759.42 118 | 75352 6704 174.9 8.3 95.9 8.26 77.0 72.8 6.0 0.1417E+08 0.6281E+10 0.4974E+10 0.5970E+09 0.6109E+07 0.4308E-12 0.7802E+06 0.1665E+08 0.8094E-15 657.44 119 | 9305 984 175.0 -59.3 143.7 9.85 72.9 75.1 3.0 0.5159E+07 0.3857E+10 0.6177E+10 0.7613E+09 0.1300E+08 0.4316E-12 0.4432E+06 0.1940E+08 0.5799E-14 767.16 120 | 85081 56707 177.7 2.9 286.6 10.86 74.1 76.7 4.0 0.1461E+08 0.5862E+10 0.4701E+10 0.4549E+09 0.4967E+07 0.4000E-12 0.6748E+06 0.4291E+08 0.4412E-14 696.39 121 | 79095 21358 183.1 -1.7 64.0 10.20 178.5 183.4 52.0 0.2041E+08 0.1075E+11 0.5777E+10 0.2764E+09 0.7521E+07 0.5719E-12 0.1334E+06 0.9529E+08 0.6279E-12 955.08 122 | 83289 36443 188.9 -35.5 2.5 10.29 105.6 127.7 13.0 0.1056E+08 0.5027E+10 0.4041E+10 0.3369E+09 0.5457E+07 0.3411E-12 0.2095E+06 0.5389E+08 0.8709E-11 872.35 123 | 74024 62940 198.2 47.4 -35.8 15.09 83.4 83.6 5.0 0.1671E+08 0.3320E+10 0.2357E+10 0.1945E+09 0.1320E+07 0.2090E-12 0.2602E+06 0.2593E+08 0.1650E-09 768.89 124 | 75350 79275 199.6 44.4 154.6 8.33 77.1 74.1 14.0 0.3068E+08 0.3512E+10 0.1482E+10 0.1439E+09 0.7484E+06 0.1705E-12 0.5498E+06 0.1292E+08 0.3098E-09 659.39 125 | 75112 24270 205.5 -17.1 -118.0 22.88 70.7 67.5 15.0 0.1291E+08 0.2340E+10 0.9880E+09 0.8133E+08 0.6113E+06 0.1131E-12 0.6543E+06 0.2054E+08 0.7539E-08 675.00 126 | 86130 22044 206.1 -21.6 99.3 12.74 72.5 68.5 6.0 0.1548E+08 0.2475E+10 0.1321E+10 0.9257E+08 0.6884E+06 0.1330E-12 0.3871E+06 0.3281E+08 0.4674E-08 771.75 127 | 75354 3090 209.2 17.5 86.2 6.61 76.7 71.4 3.0 0.1560E+08 0.2515E+10 0.9402E+09 0.8276E+08 0.4616E+06 0.1153E-12 0.7340E+06 0.8241E+07 0.1078E-07 632.22 128 | 74057 42345 209.8 63.4 -59.9 7.77 79.6 80.2 28.0 0.7989E+07 0.1610E+10 0.1598E+10 0.2343E+09 0.2321E+07 0.1300E-12 0.2355E+06 0.1078E+08 0.3597E-07 848.01 129 | 83249 19422 211.3 44.1 -108.6 22.16 120.1 115.7 5.0 0.4982E+07 0.2162E+10 0.1551E+10 0.1033E+09 0.1202E+07 0.1358E-12 0.1970E+06 0.2709E+08 0.1413E-06 860.57 130 | 75293 34770 213.1 4.9 -143.6 0.09 77.7 78.6 5.0 0.1006E+08 0.2004E+10 0.8006E+09 0.5252E+08 0.4118E+06 0.9371E-13 0.5450E+06 0.1464E+08 0.7468E-07 699.22 131 | 76291 42555 216.1 18.5 -8.0 11.29 74.1 77.5 31.0 0.1359E+08 0.2381E+10 0.1022E+10 0.7755E+08 0.5683E+06 0.1157E-12 0.3251E+06 0.2658E+08 0.6768E-06 813.88 132 | 88326 66224 217.5 -2.8 -155.9 8.00 174.3 150.2 6.0 0.1335E+08 0.3726E+10 0.1532E+10 0.5897E+08 0.1081E+07 0.1743E-12 0.1125E+06 0.3451E+08 0.4010E-06 938.21 133 | 76071 5820 218.4 10.6 26.2 3.36 74.4 69.3 27.0 0.1157E+08 0.2085E+10 0.9462E+09 0.5764E+08 0.3605E+06 0.1029E-12 0.6757E+06 0.1198E+08 0.1605E-05 696.96 134 | 73133 57798 220.9 -19.5 -18.7 14.81 98.0 85.0 20.0 0.1107E+08 0.2172E+10 0.1235E+10 0.5663E+08 0.5149E+06 0.1191E-12 0.2021E+06 0.3627E+08 0.2093E-05 898.64 135 | 79092 38525 222.2 8.8 172.0 22.17 179.9 202.9 36.0 0.1030E+08 0.4115E+10 0.1283E+10 0.4988E+08 0.1406E+07 0.1732E-12 0.7511E+05 0.5864E+08 0.7476E-05 1034.56 136 | 73210 50056 224.2 81.3 -115.6 6.20 88.2 81.7 18.0 0.7411E+06 0.7382E+09 0.1286E+10 0.1239E+09 0.2027E+07 0.8649E-13 0.1452E+06 0.1491E+08 0.3483E-04 928.00 137 | 79080 80550 227.7 50.7 -10.0 21.71 185.9 185.6 5.0 0.7981E+07 0.3078E+10 0.1271E+10 0.4912E+08 0.1022E+07 0.1449E-12 0.6748E+05 0.5642E+08 0.5558E-04 1055.90 138 | 13191 22400 230.3 -10.7 -160.3 19.53 113.2 120.0 26.0 0.4724E+07 0.1330E+10 0.6968E+09 0.4537E+08 0.5419E+06 0.7073E-13 0.1580E+06 0.2178E+08 0.4874E-04 927.80 139 | 76010 17820 234.5 -7.7 27.0 6.75 73.8 71.8 47.0 0.6670E+07 0.1353E+10 0.4653E+09 0.3054E+08 0.2352E+06 0.5947E-13 0.3903E+06 0.7669E+07 0.4170E-03 740.34 140 | 83309 45247 234.6 -38.2 155.5 22.93 103.0 104.8 3.0 0.4593E+07 0.1351E+10 0.6432E+09 0.3321E+08 0.3116E+06 0.6799E-13 0.2208E+06 0.1534E+08 0.1949E-03 834.08 141 | 79079 49127 244.1 -1.1 129.0 22.25 186.6 179.2 6.0 0.7753E+07 0.2428E+10 0.5497E+09 0.1787E+08 0.3751E+06 0.9184E-13 0.8699E+05 0.3156E+08 0.6802E-03 1003.37 142 | 13111 770 244.6 39.3 112.5 7.71 123.6 104.9 2.0 0.1026E+08 0.1503E+10 0.3869E+09 0.1858E+08 0.1443E+06 0.5931E-13 0.2146E+06 0.1401E+08 0.2138E-02 866.25 143 | 75031 30540 245.1 18.2 -142.4 22.99 76.1 72.7 17.0 0.7704E+07 0.8550E+09 0.1676E+09 0.1168E+08 0.5647E+05 0.3136E-13 0.4222E+06 0.7561E+07 0.1371E-02 706.18 144 | 12167 1526 250.0 -12.7 -74.9 19.43 126.8 148.6 2.0 0.3677E+07 0.9591E+09 0.3812E+09 0.1966E+08 0.2151E+06 0.4471E-13 0.1234E+06 0.1764E+08 0.1383E-02 977.84 145 | 6239 55200 250.0 -84.7 -135.0 6.33 77.6 75.7 17.0 0.7384E+07 0.6516E+09 0.3185E+09 0.3178E+08 0.2516E+06 0.3395E-13 0.2499E+06 0.2698E+07 0.3264E-02 844.92 146 | 12178 13881 250.0 -6.9 -38.6 1.29 127.6 88.5 7.0 0.5180E+07 0.8139E+09 0.2842E+09 0.9765E+07 0.6544E+05 0.3556E-13 0.2933E+06 0.6650E+07 0.2493E-02 790.58 147 | 3187 68511 250.0 35.8 -51.9 15.57 127.9 141.9 9.0 0.1885E+07 0.1008E+10 0.6118E+09 0.2417E+08 0.3941E+06 0.5711E-13 0.6923E+05 0.2358E+08 0.1396E-01 1086.84 148 | 12213 82495 250.0 -6.9 -38.6 20.34 124.0 136.0 5.0 0.3330E+07 0.8950E+09 0.3111E+09 0.1721E+08 0.1824E+06 0.3949E-13 0.1418E+06 0.1277E+08 0.2153E-02 919.74 149 | 11328 8432 250.0 -6.9 -38.6 23.77 146.0 140.0 7.0 0.6764E+07 0.1439E+10 0.4399E+09 0.1501E+08 0.2008E+06 0.5989E-13 0.1324E+06 0.1473E+08 0.3472E-02 925.86 150 | 1139 7200 250.0 -86.4 0.0 2.00 163.0 138.3 10.0 0.1612E+08 0.1425E+10 0.4153E+09 0.2281E+08 0.2930E+06 0.5870E-13 0.1073E+06 0.7848E+07 0.9576E-02 960.11 151 | 6332 20181 250.0 -40.3 47.8 8.79 83.8 82.4 6.0 0.2946E+07 0.7570E+09 0.3711E+09 0.2490E+08 0.2128E+06 0.3898E-13 0.2068E+06 0.1090E+08 0.1057E-01 876.85 152 | 1126 47520 250.0 -86.4 45.0 16.20 169.2 160.7 5.0 0.1483E+08 0.1643E+10 0.4238E+09 0.2139E+08 0.3016E+06 0.6501E-13 0.9266E+05 0.1670E+08 0.1513E-01 1002.65 153 | 11200 75960 250.0 -84.7 -135.0 12.10 96.6 101.9 15.0 0.1808E+08 0.8168E+09 0.3198E+09 0.2454E+08 0.1615E+06 0.3809E-13 0.1727E+06 0.3440E+07 0.2348E-02 819.83 154 | 12261 81339 250.0 -6.9 -38.6 20.02 119.6 97.3 4.0 0.5152E+07 0.1048E+10 0.3016E+09 0.1473E+08 0.1554E+06 0.4304E-13 0.1801E+06 0.1419E+08 0.2213E-02 871.90 155 | 3069 10185 250.0 18.6 -175.0 15.17 125.9 152.8 16.0 0.7605E+07 0.1771E+10 0.7297E+09 0.2538E+08 0.3149E+06 0.8350E-13 0.8199E+05 0.4731E+08 0.5290E-02 1096.31 156 | 5335 78829 250.0 -48.0 154.4 8.19 85.9 94.6 17.0 0.1761E+07 0.6933E+09 0.4949E+09 0.3780E+08 0.5402E+06 0.4376E-13 0.1526E+06 0.1130E+08 0.1825E-01 974.05 157 | 5188 2905 250.0 5.5 -137.9 15.61 93.8 123.0 6.0 0.3046E+07 0.8589E+09 0.5194E+09 0.2057E+08 0.2165E+06 0.4862E-13 0.1283E+06 0.2230E+08 0.3178E-02 988.27 158 | 84302 25808 252.2 -10.6 103.9 14.09 75.0 69.5 8.0 0.6351E+07 0.1056E+10 0.4034E+09 0.1579E+08 0.1355E+06 0.4822E-13 0.2337E+06 0.2127E+08 0.6348E-02 859.40 159 | 77111 85830 258.0 -16.9 132.0 8.64 77.9 79.6 12.0 0.1509E+08 0.8928E+09 0.1280E+09 0.8772E+07 0.3171E+05 0.3048E-13 0.4004E+06 0.1011E+08 0.1871E-01 739.54 160 | 12016 83330 260.0 5.8 118.1 7.02 126.0 133.5 9.0 0.8242E+07 0.1146E+10 0.2341E+09 0.1184E+08 0.7976E+05 0.4222E-13 0.1771E+06 0.8696E+07 0.2242E-01 839.40 161 | 11081 11310 260.5 -7.2 52.8 6.66 110.9 101.0 8.0 0.1234E+08 0.1149E+10 0.1850E+09 0.8527E+07 0.4700E+05 0.3990E-13 0.2778E+06 0.9466E+07 0.2667E-01 770.48 162 | 12100 50610 260.9 -3.3 -105.8 7.01 116.5 93.2 3.0 0.1278E+08 0.1072E+10 0.1686E+09 0.7456E+07 0.3882E+05 0.3703E-13 0.2935E+06 0.9359E+07 0.1663E-01 755.67 163 | 11153 83910 262.3 -1.1 112.0 6.78 95.8 113.6 7.0 0.9570E+07 0.7905E+09 0.1508E+09 0.7562E+07 0.3638E+05 0.2863E-13 0.2957E+06 0.6273E+07 0.3264E-01 758.94 164 | 82137 51096 262.3 -78.1 154.2 0.47 161.1 135.9 12.0 0.9939E+07 0.9501E+09 0.3455E+09 0.1944E+08 0.2441E+06 0.4256E-13 0.1005E+06 0.5660E+07 0.9367E-01 1043.96 165 | 11168 39830 262.6 15.5 114.2 18.68 93.5 103.3 9.0 0.2792E+07 0.5658E+09 0.2222E+09 0.9478E+07 0.7593E+05 0.2613E-13 0.1632E+06 0.1013E+08 0.6652E-01 913.77 166 | 77034 71895 263.1 -18.1 170.3 7.33 79.3 84.5 11.0 0.4812E+07 0.6461E+09 0.1268E+09 0.7319E+07 0.3094E+05 0.2361E-13 0.3469E+06 0.5334E+07 0.7900E-01 749.17 167 | 82227 25107 266.4 -82.1 -2.1 6.83 165.8 172.3 3.0 0.1033E+08 0.8907E+09 0.2841E+09 0.1170E+08 0.1230E+06 0.3772E-13 0.1081E+06 0.6153E+07 0.3539E+00 977.90 168 | 10197 40860 269.5 9.0 47.7 14.53 77.9 75.9 3.0 0.3026E+07 0.5413E+09 0.1759E+09 0.7112E+07 0.4472E+05 0.2321E-13 0.2323E+06 0.1067E+08 0.9918E-01 839.61 169 | 11302 66860 272.0 -27.4 -178.3 6.68 146.7 133.9 0.0 0.8937E+07 0.9496E+09 0.1867E+09 0.6176E+07 0.4044E+05 0.3447E-13 0.1576E+06 0.6999E+07 0.2144E+00 913.36 170 | 75327 8157 278.9 5.5 -150.5 16.23 77.1 83.6 8.0 0.4994E+07 0.5543E+09 0.1616E+09 0.5794E+07 0.3604E+05 0.2288E-13 0.1674E+06 0.1262E+08 0.4711E+00 900.71 171 | 77181 8775 279.7 17.8 -6.3 2.02 84.9 108.3 9.0 0.2330E+07 0.3447E+09 0.8224E+08 0.3348E+07 0.1287E+05 0.1326E-13 0.3248E+06 0.3503E+07 0.1078E+01 753.03 172 | 81129 8805 281.1 -17.9 131.4 11.21 195.2 218.3 39.0 0.1200E+08 0.1659E+10 0.3441E+09 0.8960E+07 0.1225E+06 0.6159E-13 0.4364E+05 0.4049E+08 0.1912E+01 1266.07 173 | 10351 42720 282.6 -72.0 -99.4 5.24 83.8 84.1 4.0 0.8600E+06 0.2953E+09 0.1505E+09 0.6250E+07 0.4845E+05 0.1527E-13 0.1573E+06 0.3418E+07 0.8356E+01 927.16 174 | 77278 14894 284.6 -19.4 -31.6 2.03 96.5 98.7 8.0 0.6995E+07 0.4713E+09 0.8106E+08 0.2704E+07 0.1034E+05 0.1658E-13 0.3229E+06 0.4067E+07 0.1417E+01 769.75 175 | 9328 55200 285.1 -65.4 58.3 19.22 75.0 75.7 8.0 0.1117E+07 0.3260E+09 0.1354E+09 0.8942E+07 0.7431E+05 0.1560E-13 0.1387E+06 0.6693E+07 0.7123E+01 930.37 176 | 75293 83456 285.7 26.6 175.0 10.85 77.6 78.6 5.0 0.1030E+08 0.4707E+09 0.5603E+08 0.2925E+07 0.8701E+04 0.1551E-13 0.2859E+06 0.7707E+07 0.9017E+00 799.18 177 | 9051 37540 318.9 28.8 55.6 14.13 69.7 69.0 5.0 0.5466E+07 0.2037E+09 0.1965E+08 0.7426E+06 0.1708E+04 0.6509E-14 0.2708E+06 0.4589E+07 0.2562E+02 799.02 178 | 81306 49104 322.0 -55.0 94.3 19.93 221.5 225.9 5.0 0.3083E+07 0.7991E+09 0.2070E+09 0.4455E+07 0.1090E+06 0.3174E-13 0.2331E+05 0.2642E+08 0.9384E+03 1381.34 179 | 73067 40394 337.7 -68.6 71.1 15.96 101.0 93.6 7.0 0.2350E+07 0.1867E+09 0.3070E+08 0.1443E+07 0.6374E+04 0.6574E-14 0.9943E+05 0.4072E+07 0.5570E+03 991.25 180 | 76138 83955 349.9 4.2 -20.6 21.95 72.4 77.4 4.0 0.2403E+07 0.5468E+08 0.2015E+07 0.6723E+05 0.1157E+03 0.1586E-14 0.3428E+06 0.8322E+06 0.2325E+03 720.00 181 | 73185 12398 354.2 82.3 102.8 10.30 87.4 90.4 5.0 0.4449E+06 0.7518E+08 0.1681E+08 0.5234E+06 0.2550E+04 0.2862E-14 0.9986E+05 0.2226E+07 0.3868E+04 932.61 182 | 6283 41080 358.8 46.2 160.7 22.13 79.3 75.0 2.0 0.5110E+07 0.5670E+08 0.1630E+07 0.4404E+05 0.6111E+02 0.1644E-14 0.3725E+06 0.1066E+07 0.3756E+03 708.57 183 | 78227 81675 374.5 -8.8 32.6 0.86 134.4 129.3 3.0 0.3137E+07 0.7951E+08 0.3852E+07 0.7351E+05 0.1660E+03 0.2342E-14 0.1718E+06 0.1088E+07 0.4769E+03 847.45 184 | 76191 38513 374.6 25.0 -28.0 8.83 70.9 67.4 10.0 0.1239E+07 0.3609E+08 0.1448E+07 0.4714E+05 0.5998E+02 0.1054E-14 0.3019E+06 0.7240E+06 0.2005E+04 751.93 185 | 78040 76395 377.8 -24.9 108.2 4.43 133.6 161.3 16.0 0.2582E+07 0.1205E+09 0.8593E+07 0.1982E+06 0.7541E+03 0.3654E-14 0.9619E+05 0.1056E+07 0.2907E+04 962.02 186 | 78279 63960 379.2 -8.1 14.2 18.71 156.5 138.7 4.0 0.3138E+07 0.1962E+09 0.1783E+08 0.3375E+06 0.2317E+04 0.6206E-14 0.6862E+05 0.5332E+07 0.7463E+03 1126.46 187 | 74001 19005 379.8 -10.4 -124.4 20.99 83.9 74.5 17.0 0.1805E+07 0.4271E+08 0.1421E+07 0.3939E+05 0.7362E+02 0.1230E-14 0.2221E+06 0.6489E+06 0.2487E+04 779.97 188 | 80314 38265 386.7 -2.3 -94.8 4.31 213.6 271.4 9.0 0.6457E+07 0.2770E+09 0.1970E+08 0.1976E+06 0.1413E+04 0.8412E-14 0.4221E+05 0.3536E+07 0.1369E+04 1068.84 189 | 77229 78825 386.7 22.6 -89.7 15.92 87.8 85.1 29.0 0.1579E+07 0.7584E+08 0.5775E+07 0.1567E+06 0.4801E+03 0.2361E-14 0.1249E+06 0.2496E+07 0.4726E+04 967.82 190 | 77183 77580 397.2 63.6 56.4 1.31 85.1 98.7 11.0 0.4244E+06 0.2643E+08 0.2960E+07 0.7171E+05 0.1914E+03 0.8587E-15 0.1310E+06 0.4969E+06 0.1163E+05 884.34 191 | 74213 39510 399.1 -24.4 119.1 18.91 86.5 84.8 6.0 0.2531E+07 0.3257E+08 0.9459E+06 0.2968E+05 0.3580E+02 0.9457E-15 0.1936E+06 0.7635E+06 0.1112E+04 818.12 192 | 80228 60210 400.6 15.2 -112.7 9.21 180.0 186.9 5.0 0.3359E+07 0.1405E+09 0.1002E+08 0.1912E+06 0.8973E+03 0.4354E-14 0.5674E+05 0.5328E+07 0.2083E+04 1119.06 193 | 3338 77110 407.0 -47.8 0.9 21.48 136.1 123.8 6.0 0.1301E+07 0.8276E+08 0.6961E+07 0.1327E+06 0.6410E+03 0.2586E-14 0.7832E+05 0.2045E+07 0.1436E+05 1033.99 194 | 73081 82086 414.7 -78.1 23.7 0.38 102.1 88.1 53.0 0.2390E+07 0.4905E+08 0.3307E+07 0.1790E+06 0.4793E+03 0.1493E-14 0.8172E+05 0.4432E+06 0.4898E+04 979.00 195 | 88156 58563 434.6 3.0 -4.3 15.98 129.9 145.2 3.0 0.1808E+07 0.7268E+08 0.5764E+07 0.8466E+05 0.3325E+03 0.2289E-14 0.6491E+05 0.3160E+07 0.2337E+04 1166.44 196 | 79201 34500 448.1 15.5 -101.5 2.82 167.5 139.0 18.0 0.1493E+07 0.2899E+08 0.9769E+06 0.1097E+05 0.2810E+02 0.8370E-15 0.8473E+05 0.4491E+06 0.1196E+05 929.98 197 | 79026 70695 468.8 -19.5 44.6 22.61 193.1 212.7 28.0 0.1824E+07 0.7145E+08 0.2655E+07 0.3112E+05 0.1968E+03 0.2079E-14 0.3660E+05 0.1830E+07 0.2300E+05 1139.71 198 | 89039 31499 500.0 42.6 -71.5 3.98 225.1 216.4 14.0 0.4757E+07 0.3637E+08 0.6486E+06 0.5766E+04 0.1359E+02 0.1034E-14 0.4667E+05 0.2132E+06 0.1455E+05 1036.90 199 | 3128 34273 500.0 6.5 96.0 15.92 125.8 110.2 39.0 0.2853E+07 0.3467E+08 0.9328E+06 0.1061E+05 0.2437E+02 0.1017E-14 0.7232E+05 0.1378E+07 0.2465E+05 1116.07 200 | 6257 18301 500.0 -47.6 125.3 13.44 77.6 82.9 4.0 0.2489E+07 0.6229E+07 0.4587E+05 0.9514E+03 0.3957E+00 0.1879E-15 0.1415E+06 0.1426E+06 0.6172E+04 838.19 201 | 6022 4328 500.0 22.0 130.3 9.89 82.1 93.9 6.0 0.2537E+07 0.4276E+07 0.1299E+05 0.2828E+03 0.7444E-01 0.1339E-15 0.2284E+06 0.1003E+06 0.3559E+04 777.86 202 | -------------------------------------------------------------------------------- /tests/msis2.1_test_ref_dp.txt: -------------------------------------------------------------------------------- 1 | iyd sec alt glat glong stl f107a f107 Ap He O N2 O2 Ar rho H N O* NO T 2 | 70178 64800 0.2 50.0 55.0 21.67 153.3 146.5 35.0 0.1255E+15 0.9999E-37 0.1884E+20 0.5052E+19 0.2252E+18 0.1160E-02 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 294.10 3 | 70022 64800 0.6 -15.0 35.0 20.33 163.8 182.8 5.0 0.1190E+15 0.9999E-37 0.1787E+20 0.4792E+19 0.2136E+18 0.1100E-02 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 296.25 4 | 70280 43200 0.8 -10.0 160.0 22.67 149.4 128.1 4.0 0.1174E+15 0.9999E-37 0.1763E+20 0.4727E+19 0.2107E+18 0.1085E-02 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 293.36 5 | 70305 21600 1.0 -65.0 230.0 21.33 155.9 171.1 2.0 0.1254E+15 0.9999E-37 0.1884E+20 0.5051E+19 0.2251E+18 0.1160E-02 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 264.78 6 | 70281 0 1.0 -70.0 5.0 0.33 150.0 133.5 2.0 0.1308E+15 0.9999E-37 0.1964E+20 0.5267E+19 0.2347E+18 0.1209E-02 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 252.35 7 | 70036 43200 1.0 45.0 105.0 19.00 165.1 122.9 10.0 0.1241E+15 0.9999E-37 0.1863E+20 0.4997E+19 0.2227E+18 0.1147E-02 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 268.25 8 | 70184 21600 1.3 75.0 305.0 2.33 149.9 182.8 24.0 0.1181E+15 0.9999E-37 0.1773E+20 0.4755E+19 0.2119E+18 0.1091E-02 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 271.81 9 | 70003 21600 1.8 -90.0 55.0 9.67 152.9 149.5 9.0 0.1170E+15 0.9999E-37 0.1757E+20 0.4712E+19 0.2100E+18 0.1082E-02 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 254.18 10 | 70239 0 2.0 25.0 230.0 15.33 142.0 132.9 12.0 0.1039E+15 0.9999E-37 0.1561E+20 0.4185E+19 0.1865E+18 0.9608E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 287.80 11 | 70069 0 3.1 40.0 275.0 18.33 168.7 166.8 7.0 0.9564E+14 0.9999E-37 0.1436E+20 0.3851E+19 0.1716E+18 0.8841E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 269.19 12 | 70148 43200 3.1 -20.0 100.0 18.67 159.2 151.2 45.0 0.9350E+14 0.9999E-37 0.1404E+20 0.3765E+19 0.1678E+18 0.8643E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 279.82 13 | 70070 64800 3.2 -70.0 320.0 15.33 168.2 160.5 3.0 0.9843E+14 0.9999E-37 0.1478E+20 0.3964E+19 0.1766E+18 0.9099E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 251.74 14 | 70084 0 3.5 45.0 155.0 10.33 166.7 170.8 3.0 0.9349E+14 0.9999E-37 0.1404E+20 0.3765E+19 0.1678E+18 0.8642E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 258.34 15 | 70054 0 4.2 70.0 290.0 19.33 167.1 187.0 4.0 0.8809E+14 0.9999E-37 0.1323E+20 0.3547E+19 0.1581E+18 0.8143E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 242.78 16 | 70363 43200 4.6 -65.0 225.0 3.00 156.8 121.3 12.0 0.8310E+14 0.9999E-37 0.1248E+20 0.3346E+19 0.1491E+18 0.7682E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 249.43 17 | 70011 43200 4.9 75.0 125.0 20.33 156.3 139.1 4.0 0.8178E+14 0.9999E-37 0.1228E+20 0.3293E+19 0.1468E+18 0.7560E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 233.91 18 | 70206 0 5.9 -5.0 300.0 20.00 148.8 158.9 92.0 0.6919E+14 0.9999E-37 0.1039E+20 0.2786E+19 0.1242E+18 0.6396E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 268.30 19 | 70062 21600 7.4 20.0 325.0 3.67 169.1 176.1 15.0 0.5974E+14 0.9999E-37 0.8971E+19 0.2406E+19 0.1072E+18 0.5522E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 253.08 20 | 70332 43200 7.7 50.0 115.0 19.67 157.6 138.8 8.0 0.5819E+14 0.9999E-37 0.8738E+19 0.2343E+19 0.1044E+18 0.5379E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 229.60 21 | 70180 43200 11.8 -60.0 310.0 8.67 151.7 155.3 6.0 0.3240E+14 0.9999E-37 0.4865E+19 0.1305E+19 0.5814E+17 0.2995E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 209.01 22 | 70363 43200 12.2 -55.0 35.0 14.33 156.8 121.3 12.0 0.3096E+14 0.9999E-37 0.4650E+19 0.1247E+19 0.5557E+17 0.2862E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 219.78 23 | 70068 43200 12.2 -30.0 260.0 5.33 169.1 175.4 47.0 0.3456E+14 0.9999E-37 0.5190E+19 0.1392E+19 0.6203E+17 0.3195E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 218.03 24 | 70133 64800 14.4 -5.0 145.0 3.67 162.9 176.6 7.0 0.2714E+14 0.9999E-37 0.4075E+19 0.1093E+19 0.4870E+17 0.2509E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 203.16 25 | 70252 39617 16.4 -78.5 74.2 15.95 139.5 154.6 5.0 0.1424E+14 0.9999E-37 0.2138E+19 0.5732E+18 0.2555E+17 0.1316E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 186.14 26 | 70285 43200 18.6 -25.0 140.0 21.33 151.7 143.3 16.0 0.1290E+14 0.9999E-37 0.1937E+19 0.5194E+18 0.2315E+17 0.1192E-03 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 206.54 27 | 70303 21600 23.7 -55.0 320.0 3.33 155.1 191.6 9.0 0.4852E+13 0.9999E-37 0.7285E+18 0.1954E+18 0.8707E+16 0.4485E-04 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 219.18 28 | 70252 64800 26.4 -15.0 145.0 3.67 139.5 154.6 5.0 0.3449E+13 0.9999E-37 0.5180E+18 0.1389E+18 0.6190E+16 0.3188E-04 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 219.71 29 | 70090 0 26.5 -10.0 235.0 15.67 163.5 149.8 51.0 0.3345E+13 0.9999E-37 0.5023E+18 0.1347E+18 0.6003E+16 0.3092E-04 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 222.71 30 | 70211 43200 34.2 50.0 45.0 15.00 147.2 156.2 7.0 0.1124E+13 0.9999E-37 0.1688E+18 0.4527E+17 0.2017E+16 0.1039E-04 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 242.73 31 | 70248 27142 35.3 35.7 -28.5 5.64 140.9 160.7 8.0 0.9091E+12 0.9999E-37 0.1365E+18 0.3661E+17 0.1631E+16 0.8403E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 237.05 32 | 70230 48384 38.9 -66.1 165.5 0.47 141.4 147.9 36.0 0.3396E+12 0.9999E-37 0.5099E+17 0.1367E+17 0.6094E+15 0.3139E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 239.15 33 | 70008 33536 39.1 56.1 54.7 12.96 154.0 116.9 6.0 0.3887E+12 0.9999E-37 0.5838E+17 0.1565E+17 0.6977E+15 0.3594E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 245.01 34 | 70087 0 39.5 -45.0 295.0 19.67 165.2 159.9 21.0 0.4759E+12 0.9999E-37 0.7146E+17 0.1916E+17 0.8540E+15 0.4399E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 248.07 35 | 70165 6144 41.0 -42.9 -7.2 1.22 158.1 194.2 8.0 0.3296E+12 0.9999E-37 0.4950E+17 0.1327E+17 0.5916E+15 0.3047E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 239.29 36 | 70312 62592 41.3 67.5 -74.7 12.41 156.7 153.4 8.0 0.2890E+12 0.9999E-37 0.4340E+17 0.1164E+17 0.5186E+15 0.2671E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 228.28 37 | 70013 0 42.3 -15.0 125.0 8.33 157.7 177.4 4.0 0.3018E+12 0.9999E-37 0.4531E+17 0.1215E+17 0.5415E+15 0.2789E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 255.20 38 | 70297 64800 42.5 -55.0 5.0 18.33 151.5 150.5 14.0 0.2805E+12 0.9999E-37 0.4212E+17 0.1129E+17 0.5034E+15 0.2593E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 267.26 39 | 70151 39040 43.7 -3.0 -135.9 1.78 159.6 159.3 6.0 0.2611E+12 0.9999E-37 0.3920E+17 0.1051E+17 0.4685E+15 0.2413E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 261.94 40 | 70035 16786 44.0 13.3 30.5 6.70 165.3 127.3 13.0 0.2378E+12 0.9999E-37 0.3571E+17 0.9576E+16 0.4268E+15 0.2198E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 258.97 41 | 70046 38400 44.2 40.0 39.3 13.28 165.5 201.2 8.0 0.2243E+12 0.9999E-37 0.3368E+17 0.9032E+16 0.4026E+15 0.2073E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 261.12 42 | 70024 61055 44.9 -75.5 -13.5 16.06 164.6 158.3 7.0 0.2516E+12 0.9999E-37 0.3778E+17 0.1013E+17 0.4515E+15 0.2326E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 280.78 43 | 70072 26161 45.4 -80.8 322.4 4.76 167.5 166.5 8.0 0.1993E+12 0.9999E-37 0.2993E+17 0.8027E+16 0.3578E+15 0.1843E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 259.96 44 | 70355 49246 45.9 -57.1 207.5 3.51 159.6 160.8 4.0 0.2119E+12 0.9999E-37 0.3183E+17 0.8534E+16 0.3804E+15 0.1959E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 272.06 45 | 70035 35265 47.1 -18.2 132.1 18.60 165.3 127.3 13.0 0.1591E+12 0.9999E-37 0.2388E+17 0.6405E+16 0.2854E+15 0.1470E-05 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 267.41 46 | 70365 13741 51.9 -65.4 324.5 1.45 155.2 133.4 2.0 0.1054E+12 0.3340E+10 0.1583E+17 0.4246E+16 0.1892E+15 0.9747E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 280.96 47 | 70301 20270 52.0 -23.8 183.9 17.89 153.9 194.2 15.0 0.8959E+11 0.4934E+10 0.1345E+17 0.3607E+16 0.1608E+15 0.8281E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 260.58 48 | 70338 71881 52.1 66.3 259.3 13.25 158.3 152.4 5.0 0.5788E+11 0.3613E+10 0.8691E+16 0.2331E+16 0.1039E+15 0.5350E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 250.34 49 | 70225 29883 53.1 -25.3 139.5 17.60 142.7 133.3 7.0 0.7391E+11 0.4328E+10 0.1110E+17 0.2976E+16 0.1326E+15 0.6832E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 254.87 50 | 70037 32896 53.4 7.4 -110.3 1.78 164.9 124.5 4.0 0.7137E+11 0.5163E+08 0.1072E+17 0.2874E+16 0.1281E+15 0.6597E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 262.98 51 | 70250 16640 53.6 68.9 115.0 12.29 139.8 159.0 6.0 0.7479E+11 0.5117E+10 0.1123E+17 0.3012E+16 0.1342E+15 0.6914E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 259.74 52 | 70293 70912 53.8 -16.3 87.8 1.55 150.5 140.1 7.0 0.7193E+11 0.5082E+08 0.1080E+17 0.2896E+16 0.1291E+15 0.6649E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 257.37 53 | 70135 39420 54.1 -69.4 348.6 10.19 162.2 192.6 10.0 0.3717E+11 0.4207E+10 0.5582E+16 0.1497E+16 0.6671E+14 0.3436E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 257.12 54 | 11360 44281 55.0 -46.0 208.7 2.21 136.6 144.3 1.0 0.6618E+11 0.7561E+08 0.9938E+16 0.2665E+16 0.1188E+15 0.6118E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 261.28 55 | 70339 26112 55.2 77.8 56.0 10.99 158.3 158.6 8.0 0.3235E+11 0.2078E+09 0.4857E+16 0.1302E+16 0.5805E+14 0.2990E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 259.93 56 | 70254 54016 56.1 -81.3 91.4 21.10 139.6 141.9 1.0 0.3185E+11 0.1706E+09 0.4783E+16 0.1283E+16 0.5716E+14 0.2944E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 266.13 57 | 70123 60787 56.9 -6.8 348.1 16.09 160.7 158.2 10.0 0.5061E+11 0.6843E+10 0.7600E+16 0.2038E+16 0.9083E+14 0.4679E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 252.08 58 | 10338 58684 58.5 -48.1 70.3 20.99 84.0 86.9 1.0 0.4501E+11 0.2287E+09 0.6759E+16 0.1813E+16 0.8078E+14 0.4161E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 255.21 59 | 70333 82943 58.9 36.7 117.3 6.86 158.0 153.0 3.0 0.3356E+11 0.4720E+10 0.5039E+16 0.1351E+16 0.6022E+14 0.3102E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 238.67 60 | 13365 68019 60.8 -81.2 42.8 21.75 154.3 142.9 5.0 0.4022E+11 0.6157E+10 0.6040E+16 0.1620E+16 0.7218E+14 0.3718E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 271.28 61 | 70042 59285 62.0 -47.1 143.9 2.06 164.2 174.9 3.0 0.2824E+11 0.4085E+08 0.4241E+16 0.1137E+16 0.5069E+14 0.2611E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 237.73 62 | 70016 64466 62.6 52.7 104.9 0.90 159.6 186.0 17.0 0.1889E+11 0.5651E+08 0.2837E+16 0.7608E+15 0.3391E+14 0.1746E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 227.49 63 | 70052 38015 62.6 28.1 110.4 17.92 167.4 201.0 2.0 0.2291E+11 0.4590E+10 0.3440E+16 0.9225E+15 0.4111E+14 0.2118E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 229.00 64 | 70114 32400 62.9 -74.9 9.2 9.62 161.3 128.7 16.0 0.1356E+11 0.8568E+10 0.2036E+16 0.5458E+15 0.2433E+14 0.1253E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 243.58 65 | 12355 50214 65.4 35.6 168.5 1.18 121.4 113.5 8.0 0.1400E+11 0.5491E+08 0.2102E+16 0.5637E+15 0.2512E+14 0.1294E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 224.08 66 | 70242 13960 68.1 50.3 269.5 21.85 142.3 145.8 5.0 0.1239E+11 0.7528E+08 0.1860E+16 0.4988E+15 0.2223E+14 0.1145E-06 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 213.34 67 | 70054 61659 69.0 -17.9 18.0 18.33 167.1 187.0 4.0 0.1002E+11 0.5316E+10 0.1505E+16 0.4037E+15 0.1799E+14 0.9267E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 218.60 68 | 70269 70016 69.1 45.9 -93.6 13.21 145.7 154.9 6.0 0.9175E+10 0.7994E+10 0.1378E+16 0.3695E+15 0.1647E+14 0.8481E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 211.10 69 | 12104 45507 71.5 10.5 270.6 6.68 117.3 95.3 20.0 0.7022E+10 0.6242E+10 0.1054E+16 0.2828E+15 0.1260E+14 0.6491E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 199.16 70 | 70361 6656 71.6 64.7 16.8 2.97 158.0 126.5 8.0 0.4047E+10 0.3846E+09 0.6077E+15 0.1630E+15 0.7262E+13 0.3741E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 234.81 71 | 70128 75684 72.2 6.2 47.0 0.16 162.9 159.5 4.0 0.6653E+10 0.1136E+09 0.9991E+15 0.2679E+15 0.1194E+14 0.6150E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 194.11 72 | 70026 46656 72.6 39.3 274.8 7.28 165.4 154.3 3.0 0.4518E+10 0.6878E+10 0.6785E+15 0.1819E+15 0.8109E+13 0.4177E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 217.67 73 | 11137 43521 73.0 41.7 286.1 7.16 101.2 92.2 9.0 0.5741E+10 0.5860E+10 0.8621E+15 0.2312E+15 0.1030E+14 0.5307E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.9999E-37 203.13 74 | 70132 15320 73.6 -72.0 283.0 23.12 163.1 172.7 15.0 0.2809E+10 0.5839E+09 0.4218E+15 0.1131E+15 0.5040E+13 0.2596E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.4027E+08 227.21 75 | 70007 66215 75.3 -17.4 312.2 15.21 153.6 121.6 5.0 0.3833E+10 0.6183E+10 0.5756E+15 0.1544E+15 0.6879E+13 0.3543E-07 0.9999E-37 0.9999E-37 0.9999E-37 0.1596E+08 210.49 76 | 70253 28950 76.3 -30.3 -70.7 3.33 139.5 155.0 6.0 0.2961E+10 0.4547E+09 0.4445E+15 0.1192E+15 0.5313E+13 0.2737E-07 0.1642E+08 0.9999E-37 0.9999E-37 0.1425E+08 201.00 77 | 11146 37212 77.4 -13.4 321.4 7.77 98.4 80.3 6.0 0.2494E+10 0.7867E+10 0.3739E+15 0.1003E+15 0.4468E+13 0.2302E-07 0.1406E+09 0.9999E-37 0.9999E-37 0.8067E+07 194.94 78 | 70301 81666 77.8 52.1 53.6 2.26 154.6 194.2 15.0 0.2156E+10 0.1033E+10 0.3227E+15 0.8652E+14 0.3856E+13 0.1986E-07 0.5184E+08 0.9999E-37 0.9999E-37 0.2183E+08 200.27 79 | 70362 38908 77.9 -20.8 94.5 17.10 157.5 123.9 18.0 0.2614E+10 0.7854E+10 0.3915E+15 0.1050E+15 0.4679E+13 0.2410E-07 0.1368E+09 0.9999E-37 0.9999E-37 0.1497E+08 200.80 80 | 70232 25110 78.2 -30.3 -70.7 2.26 141.4 142.0 4.0 0.2124E+10 0.8911E+09 0.3177E+15 0.8520E+14 0.3797E+13 0.1956E-07 0.7304E+08 0.9999E-37 0.9999E-37 0.1219E+08 198.61 81 | 70079 2581 78.2 -82.5 74.6 5.69 167.9 131.3 4.0 0.2396E+10 0.4817E+10 0.3578E+15 0.9595E+14 0.4276E+13 0.2203E-07 0.9640E+08 0.9999E-37 0.9999E-37 0.1477E+08 193.48 82 | 70090 72448 78.6 57.6 -108.2 12.91 163.1 149.8 51.0 0.2017E+10 0.1130E+11 0.3010E+15 0.8071E+14 0.3597E+13 0.1853E-07 0.1265E+09 0.9999E-37 0.9999E-37 0.2024E+08 209.59 83 | 70145 20790 78.7 -30.3 -70.7 1.06 158.4 158.5 7.0 0.1994E+10 0.1194E+10 0.2976E+15 0.7981E+14 0.3557E+13 0.1832E-07 0.9840E+08 0.9999E-37 0.9999E-37 0.1090E+08 194.19 84 | 70335 24360 79.9 40.1 -105.2 23.75 158.3 156.8 1.0 0.1571E+10 0.3066E+10 0.2329E+15 0.6245E+14 0.2783E+13 0.1434E-07 0.1186E+09 0.9999E-37 0.9999E-37 0.1637E+08 200.91 85 | 70284 2730 80.9 -30.3 -70.7 20.05 151.5 148.0 15.0 0.1524E+10 0.8263E+10 0.2242E+15 0.6013E+14 0.2680E+13 0.1380E-07 0.1624E+09 0.9999E-37 0.9999E-37 0.1475E+08 195.86 86 | 70036 82500 80.9 69.3 16.8 0.03 164.9 122.9 10.0 0.1170E+10 0.2033E+11 0.1721E+15 0.4616E+14 0.2057E+13 0.1060E-07 0.8159E+08 0.9999E-37 0.9999E-37 0.4236E+08 215.14 87 | 70010 38912 81.4 75.5 -102.5 3.98 155.5 123.7 6.0 0.9669E+09 0.4276E+11 0.1417E+15 0.3800E+14 0.1694E+13 0.8725E-08 0.6366E+08 0.9999E-37 0.9999E-37 0.5969E+08 221.12 88 | 70077 30600 81.9 40.6 -105.1 1.49 167.7 135.0 6.0 0.1282E+10 0.8586E+10 0.1869E+15 0.5011E+14 0.2233E+13 0.1150E-07 0.1621E+09 0.9999E-37 0.9999E-37 0.1830E+08 197.55 89 | 10353 32086 82.3 -55.9 137.8 18.10 83.7 80.6 2.0 0.1785E+10 0.1083E+11 0.2582E+15 0.6907E+14 0.3078E+13 0.1589E-07 0.3759E+09 0.9999E-37 0.9999E-37 0.8302E+07 168.45 90 | 70081 28740 82.6 -30.3 -70.7 3.27 167.8 150.8 2.0 0.1153E+10 0.1250E+11 0.1668E+15 0.4472E+14 0.1993E+13 0.1027E-07 0.2162E+09 0.9999E-37 0.9999E-37 0.1291E+08 194.71 91 | 70325 32728 82.8 11.8 328.4 6.99 156.8 177.9 30.0 0.1089E+10 0.1731E+11 0.1574E+15 0.4220E+14 0.1881E+13 0.9688E-08 0.1896E+09 0.9999E-37 0.9999E-37 0.9564E+07 195.15 92 | 70060 16800 83.2 69.3 15.2 5.68 168.5 177.4 22.0 0.8831E+09 0.4533E+11 0.1271E+15 0.3407E+14 0.1519E+13 0.7823E-08 0.1105E+09 0.9999E-37 0.9999E-37 0.3571E+08 217.95 93 | 70250 12570 83.6 -30.3 -70.7 22.78 139.8 159.0 6.0 0.9437E+09 0.4746E+11 0.1349E+15 0.3618E+14 0.1612E+13 0.8307E-08 0.1626E+09 0.9999E-37 0.9999E-37 0.1545E+08 196.29 94 | 9029 33427 84.1 -48.2 245.3 1.64 69.7 69.5 4.0 0.1097E+10 0.2454E+11 0.1549E+15 0.4146E+14 0.1848E+13 0.9530E-08 0.3311E+09 0.9999E-37 0.9999E-37 0.1020E+08 172.72 95 | 12339 43649 88.8 -67.7 266.6 5.90 120.4 96.7 4.0 0.6003E+09 0.9982E+11 0.7589E+14 0.1981E+14 0.8791E+12 0.4644E-08 0.2212E+09 0.9999E-37 0.9999E-37 0.1097E+08 149.16 96 | 13160 72325 89.3 41.8 136.9 5.22 120.5 103.2 7.0 0.3940E+09 0.2631E+12 0.4948E+14 0.1306E+14 0.5791E+12 0.3041E-08 0.1439E+09 0.9999E-37 0.9999E-37 0.2458E+08 173.97 97 | 11147 66312 95.0 -44.6 11.8 19.21 98.0 82.7 11.0 0.2004E+09 0.5130E+12 0.1857E+14 0.4897E+13 0.2120E+12 0.1152E-08 0.4393E+08 0.8685E+05 0.9999E-37 0.3888E+08 186.96 98 | 12139 6690 95.0 -55.0 59.8 5.84 119.9 136.3 8.0 0.1977E+09 0.5809E+12 0.1811E+14 0.4782E+13 0.2067E+12 0.1126E-08 0.4016E+08 0.2289E+05 0.9999E-37 0.9528E+08 184.66 99 | 12104 37819 96.0 50.9 210.5 0.54 117.3 95.3 20.0 0.1987E+09 0.8001E+12 0.1815E+14 0.4681E+13 0.2031E+12 0.1128E-08 0.6931E+08 0.5275E+05 0.9999E-37 0.5162E+08 191.36 100 | 13135 34150 97.5 63.4 226.0 0.56 123.6 147.9 8.0 0.1516E+09 0.6957E+12 0.1218E+14 0.3018E+13 0.1290E+12 0.7540E-09 0.6854E+08 0.9230E+05 0.9999E-37 0.6886E+08 181.81 101 | 13319 80974 102.0 -82.3 66.2 2.91 145.6 175.7 10.0 0.8703E+08 0.2131E+12 0.4486E+13 0.1024E+13 0.4243E+11 0.2715E-09 0.3175E+08 0.1761E+06 0.9999E-37 0.9512E+08 198.98 102 | 8174 36372 110.0 62.7 2.3 10.25 66.5 64.9 3.0 0.2948E+08 0.5349E+11 0.1020E+13 0.2081E+12 0.7436E+10 0.6041E-10 0.1402E+08 0.2850E+06 0.9999E-37 0.2643E+08 280.27 103 | 11154 26650 115.0 10.6 42.1 10.21 95.8 111.6 3.0 0.4479E+08 0.9400E+11 0.5959E+12 0.1122E+12 0.3433E+10 0.3641E-10 0.8157E+07 0.1084E+07 0.9999E-37 0.3748E+08 277.19 104 | 10249 6525 115.0 -70.8 117.4 9.64 80.1 82.1 8.0 0.4178E+08 0.6777E+11 0.4989E+12 0.1107E+12 0.3340E+10 0.3111E-10 0.5642E+07 0.3614E+06 0.9999E-37 0.5196E+08 291.22 105 | 10169 72244 125.0 20.9 -147.3 10.25 74.8 70.4 5.0 0.2032E+08 0.3165E+11 0.1482E+12 0.2452E+11 0.6237E+09 0.9080E-11 0.4132E+07 0.2274E+07 0.4160E-34 0.1452E+08 408.06 106 | 95300 44459 127.7 -10.9 31.2 14.43 75.4 73.9 6.0 0.2568E+08 0.3500E+11 0.1392E+12 0.1792E+11 0.4983E+09 0.8391E-11 0.2762E+07 0.3840E+07 0.7857E-33 0.1266E+08 475.70 107 | 11104 81874 135.0 61.8 173.4 10.30 107.8 117.7 4.0 0.1712E+08 0.2051E+11 0.5937E+11 0.8120E+10 0.2130E+09 0.3753E-11 0.1394E+07 0.6171E+07 0.2433E-28 0.2022E+08 555.61 108 | 76028 53610 147.6 19.7 24.5 16.53 72.4 69.3 5.0 0.1332E+08 0.1133E+11 0.2604E+11 0.3006E+10 0.5427E+08 0.1676E-11 0.9610E+06 0.1420E+08 0.6838E-24 0.5771E+07 626.17 109 | 76032 25650 148.9 18.9 143.2 16.67 72.3 71.2 24.0 0.1364E+08 0.1175E+11 0.2514E+11 0.2919E+10 0.4852E+08 0.1640E-11 0.8669E+06 0.1511E+08 0.6140E-23 0.6665E+07 646.00 110 | 6264 42650 155.0 80.8 150.2 21.86 77.7 70.9 3.0 0.1190E+08 0.8226E+10 0.1279E+11 0.1803E+10 0.3382E+08 0.9118E-12 0.8136E+06 0.1051E+08 0.2784E-20 0.4776E+07 638.85 111 | 76264 1515 155.6 15.0 97.8 6.94 74.1 72.7 51.0 0.2021E+08 0.1449E+11 0.1781E+11 0.2266E+10 0.3527E+08 0.1337E-11 0.1204E+07 0.1234E+08 0.5298E-20 0.6786E+07 614.99 112 | 11014 39689 160.0 60.6 -11.5 10.26 87.1 79.5 9.0 0.2815E+08 0.9050E+10 0.1251E+11 0.1614E+10 0.1989E+08 0.9099E-12 0.5920E+06 0.1080E+08 0.3170E-19 0.4497E+07 665.88 113 | 11114 50370 160.0 -86.6 -172.3 2.50 104.5 119.1 6.0 0.2003E+08 0.8499E+10 0.9658E+10 0.1296E+10 0.2552E+08 0.7460E-12 0.4934E+06 0.1161E+08 0.1007E-18 0.5032E+07 719.60 114 | 74274 80445 164.3 -17.1 120.5 6.38 90.2 91.2 28.0 0.1822E+08 0.1123E+11 0.1111E+11 0.1152E+10 0.2032E+08 0.8781E-12 0.6999E+06 0.1209E+08 0.2760E-17 0.6054E+07 668.95 115 | 75363 7425 166.8 9.4 107.4 9.22 75.0 73.9 15.0 0.1435E+08 0.7864E+10 0.7174E+10 0.9580E+09 0.1118E+08 0.5948E-12 0.8260E+06 0.1731E+08 0.7202E-17 0.3533E+07 655.36 116 | 75340 54408 170.1 61.8 -91.8 8.99 77.0 79.7 7.0 0.3017E+08 0.6139E+10 0.6620E+10 0.9442E+09 0.7810E+07 0.5222E-12 0.5901E+06 0.9089E+07 0.3503E-16 0.1849E+07 673.94 117 | 74043 46665 173.1 36.8 -6.9 12.50 82.0 79.6 40.0 0.1626E+08 0.6683E+10 0.6990E+10 0.8224E+09 0.9916E+07 0.5479E-12 0.3933E+06 0.3412E+08 0.6431E-15 0.3714E+07 759.42 118 | 75352 6704 174.9 8.3 95.9 8.26 77.0 72.8 6.0 0.1417E+08 0.6281E+10 0.4974E+10 0.5970E+09 0.6109E+07 0.4308E-12 0.7802E+06 0.1665E+08 0.8094E-15 0.2585E+07 657.44 119 | 9305 984 175.0 -59.3 143.7 9.85 72.9 75.1 3.0 0.5159E+07 0.3857E+10 0.6177E+10 0.7613E+09 0.1300E+08 0.4316E-12 0.4432E+06 0.1940E+08 0.5799E-14 0.3472E+07 767.16 120 | 85081 56707 177.7 2.9 286.6 10.86 74.1 76.7 4.0 0.1461E+08 0.5862E+10 0.4701E+10 0.4549E+09 0.4967E+07 0.4000E-12 0.6748E+06 0.4291E+08 0.4412E-14 0.2051E+07 696.39 121 | 79095 21358 183.1 -1.7 64.0 10.20 178.5 183.4 52.0 0.2041E+08 0.1075E+11 0.5777E+10 0.2764E+09 0.7521E+07 0.5719E-12 0.1334E+06 0.9529E+08 0.6279E-12 0.4659E+07 955.08 122 | 83289 36443 188.9 -35.5 2.5 10.29 105.6 127.7 13.0 0.1056E+08 0.5027E+10 0.4041E+10 0.3369E+09 0.5457E+07 0.3411E-12 0.2095E+06 0.5389E+08 0.8709E-11 0.3690E+07 872.35 123 | 74024 62940 198.2 47.4 -35.8 15.09 83.4 83.6 5.0 0.1671E+08 0.3320E+10 0.2357E+10 0.1945E+09 0.1320E+07 0.2090E-12 0.2602E+06 0.2593E+08 0.1650E-09 0.7691E+06 768.89 124 | 75350 79275 199.6 44.4 154.6 8.33 77.1 74.1 14.0 0.3068E+08 0.3512E+10 0.1482E+10 0.1439E+09 0.7484E+06 0.1705E-12 0.5498E+06 0.1292E+08 0.3098E-09 0.5050E+06 659.39 125 | 75112 24270 205.5 -17.1 -118.0 22.88 70.7 67.5 15.0 0.1291E+08 0.2340E+10 0.9880E+09 0.8133E+08 0.6113E+06 0.1131E-12 0.6543E+06 0.2054E+08 0.7539E-08 0.1122E+06 675.00 126 | 86130 22044 206.1 -21.6 99.3 12.74 72.5 68.5 6.0 0.1548E+08 0.2475E+10 0.1321E+10 0.9257E+08 0.6884E+06 0.1330E-12 0.3871E+06 0.3281E+08 0.4674E-08 0.6358E+06 771.75 127 | 75354 3090 209.2 17.5 86.2 6.61 76.7 71.4 3.0 0.1560E+08 0.2515E+10 0.9402E+09 0.8276E+08 0.4616E+06 0.1153E-12 0.7340E+06 0.8241E+07 0.1078E-07 0.3860E+06 632.22 128 | 74057 42345 209.8 63.4 -59.9 7.77 79.6 80.2 28.0 0.7989E+07 0.1610E+10 0.1598E+10 0.2343E+09 0.2321E+07 0.1300E-12 0.2355E+06 0.1078E+08 0.3597E-07 0.1142E+07 848.01 129 | 83249 19422 211.3 44.1 -108.6 22.16 120.1 115.7 5.0 0.4982E+07 0.2162E+10 0.1551E+10 0.1033E+09 0.1202E+07 0.1358E-12 0.1970E+06 0.2709E+08 0.1413E-06 0.4008E+06 860.57 130 | 75293 34770 213.1 4.9 -143.6 0.09 77.7 78.6 5.0 0.1006E+08 0.2004E+10 0.8006E+09 0.5252E+08 0.4118E+06 0.9371E-13 0.5450E+06 0.1464E+08 0.7468E-07 0.1031E+06 699.22 131 | 76291 42555 216.1 18.5 -8.0 11.29 74.1 77.5 31.0 0.1359E+08 0.2381E+10 0.1022E+10 0.7755E+08 0.5683E+06 0.1157E-12 0.3251E+06 0.2658E+08 0.6768E-06 0.5305E+06 813.88 132 | 88326 66224 217.5 -2.8 -155.9 8.00 174.3 150.2 6.0 0.1335E+08 0.3726E+10 0.1532E+10 0.5897E+08 0.1081E+07 0.1743E-12 0.1125E+06 0.3451E+08 0.4010E-06 0.1193E+07 938.21 133 | 76071 5820 218.4 10.6 26.2 3.36 74.4 69.3 27.0 0.1157E+08 0.2085E+10 0.9462E+09 0.5764E+08 0.3605E+06 0.1029E-12 0.6757E+06 0.1198E+08 0.1605E-05 0.1238E+06 696.96 134 | 73133 57798 220.9 -19.5 -18.7 14.81 98.0 85.0 20.0 0.1107E+08 0.2172E+10 0.1235E+10 0.5663E+08 0.5149E+06 0.1191E-12 0.2021E+06 0.3627E+08 0.2093E-05 0.7819E+06 898.64 135 | 79092 38525 222.2 8.8 172.0 22.17 179.9 202.9 36.0 0.1030E+08 0.4115E+10 0.1283E+10 0.4988E+08 0.1406E+07 0.1732E-12 0.7511E+05 0.5864E+08 0.7476E-05 0.5215E+06 1034.56 136 | 73210 50056 224.2 81.3 -115.6 6.20 88.2 81.7 18.0 0.7411E+06 0.7382E+09 0.1286E+10 0.1239E+09 0.2027E+07 0.8649E-13 0.1452E+06 0.1491E+08 0.3483E-04 0.2907E+07 928.00 137 | 79080 80550 227.7 50.7 -10.0 21.71 185.9 185.6 5.0 0.7981E+07 0.3078E+10 0.1271E+10 0.4912E+08 0.1022E+07 0.1449E-12 0.6748E+05 0.5642E+08 0.5558E-04 0.3604E+06 1055.90 138 | 13191 22400 230.3 -10.7 -160.3 19.53 113.2 120.0 26.0 0.4724E+07 0.1330E+10 0.6968E+09 0.4537E+08 0.5419E+06 0.7073E-13 0.1580E+06 0.2178E+08 0.4874E-04 0.2705E+06 927.80 139 | 76010 17820 234.5 -7.7 27.0 6.75 73.8 71.8 47.0 0.6670E+07 0.1353E+10 0.4653E+09 0.3054E+08 0.2352E+06 0.5947E-13 0.3903E+06 0.7669E+07 0.4170E-03 0.2080E+06 740.34 140 | 83309 45247 234.6 -38.2 155.5 22.93 103.0 104.8 3.0 0.4593E+07 0.1351E+10 0.6432E+09 0.3321E+08 0.3116E+06 0.6799E-13 0.2208E+06 0.1534E+08 0.1949E-03 0.1360E+06 834.08 141 | 79079 49127 244.1 -1.1 129.0 22.25 186.6 179.2 6.0 0.7753E+07 0.2428E+10 0.5497E+09 0.1787E+08 0.3751E+06 0.9184E-13 0.8699E+05 0.3156E+08 0.6802E-03 0.2045E+06 1003.37 142 | 13111 770 244.6 39.3 112.5 7.71 123.6 104.9 2.0 0.1026E+08 0.1503E+10 0.3869E+09 0.1858E+08 0.1443E+06 0.5931E-13 0.2146E+06 0.1401E+08 0.2138E-02 0.2533E+06 866.25 143 | 75031 30540 245.1 18.2 -142.4 22.99 76.1 72.7 17.0 0.7704E+07 0.8550E+09 0.1676E+09 0.1168E+08 0.5647E+05 0.3136E-13 0.4222E+06 0.7561E+07 0.1371E-02 0.2046E+05 706.18 144 | 12167 1526 250.0 -12.7 -74.9 19.43 126.8 148.6 2.0 0.3677E+07 0.9591E+09 0.3812E+09 0.1966E+08 0.2151E+06 0.4471E-13 0.1234E+06 0.1764E+08 0.1383E-02 0.1459E+06 977.84 145 | 6239 55200 250.0 -84.7 -135.0 6.33 77.6 75.7 17.0 0.7384E+07 0.6516E+09 0.3185E+09 0.3178E+08 0.2516E+06 0.3395E-13 0.2499E+06 0.2698E+07 0.3264E-02 0.2498E+06 844.92 146 | 12178 13881 250.0 -6.9 -38.6 1.29 127.6 88.5 7.0 0.5180E+07 0.8139E+09 0.2842E+09 0.9765E+07 0.6544E+05 0.3556E-13 0.2933E+06 0.6650E+07 0.2493E-02 0.6681E+05 790.58 147 | 3187 68511 250.0 35.8 -51.9 15.57 127.9 141.9 9.0 0.1885E+07 0.1008E+10 0.6118E+09 0.2417E+08 0.3941E+06 0.5711E-13 0.6923E+05 0.2358E+08 0.1396E-01 0.7170E+06 1086.84 148 | 12213 82495 250.0 -6.9 -38.6 20.34 124.0 136.0 5.0 0.3330E+07 0.8950E+09 0.3111E+09 0.1721E+08 0.1824E+06 0.3949E-13 0.1418E+06 0.1277E+08 0.2153E-02 0.1003E+06 919.74 149 | 11328 8432 250.0 -6.9 -38.6 23.77 146.0 140.0 7.0 0.6764E+07 0.1439E+10 0.4399E+09 0.1501E+08 0.2008E+06 0.5989E-13 0.1324E+06 0.1473E+08 0.3472E-02 0.1122E+06 925.86 150 | 1139 7200 250.0 -86.4 0.0 2.00 163.0 138.3 10.0 0.1612E+08 0.1425E+10 0.4153E+09 0.2281E+08 0.2930E+06 0.5870E-13 0.1073E+06 0.7848E+07 0.9576E-02 0.2186E+06 960.11 151 | 6332 20181 250.0 -40.3 47.8 8.79 83.8 82.4 6.0 0.2946E+07 0.7570E+09 0.3711E+09 0.2490E+08 0.2128E+06 0.3898E-13 0.2068E+06 0.1090E+08 0.1057E-01 0.2254E+06 876.85 152 | 1126 47520 250.0 -86.4 45.0 16.20 169.2 160.7 5.0 0.1483E+08 0.1643E+10 0.4238E+09 0.2139E+08 0.3016E+06 0.6501E-13 0.9266E+05 0.1670E+08 0.1513E-01 0.1912E+06 1002.65 153 | 11200 75960 250.0 -84.7 -135.0 12.10 96.6 101.9 15.0 0.1808E+08 0.8168E+09 0.3198E+09 0.2454E+08 0.1615E+06 0.3809E-13 0.1727E+06 0.3440E+07 0.2348E-02 0.1917E+06 819.83 154 | 12261 81339 250.0 -6.9 -38.6 20.02 119.6 97.3 4.0 0.5152E+07 0.1048E+10 0.3016E+09 0.1473E+08 0.1554E+06 0.4304E-13 0.1801E+06 0.1419E+08 0.2213E-02 0.7333E+05 871.90 155 | 3069 10185 250.0 18.6 -175.0 15.17 125.9 152.8 16.0 0.7605E+07 0.1771E+10 0.7297E+09 0.2538E+08 0.3149E+06 0.8350E-13 0.8199E+05 0.4731E+08 0.5290E-02 0.5348E+06 1096.31 156 | 5335 78829 250.0 -48.0 154.4 8.19 85.9 94.6 17.0 0.1761E+07 0.6933E+09 0.4949E+09 0.3780E+08 0.5402E+06 0.4376E-13 0.1526E+06 0.1130E+08 0.1825E-01 0.4543E+06 974.05 157 | 5188 2905 250.0 5.5 -137.9 15.61 93.8 123.0 6.0 0.3046E+07 0.8589E+09 0.5194E+09 0.2057E+08 0.2165E+06 0.4862E-13 0.1283E+06 0.2230E+08 0.3178E-02 0.3946E+06 988.27 158 | 84302 25808 252.2 -10.6 103.9 14.09 75.0 69.5 8.0 0.6351E+07 0.1056E+10 0.4034E+09 0.1579E+08 0.1355E+06 0.4822E-13 0.2337E+06 0.2127E+08 0.6348E-02 0.1795E+06 859.40 159 | 77111 85830 258.0 -16.9 132.0 8.64 77.9 79.6 12.0 0.1509E+08 0.8928E+09 0.1280E+09 0.8772E+07 0.3171E+05 0.3048E-13 0.4004E+06 0.1011E+08 0.1871E-01 0.6265E+05 739.54 160 | 12016 83330 260.0 5.8 118.1 7.02 126.0 133.5 9.0 0.8242E+07 0.1146E+10 0.2341E+09 0.1184E+08 0.7976E+05 0.4222E-13 0.1771E+06 0.8696E+07 0.2242E-01 0.1606E+06 839.40 161 | 11081 11310 260.5 -7.2 52.8 6.66 110.9 101.0 8.0 0.1234E+08 0.1149E+10 0.1850E+09 0.8527E+07 0.4700E+05 0.3990E-13 0.2778E+06 0.9466E+07 0.2667E-01 0.9275E+05 770.48 162 | 12100 50610 260.9 -3.3 -105.8 7.01 116.5 93.2 3.0 0.1278E+08 0.1072E+10 0.1686E+09 0.7456E+07 0.3882E+05 0.3703E-13 0.2935E+06 0.9359E+07 0.1663E-01 0.8717E+05 755.67 163 | 11153 83910 262.3 -1.1 112.0 6.78 95.8 113.6 7.0 0.9570E+07 0.7905E+09 0.1508E+09 0.7562E+07 0.3638E+05 0.2863E-13 0.2957E+06 0.6273E+07 0.3264E-01 0.8837E+05 758.94 164 | 82137 51096 262.3 -78.1 154.2 0.47 161.1 135.9 12.0 0.9939E+07 0.9501E+09 0.3455E+09 0.1944E+08 0.2441E+06 0.4256E-13 0.1005E+06 0.5660E+07 0.9367E-01 0.1500E+06 1043.96 165 | 11168 39830 262.6 15.5 114.2 18.68 93.5 103.3 9.0 0.2792E+07 0.5658E+09 0.2222E+09 0.9478E+07 0.7593E+05 0.2613E-13 0.1632E+06 0.1013E+08 0.6652E-01 0.9797E+05 913.77 166 | 77034 71895 263.1 -18.1 170.3 7.33 79.3 84.5 11.0 0.4812E+07 0.6461E+09 0.1268E+09 0.7319E+07 0.3094E+05 0.2361E-13 0.3469E+06 0.5334E+07 0.7900E-01 0.5738E+05 749.17 167 | 82227 25107 266.4 -82.1 -2.1 6.83 165.8 172.3 3.0 0.1033E+08 0.8907E+09 0.2841E+09 0.1170E+08 0.1230E+06 0.3772E-13 0.1081E+06 0.6153E+07 0.3539E+00 0.9858E+05 977.90 168 | 10197 40860 269.5 9.0 47.7 14.53 77.9 75.9 3.0 0.3026E+07 0.5413E+09 0.1759E+09 0.7112E+07 0.4472E+05 0.2321E-13 0.2323E+06 0.1067E+08 0.9918E-01 0.1020E+06 839.61 169 | 11302 66860 272.0 -27.4 -178.3 6.68 146.7 133.9 0.0 0.8937E+07 0.9496E+09 0.1867E+09 0.6176E+07 0.4044E+05 0.3447E-13 0.1576E+06 0.6999E+07 0.2144E+00 0.1164E+06 913.36 170 | 75327 8157 278.9 5.5 -150.5 16.23 77.1 83.6 8.0 0.4994E+07 0.5543E+09 0.1616E+09 0.5794E+07 0.3604E+05 0.2288E-13 0.1674E+06 0.1262E+08 0.4711E+00 0.7356E+05 900.71 171 | 77181 8775 279.7 17.8 -6.3 2.02 84.9 108.3 9.0 0.2330E+07 0.3447E+09 0.8224E+08 0.3348E+07 0.1287E+05 0.1326E-13 0.3248E+06 0.3503E+07 0.1078E+01 0.1510E+05 753.03 172 | 81129 8805 281.1 -17.9 131.4 11.21 195.2 218.3 39.0 0.1200E+08 0.1659E+10 0.3441E+09 0.8960E+07 0.1225E+06 0.6159E-13 0.4364E+05 0.4049E+08 0.1912E+01 0.2924E+06 1266.07 173 | 10351 42720 282.6 -72.0 -99.4 5.24 83.8 84.1 4.0 0.8600E+06 0.2953E+09 0.1505E+09 0.6250E+07 0.4845E+05 0.1527E-13 0.1573E+06 0.3418E+07 0.8356E+01 0.1038E+06 927.16 174 | 77278 14894 284.6 -19.4 -31.6 2.03 96.5 98.7 8.0 0.6995E+07 0.4713E+09 0.8106E+08 0.2704E+07 0.1034E+05 0.1658E-13 0.3229E+06 0.4067E+07 0.1417E+01 0.1174E+05 769.75 175 | 9328 55200 285.1 -65.4 58.3 19.22 75.0 75.7 8.0 0.1117E+07 0.3260E+09 0.1354E+09 0.8942E+07 0.7431E+05 0.1560E-13 0.1387E+06 0.6693E+07 0.7123E+01 0.9259E+05 930.37 176 | 75293 83456 285.7 26.6 175.0 10.85 77.6 78.6 5.0 0.1030E+08 0.4707E+09 0.5603E+08 0.2925E+07 0.8701E+04 0.1551E-13 0.2859E+06 0.7707E+07 0.9017E+00 0.2273E+05 799.18 177 | 9051 37540 318.9 28.8 55.6 14.13 69.7 69.0 5.0 0.5466E+07 0.2037E+09 0.1965E+08 0.7426E+06 0.1708E+04 0.6509E-14 0.2708E+06 0.4589E+07 0.2562E+02 0.6157E+04 799.02 178 | 81306 49104 322.0 -55.0 94.3 19.93 221.5 225.9 5.0 0.3083E+07 0.7991E+09 0.2070E+09 0.4455E+07 0.1090E+06 0.3174E-13 0.2331E+05 0.2642E+08 0.9384E+03 0.8630E+05 1381.34 179 | 73067 40394 337.7 -68.6 71.1 15.96 101.0 93.6 7.0 0.2350E+07 0.1867E+09 0.3070E+08 0.1443E+07 0.6374E+04 0.6574E-14 0.9943E+05 0.4072E+07 0.5570E+03 0.1707E+05 991.25 180 | 76138 83955 349.9 4.2 -20.6 21.95 72.4 77.4 4.0 0.2403E+07 0.5468E+08 0.2015E+07 0.6723E+05 0.1157E+03 0.1586E-14 0.3428E+06 0.8322E+06 0.2325E+03 0.1785E+03 720.00 181 | 73185 12398 354.2 82.3 102.8 10.30 87.4 90.4 5.0 0.4449E+06 0.7518E+08 0.1681E+08 0.5234E+06 0.2550E+04 0.2862E-14 0.9986E+05 0.2226E+07 0.3868E+04 0.1544E+05 932.61 182 | 6283 41080 358.8 46.2 160.7 22.13 79.3 75.0 2.0 0.5110E+07 0.5670E+08 0.1630E+07 0.4404E+05 0.6111E+02 0.1644E-14 0.3725E+06 0.1066E+07 0.3756E+03 0.9071E+02 708.57 183 | 78227 81675 374.5 -8.8 32.6 0.86 134.4 129.3 3.0 0.3137E+07 0.7951E+08 0.3852E+07 0.7351E+05 0.1660E+03 0.2342E-14 0.1718E+06 0.1088E+07 0.4769E+03 0.7267E+03 847.45 184 | 76191 38513 374.6 25.0 -28.0 8.83 70.9 67.4 10.0 0.1239E+07 0.3609E+08 0.1448E+07 0.4714E+05 0.5998E+02 0.1054E-14 0.3019E+06 0.7240E+06 0.2005E+04 0.5502E+03 751.93 185 | 78040 76395 377.8 -24.9 108.2 4.43 133.6 161.3 16.0 0.2582E+07 0.1205E+09 0.8593E+07 0.1982E+06 0.7541E+03 0.3654E-14 0.9619E+05 0.1056E+07 0.2907E+04 0.2969E+04 962.02 186 | 78279 63960 379.2 -8.1 14.2 18.71 156.5 138.7 4.0 0.3138E+07 0.1962E+09 0.1783E+08 0.3375E+06 0.2317E+04 0.6206E-14 0.6862E+05 0.5332E+07 0.7463E+03 0.6193E+04 1126.46 187 | 74001 19005 379.8 -10.4 -124.4 20.99 83.9 74.5 17.0 0.1805E+07 0.4271E+08 0.1421E+07 0.3939E+05 0.7362E+02 0.1230E-14 0.2221E+06 0.6489E+06 0.2487E+04 0.1450E+03 779.97 188 | 80314 38265 386.7 -2.3 -94.8 4.31 213.6 271.4 9.0 0.6457E+07 0.2770E+09 0.1970E+08 0.1976E+06 0.1413E+04 0.8412E-14 0.4221E+05 0.3536E+07 0.1369E+04 0.9894E+04 1068.84 189 | 77229 78825 386.7 22.6 -89.7 15.92 87.8 85.1 29.0 0.1579E+07 0.7584E+08 0.5775E+07 0.1567E+06 0.4801E+03 0.2361E-14 0.1249E+06 0.2496E+07 0.4726E+04 0.3318E+04 967.82 190 | 77183 77580 397.2 63.6 56.4 1.31 85.1 98.7 11.0 0.4244E+06 0.2643E+08 0.2960E+07 0.7171E+05 0.1914E+03 0.8587E-15 0.1310E+06 0.4969E+06 0.1163E+05 0.1831E+04 884.34 191 | 74213 39510 399.1 -24.4 119.1 18.91 86.5 84.8 6.0 0.2531E+07 0.3257E+08 0.9459E+06 0.2968E+05 0.3580E+02 0.9457E-15 0.1936E+06 0.7635E+06 0.1112E+04 0.1780E+03 818.12 192 | 80228 60210 400.6 15.2 -112.7 9.21 180.0 186.9 5.0 0.3359E+07 0.1405E+09 0.1002E+08 0.1912E+06 0.8973E+03 0.4354E-14 0.5674E+05 0.5328E+07 0.2083E+04 0.6673E+04 1119.06 193 | 3338 77110 407.0 -47.8 0.9 21.48 136.1 123.8 6.0 0.1301E+07 0.8276E+08 0.6961E+07 0.1327E+06 0.6410E+03 0.2586E-14 0.7832E+05 0.2045E+07 0.1436E+05 0.2280E+04 1033.99 194 | 73081 82086 414.7 -78.1 23.7 0.38 102.1 88.1 53.0 0.2390E+07 0.4905E+08 0.3307E+07 0.1790E+06 0.4793E+03 0.1493E-14 0.8172E+05 0.4432E+06 0.4898E+04 0.2268E+04 979.00 195 | 88156 58563 434.6 3.0 -4.3 15.98 129.9 145.2 3.0 0.1808E+07 0.7268E+08 0.5764E+07 0.8466E+05 0.3325E+03 0.2289E-14 0.6491E+05 0.3160E+07 0.2337E+04 0.3254E+04 1166.44 196 | 79201 34500 448.1 15.5 -101.5 2.82 167.5 139.0 18.0 0.1493E+07 0.2899E+08 0.9769E+06 0.1097E+05 0.2810E+02 0.8370E-15 0.8473E+05 0.4491E+06 0.1196E+05 0.2836E+03 929.98 197 | 79026 70695 468.8 -19.5 44.6 22.61 193.1 212.7 28.0 0.1824E+07 0.7145E+08 0.2655E+07 0.3112E+05 0.1968E+03 0.2079E-14 0.3660E+05 0.1830E+07 0.2300E+05 0.6894E+03 1139.71 198 | 89039 31499 500.0 42.6 -71.5 3.98 225.1 216.4 14.0 0.4757E+07 0.3637E+08 0.6486E+06 0.5766E+04 0.1359E+02 0.1034E-14 0.4667E+05 0.2132E+06 0.1455E+05 0.1051E+03 1036.90 199 | 3128 34273 500.0 6.5 96.0 15.92 125.8 110.2 39.0 0.2853E+07 0.3467E+08 0.9328E+06 0.1061E+05 0.2437E+02 0.1017E-14 0.7232E+05 0.1378E+07 0.2465E+05 0.4057E+03 1116.07 200 | 6257 18301 500.0 -47.6 125.3 13.44 77.6 82.9 4.0 0.2489E+07 0.6229E+07 0.4587E+05 0.9514E+03 0.3957E+00 0.1879E-15 0.1415E+06 0.1426E+06 0.6172E+04 0.1105E+02 838.19 201 | 6022 4328 500.0 22.0 130.3 9.89 82.1 93.9 6.0 0.2537E+07 0.4276E+07 0.1299E+05 0.2828E+03 0.7444E-01 0.1339E-15 0.2284E+06 0.1003E+06 0.3559E+04 0.3811E+01 777.86 202 | -------------------------------------------------------------------------------- /tests/test_msis.py: -------------------------------------------------------------------------------- 1 | import concurrent.futures 2 | from unittest.mock import patch 3 | 4 | import numpy as np 5 | import pytest 6 | from numpy.testing import assert_allclose, assert_array_equal 7 | 8 | import pymsis 9 | from pymsis import msis, msis00f, msis20f, msis21f 10 | 11 | 12 | @pytest.fixture 13 | def input_data(): 14 | date = np.datetime64("2010-01-01T12:00") 15 | lon = 0 16 | lat = 0 17 | alt = 200 18 | f107 = 150 19 | f107a = 150 20 | ap = [[3] * 7] 21 | return (date, lon, lat, alt, f107, f107a, ap) 22 | 23 | 24 | @pytest.fixture 25 | def expected_input(): 26 | return [1.0, 86400 / 2, 0, 0, 200, 150, 150] + [3] * 7 27 | 28 | 29 | @pytest.fixture 30 | def expected_output(): 31 | return np.array( 32 | [ 33 | 2.466441e-10, 34 | 2.929253e15, 35 | 1.250981e14, 36 | 3.845766e15, 37 | 8.506817e12, 38 | 1.300092e11, 39 | 2.662435e12, 40 | 5.717013e13, 41 | 3.302753e-04, 42 | 2.204773e12, 43 | 9.846238e02, 44 | ], 45 | dtype=np.float32, 46 | ) 47 | 48 | 49 | @pytest.fixture 50 | def expected_output_with_options(): 51 | return np.array( 52 | [ 53 | 2.427699e-10, 54 | 2.849738e15, 55 | 1.364307e14, 56 | 3.836351e15, 57 | 9.207778e12, 58 | 1.490457e11, 59 | 2.554763e12, 60 | 3.459567e13, 61 | 8.023306e-04, 62 | 1.326593e12, 63 | 9.277623e02, 64 | ], 65 | dtype=np.float32, 66 | ) 67 | 68 | 69 | @pytest.fixture 70 | def expected_output00(): 71 | return np.array( 72 | [ 73 | 2.790941e-10, 74 | 3.354463e15, 75 | 1.242698e14, 76 | 4.331106e15, 77 | 8.082919e12, 78 | 1.126601e11, 79 | 2.710179e12, 80 | 5.634838e13, 81 | 1.665595e-03, 82 | np.nan, 83 | 9.838066e02, 84 | ], 85 | dtype=np.float32, 86 | ) 87 | 88 | 89 | @pytest.fixture 90 | def input_auto_f107_ap(): 91 | return ( 92 | np.datetime64("2000-07-01T12:00"), 93 | 0, 94 | 0, 95 | 200, 96 | 159.6, 97 | 186.3, 98 | [[7, 4, 5, 9, 4, 5.25, 5.75]], 99 | ) 100 | 101 | 102 | def test_create_options(): 103 | options = [ 104 | "f107", 105 | "time_independent", 106 | "symmetrical_annual", 107 | "symmetrical_semiannual", 108 | "asymmetrical_annual", 109 | "asymmetrical_semiannual", 110 | "diurnal", 111 | "semidiurnal", 112 | "geomagnetic_activity", 113 | "all_ut_effects", 114 | "longitudinal", 115 | "mixed_ut_long", 116 | "mixed_ap_ut_long", 117 | "terdiurnal", 118 | ] 119 | 120 | # Default is all 1's 121 | assert [1] * 25 == msis.create_options() 122 | 123 | # Check each individual keyword argument 124 | for i, opt in enumerate(options): 125 | expected = [1] * 25 126 | expected[i] = 0 127 | assert expected == msis.create_options(**{opt: 0}) 128 | 129 | # Check multiple keyword arguments 130 | expected = [0] * 14 + [1] * 11 131 | assert expected == msis.create_options(**{opt: 0 for opt in options}) 132 | 133 | 134 | def test_create_input_single_point(input_data, expected_input): 135 | shape, data = msis.create_input(*input_data) 136 | assert shape == (1,) 137 | assert data.shape == (1, 14) 138 | assert_array_equal(data[0, :], expected_input) 139 | 140 | 141 | def test_create_input_datetime(input_data, expected_input): 142 | # Test with datetime, not just np.datetime64s 143 | # .item() gets the datetime object from the np.datetime64 object 144 | input_data = (input_data[0].item(),) + input_data[1:] 145 | shape, data = msis.create_input(*input_data) 146 | assert shape == (1,) 147 | assert data.shape == (1, 14) 148 | assert_array_equal(data[0, :], expected_input) 149 | 150 | 151 | def test_create_input_f107_date_mismatch(input_data): 152 | # Make sure we raise when f107 and dates are different shapes 153 | # Repeat 5 dates, but not f107 154 | input_data = ([input_data[0]] * 5,) + input_data[1:] 155 | with pytest.raises(ValueError, match="The length of dates"): 156 | msis.create_input(*input_data) 157 | 158 | 159 | def test_create_input_multi_date(input_data, expected_input): 160 | date, lon, lat, alt, f107, f107a, ap = input_data 161 | # Repeat 5 dates 162 | input_data = ([date] * 5, lon, lat, alt, [f107] * 5, [f107a] * 5, ap * 5) 163 | shape, data = msis.create_input(*input_data) 164 | assert shape == (5, 1, 1, 1) 165 | assert data.shape == (5, 14) 166 | assert_array_equal(data, [expected_input] * 5) 167 | 168 | 169 | def test_create_input_multi_lon(input_data, expected_input): 170 | date, lon, lat, alt, f107, f107a, ap = input_data 171 | # Repeat 5 dates 172 | input_data = (date, [lon] * 5, lat, alt, f107, f107a, ap) 173 | shape, data = msis.create_input(*input_data) 174 | assert shape == (1, 5, 1, 1) 175 | assert data.shape == (5, 14) 176 | assert_array_equal(data, [expected_input] * 5) 177 | 178 | 179 | def test_create_input_multi_lat(input_data, expected_input): 180 | date, lon, lat, alt, f107, f107a, ap = input_data 181 | # Repeat 5 dates 182 | input_data = (date, lon, [lat] * 5, alt, f107, f107a, ap) 183 | shape, data = msis.create_input(*input_data) 184 | assert shape == (1, 1, 5, 1) 185 | assert data.shape == (5, 14) 186 | assert_array_equal(data, [expected_input] * 5) 187 | 188 | 189 | def test_create_input_multi_alt(input_data, expected_input): 190 | date, lon, lat, alt, f107, f107a, ap = input_data 191 | # Repeat 5 dates 192 | input_data = (date, lon, lat, [alt] * 5, f107, f107a, ap) 193 | shape, data = msis.create_input(*input_data) 194 | assert shape == (1, 1, 1, 5) 195 | assert data.shape == (5, 14) 196 | assert_array_equal(data, [expected_input] * 5) 197 | 198 | 199 | def test_create_input_multi_lon_lat(input_data, expected_input): 200 | date, lon, lat, alt, f107, f107a, ap = input_data 201 | # Repeat 5 dates 202 | input_data = (date, [lon] * 5, [lat] * 5, alt, f107, f107a, ap) 203 | shape, data = msis.create_input(*input_data) 204 | assert shape == (1, 5, 5, 1) 205 | assert data.shape == (5 * 5, 14) 206 | assert_array_equal(data, [expected_input] * 5 * 5) 207 | 208 | 209 | @pytest.mark.parametrize("version", ["0", "2.0", "2.1"]) 210 | def test_create_input_lon_wrapping(input_data, expected_input, version): 211 | date, lon, lat, alt, f107, f107a, ap = input_data 212 | # Repeat 5 dates 213 | lons = np.array([-90] * 5) 214 | input_data = (date, lons, [lat] * 5, alt, f107, f107a, ap) 215 | shape, data = msis.create_input(*input_data) 216 | assert shape == (1, 5, 5, 1) 217 | assert data.shape == (5 * 5, 14) 218 | expected_input[2] = -90 219 | assert_array_equal(data, [expected_input] * 5 * 5) 220 | # Make sure that our input lons array wasn't transfomrmed inplace 221 | assert_array_equal(lons, [-90] * 5) 222 | 223 | # Test that -90 and 270 produce the same output 224 | assert_array_equal( 225 | pymsis.calculate(date, lons, [lat] * 5, alt, f107, f107a, ap, version=version), 226 | pymsis.calculate( 227 | date, lons + 360, [lat] * 5, alt, f107, f107a, ap, version=version 228 | ), 229 | ) 230 | 231 | 232 | def test_calculate_options(input_data, expected_output): 233 | # Default options is all 1's, so make sure they are equivalent 234 | assert_allclose( 235 | np.squeeze(pymsis.calculate(*input_data, options=None)), 236 | expected_output, 237 | rtol=1e-5, 238 | ) 239 | assert_allclose( 240 | np.squeeze(pymsis.calculate(*input_data, options=[1] * 25)), 241 | expected_output, 242 | rtol=1e-5, 243 | ) 244 | 245 | with pytest.raises(ValueError, match="options needs to be a list"): 246 | pymsis.calculate(*input_data, options=[1] * 22) 247 | 248 | 249 | def test_calculate_single_point(input_data, expected_output): 250 | output = pymsis.calculate(*input_data) 251 | assert output.shape == (1, 11) 252 | assert_allclose(np.squeeze(output), expected_output, rtol=1e-5) 253 | 254 | 255 | def test_calculate_gridded_multi_point(input_data, expected_output): 256 | date, lon, lat, alt, f107, f107a, ap = input_data 257 | # 5 x 5 surface 258 | input_data = (date, [lon] * 5, [lat] * 5, alt, f107, f107a, ap) 259 | output = pymsis.calculate(*input_data) 260 | assert output.shape == (1, 5, 5, 1, 11) 261 | expected = np.tile(expected_output, (5, 5, 1)) 262 | assert_allclose(np.squeeze(output), expected, rtol=1e-5) 263 | 264 | 265 | def test_calculate_multi_point(input_data, expected_output): 266 | # test multi-point run, like a satellite fly-through 267 | # and make sure we don't grid the input data 268 | # 5 input points 269 | date, lon, lat, alt, f107, f107a, ap = input_data 270 | input_data = ( 271 | [date] * 5, 272 | [lon] * 5, 273 | [lat] * 5, 274 | [alt] * 5, 275 | [f107] * 5, 276 | [f107a] * 5, 277 | ap * 5, 278 | ) 279 | output = pymsis.calculate(*input_data) 280 | assert output.shape == (5, 11) 281 | expected = np.tile(expected_output, (5, 1)) 282 | assert_allclose(np.squeeze(output), expected, rtol=1e-5) 283 | 284 | 285 | def test_calculate_wrapped_lon(input_data, expected_output): 286 | date, _, lat, alt, f107, f107a, ap = input_data 287 | 288 | input_data = (date, -180, lat, alt, f107, f107a, ap) 289 | output1 = pymsis.calculate(*input_data) 290 | input_data = (date, 180, lat, alt, f107, f107a, ap) 291 | output2 = pymsis.calculate(*input_data) 292 | assert_allclose(output1, output2, rtol=1e-5) 293 | 294 | input_data = (date, 0, lat, alt, f107, f107a, ap) 295 | output1 = pymsis.calculate(*input_data) 296 | input_data = (date, 360, lat, alt, f107, f107a, ap) 297 | output2 = pymsis.calculate(*input_data) 298 | assert_allclose(output1, output2, rtol=1e-5) 299 | 300 | 301 | def test_calculate_poles(input_data): 302 | # Test that moving in longitude around a pole 303 | # returns the same values 304 | # North pole 305 | date, _, _, alt, f107, f107a, ap = input_data 306 | input_data = (date, 0, 90, alt, f107, f107a, ap) 307 | output1 = pymsis.calculate(*input_data) 308 | input_data = (date, 20, 90, alt, f107, f107a, ap) 309 | output2 = pymsis.calculate(*input_data) 310 | assert_allclose(output1, output2, rtol=1e-5) 311 | 312 | # South pole 313 | input_data = (date, 0, -90, alt, f107, f107a, ap) 314 | output1 = pymsis.calculate(*input_data) 315 | input_data = (date, 20, -90, alt, f107, f107a, ap) 316 | output2 = pymsis.calculate(*input_data) 317 | assert_allclose(output1, output2, rtol=1e-5) 318 | 319 | 320 | def test_calculate_versions(input_data): 321 | # Make sure we accept these version numbers and don't throw an error 322 | for ver in [0, 2, 2.0, 2.1, "00", "0", "2.0", "2.1", "2"]: 323 | pymsis.calculate(*input_data, version=ver) 324 | # Test for something outside of that list for an error to be thrown 325 | with pytest.raises(ValueError, match="The MSIS version selected"): 326 | pymsis.calculate(*input_data, version=1) 327 | 328 | 329 | def test_calculate_version00(input_data, expected_output00): 330 | output = pymsis.calculate(*input_data, version=0) 331 | assert output.shape == (1, 11) 332 | assert_allclose(np.squeeze(output), expected_output00, rtol=1e-5) 333 | 334 | 335 | def test_calculate_multi_point00(input_data, expected_output00): 336 | date, lon, lat, alt, f107, f107a, ap = input_data 337 | # 5 x 5 surface 338 | input_data = (date, [lon] * 5, [lat] * 5, alt, f107, f107a, ap) 339 | output = pymsis.calculate(*input_data, version=0) 340 | assert output.shape == (1, 5, 5, 1, 11) 341 | expected = np.tile(expected_output00, (5, 5, 1)) 342 | assert_allclose(np.squeeze(output), expected, rtol=1e-5) 343 | 344 | 345 | def test_calculate_version00_low_altitude(input_data): 346 | # There is no O, H, N below 72.5 km, should be NaN 347 | date, lon, lat, _, f107, f107a, ap = input_data 348 | input_data = (date, lon, lat, 71, f107, f107a, ap) 349 | output = pymsis.calculate(*input_data, version=0) 350 | assert np.all(np.isnan(np.squeeze(output)[[3, 5, 7]])) 351 | 352 | 353 | def test_create_input_auto_f107(input_auto_f107_ap): 354 | date, lon, lat, alt, f107, f107a, ap = input_auto_f107_ap 355 | 356 | # What we put in is what we get out 357 | shape, data = msis.create_input(*input_auto_f107_ap) 358 | assert shape == (1,) 359 | assert data.shape == (1, 14) 360 | assert_allclose(data[0, :], [183, 43200, lon, lat, alt, f107, f107a, *ap[0]]) 361 | 362 | # No f107 363 | _, test_data = msis.create_input(date, lon, lat, alt, f107as=f107a, aps=ap) 364 | assert_allclose(data, test_data) 365 | 366 | # No f107a 367 | _, test_data = msis.create_input(date, lon, lat, alt, f107s=f107, aps=ap) 368 | assert_allclose(data, test_data) 369 | 370 | # No ap 371 | _, test_data = msis.create_input(date, lon, lat, alt, f107s=f107, f107as=f107a) 372 | assert_allclose(data, test_data) 373 | 374 | # Nothing auto-fills 375 | _, test_data = msis.create_input(date, lon, lat, alt) 376 | assert_allclose(data, test_data) 377 | 378 | 379 | def test_calculate_auto_f107(input_auto_f107_ap): 380 | # Dropping any of the f107/f107a/ap will go and get those 381 | # values automatically as needed 382 | date, lon, lat, alt, f107, f107a, ap = input_auto_f107_ap 383 | 384 | # fully specified run 385 | expected = pymsis.calculate(*input_auto_f107_ap) 386 | 387 | # auto 388 | assert_allclose(pymsis.calculate(date, lon, lat, alt), expected) 389 | 390 | # f107 391 | assert_allclose(pymsis.calculate(date, lon, lat, alt, f107s=f107), expected) 392 | 393 | # f107a 394 | assert_allclose(pymsis.calculate(date, lon, lat, alt, f107as=f107a), expected) 395 | 396 | # ap 397 | assert_allclose(pymsis.calculate(date, lon, lat, alt, aps=ap), expected) 398 | 399 | 400 | @pytest.mark.parametrize( 401 | "inputs", 402 | [ 403 | (np.datetime64("2000-01-01T00:00"), np.nan, 0, 100), 404 | (np.datetime64("2000-01-01T00:00"), 0, 0, 100), 405 | (np.datetime64("2000-01-01T00:00"), 0, 0, 100, np.nan), 406 | ], 407 | ) 408 | def test_bad_run_inputs(inputs): 409 | # Inputs that have nan's in them at various places should all raise for the user 410 | with pytest.raises(ValueError, match="Input data has non-finite values"): 411 | pymsis.calculate(*inputs) 412 | 413 | 414 | @pytest.mark.parametrize( 415 | ("version", "msis_lib"), 416 | [("0", msis00f), ("2.0", msis20f), ("2.1", msis21f)], 417 | ) 418 | def test_keyword_argument_call(input_data, version, msis_lib): 419 | # Make sure that the wrapper definition is correct for whether we 420 | # call it with or without keyword arguments 421 | date, lon, lat, alt, f107, f107a, ap = input_data 422 | dyear = (date.astype("datetime64[D]") - date.astype("datetime64[Y]")).astype( 423 | float 424 | ) + 1 # DOY 1-366 425 | dseconds = (date.astype("datetime64[s]") - date.astype("datetime64[D]")).astype( 426 | float 427 | ) 428 | run_output = pymsis.calculate( 429 | dates=date, 430 | alts=alt, 431 | lats=lat, 432 | lons=lon, 433 | f107s=f107, 434 | f107as=f107a, 435 | aps=ap, 436 | version=version, 437 | ) 438 | direct_output = msis_lib.pymsiscalc( 439 | day=np.array([dyear]), 440 | utsec=np.array([dseconds]), 441 | z=np.array([alt]), 442 | lat=np.array([lat]), 443 | lon=np.array([lon]), 444 | sflux=np.array([f107]), 445 | sfluxavg=np.array([f107a]), 446 | ap=np.array(ap), 447 | ) 448 | if version < "2.1": 449 | # NO missing from versions before 2.1 450 | direct_output[:, -2] = np.nan 451 | assert_array_equal(run_output, direct_output) 452 | 453 | 454 | def test_changing_options(input_data, expected_output, expected_output_with_options): 455 | # Calling the function again while just changing options should 456 | # also update the output data. There is global caching in MSIS, 457 | # so we need to make sure that we are actually changing the model 458 | # when the options change. 459 | assert_allclose( 460 | np.squeeze(pymsis.calculate(*input_data, options=[1] * 25)), 461 | expected_output, 462 | rtol=1e-5, 463 | ) 464 | assert_allclose( 465 | np.squeeze(pymsis.calculate(*input_data, options=[0] * 25)), 466 | expected_output_with_options, 467 | rtol=1e-5, 468 | ) 469 | 470 | 471 | @pytest.mark.parametrize( 472 | ("version", "msis_lib"), [("00", msis00f), ("2.0", msis20f), ("2.1", msis21f)] 473 | ) 474 | def test_options_calls(input_data, version, msis_lib): 475 | # Check that we don't call the initialization function unless 476 | # our options have changed between calls. 477 | # Reset the cache 478 | msis_lib._last_used_options = None 479 | with patch(msis_lib.__name__ + ".pyinitswitch") as mock_init: 480 | pymsis.calculate(*input_data, options=[0] * 25, version=version) 481 | mock_init.assert_called_once() 482 | pymsis.calculate(*input_data, options=[0] * 25, version=version) 483 | # Called again shouldn't call the initialization function 484 | mock_init.assert_called_once() 485 | 486 | 487 | def test_multithreaded( 488 | input_data, expected_output, expected_output00, expected_output_with_options 489 | ): 490 | """ 491 | Multithreaded run submission 492 | Make sure that we can run the function in a multithreaded environment 493 | and that the output is still correct and our global Fortran code hasn't 494 | been shared between threads incorrectly. 495 | NOTE: This will cause segfaults without the locks in place within the 496 | Python code. 497 | """ 498 | # We need to make the list of items long enough to cause some computation 499 | # within the Fortran code during each run() call. 500 | date, lon, lat, alt, f107, f107a, ap = input_data 501 | n = 2000 502 | input_data = ( 503 | [date] * n, 504 | [lon] * n, 505 | [lat] * n, 506 | [alt] * n, 507 | [f107] * n, 508 | [f107a] * n, 509 | ap * n, 510 | ) 511 | # Create a tuple of items (version, options, expected_output) 512 | # 3 items cycled over 100 times 513 | list_of_inputs = [ 514 | (0, None, np.tile(expected_output00, (n, 1))), 515 | (2.1, None, np.tile(expected_output, (n, 1))), 516 | (2.1, [0] * 25, np.tile(expected_output_with_options, (n, 1))), 517 | ] * 100 518 | 519 | def run_function(input_items): 520 | version, options, expected = input_items 521 | return np.squeeze( 522 | pymsis.calculate(*input_data, version=version, options=options) 523 | ), expected 524 | 525 | with concurrent.futures.ThreadPoolExecutor() as executor: 526 | # Start the load operations and mark each future with its URL 527 | results = [executor.submit(run_function, x) for x in list_of_inputs] 528 | 529 | for future in results: 530 | result, expected_result = future.result() 531 | assert_allclose(result, expected_result, rtol=1e-5) 532 | 533 | 534 | def test_output_enum(input_data): 535 | # Make sure we can access the output enums 536 | assert pymsis.Variable.MASS_DENSITY == 0 537 | assert pymsis.Variable._member_names_ == [ 538 | "MASS_DENSITY", 539 | "N2", 540 | "O2", 541 | "O", 542 | "HE", 543 | "H", 544 | "AR", 545 | "N", 546 | "ANOMALOUS_O", 547 | "NO", 548 | "TEMPERATURE", 549 | ] 550 | data = pymsis.calculate(*input_data) 551 | assert data[..., pymsis.Variable.MASS_DENSITY] == data[..., 0] 552 | 553 | 554 | def test_deprecated_legacy_imports(): 555 | # Make sure that msis.run() is still available and 556 | # we are getting our expected variables 557 | assert pymsis.calculate == msis.run == msis.calculate 558 | -------------------------------------------------------------------------------- /tests/test_regression.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import numpy as np 4 | from numpy.testing import assert_allclose 5 | 6 | import pymsis 7 | 8 | 9 | def run_input_line(line, version): 10 | items = line.split() 11 | expected = np.array(items[9:], dtype=np.float32) 12 | 13 | # Needs to be "-3:" due to strip() taking off leading spaces 14 | doy = int(items[0][-3:]) - 1 # Python wants DOY to start with 0 15 | sec, alt, lat, lon, _, f107a, f107, ap = (float(x) for x in items[1:9]) 16 | ap = [[ap] * 7] 17 | year = int(items[0][:-3]) 18 | # Two digit year? 19 | start_two_digit_year = 60 20 | if year > start_two_digit_year: 21 | year += 1900 22 | else: 23 | year += 2000 24 | date = ( 25 | np.datetime64(str(year)) 26 | + np.timedelta64(doy, "D") 27 | + np.timedelta64(int(sec), "s") 28 | ) 29 | 30 | test_inp = (date, lon, lat, alt, f107, f107a, ap) 31 | test_output = np.squeeze(pymsis.calculate(*test_inp, version=version)) 32 | 33 | # Rearrange the run's output to match the expected 34 | # ordering of elements 35 | if version == "2.0": 36 | x = np.array( 37 | [ 38 | test_output[4], 39 | test_output[3], 40 | test_output[1], 41 | test_output[2], 42 | test_output[6], 43 | test_output[0], 44 | test_output[5], 45 | test_output[7], 46 | test_output[8], 47 | test_output[10], 48 | ] 49 | ) 50 | elif version == "2.1": 51 | x = np.array( 52 | [ 53 | test_output[4], 54 | test_output[3], 55 | test_output[1], 56 | test_output[2], 57 | test_output[6], 58 | test_output[0], 59 | test_output[5], 60 | test_output[7], 61 | test_output[8], 62 | test_output[9], 63 | test_output[10], 64 | ] 65 | ) 66 | else: 67 | raise ValueError("Version number incorrect") 68 | 69 | # Different units in the test file (cgs) 70 | x[np.isclose(x, 9.9e-38, atol=1e-38)] = np.nan 71 | x[:-1] *= 1e-6 72 | x[5] *= 1e3 73 | x[np.isnan(x)] = 9.999e-38 74 | # The output print statements are messy. 75 | # They technically give 4 decimal places, but only truly 3 decimal places 76 | # in scientific notation. Example: 0.8888e+23 -> 8.888e+24 77 | # We will require relative comparisons of 2e-3 to account for the 78 | # last digit errors. 79 | assert_allclose(x, expected, rtol=2e-3) 80 | 81 | 82 | def test_included_msis20_f90_file(): 83 | # Regressing to the included file 84 | test_dir = Path(__file__).parent 85 | with open(test_dir / "msis2.0_test_ref_dp.txt") as f: 86 | f.readline() # Header 87 | for line in f: 88 | run_input_line(line, version="2.0") 89 | 90 | 91 | def test_included_msis_21_f90_file(): 92 | # Regressing to the included file 93 | test_dir = Path(__file__).parent 94 | with open(test_dir / "msis2.1_test_ref_dp.txt") as f: 95 | f.readline() # Header 96 | for line in f: 97 | run_input_line(line, version="2.1") 98 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pytest 3 | from numpy.testing import assert_allclose, assert_array_equal 4 | 5 | from pymsis import utils 6 | 7 | 8 | def test_downloading(monkeypatch, tmp_path): 9 | tmp_file = tmp_path / "testfile.txt" 10 | monkeypatch.setattr(utils, "_F107_AP_PATH", tmp_file) 11 | # just be nice and make sure we are getting the monkeypatched local file 12 | assert utils._F107_AP_URL.startswith("file://") 13 | with pytest.warns(UserWarning, match="Downloading ap and F10.7"): 14 | utils.download_f107_ap() 15 | assert tmp_file.exists() 16 | with ( 17 | open(tmp_file, "rb") as f_downloaded, 18 | open(utils._F107_AP_PATH, "rb") as f_expected, 19 | ): 20 | assert f_downloaded.read() == f_expected.read() 21 | 22 | 23 | def test_loading_data(monkeypatch, tmp_path): 24 | # Make sure we are starting off fresh with nothing loaded yet 25 | utils._DATA = None 26 | 27 | # Make sure a download warning is emitted if the file doesn't exist 28 | tmp_file = tmp_path / "testfile.txt" 29 | with monkeypatch.context() as m: 30 | m.setattr(utils, "_F107_AP_PATH", tmp_file) 31 | assert not tmp_file.exists() 32 | with pytest.warns(UserWarning, match="Downloading ap and F10.7"): 33 | utils._load_f107_ap_data() 34 | assert utils._DATA is not None 35 | 36 | # If the file is already present locally we don't want a download warning 37 | utils._DATA = None 38 | utils._load_f107_ap_data() 39 | assert utils._DATA is not None 40 | 41 | # Now make some assertions on the loaded data 42 | data = utils._DATA 43 | assert data["dates"][0] == np.datetime64("2000-01-01T00:00") 44 | assert data["dates"][-1] == np.datetime64("2000-12-31T21:00") 45 | expected_data_length = ( 46 | np.datetime64("2001-01-01T00:00") - np.datetime64("2000-01-01T00:00") 47 | ).astype("timedelta64[h]").astype(int) // 3 48 | assert len(data["dates"]) == expected_data_length 49 | assert len(data["ap"]) == expected_data_length 50 | # F10.7 is daily and therefore should be 1/8 the size 51 | expected_data_length //= 8 52 | assert len(data["f107"]) == expected_data_length 53 | assert len(data["f107a"]) == expected_data_length 54 | 55 | 56 | @pytest.mark.parametrize( 57 | ("dates", "expected_f107", "expected_f107a", "expected_ap"), 58 | [ 59 | # First timestep of the file 60 | # No F10.7 the day before 61 | # Ap should only have the daily value and the previous days 62 | ( 63 | np.datetime64("2000-01-01T00:00"), 64 | [np.nan], 65 | [166.2], 66 | [30, 56, np.nan, np.nan, np.nan, np.nan, np.nan], 67 | ), 68 | # Middle of the data file, should be fully filled 69 | ( 70 | np.datetime64("2000-07-01T12:00"), 71 | [159.6], 72 | [186.3], 73 | [7, 4, 5, 9, 4, 5.25, 5.75], 74 | ), 75 | # Requesting two dates should return length two arrays 76 | ( 77 | [np.datetime64("2000-07-01T12:00"), np.datetime64("2000-07-01T12:00")], 78 | [159.6, 159.6], 79 | [186.3, 186.3], 80 | [[7, 4, 5, 9, 4, 5.25, 5.75], [7, 4, 5, 9, 4, 5.25, 5.75]], 81 | ), 82 | # Bad F10.7 value artificially added to 2000-12-29 test file 83 | # should be replaced with F10.7a value on that same day 84 | ( 85 | np.datetime64("2000-12-30T12:00"), 86 | [173.7], 87 | [173.5], 88 | [3, 4, 4, 3, 3, 6.375, 5.375], 89 | ), 90 | ], 91 | ) 92 | @pytest.mark.filterwarnings("ignore:There is data that was either interpolated") 93 | def test_get_f107_ap(dates, expected_f107, expected_f107a, expected_ap): 94 | f107, f107a, ap = utils.get_f107_ap(dates) 95 | assert_array_equal(f107, expected_f107) 96 | assert_allclose(f107a, expected_f107a) 97 | assert_array_equal(ap, expected_ap) 98 | 99 | 100 | @pytest.mark.parametrize( 101 | "dates", 102 | [ 103 | # edge cases 104 | (np.datetime64("1999-12-31T23:00")), 105 | (np.datetime64("2001-01-01T00:00")), 106 | # Array of dates should raise if one of them is outside the bounds 107 | ([np.datetime64("2000-12-31T00:00"), np.datetime64("2001-01-01T00:00")]), 108 | # Extreme cases 109 | (np.datetime64("1900-01-01T00:00")), 110 | (np.datetime64("2100-01-01T00:00")), 111 | ], 112 | ) 113 | def test_get_f107_ap_out_of_range(dates): 114 | with pytest.raises( 115 | ValueError, match="The geomagnetic data is not available for these dates" 116 | ): 117 | utils.get_f107_ap(dates) 118 | 119 | 120 | @pytest.mark.parametrize( 121 | "dates", 122 | [ 123 | (np.datetime64("2000-12-30T00:00")), # Bad data inserted 124 | (np.datetime64("2000-12-31T00:00")), # Interpolated data 125 | ([np.datetime64("2000-12-01T00:00"), np.datetime64("2000-12-31T00:00")]), 126 | ], 127 | ) 128 | def test_get_f107_ap_interpolated_warns(dates): 129 | with pytest.warns( 130 | UserWarning, match="There is data that was either interpolated or" 131 | ): 132 | utils.get_f107_ap(dates) 133 | -------------------------------------------------------------------------------- /tools/download_source.py: -------------------------------------------------------------------------------- 1 | """ 2 | Tools to download the MSIS Fortran source code. 3 | 4 | There are some additional routines to clean up the source 5 | code which helps with compiling with gfortran. 6 | """ 7 | 8 | import shutil 9 | import tarfile 10 | import urllib.request 11 | import warnings 12 | from pathlib import Path 13 | 14 | 15 | SOURCE_DIR = "https://map.nrl.navy.mil/map/pub/nrl/NRLMSIS/" 16 | MSIS20_FILE = SOURCE_DIR + "NRLMSIS2.0/NRLMSIS2.0.tar.gz" 17 | MSIS21_FILE = SOURCE_DIR + "NRLMSIS2.1/nrlmsis2.1.tar.gz" 18 | MSIS00_FILE = SOURCE_DIR + "NRLMSISE-00/NRLMSISE-00.FOR" 19 | 20 | 21 | def get_source(): 22 | """Download and install the Fortran source code.""" 23 | # Start with MSIS20 24 | if not Path("src/msis2.0/msis_init.F90").exists(): 25 | # No source code yet, so go download and extract it 26 | try: 27 | warnings.warn(f"Downloading the MSIS2.0 source code from {MSIS20_FILE}") 28 | with urllib.request.urlopen(MSIS20_FILE) as stream: 29 | tf = tarfile.open(fileobj=stream, mode="r|gz") 30 | tf.extractall(path=Path("src/msis2.0")) 31 | except Exception as e: 32 | print( 33 | "Downloading the source code from the original repository " 34 | "failed. You can manually download and extract the source " 35 | "code following instructions in the README." 36 | ) 37 | raise e 38 | 39 | # Rename the parameter file to what the Fortran is expecting 40 | param_file = Path("pymsis/msis2.0.parm") 41 | if not param_file.exists(): 42 | # Notice that the original is "20", not "2.0" 43 | shutil.copy(Path("src/msis2.0/msis20.parm"), param_file) 44 | 45 | # Now go through and clean the source files 46 | clean_utf8(Path("src/msis2.0").glob("*.F90")) 47 | 48 | # MSIS21 49 | if not Path("src/msis2.1/msis_init.F90").exists(): 50 | # No source code yet, so go download and extract it 51 | try: 52 | warnings.warn(f"Downloading the MSIS2.1 source code from {MSIS21_FILE}") 53 | with urllib.request.urlopen(MSIS21_FILE) as stream: 54 | tf = tarfile.open(fileobj=stream, mode="r|gz") 55 | tf.extractall(path=Path("src/msis2.1")) 56 | except Exception as e: 57 | print( 58 | "Downloading the source code from the original repository " 59 | "failed. You can manually download and extract the source " 60 | "code following instructions in the README." 61 | ) 62 | raise e 63 | 64 | # Rename the parameter file to what the Fortran is expecting 65 | param_file = Path("pymsis/msis21.parm") 66 | if not param_file.exists(): 67 | # Notice that the original is "21", so keep it this time 68 | shutil.copy("src/msis2.1/msis21.parm", param_file) 69 | 70 | # Now go through and clean the source files 71 | clean_utf8(Path("src/msis2.1").glob("*.F90")) 72 | 73 | # Now go to MSIS-00 74 | local_msis00_path = Path("src/msis00/NRLMSISE-00.FOR") 75 | if not local_msis00_path.exists(): 76 | local_msis00_path.parent.mkdir(parents=True, exist_ok=True) 77 | # No source code yet, so go download and extract it 78 | try: 79 | warnings.warn(f"Downloading the MSIS-00 source code from {MSIS00_FILE}") 80 | 81 | with urllib.request.urlopen(MSIS00_FILE) as response: 82 | with open(local_msis00_path, "wb") as f: 83 | f.write(response.read()) 84 | except Exception as e: 85 | print( 86 | "Downloading the source code from the original repository " 87 | "failed. You can manually download and extract the source " 88 | "code following instructions in the README." 89 | ) 90 | raise e 91 | 92 | # Fix up the file outside of the download incase it is an offline install 93 | fix_msis00(local_msis00_path) 94 | 95 | 96 | # Clean up the source files 97 | def clean_utf8(fnames): 98 | """ 99 | Remove bad characters. 100 | 101 | fnames: list 102 | filenames to be cleaned 103 | """ 104 | for fname in fnames: 105 | with open(fname, "rb+") as f: 106 | # Ignore bad decode errors 107 | data = f.read().decode("utf-8", "ignore") 108 | # write the good encoded bytes out to the same file 109 | f.seek(0) 110 | f.write(data.encode("utf-8")) 111 | f.truncate() 112 | 113 | 114 | def fix_msis00(fname): 115 | """ 116 | Fix bad lines in msis00. 117 | 118 | The gfortran compiler thinks there is a character/int mismatch, 119 | so fix these bad lines. 120 | 121 | fname: string 122 | filename of the file to be fixed 123 | """ 124 | # Example of the bad line 125 | # NRLMSISE-00.FOR:1671:10: 126 | 127 | # DATA NAME/'MSIS','E-00'/ 128 | # 1 129 | # Error: Incompatible types in DATA statement at (1); 130 | # attempted conversion of CHARACTER(1) to INTEGER(4) 131 | 132 | bad1 = ( 133 | "DATA ISDATE/'01-F','EB-0','2 '/,ISTIME/'15:4','9:27'/", 134 | "DATA ISDATE/4H13-A,4HPR-0,4H0 /,ISTIME/4H17:4,4H6:08/", 135 | ) 136 | bad2 = ("DATA NAME/'MSIS','E-00'/", "DATA NAME/4HMSIS,4HE-00/") 137 | with open(fname, "r+") as f: 138 | data = f.read() 139 | data = data.replace(bad1[0], bad1[1]).replace(bad2[0], bad2[1]) 140 | f.seek(0) 141 | f.write(data) 142 | f.truncate() 143 | 144 | 145 | if __name__ == "__main__": 146 | get_source() 147 | -------------------------------------------------------------------------------- /tools/generate_f2pymod.py: -------------------------------------------------------------------------------- 1 | """Generate the f2py wrappers.""" 2 | 3 | import argparse 4 | import os 5 | import subprocess 6 | import sys 7 | 8 | 9 | def main(): 10 | """Run the script.""" 11 | parser = argparse.ArgumentParser() 12 | parser.add_argument("infile", type=str, help="Path to the input file") 13 | parser.add_argument("-o", "--outdir", type=str, help="Path to the output directory") 14 | args = parser.parse_args() 15 | 16 | if not args.infile.endswith(".pyf"): 17 | raise ValueError(f"Input file has unknown extension: {args.infile}") 18 | 19 | outdir_abs = os.path.join(os.getcwd(), args.outdir) 20 | 21 | # Now invoke f2py to generate the C API module file 22 | if args.infile.endswith((".pyf.src", ".pyf")): 23 | p = subprocess.Popen( 24 | [ 25 | sys.executable, 26 | "-m", 27 | "numpy.f2py", 28 | args.infile, 29 | "--build-dir", 30 | outdir_abs, 31 | "--freethreading-compatible", 32 | ], 33 | stdout=subprocess.PIPE, 34 | stderr=subprocess.PIPE, 35 | cwd=os.getcwd(), 36 | ) 37 | out, err = p.communicate() 38 | if not (p.returncode == 0): 39 | raise RuntimeError( 40 | f"Writing {args.outfile} with f2py failed!\n" f"{out}\n" r"{err}" 41 | ) 42 | 43 | 44 | if __name__ == "__main__": 45 | main() 46 | --------------------------------------------------------------------------------