├── .github
└── workflows
│ └── python-publish.yml
├── .gitignore
├── .readthedocs.yaml
├── CITATION.cff
├── LICENSE
├── MANIFEST.in
├── README.md
├── docs
├── Makefile
├── make.bat
├── requirements.txt
└── source
│ ├── _static
│ ├── favicon.ico
│ ├── shxarraylogo_dark.png
│ └── shxarraylogo_light.png
│ ├── _templates
│ ├── module.rst_t
│ ├── package.rst_t
│ └── toc.rst_t
│ ├── api.rst
│ ├── changelog.rst
│ ├── conf.py
│ ├── dev.rst
│ ├── index.rst
│ ├── installation.rst
│ ├── introduction.rst
│ ├── logo_etc
│ ├── favicon.ico
│ ├── logo_base.png
│ ├── logo_base.xcf
│ ├── make_logobase.ipynb
│ ├── makefavicon
│ ├── shxarray_logo_wtext.svg
│ ├── shxarray_logo_wtext_dark.png
│ ├── shxarray_logo_wtext_light.png
│ ├── shxarraylogo_dark.png
│ └── shxarraylogo_light.png
│ ├── notebooks
│ ├── .gitignore
│ ├── Geometry2sphericalHarmonics.ipynb
│ ├── OceanMask.ipynb
│ ├── SealevelEquation.ipynb
│ ├── TerrestrialWaterStorage.ipynb
│ └── visualize_filter.ipynb
│ └── tutorial.rst
├── headerlicense.txt
├── pyproject.toml
├── requirements.txt
├── sample-data
├── GRACEDataSample_2020.sql
├── GRACEDataSample_2020_files.tgz
└── dump-grace.py
├── setup.py
├── src
├── builtin_backend
│ ├── .gitignore
│ ├── Gaunt.hpp
│ ├── Helpers.hpp
│ ├── Legendre.hpp
│ ├── Legendre_nm.hpp
│ ├── Wigner3j.hpp
│ ├── Ynm.hpp
│ ├── analysis.pyx
│ ├── gaunt.pxd
│ ├── gaunt.pyx
│ ├── legendre.pxd
│ ├── legendre.pyx
│ ├── shindex.pxd
│ ├── shlib.cpp
│ ├── shlib.pyx
│ ├── sinex.pyx
│ ├── synthesis.pyx
│ ├── wigner3j.pxd
│ ├── wigner3j.pyx
│ └── ynm.pyx
├── shtns_backend
│ └── shtns.py
└── shxarray
│ ├── __init__.py
│ ├── _version.py
│ ├── core
│ ├── __init__.py
│ ├── admin.py
│ ├── cf.py
│ ├── logging.py
│ ├── sh_indexing.py
│ ├── shcomputebase.py
│ ├── shxarbase.py
│ ├── time.py
│ └── xr_accessor.py
│ ├── earth
│ ├── __init__.py
│ ├── constants.py
│ ├── ellipsoid.py
│ ├── rotation.py
│ ├── sealevel
│ │ ├── __init__.py
│ │ ├── sealevel.py
│ │ └── spectralsealevel.py
│ └── snrei.py
│ ├── exp
│ ├── __init__.py
│ ├── multiply.py
│ └── p2s.py
│ ├── geom
│ ├── __init__.py
│ ├── points.py
│ └── polygons.py
│ ├── geoslurp
│ ├── GRACEDsets.py
│ ├── TUGRAZDsets.py
│ ├── __init__.py
│ ├── deg1n2.py
│ ├── gracefilters.py
│ ├── graceviews.py
│ ├── gravity.py
│ ├── icgem.py
│ ├── icgemdset.py
│ └── loadlove.py
│ ├── io
│ ├── __init__.py
│ ├── binv_legacy.py
│ ├── gsmv6.py
│ ├── gzipwrap.py
│ ├── icgem.py
│ ├── shascii.py
│ ├── shiobackend.py
│ └── sinex.py
│ ├── kernels
│ ├── __init__.py
│ ├── anisokernel.py
│ ├── axial.py
│ ├── ddk.py
│ ├── factory.py
│ ├── gauss.py
│ ├── gravfunctionals.py
│ └── isokernelbase.py
│ └── signal
│ ├── basinav.py
│ └── leakage_vishwa.py
└── tests
├── fixtures.py
├── test_analysis.py
├── test_basic_ops.py
├── test_basinav.py
├── test_filters.py
├── test_prod2sum.py
├── test_shformats.py
├── test_shtns_backend.py
├── test_synthesis.py
├── test_wigner3j_gaunt.py
└── testdata
├── GSM-2_2008122-2008153_0030_EIGEN_G---_0004in.gz
├── GSM-2_2008122-2008153_0030_EIGEN_G---_0004lmax60out.gz
├── GSM-2_2008122-2008153_0030_EIGEN_G---_0004out.gz
├── P2Sum_ocean10.nc
├── gauss300testoutsub.sh.gz
├── icgem_test_nosig_ITSG.gfc
├── icgem_test_sig_ITSG.gfc.gz
├── shanalysis-test-paracap-n200.nc
├── sympy_gauntvalidation.pkl.gz
├── sympy_realgauntvalidation.pkl.gz
└── sympy_wigner3jvalidation.pkl.gz
/.github/workflows/python-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will upload a Python Package using Twine when a release is created
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
3 |
4 | # This workflow uses actions that are not certified by GitHub.
5 | # They are provided by a third-party and are governed by
6 | # separate terms of service, privacy policy, and support
7 | # documentation.
8 |
9 | name: Build, Publish and release shxarray distribution 📦 to PyPI
10 |
11 | on:
12 | push:
13 | tags:
14 | - 'v[0-9]+.[0-9]+.[0-9]+'
15 |
16 | permissions:
17 | contents: read
18 |
19 | jobs:
20 | build:
21 | name: Build distribution 📦
22 | runs-on: ubuntu-latest
23 |
24 | steps:
25 | - uses: actions/checkout@v4
26 | - name: Set up Python
27 | uses: actions/setup-python@v4
28 | with:
29 | python-version: "3.x"
30 | - name: Install pypa/build
31 | run: >-
32 | python3 -m
33 | pip install
34 | build
35 | --user
36 | - name: Build a source tarball
37 | run: python3 -m build -s
38 | - name: Store the distribution packages
39 | uses: actions/upload-artifact@v4
40 | with:
41 | name: python-package-distributions
42 | path: dist/
43 | publish-to-pypi:
44 | name: >-
45 | Publish Shxarray Python 🐍 distribution 📦 to PyPI
46 | if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes
47 | needs:
48 | - build
49 | runs-on: ubuntu-latest
50 | environment:
51 | name: pypi
52 | url: https://pypi.org/p/shxarray
53 | permissions:
54 | id-token: write # IMPORTANT: mandatory for trusted publishing
55 |
56 | steps:
57 | - name: Download all the dists
58 | uses: actions/download-artifact@v4
59 | with:
60 | name: python-package-distributions
61 | path: dist/
62 | - name: Publish distribution 📦 to PyPI
63 | uses: pypa/gh-action-pypi-publish@release/v1
64 | github-release:
65 | name: >-
66 | Upload the Python 🐍 distribution 📦 to gituhub release
67 | needs:
68 | - publish-to-pypi
69 | runs-on: ubuntu-latest
70 |
71 | permissions:
72 | contents: write # IMPORTANT: mandatory for making GitHub Releases
73 | id-token: write # IMPORTANT: mandatory for sigstore
74 |
75 | steps:
76 | - name: Download all the dists
77 | uses: actions/download-artifact@v4
78 | with:
79 | name: python-package-distributions
80 | path: dist/
81 | #- name: Sign the dists with Sigstore
82 | #uses: sigstore/gh-action-sigstore-python@v2.1.1
83 | #with:
84 | #inputs: >-
85 | #./dist/*.tar.gz
86 | #./dist/*.whl
87 | - name: Create GitHub Release
88 | env:
89 | GITHUB_TOKEN: ${{ github.token }}
90 | run: >-
91 | gh release create
92 | '${{ github.ref_name }}'
93 | --repo '${{ github.repository }}'
94 | --notes ""
95 | - name: Upload artifact signatures to GitHub Release
96 | env:
97 | GITHUB_TOKEN: ${{ github.token }}
98 | # Upload to GitHub Release using the `gh` CLI.
99 | # `dist/` contains the built packages, and the
100 | # sigstore-produced signatures and certificates.
101 | run: >-
102 | gh release upload
103 | '${{ github.ref_name }}' dist/**
104 | --repo '${{ github.repository }}'
105 | #publish-to-testpypi:
106 | #name: Publish Python 🐍 distribution 📦 to TestPyPI
107 | #needs:
108 | #- build
109 | #runs-on: ubuntu-latest
110 |
111 | #environment:
112 | #name: testpypi
113 | #url: https://test.pypi.org/p/shxarray
114 |
115 | #permissions:
116 | #id-token: write # IMPORTANT: mandatory for trusted publishing
117 |
118 | #steps:
119 | #- name: Download all the dists
120 | #uses: actions/download-artifact@v3
121 | #with:
122 | #name: python-package-distributions
123 | #path: dist/
124 | #- name: Publish distribution 📦 to TestPyPI
125 | #uses: pypa/gh-action-pypi-publish@release/v1
126 | #with:
127 | #repository-url: https://test.pypi.org/legacy/
128 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | .ipynb_checkpoints/
3 | *egg-info/
4 | build/
5 | #Exclude in source pip environment for testing the code
6 | pyshxarray/
7 | #don't include the compiled shared library
8 | shlib.cpython-*so
9 |
10 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | version: "2"
2 |
3 | build:
4 | os: "ubuntu-22.04"
5 | tools:
6 | python: "3.10"
7 |
8 | python:
9 | install:
10 | - method: pip
11 | path: .
12 | - requirements: docs/requirements.txt
13 |
14 | sphinx:
15 | configuration: docs/source/conf.py
16 |
--------------------------------------------------------------------------------
/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: "Rietbroek"
5 | given-names: "Roelof"
6 | orcid: "https://orcid.org/0000-0001-5276-5943"
7 | - family-names: "Karimi"
8 | given-names: "Sedigheh"
9 | title: "SHxarray: an extension to xarray providing functionality to work with spherical harmonic datasets"
10 | version: 1.3.2
11 | doi: 10.5281/zenodo.15236539
12 | date-released: 2025-04-17
13 | url: "https://github.com/ITC-Water-Resources/shxarray"
14 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | graft src/builtin_backend/
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Xarray Extension for working with spherical harmonic data
2 | [](https://doi.org/10.5281/zenodo.15236539)
3 | [](https://github.com/ITC-Water-Resources/shxarray/actions/workflows/python-publish.yml)
4 | [](https://badge.fury.io/py/shxarray)
5 | [](https://shxarray.wobbly.earth/latest/?badge=latest)
6 |
7 |
8 | This extension adds functionality to work with Spherical Harmonics to [Xarray](https://github.com/pydata/xarray).
9 |
10 |
11 | ## Features and functionality
12 | * Gravity functionals: (convert from Stokes coefficients to various gravity functionals, such as equivalent water heights, geoid changes, etc.)
13 | * Filter (e.g. Gaussian or anisotropic filter applied in the spectral domain)
14 | * The use of Xarray-like operations allow for applying functionality to multi-dimensional datasets
15 | * A spectral sea level equation solver
16 |
17 | ## Getting started
18 | The tutorials in the [documentation](https://shxarray.wobbly.earth/stable/tutorial.html) provide Jupyter Notebooks with examples of how to make use of the module. The notebooks can also be found on the [github repository](https://github.com/ITC-Water-Resources/shxarray/tree/main/docs/source/notebooks).
19 |
20 | The functionality of shxarray becomes available when importing the module together with Xarray:
21 |
22 | ```
23 | import shxarray
24 | import xarray as xr
25 | ```
26 | after which the shxarray accessor becomes available for use, e.g.:
27 | ```
28 | nmax=20
29 | nmin=2
30 | dazeros=xr.DataArray.sh.ones(nmax=nmax,nmin=nmin)
31 | ```
32 |
33 | ## Installation
34 | You can install this package from PyPi using:
35 | ```
36 | pip install shxarray
37 | ```
38 |
39 | ## Backends
40 | Shxarray comes with a default **shlib** backend written in C++ and Cython. In addition, a very fast 'shtns' backend can be used when [SHTns](https://nschaeff.bitbucket.io/shtns/) is installed. The backends can be specified in enabled routines as the options: `engine='shlib'` or `engine='shtns'`.
41 |
42 | ## Development Installation
43 | If you want to help in the development of this package, it's best to clone the repository to allow for modifications and pull requests. The extension makes use of [Cython](https://cython.readthedocs.io/en/latest/) generated code to speed up spherical harmonic synthesis and analysis.
44 |
45 | 1. Create your own virtual environment with `venv` or Anaconda *(Optional but recommended, when a user installation is desired)*
46 | 2. Clone this repository `git clone https://github.com/ITC-Water-Resources/shxarray.git`
47 | 3. Change to the repository directory `cd shaxarray`
48 | 4. Set the environment variable `export USE_CYTHON=1` *(Optional and only in the case Cython code is being developed or needs to be regenerated)*
49 | 5. Install using pip `pip install .` or use `pip install -e .` for an editable install
50 |
51 | ### Cython build tip on an editable install
52 | From the repository root directory, regenerating the shared library running
53 |
54 | ```python ./setup.py build_ext```
55 |
56 | will be much faster than using
57 |
58 | ```pip install -e .```
59 |
60 |
61 | This will build the shared library in for example `./build/lib.linux-x86_64-cpython-3xx/shxarray/shlib.cpython-3xx-x86_64-linux-gnu.so`. To make sure changes are picked up in your editable install you should create a symbolic link in the Python part of the library e.g. :
62 |
63 | ```
64 | cd src/shxarray/
65 | ln -sf ../../build/lib.linux-x86_64-cpython-311/shxarray/shlib.cpython-311-x86_64-linux-gnu.so
66 | ```
67 |
68 | ### Numpy version issues
69 | The provided c++ files are cythonized against numpy > 2. When building against older numpy versions (<2), the cpp files are re-cythonized upon install, this requires a working cython installation.
70 |
71 |
72 | ## Contributing
73 | This repository is under development and contributions and feedback is welcome.
74 |
75 | ### Contributors
76 | * Main developer: Roelof Rietbroek (r.rietbroek@utwente.nl)
77 | * Kiana Karimi
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/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 | #target to regenerate api doc files (in a new directory so it doesn't override older ones)
18 | apidoc:
19 | sphinx-apidoc -o source/references ../src/shxarray
20 |
21 | # Catch-all target: route all unknown targets to Sphinx using the new
22 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
23 | %: Makefile
24 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
25 |
--------------------------------------------------------------------------------
/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 | %SPHINXBUILD% >NUL 2>NUL
14 | if errorlevel 9009 (
15 | echo.
16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
17 | echo.installed, then set the SPHINXBUILD environment variable to point
18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
19 | echo.may add the Sphinx directory to PATH.
20 | echo.
21 | echo.If you don't have Sphinx installed, grab it from
22 | echo.https://www.sphinx-doc.org/
23 | exit /b 1
24 | )
25 |
26 | if "%1" == "" goto help
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/requirements.txt:
--------------------------------------------------------------------------------
1 | #sphinx-rtd-theme >= 1.3.0
2 | nbsphinx >= 0.8.11
3 | sphinx-tabs==3.4.4
4 | sphinxcontrib-apidoc >= 0.5.0
5 | sphinxcontrib-applehelp >= 1.0.7
6 | sphinxcontrib-devhelp >= 1.0.5
7 | sphinxcontrib-htmlhelp >= 2.0.4
8 | sphinxcontrib-jquery >= 4.1
9 | sphinxcontrib-jsmath >= 1.0.1
10 | sphinxcontrib-qthelp >= 1.0.6
11 | sphinxcontrib-serializinghtml >= 1.1.9
12 | sphinx-github-style >= 0.7.0
13 | pydata_sphinx_theme >= 0.4.0
14 |
15 |
--------------------------------------------------------------------------------
/docs/source/_static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/_static/favicon.ico
--------------------------------------------------------------------------------
/docs/source/_static/shxarraylogo_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/_static/shxarraylogo_dark.png
--------------------------------------------------------------------------------
/docs/source/_static/shxarraylogo_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/_static/shxarraylogo_light.png
--------------------------------------------------------------------------------
/docs/source/_templates/module.rst_t:
--------------------------------------------------------------------------------
1 | {%- if show_headings %}
2 | {{- basename | e | heading }}
3 |
4 | {% endif -%}
5 | .. automodule:: {{ qualname }}
6 | {%- for option in automodule_options %}
7 | :{{ option }}:
8 | {%- endfor %}
9 |
10 |
--------------------------------------------------------------------------------
/docs/source/_templates/package.rst_t:
--------------------------------------------------------------------------------
1 | {%- macro automodule(modname, options) -%}
2 | .. automodule:: {{ modname }}
3 | {%- for option in options %}
4 | :{{ option }}:
5 | {%- endfor %}
6 | {%- endmacro %}
7 |
8 | {%- macro toctree(docnames) -%}
9 | .. toctree::
10 | :maxdepth: {{ maxdepth }}
11 | {% for docname in docnames %}
12 | {{ docname }}
13 | {%- endfor %}
14 | {%- endmacro %}
15 |
16 | {%- if is_namespace %}
17 | {{- [pkgname, "namespace"] | join(" ") | e | heading }}
18 | {% else %}
19 | {{- pkgname | e | heading }}
20 | {% endif %}
21 |
22 | {%- if is_namespace %}
23 | .. py:module:: {{ pkgname }}
24 | {% endif %}
25 |
26 | {%- if modulefirst and not is_namespace %}
27 | {{ automodule(pkgname, automodule_options) }}
28 | {% endif %}
29 |
30 | {{ toctree(subpackages) }}
31 |
32 | {%- if submodules %}
33 | {% if separatemodules %}
34 | {{ toctree(submodules) }}
35 | {% else %}
36 | {%- for submodule in submodules %}
37 | {% if show_headings %}
38 | {{- submodule | e | heading(2) }}
39 | {% endif %}
40 | {{ automodule(submodule, automodule_options) }}
41 | {% endfor %}
42 | {%- endif %}
43 | {%- endif %}
44 |
45 | {%- if not modulefirst and not is_namespace %}
46 | Module contents
47 | ---------------
48 |
49 | {{ automodule(pkgname, automodule_options) }}
50 | {% endif %}
51 |
--------------------------------------------------------------------------------
/docs/source/_templates/toc.rst_t:
--------------------------------------------------------------------------------
1 | {{ header | heading }}
2 |
3 | .. toctree::
4 | :maxdepth: {{ maxdepth }}
5 | {% for docname in docnames %}
6 | {{ docname }}
7 | {%- endfor %}
8 |
9 |
--------------------------------------------------------------------------------
/docs/source/api.rst:
--------------------------------------------------------------------------------
1 | API Reference
2 | =============
3 |
4 | .. toctree::
5 | :maxdepth: 2
6 |
7 | references/shxarray.core.rst
8 | references/shxarray.kernels.rst
9 | references/shxarray.earth.rst
10 | references/shxarray.geom.rst
11 | references/shxarray.io.rst
12 | references/shxarray.exp.rst
13 | references/shxarray.geoslurp.rst
14 | references/shxarray.shlib.rst
15 |
--------------------------------------------------------------------------------
/docs/source/changelog.rst:
--------------------------------------------------------------------------------
1 | Releases
2 | ========
3 | Change log for shxarray
4 |
5 | Last change: |today|
6 |
7 | Upcoming Version
8 | ----------------
9 | - ..
10 |
11 |
12 | Version 1.3.0
13 | -------------
14 | - Add logo, favicon and change documentation theme
15 | - Add `SHtns `_ as a (very fast) computational backend for analysis and synthesis operations of spherical harmonic data.
16 | - Fix code such that it can be compiled on Windows with MSVC see: `Installation fails on Windows `_
17 | - Add Xarray reading backend to read SINEX files
18 | - Add option to decorate xarray with user defined attributes which can be saved to file
19 | - Add a Spectral Sea Level Equation solver
20 |
21 |
22 | Version <= 1.2.0
23 | ----------------
24 | Consult the `commit messages `_ for information on earlier releases
25 |
--------------------------------------------------------------------------------
/docs/source/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # For the full list of built-in configuration values, see the documentation:
4 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
5 |
6 | # -- Project information -----------------------------------------------------
7 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8 | from datetime import datetime
9 | import os
10 |
11 | project = 'shxarray'
12 | copyright = str(datetime.now().year)+', Roelof Rietbroek'
13 | author = 'Roelof Rietbroek'
14 |
15 | # -- General configuration ---------------------------------------------------
16 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
17 |
18 |
19 | extensions = ['nbsphinx', 'sphinxcontrib.apidoc', 'sphinx.ext.autodoc','sphinx.ext.napoleon']
20 |
21 | templates_path = ['_templates']
22 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store','**.ipynb_checkpoints']
23 |
24 | apidoc_template_dir='_templates'
25 | #figure out the actual installation directory
26 | import shxarray
27 | apidoc_module_dir=os.path.dirname(shxarray.__file__)
28 |
29 | apidoc_output_dir = 'references'
30 | apidoc_separate_modules = True
31 | apidoc_module_first=True
32 | apidoc_toc_file=False
33 |
34 | # -- Options for HTML output -------------------------------------------------
35 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
36 | #html_theme = 'sphinx_rtd_theme'
37 | html_theme='pydata_sphinx_theme'
38 |
39 | html_static_path = ['_static']
40 |
41 | napoleon_numpy_docstring = True
42 |
43 | nbsphinx_prolog = """
44 | Download this Jupyter notebook from `github `_
45 |
46 | ----
47 | """
48 | html_theme_options = {
49 | "use_edit_page_button": False,
50 | "icon_links": [
51 | {
52 | # Label for this link
53 | "name": "GitHub",
54 | # URL where the link will redirect
55 | "url": "https://github.com/ITC-Water-Resources/shxarray", # required
56 | # Icon class (if "type": "fontawesome"), or path to local image (if "type": "local")
57 | "icon": "fa-brands fa-github",
58 | # The type of image to be used (see below for details)
59 | "type": "fontawesome",
60 | }
61 | ],
62 | "logo": {
63 | "alt_text": "shxarray - Home",
64 | "image_light": "_static/shxarraylogo_dark.png",
65 | "image_dark": "_static/shxarraylogo_light.png",
66 | }
67 | }
68 |
69 | html_favicon= '_static/favicon.ico'
70 |
71 | # html_context = {
72 | # # "github_url": "https://github.com", # or your GitHub Enterprise site
73 | # "github_user": "ITC-Water-Resources",
74 | # "github_repo": "shxarray",
75 | # "github_version": "main",
76 | # "doc_path": "docs/source",
77 | # }
78 |
79 |
80 | # def linkcode_resolve(domain, info):
81 | # if domain != 'py':
82 | # return None
83 | # if not info['module']:
84 | # return None
85 | # filename = info['module'].replace('.', '/')
86 | # return "https://github.com/ITC-Water-Resources/shxarray/%s.py" % filename
87 |
88 | # linkcode_url="https://github.com/ITC-Water-Resources/shxarray"
89 | # linkcode_link_text="view on"
90 |
--------------------------------------------------------------------------------
/docs/source/dev.rst:
--------------------------------------------------------------------------------
1 | Development
2 | ===========
3 |
4 |
5 | Development install
6 | -------------------
7 | Users interested in developing can install the latest version from `github `_. Cython is needed in case the binary extension is being developed, and users can consult the dedicated instructions on the github repository.
8 |
9 |
10 |
11 |
12 | Testing suite with pytest
13 | -------------------------
14 | From the repositories root directory, the entire test suite can be run with the following command:
15 |
16 | .. code-block:: console
17 |
18 | python -m pytest tests/
19 |
20 |
21 | Coding style
22 | ------------
23 | Code can be supplied with `numpy docstrings `_ so they can be parsed into this documentation.
24 |
--------------------------------------------------------------------------------
/docs/source/index.rst:
--------------------------------------------------------------------------------
1 | .. shxarray documentation master file, created by
2 | sphinx-quickstart on Fri Dec 8 22:28:53 2023.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 |
7 | Welcome to shxarray's documentation!
8 | ====================================
9 |
10 | .. toctree::
11 | :maxdepth: 2
12 | :caption: Contents:
13 |
14 | introduction.rst
15 | installation.rst
16 | tutorial.rst
17 | api.rst
18 | dev.rst
19 | changelog.rst
20 |
21 | Indices and tables
22 | ==================
23 |
24 | * :ref:`genindex`
25 | * :ref:`modindex`
26 | * :ref:`search`
27 |
--------------------------------------------------------------------------------
/docs/source/installation.rst:
--------------------------------------------------------------------------------
1 | Getting started
2 | ===============
3 |
4 | Installation
5 | ------------
6 | The latest **shxarray** is hosted on `pypi `_ and can be installed through pip:
7 |
8 | ``pip install shxarray``
9 |
10 | Part of the module is written is `Cython `_, which means that a c compiler is needed to build the package. A binary wheel is currently not offered, but this may be offered in the future.
11 |
12 | Import and usage
13 | ----------------
14 | For most operations, a simple import will expose the xarray extensions. For example:
15 |
16 | .. code-block:: python
17 |
18 | import shxarray
19 | import xarray as xr
20 |
21 | #Initialize a DataArray with zeros which has a dimension spanning degrees from nmin to nmax
22 |
23 | nmax=20
24 | nmin=2
25 | dazeros=xr.DataArray.sh.ones(nmax=nmax,nmin=nmin)
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/docs/source/introduction.rst:
--------------------------------------------------------------------------------
1 | Introduction
2 | ============
3 |
4 | The **shxarray** package is aimed to make using spherical harmonic operations more accessible to a community which is used to xarray.
5 |
6 | Putting degrees and orders in a MultiIndex
7 | ------------------------------------------
8 |
9 | Spherical harmonic coefficients are often put in 2-dimensional arrays with degree and order spanning the 2 dimensions. This has the advantage that individual coefficients can be easily referenced as e.g. `cnm=mat[n,m]`. However, since only the upper triangle of those matrices are non-zero, the sparseness of these matrices is not made use of. When working with large datasets which also span other dimensions such as time or different levels, this will cause large segments of zeros.
10 |
11 | A `pandas.MultiIndex `_ can facilitate the stacking of degrees and orders in a single dimension. On top of that, this multindex can then be used in `xarray` to work with spherical harmonics along a single coordinate. In **shxarray**, the spherical harmonic index is generally denoted as ``nm``, while when two spherical harmonic coordinates are needed alternative versions such as ``nm_`` are added.
12 |
13 | Exposing spherical harmonic functionality to xarray
14 | ---------------------------------------------------
15 |
16 | Some of the functionality needed for working with spherical harmonics need specialized access to the degree and order information. The aim of **shxarray** is to expose common functionality through `xarray accessors `_. This allows, besides familiar syntax for xarray users, also chaining of operations in a compact syntax.
17 |
18 | Delegate common operations to xarray
19 | ------------------------------------
20 |
21 | In contrast to specialized spherical harmonic operations, many operations on spherical harmonic data can be delegated to xarray itself. Wherever possible, functionality and broadcasting features of xarray are made use for a consistent syntax.
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/docs/source/logo_etc/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/logo_etc/favicon.ico
--------------------------------------------------------------------------------
/docs/source/logo_etc/logo_base.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/logo_etc/logo_base.png
--------------------------------------------------------------------------------
/docs/source/logo_etc/logo_base.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/logo_etc/logo_base.xcf
--------------------------------------------------------------------------------
/docs/source/logo_etc/makefavicon:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | fin=shxarraylogo_light.png
4 | fout=favicon.ico
5 | extent=`identify $fin | awk '{\
6 | split($3,dim,"x");\
7 | if(dim[1] > dim[2]){\
8 | printf "0x%d\n",(dim[1])\
9 | }else{\
10 | printf "%dx0\n",(dim[2])}\
11 | }'`
12 |
13 | convert $fin -background none -gravity center -extent $extent -resize 16x16 $fout
14 |
--------------------------------------------------------------------------------
/docs/source/logo_etc/shxarray_logo_wtext_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/logo_etc/shxarray_logo_wtext_dark.png
--------------------------------------------------------------------------------
/docs/source/logo_etc/shxarray_logo_wtext_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/logo_etc/shxarray_logo_wtext_light.png
--------------------------------------------------------------------------------
/docs/source/logo_etc/shxarraylogo_dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/logo_etc/shxarraylogo_dark.png
--------------------------------------------------------------------------------
/docs/source/logo_etc/shxarraylogo_light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/docs/source/logo_etc/shxarraylogo_light.png
--------------------------------------------------------------------------------
/docs/source/notebooks/.gitignore:
--------------------------------------------------------------------------------
1 | data/*
2 |
--------------------------------------------------------------------------------
/docs/source/tutorial.rst:
--------------------------------------------------------------------------------
1 | Tutorials
2 | =========
3 | The examples in this tutorials are builds using Jupyter notebooks, which can be downloaded from `the github repository `_.
4 |
5 | .. toctree::
6 |
7 | notebooks/visualize_filter
8 | notebooks/TerrestrialWaterStorage
9 | notebooks/OceanMask
10 | notebooks/Geometry2sphericalHarmonics
11 | notebooks/SealevelEquation
12 |
--------------------------------------------------------------------------------
/headerlicense.txt:
--------------------------------------------------------------------------------
1 | # This file is part of the shxarray software which is licensed
2 | # under the Apache License version 2.0 (see the LICENSE file in the main repository)
3 | # Copyright Roelof Rietbroek (r.rietbroek@utwente.nl), 2025
4 | #
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools>=61.0","setuptools-scm>=8","numpy","Cython>=3","wheel","pytest","scipy","packaging"]
3 | build-backend = "setuptools.build_meta"
4 | [project]
5 | name = "shxarray"
6 | dynamic = ["version"]
7 | #version = "0.0.1"
8 | authors = [
9 | { name="Roelof Rietbroek", email="r.rietbroek@utwente.nl" },
10 | ]
11 | description = "Spherical harmonic extension for Xarray"
12 | readme = "README.md"
13 | requires-python = ">=3.8"
14 | classifiers = [
15 | "Programming Language :: Python :: 3",
16 | "License :: OSI Approved :: Apache Software License",
17 | "Operating System :: POSIX :: Linux",
18 | "Intended Audience :: Science/Research",
19 | "Topic :: Scientific/Engineering",
20 | "Development Status :: 1 - Planning"
21 | ]
22 | dependencies = [ "pandas >= 2.0", "pyaml >= 23.9.0", "scipy", "xarray >= 2023.1.0",
23 | "numpy","numba", "sparse","importlib_metadata","requests","openpyxl","geopandas"]
24 |
25 | [project.optional-dependencies]
26 | #you need dask in combination with when using older xarray versions
27 | dask=["dask>=2022.9.2"]
28 | #Optionally speed up reading of gzip compressed files:
29 | rapidgzip=["rapidgzip"]
30 |
31 | [tool.setuptools_scm]
32 | version_file = "src/shxarray/_version.py"
33 |
34 | #For some reason specifying the xarray-backend as below is not working in toml (it's added in setup.py)
35 | [project.entry-points."xarray.backends"]
36 | icgem = "shxarray.io.shiobackend:ICGEMBackEntryPoint"
37 | gsmv6 = "shxarray.io.shiobackend:GSMv6BackEntryPoint"
38 | shascii ="shxarray.io.shiobackend:SHAsciiBackEntryPoint"
39 | sinex ="shxarray.io.shiobackend:SINEXBackEntryPoint"
40 | #ddk = "shxarray.io.shiobackend:DDKBackEntryPoint"
41 |
42 | [project.entry-points."shxarray.computebackends"]
43 | shtns = "shtns_backend.shtns:SHTnsBackend"
44 | shlib = "shxarray.shlib:SHComputeBackend"
45 |
46 | [project.urls]
47 | "Homepage" = "https://github.com/ITC-Water-Resources/shxarray"
48 | "Bug Tracker" = "https://github.com/ITC-Water-Resources/shxarray/issues"
49 |
50 | [project.entry-points."geoslurp.dsetfactories"]
51 | deg1n2corr = "shxarray.geoslurp.deg1n2:getDeg1n2corrDsets"
52 | tugrazgrace = "shxarray.geoslurp.TUGRAZDsets:TUGRAZGRACEDsets"
53 | gracel2 = "shxarray.geoslurp.GRACEDsets:GRACEDsets"
54 |
55 | [project.entry-points."geoslurp.dsets"]
56 | icgemstatic = "shxarray.geoslurp.icgemdset:ICGEMstatic"
57 | loadlove = "shxarray.geoslurp.loadlove:LLove"
58 | gracefilter = "shxarray.geoslurp.gracefilters:GRACEfilter"
59 |
60 | [project.entry-points."geoslurp.viewfactories"]
61 | graceviews = "shxarray.geoslurp.graceviews:getGRACEviews"
62 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | pandas >= 2.0
2 | pyaml >= 23.9.0
3 | scipy
4 | xarray >= 2023.1.0
5 | numba
6 | sparse
7 | importlib_metadata
8 | requests
9 | openpyxl
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/sample-data/GRACEDataSample_2020.sql:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/sample-data/GRACEDataSample_2020.sql
--------------------------------------------------------------------------------
/sample-data/GRACEDataSample_2020_files.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ITC-Water-Resources/shxarray/72aa3a0ff47fc0a075e79c28f8dd203d9951c725/sample-data/GRACEDataSample_2020_files.tgz
--------------------------------------------------------------------------------
/sample-data/dump-grace.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # R. Rietbroek
3 | # Extract a subset of GRACE data from a geoslurp database in a sqlite table and tar archive
4 | # This can be used in the testing and tutorial examples
5 |
6 | #Note to execute this script a populated database and a geoslurp client (https://github.com/strawpants/geoslurp) is required
7 | from geoslurp.db.exporter import exportQuery
8 | from geoslurp.db import geoslurpConnect
9 | import os
10 |
11 | qry2020="select * from gravity.gracecomb_l2_jpl_n60 WHERE date_part('year',time) = 2020"
12 |
13 | outputfile="GRACEDataSample_2020.sql"
14 |
15 | conn=geoslurpConnect(dbalias="marge")
16 | localdataroot=conn.localdataroot
17 | exportQuery(conn.dbeng.execute(qry2020),outputfile,layer="gracel2",packUriCols=["gsm","gac","gaa","gad"],striproot=os.path.join(localdataroot,"gravity"),localdataroot=localdataroot)
18 |
19 | #Also add a static gravity field to subtract
20 | staticqry="select * from gravity.icgem_static where uri LIKE '%%GGM05C%%'"
21 | exportQuery(conn.dbeng.execute(staticqry),outputfile,layer="static",packUriCols=["uri"],striproot=os.path.join(localdataroot,"gravity"),localdataroot=localdataroot)
22 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # This file is part of the shxarray software which is licensed
2 | # under the Apache License version 2.0 (see the LICENSE file in the main repository)
3 | # Copyright Roelof Rietbroek (r.rietbroek@utwente.nl), 2023
4 | #
5 |
6 |
7 |
8 |
9 | from setuptools import setup,Extension
10 | from setuptools_scm import get_version
11 | from Cython.Build import cythonize
12 | import Cython.Compiler.Options
13 | from packaging.version import Version
14 |
15 | import os
16 | import numpy as np
17 | import sys
18 |
19 | if sys.platform.startswith("win"):
20 | winplatform=True
21 | extra_args = ['/openmp']
22 | else:
23 | winplatform=False
24 | extra_args = ['-fopenmp']
25 |
26 | if "DEBUG_CYTHON" in os.environ:
27 | debug=True
28 | extra_args.append('-O0')
29 | #extra_args.append('-pg')
30 | else:
31 | debug=False
32 | extra_args.append('-O3')
33 |
34 | #don't necessarily use cython
35 | if "USE_CYTHON" in os.environ or winplatform:
36 | # note being on windows forces the use of cython
37 | useCython=True
38 | ext=".pyx"
39 | Cython.Compiler.Options.annotate = True
40 | else:
41 | useCython=False
42 | ext=".cpp"
43 |
44 | #Force the use of cython if numpy has a version < 2
45 | if not useCython and Version(np.__version__) < Version ("2.0.0"):
46 | useCython=True
47 | ext=".pyx"
48 |
49 |
50 | def listexts():
51 | names=["shlib"]
52 | exts=[]
53 | for nm in names:
54 | exts.append(Extension("shxarray."+nm.replace("/","."),["src/builtin_backend/"+nm+ext],include_dirs=[np.get_include(),"."], define_macros=[('NPY_NO_DEPRECATED_API', 'NPY_1_7_API_VERSION')],extra_compile_args=extra_args,extra_link_args=extra_args))
55 | return exts
56 |
57 | extensions=listexts()
58 |
59 |
60 | if useCython:
61 | #additionally cythonize pyx files before building
62 | extensions=cythonize(extensions,language_level=3,annotate=True,gdb_debug=debug,compiler_directives={'embedsignature': True})
63 |
64 | setup(
65 | version = get_version(root='.', relative_to=__file__),
66 | ext_modules=extensions
67 | )
68 |
--------------------------------------------------------------------------------
/src/builtin_backend/.gitignore:
--------------------------------------------------------------------------------
1 | *html
2 |
--------------------------------------------------------------------------------
/src/builtin_backend/Helpers.hpp:
--------------------------------------------------------------------------------
1 | /*! \file
2 | \brief
3 | \copyright Roelof Rietbroek 2023
4 | \license
5 | This file is part of shxarray.
6 | */
7 |
8 |
9 | #include
10 | #include
11 | #ifndef HELPERS_HPP_
12 | #define HELPERS_HPP_
13 |
14 |
15 | inline int csphase(int m){
16 | return 1-2*(std::abs(m)%2);
17 | }
18 |
19 | inline int kronecker(int n1,int n2){
20 | return n1==n2?1:0;
21 | }
22 |
23 |
24 |
25 |
26 | typedef std::pair nmpair;
27 | struct nm_hash
28 | {
29 | template
30 | std::size_t operator() (const std::pair &pair) const {
31 | //we take advantage of the fact that we don't expect very large degrees which saturate the 32 bit integer
32 | //so we left shift the degree and add the absolute of the order (left shifted by 1 position) and add the signbit of the order
33 | // this should be fine for hasing degree order combinations up to 2**15 (32768)
34 | return static_cast((pair.first<<15) + (std::abs(pair.second)<<1)+std::signbit(pair.second));
35 | }
36 | };
37 |
38 | typedef std::unordered_map nm_umap;
39 |
40 | class Nmindex{
41 | public:
42 | Nmindex():nmmap_(1){};
43 | Nmindex(size_t maxsize):nmmap_(maxsize){};
44 | //size_t operator()(int n,int m)const{
45 | //return nmmap_[std::make_pair(n,m)];
46 | //}
47 | //const size_t & operator[](const nmpair & nm)const{
48 | //return nmmap_[nm];
49 | //}
50 | const size_t & operator[](const nmpair & nm)const{
51 | return nmmap_.at(nm);
52 | }
53 | const size_t & operator()(const int & n,const int & m)const{
54 | return nmmap_.at(std::make_pair(n,m));
55 | }
56 |
57 | void set(nmpair nm,size_t ix){
58 | nmmap_[nm]=ix;
59 | }
60 | //size_t get(const nmpair & nm){
61 | //return nmmap_[nm];
62 | //}
63 | private:
64 | nm_umap nmmap_;
65 | };
66 |
67 |
68 |
69 | #endif
70 |
--------------------------------------------------------------------------------
/src/builtin_backend/Legendre.hpp:
--------------------------------------------------------------------------------
1 | /*! \file
2 | \brief
3 | \copyright Roelof Rietbroek 2019
4 | \license
5 | This file is part of shxarray.
6 | */
7 |
8 | #include
9 | #include "cassert"
10 | #ifndef FR_SH_LEGENDRE_HPP_
11 | #define FR_SH_LEGENDRE_HPP_
12 |
13 | #define UNNORM 0
14 | #define GEODESY4PI 1
15 |
16 |
17 | ///@brief a class which computes and caches a (un)normalized Legendre polynomial
18 | template
19 | class Legendre{
20 | public:
21 | Legendre(){}
22 | Legendre(int nmax):nmax_(nmax),pn_(nmax+1){}
23 | Legendre(int nmax,int norm):nmax_(nmax),pn_(nmax+1),norm_(norm){}
24 | const std::vector get(const ftype costheta);
25 | private:
26 | int nmax_=-1;
27 | std::vector pn_{};
28 | int norm_=UNNORM;
29 | };
30 |
31 |
32 | ///@brief a class which computes and caches a (un)normalized Legendre polynomial
33 | template
34 | const std::vector Legendre::get(const ftype costheta){
35 | assert(nmax_ >0);
36 | if (pn_[1] == costheta){
37 | ///Quick return if already computed
38 | return pn_;
39 | }
40 |
41 | ftype pnmin1=costheta;
42 | ftype pnmin2=1;
43 | ftype pn;
44 | pn_[0]=pnmin2;
45 | pn_[1]=pnmin1;
46 |
47 | for(int n=2;n<=nmax_;++n){
48 | pn=((2*n-1)*costheta*pnmin1-(n-1)*pnmin2)/static_cast(n);
49 | pnmin2=pnmin1;
50 | pnmin1=pn;
51 | pn_[n]=pn;
52 | }
53 |
54 | /* possibly normalize */
55 | if (norm_ == GEODESY4PI){
56 | for(int n=0;n<=nmax_;++n){
57 | pn_[n]=pn_[n]/(2*n+1);
58 |
59 | }
60 | }
61 | return pn_;
62 | }
63 |
64 | #endif
65 |
--------------------------------------------------------------------------------
/src/builtin_backend/Legendre_nm.hpp:
--------------------------------------------------------------------------------
1 | /*! \file
2 | \brief Computation of (associated) Legendre functions (fast version)
3 | \copyright Roelof Rietbroek 2022
4 | \license
5 | This file is part of shxarray.
6 | */
7 |
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 | #ifndef FR_SH_LEGENDRE_NM_HPP
14 | #define FR_SH_LEGENDRE_NM_HPP
15 | template
16 | class Legendre_nm {
17 | public:
18 | Legendre_nm(const int nmax);
19 | Legendre_nm() {}
20 | using nmp=std::pair;
21 | void set(const ftype costheta, ftype pnm[])const;
22 | inline size_t idx(int n, int m)const {
23 | return Legendre_nm::i_from_nm(n,m,nmax_);
24 | }
25 | inline static size_t i_from_nm(int n, int m, int nmax) {
26 | assert(m <= n);
27 | assert(n <= nmax);
28 | return m * (nmax + 1) - (m * (m + 1)) / 2 + n;
29 | }
30 | inline nmp nm(const size_t idx)const{
31 | return nmindex_[idx];
32 | }
33 | inline static nmp nm_from_i(const size_t idx,
34 | const int nmax) {
35 | int m = (3.0 + 2 * nmax) / 2 -
36 | std::sqrt(std::pow(3 + 2 * nmax, 2) / 4.0 - 2 * idx);
37 | int n = idx - (((m + 1) * (m + 2)) / 2 + m * (nmax - m)) + m + 1;
38 | assert(m <= n);
39 | assert(n <= nmax);
40 | return std::make_pair(n, m);
41 | }
42 |
43 | int nmax() const { return nmax_; }
44 | int size() const { return sz_; }
45 |
46 | private:
47 | struct alignas(64) cacheEntry {
48 | ftype pnmin2 = 0.0;
49 | ftype pnmin1 = 0.0;
50 | ftype pn = 0.0;
51 | ftype sectorial = 0.0;
52 | };
53 |
54 | int nmax_ = -1;
55 | size_t sz_ = 0;
56 | std::vector wnn_ = {};
57 | std::vector wnm_ = {};
58 | std::vector nmindex_ = {};
59 | };
60 |
61 | template
62 | Legendre_nm::Legendre_nm(int nmax)
63 | : nmax_(nmax),
64 | sz_(i_from_nm(nmax, nmax, nmax) + 1),
65 | wnn_(nmax + 1),
66 | wnm_(sz_),nmindex_(sz_){
67 | // precompute factors involving square roots
68 | wnn_[0] = 0.0;
69 | wnn_[1] = sqrt(3.0);
70 | for (int n = 2; n <= nmax_; ++n) {
71 | wnn_[n] = sqrt((2 * n + 1) / (2.0 * n));
72 | }
73 | for (int m = 0; m <= nmax_; ++m) {
74 | for (int n = m + 1; n <= nmax_; ++n) {
75 | wnm_[i_from_nm(n, m, nmax_)] =
76 | sqrt((2 * n + 1.0) / (n + m) * (2 * n - 1.0) / (n - m));
77 | }
78 | }
79 |
80 | //create a cached lookup index for degree and order
81 | for(size_t i =0;i
89 | void Legendre_nm::set(const ftype costheta, ftype pnm[])const {
90 | assert(nmax_ > 0);
91 | assert(costheta >= -1.0 and costheta <= 1.0);
92 |
93 | ftype sinTheta = std::sqrt(1 - pow(costheta, 2));
94 |
95 | ftype numericStabilityFactor = 1e-280;
96 |
97 | // Loop over orders (slowly varying)
98 | cacheEntry L1CacheEntry;
99 |
100 | // initial rescaling is 1e280
101 | L1CacheEntry.sectorial = 1.0 / numericStabilityFactor;
102 | /// Initial value of the recursion
103 | pnm[0] = 1.0;
104 |
105 | size_t idx;
106 | for (int m = 0; m < nmax_; ++m) {
107 | idx = i_from_nm(m, m, nmax_);
108 | L1CacheEntry.pnmin2 = numericStabilityFactor;
109 |
110 | // compute offdiagonal element
111 | L1CacheEntry.pnmin1 = wnm_[idx + 1] * costheta * L1CacheEntry.pnmin2;
112 | pnm[idx + 1] = L1CacheEntry.pnmin1 * L1CacheEntry.sectorial;
113 | // loop over remaining degrees
114 | for (int n = m + 2; n <= nmax_; ++n) {
115 | idx = i_from_nm(n, m, nmax_);
116 |
117 | L1CacheEntry.pn = wnm_[idx] * (costheta * L1CacheEntry.pnmin1 -
118 | L1CacheEntry.pnmin2 / wnm_[idx - 1]);
119 | // write value to output vector and shift entries in the cache
120 | pnm[idx] = L1CacheEntry.pn * L1CacheEntry.sectorial;
121 | // shift entry to prepare for the next degree
122 | L1CacheEntry.pnmin2 = L1CacheEntry.pnmin1;
123 | L1CacheEntry.pnmin1 = L1CacheEntry.pn;
124 | }
125 |
126 | // Update the m+1 sectorial (applies n+1,n+1 <- n,n recursion)
127 | L1CacheEntry.sectorial *= wnn_[m + 1] * sinTheta;
128 | // also write the next sectorial to the output vector (scaled correctly)
129 | pnm[i_from_nm(m + 1, m + 1, nmax_)] =
130 | L1CacheEntry.sectorial * numericStabilityFactor;
131 | }
132 | }
133 |
134 | #endif /// FR_SH_LEGENDRE_NM_HPP///
135 |
--------------------------------------------------------------------------------
/src/builtin_backend/Ynm.hpp:
--------------------------------------------------------------------------------
1 | /*! \file
2 | \brief Computation of (associated) Legendre functions (fast version)
3 | \copyright Roelof Rietbroek 2022
4 | \license
5 | This file is part of shxarray.
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include "Legendre_nm.hpp"
14 | #include