Skip to content

Commit 39ca4d6

Browse files
authored
Merge pull request #243 from GeoStat-Framework/20220620_add_expint
Add: (Exponential-)Integral model
2 parents 267efa6 + 3b1ac65 commit 39ca4d6

File tree

5 files changed

+114
-12
lines changed

5 files changed

+114
-12
lines changed

Diff for: examples/02_cov_model/README.rst

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ The following standard covariance models are provided by GSTools
6262
Gaussian
6363
Exponential
6464
Matern
65+
Integral
6566
Stable
6667
Rational
6768
Cubic

Diff for: src/gstools/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
Gaussian
6767
Exponential
6868
Matern
69+
Integral
6970
Stable
7071
Rational
7172
Cubic
@@ -145,6 +146,7 @@
145146
Exponential,
146147
Gaussian,
147148
HyperSpherical,
149+
Integral,
148150
JBessel,
149151
Linear,
150152
Matern,
@@ -193,6 +195,7 @@
193195
"Gaussian",
194196
"Exponential",
195197
"Matern",
198+
"Integral",
196199
"Stable",
197200
"Rational",
198201
"Cubic",

Diff for: src/gstools/covmodel/__init__.py

+3
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
Gaussian
3232
Exponential
3333
Matern
34+
Integral
3435
Stable
3536
Rational
3637
Cubic
@@ -59,6 +60,7 @@
5960
Exponential,
6061
Gaussian,
6162
HyperSpherical,
63+
Integral,
6264
JBessel,
6365
Linear,
6466
Matern,
@@ -79,6 +81,7 @@
7981
"Gaussian",
8082
"Exponential",
8183
"Matern",
84+
"Integral",
8285
"Stable",
8386
"Rational",
8487
"Cubic",

Diff for: src/gstools/covmodel/models.py

+99-11
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
Gaussian
1111
Exponential
1212
Matern
13+
Integral
1314
Stable
1415
Rational
1516
Cubic
@@ -28,11 +29,13 @@
2829

2930
from gstools.covmodel.base import CovModel
3031
from gstools.covmodel.tools import AttributeWarning
32+
from gstools.tools.special import exp_int, inc_gamma_low
3133

