diff --git a/.circleci/config.yml b/.circleci/config.yml index fbf8a31..7f163a2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,7 +36,7 @@ jobs: command: | apt update apt-get update --yes && apt-get upgrade --yes - apt-get install --yes wget + apt-get install --yes wget git # download and install miniconda3 mkdir -p ${HOME}/tools cd ${HOME}/tools diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1230149 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/build-and-publish-to-pypi.yml b/.github/workflows/build-and-publish-to-pypi.yml new file mode 100644 index 0000000..6c1fb14 --- /dev/null +++ b/.github/workflows/build-and-publish-to-pypi.yml @@ -0,0 +1,101 @@ +name: Publish 📦 to PyPI + +# Build on every branch push, tag push, and pull request change: +on: + push: + branches: + - main + tags: + - v* + pull_request: + +jobs: + build_wheels: + name: Build Python 🐍 wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, macos-11] #windows-2019 + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Provide gfortran (macOS) + if: runner.os == 'macOS' + run: | + # https://github.com/actions/virtual-environments/issues/2524 + # https://github.com/cbg-ethz/dce/blob/master/.github/workflows/pkgdown.yaml + sudo ln -s /usr/local/bin/gfortran-11 /usr/local/bin/gfortran + sudo mkdir /usr/local/gfortran + sudo ln -s /usr/local/Cellar/gcc@11/*/lib/gcc/11 /usr/local/gfortran/lib + gfortran --version + + - name: Provide gfortran (Windows) + if: runner.os == 'Windows' + uses: msys2/setup-msys2@v2 + + - name: Tell distutils to use mingw (Windows) + if: runner.os == 'Windows' + run: | + echo "[build]`ncompiler=mingw32" | Out-File -Encoding ASCII ~/pydistutils.cfg + + - name: Build wheels + uses: pypa/cibuildwheel@v2.11.2 + env: + # Disable building for PyPy and 32bit. + CIBW_SKIP: pp* *-win32 *-manylinux_i686 + # Package the DLL dependencies in the wheel for windows (done by default for the other platforms). + # delvewheel cannot mangle the libraries, stripping does not work. + CIBW_BEFORE_BUILD_WINDOWS: pip install delvewheel + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "delvewheel show {wheel} && delvewheel repair -w {dest_dir} {wheel} --no-mangle-all" + + - uses: actions/upload-artifact@v3 + with: + path: ./wheelhouse/*.whl + + build_sdist: + name: Build Python 🐍 source distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Build sdist + run: pipx run build --sdist + + - uses: actions/upload-artifact@v3 + with: + path: dist/*.tar.gz + + upload_pypi: + needs: [build_wheels, build_sdist] + runs-on: ubuntu-latest + if: github.repository_owner == 'insarlab' && github.event_name == 'push' + steps: + - uses: actions/download-artifact@v3 + with: + # unpacks default artifact into dist/ + # if `name: artifact` is omitted, the action will create extra parent dir + name: artifact + path: dist + + - name: Publish developed version 📦 to Test PyPI + uses: pypa/gh-action-pypi-publish@release/v1.5.0 + with: + user: __token__ + password: ${{ secrets.TEST_PYPI_API_TOKEN }} + repository_url: https://test.pypi.org/legacy/ + skip_existing: false + verbose: true + + - name: Publish released version 📦 to PyPI + uses: pypa/gh-action-pypi-publish@v1.5.0 + if: startsWith(github.ref, 'refs/tags/v') + with: + user: __token__ + password: ${{ secrets.PYPI_API_TOKEN }} + verbose: true diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml deleted file mode 100644 index 942ffef..0000000 --- a/.github/workflows/publish-to-test-pypi.yml +++ /dev/null @@ -1,72 +0,0 @@ -# link: https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ -name: publish distributions 📦 to PyPI and TestPyPI - -on: - push: - branches: - - main - tags: - - v* - -# activate miniconda environment -# link: https://github.com/marketplace/actions/provision-with-micromamba#IMPORTANT -defaults: - run: - shell: bash -l {0} - -jobs: - build-n-publish: - if: github.repository_owner == 'insarlab' - - name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Set up Python 3.10 - uses: actions/setup-python@v3 - with: - python-version: "3.10" - - # link: https://github.com/marketplace/actions/provision-with-micromamba - - name: Install Conda environment with Micromamba - uses: mamba-org/provision-with-micromamba@main - with: - environment-file: environment.yml - - - name: Install pypa/build - run: >- - python -m - pip install - build - --user - - - name: Build a binary wheel and a source tarball - run: >- - python -m - build - --sdist - --no-isolation # not install in an isolated environment - --outdir dist/ - . - # skip due to the bad request error from pypi: - # binary wheel has an unsupported platform tag 'linux_x86_64' - #--wheel - - - name: Publish developed version 📦 to Test PyPI - if: startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository_url: https://test.pypi.org/legacy/ - skip_existing: false - verbose: true - - - name: Publish released version 📦 to PyPI - if: startsWith(github.ref, 'refs/tags/v') - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_API_TOKEN }} - verbose: true diff --git a/README.md b/README.md index 78bb1d0..6775861 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ or via `apt` (or other package managers) for [Debian-derivative OS](https://wiki apt install python3-pysolid ``` -Installing via `conda` and `apt` is recomended because PySolid contains Fortran source code, which required compilcation. Otherwise, you may build it from source as described below. +Installing via `conda` and `apt` is recommended because PySolid contains Fortran source code, which required compilation. Otherwise, you may build it from source as described below. #### 1.1 Build from source @@ -56,10 +56,14 @@ python -m pip install -r PySolid/requirements.txt ```bash # option 1: use pip to install pysolid into the current environment -# use "pip install -e" to install in the development mode python -m pip install PySolid -# option 2: manually compile the Fortran code and setup environment variable +# option 2: use pip to install pysolid in develop mode (editable) into the current environment +# setting an environmental variable as below is required for editable installs via pyproject.toml +export SETUPTOOLS_ENABLE_FEATURES="legacy-editable" +python -m pip install -e PySolid + +# option 3: manually compile the Fortran code and setup environment variable cd PySolid/src/pysolid f2py -c -m solid solid.for export PYTHONPATH=${PYTHONPATH}:~/tools/PySolid diff --git a/environment.yml b/environment.yml index b21dec6..8f069af 100644 --- a/environment.yml +++ b/environment.yml @@ -3,8 +3,15 @@ channels: - conda-forge - defaults dependencies: + - python>=3.8 + # for running - numpy - scipy - matplotlib - scikit-image - - fortran-compiler # A generic way to obtain the Fortran compiler across platforms through conda-forge channel + # for packaging and installation + - fortran-compiler # Fortran compiler across platforms through conda-forge channel + - pip + - setuptools + - setuptools_scm + - wheel diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..f8152fd --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,49 @@ +[build-system] +requires = ["setuptools>=61.0", "setuptools_scm[toml]>=6.2", "numpy", "wheel"] + +build-backend = "setuptools.build_meta" + +[project] +name = "pysolid" +authors = [ + {name="Zhang Yunjun", email="yunjunzgeo@gmail.com"}, + {name="Dennis Milbert"}, +] +description = "A Python wrapper for solid to compute solid Earth tides" +readme = "README.md" +requires-python = ">=3.8" +keywords = ["solid Earth tides", "deformation", "geodesy", "geophysics"] +license = {text = "GPL-3.0-or-later"} +classifiers=[ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", +] +dependencies = [ + "numpy", + "scipy", + "matplotlib", + "scikit-image", +] +dynamic = ["version"] + +[project.urls] +"Homepage" = "https://github.com/insarlab/PySolid" +"Bug Tracker" = "https://github.com/insarlab/PySolid/issues" + +[tool.setuptools] +include-package-data = true +zip-safe = false + +[tool.setuptools.packages.find] +where = ["src"] + +[tool.setuptools.package-data] +pysolid = ["*.for"] + +[tool.setuptools_scm] +version_scheme = "post-release" +local_scheme = "no-local-version" diff --git a/requirements.txt b/requirements.txt index 2aa8729..2a32a24 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,11 @@ +# for running numpy scipy matplotlib scikit-image +# for packaging and installation +#fortran-compiler # Fortran compiler across platforms through conda-forge channel +pip +setuptools +setuptools_scm +wheel diff --git a/setup.py b/setup.py index f28aa09..1a4a3a6 100644 --- a/setup.py +++ b/setup.py @@ -1,74 +1,13 @@ # Author: Zhang Yunjun, Jan 2021 # Copyright 2020, by the California Institute of Technology. -# Note by Yunjun, Oct 2022: "pip install pysolid" does not work, -# because a Fortran compiler is required but not available via pip - - -import os -import sys # always prefer setuptools over distutils import setuptools from numpy.distutils.core import setup, Extension -# Grab from pysolid.version: version -# link: https://stackoverflow.com/questions/53648900 -sys.path.append(os.path.join(os.path.dirname(__file__), 'src')) -from pysolid.version import version - -# Grab from README file: long_description -with open("README.md", "r") as f: - long_description = f.read() - setup( - name='pysolid', - version=version, - description="A Python wrapper for solid to compute solid Earth tides", - url="https://github.com/insarlab/PySolid", - download_url=("https://github.com/insarlab/PySolid/archive/v{}.tar.gz".format(version)), - long_description=long_description, - long_description_content_type="text/markdown", - author="Zhang Yunjun, Dennis Milbert", - author_email="yunjunzgeo@gmail.com", - license="GPL-3.0-or-later", - license_files=("LICENSE",), - - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Science/Research", - "Topic :: Scientific/Engineering", - "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - ], - keywords="solid Eartth tides, deformation, geodesy, geophysics", - - project_urls={ - "Bug Reports": "https://github.com/insarlab/PySolid/issues", - "Source": "https://github.com/insarlab/PySolid", - }, - - # package discovery - packages=setuptools.find_packages("src"), # include all packages under src - package_dir={"": "src"}, # tell distutils packages are under src - - # build fortran deps with numpy.f2py + # fortran extensions to build with numpy.f2py ext_modules=[ Extension(name='pysolid.solid', sources=['src/pysolid/solid.for']), ], - - # dependencies - python_requires=">=3.6", - install_requires=[ - 'numpy', - 'scipy', - 'matplotlib', - 'scikit-image', - ], - - # data files - include_package_data=True, - package_data={ - "pysolid": ["solid.for"], - }, ) diff --git a/src/pysolid/__init__.py b/src/pysolid/__init__.py index 5c19a38..8093ff1 100644 --- a/src/pysolid/__init__.py +++ b/src/pysolid/__init__.py @@ -1,5 +1,14 @@ +from importlib.metadata import PackageNotFoundError, version + # get version info -from pysolid.version import version as __version__ +try: + __version__ = version(__name__) +except PackageNotFoundError: + print('package is not installed!\n' + 'Please follow the installation instructions in the README.md.\n' + 'Or, to just get the version number, use:\n' + ' python -m setuptools_scm') + # top-level functions from pysolid.grid import ( @@ -12,3 +21,13 @@ plot_solid_earth_tides_point, plot_power_spectral_density4tides, ) + +__all__ = [ + '__version__', + 'calc_solid_earth_tides_grid', + 'plot_solid_earth_tides_grid', + 'TIDES', + 'calc_solid_earth_tides_point', + 'plot_solid_earth_tides_point', + 'plot_power_spectral_density4tides', +] diff --git a/src/pysolid/grid.py b/src/pysolid/grid.py index 967bfd1..3920943 100644 --- a/src/pysolid/grid.py +++ b/src/pysolid/grid.py @@ -15,6 +15,7 @@ import os import numpy as np +from skimage.transform import resize ################################## Earth tides - grid mode ################################### @@ -41,7 +42,6 @@ def calc_solid_earth_tides_grid(dt_obj, atr, step_size=1e3, display=False, verbo Examples: atr = readfile.read_attribute('geo_velocity.h5') tide_e, tide_n, tide_u = calc_solid_earth_tides_grid('20180219', atr) """ - from skimage.transform import resize try: from pysolid.solid import solid_grid except ImportError: diff --git a/src/pysolid/point.py b/src/pysolid/point.py index adb7302..07d109a 100644 --- a/src/pysolid/point.py +++ b/src/pysolid/point.py @@ -16,6 +16,8 @@ import os import numpy as np +from matplotlib import pyplot as plt, ticker, dates as mdates +from scipy import signal ## Tidal constituents @@ -203,8 +205,6 @@ def calc_solid_earth_tides_point_per_day(lat, lon, date_str, step_sec=60): def plot_solid_earth_tides_point(dt_out, tide_e, tide_n, tide_u, lalo=None, out_fig=None, save=False, display=True): """Plot the solid Earth tides at one point.""" - from matplotlib import pyplot as plt, dates as mdates - # plot fig, axs = plt.subplots(nrows=3, ncols=1, figsize=[6, 4], sharex=True) for ax, data, label in zip(axs.flatten(), @@ -247,9 +247,6 @@ def plot_power_spectral_density4tides(tide_ts, sample_spacing, out_fig=None, fig """Plot the power spectral density (PSD) of tides time-series. Note: for accurate PSD analysis, a long time-series, e.g. one year, is recommended. """ - from matplotlib import pyplot as plt, ticker - from scipy import signal - ## calc PSD freq, psd = signal.periodogram(tide_ts, fs=1/sample_spacing, scaling='density') # get rid of zero in the first element diff --git a/src/pysolid/version.py b/src/pysolid/version.py deleted file mode 100644 index 6c4d9e7..0000000 --- a/src/pysolid/version.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 -# Author: Zhang Yunjun, Jan 2021 -# Copyright 2020, by the California Institute of Technology. - - -import collections -import os -import subprocess - - -########################################################################### -# release history -Tag = collections.namedtuple('Tag', 'version date') -release_history = ( - Tag('0.2.4', '2022-10-24'), - Tag('0.2.3', '2022-10-23'), - Tag('0.2.2', '2022-07-20'), - Tag('0.2.1', '2022-01-05'), - Tag('0.2.0', '2021-11-10'), - Tag('0.1.2', '2021-02-24'), - Tag('0.1.1', '2021-02-01'), - Tag('0.1.0', '2021-01-22'), -) - -# latest release -release_version = release_history[0].version -release_date = release_history[0].date - -# get development version info -def get_version_info(): - """Grab version and date of the latest commit from a git repository""" - # go to the repository directory - dir_orig = os.getcwd() - os.chdir(os.path.dirname(os.path.dirname(__file__))) - - try: - # grab from git cmd - cmd = "git describe --tags" - version = subprocess.check_output(cmd.split(), stderr=subprocess.DEVNULL) - version = version.decode('utf-8').strip()[1:] - - # if there are new commits after the latest release - if '-' in version: - version, num_commit = version.split('-')[:2] - version += f'-{num_commit}' - - cmd = "git log -1 --date=short --format=%cd" - date = subprocess.check_output(cmd.split(), stderr=subprocess.DEVNULL) - date = date.decode('utf-8').strip() - - except: - # use the latest release version/date - version = release_version - date = release_date - - # go back to the original directory - os.chdir(dir_orig) - return version, date - - -########################################################################### -version, version_date = get_version_info() -