├── .gitattributes ├── .gitignore ├── .readthedocs.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── Structural Analysis.ipynb ├── docs ├── Makefile ├── basics │ ├── cokriging.rst │ ├── kriging.rst │ └── simulation.rst ├── conf.py ├── doc_env.yml ├── index.rst ├── install.rst └── tutorials │ ├── EDA.ipynb │ └── NST.ipynb ├── gslib_help ├── cokb3d_help.md ├── gam_params.md ├── kb2d_help.md ├── kt3d_help.md ├── sgsim_help.md └── super_block_search.md ├── img ├── Grid_Definition.png ├── behavior_near_origin.png ├── cokb3d_params.png ├── gam_params.png ├── gamv_params.png ├── graph_of_lag_model.png ├── head_tail.png ├── kb2d_params.png └── super_block_search.png ├── parameters ├── gam_write_parameters.py ├── gamv_write_parameters.py ├── krige2d_write_params.py ├── krige3d_write_params.py └── sgsim_write_params.py ├── pygeostatistics ├── __init__.py ├── _version.py ├── cokrige.py ├── eda.py ├── gam.py ├── gamv.py ├── gslib_reader.py ├── krige2d.py ├── krige3d.py ├── normal_score_transform.py ├── sgsim.py ├── super_block.py ├── variogram_model.py └── yaml_patch.py ├── setup.cfg ├── setup.py ├── testData ├── test.gslib ├── test_krige2d.par ├── test_krige3d.par ├── test_sgsim.par ├── xihuSmall_sparse_gam.par ├── xihuSmall_sparse_gamv.par └── xihu_sparse.gslib ├── tests ├── __init__.py └── test_eda.py └── versioneer.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.ipynb linguist-language=Pythonpygeostatistics/_version.py export-subst 2 | pygeostatistics/_version.py export-subst 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # Jupyter Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | .venv/ 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | # VS code 93 | .vscode/ 94 | 95 | # Numpy data 96 | *.npy 97 | 98 | # pytest 99 | .pytest_cache/ 100 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | conda: 2 | file: docs/doc_env.yml 3 | 4 | python: 5 | version: 3 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yu Hao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | include pygeostatistics/_version.py 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyGeoStatistics 2 | 3 | ![status](https://img.shields.io/badge/status-alpha-green.svg) 4 | [![Documentation Status](https://readthedocs.org/projects/pygeostatistics/badge/?version=latest)](http://pygeostatistics.readthedocs.io/en/latest/?badge=latest) 5 | 6 | A collection of python routines (accelerated with [Numba](https://github.com/numba/numba)) 7 | and jupyter notebooks for geostatistics, 8 | which is immensely inspired by gslib (in Fortran). 9 | 10 | # Usage 11 | 12 | Every routine reads its parameters from a parameter file written in `json`. 13 | All parameters including input/output file path need to be specified in these parameter 14 | files. 15 | 16 | I've created scripts that assist in creating parameter files, they could be 17 | found in `\parameters` folder. 18 | 19 | I tried to adhere to the naming convention of `gslib` when it comes to parameter 20 | names. 21 | 22 | Markdown files describing parameters needed for each routine are in 23 | `\gslib_help`. 24 | 25 | ## Example: 26 | 27 | ```Python 28 | from pygeostatistics import Sgsim 29 | 30 | sgsimulator = Sgsim("testData/test_sgsim.par") 31 | sgsimulator.simulate() 32 | ``` 33 | 34 | # Routines 35 | 36 | - `eda.py`: exploratory data anaylysis. 37 | 38 | - [`nst.py`](#normal-score-transform-nstpy): apply normal score transform to data. 39 | 40 | - `gam.py`: calculate variogram for regular data. 41 | 42 | - `gamv.py`: calculate variogram for irregular data. 43 | 44 | - `sa.ipynb`: interactive structural analysis. 45 | 46 | - [`krige2d.py`](#2d-kriging-krige2dpy): kriging 2d data. 47 | 48 | - Simple Kriging 49 | - Ordinary Kriging 50 | 51 | - [`krige3d.py`](#3d-kriging-krige3dpy): kriging 3d data. 52 | 53 | - Simple Kriging 54 | - Ordinary Kriging 55 | - Universal Kriging (Kriging with a Trend) 56 | - Kriging the Trend 57 | - Kriging with External drift 58 | - SK with non-stationary drift 59 | 60 | - [`sgsim.py`](#sequential-gaussian-simulation-sgsimpy): Sequential Gaussian Simulation. 61 | 62 | # Other Utilities 63 | 64 | - `super_block.py`: Class for performing super block search used in kriging. 65 | - used in `krige3d.py` 66 | - used in `sgsim.py` 67 | 68 | - `normal_score_transform.py`: Class for NST used in Gaussian Simulation. 69 | - used in `sgsim.py` 70 | 71 | # Documentation 72 | 73 | For full documentation, including installation, tutorials and PDF documents, please see http://pygeostatistics.readthedocs.io/. 74 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = pyGeoStatistics 8 | SOURCEDIR = . 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 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/basics/cokriging.rst: -------------------------------------------------------------------------------- 1 | CoKriging 2 | ========= 3 | 4 | Intro 5 | ----- 6 | 7 | The term kriging is traditionally reserved for liear regression using data on 8 | the same attribute as that being estimated. For example, an unsampled porosity 9 | value :math:`z(u)` is estimated from neighboring porosity sample values defined on 10 | the same volume support. 11 | 12 | The term cokriging is reserved for linear regression that also uses data defined 13 | on different attributes. For example, the porosity values :math:`z(u)` may be estimated 14 | from combination of porosity samples and related acoustic data values. 15 | 16 | In the case of a single secondary variable (:math:`Y`), the ordinary cokriging 17 | estimator of :math:`Z(\mathbf{u})` is written: 18 | 19 | .. math:: 20 | Z_{COK}^{*}(\mathbf{u}) 21 | =\sum_{{\alpha}_{1}=1}^{{n}_{1}}{{\lambda}_{{\alpha}_{1}}(\mathbf{u})Z({\mathbf{u}}_{{\alpha}_{1}})} 22 | +\sum_{{\alpha}_{2}=1}^{{n}_{2}}{{\lambda}_{{\alpha}_{2}}^{'}(\mathbf{u})Y({\mathbf{u}}_{{\alpha}_{2}}^{'})} 23 | 24 | where the :math:`{\lambda}_{{\alpha}_{1}}` are the weights applied to the :math:`{n}_{1}` 25 | :math:`z` samples and the :math:`{\lambda}_{{\alpha}_{2}}^{'}` are the weights applied to 26 | the :math:`n_2` `y` samples. 27 | 28 | Kriging requires a model for the :math:`Z` covariance. Cokriging requires a joint 29 | model for the matrix of covariance functions including the :math:`Z` covariance 30 | :math:`C_{Z}(\mathbf{h})`, the :math:`Y` covariance :math:`C_{Y}(\mathbf{h})`, the cross :math:`Z-Y` 31 | covariance :math:`C_{ZY}(\mathbf{h})=Cov\{Z(\mathbf{u}),Y(\mathbf{u+h})\}`, and the 32 | cross :math:`Y-Z` covariance :math:`C_{YZ}(\mathbf{h})` 33 | 34 | The covariance matrix requires :math:`K^2` covariance functions when :math:`K` different 35 | variables are considered in a cokriging exercise. The inference becomes 36 | extremely demanding in terms of data and the subsequent joint modeling is 37 | particularly tedious. This is the main reason why cokriging has not been 38 | extensively used in practice. Algorithms such as kriging with an external 39 | drift and collocated cokriging have been developed to shortcut the tedious 40 | inference and modeling process required by cokriging. 41 | 42 | Ordinary Cokriging 43 | ------------------ 44 | 45 | The sum of the weights applied to the primary variable is set to one, and 46 | the sum of the weigths applied to any other variable is set to zero. In the 47 | case of two variables, these two conditions are: 48 | 49 | .. math:: 50 | \begin{cases} 51 | \sum\limits_{{\alpha}_{1}}^{}{{\lambda}_{{\alpha}_{1}}(\mathbf{u})}=1\\ 52 | \sum\limits_{{\alpha}_{2}}^{}{{\lambda}_{{\alpha}_{2}}(\mathbf{u})}=0 53 | \end{cases} 54 | 55 | The problem with this traditional formalism is that the second condition tends 56 | to limit severely the influence of the secondary variables. 57 | 58 | Standardized Ordinary Cokriging 59 | ------------------------------- 60 | 61 | Often, a better approach consists of creating new secondary variables with the 62 | same mean as the primary variable. Then all the weights are constrained to 63 | sum to one. 64 | 65 | In the case of two variables, the expression could be written as: 66 | 67 | .. math:: 68 | Z_{COK}^{*}(\mathbf{u}) 69 | =\sum_{{\alpha}_{1}=1}^{{n}_{1}}{{\lambda}_{{\alpha}_{1}}(\mathbf{u})Z({\mathbf{u}}_{{\alpha}_{1}})} 70 | +\sum_{{\alpha}_{2}=1}^{{n}_{2}}{{\lambda}_{{\alpha}_{2}}^{'}(\mathbf{u})[Y({\mathbf{u}}_{{\alpha}_{2}}^{'})+{m}_{Z}-{m}_{Y}]} 71 | 72 | 73 | with a single condition: 74 | 75 | .. math:: 76 | \sum_{{\alpha}_{1}=1}^{{n}_{1}}{{\lambda}_{{\alpha}_{1}}}(\mathbf{u})+\sum_{{\alpha}_{2}=1}^{{n}_{2}}{{\lambda}_{{\alpha}_{2}}}(\mathbf{u})=1 77 | 78 | where :math:`m_Z=E\{Z(u)\}` and :math:`m_Y=E\{Y(u)\}` are stationary means 79 | of :math:`Z` and :math:`Y`. 80 | 81 | Simple Cokriging 82 | ---------------- 83 | 84 | There is no constraint on the weights. Just like simple kriging, this version 85 | of cokriging requires working on data residuals or equivalently, on variables 86 | whose means have all been standardized to zero. This is the case when applying 87 | simple cokriging in an MG approach (the normal score transforms of each variable 88 | have a stationary mean of zero). 89 | 90 | Collocated Cokriging 91 | -------------------- 92 | 93 | A reduced form of cokriging consists of retaining only the collocated 94 | variable :math:`y(\mathbf{u})`, provided that it is availible at all locations 95 | :math:`\mathbf{u}` being estimated. The cokriging estimator is written as: 96 | 97 | .. math:: 98 | Z_{COK}^{*}(\mathbf{u}) 99 | =\sum_{{\alpha}_{1}=1}^{{n}_{1}}{{\lambda}_{{\alpha}_{1}}(\mathbf{u})Z({\mathbf{u}}_{{\alpha}_{1}})} 100 | +{\lambda}^{'}(\mathbf{u})Y(\mathbf{u}) 101 | 102 | The corresponding cokriging system requires knowledge of only the :math:`Z` 103 | covariance :math:`C_{Z}(\mathbf{h})` and the :math:`Z-Y` cross-covariance 104 | :math:`C_{ZY}(\mathbf{h})`. The latter can be approximated through the following model: 105 | 106 | .. math:: 107 | C_{ZY}(\mathbf{h})=B\cdot C_{Z}(\mathbf{h}),\quad\forall \mathbf{h} 108 | 109 | where :math:`B=\sqrt{C_Y(0)/C_Z(0)}\cdot{\rho}_{ZY}(0)`, :math:`C_Z(0)`, :math:`C_Y(0)` are 110 | the variances of Z and Y, and :math:`{\rho}_{ZY}(0)` is the linear coefficient 111 | of correlation of collocated z-y data. 112 | -------------------------------------------------------------------------------- /docs/basics/kriging.rst: -------------------------------------------------------------------------------- 1 | Kriging 2 | ======= 3 | 4 | Intro 5 | ----- 6 | 7 | Kriging is **"a collection of gneralized linear regression techniques for 8 | minimizing an estimation variance defined from a priori model for a convariance".** 9 | 10 | Consider the estimate of an unsampled value :math:`z(\mathbf{u})` from 11 | neighboring data values :math:`z({\mathbf{u}}_{\alpha}),\alpha=1,\dots,n`. 12 | The RF model :math:`Z(\mathbf{u})` is stationary with mean :math:`m` and 13 | covariance :math:`C(\mathbf{h})`. In its simplest form, also known as 14 | **Simple Kriging (SK)**, the algorithm considers the following linear estimator: 15 | 16 | .. math:: 17 | {Z}_{SK}^{*}(\mathbf{u})=\sum_{\alpha=1}^{n}{\lambda}_{\alpha}(\mathbf{u}) 18 | Z({\mathbf{u}}_{\alpha})+\left(1-\sum_{\alpha=1}^{n}{\lambda}_{\alpha} 19 | (\mathbf{u})\right)m 20 | 21 | The weights :math:`{\lambda}_{\alpha}(\mathbf{u})` are determined to minnimize 22 | the error variance, also called the "estimation variance." That minimization 23 | results in a set of normal equations: 24 | 25 | .. math:: 26 | \sum_{\beta=1}^{n}{\lambda}_{\beta}(\mathbf{u})C({\mathbf{u}}_{\beta}-{\mathbf{u}}_{\alpha})=C(\mathbf{u}-{\mathbf{u}}_{\alpha})\\ 27 | \forall{\alpha}=1,\dots,n 28 | 29 | The corresponding minimized estimation variance, or kriging variance, is: 30 | 31 | .. math:: 32 | {\sigma}_{SK}^{2}(\mathbf{u})=C(0)-\sum_{\alpha=1}^{n}{\lambda}_{\alpha}(\mathbf{u})C(\mathbf{u}-{\mathbf{u}}_{\alpha})\geq 0 33 | 34 | **Ordinary Kriging (OK)** is the most commonly used variant of the previous 35 | simple kriging algorithm, whereby the sum of the weights 36 | :math:`\sum_{\alpha=1}^{n}{\lambda}_{\alpha}(\mathbf{u})` is considered to equal 1. 37 | This allows building an estimator :math:`Z_{OK}^{*}(\mathbf{u})` that does not 38 | require prior knowledge of the stationary mean m, yet remains unbiased in the 39 | sense that :math:`E\{{Z}_{OK}^{*}(\mathbf{u})\}=E\{Z(\mathbf{u})\}`. 40 | 41 | **None-linear kriging** is but linear kriging performed on some non-linear 42 | transform of the z-data, e.g., the **log-transform** 43 | :math:`\mathrm{ln}z` privided that :math:`z>0`, or the **indicator transform** 44 | as defined in relation: 45 | 46 | .. math:: 47 | I(\mathbf{u};z)=\begin{cases} 48 | 1,\quad Z(\mathbf{u})\leq z\\ 49 | 0,\quad \text{otherwise} 50 | \end{cases} 51 | 52 | Traditionally, kriging (**SK** or **OK**) has been performed to provide 53 | a "best" linear unbiased estimate (**BLUE**) for unsampled values 54 | :math:`z(\mathbf{u})`, with the kriging variance being used to define Gaussian-type 55 | confidence intervals, e.g., 56 | 57 | .. math:: 58 | \text{Prob}\{Z(\mathbf{u})\in [{z}_{SK}^{*}(\mathbf{u})\pm 2{\sigma}_{SK}(\mathbf{u})]\cong 0.95\} 59 | 60 | **Unfortunately, kriging variances of the type, being independent of the data** 61 | **values, only provides a comparison of alternative geometric data configurations.** 62 | **Kriging variances are usually not measures of local estimation accuracy.** 63 | 64 | The kriging algorithm has two characteristic properties that allow its 65 | use in determining **posterior ccdfs**. These two characteristic properties 66 | are the basis for, respectively, the **multi-Gaussian (MG) approach** and 67 | the **indicator kriging (IK) approach** to determination of ccdfs: 68 | 69 | 1. The Multi-Gaussian Approach: If the RF model :math:`Z(\mathbf{u})` is 70 | multivariate Gaussian, then the simple kriging estimate and variance identify 71 | the mean and variance of the posterior ccdf. In addition, since that ccdf is 72 | Gaussian, it is fully determined by these two parameters. This remarkable 73 | result is at the basis of multi-Gaussian (MG) kriging and simulation. 74 | The MG approach is said to be **parametric** in the sense that it determines 75 | the ccdfs through their parameters (mean and variance). The MG algorithm is 76 | remarkably fast and trouble-free; its limitation is the reliance on the very 77 | specific and sometimes inappropriate properties fo the Gaussian RF model. 78 | 79 | 2. The Indicator Kriging Approach: If the value to be estimated is the expected 80 | value (mean) of a distribution, then least-squares (LS) regression 81 | (i.e., kriging) is a priori the prefered algorithm. The reason is that 82 | the LS estimator fo the variable :math:`Z(\mathbf{u})` is also the LS estimator of 83 | its conditional expectation :math:`E\{Z(\mathbf{u})\mid(n)\}`, that is, of the 84 | expected value of the ccdf. Instead of the variable :math:`Z(\mathbf{u})`, 85 | consider its binary indicator transform :math:`I(\mathbf{u};z)`. 86 | Kriging of the indicator RV :math:`I(\mathbf{u};z)` provides an estimate that 87 | is also the best LS estimate fo the conditional expectation of :math:`I(\mathbf{u};z)`. 88 | Now, the conditional expecation of :math:`I(\mathbf{u};z)` 89 | is equal to the ccdf of :math:`Z(\mathbf{u})`, indeed: 90 | 91 | .. math:: 92 | \begin{array}{l} 93 | E\{I(\mathbf{u};z)\mid (n)\}&= 94 | \begin{array}{l} 95 | 1\cdot \text{Prob}\{I(\mathbf{u};z)=1\mid (n)\}\\ 96 | +0\cdot \text{Prob}\{I(\mathbf{u};z)=0\mid (n)\} 97 | \end{array}\\ 98 | &=1\cdot \text{Prob}\{Z(\mathbf{u})\leq z\mid (n)\}\\\ 99 | &\equiv F(\mathbf{u};z\mid (n)) 100 | \end{array} 101 | 102 | Thus the kriging algorithm applied to indicator data provides LS estimates 103 | of the ccdf. Note that indicator kriging (IK) is not aimed at estimating the 104 | unsampled value :math:`z(\mathbf{u})` or its indicator transform 105 | :math:`I(\mathbf{u};z)` but at providing a ccdf model of uncertainty about 106 | :math:`z(\mathbf{u})`. The IK algorithm is said to be **non-parametric** 107 | in the sense that is does not approach the ccdf through its parameters 108 | (mean and variance); rather, the ccdf values for various threshold values 109 | :math:`z` are estimated directly. 110 | 111 | Types of Kriging 112 | ---------------- 113 | 114 | +-----------------------------------+---------+--------------------------+--------------+ 115 | | Kriging Form | Mean | Drift Model | Prerequisite | 116 | +===================================+=========+==========================+==============+ 117 | | Simple Kriging (SK) | Known | None | Covariance | 118 | +-----------------------------------+---------+--------------------------+--------------+ 119 | | Ordinary Kriging (OK) | Unknown | Constant | Variogram | 120 | +-----------------------------------+---------+--------------------------+--------------+ 121 | | Universal Kriging (UK) | Unknown | Functions of coordinates | Variogram | 122 | +-----------------------------------+---------+--------------------------+--------------+ 123 | | Kriging with external drift (KED) | Unknown | External variable | Variogram | 124 | +-----------------------------------+---------+--------------------------+--------------+ 125 | 126 | 127 | Simple Kriging 128 | -------------- 129 | 130 | In its simplist form, also known as simple kriging (SK), the algorithm considers 131 | the following linear estimator: 132 | 133 | .. math:: 134 | Z_{SK}^{*}(\mathbf{u}) = \sum_{\alpha=1}^{n} \lambda_{\alpha}(\mathbf{u}) Z(\mathbf{u_{\alpha}}) + \left(1-\sum_{\alpha=1}^{n}\lambda_{\alpha}(\mathbf{u})\right) m 135 | 136 | The weights :math:`\lambda_{\alpha}` are determined to minimize the error 137 | variance, also called the "estimation vairiance." That minimization result 138 | in a set of normal equations known as *Simple Kriging System*: 139 | 140 | .. math:: 141 | \sum_{\beta=1}^{n} \lambda_{\beta}(\mathbf{u}) C(\mathbf{u_{\beta}}-\mathbf{u_{\alpha}})=C(\mathbf{u}-\mathbf{{u}_{\alpha}}),\\\forall \alpha=1, ... , n 142 | 143 | In matrix notation, we have 144 | 145 | .. math:: 146 | \boldsymbol{\Sigma}\boldsymbol{\lambda}=\boldsymbol{\sigma_{0}} 147 | 148 | where :math:`\boldsymbol{\Sigma}=[{\sigma}_{\alpha\beta}]` is the :math:`N\times N` 149 | matrix of data-to-data covariances, :math:`\boldsymbol{\sigma_{0}}=[{\sigma}_{\alpha0}]` 150 | is the N-vector of covariances between the data and the target, and :math:`\boldsymbol{\lambda}=[\lambda_\alpha]` 151 | is the N-vector of solutions. 152 | 153 | The corresponding minimized estimation variance, or kirging variance, is: 154 | 155 | .. math:: 156 | \sigma_{SK}^{2}(\mathbf{u}) = C(0) - \sum_{\lambda=1}^{n}\lambda_{\alpha}(\mathbf{u}) C(\mathbf{u}-\mathbf{u_{\alpha}}) \geq 0 157 | 158 | 159 | Ordinary Kriging (OK) 160 | --------------------- 161 | 162 | Ordinary Kriging (OK) filters the mean from the SK estimator by requiring that 163 | the kriging weights sum to one. This results in the following ordinary kriging 164 | estimator: 165 | 166 | .. math:: 167 | {Z}_{OK}^{*}(\mathbf{u})=\sum_{\alpha=1}^{n}{{\lambda}_{\alpha}^{(OK)}(\mathbf{u})Z({\mathbf{u}}_{\alpha})} 168 | 169 | and the sationary OK system: 170 | 171 | .. math:: 172 | \begin{cases} 173 | \sum_{\beta=1}^{n}{{\lambda}_{\beta}^{(OK)}(\mathbf{u}) C({\mathbf{u}}_{\beta}-{\mathbf{u}}_{\alpha})}+\mu(\mathbf{u})=C(\mathbf{u}-{\mathbf{u}}_{\alpha}),\quad \alpha=1,\dots,n \\ 174 | \sum_{\beta=1}^{n}{{\lambda}_{\beta}^{(OK)}(\mathbf{u})}=1\\ 175 | \end{cases} 176 | 177 | In matrix notation, the above linear equations correspond to: 178 | 179 | .. math:: 180 | \begin{bmatrix}{C}_{11} & {C}_{12} & \cdots & {C}_{1N} & 1 \\{C}_{21} & {C}_{22} & \cdots & {C}_{2N} & 1 \\ \vdots & \vdots & \cdots & \vdots & 1 \\ {C}_{N1} & {C}_{N2} & \cdots & {C}_{NN} & 1 \\ 1 & 1 & 1 & 1 & 0\end{bmatrix} \times \begin{bmatrix}{\lambda}_{1}\\{\lambda}_{2}\\ \vdots \\{\lambda}_{N}\\ \mu \end{bmatrix} 181 | = \begin{bmatrix}{C}_{10}\\{C}_{20}\\ \vdots \\{C}_{N0}\\ 1 \end{bmatrix} 182 | 183 | 184 | The kriging variance is obtained by multiplying the first N equations of the 185 | kriging system by :math:`\lambda_\alpha`, summing over :math:`\alpha`, and 186 | then using the last equations. The result is the OK variance: 187 | 188 | .. math:: 189 | {\sigma}_{OK}^{2}=E{({Z}^{*}-{Z}_{0})}^{2}={\sigma}_{00}-\sum\limits_{\alpha}{{\lambda}_{\alpha}{\sigma}_{\alpha0}}-\mu 190 | 191 | The linear system has a unique solution if and only if the covarance matrix 192 | :math:`\boldsymbol{\Sigma}[{\sigma}_{\alpha\beta}]` is strictly positive 193 | definite, which is the case if we use strictly positive definite covariance 194 | function model and if all data are distinct. 195 | 196 | Universal Kriging (UK) or Kriging with a Trend Model (KT) 197 | --------------------------------------------------------- 198 | 199 | The general model, which Matheron(1969) named the *universal kriging* model 200 | for reasons explained below, assumes that the mean function can be represented 201 | as a reponse surface function 202 | 203 | .. math:: 204 | m(x)=\sum\limits_{\mathscr{l}=0}^{L}{{a}_{\mathscr{l}}{f}^{\mathscr{l}}(x)} 205 | 206 | where the :math:`{f}^{\mathscr{l}}(x)` are kown basis functions and :math:`{a}_{\mathscr{l}}` 207 | are fixed but unknown coefficients. Usually the first basis function 208 | (case :math:`\mathscr{l}=0`) is the constant function identically equal to 1, 209 | which guarantees that the constant-mean case is included in the model. 210 | The other functions are typically monomials of low degree in the cooridinates 211 | of x (in practice, the degree does not exceed two). In the case of monomials, 212 | the superscript :math:`\mathscr{l}`, which is an index, has the meaning of a 213 | power (in 1D, :math:`{f}^{\mathscr{l}}(x)={x}^{\mathscr{l}}`). Note that the 214 | above function may be regarded as a local approximation to :math:`m(x)`; that 215 | is, the coefficients :math:`{a}_{\mathscr{l}}` may vary in space but sufficiently 216 | slowly to be considered constant within estimation neighborhoods. 217 | 218 | The universal kriging model is the decomposition of the variable :math:`Z(x)` 219 | into the sum: 220 | 221 | .. math:: 222 | Z(x)=m(x)+Y(x) 223 | 224 | of a smooth deterministic function :math:`m(x)`, describing the systematic aspect of 225 | the phenomenon, and called the drift, and a zero-mean random function :math:`Y(x)`, 226 | called the residual and capturing its erratic fluctuations. Note that the drift 227 | refers to a technically precise notion (the mean of the RF :math:`Z`), 228 | whereas *trend* is a generic term designating a general tendency, a systematic 229 | effect (besides, "trend" may imply an underlying driving force). 230 | 231 | In order to minimize :math:`E{({Z}^{*}-{Z}_{0})}^{2}:math:`, we have to make 232 | :math:`{[E({Z}^{*}-{Z}_{0})]}^{2}` zero whatever the unknown coefficients 233 | :math:`{a}_{\mathscr{l}}`, which implies annihilating their factors in the above. 234 | This leads to the set of L+1 conditions: 235 | 236 | .. math:: 237 | \sum\limits_{\alpha}{\lambda}_{\alpha}{f}_{\alpha}^{\mathscr{l}}={f}_{0}^{\mathscr{l}}, \quad \mathscr{l}=0,1,\dots,L 238 | 239 | that Matheron(1969) called universality conditions, hence the name universal 240 | kriging (UK). They express that the estimator :math:`{Z}^{*}` is unbiased for 241 | all values of :math:`{\alpha}_{\mathscr{l}}`. 242 | 243 | The Universal Kriging System can be expressed as: 244 | 245 | .. math:: 246 | \begin{cases} 247 | \sum\limits_{\beta}{{\lambda}_{\beta}{\sigma}_{\alpha\beta}}+\sum\limits_{\mathscr{l}}{{\mu}_{\mathscr{l}}{f}_{\alpha}^{\mathscr{l}}}={\sigma}_{\alpha0}, &\quad \alpha=1,\dots,N\\ 248 | \sum\limits_{\alpha}{{\lambda}_{\alpha}{f}_{\alpha}^{\mathscr{l}}}={f}_{0}^{\mathscr{l}}, &\quad \mathscr{l}=0,\dots,L 249 | \end{cases} 250 | 251 | In matrix notation the system is of the form :math:`\mathbf{Aw=b}` with the following structure: 252 | 253 | .. math:: 254 | \begin{bmatrix} 255 | \boldsymbol{\Sigma} & \mathbf{F} \\ 256 | {\mathbf{F}}^{'} & 0 257 | \end{bmatrix} 258 | \begin{bmatrix} 259 | \boldsymbol{\lambda} \\ 260 | \boldsymbol{\mu} 261 | \end{bmatrix} 262 | = 263 | \begin{bmatrix} 264 | {\boldsymbol{\sigma}}_{0}\\ 265 | {\mathbf{f}}_{0} 266 | \end{bmatrix} 267 | 268 | where :math:`\boldsymbol{\Sigma}`, :math:`\boldsymbol{\lambda}` and 269 | :math:`{\boldsymbol{\sigma}}_{0}` are defined as for simple kriging and where 270 | 271 | .. math:: 272 | \mathbf{F}= 273 | \begin{bmatrix} 274 | 1&{f}_{1}^{1}&.&{f}_{1}^{L}\\ 275 | 1&{f}_{1}^{1}&.&{f}_{1}^{L}\\ 276 | .&.&.&.\\ 277 | .&.&.&.\\ 278 | .&.&.&.\\ 279 | 1&{f}_{1}^{1}&.&{f}_{1}^{L} 280 | \end{bmatrix}, \quad 281 | \boldsymbol{\mu}= 282 | \begin{bmatrix} 283 | {\mu}_{0}\\ 284 | {\mu}_{1}\\ 285 | .\\ 286 | .\\ 287 | .\\ 288 | {\mu}_{L} 289 | \end{bmatrix}, \quad 290 | {\mathbf{f}}_{0}= 291 | \begin{bmatrix} 292 | 1 \\ 293 | {f}_{0}^{1} \\ 294 | .\\ 295 | .\\ 296 | .\\ 297 | {f}_{0}^{L} 298 | \end{bmatrix} 299 | 300 | Those :math:`1` s in :math:`\mathbf{F}` correspond to OK. 301 | 302 | 303 | Kriging with an External Drift 304 | ------------------------------ 305 | 306 | Kriging with an external drift variable is an extention of UK. The trend model 307 | is limited to two terms :math:`m(\mathbf{u})={a}_{0}+{a}_{1}{f}_{1}(\mathbf{u})` with 308 | the term :math:`{f}_{1}(\mathbf{u})` set equal to a secondary (external) variable. 309 | The smooth variability of the second variable is deemed related to that of the 310 | primary variable :math:`Z(\mathbf{u})` being estimated. 311 | 312 | Let :math:`y(\mathbf{u})` be the secondary variable; the trend model is then: 313 | 314 | .. math:: 315 | E\{Z(\mathbf{u})\}=m(\mathbf{u})={a}_{0}+{a}_{1}y(\mathbf{u}) 316 | 317 | :math:`y(\mathbf{u})` is assumed to reflect the spacial trends of the :math:`z` variability 318 | up to a linear rescalling of units (corresponding to the two parameters :math:`{a}_{0}` 319 | and :math:`{a}_{1}`) 320 | 321 | The estimate of the :math:`z` variable and the corresponding system of equations are 322 | identical to the UK estimate and system with K=1, and 323 | :math:`{f}_{1}(\mathbf{u})={y}(\mathbf{u})` 324 | 325 | .. math:: 326 | Z_{UK}^{*}(\mathbf{u})=\sum_{\alpha=1}^{n}{{\lambda}_{\alpha}^{UK}(\mathbf{u})Z({\mathbf{u}}_{\alpha})} 327 | 328 | .. math:: 329 | \begin{cases} 330 | \sum_{\beta=1}^{n}{{\lambda}_{\beta}^{UK}(\mathbf{u})C({\mathbf{u}}_{\alpha}-{\mathbf{u}}_{\alpha})} + {\mu}_{0}(\mathbf{u}) + {\mu}_{a}(\mathbf{u})y({\mathbf{u}}_{\alpha}) = C(\mathbf{u}-{\mathbf{u}}_{\alpha}) &\alpha=1,\dots,n\\ 331 | \sum_{\beta=1}^{n}{{\lambda}_{\beta}^{UK}}=1\\ 332 | \sum_{\beta=1}^{n}{{\lambda}_{\beta}^{UK}y({\mathbf{u}}_{\beta})}=y(\mathbf{u}) 333 | \end{cases} 334 | 335 | The fundamental (hypothesis) relation must make physical sense. 336 | 337 | Two conditions must be met before applying the external drift algorithm: 338 | (1) The external variable must vary smoothly in space, otherwise the resulting 339 | UK system may be unstable; and (2) the external variable must be known at all 340 | locations :math:`{\mathbf{u}}_{\alpha}` of the primary data values and at all locations 341 | :math:`\mathbf{u}` to be estimated. 342 | 343 | Block Kriging 344 | ------------- 345 | 346 | The linearity of the kriging algorithm allows direct estimation of *linear* 347 | averages of the attributes :math:`z(\mathbf{u})`. For example, consider the 348 | estimation of the block average defined as: 349 | 350 | .. math:: 351 | z_{V}(\mathbf{u})=\frac{1}{|V|}\int_{V(\mathbf{u})}{z({\mathbf{u}}')d{\mathbf{u}}'}\approx \frac{1}{N}\sum_{j=1}^{N}{z({\mathbf{u}}_{j}^{'})} 352 | 353 | where :math:`V(\mathbf{u})` is a block of measure :math:`|V|` centered at u, and the 354 | :math:`{\mathbf{u}}_{j}^{'}` are N points discretizing the volume :math:`V(\mathbf{u})`. 355 | 356 | Doing point kriging or block krging only affect the right handside of the 357 | kriging system. Each element in the right hand side matrix is the average 358 | covariance between the sample point and all points in the target block instead 359 | of just the covariance between the sample point and the target point. -------------------------------------------------------------------------------- /docs/basics/simulation.rst: -------------------------------------------------------------------------------- 1 | Simulation 2 | ========== 3 | 4 | Simulation differs from kriging or any interpolation algorithm, in two major aspects: 5 | 6 | 1. In most interpolation algorithms, including kriging, the goal is to provide 7 | a "best", hence unique, local estimate of the variable or any of its trend 8 | components without specific regard to the resulting spatial statistics of the 9 | estimates taken together. **In simulation, reproduction of global features 10 | (texture) and statistics (histogram, covariance) take precedence over local 11 | accuracy.** Kriging provides a set of local representations, say 12 | :math:`z^{*}(\mathbf{u}),\mathbf{u}\in A`, where local accuracy prevails. 13 | Simulation provides alternative global representations, :math:`z^{(l)}(u),u\in A`, 14 | where reproduction of patterns of spatial continuity prevails. 15 | 16 | 2. Except if a Gaussian model for errors is assumed, kriging provides only an 17 | incomplete measure of local accuracy, and no appreciation of joint accuracy 18 | when several locations are considered together. 19 | **Simulations are designed specifically to provide such measures of accuracy, both local and involving several locations.** 20 | These measures are given by the differences between :math:`L` alternative 21 | simulated vlaues at any location (local accuracy) or the :math:`L` alternative 22 | simulated fields (global or joint accuracy). 23 | 24 | Different simulation algorithms impart different global statistics and spatial 25 | features on each realization, For example, simulated categorical values can be 26 | made to honor specific geometrical patterns as in *object-based simulation* or 27 | the covariance of simulated continuous values can be made to honor a prior 28 | covariance model as for *Gaussian-related simulations*. A hybrid approach 29 | could be considered to generate numerical models that reflect widely different 30 | types of features. For example, one may start with an object-based process or 31 | categorical *indicator simulation* to generate the geometric architecture of 32 | the various lithofacies, following with a Gaussian algorithm to simulate the 33 | distribution of continuous petrophysical properties within each sperate lithofacies, 34 | then a simulated annealing process could be used to modify locally the petrophysical 35 | properties to match, say, well test data. -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import sys 5 | sys.path.insert(0, os.path.abspath('..')) 6 | # -- General configuration ------------------------------------------------ 7 | 8 | # If your documentation needs a minimal Sphinx version, state it here. 9 | # 10 | # needs_sphinx = '1.0' 11 | 12 | # Add any Sphinx extension module names here, as strings. They can be 13 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 14 | # ones. 15 | extensions = [ 16 | 'sphinx.ext.autodoc', 17 | 'sphinx.ext.doctest', 18 | # 'sphinx.ext.todo', 19 | 'sphinx.ext.intersphinx', 20 | 'sphinx.ext.coverage', 21 | 'sphinx.ext.mathjax', 22 | 'sphinx.ext.ifconfig', 23 | 'sphinx.ext.viewcode', 24 | 'sphinx.ext.napoleon', 25 | 'sphinx.ext.autosummary', 26 | 'nbsphinx'] 27 | 28 | # numpydoc_class_members_toctree = False 29 | 30 | napoleon_use_param = False 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix(es) of source filenames. 35 | # You can specify multiple suffix as a list of string: 36 | # 37 | # from recommonmark.parser import CommonMarkParser 38 | 39 | # source_parsers = { 40 | # '.md': CommonMarkParser, 41 | # } 42 | 43 | # source_suffix = ['.rst', '.md'] 44 | # source_suffix = '.rst' 45 | 46 | # nbsphinx_prompt_width = 0 47 | nbsphinx_prolog = """ 48 | .. raw:: html 49 | 55 | """ 56 | 57 | 58 | # The master toctree document. 59 | master_doc = 'index' 60 | 61 | # General information about the project. 62 | project = 'pyGeoStatistics' 63 | copyright = '2018, Yu Hao' 64 | author = 'Yu Hao' 65 | 66 | # The version info for the project you're documenting, acts as replacement for 67 | # |version| and |release|, also used in various other places throughout the 68 | # built documents. 69 | # 70 | # The short X.Y version. 71 | version = '0.1' 72 | # The full version, including alpha/beta/rc tags. 73 | release = '0.1.0' 74 | 75 | # The language for content autogenerated by Sphinx. Refer to documentation 76 | # for a list of supported languages. 77 | # 78 | # This is also used if you do content translation via gettext catalogs. 79 | # Usually you set "language" from the command line for these cases. 80 | language = None 81 | 82 | # List of patterns, relative to source directory, that match files and 83 | # directories to ignore when looking for source files. 84 | # This patterns also effect to html_static_path and html_extra_path 85 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 86 | '*/.ipynb_checkpoints'] 87 | 88 | # The name of the Pygments (syntax highlighting) style to use. 89 | pygments_style = 'sphinx' 90 | 91 | # If true, `todo` and `todoList` produce output, else they produce nothing. 92 | todo_include_todos = False 93 | 94 | 95 | # -- Options for HTML output ---------------------------------------------- 96 | 97 | # The theme to use for HTML and HTML Help pages. See the documentation for 98 | # a list of builtin themes. 99 | import sphinx_bootstrap_theme 100 | html_theme = 'bootstrap' 101 | html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() 102 | 103 | # Custom sidebar templates, must be a dictionary that maps document names 104 | # to template names. 105 | # 106 | # Theme options are theme-specific and customize the look and feel of a theme 107 | # further. For a list of options available for each theme, see the 108 | # documentation. 109 | # 110 | html_theme_options = dict( 111 | bootstrap_version="3", 112 | bootswatch_theme="simplex", 113 | navbar_sidebarrel=False, 114 | source_link_position="footer", 115 | globaltoc_depth=2, 116 | navbar_links=[ 117 | # ("Cookbook", "cookbook/index"), 118 | # ("API", "api/api") 119 | # ("Basics", "basics/index") 120 | ] 121 | ) 122 | 123 | # Add any paths that contain custom static files (such as style sheets) here, 124 | # relative to this directory. They are copied after the builtin static files, 125 | # so a file named "default.css" will overwrite the builtin "default.css". 126 | html_static_path = ['_static'] 127 | 128 | # -- Options for HTMLHelp output ------------------------------------------ 129 | 130 | # Output file base name for HTML help builder. 131 | htmlhelp_basename = 'pyGeoSatisticsdoc' 132 | 133 | 134 | # -- Options for LaTeX output --------------------------------------------- 135 | 136 | latex_elements = { 137 | # The paper size ('letterpaper' or 'a4paper'). 138 | # 139 | # 'papersize': 'letterpaper', 140 | 141 | # The font size ('10pt', '11pt' or '12pt'). 142 | # 143 | # 'pointsize': '10pt', 144 | 145 | # Additional stuff for the LaTeX preamble. 146 | # 147 | # 'preamble': '', 148 | 149 | # Latex figure (float) alignment 150 | # 151 | # 'figure_align': 'htbp', 152 | } 153 | 154 | # Grouping the document tree into LaTeX files. List of tuples 155 | # (source start file, target name, title, 156 | # author, documentclass [howto, manual, or own class]). 157 | latex_documents = [ 158 | (master_doc, 'pyGeoStatistics.tex', 'pyGeoStatistics Documentation', 159 | 'Yu Hao', 'manual'), 160 | ] 161 | 162 | 163 | # -- Options for manual page output --------------------------------------- 164 | 165 | # One entry per manual page. List of tuples 166 | # (source start file, name, description, authors, manual section). 167 | man_pages = [ 168 | (master_doc, 'pyGeoStatistics', 'pyGeoStatistics Documentation', 169 | [author], 1) 170 | ] 171 | 172 | 173 | # -- Options for Texinfo output ------------------------------------------- 174 | 175 | # Grouping the document tree into Texinfo files. List of tuples 176 | # (source start file, target name, title, author, 177 | # dir menu entry, description, category) 178 | texinfo_documents = [ 179 | (master_doc, 'pyGeoStatistics', 'pyGeoStatistics Documentation', 180 | author, 'pyGeoStatistics', 'One line description of project.', 181 | 'Miscellaneous'), 182 | ] 183 | 184 | 185 | 186 | 187 | # Example configuration for intersphinx: refer to the Python standard library. 188 | intersphinx_mapping = {'https://docs.python.org/': None} 189 | -------------------------------------------------------------------------------- /docs/doc_env.yml: -------------------------------------------------------------------------------- 1 | # doc_env.yml 2 | # Configuration file for creating a Conda Environment with dependencies needed for pyRSD. 3 | # Create the environment by running the following command (after installing Miniconda): 4 | # $ conda env create --file doc_env.yml 5 | 6 | name: doc_env 7 | 8 | channels: 9 | - defaults 10 | 11 | dependencies: 12 | - python=3.6 13 | - numpy 14 | - scipy 15 | - pandas 16 | - matplotlib 17 | - IPython 18 | - pip 19 | - pip: 20 | - nbsphinx 21 | - sphinx_bootstrap_theme 22 | - sphinx-issues 23 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. Pore Pressure Prediction documentation master file, created by 2 | sphinx-quickstart on Thu Oct 26 10:58:51 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | =============== 7 | pyGeoStatistics 8 | =============== 9 | 10 | Overview 11 | ======== 12 | 13 | A collection of python routines (accelerated with Numba) and jupyter notebooks 14 | for geostatistics, which is immensely inspired by gslib (in Fortran). 15 | 16 | 17 | Usage 18 | ===== 19 | Every routine reads its parameters from a parameter file written in json. All parameters including input/output file path need to be specified in these parameter files. 20 | 21 | I've created scripts that assist in creating parameter files, they could be found in \parameters folder. 22 | 23 | I tried to adhere to the naming convention of gslib when it comes to parameter names. 24 | 25 | Markdown files describing parameters needed for each routine are in \gslib_help. 26 | 27 | Example: 28 | ======== 29 | :: 30 | 31 | from pygeostatistics import Sgsim 32 | 33 | sgsimulator = Sgsim("testData/test_sgsim.par") 34 | sgsimulator.simulate() 35 | 36 | Contribute 37 | ========== 38 | - Issue Tracker: https://github.com/whimian/pyGeoStatistics/issues 39 | 40 | - Source Code: https://github.com/whimian/pyGeoStatistics 41 | 42 | License 43 | ======= 44 | `MIT `_ 45 | 46 | .. toctree:: 47 | :maxdepth: 1 48 | :caption: Getting Started 49 | :hidden: 50 | 51 | install 52 | 53 | .. toctree:: 54 | :maxdepth: 1 55 | :caption: Geostatistics Basics 56 | :hidden: 57 | 58 | basics/kriging 59 | basics/cokriging 60 | basics/simulation 61 | 62 | .. toctree:: 63 | :maxdepth: 1 64 | :caption: Tutorials 65 | :hidden: 66 | 67 | tutorials/EDA 68 | tutorials/NST 69 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | | 6 | 7 | Denpendencies 8 | ============= 9 | 10 | - Python 3.6 11 | - NumPy 1.8 (or greater) 12 | - Numba 13 | - SciPy 0.13 (or greater) 14 | - matplotlib 1.3 (or greater) 15 | 16 | *Optional:* 17 | 18 | * IPython 19 | * Jupyter Notebook 20 | 21 | Installing Python 22 | ================= 23 | The recommended way to intall Python and the dependencies of this package is 24 | using conda package manager from Anaconda Inc. You may download and install 25 | Miniconda from https://conda.io/miniconda which contains both Python and 26 | conda package manager. 27 | 28 | Installing pyGeoStatistics 29 | ========================== 30 | First, download the source code or clone from our Github repository. 31 | 32 | pyGeoStatistics is recommended to be installed in a seperate python environment 33 | which can be easily created with conda. For example, with requirements.yml file 34 | the following command will create an environment named pyGeoStatistics_env with 35 | all of our denpendencies installed. 36 | 37 | .. code:: bash 38 | 39 | conda update conda 40 | conda env create --file environments.yml 41 | 42 | Then, run the following command to install pyGeoStatistics. 43 | 44 | .. code:: bash 45 | 46 | python setup.py install 47 | -------------------------------------------------------------------------------- /gslib_help/cokb3d_help.md: -------------------------------------------------------------------------------- 1 | # Description: 2 | 3 | A cokriging program for a points or blocks on a regular grid. 4 | 5 | # Parameters: 6 | 7 | - `datafl`: the input data in a simplified Geo-EAS formatted file. 8 | 9 | - `nvar`: the number of variables (primary plus all secondary). 10 | For example, `nvar`=2 if there is only one secondary variable. 11 | 12 | - `icolx`, `icoly`, `icolz` and `icolvr()`: the columns for the x, y and 13 | z coordinates, the primary variable to be kriged, and all secondary variables. 14 | 15 | - `tmin` and `tmax`: all values (for all variables) strictly less than `tmin` 16 | and greater than or equal to `tmax` are ignored. 17 | 18 | - `icolloc`: set to 1 if performing co-located cokriging with a gridded secondary 19 | variable, otherwise set to 0. 20 | 21 | - `secfl`: if co-located cokriging, the file with 22 | the gridded secondary variable. 23 | 24 | - `icolsec`: if co-located cokriging, the column number for the secondary 25 | variable in `secfl`. 26 | 27 | - `idbg`: an integer debugging level between 0 and 3. The higher the 28 | debugging level the more output. Normally level 0 or 1 should be chosen. 29 | 30 | - `dbgfl`: the debugging output is written to this file. 31 | 32 | - `outfl`: the output grid is written to this file. The output file will 33 | contain both the kriging estimate and the kriging variance for all 34 | points/blocks. The output grid cycles fastest on x then y then z. 35 | 36 | - `nx`, `xmn`, `xsiz`: definition of the grid system (x axis). 37 | 38 | - `ny`, `ymn`, `ysiz`: definition of the grid system (y axis). 39 | 40 | - `nz`, `zmn`, `zsiz`: definition of the grid system (z axis). 41 | 42 | - `nxdis`, `nydis` and `nzdis`: the number of discretization points for 43 | a block. If `nxdis`, `nydis` and `nzdis` are set to 1 then point cokriging 44 | is performed. 45 | 46 | - `ndmin`, `ndmaxp` and `ndmaxs`: the minimum and maximum number of primary 47 | data, and the maximum number of secondary data (regardless of which secondary 48 | variable) to use for kriging a block. 49 | 50 | - `pradius_hmax`, `pradius_hmin` and `pradius_vert`: 51 | search radii for primary data 52 | 53 | - `sradius_hmax`, `sradius_hmin` and `sradius_vert`: 54 | search radii for secondary data (same for all types) 55 | 56 | - `sangle`, `sangle1` and `sangle2`: the angles defining the common 57 | orientation of the search ellipsoids for primary and secondary data 58 | 59 | - `ktype`: the kriging type must be specified: 60 | - 0 = simple cokriging; 61 | - 1 = standardized ordinary cokriging with re-centered variables 62 | and a single unbiasedness constraint; 63 | - 2 = traditional ordinary cokriging. 64 | 65 | - `mean()`: the mean of the primary and all secondary variables are required 66 | input if either simple cokriging or standardized ordinary cokriging are used. 67 | The program calculates the data residuals from these means. 68 | 69 | The direct and cross variograms may be specified in any order; they are 70 | specified according to the variable number Variable `1` is the primary 71 | (regardless of its column ordering in the input data files) 72 | and the secondary variables are numbered from `2` depending on their 73 | ordered specification in `icolvr()`. It is unnecessary to specify 74 | the `j` to `i` cross variogram if the `i` to `j` cross variogram 75 | has been specified; the cross variogram is expected to be symmetric 76 | (as from theory). For each `i` to `j` variogram the following are required: 77 | 78 | - `nst` and `c0`: the number of variogram structures and the isotropic 79 | nugget constant. The nugget constant does not count as a structure. 80 | 81 | - For each of the `nst` nested structures one must define `it` the type of 82 | structure (the power model is not allowed); 83 | - `cc`, the c parameter; 84 | - `ang1`, `ang2`, `ang3` the angles defining the geometric anisotropy; 85 | - `aa_hmax`, the maximum horizontal range; 86 | - `aa_hmin`, the minimum horizontal range; and 87 | - `aa_vert`, the vertical range. 88 | 89 | # Application notes: 90 | 91 | The construction of the cokriging matrix requires the **linear model of 92 | coregionalization**. The input variogram parameters are checked for 93 | positive definiteness. *The power model is not allowed.* 94 | 95 | A specific search is done for secondary data (same for all secondary) 96 | allowing the option of collocated cokriging. 97 | 98 | A cokriging program for scattered points and cross validation is not provided; 99 | programs `cokb3d` and `kt3d` could be combined for this purpose. 100 | -------------------------------------------------------------------------------- /gslib_help/gam_params.md: -------------------------------------------------------------------------------- 1 | `datafl`: the input data in a simplified Geo_EAS formatted file. The data are 2 | ordered rowwise (X cycles fastest, then Y, then Z). 3 | 4 | `nvar` and `ivar(1)` ... `ivar(nvar)`: the number of variables and their 5 | columns in the data file. 6 | 7 | `tmin` and `tmax`: all values, regardless of which variable, strictly less 8 | than tmin and greater than or equal to tmax are ignored. 9 | 10 | `outfl`: the output variograms are written to a single output file named outfl. 11 | The output file contains the variograms ordered by direction and then variogram 12 | type specified in the parameter file (the directions cycle fastest then the 13 | variogram number.) For each variogram there is a one-line description and then 14 | nlag lines each with the following: 15 | 16 | 1. lag number (increasing from 1 to nlag). 17 | 2. average separation distance for the lag. 18 | 3. the *semivariogram* value (whatever type was specified). 19 | 4. number of pairs for the lag. 20 | 5. mean of the data contributing to the tail. 21 | 6. mean of the data contributing to the head. 22 | 7. the tail and head variances (for the correlogram). 23 | 24 | `igrid`: the grid or realization number. Recall that realization or grids are 25 | written on after another; therefore, if igrid=2 the input file must contain 26 | at least 2 nx ny nz values and the second set of nx ny nz values will be taken 27 | as the second grid. 28 | 29 | `nx`, `xmn`, `xsiz`: definition of the grid system (x axis) 30 | 31 | `ny`, `ymn`, `ysiz`: definition of the grid system (y axis) 32 | 33 | `mz`, `zmn`, `zsiz`: definition of the grid system (z axis) -------------------------------------------------------------------------------- /gslib_help/kb2d_help.md: -------------------------------------------------------------------------------- 1 | # Description: 2 | 3 | This is a straightforward 2-D simple and ordinary kriging subroutine that can 4 | be used as is or as a basis for custom kriging programs. 5 | 6 | 7 | # Paramters: 8 | 9 | - `datafl`: the input data in a simplified Geo-EAS formatted file. 10 | 11 | - `icolx`, `icoly` and `icolvr`: the columns for the x and y coordinates, and 12 | the variable to be kriged. 13 | 14 | - `tmin` and `tmax`: all values strictly less than `tmin` and greater than or 15 | equal to `tmax` are ignored. 16 | 17 | - `idbg`: an iteger debugging level between 0 and 3. The higher the debugging 18 | level, the more output. Normally level 0 or 1 should be chosen. If there are 19 | suspected problems, or if you would like to see the actual kriging matrices, 20 | level 2 or 3 can be chosen. It is advisable to restrict the actual number of 21 | points being estimated when the debugging level is high (the debugging file 22 | can become extremely large.) 23 | 24 | - `dbgfl`: the debugging output is written to this file. 25 | 26 | - `outfl`: the output grid is written to this file. The output file will contain 27 | both the kriging estimates and the kriging variance for all points/blocks. The 28 | output grid cycles fastest on *x* and then *y*. 29 | 30 | - `nx`, `xmn`, `xsiz`: definition of the grid system (*x* axis). 31 | 32 | - `ny`, `ymn`, `ysiz`: definition of the grid system (*y* axis). 33 | 34 | - `nxdis` and `nydis`: the number of discretization points for a block. If both 35 | `nxdis` and `nydis` are set to 1, then point kriging is performed. 36 | 37 | - `ndmin` and `ndmax`: the minimum and maximum number of data points to use for 38 | kriging a block. 39 | 40 | - `radius`: the maximum isotopic search radius. 41 | 42 | - `isk` and `skmean`: if `isk`=0, then simple kriging will be performed with a 43 | mean of `skmean`. 44 | 45 | - `nst` and `c0`: the number of variogram structures and the isotopic nugget 46 | constant. The nugget constant does not count as a structure. 47 | 48 | - For each of the `nst` nested structures one must define `it`, the type of 49 | structures; `cc`, the *c* parameter; `azm`, the maximum range; `a_max`, the 50 | maximum range; and `a_min`, the minimum range. A detailed description of these 51 | parameters is given in section II.3. 52 | -------------------------------------------------------------------------------- /gslib_help/kt3d_help.md: -------------------------------------------------------------------------------- 1 | # Description: 2 | 3 | The program kt3d provides a fairly advanced 3-D kriging program for points or 4 | blocks by simple kriging (SK), ordinary kriging (OK), or kriging with a 5 | polynomial trend model (KT) with up to nine monomial terms. The program works 6 | in 2-D and is faster than kb2d if there are many data. One of the features 7 | that makes this program fairly fast is the super block search. 8 | 9 | # Parameters: 10 | 11 | - `datafl`: the input data in a simplified Geo-EAS formatted file. 12 | 13 | - `icolx`, `icoly`, `icolz`, `icolvr` and `icolsec`: the columns for the x, y, 14 | and z coordinates, the variable to be estimated, and the external drift 15 | variable (or non-stationary mean). 16 | 17 | - `tmin` and `tmax`: all values strictly less than tmin and greater than or equal 18 | to tmax are ignored. 19 | 20 | - `option`: set to 0 for kriging a grid of points or blocks, to 1 for cross 21 | validation with the data in datafl and to 2 for jackknifing with data in 22 | following file. 23 | 24 | - `jackfl`: file with locations to perform estimation (jackknife option). 25 | 26 | - `icolx`, `icoly`, `icolz`, `icolvr` and `icolsec`: the columns for the x, y, 27 | and z coordinates, the variable, and the secondary variable in `jackfl` 28 | 29 | - `idbg`: an integer debugging level between 0 and 3. The higher the debugging 30 | level the more output. The normal levels are 0 and 1 which summarize the 31 | results. Levels 2 and 3 provide all the kriging matrices and data used for 32 | the estimation of every point/block. It is recommended that a high debugging 33 | level not be used with a large grid. 34 | 35 | - `dbgfl`: the debugging output is written to this file. 36 | 37 | - `outfl`: the output grid is written to this file. The output contains the 38 | estimate and the kriging variance for every point/block on the grid, cycling 39 | fastest on x then y and finally z Unestimated points are flagged with a large 40 | negative number (-999.). The parameter UNEST, in the source code, can be 41 | changed if a different number is preferred. 42 | 43 | - `nx`, `xmn`, `xsiz`: definition of the grid system (x axis). 44 | 45 | - `ny`, `ymn`, `ysiz`: definition of the grid system (y axis). 46 | 47 | - `nz`, `zmn`, `zsiz`: definition of the grid system (z axis). 48 | 49 | - `nxdis`, `nydis` and `nzdis`: the number of discretization points for a block. 50 | If nxdis, nydis and nzdis are all set to 1 then point kriging is performed. 51 | 52 | - `ndmin` and `ndmax`: the minimum and maximum number of data points to use for 53 | kriging a block. 54 | 55 | - `noct`: the maximum number to retain from an octant (an octant search is not 56 | used if `noct`=0) 57 | 58 | - `radius_hmax`, `radius_hmin` and `radius_vert` the search radii in the maximum 59 | horizontal direction, minimum horizontal direction, and vertical direction 60 | (see angles below). 61 | 62 | - `sang1`, `sang2` and `sang3`: the angle parameters that describe the 63 | orientation of the search ellipsoid. See the discussion on anisotropy 64 | specification associated with Figure II.4. 65 | 66 | - `ikrige` and `skmean`: 67 | - if `ikrige` is set to 0 then stationary simple kriging with (`skmean`) will be performed, 68 | - if `ikrige` is set to 1 then ordinary kriging will be performed, 69 | - if `ikrige` is set to 2 then non-stationary simple kriging with means taken from `secfile` will be performed, 70 | - if `ikrige` is set to 3 then kriging with an external drift will be performed. 71 | - Note that power law variogram models (`it`=4) are not allowed with simple kriging. 72 | 73 | - `idrif(i),i=1...9`: indicators for those drift terms to be included in the 74 | trend model. `idrif(i)` is set to 1 if the drift term number `i` should be 75 | included, and is set to zero if not. The nine drift terms correspond to 76 | the following: 77 | 78 | - `i = 1` linear drift in x 79 | - `i = 2` linear drift in y 80 | - `i = 3` linear drift in z 81 | - `i = 4` quadratic drift in x 82 | - `i = 5` quadratic drift in y 83 | - `i = 6` quadratic drift in z 84 | - `i = 7` cross quadratic drift in xy 85 | - `i = 8` cross quadratic drift in xz 86 | - `i = 9` cross quadratic drift in yz 87 | 88 | - `itrend`: indicator of whether to estimate the trend (`itrend` =1) or the 89 | variable (`itrend` =0). The trend may be kriged with ordinary kriging (all 90 | `idrif(i)` values set to 0) or with any combination of trend kriging (some 91 | `idrif(i)` terms set to 1). 92 | 93 | - `secfl`: a file for the gridded external drift variable. The external drift 94 | variable is needed at all grid locations to be estimated. The origin of the 95 | grid network, the number of nodes, and the spacing of the grid nodes should 96 | be exactly the same as the grid being kriged in kt3d This variable is used 97 | only if `ikrige`=2 or 3. 98 | 99 | - `iseccol`: the column number in secfl for the gridded secondary variable. 100 | This variable is used if `ikrige`=2 or 3. 101 | 102 | - `nst` and `c0`: the number of variogram structures and the nugget constant. 103 | The nugget constant does not count as a structure. 104 | 105 | - For each of the `nst` nested structures one must define `it`, the type of 106 | structure; `cc`, the c parameter; `ang1`,`ang2`,`ang3`, the angles defining 107 | the geometric anisotropy; `aa_hmax`, the maximum horizontal range; `aa_hmin`, 108 | the minimum horizontal range; and `aa_vert`, the vertical range. 109 | 110 | # Application notes: 111 | - The program is set up so that a novice programmer can make changes to the form 112 | of the polynomial drift. The external drift concept has been incorporated, 113 | adding an additional unbiasedness constraint to the ordinary kriging system. 114 | When using an external drift, it is necessary to know the value of the drift 115 | variable at all data locations and all the locations that will be estimated 116 | (i.e., all grid nodes). 117 | 118 | - The program also allows simple kriging with non-stationary means read from 119 | an input file. The non-stationary means must be known at all data locations 120 | and all locations to be estimated. 121 | -------------------------------------------------------------------------------- /gslib_help/sgsim_help.md: -------------------------------------------------------------------------------- 1 | # Description: 2 | 3 | Sequential Gaussian simulation program 4 | 5 | # Parameters: 6 | 7 | - `datafl`: the input data in a simplified Geo-EAS formatted file. If this file does not exist then an unconditional simulation will be generated. 8 | 9 | - `icolx`, `icoly`, `icolz`, `icolvr`, `icolwt` and `icolsec`: the column numbers for 10 | the x, y and z coordinates, the variable to be simulated, the declustering 11 | weight, and the secondary variable (e.g., for external drift if used). 12 | One or two of the coordinate column numbers can be set to zero which indicates 13 | that the simulation is 2-D or 1-D. For equal weighting, set `icolwt` to zero. 14 | 15 | - `tmin` and `tmax`: all values strictly less than `tmin` 16 | and strictly greater than `tmax` are ignored. 17 | 18 | - `itrans`: if set to 0 then no transformation will be performed; 19 | the variable is assumed already standard normal (the simulation results 20 | will also be left unchanged). If `itrans`=1, transformations are performed. 21 | 22 | - `transfl`: output file for the transformation table if transformation 23 | is required (igauss=0). 24 | 25 | - `ismooth`: if set to 0, then the data histogram, possibly with declustering 26 | weights is used for transformation, if set to 1, then the data are transformed 27 | according to the values in another file (perhaps from histogram smoothing). 28 | 29 | - `smthfl`: file with the values to use for transformation to normal scores 30 | (if ismooth is set to 0). 31 | 32 | - `icolvr` and `icolwt`: columns in smthfl for the variable and the 33 | declustering weight (set to 1 and 2 if smthfl is the output from histsmth). 34 | 35 | - `zmin` and `zmax`: the minimum and maximum allowable data values. 36 | These are used in the back transformation procedure. 37 | 38 | - `ltail` and `ltpar` specify the back transformation implementation in 39 | the lower tail of the distribution: 40 | - `ltail`=1 implements linear interpolation to the lower limit zmin, 41 | - `ltail`=2 implements power model interpolation, with w=`ltpar`, 42 | to the lower limit `zmin`. 43 | - The middle class interpolation is linear. 44 | 45 | - `utail` and `utpar` specify the back transformation implementation in the 46 | upper tail of the distribution: 47 | - `utail`=1 implements linear interpolation to the upper limit `zmax`, 48 | - `utail`=2 implements power model interpolation, with w=`utpar`, 49 | to the upper limit `zmax`, 50 | - `utail`=4 implements hyperbolic model extrapolation with w=`utpar`. 51 | - The hyperbolic tail extrapolation is limited by zmax. 52 | 53 | - `idbg`: an integer debugging level between 0 and 3. 54 | The larger the debugging level the more information written out. 55 | 56 | - `dbgfl`: the file for the debugging output. 57 | 58 | - `outfl`: the output grid is written to this file. The output file will 59 | contain the results, cycling fastest on x then y then z then simulation by simulation. 60 | 61 | - `nsim`: the number of simulations to generate. 62 | 63 | - `nx`, `xmn`, `xsiz`: definition of the grid system (x axis). 64 | 65 | - `ny`, `ymn`, `ysiz`: definition of the grid system (y axis). 66 | 67 | - `nz`, `zmn`, `zsiz`: definition of the grid system (z axis). 68 | 69 | - `seed`: random number seed (a large odd integer). 70 | 71 | - `ndmin` and `ndmax`: the minimum and maximum number of original data that 72 | should be used to simulate a grid node. If there are fewer than 73 | `ndmin` data points the node is not simulated. 74 | 75 | - `ncnode`: the maximum number of previously simulated nodes to use 76 | for the simulation of another node. 77 | 78 | - `sstrat`: if set to 0, the data and previously simulated grid nodes are 79 | searched separately: the data are searched with a *super block* search and 80 | the previously simulated nodes are searched with a *spiral search*. 81 | If set to 1, the data are relocated to grid nodes and a spiral search is used 82 | and the parameters `ndmin` and `ndmax` are not considered. 83 | 84 | - `multgrid`: a multiple grid simulation will be performed if this is set to 1 85 | (otherwise a standard spiral search for previously simulated nodes is considered). 86 | 87 | - `nmult`: the number of multiple grid refinements to consider 88 | (used only if multgrid is set to 1). 89 | 90 | - `noct`: the number of original data to use per octant. If this parameter is 91 | set less than or equal to 0, then it is not used; otherwise, it overrides 92 | the ndmax parameter and the data is partitioned into octants and 93 | the closest `noct` data in each octant is retained for the simulation of a grid node. 94 | 95 | - `radius_hmax`, `radius_hmin` and `radius_vert`: 96 | the search radii in the maximum horizontal direction, 97 | minimum horizontal direction, and vertical direction (see angles below). 98 | 99 | - `sang1`, `sang2` and `sang3`: 100 | the angle parameters that describe the orientation of the search ellipsoid. 101 | 102 | - `ktype`: the kriging type ( 103 | 0 = simple kriging, 104 | 1 = ordinary kriging, 105 | 2 = simple kriging with a locally varying mean, 106 | 3 = kriging with an external drift, or 107 | 4 = collocated cokriging with one secondary variable) 108 | used throughout the loop over all nodes. 109 | SK is required by theory; only in cases where the number of original data 110 | found in the neighborhood is large enough can OK be used without 111 | the risk of spreading data values beyond their range of influence. 112 | 113 | - `rho`: correlation coefficient to use for collocated cokriging 114 | (used only if ktype = 4). 115 | 116 | - `secfl`: the file for the locally varying mean, the external drift variable, 117 | or the secondary variable for collocated cokriging (the secondary variable 118 | must be gridded at the same resolution as the model being constructed by sgsim). 119 | 120 | - `nst` and `c0`: the number of semivariogram structures and the isotropic nugget constant. 121 | For each of the nst nested structures one must define 122 | - `it`, the type of structure; 123 | - `cc`, the c parameter; 124 | - `ang1`, `ang2`, `ang3`, the angles defining the geometric anisotropy; 125 | - `aa_hmax`, the maximum horizontal range; 126 | - `aa_hmin`, the minimum horizontal range; and 127 | - `aa_vert`, the vertical range. 128 | 129 | # Application notes: 130 | 131 | This program requires standard normal data and writes standard normal simulated values. Normal score transform and back transform are to be performed outside of this program 132 | Recall that the power model is not a legitimate model for a multiGaussian phenomenon and it is not allowed in `sgsim` 133 | The semivariogram model is that of the normal scores. The kriging variance is directly interpreted as the variance of the conditional distribution; consequently, the nugget constant `c0` and `c` (sill) parameters should add to 1.0. 134 | -------------------------------------------------------------------------------- /gslib_help/super_block_search.md: -------------------------------------------------------------------------------- 1 | # Super Block Search 2 | 3 | The super block search strategy is an eficient algorithm to be used in cases 4 | where many points are to be estimated, using local data neighborhoods, with 5 | the same set of original data. The algorithm calls for an initial 6 | classification and ordering of the data according to a regular network of 7 | parallelipedic blocks. This grid network is independent of the grid 8 | network of points/blocks being estimated or simulated. Typically, the size 9 | of the search network is much larger than the final estimation or simulation 10 | grid node spacing. 11 | 12 | When estimating any one point, only those data within nearby super 13 | blocks have to be checked. A large number of data are thus quickly eliminated 14 | because they have been classified in super blocks beyond the search limits. 15 | This is illustrated in 2D on Figure II.7, where an 11 by 11 super block grid 16 | network has been established over an area containing 140 data points. When 17 | estimating a point anywhere within the dark gray super block, only those data 18 | within the dark black line need be considered. Note that all search resolution 19 | less than the size of a super block has been lost. Also note that the light 20 | gray region is defined by the search ellipse (circle in this case) with its 21 | center translated to every node to be estimated within the dark gray super 22 | block. All super blocks intersected by the light gray region must be considered 23 | to ensure that all nearby data are considered for estimahion of any node within 24 | the central dark gray superblock. 25 | 26 | ![super_block_search](img/super_block_search.png) 27 | 28 | The first task is to build a template of super blocks, centered at the super 29 | block that contains the node being estimated. For example, the template 30 | is the relative locations of all 21 blocks enclosed by the solid line on Figure 31 | 11.7. With this template, the nearby super blocks are easily established 32 | when considering any new location. 33 | 34 | -------------------------------------------------------------------------------- /img/Grid_Definition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/Grid_Definition.png -------------------------------------------------------------------------------- /img/behavior_near_origin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/behavior_near_origin.png -------------------------------------------------------------------------------- /img/cokb3d_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/cokb3d_params.png -------------------------------------------------------------------------------- /img/gam_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/gam_params.png -------------------------------------------------------------------------------- /img/gamv_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/gamv_params.png -------------------------------------------------------------------------------- /img/graph_of_lag_model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/graph_of_lag_model.png -------------------------------------------------------------------------------- /img/head_tail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/head_tail.png -------------------------------------------------------------------------------- /img/kb2d_params.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/kb2d_params.png -------------------------------------------------------------------------------- /img/super_block_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/img/super_block_search.png -------------------------------------------------------------------------------- /parameters/gam_write_parameters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Thu Nov 03 20:53:02 2016 4 | """ 5 | __author__ = "yuhao" 6 | 7 | import os 8 | import json 9 | 10 | PARAMS = { 11 | 'datafl': 'testData/xihu_sparse.gslib', 12 | 'nvar': 1, 13 | 'ivar': [1, 2], 14 | 'tmin': -1.0e21, 15 | 'tmax': 1.0e21, 16 | 'outfl': 'gam.out', 17 | 'igrid': 1, 18 | 'nx': 15, 19 | 'xmn': 0.5, 20 | 'xsiz': 5, 21 | 'ny': 23, 22 | 'ymn': 0.5, 23 | 'ysiz': 5, 24 | 'nz': 161, 25 | 'zmn': 0.5, 26 | 'zsiz': 0.5, 27 | 'ndir': 2, 28 | 'nlag': 10, 29 | 'ixd': [1, 0], 30 | 'iyd': [0, 1], 31 | 'izd': [0, 0], 32 | 'standardize': True, 33 | 'nvarg': 5, 34 | 'ivtail': [1, 1, 2, 2, 1], 35 | 'ivhead': [1, 1, 2, 2, 1], 36 | 'ivtype': [1, 3, 1, 3, 9] 37 | } 38 | 39 | PARENT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir) 40 | PARAM_DIR = os.path.join(PARENT_DIR, 'testData') 41 | 42 | with open(os.path.join(PARAM_DIR, 'xihuSmall_sparse_gam.par'), 'w') as fout: 43 | fout.write(json.dumps(PARAMS, sort_keys=True, indent=4)) 44 | -------------------------------------------------------------------------------- /parameters/gamv_write_parameters.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Nov 06 18:19:28 2016 4 | """ 5 | __author__ = "yuhao" 6 | 7 | import os 8 | import json 9 | 10 | PARAMS = { 11 | 'datafl': 'testData/test.gslib', 12 | 'icolx': 1, 13 | 'icoly': 2, 14 | 'icolz': 0, 15 | 'nvar': 1, 16 | 'ivar': [3, 4], 17 | 'tmin': -1.0e21, 18 | 'tmax': 1.0e21, 19 | 'outfl': 'out.dat', 20 | 'nlag': 20, 21 | 'xlag': 500.0, 22 | 'xltol': 300.0, 23 | 'ndir': 1, 24 | 'azm': [0.0], # [0.0, 0.0, 90.], 25 | 'atol': [90.0], # [90.0, 22.5, 22.5], 26 | 'bandwh': [200.0], # [200.0, 200.0, 200.0], 27 | 'dip': [0.0], # [0.0, 0.0, 0.0], 28 | 'dtol': [90.0], # [90.0, 22.5, 22.5], 29 | 'bandwd': [200.0], # [200.0, 200.0, 200.0], 30 | 'standardize': False, 31 | 'nvarg': 3, 32 | 'ivtail': [1, 1, 2], 33 | 'ivhead': [1, 1, 2], 34 | 'ivtype': [1, 3, 1] 35 | } 36 | 37 | PARENT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir) 38 | PARAM_DIR = os.path.join(PARENT_DIR, 'testData') 39 | 40 | with open(os.path.join(PARAM_DIR, 'xihuSmall_sparse_gamv.par'), 'w') as fout: 41 | fout.write(json.dumps(PARAMS, sort_keys=True, indent=4)) 42 | -------------------------------------------------------------------------------- /parameters/krige2d_write_params.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Nov 2016 4 | """ 5 | __author__ = "yuhao" 6 | 7 | import os 8 | import json 9 | 10 | PARAMS = { 11 | 'datafl': 'testData/test.gslib', 12 | 'icolx': 0, 13 | 'icoly': 1, 14 | 'icolvr': 3, 15 | 'tmin': -1.0e21, 16 | 'tmax': 1.0e21, 17 | 'idbg': 3, 18 | 'dbgfl': 'kb2d.dbg', 19 | 'outfl': 'out.dat', 20 | 'nx': 98, 21 | 'xmn': 100, 22 | 'xsiz': 200, 23 | 'ny': 79, 24 | 'ymn': 200, 25 | 'ysiz': 1.0, 26 | 'nxdis': 1, 27 | 'nydis': 1, 28 | 'ndmin': 3, 29 | 'ndmax': 10, 30 | 'radius': 12000, 31 | 'isk': 0, 32 | 'skmean': 14.69588, 33 | 'nst': 1, 34 | 'c0': 0.05, 35 | 'it': [0], 36 | 'cc': [0.65], 37 | 'azm': [90], 38 | 'a_max':[3715.9], 39 | 'a_min': [3715.9] 40 | } 41 | 42 | PARENT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir) 43 | PARAM_DIR = os.path.join(PARENT_DIR, 'testData') 44 | 45 | with open(os.path.join(PARAM_DIR, 'test_krige2d.par'), 'w') as fout: 46 | fout.write(json.dumps(PARAMS, sort_keys=True, indent=4)) 47 | -------------------------------------------------------------------------------- /parameters/krige3d_write_params.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Tue Nov 2016 4 | """ 5 | __author__ = "yuhao" 6 | 7 | import os 8 | import json 9 | 10 | PARAMS = { 11 | 'datafl': 'testData/test.gslib', 12 | 'icolx': 0, 13 | 'icoly': 1, 14 | 'icolz': 2, 15 | 'icolvr': 3, 16 | 'icolsec': 4, 17 | 18 | 'tmin': -1.0e21, 19 | 'tmax': 1.0e21, 20 | 21 | 'option': 0, 22 | 'jackfl': 'jackfl.dat', 23 | 'jicolx': 0, 24 | 'jicoly': 1, 25 | 'jicolz': 2, 26 | 'jicolvr': 3, 27 | 'jicolsec': 4, 28 | 29 | 'idbg': 3, 30 | 'dbgfl': 'kt3d.dbg', 31 | 'outfl': 'out.dat', 32 | 33 | 'nx': 98, 34 | 'xmn': 100, 35 | 'xsiz': 200, 36 | 'ny': 79, 37 | 'ymn': 100, 38 | 'ysiz': 200, 39 | 'nz': 1, 40 | 'zmn': 0, 41 | 'zsiz': 200, 42 | 43 | 'nxdis': 1, 44 | 'nydis': 1, 45 | 'nzdis': 1, 46 | 47 | 'ndmin': 1, 48 | 'ndmax': 30, 49 | 50 | 'noct': 0, 51 | 'radius_hmax': 4000, 52 | 'radius_hmin': 4000, 53 | 'radius_vert': 0, 54 | 'sang1' : 0, 55 | 'sang2' : 0, 56 | 'sang3' : 0, 57 | 58 | 'ikrige': 0, 59 | 'skmean': 14.69588, 60 | 61 | 'idrift': [False, False, False, False, False, False, False, False, False], 62 | 'itrend': False, 63 | 'secfl': 'secfl.dat', 64 | 'iseccol': 3, 65 | 66 | 'nst': 1, 67 | 'c0': 0.05, 68 | 'it': [1], 69 | 'cc': [0.65], 70 | 'ang1': [0], 71 | 'ang2': [0], 72 | 'ang3': [0], 73 | 'aa_hmax': [3715.9], 74 | 'aa_hmin': [3715.9], 75 | 'aa_vert': [3715.9], 76 | } 77 | 78 | PARENT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir) 79 | PARAM_DIR = os.path.join(PARENT_DIR, 'testData') 80 | 81 | with open(os.path.join(PARAM_DIR, 'test_krige3d.par'), 'w') as fout: 82 | fout.write(json.dumps(PARAMS, sort_keys=True, indent=4)) 83 | -------------------------------------------------------------------------------- /parameters/sgsim_write_params.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Apr 2 2017 4 | """ 5 | __author__ = "yuhao" 6 | 7 | import os 8 | import json 9 | 10 | PARAMS = { 11 | 'datafl': 'testData/test.gslib', 12 | 'icolx': 0, 13 | 'icoly': 1, 14 | 'icolz': -1, 15 | 'icolvr': 2, 16 | 'icolsec': -1, # for external drift if used 17 | 'icolwt': -1, # declustering weights 18 | 19 | # data limits 20 | 'tmin': -1.0e21, 21 | 'tmax': 1.0e21, 22 | 'itrans': True, # boolean 23 | # output file for transformation table if transformation is needed 24 | 'transfl': 'sgsim.trn', 25 | 'ismooth': False, # boolean 26 | # file with values used for transformation to normal scores 27 | 'smthfl': 'histsmth.out', 28 | 'icolsvr': 0, 29 | 'icolswt': 1, 30 | # allowable data values used for backtransform 31 | 'zmin': 0, 32 | 'zmax': 30, 33 | # lower and upper tail model specification for backtransform 34 | 'ltail': 1, 35 | 'ltpar': 0, 36 | 'utail': 1, 37 | 'utpar': 15, 38 | # debug and output data file 39 | 'idbg': 3, 40 | 'dbgfl': 'sgsim.dbg', 41 | 'outfl': 'sgsim.out', 42 | 'nsim': 3, # number of simulation 43 | # Grid definition 44 | 'nx': 98, 45 | 'xmn': 100, 46 | 'xsiz': 200, 47 | 'ny': 79, 48 | 'ymn': 100, 49 | 'ysiz': 200, 50 | 'nz': 1, 51 | 'zmn': 0, 52 | 'zsiz': 200, 53 | 'seed': 1, # random seed 54 | # maximum and minimum data points used in kriging 55 | 'ndmin': 1, 56 | 'ndmax': 30, 57 | 'nodmax': 12, # previously simulated nodes to use 58 | 'sstrat': 0, # search strategy 59 | 'multgrid': False, # boolean 60 | 'nmult': 2, # scalar 61 | 'noct': 0, # maximum number to retain from an octant 62 | # search radii 63 | 'radius_hmax': 4000, 64 | 'radius_hmin': 4000, 65 | 'radius_vert': 0, 66 | # search anisotropy angles 67 | 'sang1' : 0, 68 | 'sang2' : 0, 69 | 'sang3' : 0, 70 | # size of covariance lookup table size 71 | 'mxctx': 30, 72 | 'mxcty': 30, 73 | 'mxctz': 30, 74 | # kriging type 75 | 'ikrige': 0, 76 | # self.skmean = params['skmean'] 77 | 'rho': 0.7, # correlation coefficient for COCOK 78 | 'varred': 0.1, # variance reduction factor for COCOK 79 | 'secfl': 'ydata.dat', 80 | 'icollvm': 4, 81 | # Vairography definition 82 | 'nst': 1, 83 | 'c0': 0.05, 84 | 'it': [1], 85 | 'cc': [0.65], 86 | 'ang1': [0], 87 | 'ang2': [0], 88 | 'ang3': [0], 89 | 'aa_hmax': [3715.9], 90 | 'aa_hmin': [3715.9], 91 | 'aa_vert': [3715.9] 92 | } 93 | 94 | PARENT_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), os.path.pardir) 95 | PARAM_DIR = os.path.join(PARENT_DIR, 'testData') 96 | 97 | with open(os.path.join(PARAM_DIR, 'test_sgsim.par'), 'w') as fout: 98 | fout.write(json.dumps(PARAMS, sort_keys=True, indent=4)) 99 | -------------------------------------------------------------------------------- /pygeostatistics/__init__.py: -------------------------------------------------------------------------------- 1 | from .variogram_model import spherical, exponential, gaussian, power, hole_effect 2 | from .krige3d import Krige3d 3 | from .sgsim import Sgsim 4 | from .gamv import Gamv 5 | 6 | from ._version import get_versions 7 | __version__ = get_versions()['version'] 8 | del get_versions 9 | -------------------------------------------------------------------------------- /pygeostatistics/_version.py: -------------------------------------------------------------------------------- 1 | 2 | # This file helps to compute a version number in source trees obtained from 3 | # git-archive tarball (such as those provided by githubs download-from-tag 4 | # feature). Distribution tarballs (built by setup.py sdist) and build 5 | # directories (produced by setup.py build) will contain a much shorter file 6 | # that just contains the computed version number. 7 | 8 | # This file is released into the public domain. Generated by 9 | # versioneer-0.18 (https://github.com/warner/python-versioneer) 10 | 11 | """Git implementation of _version.py.""" 12 | 13 | import errno 14 | import os 15 | import re 16 | import subprocess 17 | import sys 18 | 19 | 20 | def get_keywords(): 21 | """Get the keywords needed to look up the version information.""" 22 | # these strings will be replaced by git during git-archive. 23 | # setup.py/versioneer.py will grep for the variable names, so they must 24 | # each be defined on a line of their own. _version.py will just call 25 | # get_keywords(). 26 | git_refnames = " (HEAD -> master)" 27 | git_full = "e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36" 28 | git_date = "2020-07-26 10:58:27 +0800" 29 | keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} 30 | return keywords 31 | 32 | 33 | class VersioneerConfig: 34 | """Container for Versioneer configuration parameters.""" 35 | 36 | 37 | def get_config(): 38 | """Create, populate and return the VersioneerConfig() object.""" 39 | # these strings are filled in when 'setup.py versioneer' creates 40 | # _version.py 41 | cfg = VersioneerConfig() 42 | cfg.VCS = "git" 43 | cfg.style = "pep440" 44 | cfg.tag_prefix = "" 45 | cfg.parentdir_prefix = "None" 46 | cfg.versionfile_source = "pygeostatistics/_version.py" 47 | cfg.verbose = False 48 | return cfg 49 | 50 | 51 | class NotThisMethod(Exception): 52 | """Exception raised if a method is not valid for the current scenario.""" 53 | 54 | 55 | LONG_VERSION_PY = {} 56 | HANDLERS = {} 57 | 58 | 59 | def register_vcs_handler(vcs, method): # decorator 60 | """Decorator to mark a method as the handler for a particular VCS.""" 61 | def decorate(f): 62 | """Store f in HANDLERS[vcs][method].""" 63 | if vcs not in HANDLERS: 64 | HANDLERS[vcs] = {} 65 | HANDLERS[vcs][method] = f 66 | return f 67 | return decorate 68 | 69 | 70 | def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, 71 | env=None): 72 | """Call the given command(s).""" 73 | assert isinstance(commands, list) 74 | p = None 75 | for c in commands: 76 | try: 77 | dispcmd = str([c] + args) 78 | # remember shell=False, so use git.cmd on windows, not just git 79 | p = subprocess.Popen([c] + args, cwd=cwd, env=env, 80 | stdout=subprocess.PIPE, 81 | stderr=(subprocess.PIPE if hide_stderr 82 | else None)) 83 | break 84 | except EnvironmentError: 85 | e = sys.exc_info()[1] 86 | if e.errno == errno.ENOENT: 87 | continue 88 | if verbose: 89 | print("unable to run %s" % dispcmd) 90 | print(e) 91 | return None, None 92 | else: 93 | if verbose: 94 | print("unable to find command, tried %s" % (commands,)) 95 | return None, None 96 | stdout = p.communicate()[0].strip() 97 | if sys.version_info[0] >= 3: 98 | stdout = stdout.decode() 99 | if p.returncode != 0: 100 | if verbose: 101 | print("unable to run %s (error)" % dispcmd) 102 | print("stdout was %s" % stdout) 103 | return None, p.returncode 104 | return stdout, p.returncode 105 | 106 | 107 | def versions_from_parentdir(parentdir_prefix, root, verbose): 108 | """Try to determine the version from the parent directory name. 109 | 110 | Source tarballs conventionally unpack into a directory that includes both 111 | the project name and a version string. We will also support searching up 112 | two directory levels for an appropriately named parent directory 113 | """ 114 | rootdirs = [] 115 | 116 | for i in range(3): 117 | dirname = os.path.basename(root) 118 | if dirname.startswith(parentdir_prefix): 119 | return {"version": dirname[len(parentdir_prefix):], 120 | "full-revisionid": None, 121 | "dirty": False, "error": None, "date": None} 122 | else: 123 | rootdirs.append(root) 124 | root = os.path.dirname(root) # up a level 125 | 126 | if verbose: 127 | print("Tried directories %s but none started with prefix %s" % 128 | (str(rootdirs), parentdir_prefix)) 129 | raise NotThisMethod("rootdir doesn't start with parentdir_prefix") 130 | 131 | 132 | @register_vcs_handler("git", "get_keywords") 133 | def git_get_keywords(versionfile_abs): 134 | """Extract version information from the given file.""" 135 | # the code embedded in _version.py can just fetch the value of these 136 | # keywords. When used from setup.py, we don't want to import _version.py, 137 | # so we do it with a regexp instead. This function is not used from 138 | # _version.py. 139 | keywords = {} 140 | try: 141 | f = open(versionfile_abs, "r") 142 | for line in f.readlines(): 143 | if line.strip().startswith("git_refnames ="): 144 | mo = re.search(r'=\s*"(.*)"', line) 145 | if mo: 146 | keywords["refnames"] = mo.group(1) 147 | if line.strip().startswith("git_full ="): 148 | mo = re.search(r'=\s*"(.*)"', line) 149 | if mo: 150 | keywords["full"] = mo.group(1) 151 | if line.strip().startswith("git_date ="): 152 | mo = re.search(r'=\s*"(.*)"', line) 153 | if mo: 154 | keywords["date"] = mo.group(1) 155 | f.close() 156 | except EnvironmentError: 157 | pass 158 | return keywords 159 | 160 | 161 | @register_vcs_handler("git", "keywords") 162 | def git_versions_from_keywords(keywords, tag_prefix, verbose): 163 | """Get version information from git keywords.""" 164 | if not keywords: 165 | raise NotThisMethod("no keywords at all, weird") 166 | date = keywords.get("date") 167 | if date is not None: 168 | # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant 169 | # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 170 | # -like" string, which we must then edit to make compliant), because 171 | # it's been around since git-1.5.3, and it's too difficult to 172 | # discover which version we're using, or to work around using an 173 | # older one. 174 | date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) 175 | refnames = keywords["refnames"].strip() 176 | if refnames.startswith("$Format"): 177 | if verbose: 178 | print("keywords are unexpanded, not using") 179 | raise NotThisMethod("unexpanded keywords, not a git-archive tarball") 180 | refs = set([r.strip() for r in refnames.strip("()").split(",")]) 181 | # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of 182 | # just "foo-1.0". If we see a "tag: " prefix, prefer those. 183 | TAG = "tag: " 184 | tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) 185 | if not tags: 186 | # Either we're using git < 1.8.3, or there really are no tags. We use 187 | # a heuristic: assume all version tags have a digit. The old git %d 188 | # expansion behaves like git log --decorate=short and strips out the 189 | # refs/heads/ and refs/tags/ prefixes that would let us distinguish 190 | # between branches and tags. By ignoring refnames without digits, we 191 | # filter out many common branch names like "release" and 192 | # "stabilization", as well as "HEAD" and "master". 193 | tags = set([r for r in refs if re.search(r'\d', r)]) 194 | if verbose: 195 | print("discarding '%s', no digits" % ",".join(refs - tags)) 196 | if verbose: 197 | print("likely tags: %s" % ",".join(sorted(tags))) 198 | for ref in sorted(tags): 199 | # sorting will prefer e.g. "2.0" over "2.0rc1" 200 | if ref.startswith(tag_prefix): 201 | r = ref[len(tag_prefix):] 202 | if verbose: 203 | print("picking %s" % r) 204 | return {"version": r, 205 | "full-revisionid": keywords["full"].strip(), 206 | "dirty": False, "error": None, 207 | "date": date} 208 | # no suitable tags, so version is "0+unknown", but full hex is still there 209 | if verbose: 210 | print("no suitable tags, using unknown + full revision id") 211 | return {"version": "0+unknown", 212 | "full-revisionid": keywords["full"].strip(), 213 | "dirty": False, "error": "no suitable tags", "date": None} 214 | 215 | 216 | @register_vcs_handler("git", "pieces_from_vcs") 217 | def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): 218 | """Get version from 'git describe' in the root of the source tree. 219 | 220 | This only gets called if the git-archive 'subst' keywords were *not* 221 | expanded, and _version.py hasn't already been rewritten with a short 222 | version string, meaning we're inside a checked out source tree. 223 | """ 224 | GITS = ["git"] 225 | if sys.platform == "win32": 226 | GITS = ["git.cmd", "git.exe"] 227 | 228 | out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, 229 | hide_stderr=True) 230 | if rc != 0: 231 | if verbose: 232 | print("Directory %s not under git control" % root) 233 | raise NotThisMethod("'git rev-parse --git-dir' returned error") 234 | 235 | # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] 236 | # if there isn't one, this yields HEX[-dirty] (no NUM) 237 | describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", 238 | "--always", "--long", 239 | "--match", "%s*" % tag_prefix], 240 | cwd=root) 241 | # --long was added in git-1.5.5 242 | if describe_out is None: 243 | raise NotThisMethod("'git describe' failed") 244 | describe_out = describe_out.strip() 245 | full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) 246 | if full_out is None: 247 | raise NotThisMethod("'git rev-parse' failed") 248 | full_out = full_out.strip() 249 | 250 | pieces = {} 251 | pieces["long"] = full_out 252 | pieces["short"] = full_out[:7] # maybe improved later 253 | pieces["error"] = None 254 | 255 | # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] 256 | # TAG might have hyphens. 257 | git_describe = describe_out 258 | 259 | # look for -dirty suffix 260 | dirty = git_describe.endswith("-dirty") 261 | pieces["dirty"] = dirty 262 | if dirty: 263 | git_describe = git_describe[:git_describe.rindex("-dirty")] 264 | 265 | # now we have TAG-NUM-gHEX or HEX 266 | 267 | if "-" in git_describe: 268 | # TAG-NUM-gHEX 269 | mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) 270 | if not mo: 271 | # unparseable. Maybe git-describe is misbehaving? 272 | pieces["error"] = ("unable to parse git-describe output: '%s'" 273 | % describe_out) 274 | return pieces 275 | 276 | # tag 277 | full_tag = mo.group(1) 278 | if not full_tag.startswith(tag_prefix): 279 | if verbose: 280 | fmt = "tag '%s' doesn't start with prefix '%s'" 281 | print(fmt % (full_tag, tag_prefix)) 282 | pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" 283 | % (full_tag, tag_prefix)) 284 | return pieces 285 | pieces["closest-tag"] = full_tag[len(tag_prefix):] 286 | 287 | # distance: number of commits since tag 288 | pieces["distance"] = int(mo.group(2)) 289 | 290 | # commit: short hex revision ID 291 | pieces["short"] = mo.group(3) 292 | 293 | else: 294 | # HEX: no tags 295 | pieces["closest-tag"] = None 296 | count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], 297 | cwd=root) 298 | pieces["distance"] = int(count_out) # total number of commits 299 | 300 | # commit date: see ISO-8601 comment in git_versions_from_keywords() 301 | date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], 302 | cwd=root)[0].strip() 303 | pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) 304 | 305 | return pieces 306 | 307 | 308 | def plus_or_dot(pieces): 309 | """Return a + if we don't already have one, else return a .""" 310 | if "+" in pieces.get("closest-tag", ""): 311 | return "." 312 | return "+" 313 | 314 | 315 | def render_pep440(pieces): 316 | """Build up version string, with post-release "local version identifier". 317 | 318 | Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you 319 | get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty 320 | 321 | Exceptions: 322 | 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] 323 | """ 324 | if pieces["closest-tag"]: 325 | rendered = pieces["closest-tag"] 326 | if pieces["distance"] or pieces["dirty"]: 327 | rendered += plus_or_dot(pieces) 328 | rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) 329 | if pieces["dirty"]: 330 | rendered += ".dirty" 331 | else: 332 | # exception #1 333 | rendered = "0+untagged.%d.g%s" % (pieces["distance"], 334 | pieces["short"]) 335 | if pieces["dirty"]: 336 | rendered += ".dirty" 337 | return rendered 338 | 339 | 340 | def render_pep440_pre(pieces): 341 | """TAG[.post.devDISTANCE] -- No -dirty. 342 | 343 | Exceptions: 344 | 1: no tags. 0.post.devDISTANCE 345 | """ 346 | if pieces["closest-tag"]: 347 | rendered = pieces["closest-tag"] 348 | if pieces["distance"]: 349 | rendered += ".post.dev%d" % pieces["distance"] 350 | else: 351 | # exception #1 352 | rendered = "0.post.dev%d" % pieces["distance"] 353 | return rendered 354 | 355 | 356 | def render_pep440_post(pieces): 357 | """TAG[.postDISTANCE[.dev0]+gHEX] . 358 | 359 | The ".dev0" means dirty. Note that .dev0 sorts backwards 360 | (a dirty tree will appear "older" than the corresponding clean one), 361 | but you shouldn't be releasing software with -dirty anyways. 362 | 363 | Exceptions: 364 | 1: no tags. 0.postDISTANCE[.dev0] 365 | """ 366 | if pieces["closest-tag"]: 367 | rendered = pieces["closest-tag"] 368 | if pieces["distance"] or pieces["dirty"]: 369 | rendered += ".post%d" % pieces["distance"] 370 | if pieces["dirty"]: 371 | rendered += ".dev0" 372 | rendered += plus_or_dot(pieces) 373 | rendered += "g%s" % pieces["short"] 374 | else: 375 | # exception #1 376 | rendered = "0.post%d" % pieces["distance"] 377 | if pieces["dirty"]: 378 | rendered += ".dev0" 379 | rendered += "+g%s" % pieces["short"] 380 | return rendered 381 | 382 | 383 | def render_pep440_old(pieces): 384 | """TAG[.postDISTANCE[.dev0]] . 385 | 386 | The ".dev0" means dirty. 387 | 388 | Eexceptions: 389 | 1: no tags. 0.postDISTANCE[.dev0] 390 | """ 391 | if pieces["closest-tag"]: 392 | rendered = pieces["closest-tag"] 393 | if pieces["distance"] or pieces["dirty"]: 394 | rendered += ".post%d" % pieces["distance"] 395 | if pieces["dirty"]: 396 | rendered += ".dev0" 397 | else: 398 | # exception #1 399 | rendered = "0.post%d" % pieces["distance"] 400 | if pieces["dirty"]: 401 | rendered += ".dev0" 402 | return rendered 403 | 404 | 405 | def render_git_describe(pieces): 406 | """TAG[-DISTANCE-gHEX][-dirty]. 407 | 408 | Like 'git describe --tags --dirty --always'. 409 | 410 | Exceptions: 411 | 1: no tags. HEX[-dirty] (note: no 'g' prefix) 412 | """ 413 | if pieces["closest-tag"]: 414 | rendered = pieces["closest-tag"] 415 | if pieces["distance"]: 416 | rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 417 | else: 418 | # exception #1 419 | rendered = pieces["short"] 420 | if pieces["dirty"]: 421 | rendered += "-dirty" 422 | return rendered 423 | 424 | 425 | def render_git_describe_long(pieces): 426 | """TAG-DISTANCE-gHEX[-dirty]. 427 | 428 | Like 'git describe --tags --dirty --always -long'. 429 | The distance/hash is unconditional. 430 | 431 | Exceptions: 432 | 1: no tags. HEX[-dirty] (note: no 'g' prefix) 433 | """ 434 | if pieces["closest-tag"]: 435 | rendered = pieces["closest-tag"] 436 | rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) 437 | else: 438 | # exception #1 439 | rendered = pieces["short"] 440 | if pieces["dirty"]: 441 | rendered += "-dirty" 442 | return rendered 443 | 444 | 445 | def render(pieces, style): 446 | """Render the given version pieces into the requested style.""" 447 | if pieces["error"]: 448 | return {"version": "unknown", 449 | "full-revisionid": pieces.get("long"), 450 | "dirty": None, 451 | "error": pieces["error"], 452 | "date": None} 453 | 454 | if not style or style == "default": 455 | style = "pep440" # the default 456 | 457 | if style == "pep440": 458 | rendered = render_pep440(pieces) 459 | elif style == "pep440-pre": 460 | rendered = render_pep440_pre(pieces) 461 | elif style == "pep440-post": 462 | rendered = render_pep440_post(pieces) 463 | elif style == "pep440-old": 464 | rendered = render_pep440_old(pieces) 465 | elif style == "git-describe": 466 | rendered = render_git_describe(pieces) 467 | elif style == "git-describe-long": 468 | rendered = render_git_describe_long(pieces) 469 | else: 470 | raise ValueError("unknown style '%s'" % style) 471 | 472 | return {"version": rendered, "full-revisionid": pieces["long"], 473 | "dirty": pieces["dirty"], "error": None, 474 | "date": pieces.get("date")} 475 | 476 | 477 | def get_versions(): 478 | """Get version information or return default if unable to do so.""" 479 | # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have 480 | # __file__, we can work backwards from there to the root. Some 481 | # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which 482 | # case we can only use expanded keywords. 483 | 484 | cfg = get_config() 485 | verbose = cfg.verbose 486 | 487 | try: 488 | return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, 489 | verbose) 490 | except NotThisMethod: 491 | pass 492 | 493 | try: 494 | root = os.path.realpath(__file__) 495 | # versionfile_source is the relative path from the top of the source 496 | # tree (where the .git directory might live) to this file. Invert 497 | # this to find the root from __file__. 498 | for i in cfg.versionfile_source.split('/'): 499 | root = os.path.dirname(root) 500 | except NameError: 501 | return {"version": "0+unknown", "full-revisionid": None, 502 | "dirty": None, 503 | "error": "unable to find root of source tree", 504 | "date": None} 505 | 506 | try: 507 | pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) 508 | return render(pieces, cfg.style) 509 | except NotThisMethod: 510 | pass 511 | 512 | try: 513 | if cfg.parentdir_prefix: 514 | return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) 515 | except NotThisMethod: 516 | pass 517 | 518 | return {"version": "0+unknown", "full-revisionid": None, 519 | "dirty": None, 520 | "error": "unable to compute version", "date": None} 521 | -------------------------------------------------------------------------------- /pygeostatistics/cokrige.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | A cokriging program for a points or blocks on a regular grid. 4 | 5 | Created on Fri Dec 2 2016 6 | """ 7 | from __future__ import division, print_function, absolute_import 8 | import yaml 9 | from itertools import product 10 | import time 11 | from collections import namedtuple 12 | import numpy as np 13 | from scipy import linalg 14 | import matplotlib.pyplot as plt 15 | 16 | from pygeostatistics.yaml_patch import loader_patched 17 | from pygeostatistics.super_block import SuperBlockSearcher 18 | 19 | __author__ = "yuhao" 20 | 21 | class Cokrige(object): 22 | def __init__(self, param_file): 23 | self.param_file = param_file 24 | self._read_params() 25 | self._check_params() 26 | self.property_name = None 27 | self.vr = None 28 | self.rotmat = None 29 | self.estimation = None 30 | self.estimation_variance = None 31 | 32 | self.xdb = None 33 | self.ydb = None 34 | self.zdb = None 35 | 36 | self._2d = False 37 | self.searcher = None 38 | self.const = None 39 | 40 | self._block_covariance = None 41 | self._unbias = None 42 | self.maxcov = None 43 | self._mdt = None 44 | 45 | self.resc = None 46 | 47 | self.nst = list() 48 | self.c0 = list() 49 | self.it = list() 50 | self.cc = list() 51 | self.ang1 = list() 52 | self.ang2 = list() 53 | self.ang3 = list() 54 | self.aa_hmax = list() 55 | self.aa_hmin = list() 56 | self.aa_vert = list() 57 | 58 | def _read_params(self): 59 | with open(self.param_file, "r") as fin: 60 | params = yaml.load(fin, Loader=loader_patched()) 61 | # data file definition 62 | self.datafl = params['datafl'] #: 'testData/test.gslib', 63 | self.nvr = params['nvar'] # number (primary + secondary) 64 | self.ixl = params['icolx'] #: 1, 65 | self.iyl = params['icoly'] #: 2, 66 | self.izl = params['icolz'] 67 | self.ivrl = params['icolvr'] # list 68 | # data limits 69 | self.tmin = params['tmin'] #: -1.0e21, 70 | self.tmax = params['tmax'] #: 1.0e21, 71 | # collocated cokriging or not 72 | self.icolloc = params['icolloc'] # boolean 73 | 74 | # definition of collocated data file 75 | self.secfl = params['secfl'] 76 | self.iclcol = params['iclcol'] 77 | 78 | self.idbg = params['idbg'] #: 3, 79 | self.dbgfl = params['dbgfl'] #: 'kb2d.dbg', 80 | self.outfl = params['outfl'] #: 'out.dat', 81 | # Grid definition 82 | self.nx = params['nx'] #: 50, 83 | self.xmn = params['xmn'] #: 0.5, 84 | self.xsiz = params['xsiz'] #: 1.0, 85 | self.ny = params['ny'] #: 50, 86 | self.ymn = params['ymn'] #: 0.5, 87 | self.ysiz = params['ysiz'] #: 1.0, 88 | self.nz = params['nz'] #: 50, 89 | self.zmn = params['zmn'] #: 0.5, 90 | self.zsiz = params['zsiz'] #: 1.0, 91 | # discretization definition 92 | self.nxdis = params['nxdis'] #: 1, 93 | self.nydis = params['nydis'] #: 1, 94 | self.nzdis = params['nzdis'] #: 1, 95 | # maximum and minimum data points used in kriging 96 | self.ndmin = params['ndmin'] # for both 97 | self.ndmaxp = params['ndmaxp'] # primary 98 | self.ndmaxs = params['ndmaxs'] # secondary 99 | # search radii for primary variable 100 | self.pradius_hmax = params['radius_hmax'] # scalar 101 | self.pradius_hmin = params['radius_hmin'] # scalar 102 | self.pradius_vert = params['radius_vert'] # scalar 103 | # search radii for secondary variables 104 | self.sradius_hmax = params['radius_hmax'] # scalar 105 | self.sradius_hmin = params['radius_hmin'] # scalar 106 | self.sradius_vert = params['radius_vert'] # scalar 107 | # search ellipsoid 108 | self.sang1 = params['sang1'] # scalar 109 | self.sang2 = params['sang2'] # scalar 110 | self.sang3 = params['sang3'] # scalar 111 | # kriging type 112 | self.ktype = params['ikrige'] 113 | # mean values for primary and secondary variables 114 | self.vmean = params['mean'] # list 115 | # Vairography definition 116 | self.vario = params['vario'] # list of dictionaries 117 | 118 | def _fill_check_covariance(self): 119 | self.variography = [dict()] * self.nvr * self.nvr 120 | for var in self.vario: 121 | self.variography[(var['i']-1) * self.nvr + (var['j']-1)] = var 122 | # try fill in symmetric covariance element 123 | for i, j in product(range(self.nvr), range(self.nvr)): 124 | idx1 = i + j * self.nvr 125 | idx2 = j + i * self.nvr 126 | if idx1 == {} and idx2 == {}: 127 | raise ValueError("need variogram between {},{}".format(i, j)) 128 | elif idx1 == {}: 129 | self.variography[idx1] = self.variography[idx2] 130 | elif idx2 == {}: 131 | self.variography[idx2] = self.variography[idx1] 132 | for var in self.variography: 133 | self.nst.append(var['nst']) 134 | self.c0.append(var['c0']) 135 | self.it.append(var['it']) 136 | for idx in range(var['nst']): 137 | self.cc.append(var['cc'][idx]) 138 | self.ang1.append(var['ang1'][idx]) 139 | self.ang2.append(var['ang2'][idx]) 140 | self.ang3.append(var['ang3'][idx]) 141 | self.aa_hmax.append(var['aa_hmax'][idx]) 142 | self.aa_hmin.append(var['aa_hmin'][idx]) 143 | self.aa_vert.append(var['aa_vert'][idx]) 144 | 145 | # check linear model of coregionalization 146 | # check definite positiveness 147 | 148 | def _check_params(self): 149 | # Check search radius 150 | if self.pradius_hmax <= 0: 151 | raise ValueError("pradius_hmax should be larger than zero.") 152 | if self.sradius_hmax <= 0: 153 | raise ValueError("sradius_hmax should be larger than zero.") 154 | # Check data file definition 155 | if self.ixl < 0 and self.nx > 1: 156 | raise ValueError("WARNING: ixl=0 and nx>1 !") 157 | if self.iyl < 0 and self.ny > 1: 158 | raise ValueError("WARNING: iyl=0 and ny>1 !") 159 | if self.izl < 0 and self.nz > 1: 160 | raise ValueError("WARNING: izl=0 and nz>1 !") 161 | if self.ndmin <= 0: 162 | raise ValueError("ndmin too small") 163 | if self.ndmaxs/2 <= self.nvr and self.ktype == 2: 164 | print('WARNING: with traditional ordinary cokriging the '+\ 165 | 'sum of the weights applied to EACH secondary data'+\ 166 | 'is zero. With ndmaxs set low and nvr large the'+\ 167 | 'secondary data will not contribute to the estimate') 168 | 169 | def read_data(self): 170 | "Read a simplified Geo-EAS formatted file." 171 | data_list = None 172 | with open(self.datafl, 'r') as fin: 173 | data_list = fin.readlines() 174 | name = data_list[0].strip() 175 | ncols = int(data_list[1].strip()) 176 | column_name = [item.strip() for item in data_list[2: ncols+2]] 177 | self.property_name = [item for item in column_name 178 | if item not in ['x', 'y', 'z']] 179 | if 'z' not in column_name: 180 | self._2d = True 181 | column_name.append('z') 182 | data_list = [tuple(item.strip().split() + ['0']) 183 | for item in data_list[ncols+2:]] 184 | else: 185 | data_list = [tuple(item.strip().split()) 186 | for item in data_list[ncols+2:]] 187 | data_dtype = np.dtype({ 188 | 'names': column_name, 189 | 'formats': ['f8'] * len(column_name)}) 190 | self.vr = np.array(data_list, dtype=data_dtype) 191 | 192 | def _preprocess(self): 193 | """create variables needed before performing kriging""" 194 | # calculate dimensional constants 195 | cokrige_const = namedtuple('Cokrige_const', 196 | ['PMX', 'MAXNST', 'MAXSB', 'MAXDIS', 197 | 'MAXSAM', 'UNEST', 'MAXVAR', 'MAXARG', 198 | 'MAXCOK']) 199 | maxsbx = 1 200 | if self.nx > 1: 201 | maxsbx = int(self.nx/2) 202 | if maxsbx > 50: 203 | maxsbx = 50 204 | maxsby = 1 205 | if self.ny > 1: 206 | maxsby = int(self.ny/2) 207 | if maxsby > 50: 208 | maxsby = 50 209 | maxsbz = 1 210 | if self.nz > 1: 211 | maxsbz = int(self.nz/2) 212 | if maxsbz > 50: 213 | maxsbz = 50 214 | self.const = cokrige_const( 215 | PMX=999, 216 | MAXNST=4, 217 | MAXSB=(maxsbx, maxsby, maxsbz), 218 | MAXDIS=self.nxdis * self.nydis * self.nzdis, 219 | MAXSAM=self.ndmaxp + self.ndmaxs, 220 | UNEST=np.nan, 221 | MAXVAR=self.nvr, 222 | MAXARG=self.nvr*self.nvr, 223 | MAXCOK=(self.ndmaxp + self.ndmaxs)*self.nvr + self.nvr 224 | ) 225 | # Calculate needed programing variables from input parameters 226 | self.pradsqd = self.pradius_hmax * self.pradius_hmax 227 | self.psanis1 = self.pradius_hmin / self.pradius_hmax 228 | self.psanis2 = self.pradius_vert / self.pradius_hmax 229 | 230 | self.sradsqd = self.sradius_hmax * self.sradius_hmax 231 | self.ssanis1 = self.sradius_hmin / self.sradius_hmax 232 | self.ssanis2 = self.sradius_vert / self.sradius_hmax 233 | 234 | self.anis1 = np.array(self.aa_hmin) / \ 235 | np.maximum(self.aa_hmax, np.finfo(float).eps) 236 | self.anis2 = np.array(self.aa_vert) / \ 237 | np.maximum(self.aa_hmax, np.finfo(float).eps) 238 | 239 | self._fill_check_covariance() 240 | 241 | def _set_rotation(self): 242 | """ 243 | Set up rotation matrix for both anisotropy and searching. 244 | with self.rotmat being an array of 3*3 rotation matrix, the last matrix 245 | in the array are the searching matrix 246 | """ 247 | ang1 = np.append(self.ang1, self.sang1) 248 | ang2 = np.append(self.ang2, self.sang2) 249 | ang3 = np.append(self.ang3, self.sang3) 250 | anis1 = np.append(self.anis1, self.psanis1) 251 | anis2 = np.append(self.anis2, self.psanis2) 252 | anis1 = np.append(anis1, self.ssanis1) 253 | anis2 = np.append(anis2, self.ssanis2) 254 | self.rotmat = np.full((ang1.shape[0], 3, 3), np.nan) 255 | def convert_ang1(ang): 256 | if ang <= 0 and ang < 270: 257 | alpha = np.deg2rad(90 - ang) 258 | else: 259 | alpha = np.deg2rad(450 - ang) 260 | return alpha 261 | v_convert = np.vectorize(convert_ang1) 262 | 263 | alpha = v_convert(ang1) 264 | beta = np.deg2rad(-ang2) 265 | theta = np.deg2rad(ang3) 266 | 267 | sina = np.sin(alpha) 268 | sinb = np.sin(beta) 269 | sint = np.sin(theta) 270 | cosa = np.cos(alpha) 271 | cosb = np.cos(beta) 272 | cost = np.cos(theta) 273 | 274 | afac1 = 1.0 / np.maximum(anis1, np.finfo(float).eps) 275 | afac2 = 1.0 / np.maximum(anis2, np.finfo(float).eps) 276 | self.rotmat[:, 0, 0] = cosb * cosa 277 | self.rotmat[:, 0, 1] = cosb * sina 278 | self.rotmat[:, 0, 2] = -sinb 279 | self.rotmat[:, 1, 0] = afac1 * (-cost * sina + sint * sinb * cosa) 280 | self.rotmat[:, 1, 1] = afac1 * (cost * cosa + sint * sinb * sina) 281 | self.rotmat[:, 1, 2] = afac1 * (sint * cosb) 282 | self.rotmat[:, 2, 0] = afac2 * (sint * sina + cost * sinb * cosa) 283 | self.rotmat[:, 2, 1] = afac2 * (-sint * cosa + cost * sinb * sina) 284 | self.rotmat[:, 2, 2] = afac2 * (cost * cosb) 285 | 286 | def krige(self): 287 | self._fill_check_covariance() 288 | self._preprocess() 289 | # Set up the rotation/anisotropy matrices needed for variogram 290 | # and searching 291 | self._set_rotation() 292 | # compute maximum covariance for the rescaling factor: 293 | self._max_covariance() 294 | # Set up for super block searching: 295 | print("Setting up Super Block Search...") 296 | self._create_searcher() 297 | # Set up discretization points per block 298 | self._block_discretization() 299 | # Find unbias value 300 | self.unbias = self.maxcov 301 | 302 | nxy = self.nx * self.ny 303 | nloop = self.nx * self.ny * self.nz 304 | print("Start working on the kriging...") 305 | # time 306 | t1 = time.time() 307 | ts = 0 308 | percent_od = 0 309 | self.estimation = np.full((nloop,), np.nan) 310 | self.estimation_variance = np.full((nloop,), np.nan) 311 | # MAIN LOOP 312 | for index in range(nloop): 313 | self.iz = index // nxy 314 | self.iy = (index - self.iz * nxy) // self.nx 315 | self.ix = index - self.iz * nxy - self.iy * self.nx 316 | xloc = self.xmn + self.ix * self.xsiz 317 | yloc = self.ymn + self.iy * self.ysiz 318 | zloc = self.zmn + self.iz * self.zsiz 319 | # Search for proximity data 320 | ts_1 = time.time() 321 | self.searcher.search(xloc, yloc, zloc) 322 | ts += time.time() - ts_1 323 | # load nearest data in xa, ya, za, vra, vea 324 | xa = list() 325 | ya = list() 326 | za = list() 327 | vra = list() 328 | iva = list() # which variable 329 | npri = 0 # number of primary data 330 | nsec = 0 # number of secondary data 331 | na = 0 # number of both kinds 332 | for i in range(self.searcher.nclose): 333 | if npri == self.ndmaxp and nsec == self.ndmaxs: 334 | continue 335 | idx = self.searcher.close_samples[i] 336 | # Load primary data 337 | prim = self.vr[self.property_name[0]][idx] 338 | if prim <= self.tmin and prim > self.tmax and \ 339 | npri < self.ndmaxp: 340 | npri += 1 341 | na += 1 342 | xa.append(self.vr['x'][idx] - xloc) 343 | ya.append(self.vr['y'][idx] - yloc) 344 | za.append(self.vr['z'][idx] - zloc) 345 | vra.append(prim) 346 | iva.append(0) 347 | # Load secondary data 348 | sec1 = self.vr[self.property_name[1]][idx] 349 | if sec1 <= self.tmin and sec1 > self.tmax and \ 350 | nsec < self.ndmaxs: 351 | nsec += 1 352 | na += 1 353 | xa.append(self.vr['x'][idx] - xloc) 354 | ya.append(self.vr['y'][idx] - yloc) 355 | za.append(self.vr['z'][idx] - zloc) 356 | if self.ktype != 2: 357 | vra.append(sec1 - self.vmean[1] - self.vmean[0]) 358 | else: 359 | vra.append(sec1) 360 | iva.append(1) 361 | sec2 = self.vr[self.property_name[2]][idx] 362 | if sec2 <= self.tmin and sec2 > self.tmax and \ 363 | nsec < self.ndmaxs: 364 | nsec += 1 365 | na += 1 366 | xa.append(self.vr['x'][idx] - xloc) 367 | ya.append(self.vr['y'][idx] - yloc) 368 | za.append(self.vr['z'][idx] - zloc) 369 | if self.ktype != 2: 370 | vra.append(sec1 - self.vmean[2] - self.vmean[0]) 371 | else: 372 | vra.append(sec1) 373 | iva.append(2) 374 | sec3 = self.vr[self.property_name[3]][idx] 375 | if sec3 <= self.tmin and sec3 > self.tmax and \ 376 | nsec < self.ndmaxs: 377 | nsec += 1 378 | na += 1 379 | xa.append(self.vr['x'][idx] - xloc) 380 | ya.append(self.vr['y'][idx] - yloc) 381 | za.append(self.vr['z'][idx] - zloc) 382 | if self.ktype != 2: 383 | vra.append(sec1 - self.vmean[3] - self.vmean[0]) 384 | else: 385 | vra.append(sec1) 386 | iva.append(3) 387 | 388 | est, estv = self._many_samples(xa, ya, za, vra, na) 389 | self.estimation[index] = est 390 | self.estimation_variance[index] = estv 391 | # print working percentage 392 | percent = np.round(index/nloop*100, decimals=0) 393 | dtime = time.time() - t1 394 | if percent != percent_od: 395 | print("{}% ".format(percent) +\ 396 | "."*20 + "{}s elapsed.".format(np.round(dtime, decimals=3))) 397 | percent_od = percent 398 | print("Kriging Finished.") 399 | print("Time used for searching: {}s".format(ts)) 400 | 401 | def _many_samples(self, xa, ya, za, vra, na): 402 | if self.ktype == 0: 403 | neq = na 404 | elif self.ktype == 1: 405 | neq = na + 1 406 | elif self.ktype == 2: 407 | neq = na + self.nvr 408 | if (neq - na) > na or na < self.ndmin: 409 | print("not enough data.") 410 | return np.nan, np.nan 411 | # left side 412 | left = np.full((neq, neq), np.nan) 413 | # fill the kriging matrix: 414 | for i, j in product(range(na), range(na)): 415 | if np.isnan(left[j, i]): 416 | left[i, j] = self._cova3((xa[i], ya[i], za[i]), 417 | (xa[j], ya[j], za[j])) 418 | else: 419 | left[i, j] = left[j, i] 420 | 421 | 422 | @property 423 | def block_covariance(self): 424 | "return average covariance within block" 425 | if self._block_covariance is None: 426 | if self.ndb <= 1: # point kriging 427 | self._block_covariance = self.unbias 428 | else: 429 | cov = list() 430 | for x1, y1, z1 in zip(self.xdb, self.ydb, self.zdb): 431 | for x2, y2, z2 in zip(self.xdb, self.ydb, self.zdb): 432 | cov.append(self._cova3((x1, y1, z1), (x2, y2, z2))) 433 | cov = np.array(cov).reshape((self.ndb, self.ndb)) 434 | cov[np.diag_indices_from(cov)] -= self.c0 435 | self._block_covariance = np.mean(cov) 436 | return self._block_covariance 437 | 438 | def _block_discretization(self): 439 | self.nxdis = 1 if self.nxdis < 1 else self.nxdis 440 | self.nydis = 1 if self.nydis < 1 else self.nydis 441 | self.nzdis = 1 if self.nzdis < 1 else self.nzdis 442 | self.ndb = self.nxdis * self.nydis * self.nzdis 443 | if self.ndb > self.const.MAXDIS: 444 | raise ValueError("Too many discretization points") 445 | xdis = self.xsiz / max(self.nxdis, 1) 446 | ydis = self.ysiz / max(self.nydis, 1) 447 | zdis = self.zsiz / max(self.nzdis, 1) 448 | self.xdb = np.arange(0, self.nxdis, 1) * xdis + \ 449 | (-0.5 * self.xsiz + 0.5 * xdis) 450 | self.ydb = np.arange(0, self.nydis, 1) * ydis + \ 451 | (-0.5 * self.ysiz + 0.5 * ydis) 452 | self.zdb = np.arange(0, self.nzdis, 1) * zdis + \ 453 | (-0.5 * self.zsiz + 0.5 * zdis) 454 | 455 | def _max_covariance(self): 456 | ''' 457 | Calculate the maximum covariance value (used for zero distances and 458 | for power model covariance): 459 | ''' 460 | self.maxcov = self.c0 461 | for ist in range(self.nst): 462 | if self.it[ist] == 4: 463 | self.maxcov += self.const.PMX 464 | else: 465 | self.maxcov += self.cc[ist] 466 | 467 | def _create_searcher(self): 468 | "Help create and initialize the searcher object" 469 | self.searcher = SuperBlockSearcher() 470 | # initialize required atrributes 471 | # grid definition 472 | self.searcher.nx = self.nx 473 | self.searcher.xmn = self.xmn 474 | self.searcher.xsiz = self.xsiz 475 | self.searcher.ny = self.ny 476 | self.searcher.ymn = self.ymn 477 | self.searcher.ysiz = self.ysiz 478 | self.searcher.nz = self.nz 479 | self.searcher.zmn = self.zmn 480 | self.searcher.zsiz = self.zsiz 481 | # data 482 | self.searcher.vr = self.vr 483 | self.searcher.MAXSB = self.const.MAXSB 484 | # rotation matrix 485 | self.searcher.rotmat = self.rotmat[-1] 486 | self.searcher.radsqd = self.radsqd 487 | # octant search 488 | self.searcher.noct = self.noct 489 | # Setup 490 | self.searcher.setup() 491 | self.searcher.pickup() 492 | # sort data according to superblock number 493 | self.vr = self.vr[self.searcher.sort_index] 494 | 495 | def _cova3(self, point1, point2, ivarg): 496 | """ 497 | Parameters 498 | ---------- 499 | point1, point2: tuple of 3 500 | coordinates of two points 501 | ivarg: 0, 1, 2, 3 502 | 0 for primary, 1,2,3 for secondary 503 | Returns 504 | ------- 505 | cova: scalar 506 | covariance between (x1,y1,z1) and (x2,y2,z2) 507 | """ 508 | # Calculate the maximum covariance 509 | istart = sum(self.nst[:ivarg]) 510 | cmax = self.c0[ivarg] 511 | for iss in range(self.nst[ivarg]): 512 | ist = istart + iss 513 | if self.it[ist] == 4: 514 | cmax += self.const.PMX 515 | else: 516 | cmax += self.cc[ist] 517 | # check for 'zero' distance, return maxcov if so: 518 | hsqd = self._sqdist(point1, point2, self.rotmat[istart]) 519 | if hsqd < np.finfo(float).eps: 520 | cova = cmax 521 | return cova 522 | 523 | # loop over all structures 524 | cova = 0 525 | for ist in range(istart, self.nst[ivarg]): 526 | if ist != 1: 527 | hsqd = self._sqdist(point1, point2, self.rotmat[ist]) 528 | h = np.sqrt(hsqd) 529 | if self.it[ist] == 1: # Spherical 530 | hr = h / self.aa_hmax[ist] 531 | if hr < 1: 532 | cova += self.cc[ist] * (1 - hr * (1.5 - 0.5 * hr * hr)) 533 | elif self.it[ist] == 2: # Exponential 534 | cova += self.cc[ist] * np.exp(-3.0 * h / self.aa_hmax[ist]) 535 | elif self.it[ist] == 3: # Gaussian 536 | cova += self.cc[ist] * \ 537 | np.exp(-3.0 * (h / self.aa_hmax[ist]) * 538 | (h/self.aa_hmax[ist])) 539 | elif self.it[ist] == 4: # Power 540 | cova += self.maxcov - self.cc[ist] * (h**(self.aa_hmax[ist])) 541 | elif self.it[ist] == 5: # Hole Effect 542 | cova += self.cc[ist] * np.cos(h / self.aa_hmax[ist] * np.pi) 543 | return cova 544 | 545 | def _sqdist(self, point1, point2, rotmat): 546 | """ 547 | This routine calculates the anisotropic distance between two points 548 | given the coordinates of each point and a definition of the 549 | anisotropy. 550 | 551 | This method only consider a single anisotropy senario. 552 | 553 | Parameters 554 | ---------- 555 | point1 : tuple 556 | Coordinates of first point (x1,y1,z1) 557 | point2 : tuple 558 | Coordinates of second point (x2,y2,z2) 559 | rotmat : 3*3 ndarray 560 | matrix of rotation for this structure 561 | 562 | Returns 563 | ------- 564 | sqdist : scalar 565 | The squared distance accounting for the anisotropy 566 | and the rotation of coordinates (if any). 567 | """ 568 | dx = point1[0] - point2[0] 569 | dy = point1[1] - point2[1] 570 | dz = point1[2] - point2[2] 571 | sqdist = 0.0 572 | for i in range(3): 573 | cont = rotmat[i, 0] * dx + \ 574 | rotmat[i, 1] * dy + \ 575 | rotmat[i, 2] * dz 576 | sqdist += cont * cont 577 | return sqdist 578 | 579 | if __name__ == '__main__': 580 | test_cokrige = Cokrige("testData/test_cokrige.par") 581 | test_cokrige.read_data() 582 | test_cokrige.krige() 583 | -------------------------------------------------------------------------------- /pygeostatistics/eda.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Exploratory Data Analysis 4 | 5 | Created on Mon Nov 07 2016 6 | """ 7 | from __future__ import division, print_function 8 | import numpy as np 9 | import matplotlib.pyplot as plt 10 | from scipy.spatial.distance import pdist 11 | 12 | 13 | class EDA(): 14 | def __init__(self, datafile): 15 | self.datafl = datafile 16 | self.vr = None 17 | self.property_name = None 18 | self._2d = False 19 | 20 | def preview(self): 21 | print("Data File") 22 | print("---------") 23 | with open(self.datafl, 'r') as fin: 24 | for line in fin.readlines(20): 25 | print(line.strip()) 26 | 27 | def read(self): 28 | data_list = None 29 | with open(self.datafl, 'r') as fin: 30 | data_list = fin.readlines() 31 | self.name = data_list[0].strip() 32 | ncols = int(data_list[1].strip()) 33 | column_name = [item.strip() for item in data_list[2: ncols+2]] 34 | self.property_name = [item for item in column_name 35 | if item not in ['x', 'y', 'z']] 36 | if 'z' not in column_name: 37 | self._2d = True 38 | column_name.append('z') 39 | data_list = [tuple(item.strip().split() + ['0']) 40 | for item in data_list[ncols+2:]] 41 | else: 42 | data_list = [tuple(item.strip().split()) 43 | for item in data_list[ncols+2:]] 44 | data_dtype = np.dtype({ 45 | 'names': column_name, 46 | 'formats': ['f8'] * len(column_name)}) 47 | self.vr = np.array(data_list, dtype=data_dtype) 48 | 49 | def pdf(self, bins=15): 50 | hist, bin_edges = np.histogram(self.vr[self.property_name[0]], 51 | bins=bins) 52 | fig, ax = plt.subplots() 53 | ax.set_title("pdf") 54 | plt.bar(bin_edges[:-1], hist, width=bin_edges[1]-bin_edges[0], 55 | color='red', alpha=0.5) 56 | fig.show() 57 | 58 | def cdf(self): 59 | data = self.vr[self.property_name[0]] 60 | data = np.sort(data) 61 | cdf = np.arange(1, len(data) + 1) / len(data) 62 | fig, ax = plt.subplots() 63 | ax.set_title("cdf") 64 | ax.plot(data, cdf) 65 | fig.show() 66 | 67 | @property 68 | def maximum(self): 69 | return np.max(self.vr[self.property_name[0]]) 70 | 71 | @property 72 | def minimum(self): 73 | return np.min(self.vr[self.property_name[0]]) 74 | 75 | @property 76 | def mean(self): 77 | return np.mean(self.vr[self.property_name[0]]) 78 | 79 | @property 80 | def variance(self): 81 | return np.var(self.vr[self.property_name[0]]) 82 | 83 | @property 84 | def meadian(self): 85 | return np.median(self.vr[self.property_name[0]]) 86 | 87 | @property 88 | def upper_quartile(self): 89 | index = None 90 | even = False 91 | length = self.vr.shape[0] 92 | if length / 2 == 0: 93 | even = True 94 | if even: 95 | index = int((3*length + 2)/4) + 1 96 | else: 97 | index = int((3*length + 3)/4) + 1 98 | return self.vr[self.property_name[0]][index] 99 | 100 | @property 101 | def lower_quartile(self): 102 | index = None 103 | even = False 104 | length = self.vr.shape[0] 105 | if length / 2 == 0: 106 | even = True 107 | if even: 108 | index = int((length + 2)/4) 109 | else: 110 | index = int((length + 1)/4) 111 | return self.vr[self.property_name[0]][index] 112 | @property 113 | def num(self): 114 | return self.vr.shape[0] 115 | 116 | def statistics(self): 117 | print("\nStatistics") 118 | print("-"*10) 119 | print("Number of Data: {}".format(self.num)) 120 | print("Mean: {}".format(self.mean)) 121 | print("Variance: {}".format(self.variance)) 122 | print("-"*10) 123 | print("Minimum: {}".format(self.minimum)) 124 | print("Lower Quartile: {}".format(self.lower_quartile)) 125 | print("Median: {}".format(self.meadian)) 126 | print("Upper Quartile: {}".format(self.upper_quartile)) 127 | print("Maximum: {}".format(self.maximum)) 128 | print("-"*10) 129 | 130 | def distance(self): 131 | num = self.vr.shape[0] 132 | dist = pdist(np.concatenate((self.vr['x'].reshape((num, 1)), 133 | self.vr['y'].reshape((num, 1))), axis=1)) 134 | print("\nDistance\n"+"-"*8) 135 | print("Max distance: {}\nMin distance: {}".format(np.max(dist), 136 | np.min(dist))) 137 | print("-"*8) 138 | print("X: {} - {}".format(np.min(self.vr['x']), np.max(self.vr['x']))) 139 | print("Y: {} - {}".format(np.min(self.vr['y']), np.max(self.vr['y']))) 140 | if self._2d is False: 141 | print("Z: {} - {}".format(np.min(self.vr['z']), 142 | np.max(self.vr['z']))) 143 | 144 | def view2d(self, pname=None): 145 | pname = self.property_name[0] if pname is None else pname 146 | if self._2d is False: 147 | print("3D data, use view3d() instead.") 148 | else: 149 | fig, ax = plt.subplots() 150 | abscissa = self.vr['x'] 151 | ordinate = self.vr['y'] 152 | sc = ax.scatter(abscissa, ordinate, c=self.vr[pname], 153 | cmap='jet') 154 | ax.set(xlim=(np.min(abscissa), np.max(abscissa)), 155 | ylim=(np.min(ordinate), np.max(ordinate)), 156 | xlabel="X (m)", ylabel="Y (m)", 157 | title="Data Scatter", aspect='equal', 158 | facecolor='grey') 159 | fig.colorbar(sc) 160 | fig.show() 161 | 162 | if __name__ == "__main__": 163 | eda = EDA("testData/test.gslib") 164 | # eda.preview() 165 | eda.read() 166 | eda.pdf() 167 | eda.cdf() 168 | eda.statistics() 169 | eda.distance() 170 | eda.view2d() 171 | -------------------------------------------------------------------------------- /pygeostatistics/gam.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Compute Variogram on regularly spaced data 4 | 5 | Created on Tue Nov 03 2016 6 | """ 7 | __author__ = "yuhao" 8 | 9 | import yaml 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | 13 | from pygeostatistics.yaml_patch import loader_patched 14 | 15 | 16 | class Gam(): 17 | def __init__(self, param_file): 18 | self.param_file = param_file 19 | self._read_params() 20 | self._check_params() 21 | self.vr = None 22 | self.gam = None 23 | self.npair = None 24 | self.directions = None 25 | self.mean = None 26 | self.variance = None 27 | 28 | def _read_params(self): 29 | with open(self.param_file, "r") as fin: 30 | params = yaml.load(fin, Loader=loader_patched()) 31 | self.datafl = params['datafl'] 32 | self.nvar = params['nvar'] 33 | # 'ivar' 34 | self.tmin = params['tmin'] 35 | self.tmax = params['tmax'] 36 | # 'outfl' 37 | # 'igrid' 38 | self.nx = params['nx'] 39 | self.xmn = params['xmn'] 40 | self.xsiz = params['xsiz'] 41 | self.ny = params['ny'] 42 | self.ymn = params['ymn'] 43 | self.ysiz = params['ysiz'] 44 | self.nz = params['nz'] 45 | self.zmn = params['zmn'] 46 | self.zsiz = params['zsiz'] 47 | self.ndir = params['ndir'] 48 | self.nlag = params['nlag'] 49 | self.ixd = params['ixd'] 50 | self.iyd = params['iyd'] 51 | self.izd = params['izd'] 52 | self.standardize = params['standardize'] 53 | self.nvarg = params['nvarg'] 54 | self.ivtail = params['ivtail'] 55 | self.ivhead = params['ivhead'] 56 | self.ivtype = params['ivtype'] 57 | 58 | def read_data(self): 59 | data_list = list() 60 | name = None 61 | ncols = None 62 | column_name = None 63 | data_dtype = None 64 | with open(self.datafl, 'r') as fin: 65 | name = fin.readline() 66 | ncols = int(fin.readline()) 67 | column_name = list() 68 | for i in range(ncols): 69 | column_name.append(fin.readline().rstrip('\n')) 70 | data_dtype = np.dtype({ 71 | 'names': column_name, 72 | 'formats': ['f8'] * ncols}) 73 | for line in fin: 74 | data = line.split() 75 | for i in range(ncols): 76 | data[i] = float(data[i]) 77 | data = tuple(data) 78 | data_list.append(data) 79 | input_data = np.array(data_list, dtype=data_dtype) 80 | self.vr = input_data[column_name[-1]] 81 | self.vr = self.vr.reshape((self.nx, self.ny, self.nz)) 82 | 83 | def _check_params(self): 84 | try: 85 | # self.datafl 86 | if not isinstance(type(self.nvar), int) != 'int' or self.nvar < 1: 87 | raise ValueError("wrong value with number of variables") 88 | # 'ivar' 89 | # self.tmin = params['tmin'] 90 | # self.tmax = params['tmax'] 91 | # # 'outfl' 92 | # # 'igrid' 93 | # self.nx = params['nx'] 94 | # self.xmn = params['xmn'] 95 | # self.xsiz = ['xsiz'] 96 | # self.ny = params['ny'] 97 | # self.ymn = params['ymn'] 98 | # self.ysiz = ['ysiz'] 99 | # self.nz = params['nz'] 100 | # self.zmn = params['zmn'] 101 | # self.zsiz = ['zsiz'] 102 | # self.ndir = ['ndir'] 103 | # self.nlag = ['nlag'] 104 | # self.ixd = params['ixd'] 105 | # self.iyd = params['iyd'] 106 | # self.izd = params['izd'] 107 | # # 'standardize':True, 108 | # self.nvarg = ['nvarg'] 109 | # self.ivtail = ['ivtail'] 110 | # self.ivhead = ['ivhead'] 111 | # self.ivtype = ['ivtype'] 112 | except Exception as inst: 113 | print(inst) 114 | 115 | def _preprocess(self): 116 | # put three input directional vector into a direction list 117 | self.directions = list() 118 | for ix, iy, iz in zip(self.ixd, self.iyd, self.izd): 119 | self.directions.append((ix, iy, iz)) 120 | self.mean = np.mean(self.vr) 121 | self.variance = np.var(self.vr) 122 | 123 | def gamma(self): 124 | """ 125 | This subroutine computes any of eight different measures of spatial 126 | continuity for regular spaced 3-D data. Missing values are allowed 127 | and the grid need not be cubic. 128 | """ 129 | # initialize the summation arrays for each direction, variogram and lag 130 | self._preprocess() 131 | 132 | head_data = list() 133 | tail_data = list() 134 | for i in range(self.ndir): 135 | head_data.append(list()) 136 | tail_data.append(list()) 137 | for j in range(self.nlag): 138 | head_data[i].append(list()) 139 | tail_data[i].append(list()) 140 | # loop over all points on the grid 141 | coordination = np.meshgrid( 142 | np.arange(self.nx), np.arange(self.ny), np.arange(self.nz)) 143 | # loop over all points on the grid 144 | for ix, iy, iz in zip( 145 | coordination[0].flatten(), 146 | coordination[1].flatten(), 147 | coordination[2].flatten()): 148 | # loop over each direction 149 | for idir, (ixd, iyd, izd) in enumerate(self.directions): 150 | ix1, iy1, iz1 = ix + ixd, iy + iyd, iz + izd 151 | ilag = 0 152 | # add indexes of every eligible pair to 153 | # index_pairs[nlag][npairs] list 154 | while ix1 < self.nx and iy1 < self.ny and iz1 < self.nz: 155 | if ilag < self.nlag: 156 | head_value = self.vr[(ix, iy, iz)] 157 | tail_value = self.vr[(ix1, iy1, iz1)] 158 | # if head_value > s 159 | if not np.isnan(head_value) and \ 160 | not np.isnan(tail_value): 161 | head_data[idir][ilag].append(head_value) 162 | tail_data[idir][ilag].append(tail_value) 163 | else: 164 | break 165 | ilag = ilag + 1 166 | ix1 = ix1 + ixd 167 | iy1 = iy1 + iyd 168 | iz1 = iz1 + izd 169 | # after figure out all the index pairs, use the index to get data 170 | 171 | self.gam = np.zeros((self.ndir, self.nlag)) 172 | self.npair = np.zeros((self.ndir, self.nlag), dtype='>i4') 173 | # hm = np.zeros((self.ndir, self.nlag)) 174 | # tm = np.zeros((self.ndir, self.nlag)) 175 | # hv = np.zeros((self.ndir, self.nlag)) 176 | # tv = np.zeros((self.ndir, self.nlag)) 177 | 178 | for i, (head_lags, tail_lags) in enumerate(zip(head_data, tail_data)): 179 | for j, (head_lag, tail_lag) in enumerate(zip(head_lags, tail_lags)): 180 | self.npair[i][j] = len(head_lag) 181 | for hd, td in zip(head_lag, tail_lag): 182 | self.gam[i][j] = self.gam[i][j] + (hd - td)**2 183 | self.gam /= self.npair 184 | self.gam *= 0.5 # * self.gam 185 | if self.standardize is True: 186 | self.gam /= self.variance 187 | 188 | def graph(self): 189 | fig, axes = plt.subplots(nrows=self.ndir, ncols=1) 190 | for ax, data, (idx, idy, idz) in zip(axes, self.gam, self.directions): 191 | real_lag_value = np.sqrt((idx * self.xsiz)**2 + 192 | (idy * self.ysiz)**2 + 193 | (idz * self.zsiz)**2) 194 | temp = np.ones(self.nlag) * real_lag_value 195 | abscissa = temp * np.arange(1, self.nlag + 1) 196 | ax.plot(abscissa, data, linestyle='--', marker='s', 197 | fillstyle='none', color='black', markeredgecolor='blue') 198 | # ax.step(abscissa, data, label='pre (default)') 199 | # ax.scatter(abscissa, data, marker='D') 200 | ax.set_xlim(left=0, right=real_lag_value*(self.nlag+1)) 201 | ax.set_ylim(bottom=0) 202 | ax.set_title("directional vector [{}, {}, {}]".format( 203 | idx, idy, idz)) 204 | ax.set_ylabel("Variogram $\gamma(h)$") 205 | ax.set_xlabel("Distance") 206 | fig.tight_layout() 207 | plt.draw() 208 | 209 | if __name__ == "__main__": 210 | data_analysis = Gam("testData/xihuSmall_sparse_gam.par") 211 | data_analysis.read_data() 212 | data_analysis.gamma() 213 | data_analysis.graph() 214 | -------------------------------------------------------------------------------- /pygeostatistics/gamv.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Compute Variogram on irregularly spaced data 4 | 5 | Created on Sun Nov 06 2016 6 | """ 7 | __author__ = "yuhao" 8 | 9 | import yaml 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | 13 | from pygeostatistics.yaml_patch import loader_patched 14 | 15 | 16 | class Gamv(): 17 | def __init__(self, param_file): 18 | self.param_file = param_file 19 | self._read_params() 20 | self._check_params() 21 | self.vr = None 22 | self.gam = None 23 | self.npair = None 24 | self.distance = None 25 | self.mean = None 26 | self.variance = None 27 | self.hm = None 28 | self.tm = None 29 | self.property_name = None 30 | self.lag_interval = None 31 | 32 | def _read_params(self): 33 | with open(self.param_file, "r") as fin: 34 | params = yaml.load(fin, Loader=loader_patched()) 35 | self.datafl = params['datafl'] 36 | self.icolx = params['icolx'] #: 1, 37 | self.icoly = params['icoly'] #: 2, 38 | self.icolz = params['icolz'] #: 0, 39 | self.nvar = params['nvar'] # : 2, 40 | self.ivar = params['ivar'] # : [3, 4], 41 | self.tmin = params['tmin'] # : -1.0e21, 42 | self.tmax = params['tmax'] # : 1.0e21, 43 | self.outfl = params['outfl'] # : 'out.dat', 44 | self.nlag = params['nlag'] # : 10, 45 | self.xlag = params['xlag'] # : 5.0, 46 | self.xltol = params['xltol'] # : 3.0, 47 | self.ndir = params['ndir'] # : 3, 48 | self.azm = params['azm'] # : [0.0, 0.0, 90.], 49 | self.atol = params['atol'] # : [90.0, 22.5, 22.5], 50 | self.bandwh = params['bandwh'] # : [50.0, 25.0, 25.0], 51 | self.dip = params['dip'] # : [0.0, 0.0, 0.0], 52 | self.dtol = params['dtol'] # : [90.0, 22.5, 22.5], 53 | self.bandwd = params['bandwd'] # : [50.0, 25.0, 25.0], 54 | self.standardize = params['standardize'] # : False, 55 | self.nvarg = params['nvarg'] # : 3, 56 | self.ivtail = params['ivtail'] # : [1, 1, 2], 57 | self.ivhead = params['ivhead'] # : [1, 1, 2], 58 | self.ivtype = params['ivtype'] # : [1, 3, 1] 59 | 60 | def _check_params(self): 61 | # # check lag tolerance 62 | # if self.xltol <= 0: 63 | # self.xltol = 0.5 * self.xlag 64 | # # check azimuth tolerance 65 | # for i, item in enumerate(self.atol): 66 | # if item <= 0: 67 | # self.atol[i] = 45.0 68 | # # check dip tolerance 69 | # for i, item in enumerate(self.dtol): 70 | # if item <= 0: 71 | # self.dtol[i] = 45.0 72 | if self.ndir != len(self.azm): 73 | raise ValueError('number of directions does not match provided \ 74 | azimuth.') 75 | 76 | def read_data(self): 77 | data_list = None 78 | with open(self.datafl, 'r') as fin: 79 | data_list = fin.readlines() 80 | name = data_list[0].strip() 81 | ncols = int(data_list[1].strip()) 82 | column_name = [item.strip() for item in data_list[2: ncols+2]] 83 | self.property_name = [item for item in column_name 84 | if item not in ['x', 'y', 'z']] 85 | if 'z' not in column_name: 86 | column_name.append('z') 87 | data_list = [tuple(item.strip().split() + ['0']) 88 | for item in data_list[ncols+2:]] 89 | else: 90 | data_list = [tuple(item.strip().split()) 91 | for item in data_list[ncols+2:]] 92 | data_dtype = np.dtype({ 93 | 'names': column_name, 94 | 'formats': ['f8'] * len(column_name)}) 95 | self.vr = np.array(data_list, dtype=data_dtype) 96 | 97 | def _preprocess(self): 98 | self.mean = np.mean(self.vr[self.property_name[0]]) 99 | self.variance = np.var(self.vr[self.property_name[0]]) 100 | # check lag tolerance 101 | if self.xltol <= 0: 102 | self.xltol = 0.5 * self.xlag 103 | # check azimuth tolerance 104 | # for i, item in enumerate(self.atol): 105 | # if item <= 0: 106 | # self.atol[i] = 45.0 107 | self.atol = [45.0 if item <= 0 else item for item in self.atol] 108 | # check dip tolerance 109 | # for i, item in enumerate(self.dtol): 110 | # if item <= 0: 111 | # self.dtol[i] = 45.0 112 | self.dtol = [45.0 if item <= 0 else item for item in self.dtol] 113 | 114 | def gamv(self): 115 | self._check_params() 116 | self._preprocess() 117 | self.gam = np.zeros((self.ndir, self.nlag+2)) 118 | self.npair = np.zeros((self.ndir, self.nlag+2), dtype='>i4') 119 | self.distance = np.zeros((self.ndir, self.nlag+2)) 120 | self.tm = np.zeros((self.ndir, self.nlag+2)) 121 | self.hm = np.zeros((self.ndir, self.nlag+2)) 122 | 123 | 124 | # tail_data = list() 125 | # head_data = list() 126 | # for i in range(self.ndir): 127 | # tail_data.append(list()) 128 | # head_data.append(list()) 129 | # for j in range(self.nlag+2): 130 | # tail_data[i].append(list()) 131 | # head_data[i].append(list()) 132 | # calculate the value interval of each lag 133 | # method 1 134 | # temp = np.arange(0, self.nlag+2, 1.) 135 | # temp -= 1 136 | # temp[0] = 0 137 | # temp *= self.xlag 138 | # length, = temp.shape 139 | # temp.reshape((length, 1)) 140 | # upper = temp + self.xltol 141 | # upper[0][0] = 0 142 | # lower = temp - self.xltol 143 | # lower[0][0] = 0 144 | # self.lag_interval = np.concatenate((lower, upper), axis=1) 145 | # method 2 146 | self.lag_interval = np.ones((self.nlag+2, 2)) 147 | self.lag_interval[:2, :] = 0 148 | self.lag_interval *= self.xlag 149 | scale = np.arange(0, self.nlag+2, 1) - 1 150 | self.lag_interval[:, 0] *= scale 151 | self.lag_interval[:, 1] *= scale 152 | self.lag_interval[:, 0] -= self.xltol 153 | self.lag_interval[0, 0] = 0.0 154 | self.lag_interval[:, 1] += self.xltol 155 | self.lag_interval[0, 1] = 0.0 156 | # The mathematical azimuth is measured counterclockwise from EW and 157 | # not clockwise from NS as the conventional azimuth is: 158 | # name it azmuth instead of azimuth 159 | azmuth = np.deg2rad(90.0 - np.array(self.azm)) 160 | uvxazm = np.cos(azmuth) 161 | uvyazm = np.sin(azmuth) 162 | csatol = np.cos(np.deg2rad(self.atol)) 163 | # for i, az in enumerate(self.atol): 164 | # if az == 90: 165 | # csatol[i] = 0 166 | # The declination is measured positive from vertical (up) rather than 167 | # negative down from horizontal: 168 | declin = np.deg2rad(90.0 - np.array(self.dip)) 169 | uvzdec = np.cos(declin) 170 | uvhdec = np.sin(declin) 171 | csdtol = np.cos(np.deg2rad(self.dtol)) 172 | # for i, di in enumerate(self.dtol): 173 | # if di == 90: 174 | # csdtol[i] = 0 175 | # square of maxmium distance 176 | dismxs = ((self.nlag + 0.5 - np.finfo('float').eps) * self.xlag)**2 177 | num_of_data = int(self.vr.shape[0]) 178 | ijlist = list() 179 | for i in range(num_of_data - 1): 180 | for j in range(i+1, num_of_data): 181 | ijlist.append((i, j)) 182 | for ijtuple in ijlist: 183 | i, j = ijtuple 184 | # i_coor = list(self.vr[i])[:-1] 185 | # j_coor = list(self.vr[j])[:-1] 186 | # calculate the lag corresponding to the current pair 187 | # dx = j_coor[0] - i_coor[0] 188 | # dy = j_coor[1] - i_coor[1] 189 | # dz = j_coor[2] - i_coor[2] 190 | dx = self.vr['x'][j] - self.vr['x'][i] 191 | dy = self.vr['y'][j] - self.vr['y'][i] 192 | dz = self.vr['z'][j] - self.vr['z'][i] 193 | # square of lag h 194 | hs = dx**2 + dy**2 + dz**2 195 | if hs > dismxs: 196 | # print("skip pair {},{} for maxdistance".format(i, j)) 197 | continue # skip to next pair 198 | h = np.sqrt(hs) 199 | # determine which lag 200 | lag_num = list() # could be in two lags 201 | for k, lg_in in enumerate(self.lag_interval): 202 | if h >= lg_in[0] and h <= lg_in[1]: 203 | lag_num.append(k) 204 | if len(lag_num) == 0: 205 | # print("skip pair {},{} for no lag".format(i, j)) 206 | continue # skip if cannot find which lag 207 | 208 | for idir in range(self.ndir): 209 | omni = self.atol[idir] >= 90.0 210 | # check for an acceptable azimuth angle: 211 | dxy = np.sqrt(max(dx**2 + dy**2, 0.0)) 212 | if dxy < np.finfo('float').eps: 213 | dcazm = 1.0 214 | else: 215 | dcazm = (dx*uvxazm[idir] + dy*uvyazm[idir])/dxy 216 | if np.abs(dcazm) < csatol[idir]: 217 | # print("skip pair {},{} for az".format(i, j)) 218 | continue 219 | # check for the horizontal bandwidth criteria (maximium 220 | # deviation perpendicular to the specified direction azimuth): 221 | band = uvxazm[idir]*dy - uvyazm[idir]*dx 222 | if np.abs(band) > self.bandwh[idir] and not omni: 223 | # print("skip pair {},{} for az bwd".format(i, j)) 224 | continue 225 | # check for an acceptable dip angle: 226 | if dcazm < 0: 227 | dxy = -dxy 228 | if lag_num[0] == 0: 229 | dcdec = 0 230 | else: 231 | dcdec = (dxy*uvhdec[idir] + dz*uvzdec[idir])/h 232 | if np.abs(dcdec) < csdtol[idir]: 233 | # print("skip pair {},{} for dip".format(i, j)) 234 | continue 235 | # check the vertical bandwidth criteria (maximium deviation 236 | # perpendicular to the specified dip direction): 237 | band = uvhdec[idir]*dz - uvzdec[idir]*dxy 238 | if np.abs(band) > self.bandwd[idir] and not omni: 239 | # print("skip pair {},{} for dip bwd".format(i, j)) 240 | continue 241 | # check whether or not an omi-directional variogram is being 242 | # computed: 243 | # omni = False 244 | # if self.atol[idir] >= 90.0: 245 | # omni = True 246 | omni = self.atol[idir] >= 90.0 247 | # then this pair is acceptable, proceed to compute variogram 248 | # sort out which is tail and head: 249 | if dcazm >= 0 and dcdec <= 0: 250 | # vrh = list(self.vr[i])[-1] 251 | # vrt = list(self.vr[j])[-1] 252 | vrh = self.vr[self.property_name[0]][i] 253 | vrt = self.vr[self.property_name[0]][j] 254 | if omni: 255 | # vrtpr = list(self.vr[i])[-1] 256 | # vrhpr = list(self.vr[j])[-1] 257 | vrtpr = self.vr[self.property_name[0]][i] 258 | vrhpr = self.vr[self.property_name[0]][j] 259 | else: 260 | # vrh = list(self.vr[j])[-1] 261 | # vrt = list(self.vr[i])[-1] 262 | vrh = self.vr[self.property_name[0]][j] 263 | vrt = self.vr[self.property_name[0]][i] 264 | if omni: 265 | # vrtpr = list(self.vr[j])[-1] 266 | # vrhpr = list(self.vr[i])[-1] 267 | vrtpr = self.vr[self.property_name[0]][j] 268 | vrhpr = self.vr[self.property_name[0]][i] 269 | # reject this pair on the basis of missing values: 270 | if vrt < self.tmin or vrh < self.tmin or\ 271 | vrt > self.tmax or vrh > self.tmax: 272 | continue 273 | # COMPUTE THE APPRORIATE "VARIOGRAM" MEASURE 274 | # ***Semivariogram*** 275 | for ilag in lag_num: 276 | self.npair[idir][ilag] += 1 277 | self.distance[idir][ilag] += h 278 | self.tm[idir][ilag] += vrt 279 | self.hm[idir][ilag] += vrh 280 | self.gam[idir][ilag] += (vrh - vrt)**2 281 | if omni: 282 | if vrtpr >= self.tmin or vrhpr >= self.tmin or\ 283 | vrtpr < self.tmax or vrhpr < self.tmax: 284 | self.npair[idir][ilag] += 1 285 | self.distance[idir][ilag] += h 286 | self.tm[idir][ilag] += vrtpr 287 | self.hm[idir][ilag] += vrhpr 288 | self.gam[idir][ilag] += (vrhpr - vrtpr)**2 289 | self.gam /= self.npair 290 | if self.standardize is True: 291 | self.gam /= self.variance 292 | self.gam /= 2 293 | 294 | self.distance /= self.npair 295 | self.tm /= self.npair 296 | self.hm /= self.npair 297 | 298 | def graph(self): 299 | abscissa = np.arange(0, self.nlag+1, 1.0) 300 | abscissa *= self.xlag 301 | fig, axes = plt.subplots(nrows=self.ndir, ncols=1) 302 | if isinstance(axes, list): 303 | for i, ax in enumerate(axes): 304 | ordinate = np.insert(self.gam[i][2:], 0, None) 305 | ax.scatter(abscissa, ordinate) 306 | ax.set_title(r"Azimuth: {}$^\circ$({}$^\circ$), ".format( 307 | self.azm[i], self.atol[i]) + 308 | r"Dip: {}$^\circ$({}$^\circ$)".format( 309 | self.dip[i], self.dtol[i])) 310 | ax.set_ylim(bottom=0) 311 | ax.set_xlim(left=0) 312 | ax.grid() 313 | else: 314 | ordinate = np.insert(self.gam[0][2:], 0, None) 315 | axes.scatter(abscissa, ordinate) 316 | axes.set_title(r"Azimuth: {}$^\circ$({}$^\circ$), ".format( 317 | self.azm[0], self.atol[0]) + 318 | r"Dip: {}$^\circ$({}$^\circ$)".format( 319 | self.dip[0], self.dtol[0])) 320 | axes.set_ylim(bottom=0) 321 | axes.set_xlim(left=0) 322 | axes.grid() 323 | fig.tight_layout() 324 | # plt.draw() 325 | return fig, axes 326 | 327 | 328 | if __name__ == "__main__": 329 | data_analysis = Gamv("testData/xihuSmall_sparse_gamv.par") 330 | data_analysis.read_data() 331 | data_analysis.gamv() 332 | data_analysis.graph() 333 | -------------------------------------------------------------------------------- /pygeostatistics/gslib_reader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Read gslib file format 4 | 5 | Created on Wen Sep 5th 2018 6 | """ 7 | from __future__ import absolute_import, division, print_function 8 | 9 | __author__ = "yuhao" 10 | 11 | import numpy as np 12 | import pandas as pd 13 | from scipy.spatial.distance import pdist 14 | from mpl_toolkits.mplot3d import Axes3D 15 | 16 | 17 | class SpatialData(object): 18 | def __init__(self, file_path): 19 | self.datafl = file_path 20 | self.vr = None 21 | self.property_name = None 22 | self._2d = False 23 | self._read_data() 24 | 25 | def _read_data(self): 26 | """ 27 | read gslib file 28 | """ 29 | column_name = [] 30 | with open(self.datafl, 'r') as fin: 31 | _ = fin.readline().strip() 32 | ncols = int(fin.readline().strip()) 33 | for _ in range(ncols): 34 | column_name.append(fin.readline().strip()) 35 | self.property_name = [item for item in column_name 36 | if item not in ['x', 'y', 'z']] 37 | df = pd.read_csv(self.datafl, sep='\t', header=None, names=column_name, 38 | skiprows=ncols+2) 39 | if 'z' not in column_name: 40 | self._2d = True 41 | column_name.append('z') 42 | df['z'] = 0 43 | self.df = df 44 | 45 | data_dtype = np.dtype({ 46 | 'names': column_name, 47 | 'formats': ['f8'] * len(column_name)}) 48 | 49 | self.vr = np.core.records.fromarrays( 50 | df.values.transpose(), dtype=data_dtype) 51 | 52 | def preview(self): 53 | return self.vr.head(20) 54 | 55 | def pdf(self, ax, bins=15): 56 | hist, bin_edges = np.histogram(self.vr[self.property_name[0]], 57 | bins=bins) 58 | ax.set_title("pdf") 59 | ax.bar(bin_edges[:-1], hist, width=bin_edges[1]-bin_edges[0], 60 | color='red', alpha=0.5) 61 | 62 | def cdf(self, ax): 63 | data = self.vr[self.property_name[0]] 64 | data = np.sort(data) 65 | cdf = np.arange(1, len(data) + 1) / len(data) 66 | ax.set_title("cdf") 67 | ax.plot(data, cdf) 68 | 69 | @property 70 | def maximum(self): 71 | return self.df[self.property_name[0]].max() 72 | 73 | @property 74 | def minimum(self): 75 | return self.df[self.property_name[0]].min() 76 | 77 | @property 78 | def mean(self): 79 | return self.df[self.property_name[0]].mean() 80 | 81 | @property 82 | def variance(self): 83 | return self.df[self.property_name[0]].var() 84 | 85 | @property 86 | def meadian(self): 87 | return np.median(self.vr[self.property_name[0]]) 88 | 89 | @property 90 | def upper_quartile(self): 91 | return self.df[self.property_name[0]].quantile(0.75) 92 | 93 | @property 94 | def lower_quartile(self): 95 | return self.df[self.property_name[0]].quantile(0.25) 96 | 97 | @property 98 | def num(self): 99 | return self.vr.shape[0] 100 | 101 | def distance(self): 102 | num = self.vr.shape[0] 103 | return pdist(np.concatenate((self.vr['x'].reshape((num, 1)), 104 | self.vr['y'].reshape((num, 1))), axis=1)) 105 | 106 | @property 107 | def summary(self): 108 | return ( 109 | "Summary\n" 110 | "-------\n" 111 | "Number of Points: {}\n" 112 | "Mean: {}\n" 113 | "Variance: {}\n" 114 | "Minimum: {}\n" 115 | "Lower Quartile: {}\n" 116 | "Median: {}\n" 117 | "Upper Quartile: {}\n" 118 | "Maximum: {}\n").format( 119 | self.num, 120 | self.mean, 121 | self.variance, 122 | self.minimum, 123 | self.lower_quartile, 124 | self.meadian, 125 | self.upper_quartile, 126 | self.maximum) 127 | 128 | def scatter(self, ax, prop=None): 129 | """ 130 | Plot scatter of data points on given axis 131 | 132 | Parameters 133 | ---------- 134 | ax : AxesSubplot or Axes3DSubplot 135 | axis on which the scatter plot is drawn 136 | prop : str 137 | property to display with colormap 138 | """ 139 | sc = None 140 | prop = self.property_name[0] if prop is None else prop 141 | 142 | if not self._2d and isinstance(ax, Axes3D): 143 | sc = ax.scatter( 144 | self.vr['x'], self.vr['y'], self.vr['z'], 145 | c=prop) 146 | else: 147 | sc = ax.scatter( 148 | self.vr['x'], self.vr['y'], c=prop) 149 | return sc 150 | -------------------------------------------------------------------------------- /pygeostatistics/krige2d.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | A straightforward 2D kriging program 4 | 5 | Created on Fri Nov 11 2016 6 | """ 7 | __author__ = "yuhao" 8 | 9 | import yaml 10 | import numpy as np 11 | from scipy import linalg 12 | import matplotlib.pyplot as plt 13 | from itertools import product 14 | import time 15 | 16 | from pygeostatistics.yaml_patch import loader_patched 17 | 18 | 19 | class Krige2d(): 20 | def __init__(self, param_file): 21 | self.param_file = param_file 22 | self._read_params() 23 | self._check_params() 24 | self.property_name = None 25 | self.vr = None 26 | self.maxcov = None 27 | self.rotmat = None 28 | self.estimation = None 29 | self.estimation_variance = None 30 | 31 | self.xdb = None 32 | self.ydb = None 33 | 34 | self._block_covariance = None 35 | self._unbias = None 36 | 37 | self._2d = False 38 | 39 | def _read_params(self): 40 | with open(self.param_file) as fin: 41 | params = yaml.load(fin, Loader=loader_patched()) 42 | self.datafl = params['datafl'] #: 'testData/test.gslib', 43 | self.icolx = params['icolx'] #: 1, 44 | self.icoly = params['icoly'] #: 2, 45 | self.icolvr = params['icolvr'] #: 0, 46 | self.tmin = params['tmin'] #: -1.0e21, 47 | self.tmax = params['tmax'] #: 1.0e21, 48 | self.idbg = params['idbg'] #: 3, 49 | self.dbgfl = params['dbgfl'] #: 'kb2d.dbg', 50 | self.outfl = params['outfl'] #: 'out.dat', 51 | self.nx = params['nx'] #: 50, 52 | self.xmn = params['xmn'] #: 0.5, 53 | self.xsiz = params['xsiz'] #: 1.0, 54 | self.ny = params['ny'] #: 50, 55 | self.ymn = params['ymn'] #: 0.5, 56 | self.ysiz = params['ysiz'] #: 1.0, 57 | self.nxdis = params['nxdis'] #: 1, 58 | self.nydis = params['nydis'] #: 1, 59 | self.ndmin = params['ndmin'] #: , 60 | self.ndmax = params['ndmax'] #: , 61 | self.radius = params['radius'] #: , 62 | self.ktype = params['isk'] #: , 63 | self.skmean = params['skmean'] #: , 64 | self.nst = params['nst'] #: 1, 65 | self.c0 = params['c0'] #: 0, 66 | self.it = params['it'] #: [], 67 | self.cc = params['cc'] #: [], 68 | self.azm = params['azm'] #: [], 69 | self.a_max = params['a_max'] #:[], 70 | self.a_min = params['a_min'] #: [] 71 | 72 | def read_data(self): 73 | data_list = None 74 | with open(self.datafl, 'r') as fin: 75 | data_list = fin.readlines() 76 | name = data_list[0].strip() 77 | ncols = int(data_list[1].strip()) 78 | column_name = [item.strip() for item in data_list[2: ncols+2]] 79 | self.property_name = [item for item in column_name 80 | if item not in ['x', 'y', 'z']] 81 | if 'z' not in column_name: 82 | self._2d = True 83 | column_name.append('z') 84 | data_list = [tuple(item.strip().split() + ['0']) 85 | for item in data_list[ncols+2:]] 86 | else: 87 | data_list = [tuple(item.strip().split()) 88 | for item in data_list[ncols+2:]] 89 | data_dtype = np.dtype({ 90 | 'names': column_name, 91 | 'formats': ['f8'] * len(column_name)}) 92 | self.vr = np.array(data_list, dtype=data_dtype) 93 | 94 | def _check_params(self): 95 | for vtype, a_range in zip(self.it, self.a_max): 96 | if vtype not in np.arange(1, 6): 97 | raise ValueError("INVALID variogram number {}".format(vtype)) 98 | if vtype == 4: 99 | if a_range < 0: 100 | raise ValueError("INVALID power variogram") 101 | elif a_range > 2.0: 102 | raise ValueError("INVALID power variogram") 103 | if vtype == 5: 104 | raise ValueError("Cannot handle this type of variogram.") 105 | 106 | def _rotation_matirx(self): 107 | azumth = np.deg2rad(90.0 - np.array(self.ang)) 108 | self.rotmat = np.zeros((4, self.nst)) 109 | self.rotmat[0] = np.cos(azumth) 110 | self.rotmat[1] = np.sin(azumth) 111 | self.rotmat[2] = -np.sin(azumth) 112 | self.rotmat[3] = np.cos(azumth) 113 | 114 | def _max_covariance(self): 115 | PMX = 9999.0 # max value used for power model 116 | self.maxcov = self.c0 117 | for kind, contri in zip(self.it, self.cc): 118 | if kind == 4: 119 | self.maxcov += PMX 120 | else: 121 | self.maxcov += contri 122 | 123 | def _cova2(self, x1, y1, x2, y2): 124 | "calculte covariance using provided variogram model" 125 | PMX = 9999.0 # max value used for power model 126 | dx = x2 - x1 127 | dy = y2 - y1 128 | # check for small distance 129 | if (dx*dx + dy*dy) < np.finfo("float").eps: 130 | return self.maxcov 131 | # for non-zero distance 132 | cova = 0.0 133 | for iss in range(self.nst): 134 | dx1 = dx*self.rotmat[0, iss] + dy*self.rotmat[1, iss] 135 | dy1 = (dx*self.rotmat[2, iss] + dy*self.rotmat[3, iss]) / \ 136 | self.anis[iss] 137 | h = np.sqrt(np.maximum(dx1*dx1 + dy1*dy1, 0)) 138 | if self.it[iss] == 1: # spherical model 139 | hr = h/self.a_max[iss] 140 | if hr < 1: 141 | cova += self.cc[iss] * (1 - hr * (1.5 - 0.5 * hr * hr)) 142 | elif self.it[iss] == 2: # exponential model 143 | cova += self.cc[iss]*np.exp(-3.0*h/self.a_max[iss]) 144 | elif self.it[iss] == 3: # gaussian model 145 | cova += self.cc*np.exp(-3.0 * h * h / \ 146 | (self.a_max[iss] * self.a_max[iss])) 147 | elif self.it[iss] == 4: # power model 148 | cova += PMX - self.cc[iss]*(h**(self.a_max[iss])) 149 | return cova 150 | 151 | def _block_discretization(self): 152 | """ 153 | Set up the discretization points per block. Figure out how many are 154 | needed, the spacing, and fill the xdb and ydb arrays with the 155 | offsets relative to the block center 156 | """ 157 | xdis = self.xsiz / np.maximum(self.nxdis, 1.0) 158 | ydis = self.ysiz / np.maximum(self.nydis, 1.0) 159 | xloc = -0.5*(self.xsiz + xdis) 160 | yloc = -0.5*(self.ysiz + ydis) 161 | xdb_temp = np.arange(1, self.nxdis+1, 1) * xdis + xloc 162 | ydb_temp = np.arange(1, self.nydis+1, 1) * ydis + yloc 163 | xdb, ydb = np.meshgrid(xdb_temp, ydb_temp) 164 | self.xdb, self.ydb = xdb.flat, ydb.flat 165 | # xdb and ydb are nxdis * nydis array 166 | 167 | @property 168 | def unbias(self): 169 | "the unbiasedness constraint" 170 | if self._unbias is None: 171 | self._unbias = self._cova2(self.xdb[0], self.ydb[0], 172 | self.xdb[0], self.ydb[0]) 173 | return self._unbias 174 | 175 | @property 176 | def block_covariance(self): 177 | "the block covariance" 178 | if self._block_covariance is None: 179 | self._block_covariance = 0 180 | if self.ndb <= 1: # point kriging 181 | self._block_covariance = self.unbias 182 | else: # block kriging 183 | cov = list() 184 | for x1, y1 in zip(self.xdb, self.ydb): 185 | for x2, y2 in zip(self.xdb, self.ydb): 186 | cov.append(self._cova2(x1, y1, x2, y2)) 187 | cov = np.array(cov).reshape((self.ndb, self.ndb)) 188 | cov[np.diag_indices_from(cov)] -= self.c0 189 | self._block_covariance = np.mean(cov) 190 | return self._block_covariance 191 | 192 | def _preprocess(self): 193 | self._read_params() 194 | # number of points in discretization block 195 | self.ndb = self.nxdis * self.nydis 196 | 197 | self.anis = np.array(self.a_min)/np.array(self.a_max) 198 | self.ang = np.array(self.azm) 199 | 200 | self._rotation_matirx() 201 | self._max_covariance() 202 | self._block_discretization() 203 | if self.nxdis == 1 and self.nydis == 1: 204 | self.block_kriging = False 205 | 206 | def kd2d(self): 207 | self._preprocess() 208 | print("Start kriging...") 209 | # For each target point on the grid 210 | xloc_temp = np.arange(self.nx) * self.xsiz + self.xmn 211 | yloc_temp = np.arange(self.ny) * self.ysiz + self.ymn 212 | yloc_mesh, xloc_mesh = np.meshgrid(yloc_temp, xloc_temp) 213 | self.estimation = list() 214 | self.estimation_variance = list() 215 | num_of_points = self.nx*self.ny 216 | t1 = time.time() 217 | ts = 0 218 | percent_od = 0 219 | for idx, (xloc, yloc) in enumerate(zip(xloc_mesh.flat, yloc_mesh.flat)): 220 | ts_1 = time.time() 221 | # Find the nearest samples within each octant: 222 | nums, dist = self._search(xloc, yloc) 223 | 224 | ts += time.time() - ts_1 225 | # is there enough samples? 226 | if len(dist) < self.ndmin: 227 | print("Block {},{} not estimated.".format( 228 | (xloc-self.xmn)/self.xsiz, 229 | (yloc-self.ymn)/self.ysiz)) 230 | self.estimation.append(np.nan) 231 | self.estimation_variance.append(np.nan) 232 | continue 233 | na = dist.shape[0] 234 | 235 | # Put coordinates and values of neighborhood samples into xa,ya,vra 236 | xa = self.vr['x'][nums] 237 | ya = self.vr['y'][nums] 238 | vra = self.vr[self.property_name[0]][nums] 239 | # handle the situation of only one sample: 240 | if na == 1: 241 | est, estv = self._one_sample(xloc, yloc, xa, ya, vra) 242 | self.estimation.append(est) 243 | self.estimation_variance.append(estv) 244 | else: # many samples 245 | est, estv = self._many_sample(xloc, yloc, xa, ya, vra) 246 | self.estimation.append(est) 247 | self.estimation_variance.append(estv) 248 | 249 | percent = np.round(idx/num_of_points*100, decimals=0) 250 | dtime = time.time() - t1 251 | if percent != percent_od: 252 | print("{}% ".format(percent) +\ 253 | "."*20 + "{}s elapsed.".format(np.round(dtime, decimals=3))) 254 | percent_od = percent 255 | print("Kriging finished.") 256 | print("Time used for searching: {}s".format(ts)) 257 | self.estimation = np.array(self.estimation).reshape((self.nx, self.ny)) 258 | self.estimation_variance = np.array( 259 | self.estimation_variance).reshape((self.nx, self.ny)) 260 | 261 | def _search(self, xloc, yloc): 262 | "Search all points return point index and distance to (xloc,yloc)" 263 | dist = list() 264 | nums = list() 265 | # Scan all the samples: 266 | for idd in range(self.vr.shape[0]): 267 | dx = self.vr['x'][idd] - xloc 268 | dy = self.vr['y'][idd] - yloc 269 | h2 = dx*dx + dy*dy 270 | if h2 > self.radius*self.radius: 271 | continue 272 | # do not consider this sample if there are enough close ones: 273 | if len(nums) == self.ndmax: 274 | if h2 >= dist[-1]: 275 | continue 276 | elif h2 < dist[-1]: 277 | del nums[-1] 278 | del dist[-1] 279 | # consider this sample (it will be added in the correct location): 280 | if len(nums) < self.ndmax: 281 | nums.append(idd) 282 | dist.append(h2) 283 | if len(dist) == 0: 284 | return np.array([]), np.array([]) 285 | else: 286 | # Sort samples found thus far in increasing order of distance: 287 | dist = np.array(dist) 288 | nums = np.array(nums) 289 | sort_index = np.argsort(dist) 290 | dist = dist[sort_index] 291 | nums = nums[sort_index] 292 | return nums, dist 293 | 294 | def _one_sample(self, xloc, yloc, xa, ya, vra): 295 | # Left Hand Side Covariance: 296 | left = self._cova2(xa[0], ya[0], xa[0], ya[0]) 297 | 298 | # Right Hand Side Covariance: 299 | xx = xa[0] - xloc 300 | yy = ya[0] - yloc 301 | if not self.block_kriging: # point kriging 302 | right = self._cova2(xx, yy, self.xdb[0], self.ydb[0]) 303 | else: # block kriging 304 | right = 0.0 305 | # cb_list = list() 306 | for i in range(self.ndb): 307 | right = self._cova2(xx, yy, self.xdb[i], self.ydb[i]) 308 | dx = xx - self.xdb[i] 309 | dy = yy - self.ydb[i] 310 | if dx*dx + dy*dy < np.finfo('float').eps: 311 | right -= self.c0 312 | right /= self.ndb 313 | 314 | # Estimation 315 | if self.ktype == 0: # Simple kriging 316 | # Solve for lambda 317 | s = right / self.block_covariance 318 | 319 | est = s * vra[0] + (1.0 - s) * self.skmean 320 | estv = self.block_covariance - s * right 321 | return est, estv 322 | 323 | else: # Ordinary kriging 324 | est = vra[0] 325 | estv = self.block_covariance - 2.0 * right + left 326 | return est, estv 327 | 328 | def _many_sample(self, xloc, yloc, xa, ya, vra): 329 | "Solve the Kriging System with more than one sample" 330 | na = len(vra) 331 | # number of equations, for simple kriging there're na, 332 | # for ordinary there're na + 1 333 | neq = na + self.ktype 334 | 335 | # Establish left hand side covariance matrix: 336 | left = np.full((neq, neq), np.nan) 337 | for i, j in product(range(na), range(na)): 338 | if np.isnan(left[j, i]): 339 | left[i, j] = self._cova2(xa[i], ya[i], xa[j], ya[j]) 340 | else: 341 | left[i, j] = left[j, i] 342 | 343 | # Establish the Right Hand Side Covariance: 344 | right = list() 345 | 346 | for j in range(na): 347 | xx = xa[j] - xloc 348 | yy = ya[j] - yloc 349 | if not self.block_kriging: 350 | cb = self._cova2(xx, yy, self.xdb[0], self.ydb[0]) 351 | else: 352 | cb = 0.0 353 | for i in range(self.ndb): 354 | cb += self._cova2(xx, yy, self.xdb[i], self.ydb[i]) 355 | dx = xx - self.xdb[i] 356 | dy = yy - self.ydb[i] 357 | if dx*dx + dy*dy < np.finfo('float').eps: 358 | cb -= self.c0 359 | cb /= self.ndb 360 | right.append(cb) 361 | 362 | if self.ktype == 1: # for ordinary kriging 363 | # Set the unbiasedness constraint 364 | left[neq-1, :-1] = self.unbias 365 | left[:-1, neq-1] = self.unbias 366 | left[-1, -1] = 0 367 | right.append(self.unbias) 368 | 369 | # Solve the kriging system 370 | s = None 371 | try: 372 | s = linalg.solve(left, right) 373 | except linalg.LinAlgError as inst: 374 | print("Warning kb2d: singular matrix for block " + \ 375 | "{},{}".format((xloc-self.xmn)/self.xsiz, 376 | (yloc-self.ymn)/self.ysiz)) 377 | return np.nan, np.nan 378 | 379 | estv = self.block_covariance 380 | if self.ktype == 1: # ordinary kriging 381 | estv -= s[-1]*self.unbias # s[-1] is mu 382 | est = np.sum(s[:na]*vra[:na]) 383 | estv -= np.sum(s[:na]*right[:na]) 384 | if self.ktype == 0: # simple kriging 385 | est += (1 - np.sum(s[:na])) * self.skmean 386 | return est, estv 387 | 388 | def view(self, pname=None): 389 | pname = self.property_name[0] if pname is None else pname 390 | fig, ax = plt.subplots() 391 | im = ax.imshow(self.estimation.T, interpolation='nearest', 392 | origin='lower', 393 | extent=[self.xmn, 394 | self.xmn + (self.nx - 1)*self.xsiz, 395 | self.ymn, 396 | self.ymn + (self.ny - 1)*self.ysiz]) 397 | ax.set_xlabel("X (m)") 398 | ax.set_ylabel("Y (m)") 399 | ax.set_title("Estimation") 400 | ax.set_aspect('equal') 401 | fig.colorbar(im) 402 | fig.show() 403 | 404 | if __name__ == '__main__': 405 | test_krige = Krige2d("testData/test_krige2d.par") 406 | test_krige.read_data() 407 | test_krige.kd2d() 408 | test_krige.view() 409 | -------------------------------------------------------------------------------- /pygeostatistics/normal_score_transform.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Normal Score Transform 4 | 5 | Created on Tue Dec 6 2016 6 | """ 7 | from __future__ import division, print_function, absolute_import 8 | 9 | import numpy as np 10 | from numba import jit 11 | from scipy import interpolate 12 | 13 | 14 | class NormalScoreTransform(object): 15 | def __init__(self, data, weights, zmin, zmax, ltail, ltpar, utail, utpar): 16 | """ 17 | Perform Normal score transform of data. 18 | 19 | Atrributes 20 | ---------- 21 | data: 1-d ndarray 22 | data to be transformed 23 | weights: 1-d ndarray 24 | declustering weights for transform 25 | zmin, zmax: float, float 26 | allowable values for back-transform 27 | ltail: {1, 2} 28 | option to handle values smaller than minimun data, lower tail 29 | ltpar: float 30 | parameter for lower tail option 31 | utail: int 32 | option to handle values larger than maximum data, upper tail 33 | utpar: float 34 | parameter for upper tail option 35 | """ 36 | self.data = np.array(data) # input data ndarray 37 | self.weights = np.array(weights) # input declustering weight ndarray 38 | 39 | self.transform_table = None 40 | self.zmin = zmin # allowable value for backtransform 41 | self.zmax = zmax # allowable value for backtransform 42 | self.ltail = ltail # option to handle values less than vrg[0]: 43 | self.ltpar = ltpar # parameter required for option ltail 44 | self.utail = utail # option to handle values greater than vrg[-1]: 45 | self.utpar = utpar # parameter required for option utail 46 | 47 | def _create_table(self): 48 | "create transformation lookup table" 49 | # sort input data by value 50 | sort_index = np.argsort(self.data) 51 | sorted_data = self.data[sort_index] 52 | sorted_weight = self.weights[sort_index] 53 | # compute cumulative probabilities 54 | weight_sum = np.sum(sorted_weight) 55 | cum_weight = np.cumsum(sorted_weight / weight_sum) 56 | cum_weight_old = np.append(np.array([0]), cum_weight[:-1]) 57 | average = 0.5 * (cum_weight + cum_weight_old) 58 | # calculate normal score value: 59 | score = [gauinv(element) for element in average] 60 | # create lookup table 61 | table = [(da, sc) for da, sc in zip(sorted_data, score)] 62 | self.transform_table = np.array(table, dtype=np.dtype({ 63 | 'names': ['value', 'score'], 64 | 'formats': ['f8'] * 2 65 | })) 66 | 67 | def create_transform_func(self): 68 | self._create_table() 69 | nrows = self.transform_table['value'].shape[0] 70 | self.forward_func = interpolate.interp1d( 71 | self.transform_table['value'].reshape((nrows,)), 72 | self.transform_table['score'].reshape((nrows,)), 73 | kind='linear', 74 | fill_value="extrapolate") 75 | 76 | self.back_func = interpolate.interp1d( 77 | self.transform_table['score'].reshape((nrows,)), 78 | self.transform_table['value'].reshape((nrows,)), 79 | kind='linear', 80 | fill_value="extrapolate") 81 | 82 | def transform(self, values): 83 | "transform data to normal score" 84 | return self.forward_func(values) 85 | 86 | def back_transform(self, scores): 87 | "transform normal score back to orginal data" 88 | values = np.full_like(scores, np.nan) 89 | 90 | lo_value = self.transform_table['value'][0] 91 | up_value = self.transform_table['value'][-1] 92 | lo_score = self.transform_table['score'][0] 93 | up_score = self.transform_table['score'][-1] 94 | # scores in normal range 95 | normal_mask = np.logical_and(scores <= up_score, scores >= lo_score) 96 | normal_scores = scores[normal_mask] 97 | values[normal_mask] = self.back_func(normal_scores) 98 | # scores in lower tail: 1=linear, 2=power 99 | lower_mask = scores < lo_score 100 | lower_scores = scores[lower_mask] 101 | temp = list() 102 | for sc in lower_scores: 103 | backtr = lo_value 104 | cdflo = gcum(lo_score) 105 | cdfbt = gcum(sc) 106 | if self.ltail == 1: # linear 107 | backtr = powint(0, cdflo, self.zmin, lo_value, cdfbt, 1) 108 | temp.append(backtr) 109 | elif self.ltail == 2: # power 110 | cpow = 1.0 / self.ltpar 111 | backtr = powint(0, cdflo, self.zmin, lo_value, cdfbt, cpow) 112 | temp.append(backtr) 113 | values[lower_mask] = temp 114 | # scores in upper tail: 1=linear, 2=power, 4=hyperbolic 115 | upper_mask = scores > up_score 116 | upper_scores = scores[upper_mask] 117 | temp = list() 118 | for sc in upper_scores: 119 | backtr = up_value 120 | cdfhi = gcum(up_score) 121 | cdfbt = gcum(sc) # cdf value of the score to be back-transformed 122 | if self.utail == 1: # linear 123 | backtr = powint(cdfhi, 1.0, up_value, self.zmax, cdfbt, 1) 124 | temp.append(backtr) 125 | elif self.utail == 2: # power 126 | cpow = 1.0 / self.utpar 127 | backtr = powint(cdfhi, 1.0, up_value, self.zmax, cdfbt, cpow) 128 | temp.append(backtr) 129 | elif self.utail == 4: # hyperbolic 130 | l = (up_value**self.utpar) * (1 - gcum(up_score)) 131 | backtr = (l / (1 - gcum(sc)))**(1 / self.utpar) 132 | temp.append(backtr) 133 | values[upper_mask] = temp 134 | return values 135 | 136 | @jit(nopython=True) 137 | def gauinv(p): 138 | """ 139 | Computes the inverse of the standard normal cumulative distribution 140 | function with a numerical approximation. 141 | 142 | Parameters 143 | ---------- 144 | p : scalar, ndarray 145 | Cumulative probability funciton value 146 | 147 | Returns 148 | ------- 149 | xp : scalar, ndarray 150 | Quantile function value 151 | 152 | Notes 153 | ----- 154 | .. [1] Statistical Computing, by W.J. Kennedy, Jr. and James E. Gentle, 155 | 1980, p. 95. 156 | """ 157 | lim = 1.0e-10 158 | p0 = -0.322232431088 159 | p1 = -1.0 160 | p2 = -0.342242088547 161 | p3 = -0.0204231210245 162 | p4 = -0.0000453642210148 163 | q0 = 0.0993484626060 164 | q1 = 0.588581570495 165 | q2 = 0.531103462366 166 | q3 = 0.103537752850 167 | q4 = 0.0038560700634 168 | # check for an error situation 169 | if p < lim: 170 | return -1e10 171 | if p > 1 - lim: 172 | return 1e10 173 | pp = p 174 | if p > 0.5: 175 | pp = 1 - pp 176 | if p == 0.5: 177 | return 0 178 | y = np.sqrt(np.log(1.0/(pp*pp))) 179 | xp = y + ((((y*p4 + p3)*y + p2)*y + p1)*y + p0) / \ 180 | ((((y*q4 + q3)*y + q2)*y + q1)*y + q0) 181 | if p == pp: 182 | xp = -xp 183 | 184 | return xp 185 | 186 | @jit(nopython=True) 187 | def gcum(x): 188 | """ 189 | Evaluate the standard normal cdf given a normal deviate x. gcum is 190 | the area under a unit normal curve to the left of x. The results are 191 | accurate only to about 5 decimal places. 192 | """ 193 | z = -x if x < 0 else x 194 | t = 1. / (1. + 0.2316419 * z) 195 | gcum = t*(0.31938153 + t*(-0.356563782 + t*(1.781477937 + \ 196 | t*(-1.821255978 + t*1.330274429)))) 197 | e2 = np.exp(-z*z/2.)*0.3989422803 if z <= 6 else 0 198 | gcum = 1.0 - e2 * gcum 199 | if x >= 0: 200 | return gcum 201 | else: 202 | return 1.0 - gcum 203 | 204 | @jit(nopython=True) 205 | def powint(xlow, xhigh, ylow, yhigh, value, power): 206 | "power interpolation" 207 | if xhigh-xlow < np.finfo(float).eps: 208 | return (yhigh + ylow) / 2.0 209 | else: 210 | return ylow + (yhigh - ylow) * \ 211 | (((value - xlow) / (xhigh - xlow))**power) 212 | -------------------------------------------------------------------------------- /pygeostatistics/super_block.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | class for performing Super Block Search 4 | 5 | Created on Tue Nov 22 2016 6 | """ 7 | from __future__ import division, print_function 8 | from itertools import product 9 | import numpy as np 10 | from numba import jit 11 | 12 | 13 | class SuperBlockSearcher(object): 14 | """ 15 | Class for performing Super Block Search 16 | 17 | This subroutine sets up a 3-D "super block" model and orders the data 18 | by super block number. The limits of the super block is set to the 19 | minimum and maximum limits of the grid; data outside are assigned to 20 | the nearest edge block. 21 | 22 | The idea is to establish a 3-D block network that contains all the 23 | relevant data. The data are then sorted by their index location in 24 | the search network, i.e., the index location is given after knowing 25 | the block index in each coordinate direction (ix,iy,iz): 26 | ii = (iz-1)*nxsup*nysup + (iy-1)*nxsup + ix 27 | An array, the same size as the number of super blocks, is constructed 28 | that contains the cumulative number of data in the model. With this 29 | array it is easy to quickly check what data are located near any given 30 | location. 31 | 32 | Parameters 33 | ---------- 34 | nx,xmn,xsiz Definition of the X grid being considered 35 | ny,ymn,ysiz Definition of the Y grid being considered 36 | nz,zmn,zsiz Definition of the Z grid being considered 37 | vr(nd) x, y, z, other variables 38 | MAXSB[X,Y,Z] Maximum size of super block network 39 | 40 | """ 41 | def __init__(self): 42 | # grid definition 43 | self.nx = None 44 | self.xmn = None 45 | self.xsiz = None 46 | self.ny = None 47 | self.ymn = None 48 | self.ysiz = None 49 | self.nz = None 50 | self.zmn = None 51 | self.zsiz = None 52 | # data 53 | self.vr = None # with x,y,z and variable and other secondary variable 54 | self.MAXSB = [] 55 | 56 | # rotation matrix 57 | self.rotmat = None # rotation matrix for searching!!! 58 | self.radsqd = None # squared search radius 59 | 60 | # octant search 61 | self.noct = None # the number of data noct to retain from each octant 62 | 63 | #To be calculated 64 | self.nisb = None # array with cumulative number of data in each super block. 65 | # super block definitions 66 | self.nxsup = None 67 | self.xmnsup = None 68 | self.xsizsup = None 69 | self.nysup = None 70 | self.ymnsup = None 71 | self.ysizsup = None 72 | self.nzsup = None 73 | self.zmnsup = None 74 | self.zsizsup = None 75 | # superblocks to search 76 | self.nsbtosr = None # Number of super blocks to search 77 | self.ixsbtosr = None # X offsets for super blocks to search 78 | self.iysbtosr = None # Y offsets for super blocks to search 79 | self.izsbtosr = None # Z offsets for super blocks to search 80 | # points found within nearby super blocks 81 | self.nclose = None 82 | self.close_samples = None 83 | self.infoct = None 84 | # output sort_index 85 | self.sort_index = None 86 | 87 | def setup(self): 88 | """ 89 | Variables estimated 90 | ------------------- 91 | nisb() Array with cumulative number of data in each 92 | super block. 93 | nxsup,xmnsup,xsizsup Definition of the X super block grid 94 | nysup,ymnsup,ysizsup Definition of the Y super block grid 95 | nzsup,zmnsup,zsizsup Definition of the Z super block grid 96 | """ 97 | # Establish super block definition 98 | self.nxsup = min(self.nx, self.MAXSB[0]) 99 | self.nysup = min(self.ny, self.MAXSB[1]) 100 | self.nzsup = min(self.nz, self.MAXSB[2]) 101 | 102 | self.xsizsup = self.nx * self.xsiz / self.nxsup 103 | self.ysizsup = self.ny * self.ysiz / self.nysup 104 | self.zsizsup = self.nz * self.zsiz / self.nzsup 105 | 106 | self.xmnsup = (self.xmn - 0.5 * self.xsiz) + 0.5 * self.xsizsup 107 | self.ymnsup = (self.ymn - 0.5 * self.ysiz) + 0.5 * self.ysizsup 108 | self.zmnsup = (self.zmn - 0.5 * self.zsiz) + 0.5 * self.zsizsup 109 | 110 | # partition data into each super block 111 | x_block = np.arange(self.xmnsup - 0.5 * self.xsizsup, 112 | self.xmnsup + (self.nxsup + 1) * self.xsizsup + 1, 113 | self.xsizsup) 114 | x_index = np.searchsorted(x_block, self.vr['x']) - 1 115 | 116 | y_block = np.arange(self.ymnsup - 0.5 * self.ysizsup, 117 | self.ymnsup + (self.nysup + 1) * self.ysizsup + 1, 118 | self.ysizsup) 119 | y_index = np.searchsorted(y_block, self.vr['y']) - 1 120 | 121 | z_block = np.arange(self.zmnsup - 0.5 * self.zsizsup, 122 | self.zmnsup + (self.nzsup + 1) * self.zsizsup + 1, 123 | self.zsizsup) 124 | z_index = np.searchsorted(z_block, self.vr['z']) - 1 125 | 126 | # self.super_block = np.full((self.nxsup, self.nysup, self.nzsup), []) 127 | temp = np.zeros_like(self.vr['x']) 128 | self.nisb = np.zeros((self.nxsup*self.nysup*self.nzsup,)) 129 | for idx, (ix, iy, iz) in enumerate(zip(x_index, y_index, z_index)): 130 | ii = super_flat_index(ix, iy, iz, self.nxsup, self.nysup) 131 | temp[idx] = ii 132 | self.nisb[ii] += 1 133 | 134 | # sort data by asceding super block number: 135 | self.sort_index = np.argsort(temp) 136 | self.vr = self.vr[self.sort_index] 137 | # set up nisb 138 | self.nisb = np.cumsum(self.nisb, dtype=np.int) 139 | 140 | def pickup(self): 141 | """ 142 | This subroutine establishes which super blocks must be searched given 143 | that a point being estimated/simulated falls within a super block 144 | centered at 0,0,0. 145 | 146 | Variables estimated 147 | ------------------- 148 | nsbtosr Number of super blocks to search 149 | ixsbtosr X offsets for super blocks to search 150 | iysbtosr Y offsets for super blocks to search 151 | izsbtosr Z offsets for super blocks to search 152 | """ 153 | self.nsbtosr = 0 154 | self.ixsbtosr = list() 155 | self.iysbtosr = list() 156 | self.izsbtosr = list() 157 | float_max = np.finfo('float').max 158 | self.nsbtosr, self.ixsbtosr, self.iysbtosr, self.izsbtosr = func_pickup( 159 | self.nxsup, self.nysup, self.nzsup, 160 | self.xsizsup, self.ysizsup, self.zsizsup, 161 | self.rotmat, self.radsqd, float_max) 162 | 163 | def search(self, xloc, yloc, zloc,): 164 | """ 165 | Variables estimated 166 | ------------------- 167 | nclose Number of close data 168 | close() Index of close data 169 | infoct Number of informed octants (only computes if 170 | performing an octant search) 171 | """ 172 | ix, iy, iz = getindx(xloc, yloc, zloc, 173 | self.xmnsup, self.xsizsup, self.nxsup, 174 | self.ymnsup, self.ysizsup, self.nysup, 175 | self.zmnsup, self.zsizsup, self.nzsup) 176 | 177 | self.nclose, self.close_samples = func_search( 178 | xloc, yloc, zloc, 179 | ix, iy, iz, 180 | self.nsbtosr, self.ixsbtosr, self.iysbtosr, self.izsbtosr, 181 | self.nxsup, self.nysup, self.nzsup, 182 | self.nisb, self.rotmat, self.radsqd, 183 | self.vr) 184 | # perform octant search partition 185 | if self.noct <= 0: 186 | return 187 | else: # partition the data into octant 188 | inoct = np.zeros((8,)) 189 | # pick up the closes samples in each octant 190 | nt = self.noct * 8 191 | na = 0 192 | for j in range(self.nclose): 193 | i = int(self.close_samples[j]) 194 | h = distance[j] 195 | dx = self.vr['x'][i] - xloc 196 | dy = self.vr['y'][i] - yloc 197 | dz = self.vr['z'][i] - zloc 198 | if dz >= 0: 199 | iq = 3 200 | if dx <= 0 and dy > 0: 201 | iq = 0 202 | if dx > 0 and dy >= 0: 203 | iq = 1 204 | if dx < 0 and dy <= 0: 205 | iq = 2 206 | else: 207 | iq = 7 208 | if dx <= 0 and dy > 0: 209 | iq = 4 210 | if dx > 0 and dy >= 0: 211 | iq = 5 212 | if dx < 0 and dy >= 0: 213 | iq = 6 214 | inoct[iq] += 1 215 | 216 | if inoct[iq] <= self.noct: 217 | self.close_samples[na] = i 218 | distance[na] = h 219 | if na == nt: 220 | self.nclose = na 221 | break 222 | na += 1 223 | # how many octants from which samples are drawn 224 | self.infoct = np.count_nonzero(inoct) 225 | 226 | @jit(nopython=True) 227 | def func_pickup(nxsup, nysup, nzsup, 228 | xsizsup, ysizsup, zsizsup, 229 | rotmat, radsqd, float_max): 230 | nsbtosr = 0 231 | ixsbtosr = [] 232 | iysbtosr = [] 233 | izsbtosr = [] 234 | # for i, j, k in product(range(-(nxsup-1), nxsup), 235 | # range(-(nysup-1), nysup), 236 | # range(-(nzsup-1), nzsup)): 237 | for i in range(-(nxsup-1), nxsup): 238 | for j in range(-(nysup-1), nysup): 239 | for k in range(-(nzsup-1), nzsup): 240 | xo = i * xsizsup 241 | yo = j * ysizsup 242 | zo = k * zsizsup 243 | shortest = float_max 244 | # for i1, j1, k1 in product([-1, 1], [-1, 1], [-1, 1]): 245 | # for i2, j2, k2 in product([-1, 1], [-1, 1], [-1, 1]): 246 | for i1 in [-1, 1]: 247 | for j1 in [-1, 1]: 248 | for k1 in [-1, 1]: 249 | for i2 in [-1, 1]: 250 | for j2 in [-1, 1]: 251 | for k2 in [-1, 1]: 252 | xdis = (i1 - i2) * 0.5 * xsizsup + xo 253 | ydis = (j1 - j2) * 0.5 * ysizsup + yo 254 | zdis = (k1 - k2) * 0.5 * zsizsup + zo 255 | hsqd = sqdist( 256 | (0, 0, 0), (xdis, ydis, zdis), 257 | rotmat) 258 | shortest = hsqd if hsqd < shortest \ 259 | else shortest 260 | if shortest <= radsqd: 261 | nsbtosr += 1 262 | ixsbtosr.append(i) 263 | iysbtosr.append(j) 264 | izsbtosr.append(k) 265 | return nsbtosr, ixsbtosr, iysbtosr, izsbtosr 266 | 267 | @jit(nopython=True) 268 | def func_search(xloc, yloc, zloc, 269 | ix, iy, iz, 270 | nsbtosr, ixsbtosr, iysbtosr, izsbtosr, 271 | nxsup, nysup, nzsup, 272 | nisb, rotmat, radsqd, 273 | vr): 274 | nclose = 0 275 | close_samples = [] 276 | distance = [] 277 | # loop over all super blocks 278 | for isup in range(nsbtosr): 279 | ixsup = ix + ixsbtosr[isup] 280 | iysup = iy + iysbtosr[isup] 281 | izsup = iz + izsbtosr[isup] 282 | if ixsup < 0 or ixsup >= nxsup or \ 283 | iysup < 0 or iysup >= nysup or \ 284 | izsup < 0 or izsup >= nzsup: 285 | continue 286 | # find number of points within this super block 287 | ii = super_flat_index(ixsup, iysup, izsup, nxsup, nysup) 288 | # ii = self._super_flat_index(ixsup, iysup, izsup) 289 | i = 0 290 | if ii == 0: 291 | nums = nisb[ii] 292 | i = 0 293 | else: 294 | nums = nisb[ii] - nisb[ii - 1] 295 | i = nisb[ii - 1] 296 | # loop over all the data within this super block 297 | for k in range(0, nums): 298 | # hsqd = self.sqdist((xloc, yloc, zloc), 299 | # (self.vr['x'][i], self.vr['y'][i], 300 | # self.vr['z'][i])) 301 | hsqd = sqdist( 302 | (xloc, yloc, zloc), 303 | (vr['x'][i], vr['y'][i], vr['z'][i]), 304 | # (vrx[i], vry[i], vrz[i]), 305 | rotmat) 306 | if hsqd > radsqd: 307 | continue 308 | nclose += 1 309 | close_samples.append(i) 310 | distance.append(i) 311 | i += 1 312 | # sort nearby samples by distance 313 | distance = np.array(distance) 314 | close_samples = np.array(close_samples) 315 | sort_index = np.argsort(distance) 316 | close_samples = close_samples[sort_index] 317 | return nclose, close_samples 318 | 319 | @jit(nopython=True) 320 | def getindx(xloc, yloc, zloc, 321 | xmnsup, xsizsup, nxsup, 322 | ymnsup, ysizsup, nysup, 323 | zmnsup, zsizsup, nzsup): 324 | """ 325 | determine which superblock are the given point or list of points in 326 | 327 | Parameters 328 | ---------- 329 | xloc, yloc, zloc: scalar or 1-D ndarray 330 | """ 331 | x_block = np.arange(xmnsup - 0.5 * xsizsup, 332 | xmnsup + (nxsup + 1) * xsizsup + 1, 333 | xsizsup) 334 | x_index = np.searchsorted(x_block, xloc) - 1 335 | 336 | y_block = np.arange(ymnsup - 0.5 * ysizsup, 337 | ymnsup + (nysup + 1) * ysizsup + 1, 338 | ysizsup) 339 | y_index = np.searchsorted(y_block, yloc) - 1 340 | 341 | z_block = np.arange(zmnsup - 0.5 * zsizsup, 342 | zmnsup + (nzsup + 1) * zsizsup + 1, 343 | zsizsup) 344 | z_index = np.searchsorted(z_block, zloc) - 1 345 | 346 | return x_index, y_index, z_index 347 | # return None 348 | 349 | @jit(nopython=True) 350 | def sqdist(point1, point2, rotmat): 351 | """ 352 | This routine calculates the anisotropic distance between two points 353 | given the coordinates of each point and a definition of the 354 | anisotropy. 355 | 356 | Parameters 357 | ---------- 358 | point1 : tuple 359 | Coordinates of first point (x1,y1,z1) 360 | point2 : tuple 361 | Coordinates of second point (x2,y2,z2) 362 | 363 | Returns 364 | ------- 365 | sqdist : scalar 366 | The squared distance accounting for the anisotropy 367 | and the rotation of coordinates (if any). 368 | """ 369 | dx = point1[0] - point2[0] 370 | dy = point1[1] - point2[1] 371 | dz = point1[2] - point2[2] 372 | sqdist = 0.0 373 | for i in range(3): 374 | cont = rotmat[i, 0] * dx + \ 375 | rotmat[i, 1] * dy + \ 376 | rotmat[i, 2] * dz 377 | sqdist += cont * cont 378 | return sqdist 379 | 380 | @jit(nopython=True) 381 | def super_flat_index(ixsup, iysup, izsup, nxsup, nysup): 382 | return ixsup + iysup * nxsup + izsup * nxsup * nysup 383 | -------------------------------------------------------------------------------- /pygeostatistics/variogram_model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Nov 2016 4 | 5 | Five different kinds of variogram model 6 | ----- 7 | use parameter name crange instead of range which is a built-in python function 8 | """ 9 | from __future__ import division 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | 13 | 14 | def spherical(lag, sill, crange): 15 | if lag <= crange: 16 | return sill*(1.5*(lag/crange) - 0.5*(lag/crange)**3) 17 | else: 18 | return sill 19 | 20 | 21 | def exponential(lag, sill, crange): 22 | return sill*(1 - np.exp(-(3*lag/crange))) 23 | 24 | 25 | def gaussian(lag, sill, crange): 26 | return sill*(1 - np.exp(-(3*lag**2/crange**2))) 27 | 28 | 29 | def power(lag, sill, omega): 30 | return sill*lag**omega 31 | 32 | 33 | def hole_effect(lag, sill, crange): 34 | return sill*(1-np.cos((lag/crange)*np.pi)) 35 | 36 | if __name__ == '__main__': 37 | func = np.vectorize(exponential) 38 | abscissa = np.arange(0, 100, 0.1) 39 | ordinate = func(abscissa, 1, 40) 40 | plt.plot(abscissa, ordinate) 41 | -------------------------------------------------------------------------------- /pygeostatistics/yaml_patch.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | add resolver to pyyaml for correctly parsing scientific notation 4 | 5 | Created on Wen Nov 21 6 | """ 7 | from __future__ import absolute_import, division, print_function 8 | 9 | __author__ = "yuhao" 10 | 11 | import re 12 | import yaml 13 | 14 | def loader_patched(): 15 | loader = yaml.SafeLoader 16 | loader.add_implicit_resolver( 17 | u'tag:yaml.org,2002:float', 18 | re.compile(u'''^(?: 19 | [-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)? 20 | |[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+) 21 | |\\.[0-9_]+(?:[eE][-+][0-9]+)? 22 | |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]* 23 | |[-+]?\\.(?:inf|Inf|INF) 24 | |\\.(?:nan|NaN|NAN))$''', re.X), 25 | list(u'-+0123456789.')) 26 | return loader 27 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [versioneer] 2 | VCS = git 3 | style = pep440 4 | versionfile_source = pygeostatistics/_version.py 5 | versionfile_build = pygeostatistics/_version.py 6 | tag_prefix = 7 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Created on Sep 8th 2018 4 | """ 5 | from distutils.core import setup 6 | from setuptools import find_packages 7 | import versioneer 8 | 9 | CLASSIFIERS = [ 10 | 'Development Status :: 4 - Beta', 11 | 'Intended Audience :: Developers', 12 | 'Intended Audience :: Science/Research', 13 | 'License :: OSI Approved :: MIT License', 14 | 'Programming Language :: Python', 15 | 'Topic :: Scientific/Engineering', 16 | 'Topic :: Scientific/Engineering :: Mathematics', 17 | 'Topic :: Scientific/Engineering :: Physics', 18 | 'Operating System :: Microsoft :: Windows', 19 | 'Operating System :: POSIX', 20 | 'Operating System :: Unix', 21 | 'Operating System :: MacOS', 22 | 'Natural Language :: English', 23 | ] 24 | 25 | with open("README.md") as f: 26 | LONG_DESCRIPTION = ''.join(f.readlines()) 27 | 28 | setup( 29 | name="pyGeoStatistics", 30 | version=versioneer.get_version(), 31 | cmdclass=versioneer.get_cmdclass(), 32 | install_requires=[ 33 | 'scipy', 34 | 'pandas', 35 | 'numba', 36 | 'matplotlib' 37 | ], 38 | packages=find_packages(exclude=['tests', 'testData']), 39 | author="Yu Hao", 40 | author_email="yuhao@live.cn", 41 | description="pyGeoStatistics: Geostatistics with Python", 42 | long_description=LONG_DESCRIPTION, 43 | license="MIT", 44 | keywords="geostatistics", 45 | url="https://github.com/whimian/pyGeoStatistics", 46 | download_url="https://github.com/whimian/pyGeoStatistics", 47 | classifiers=CLASSIFIERS, 48 | platforms=["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], 49 | zip_safe=False 50 | ) 51 | -------------------------------------------------------------------------------- /testData/test.gslib: -------------------------------------------------------------------------------- 1 | test 2 | 3 3 | x 4 | y 5 | por 6 | 12100.0 8300.0 14.6515 7 | 5300.0 8700.0 14.5093 8 | 3500.0 13900.0 14.0639 9 | 5100.0 1900.0 15.1084 10 | 9900.0 13700.0 13.919 11 | 2900.0 900.0 13.1304 12 | 7900.0 6700.0 14.5724 13 | 16900.0 4900.0 15.0814 14 | 18700.0 1500.0 13.91 15 | 2700.0 2100.0 13.4024 16 | 10700.0 5100.0 14.9395 17 | 7500.0 12900.0 15.2159 18 | 5500.0 11100.0 14.5777 19 | 9500.0 9100.0 14.2483 20 | 15300.0 3100.0 14.4281 21 | 4700.0 9700.0 15.2606 22 | 16700.0 15700.0 16.1859 23 | 19500.0 9700.0 14.2079 24 | 16900.0 13100.0 16.9583 25 | 900.0 3700.0 13.8354 26 | 500.0 11900.0 14.1859 27 | 9100.0 1300.0 14.0381 28 | 9100.0 13700.0 14.3685 29 | 9900.0 12900.0 13.4018 30 | 6300.0 100.0 15.8953 31 | 3700.0 5100.0 12.8667 32 | 16300.0 900.0 15.1039 33 | 18300.0 13500.0 15.7736 34 | 9500.0 6900.0 14.1333 35 | 17900.0 3100.0 13.3369 36 | 9900.0 15500.0 15.1362 37 | 7100.0 8900.0 15.0847 38 | 19300.0 7100.0 14.2498 39 | 2300.0 5700.0 12.6811 40 | 7300.0 8900.0 14.9384 41 | 13900.0 3700.0 15.6005 42 | 8500.0 10100.0 13.7796 43 | 8100.0 8700.0 15.2907 44 | 14700.0 11900.0 15.6881 45 | 6300.0 2300.0 15.3677 46 | 11900.0 12900.0 14.3283 47 | 18100.0 7100.0 14.7374 48 | 11300.0 7100.0 15.0547 49 | 12500.0 3100.0 14.8889 50 | 2700.0 12700.0 14.436 51 | 2700.0 4300.0 12.1491 52 | 8500.0 11300.0 13.624 53 | 1500.0 900.0 14.188 54 | 7300.0 1300.0 14.9072 55 | 10700.0 4100.0 15.2029 56 | 7100.0 1900.0 15.3468 57 | 3900.0 8500.0 15.939 58 | 17100.0 6100.0 15.7269 59 | 14100.0 10100.0 15.3238 60 | 11500.0 4900.0 14.0445 61 | 13300.0 15700.0 14.4032 62 | 1900.0 12100.0 14.3586 63 | 15100.0 2900.0 14.6007 64 | 6500.0 900.0 16.1458 65 | 8900.0 6100.0 15.7727 66 | 4500.0 2300.0 13.6234 67 | 12900.0 10300.0 15.1024 68 | 10900.0 5700.0 15.3546 69 | 3500.0 700.0 13.8431 70 | 16300.0 3700.0 14.9427 71 | 900.0 5100.0 14.4139 72 | 12900.0 12900.0 13.6177 73 | 15300.0 9300.0 16.3787 74 | 7300.0 6900.0 14.258 75 | 16300.0 12500.0 15.7772 76 | 100.0 8900.0 14.6553 77 | 1700.0 11700.0 14.3627 78 | 17500.0 11100.0 15.9659 79 | 14900.0 8300.0 16.0095 80 | 8300.0 10900.0 13.9639 81 | 4100.0 14500.0 14.2649 82 | 11100.0 15300.0 15.7684 83 | 500.0 4900.0 14.591 84 | 13100.0 1500.0 15.1377 85 | 18900.0 1700.0 14.095 86 | 3500.0 7500.0 15.1486 87 | 3700.0 6900.0 13.9584 88 | 14500.0 13300.0 14.7381 89 | 4900.0 9100.0 15.0689 90 | 9700.0 5700.0 15.8042 91 | -------------------------------------------------------------------------------- /testData/test_krige2d.par: -------------------------------------------------------------------------------- 1 | { 2 | "a_max": [ 3 | 3715.9 4 | ], 5 | "a_min": [ 6 | 3715.9 7 | ], 8 | "azm": [ 9 | 0 10 | ], 11 | "c0": 0.05, 12 | "cc": [ 13 | 0.65 14 | ], 15 | "datafl": "testData/test.gslib", 16 | "dbgfl": "kb2d.dbg", 17 | "icolvr": 3, 18 | "icolx": 0, 19 | "icoly": 1, 20 | "idbg": 3, 21 | "isk": 0, 22 | "it": [ 23 | 1 24 | ], 25 | "ndmax": 50, 26 | "ndmin": 1, 27 | "nst": 1, 28 | "nx": 98, 29 | "nxdis": 1, 30 | "ny": 79, 31 | "nydis": 1, 32 | "outfl": "out.dat", 33 | "radius": 4000, 34 | "skmean": 14.69588, 35 | "tmax": 1e+21, 36 | "tmin": -1e+21, 37 | "xmn": 100, 38 | "xsiz": 200, 39 | "ymn": 100, 40 | "ysiz": 200 41 | } -------------------------------------------------------------------------------- /testData/test_krige3d.par: -------------------------------------------------------------------------------- 1 | { 2 | "aa_hmax": [ 3 | 3715.9 4 | ], 5 | "aa_hmin": [ 6 | 3715.9 7 | ], 8 | "aa_vert": [ 9 | 3715.9 10 | ], 11 | "ang1": [ 12 | 0 13 | ], 14 | "ang2": [ 15 | 0 16 | ], 17 | "ang3": [ 18 | 0 19 | ], 20 | "c0": 0.05, 21 | "cc": [ 22 | 0.65 23 | ], 24 | "datafl": "testData/test.gslib", 25 | "dbgfl": "kb2d.dbg", 26 | "icolsec": 4, 27 | "icolvr": 3, 28 | "icolx": 0, 29 | "icoly": 1, 30 | "icolz": 2, 31 | "idbg": 3, 32 | "idrift": [ 33 | false, 34 | false, 35 | false, 36 | false, 37 | false, 38 | false, 39 | false, 40 | false, 41 | false 42 | ], 43 | "ikrige": 0, 44 | "iseccol": 3, 45 | "it": [ 46 | 1 47 | ], 48 | "itrend": false, 49 | "jackfl": "jackfl.dat", 50 | "jicolsec": 4, 51 | "jicolvr": 3, 52 | "jicolx": 0, 53 | "jicoly": 1, 54 | "jicolz": 2, 55 | "ndmax": 30, 56 | "ndmin": 1, 57 | "noct": 0, 58 | "nst": 1, 59 | "nx": 98, 60 | "nxdis": 1, 61 | "ny": 79, 62 | "nydis": 1, 63 | "nz": 1, 64 | "nzdis": 1, 65 | "option": 0, 66 | "outfl": "out.dat", 67 | "radius_hmax": 4000, 68 | "radius_hmin": 4000, 69 | "radius_vert": 0, 70 | "sang1": 0, 71 | "sang2": 0, 72 | "sang3": 0, 73 | "secfl": "secfl.dat", 74 | "skmean": 14.69588, 75 | "tmax": 1e+21, 76 | "tmin": -1e+21, 77 | "xmn": 100, 78 | "xsiz": 200, 79 | "ymn": 100, 80 | "ysiz": 200, 81 | "zmn": 0, 82 | "zsiz": 200 83 | } -------------------------------------------------------------------------------- /testData/test_sgsim.par: -------------------------------------------------------------------------------- 1 | { 2 | "aa_hmax": [ 3 | 3715.9 4 | ], 5 | "aa_hmin": [ 6 | 3715.9 7 | ], 8 | "aa_vert": [ 9 | 3715.9 10 | ], 11 | "ang1": [ 12 | 0 13 | ], 14 | "ang2": [ 15 | 0 16 | ], 17 | "ang3": [ 18 | 0 19 | ], 20 | "c0": 0.05, 21 | "cc": [ 22 | 0.65 23 | ], 24 | "datafl": "testData/test.gslib", 25 | "dbgfl": "sgsim.dbg", 26 | "icollvm": 4, 27 | "icolsec": -1, 28 | "icolsvr": 0, 29 | "icolswt": 1, 30 | "icolvr": 2, 31 | "icolwt": -1, 32 | "icolx": 0, 33 | "icoly": 1, 34 | "icolz": -1, 35 | "idbg": 3, 36 | "ikrige": 0, 37 | "ismooth": false, 38 | "it": [ 39 | 1 40 | ], 41 | "itrans": true, 42 | "ltail": 1, 43 | "ltpar": 0, 44 | "multgrid": false, 45 | "mxctx": 30, 46 | "mxcty": 30, 47 | "mxctz": 30, 48 | "ndmax": 30, 49 | "ndmin": 1, 50 | "nmult": 2, 51 | "noct": 0, 52 | "nodmax": 12, 53 | "nsim": 1, 54 | "nst": 1, 55 | "nx": 98, 56 | "ny": 79, 57 | "nz": 1, 58 | "outfl": "sgsim.out", 59 | "radius_hmax": 4000, 60 | "radius_hmin": 4000, 61 | "radius_vert": 0, 62 | "rho": 0.7, 63 | "sang1": 0, 64 | "sang2": 0, 65 | "sang3": 0, 66 | "secfl": "ydata.dat", 67 | "seed": 1, 68 | "smthfl": "histsmth.out", 69 | "sstrat": 0, 70 | "tmax": 1e+21, 71 | "tmin": -1e+21, 72 | "transfl": "sgsim.trn", 73 | "utail": 1, 74 | "utpar": 15, 75 | "varred": 0.1, 76 | "xmn": 100, 77 | "xsiz": 200, 78 | "ymn": 100, 79 | "ysiz": 200, 80 | "zmax": 30, 81 | "zmin": 0, 82 | "zmn": 0, 83 | "zsiz": 200 84 | } -------------------------------------------------------------------------------- /testData/xihuSmall_sparse_gam.par: -------------------------------------------------------------------------------- 1 | {"zsiz": 0.5, "xsiz": 5, "standardize": false, "nlag": 10, "ivhead": [1, 1, 2, 2, 1], "ysiz": 5, "ndir": 2, "datafl": "testData/xihu_sparse.gslib", "xmn": 0.5, "izd": [0, 0], "ixd": [1, 0], "nx": 15, "ny": 23, "nz": 161, "ivtype": [1, 3, 1, 3, 9], "ivtail": [1, 1, 2, 2, 1], "ymn": 0.5, "tmin": -1e+21, "nvarg": 5, "nvar": 1, "zmn": 0.5, "iyd": [0, 1], "ivar": [1, 2], "tmax": 1e+21, "igrid": 1, "outfl": "gam.out"} -------------------------------------------------------------------------------- /testData/xihuSmall_sparse_gamv.par: -------------------------------------------------------------------------------- 1 | { 2 | "atol": [ 3 | 90.0 4 | ], 5 | "azm": [ 6 | 0.0 7 | ], 8 | "bandwd": [ 9 | 200.0 10 | ], 11 | "bandwh": [ 12 | 200.0 13 | ], 14 | "datafl": "testData/test.gslib", 15 | "dip": [ 16 | 0.0 17 | ], 18 | "dtol": [ 19 | 90.0 20 | ], 21 | "icolx": 1, 22 | "icoly": 2, 23 | "icolz": 0, 24 | "ivar": [ 25 | 3, 26 | 4 27 | ], 28 | "ivhead": [ 29 | 1, 30 | 1, 31 | 2 32 | ], 33 | "ivtail": [ 34 | 1, 35 | 1, 36 | 2 37 | ], 38 | "ivtype": [ 39 | 1, 40 | 3, 41 | 1 42 | ], 43 | "ndir": 1, 44 | "nlag": 20, 45 | "nvar": 1, 46 | "nvarg": 3, 47 | "outfl": "out.dat", 48 | "standardize": false, 49 | "tmax": 1e+21, 50 | "tmin": -1e+21, 51 | "xlag": 500.0, 52 | "xltol": 300.0 53 | } -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/whimian/pyGeoStatistics/e119c4e47c57e0dc1ba3ff13e45782d0e33e0c36/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_eda.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Test 4 | 5 | Created on Sep. 4th 2018 6 | """ 7 | __author__ = "yuhao" 8 | 9 | import pytest 10 | from pygeostatistics.eda import EDA 11 | 12 | 13 | def test__EDA(): 14 | eda = EDA("testData/test.gslib") 15 | eda.read() 16 | assert eda.maximum == 16.9583 17 | assert eda.minimum == 12.1491 18 | assert float("{:.4f}".format(eda.mean)) == 14.6959 19 | assert float("{:.4f}".format(eda.variance)) == 0.7776 20 | assert eda.meadian == 14.6515 21 | --------------------------------------------------------------------------------