Skip to content
Open
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
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ tags
# own stuff
info/

# Cython generated C code
*.c
*.cpp

# generated version file
src/gstools/_version.py

Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to **GSTools** will be documented in this file.

### Changes

- drop Cython backend and use Rust as the only backend [#420](https://github.com/GeoStat-Framework/GSTools/pull/420)
- add decision-tree based plurigaussian fields [#387](https://github.com/GeoStat-Framework/GSTools/pull/387)
- instead of spatial rules, as before, decision trees are a more flexible approach
- each node of the binary tree is a decision based on the values of the SRF
Expand Down
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ running. Install the package by typing the following command in a command termin

To install the latest development version via pip, see the
[documentation][doc_install_link].
One thing to point out is that this way, the non-parallel version of GSTools
is installed. In case you want the parallel version, follow these easy
[steps][doc_install_link].


## Citation
Expand Down
63 changes: 5 additions & 58 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,22 @@ conda
GSTools can be installed via
`conda <https://docs.conda.io/en/latest/miniconda.html>`_ on Linux, Mac, and
Windows.
Install the package by typing the following command in a command terminal:
Install the package by typing the following command in a terminal:

.. code-block:: none

conda install gstools

In case conda forge is not set up for your system yet, see the easy to follow
instructions on `conda forge <https://github.com/conda-forge/gstools-feedstock#installing-gstools>`_.
Using conda, the parallelized version of GSTools should be installed.


pip
---

GSTools can be installed via `pip <https://pypi.org/project/gstools/>`_
on Linux, Mac, and Windows.
On Windows you can install `WinPython <https://winpython.github.io/>`_ to get
Python and pip running.
Install the package by typing the following into command in a command terminal:
Install the package by typing the following into command in a terminal:

.. code-block:: none

Expand All @@ -79,60 +76,10 @@ To get the latest development version you can install it directly from GitHub:

If something went wrong during installation, try the :code:`-I` `flag from pip <https://pip-python3.readthedocs.io/en/latest/reference/pip_install.html?highlight=i#cmdoption-i>`_.

**Speeding up GSTools by parallelization**
**Running GSTools in parallel**

We provide two possibilities to run GSTools in parallel, often causing a
massive improvement in runtime. In either case, the number of parallel
threads can be set with the global variable `config.NUM_THREADS`. If not set,
all cores are used.
When using conda, the parallel version of GSTools is installed per default.

***Parallelizing Cython***

For parallel support, the `GSTools-Cython <https://github.com/GeoStat-Framework/GSTools-Cython>`_
backend needs to be compiled from source the following way:

.. code-block:: none

export GSTOOLS_BUILD_PARALLEL=1
pip install --no-binary=gstools-cython gstools

You have to provide a C compiler and OpenMP to compile GSTools-Cython with parallel support.
The feature is controlled by the environment variable
``GSTOOLS_BUILD_PARALLEL``, that can be ``0`` or ``1`` (interpreted as ``0`` if not present).
Note, that the ``--no-binary=gstools-cython`` option forces pip to not use a wheel
for the GSTools-Cython backend.

For the development version, you can do almost the same:

.. code-block:: none

export GSTOOLS_BUILD_PARALLEL=1
pip install git+git://github.com/GeoStat-Framework/GSTools-Cython.git@main
pip install git+git://github.com/GeoStat-Framework/GSTools.git@main


***Using GSTools-Core for parallelization and even more speed***

You can install the optional dependency `GSTools-Core <https://github.com/GeoStat-Framework/GSTools-Core>`_,
which is a re-implementation of GSTools-Cython:

.. code-block:: none

pip install gstools[rust]

or by manually installing the package

.. code-block:: none

pip install gstools-core

The new package uses the language Rust and it should be safer and faster (in some cases by orders of magnitude).
Once the package GSTools-Core is available on your machine, it will be used by default.
In case you want to switch back to the Cython implementation, you can set
:code:`gstools.config.USE_GSTOOLS_CORE=False` in your code. This also works at runtime.

GSTools-Core will automatically run in parallel, without having to provide OpenMP or a local C compiler.
The number of parallel threads can be set with the global variable
`config.NUM_THREADS`. If not set, all available cores are used.


Citation
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ classifiers = [
"Topic :: Utilities",
]
dependencies = [
"gstools-cython>=1,<2",
"gstools_core>=1.3.0",
"emcee>=3.0.0",
"hankel>=1.0.0",
"meshio>=5.1.0",
Expand Down
9 changes: 0 additions & 9 deletions src/gstools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,3 @@
"""

NUM_THREADS = None

try: # pragma: no cover
import gstools_core # noqa: F401

_GSTOOLS_CORE_AVAIL = True
USE_GSTOOLS_CORE = True
except ImportError:
_GSTOOLS_CORE_AVAIL = False
USE_GSTOOLS_CORE = False
61 changes: 9 additions & 52 deletions src/gstools/field/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,62 +19,19 @@
from copy import deepcopy as dcp

import numpy as np
from gstools_cython.field import summate as summate_c
from gstools_cython.field import summate_fourier as summate_fourier_c
from gstools_cython.field import summate_incompr as summate_incompr_c
from gstools_core import summate, summate_fourier, summate_incompr

from gstools import config
from gstools.covmodel.base import CovModel
from gstools.random.rng import RNG
from gstools.tools.geometric import generate_grid

if config._GSTOOLS_CORE_AVAIL: # pragma: no cover
from gstools_core import summate as summate_gsc
from gstools_core import summate_fourier as summate_fourier_gsc
from gstools_core import summate_incompr as summate_incompr_gsc

__all__ = ["Generator", "RandMeth", "IncomprRandMeth", "Fourier"]


SAMPLING = ["auto", "inversion", "mcmc"]


def _summate(cov_samples, z_1, z_2, pos, num_threads=None):
"""A wrapper function for calling the randomization algorithms."""
if config.USE_GSTOOLS_CORE and config._GSTOOLS_CORE_AVAIL:
summate_fct = summate_gsc
else:
summate_fct = summate_c
return summate_fct(cov_samples, z_1, z_2, pos, num_threads)


def _summate_incompr(
cov_samples,
z_1,
z_2,
pos,
num_threads=None,
):
"""A wrapper function for calling the incompr. randomization algorithms."""

if config.USE_GSTOOLS_CORE and config._GSTOOLS_CORE_AVAIL:
summate_incompr_fct = summate_incompr_gsc
else:
summate_incompr_fct = summate_incompr_c
return summate_incompr_fct(cov_samples, z_1, z_2, pos, num_threads)


def _summate_fourier(spectrum_factor, modes, z_1, z_2, pos, num_threads=None):
"""A wrapper function for calling the Fourier algorithms."""
if config.USE_GSTOOLS_CORE and config._GSTOOLS_CORE_AVAIL:
summate_fourier_fct = summate_fourier_gsc
else:
summate_fourier_fct = summate_fourier_c
return summate_fourier_fct(
spectrum_factor, modes, z_1, z_2, pos, num_threads
)


class Generator(ABC):
"""
Abstract generator class.
Expand Down Expand Up @@ -243,7 +200,7 @@ def __init__(
def __call__(self, pos, add_nugget=True):
"""Calculate the random modes for the randomization method.

This method calls the `summate_*` Rust or Cython methods, which are
This method calls the `summate_*` methods from the backend. They are
the heart of the randomization method.

Parameters
Expand All @@ -263,7 +220,7 @@ def __call__(self, pos, add_nugget=True):
shp = pos.shape[1:]
return self.get_nugget(shp) if add_nugget else np.full(shp, 0.0)
# generate if var is not 0
summed_modes = _summate(
summed_modes = summate(
self._cov_sample, self._z_1, self._z_2, pos, config.NUM_THREADS
)
nugget = self.get_nugget(summed_modes.shape) if add_nugget else 0.0
Expand Down Expand Up @@ -529,8 +486,8 @@ def __init__(
def __call__(self, pos, add_nugget=True):
"""Calculate the random modes for the randomization method.

This method calls the `summate_incompr_*` Rust or Cython methods,
which are the heart of the randomization method.
This method calls the `summate_incompr_*` methods from the backend,
They are the heart of the randomization method.
In this class the method contains a projector to
ensure the incompressibility of the vector field.

Expand All @@ -551,7 +508,7 @@ def __call__(self, pos, add_nugget=True):
e1 = self._create_unit_vector(pos.shape)
if self.zero_var:
return self.mean_u * e1 + nugget
summed_modes = _summate_incompr(
summed_modes = summate_incompr(
self._cov_sample,
self._z_1,
self._z_2,
Expand Down Expand Up @@ -659,7 +616,7 @@ def __init__(
def __call__(self, pos, add_nugget=True):
"""Calculate the modes for the Fourier method.

This method calls the `summate_fourier` Cython method, which is the
This method calls the `summate_fourier` method from the backend. It is the
heart of the Fourier method.

Parameters
Expand All @@ -679,7 +636,7 @@ def __call__(self, pos, add_nugget=True):
shp = pos.shape[1:]
return self.get_nugget(shp) if add_nugget else np.full(shp, 0.0)
# generate if var is not 0
summed_modes = _summate_fourier(
summed_modes = summate_fourier(
self._spectrum_factor,
self._modes,
self._z_1,
Expand Down Expand Up @@ -810,7 +767,7 @@ def reset_seed(self, seed=np.nan):
self._z_1 = self._rng.random.normal(size=np.prod(self._mode_no))
self._z_2 = self._rng.random.normal(size=np.prod(self._mode_no))
# pre calc. the spectrum for all wave numbers they are handed over to
# Cython, which doesn't have access to the CovModel
# the backend, which doesn't have access to the CovModel
if self.zero_var:
self._spectrum_factor = np.full(np.prod(self._mode_no), 0.0)
else:
Expand Down
46 changes: 8 additions & 38 deletions src/gstools/krige/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@

import numpy as np
import scipy.linalg as spl
from gstools_cython.krige import calc_field_krige as calc_field_krige_c
from gstools_cython.krige import (
calc_field_krige_and_variance as calc_field_krige_and_variance_c,
)
from gstools_core import calc_field_krige, calc_field_krige_and_variance
from scipy.spatial.distance import cdist

from gstools import config
Expand All @@ -26,41 +23,13 @@
from gstools.tools.misc import eval_func
from gstools.variogram import vario_estimate

if config._GSTOOLS_CORE_AVAIL: # pragma: no cover
from gstools_core import calc_field_krige as calc_field_krige_gsc
from gstools_core import (
calc_field_krige_and_variance as calc_field_krige_and_variance_gsc,
)

__all__ = ["Krige"]


P_INV = {"pinv": spl.pinv, "pinvh": spl.pinvh}
"""dict: Standard pseudo-inverse routines"""


def _calc_field_krige(krig_mat, krig_vecs, cond, num_threads=None):
"""A wrapper function for calling the krige algorithms."""
if config.USE_GSTOOLS_CORE and config._GSTOOLS_CORE_AVAIL:
calc_field_krige_fct = calc_field_krige_gsc
else:
calc_field_krige_fct = calc_field_krige_c
return calc_field_krige_fct(krig_mat, krig_vecs, cond, num_threads)


def _calc_field_krige_and_variance(
krig_mat, krig_vecs, cond, num_threads=None
):
"""A wrapper function for calling the krige algorithms."""
if config.USE_GSTOOLS_CORE and config._GSTOOLS_CORE_AVAIL:
calc_field_krige_and_variance_fct = calc_field_krige_and_variance_gsc
else:
calc_field_krige_and_variance_fct = calc_field_krige_and_variance_c
return calc_field_krige_and_variance_fct(
krig_mat, krig_vecs, cond, num_threads
)


class Krige(Field):
"""
A Swiss Army knife for kriging.
Expand Down Expand Up @@ -306,14 +275,15 @@ def __call__(

def _summate(self, field, krige_var, c_slice, k_vec, return_var):
if return_var: # estimate error variance
field[c_slice], krige_var[c_slice] = (
_calc_field_krige_and_variance(
self._krige_mat, k_vec, self._krige_cond
)
field[c_slice], krige_var[c_slice] = calc_field_krige_and_variance(
self._krige_mat,
k_vec,
self._krige_cond,
config.NUM_THREADS,
)
else: # solely calculate the interpolated field
field[c_slice] = _calc_field_krige(
self._krige_mat, k_vec, self._krige_cond
field[c_slice] = calc_field_krige(
self._krige_mat, k_vec, self._krige_cond, config.NUM_THREADS
)

def _inv(self, mat):
Expand Down
Loading
Loading