Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ jobs:
strategy:
fail-fast: false
matrix:
# macos-13 is an intel runner, macos-latest is apple silicon
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-latest]
# macos-15-intel is an intel runner, macos-latest is apple silicon
os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-latest]

steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"

- name: Build wheels
uses: pypa/cibuildwheel@v2.22.0
uses: pypa/cibuildwheel@v3.3.0
with:
output-dir: dist-wheel-${{ matrix.os }}

Expand All @@ -82,19 +82,17 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-13, macos-14]
os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest]
# https://github.com/scipy/oldest-supported-numpy/blob/main/setup.cfg
ver:
- { py: "3.9", np: "==1.20.0" }
- { py: "3.10", np: "==1.21.6" }
- { py: "3.11", np: "==1.23.2" }
- { py: "3.12", np: "==1.26.2" }
- { py: "3.13", np: "==2.1.0" }
- { py: "3.13", np: ">=2.1.0" }
- { py: "3.14", np: "==2.3.2" }
- { py: "3.14", np: ">=2.3.2" }
exclude:
- os: macos-14
ver: { py: "3.9", np: "==1.20.0" }
- os: macos-14
- os: macos-latest
ver: { py: "3.10", np: "==1.21.6" }
steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -155,14 +153,14 @@ jobs:
- name: Install GSTools-Cython
env:
GSTOOLS_CY_COV: 1
GSTOOLS_BUILD_PARALLEL: 1
run: |
pip install -v --editable .[test]

- name: Run tests
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
pip install "numpy${{ matrix.ver.np }}"
python -m pytest --cov gstools_cython --cov-report term-missing -v tests/
python -m coveralls --service=github

Expand Down
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

All notable changes to **GSTools-Cython** will be documented in this file.

## [1.2.0] - 2025-12