3234
__all__ = [
3335
"Gaussian",
3436
"Exponential",
3537
"Matern",
38+
"Integral",
3639
"Stable",
3740
"Rational",
3841
"Cubic",
@@ -363,23 +366,17 @@ def cor(self, h):
363366

364367
def spectral_density(self, k): # noqa: D102
365368
k = np.asarray(k, dtype=np.double)
369+
x = (k * self.len_rescaled) ** 2
366370
# for nu > 20 we just use an approximation of the gaussian model
367371
if self.nu > 20.0:
368372
return (
369373
(self.len_rescaled / np.sqrt(np.pi)) ** self.dim
370-
* np.exp(-((k * self.len_rescaled) ** 2))
371-
* (
372-
1
373-
+ (
374-
((k * self.len_rescaled) ** 2 - self.dim / 2.0) ** 2
375-
- self.dim / 2.0
376-
)
377-
/ self.nu
378-
)
374+
* np.exp(-x)
375+
* (1 + 0.5 * x**2 / self.nu)
376+
* np.sqrt(1 + x / self.nu) ** (-self.dim)
379377
)
380378
return (self.len_rescaled / np.sqrt(np.pi)) ** self.dim * np.exp(
381-
-(self.nu + self.dim / 2.0)
382-
* np.log(1.0 + (k * self.len_rescaled) ** 2 / self.nu)
379+
-(self.nu + self.dim / 2.0) * np.log(1.0 + x / self.nu)
383380
+ sps.loggamma(self.nu + self.dim / 2.0)
384381
- sps.loggamma(self.nu)
385382
- self.dim * np.log(np.sqrt(self.nu))
@@ -394,6 +391,97 @@ def calc_integral_scale(self): # noqa: D102
394391
)
395392

396393

394+
class Integral(CovModel):
395+
r"""The Exponential Integral covariance model.
396+
397+
Notes
398+
-----
399+
This model is given by the following correlation function [Mueller2021]_:
400+
401+
.. math::
402+
\rho(r) =
403+
\frac{\nu}{2}\cdot
404+
E_{1+\frac{\nu}{2}}\left( \left( s\cdot\frac{r}{\ell} \right)^2 \right)
405+
406+
Where the standard rescale factor is :math:`s=1`.
407+
:math:`E_s(x)` is the exponential integral.
408+
409+
:math:`\nu` is a shape parameter (1 by default).
410+
411+
For :math:`\nu \to \infty`, a gaussian model is approached, since it represents
412+
the limiting case:
413+
414+
.. math::
415+
\rho(r) =
416+
\exp\left(-\left(s\cdot\frac{r}{\ell}\right)^2\right)
417+
418+
References
419+
----------
420+
.. [Mueller2021] Müller, S., Heße, F., Attinger, S., and Zech, A.,
421+
"The extended generalized radial flow model and effective
422+
conductivity for truncated power law variograms",
423+
Adv. Water Resour., 156, 104027, (2021)
424+
425+
Other Parameters
426+
----------------
427+
nu : :class:`float`, optional
428+
Shape parameter. Standard range: ``(0.0, 50]``
429+
Default: ``1.0``
430+
"""
431+
432+
def default_opt_arg(self):
433+
"""Defaults for the optional arguments.
434+
435+
* ``{"nu": 1.0}``
436+
437+
Returns
438+
-------
439+
:class:`dict`
440+
Defaults for optional arguments
441+
"""
442+
return {"nu": 1.0}
443+
444+
def default_opt_arg_bounds(self):
445+
"""Defaults for boundaries of the optional arguments.
446+
447+
* ``{"nu": [0.0, 50.0, "oc"]}``
448+
449+
Returns
450+
-------
451+
:class:`dict`
452+
Boundaries for optional arguments
453+
"""
454+
return {"nu": [0.0, 50.0, "oc"]}
455+
456+
def cor(self, h):
457+
"""Exponential Integral normalized correlation function."""
458+
h = np.asarray(h, dtype=np.double)
459+
return 0.5 * self.nu * exp_int(1.0 + 0.5 * self.nu, h**2)
460+
461+
def spectral_density(self, k): # noqa: D102
462+
k = np.asarray(k, dtype=np.double)
463+
x = (k * self.len_rescaled / 2.0) ** 2
464+
# for nu > 50 we just use an approximation of the gaussian model
465+
if self.nu > 50.0:
466+
return (
467+
(0.5 * self.len_rescaled / np.sqrt(np.pi)) ** self.dim
468+
* np.exp(-x)
469+
* self.nu
470+
/ (self.nu + self.dim)
471+
* (1.0 + 2 * x / (self.nu + self.dim + 2))
472+
)
473+
return (
474+
self.nu
475+
/ (x ** (self.nu * 0.5) * 2 * (k * np.sqrt(np.pi)) ** self.dim)
476+
* inc_gamma_low((self.nu + self.dim) / 2.0, x)
477+
)
478+
479+
def calc_integral_scale(self): # noqa: D102
480+
return (
481+
self.len_rescaled * self.nu * np.sqrt(np.pi) / (2 * self.nu + 2.0)
482+
)
483+
484+
397485
class Rational(CovModel):
398486
r"""The rational quadratic covariance model.
399487

Diff for: tests/test_covmodel.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
Exponential,
1414
Gaussian,
1515
HyperSpherical,
16+
Integral,
1617
JBessel,
1718
Linear,
1819
Matern,
@@ -82,6 +83,7 @@ def setUp(self):
8283
SuperSpherical,
8384
JBessel,
8485
TPLSimple,
86+
Integral,
8587
]
8688
self.tpl_cov_models = [
8789
TPLGaussian,
@@ -396,11 +398,16 @@ def test_rescale(self):
396398
self.assertAlmostEqual(model1.integral_scale, model3.integral_scale)
397399

398400
def test_special_models(self):
399-
# matern converges to gaussian
401+
# Matern and Integral converge to gaussian
402+
model0 = Integral(rescale=0.5)
403+
model0.set_arg_bounds(nu=[0, 1001])
404+
model0.nu = 1000
400405
model1 = Matern()
401406
model1.set_arg_bounds(nu=[0, 101])
402407
model1.nu = 100
403408
model2 = Gaussian(rescale=0.5)
409+
self.assertAlmostEqual(model0.variogram(1), model2.variogram(1), 2)
410+
self.assertAlmostEqual(model0.spectrum(1), model2.spectrum(1), 2)
404411
self.assertAlmostEqual(model1.variogram(1), model2.variogram(1))
405412
self.assertAlmostEqual(model1.spectrum(1), model2.spectrum(1), 2)
406413
# stable model gets unstable for alpha < 0.3

0 commit comments

Comments
 (0)