See [#11](https://github.com/GeoStat-Framework/GSTools-Cython/pull/11)

### Changes

- add support for Python 3.14 (incl. free-threaded support)
- move pypy version to 3.11
- add win arm64 wheels (without Python 3.10, since there are no numpy wheels prior to 3.11)
- remove support for Python 3.9 (EOL)
- fix bug in error message in variogram.pyx (undetected by cython<3.1)
- update pyproject.toml and use setuptools>=77
- increased coverage

## [1.1.0] - 2025-04

See [#5](https://github.com/GeoStat-Framework/GSTools-Cython/pull/5)
Expand All @@ -23,6 +37,7 @@ First release of GSTools-Cython
- moved Cython files into this separate package


[Unreleased]: https://github.com/GeoStat-Framework/gstools-cython/compare/v1.1.0...HEAD
[Unreleased]: https://github.com/GeoStat-Framework/gstools-cython/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/GeoStat-Framework/gstools-cython/compare/v1.1.0...v1.2.0
[1.1.0]: https://github.com/GeoStat-Framework/gstools-cython/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/GeoStat-Framework/gstools-cython/releases/tag/v1.0.0
6 changes: 4 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,13 @@ def setup(app):
# latex_show_urls = 'footnote'
# http://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-latex-output
latex_elements = {
"preamble": r"""
"preamble": (
r"""
\setcounter{secnumdepth}{1}
\setcounter{tocdepth}{2}
\pagestyle{fancy}
""",
"""
),
"pointsize": "10pt",
"papersize": "a4paper",
"fncychap": "\\usepackage[Glenn]{fncychap}",
Expand Down
26 changes: 11 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,43 +1,42 @@
[build-system]
requires = [
"setuptools>=64",
"setuptools<72.2; implementation_name == 'pypy'", # https://github.com/pypa/distutils/issues/283
"setuptools>=77",
"setuptools_scm>=7",
"numpy>=2.0.0rc1",
"Cython>=3.0.10,<3.1.0",
"numpy>=2",
"Cython>=3",
"extension-helpers>=1",
]
build-backend = "setuptools.build_meta"

[project]
requires-python = ">=3.9"
requires-python = ">=3.10"
name = "gstools_cython"
description = "Cython backend for GSTools."
authors = [
{name = "Sebastian Müller, Lennart Schüler", email = "info@geostat-framework.org"},
]
readme = "README.md"
license = {text = "LGPL-3.0"}
license = "LGPL-3.0-or-later"
license-files = ["LICENSE"]
dynamic = ["version"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Intended Audience :: End Users/Desktop",
"Intended Audience :: Science/Research",
"Intended Audience :: Education",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Natural Language :: English",
"Operating System :: Unix",
"Operating System :: Microsoft",
"Operating System :: MacOS",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: GIS",
"Topic :: Scientific/Engineering :: Hydrology",
Expand All @@ -58,7 +57,7 @@ doc = [
]
test = [
"pytest-cov>=3",
"Cython>=3.0.10,<3.1.0",
"Cython>=3",
]
lint = [
"black>=24",
Expand All @@ -75,9 +74,6 @@ Homepage = "https://geostat-framework.org/#gstools"
Source = "https://github.com/GeoStat-Framework/GSTools-Cython"
Tracker = "https://github.com/GeoStat-Framework/GSTools-Cython/issues"

[tool.setuptools]
license-files = ["LICENSE"]

[tool.setuptools_scm]
write_to = "src/gstools_cython/_version.py"
write_to_template = "__version__ = '{version}'"
Expand All @@ -90,11 +86,11 @@ multi_line_output = 3

[tool.black]
target-version = [
"py39",
"py310",
"py311",
"py312",
"py313",
"py314",
]

[tool.coverage]
Expand Down Expand Up @@ -145,8 +141,8 @@ target-version = [
build-frontend = "build"
# explicitly enable pypy
enable = ["pypy"]
# Disable building py3.6/7/8, pp3.8, 32bit linux
skip = ["cp36-*", "cp37-*", "cp38-*", "pp38-*", "*_i686"]
# Disable building py3.8/9, 32bit linux, and arm64 windows wheels for python 3.10 (no numpy support)
skip = ["cp38-*", "cp39-*", "*_i686", "cp310-win_arm64"]
# Run the package tests using `pytest`
test-extras = "test"
test-command = "pytest -v {package}/tests"
4 changes: 2 additions & 2 deletions src/gstools_cython/variogram.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ def directional(
counts of samples per bin and direciton
"""
if pos.shape[1] != f.shape[1]:
raise ValueError(f'len(pos) = {pos.shape[1]} != len(f) = {f.shape[1])}')
raise ValueError(f'len(pos) = {pos.shape[1]} != len(f) = {f.shape[1]}')

if bin_edges.shape[0] < 2:
raise ValueError('len(bin_edges) too small')
Expand Down Expand Up @@ -359,7 +359,7 @@ def unstructured(
raise ValueError(f'Haversine: dim = {dim} != 2')

if pos.shape[1] != f.shape[1]:
raise ValueError(f'len(pos) = {pos.shape[1]} != len(f) = {f.shape[1])}')
raise ValueError(f'len(pos) = {pos.shape[1]} != len(f) = {f.shape[1]}')

if bin_edges.shape[0] < 2:
raise ValueError('len(bin_edges) too small')
Expand Down
2 changes: 2 additions & 0 deletions tests/test_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ def test_summate(self):
)
summed = gs_cy.field.summate(cov_samples, z_1, z_2, pos)
np.testing.assert_allclose(summed_modes, summed)
summed_threads = gs_cy.field.summate(cov_samples, z_1, z_2, pos, num_threads=2)
np.testing.assert_allclose(summed_modes, summed_threads)

def test_summate_incompr(self):
# x = y = np.linspace(0,1,3)
Expand Down
5 changes: 5 additions & 0 deletions tests/test_krige.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ def test_calc_field_krige_and_variance(self):
)
np.testing.assert_allclose(field, self.field_ref)
np.testing.assert_allclose(error, self.error_ref)
field_threads, error_threads = gs_cy.krige.calc_field_krige_and_variance(
self.krig_mat, self.krig_vecs, self.cond, num_threads=2
)
np.testing.assert_allclose(field_threads, self.field_ref)
np.testing.assert_allclose(error_threads, self.error_ref)

def test_calc_field_krige(self):
field = gs_cy.krige.calc_field_krige(self.krig_mat, self.krig_vecs, self.cond)
Expand Down
92 changes: 92 additions & 0 deletions tests/test_variogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,58 @@ def test_directional(self):
self.assertAlmostEqual(gamma[1, len(gamma[0]) // 2], var, places=2)
self.assertAlmostEqual(gamma[1, -1], var, places=2)

def test_directional_separate_dirs(self):
pos = np.array(((0.0, 1.0), (0.0, 0.0)), dtype=np.double)
dirs = np.array(((1.0, 0.0), (1.0, 0.0)), dtype=np.double)
field = np.array(((1.0, 2.0),), dtype=np.double)
bins = np.array((0.0, 2.0), dtype=np.double)

_, counts = gs_cy.variogram.directional(
field, bins, pos, dirs, angles_tol=np.pi, separate_dirs=True
)

self.assertEqual(counts[0, 0], 1)
self.assertEqual(counts[1, 0], 0)

def test_directional_bandwidth_cressie(self):
pos = np.array(((0.0, 0.0, 1.0), (0.0, 1.0, 0.0)), dtype=np.double)
dirs = np.array(((1.0, 0.0),), dtype=np.double)
field = np.array(((1.0, 2.0, 5.0),), dtype=np.double)
bins = np.array((0.0, 2.0), dtype=np.double)

gamma, counts = gs_cy.variogram.directional(
field,
bins,
pos,
dirs,
angles_tol=np.pi,
bandwidth=0.5,
estimator_type="c",
)

self.assertEqual(counts[0, 0], 1)
f_diff = field[0, 2] - field[0, 0]
raw = np.sqrt(abs(f_diff))
expected = 0.5 * raw**4 / (0.457 + 0.494 + 0.045)
self.assertAlmostEqual(gamma[0, 0], expected, places=6)

def test_directional_error_checks(self):
pos = np.array(((0.0, 1.0), (0.0, 1.0)), dtype=np.double)
dirs = np.array(((1.0, 0.0),), dtype=np.double)
bins = np.array((0.0, 1.0), dtype=np.double)
field = np.array(((1.0, 2.0),), dtype=np.double)

with self.assertRaises(ValueError):
gs_cy.variogram.directional(field[:, :1], bins, pos, dirs)

with self.assertRaises(ValueError):
gs_cy.variogram.directional(
field, np.array((0.0,), dtype=np.double), pos, dirs
)

with self.assertRaises(ValueError):
gs_cy.variogram.directional(field, bins, pos, dirs, angles_tol=0.0)

def test_unstructured(self):
x = np.arange(1, 11, 1, dtype=np.double)
z = np.array(
Expand Down Expand Up @@ -73,6 +125,46 @@ def test_unstructured(self):
self.assertAlmostEqual(gamma[len(gamma) // 2], var, places=2)
self.assertAlmostEqual(gamma[-1], var, places=2)

def test_unstructured_haversine(self):
pos = np.array(((0.0, 0.0), (0.0, 90.0)), dtype=np.double)
field = np.array(((1.0, 3.0),), dtype=np.double)
bins = np.array((0.0, 2.0), dtype=np.double)

gamma, counts = gs_cy.variogram.unstructured(
field, bins, pos, distance_type="h"
)

self.assertEqual(counts[0], 1)
self.assertAlmostEqual(gamma[0], 2.0, places=6)

def test_unstructured_num_threads(self):
pos = np.array(((0.0, 1.0, 2.0),), dtype=np.double)
field = np.array(((1.0, 3.0, 2.0),), dtype=np.double)
bins = np.array((0.0, 2.0), dtype=np.double)

gamma_default, counts_default = gs_cy.variogram.unstructured(field, bins, pos)
gamma_threads, counts_threads = gs_cy.variogram.unstructured(
field, bins, pos, num_threads=2
)

np.testing.assert_allclose(gamma_threads, gamma_default)
np.testing.assert_array_equal(counts_threads, counts_default)

def test_unstructured_error_checks(self):
pos = np.array(((0.0, 1.0), (0.0, 1.0)), dtype=np.double)
field = np.array(((1.0, 2.0),), dtype=np.double)
bins = np.array((0.0, 1.0), dtype=np.double)

with self.assertRaises(ValueError):
gs_cy.variogram.unstructured(field[:, :1], bins, pos)

with self.assertRaises(ValueError):
gs_cy.variogram.unstructured(field, np.array((0.0,), dtype=np.double), pos)

pos_bad = np.array(((0.0, 1.0), (0.0, 1.0), (0.0, 1.0)), dtype=np.double)
with self.assertRaises(ValueError):
gs_cy.variogram.unstructured(field, bins, pos_bad, distance_type="h")

def test_structured(self):
z = np.array(
(41.2, 40.2, 39.7, 39.2, 40.1, 38.3, 39.1, 40.0, 41.1, 40.3),
Expand Down