{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "<p align=\"center\">\n",
    "    <img src=\"https://github.com/GeostatsGuy/GeostatsPy/blob/master/TCG_color_logo.png?raw=true\" width=\"220\" height=\"240\" />\n",
    "\n",
    "</p>\n",
    "\n",
    "## Interactive Variogram Calculation and Modeling Demonstration\n",
    "\n",
    "\n",
    "### Michael Pyrcz, Associate Professor, University of Texas at Austin \n",
    "\n",
    "##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)\n",
    "\n",
    "\n",
    "### The Interactive Workflow\n",
    "\n",
    "Here's an interactive workflow for calculating directional experimental variograms in 2D. \n",
    "\n",
    "* setting the variogram calculation parameters for identifying spatial data pairs \n",
    "\n",
    "This approach is essential for quantifying spatial continuity with sparsely sampled, irregular spatial data.\n",
    "\n",
    "I have more comprehensive workflows for variogram calculation:\n",
    "\n",
    "* [Experimental Variogram Calculation in Python with GeostatsPy](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/GeostatsPy_variogram_calculation.ipynb)\n",
    "\n",
    "* [Determination of Major and Minor Spatial Continuity Directions in Python with GeostatsPy](https://github.com/GeostatsGuy/PythonNumericalDemos/blob/master/GeostatsPy_spatial_continuity_directions.ipynb)\n",
    "\n",
    "#### Spatial Continuity \n",
    "\n",
    "**Spatial Continuity** is the correlation between values over distance.\n",
    "\n",
    "* No spatial continuity – no correlation between values over distance, random values at each location in space regardless of separation distance.\n",
    "\n",
    "* Homogenous phenomenon have perfect spatial continuity, since all values as the same (or very similar) they are correlated. \n",
    "\n",
    "We need a statistic to quantify spatial continuity! A convenient method is the Semivariogram.\n",
    "\n",
    "#### The Semivariogram\n",
    "\n",
    "Function of difference over distance.\n",
    "\n",
    "* The expected (average) squared difference between values separated by a lag distance vector (distance and direction), $h$:\n",
    "\n",
    "\\begin{equation}\n",
    "\\gamma(\\bf{h}) = \\frac{1}{2 N(\\bf{h})} \\sum^{N(\\bf{h})}_{\\alpha=1} (z(\\bf{u}_\\alpha) - z(\\bf{u}_\\alpha + \\bf{h}))^2  \n",
    "\\end{equation}\n",
    "\n",
    "where $z(\\bf{u}_\\alpha)$ and $z(\\bf{u}_\\alpha + \\bf{h})$ are the spatial sample values at tail and head locations of the lag vector respectively.\n",
    "\n",
    "* Calculated over a suite of lag distances to obtain a continuous function.\n",
    "\n",
    "* the $\\frac{1}{2}$ term converts a variogram into a semivariogram, but in practice the term variogram is used instead of semivariogram.\n",
    "* We prefer the semivariogram because it relates directly to the covariance function, $C_x(\\bf{h})$ and univariate variance, $\\sigma^2_x$:\n",
    "\n",
    "\\begin{equation}\n",
    "C_x(\\bf{h}) = \\sigma^2_x - \\gamma(\\bf{h})\n",
    "\\end{equation}\n",
    "\n",
    "Note the correlogram is related to the covariance function as:\n",
    "\n",
    "\\begin{equation}\n",
    "\\rho_x(\\bf{h}) = \\frac{C_x(\\bf{h})}{\\sigma^2_x}\n",
    "\\end{equation}\n",
    "\n",
    "The correlogram provides of function of the $\\bf{h}-\\bf{h}$ scatter plot correlation vs. lag offset $\\bf{h}$.  \n",
    "\n",
    "\\begin{equation}\n",
    "-1.0 \\le \\rho_x(\\bf{h}) \\le 1.0\n",
    "\\end{equation}\n",
    "\n",
    "#### Variogram Observations\n",
    "\n",
    "The following are common observations for variograms that should assist with their practical use.\n",
    "\n",
    "##### Observation \\#1 - As distance increases, variability increase (in general).\n",
    "\n",
    "This is common since in general, over greater distance offsets, there is often more difference between the head and tail samples.\n",
    "\n",
    "In some cases, such as with spatial cyclicity of the hole effect variogram model the variogram may have negative slope over somelag distance intervals\n",
    "\n",
    "Negative slopes at lag distances greater than half the data extent are often caused by too few pairs for a reliable variogram calculation\n",
    "\n",
    "##### Observation \\#2 - Calculated with over all possible pairs separated by lag vector, $\\bf{𝐡}$.\n",
    "\n",
    "We scan through the entire data set, searching for all possible pair combinations with all other data.  We then calculate the variogram as one half the expectation of squared difference between all pairs.\n",
    "\n",
    "More pairs results in a more reliable measure.\n",
    "\n",
    "##### Observation \\#3 - Need to plot the sill to know the degree of correlation.\n",
    "\n",
    "**Sill** is the variance, $\\sigma^2_x$\n",
    "\n",
    "Given stationarity of the variance, $\\sigma^2_x$, and variogram $\\gamma(\\bf{h})$:\n",
    "\n",
    "we can define the covariance function:\n",
    "\n",
    "\\begin{equation}\n",
    "C_x(\\bf{h}) = \\sigma^2_x - \\gamma(\\bf{h})\n",
    "\\end{equation}\n",
    "\n",
    "The covariance measure is a measure of similarity over distance (the mirror image of the variogram as shown by the equation above).\n",
    "\n",
    "Given a standardized distribution $\\sigma^2_x = 1.0$, the covariance, $C_x(\\bf{h})$, is equal to the correlogram, $\\rho_x(\\bf{h})$: \n",
    "\n",
    "\\begin{equation}\n",
    "\\rho_x(\\bf{h}) = \\sigma^2_x - \\gamma(\\bf{h})\n",
    "\\end{equation}\n",
    "\n",
    "##### Observation \\#4 - The lag distance at which the variogram reaches the sill is know as the range.\n",
    "\n",
    "At the range, knowing the data value at the tail location provides no information about a value at the head location of the lag distance vector.\n",
    "\n",
    "##### Observation \\#5 - The nugget effect, a discontinuity at the origin\n",
    "\n",
    "Sometimes there is a discontinuity in the variogram at distances less than the minimum data spacing.  This is known as **nugget effect**.\n",
    "\n",
    "The ratio of nugget / sill, is known as relative nugget effect (%). Modeled as a discontinuity with no correlation structure that at lags, $h \\gt \\epsilon$, an infinitesimal lag distance, and perfect correlation at $\\bf{h} = 0$.\n",
    "Caution when including nuggect effect in the variogram model as measurement error, mixing populations cause apparent nugget effect\n",
    "\n",
    "This exercise demonstrates the semivariogram calculation with GeostatsPy. The steps include:\n",
    "\n",
    "1. generate a 2D model with sequential Gaussian simulation\n",
    "2. sample from the simulation\n",
    "3. calculate and visualize experimental semivariograms\n",
    "\n",
    "#### Variogram Calculation Parameters\n",
    "\n",
    "The variogram calculation parameters include:\n",
    "\n",
    "* **azimuth** is the azimuth of the lag vector\n",
    "\n",
    "* **azimuth tolerance** is the maximum allowable departure from the azimuth (isotropic variograms are calculated with an azimuth tolerance of to 90.0)\n",
    "\n",
    "* **unit lag distance** the size of the bins in lag distance, usually set to the minimum data spacing\n",
    "\n",
    "* **lag distance tolerance** - the allowable tolerance in lage distance, commonly set to 50% of unit lag distanceonal smoothing\n",
    "\n",
    "* **number of lags** - set based on the spatial extent of the dataset, we can typically calculate reliable variograms up to 1/2 the extent of the dataset\n",
    "\n",
    "* **bandwidth** is the maximum offset allowable from the lag vector \n",
    "\n",
    "\n",
    "#### Variogram Modeling\n",
    "\n",
    "Spatial continuity can be modeled with nested, positive definate variogram structures:\n",
    "\n",
    "\\begin{equation}\n",
    "\\Gamma_x(\\bf{h}) = \\sum_{i=1}^{nst} \\gamma_i(\\bf{h})\n",
    "\\end{equation}\n",
    "\n",
    "where $\\Gamma_x(\\bf{h})$ is the nested variogram model resulting from the summation of $nst$ nested variograms  $\\gamma_i(\\bf{h})$.\n",
    "\n",
    "The types of structure commonly applied include:\n",
    "\n",
    "* spherical\n",
    "\n",
    "* exponential\n",
    "\n",
    "* gaussian\n",
    "\n",
    "* nugget\n",
    "\n",
    "Other less common models include:\n",
    "\n",
    "* hole effect\n",
    "\n",
    "* dampenned hole effect\n",
    "\n",
    "* power law\n",
    "\n",
    "these will not be covered here.\n",
    "\n",
    "Each one of these variogram structures, $\\gamma_i(\\bf{h})$, is based on a geometric anisotropy model parameterized by the orientation and range in the major and minor directions.  In 2D this is simply an azimuth and ranges, $azi$, $a_{maj}$ and $a_{min}$. Note, the range in the minor direction (orthogonal to the major direction).\n",
    "\n",
    "The geometric anisotropy model assumes that the range in all off-diagonal directions is based on an ellipse with the major and minor axes alligned with and set to the major and minor for the variogram.\n",
    "\n",
    "\\begin{equation}\n",
    "\\bf{h}_i = \\sqrt{\\left(\\frac{r_{maj}}{a_{maj_i}}\\right)^2 + \\left(\\frac{r_{maj}}{a_{maj_i}}\\right)^2}  \n",
    "\\end{equation}\n",
    "\n",
    "Therefore, if we know the major direction, range in major and minor directions, we may completely describe each nested componnent of the complete spatial continuity of the variable of interest, $i = 1,\\dots,nst$.\n",
    "\n",
    "Some comments on modeling nested variograms:\n",
    "\n",
    "* we can capture nugget, short and long range continuity structures\n",
    "\n",
    "* we rely on the geometric anisotropy model, so all structures must inform the same level of contribution (porportion of the sill) in all directions.\n",
    "\n",
    "* the geometric anisotropy model is based on azimuth of the major direction of continuity, range in the major direction and range in the minor direction (orthogonal to the major direction).  The range is interpolated between the major and minor azimuths with a ellipse model\n",
    "\n",
    "* we can vary the type of variogram, direction or azimuth of the major direction, and major and minor ranges by structure\n",
    "\n",
    "In this workflow we will explore methods to:\n",
    "\n",
    "1. detect directionality from a spatial dataset\n",
    "2. calculate the directional variograms in the major and minor directions \n",
    "3. build a consistent 2D model fit to the major and minor directions\n",
    "\n",
    "#### Objective \n",
    "\n",
    "In the PGE 383: Stochastic Subsurface Modeling class I want to provide hands-on experience with building subsurface modeling workflows. Python provides an excellent vehicle to accomplish this. I have coded a package called GeostatsPy with GSLIB: Geostatistical Library (Deutsch and Journel, 1998) functionality that provides basic building blocks for building subsurface modeling workflows. \n",
    "\n",
    "The objective is to remove the hurdles of subsurface modeling workflow construction by providing building blocks and sufficient examples. This is not a coding class per se, but we need the ability to 'script' workflows working with numerical methods.    \n",
    "\n",
    "#### Getting Started\n",
    "\n",
    "Here's the steps to get setup in Python with the GeostatsPy package:\n",
    "\n",
    "1. Install Anaconda 3 on your machine (https://www.anaconda.com/download/). \n",
    "2. From Anaconda Navigator (within Anaconda3 group), go to the environment tab, click on base (root) green arrow and open a terminal. \n",
    "3. In the terminal type: pip install geostatspy. \n",
    "4. Open Jupyter and in the top block get started by copy and pasting the code block below from this Jupyter Notebook to start using the geostatspy functionality. \n",
    "\n",
    "You will need to copy the data file to your working directory.  They are available here:\n",
    "\n",
    "* Tabular data - sample_data.csv at https://git.io/fh4gm.\n",
    "\n",
    "There are exampled below with these functions. You can go here to see a list of the available functions, https://git.io/fh4eX, other example workflows and source code. \n",
    "\n",
    "#### Load the required libraries\n",
    "\n",
    "The following code loads the required libraries."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import geostatspy.GSLIB as GSLIB                       # GSLIB utilies, visualization and wrapper\n",
    "import geostatspy.geostats as geostats                 # GSLIB methods convert to Python    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will also need some standard packages. These should have been installed with Anaconda 3."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import os                                               # to set current working directory \n",
    "import sys                                              # supress output to screen for interactive variogram modeling\n",
    "import io\n",
    "import numpy as np                                      # arrays and matrix math\n",
    "import pandas as pd                                     # DataFrames\n",
    "import matplotlib.pyplot as plt                         # plotting\n",
    "from matplotlib.pyplot import cm                        # color maps\n",
    "from matplotlib import patches                          # draw variogram ellipse\n",
    "from ipywidgets import interactive                      # widgets and interactivity\n",
    "from ipywidgets import widgets                            \n",
    "from ipywidgets import Layout\n",
    "from ipywidgets import Label\n",
    "from ipywidgets import VBox, HBox\n",
    "import math\n",
    "from math import sin, cos, radians, pi\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings(\"ignore\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you get a package import error, you may have to first install some of these packages. This can usually be accomplished by opening up a command window on Windows and then typing 'python -m pip install [package-name]'. More assistance is available with the respective package docs.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def vmodel_struct(nlag,xlag,azm,vario,istruct):\n",
    "    \n",
    "# Parameters\n",
    "    MAXNST=4\n",
    "    DEG2RAD=3.14159265/180.0 \n",
    "    MAXROT=MAXNST+1\n",
    "    EPSLON = 1.0e-20\n",
    "    VERSION= 1.01\n",
    "\n",
    "# Declare arrays\n",
    "    index = np.zeros(nlag+1)\n",
    "    h = np.zeros(nlag+1)\n",
    "    gam = np.zeros(nlag+1)\n",
    "    cov = np.zeros(nlag+1)\n",
    "    ro = np.zeros(nlag+1)\n",
    "    \n",
    "# Load the variogram\n",
    "    nst = vario[\"nst\"]; cc = np.zeros(nst); aa = np.zeros(nst); it = np.zeros(nst); ang = np.zeros(nst)\n",
    "    anis = np.zeros(nst)\n",
    "    \n",
    "    c0 = vario[\"nug\"]\n",
    "    cc[0] = vario[\"cc1\"]\n",
    "    it[0] = vario[\"it1\"]\n",
    "    ang[0] = vario[\"azi1\"]\n",
    "    aa[0] = vario[\"hmaj1\"]\n",
    "    anis[0] = vario[\"hmin1\"] / vario[\"hmaj1\"]\n",
    "    if nst == 2:\n",
    "        cc[1] = vario[\"cc2\"]\n",
    "        it[1] = vario[\"it2\"]\n",
    "        ang[1] = vario[\"azi2\"]\n",
    "        aa[1] = vario[\"hmaj2\"]\n",
    "        anis[1] = vario[\"hmin2\"] / vario[\"hmaj2\"]\n",
    "                    \n",
    "    xoff = math.sin(DEG2RAD*azm)*xlag\n",
    "    yoff = math.cos(DEG2RAD*azm)*xlag\n",
    "    print(' x,y,z offsets = ' + str(xoff) + ',' + str(yoff))\n",
    "    rotmat, maxcov = geostats.setup_rotmat(c0, nst, it, cc, ang, 99999.9)   \n",
    "          \n",
    "    \n",
    "    xx = 0.0; yy = 0.0      \n",
    "    for il in range(0,nlag+1):\n",
    "        index[il] = il\n",
    "        cov[il] = cova2_struct(0.0,0.0,xx,yy,nst,c0,9999.9,cc,aa,it,ang,anis,rotmat,maxcov,istruct)\n",
    "        #gam[il] = maxcov - cov[il]\n",
    "        if istruct == -1:\n",
    "            gam[il] = c0 - cov[il]  \n",
    "        else:\n",
    "            gam[il] = cc[istruct] - cov[il]\n",
    "        ro[il]  = cov[il]/maxcov\n",
    "        h[il]   = math.sqrt(max((xx*xx+yy*yy),0.0))\n",
    "        xx = xx + xoff\n",
    "        yy = yy + yoff\n",
    "\n",
    "    print(gam)\n",
    "# finished\n",
    "    return index,h,gam,cov,ro\n",
    "\n",
    "def cova2_struct(x1, y1, x2, y2, nst, c0, pmx, cc, aa, it, ang, anis, rotmat, maxcov, istruct):\n",
    "    EPSLON = 0.000001\n",
    "    # Check for very small distance\n",
    "    dx = x2 - x1\n",
    "    dy = y2 - y1\n",
    "\n",
    "    if (dx * dx + dy * dy) < EPSLON:\n",
    "        cova2_ = cc[istruct]\n",
    "        return cova2_\n",
    "    # Non-zero distance, loop over all the structures\n",
    "    cova2_ = 0.0\n",
    "    # Compute the appropriate structural distance \n",
    "    dx1 = dx * rotmat[0, istruct] + dy * rotmat[1, istruct]\n",
    "    dy1 = (dx * rotmat[2, istruct] + dy * rotmat[3, istruct]) / anis[istruct]\n",
    "    h = math.sqrt(max((dx1 * dx1 + dy1 * dy1), 0.0))\n",
    "    if istruct == -1:\n",
    "        return 0.0\n",
    "    elif istruct == 0 or istruct == 1:\n",
    "        if it[istruct] == 1:# Spherical model\n",
    "            hr = h / aa[istruct]\n",
    "            if hr < 1.0:\n",
    "                cova2_ = cova2_ + cc[istruct] * (1.0 - hr * (1.5 - 0.5 * hr * hr))\n",
    "        elif it[istruct] == 2: # Exponential model\n",
    "            cova2_ = cova2_ + cc[istruct] * np.exp(-3.0 * h / aa[istruct])\n",
    "        elif it[istruct] == 3: # Gaussian model\n",
    "            hh = -3.0 * (h * h) / (aa[istruct] * aa[istruct])\n",
    "            cova2_ = cova2_ + cc[istruct] * np.exp(hh)\n",
    "        elif it[istruct] == 4: # Power model\n",
    "            cov1 = pmx - cc[istruct] * (h ** aa[istruct])\n",
    "            cova2_ = cova2_ + cov1\n",
    "    return cova2_\n",
    "\n",
    "def point_pos(x0, y0, d, theta):\n",
    "    theta_rad = pi/2 - radians(theta)\n",
    "    return x0 + d*cos(theta_rad), y0 + d*sin(theta_rad)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### Set the working directory\n",
    "\n",
    "I always like to do this so I don't lose files and to simplify subsequent read and writes (avoid including the full address each time).  Also, in this case make sure to place the required (see above) GSLIB executables in this directory or a location identified in the environmental variable *Path*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "#os.chdir(\"c:/PGE383\")                                   # set the working directory"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Loading Tabular Data\n",
    "\n",
    "Here's the command to load our comma delimited data file in to a Pandas' DataFrame object. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>X</th>\n",
       "      <th>Y</th>\n",
       "      <th>Facies</th>\n",
       "      <th>Porosity</th>\n",
       "      <th>Perm</th>\n",
       "      <th>AI</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>50.0</td>\n",
       "      <td>900.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>22.076146</td>\n",
       "      <td>140.021266</td>\n",
       "      <td>3413.063944</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>50.0</td>\n",
       "      <td>850.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>23.715447</td>\n",
       "      <td>39.837129</td>\n",
       "      <td>3074.562617</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>50.0</td>\n",
       "      <td>800.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>23.435152</td>\n",
       "      <td>84.992437</td>\n",
       "      <td>2292.783358</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>50.0</td>\n",
       "      <td>750.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>24.455309</td>\n",
       "      <td>90.632307</td>\n",
       "      <td>2494.848885</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>50.0</td>\n",
       "      <td>700.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>23.178661</td>\n",
       "      <td>811.547979</td>\n",
       "      <td>2522.063995</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      X      Y  Facies   Porosity        Perm           AI\n",
       "0  50.0  900.0     1.0  22.076146  140.021266  3413.063944\n",
       "1  50.0  850.0     1.0  23.715447   39.837129  3074.562617\n",
       "2  50.0  800.0     1.0  23.435152   84.992437  2292.783358\n",
       "3  50.0  750.0     1.0  24.455309   90.632307  2494.848885\n",
       "4  50.0  700.0     1.0  23.178661  811.547979  2522.063995"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "data = 3\n",
    "\n",
    "if data == 1:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/sample_data_MV_biased.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s)\n",
    "    df['Porosity'] = df['Porosity']*100.0\n",
    "    df = df.iloc[:,1:]                                      # remove first column\n",
    "elif data == 2:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/spatial_nonlinear_MV_facies_v3.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s)\n",
    "    df = df.iloc[:,1:] \n",
    "elif data == 3:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/12_sample_data.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s) \n",
    "    df['Porosity'] = df['Porosity']*100.0\n",
    "    df = df.iloc[:,1:] \n",
    "elif data == 4:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/spatial_nonlinear_MV_facies_v5_sand_only.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s) \n",
    "    df = df.iloc[:,1:] \n",
    "else:\n",
    "    df = pd.read_csv(\"https://raw.githubusercontent.com/GeostatsGuy/GeoDataSets/master/spatial_nonlinear_MV_facies_v1.csv\") # load from Prof. Pyrcz's GitHub repository\n",
    "    df = df.rename(columns = {'Por':'Porosity'})            # rename feature(s)\n",
    "    df = df.iloc[:,1:] \n",
    "    \n",
    "df.head()                                               # we could also use this command for a table preview "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The features:\n",
    "\n",
    "* **X** - x coordinate in meters\n",
    "* **Y** - y coordinate in meters\n",
    "* **Porosity** - rock porosity averaged over a specific rock unit from a vertical well\n",
    "* **Perm** - rock permeability averaged (scaled up) over a specific rock unit from a vertical well \n",
    "* **AI** - acoustic impedance from a seismic cube assigned at a specific rock unit and at the location of a vertical well \n",
    "* **facies** - facies, 0 - shale, 1 - sandstone\n",
    "\n",
    "Concerning facies:\n",
    "\n",
    "We will work with all facies pooled together. I wanted to simplify this workflow and focus more on spatial continuity direction detection. Finally, by not using facies we do have more samples to support our statistical inference. Most often facies are essential in the subsurface model. Don't worry we will check if this is reasonable in a bit.   \n",
    "\n",
    "You are welcome to repeat this workflow on a by-facies basis.  The following code could be used to build DataFrames ('df_sand' and 'df_shale') for each facies.\n",
    "\n",
    "```p\n",
    "df_sand = pd.DataFrame.copy(df[df['Facies'] == 1]).reset_index()  # copy only 'Facies' = sand records\n",
    "df_shale = pd.DataFrame.copy(df[df['Facies'] == 0]).reset_index() # copy only 'Facies' = shale records\n",
    "```\n",
    "\n",
    "Let's look at summary statistics for all facies combined:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>count</th>\n",
       "      <th>mean</th>\n",
       "      <th>std</th>\n",
       "      <th>min</th>\n",
       "      <th>25%</th>\n",
       "      <th>50%</th>\n",
       "      <th>75%</th>\n",
       "      <th>max</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>X</th>\n",
       "      <td>480.0</td>\n",
       "      <td>430.187500</td>\n",
       "      <td>263.832692</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>200.000000</td>\n",
       "      <td>390.000000</td>\n",
       "      <td>630.000000</td>\n",
       "      <td>980.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Y</th>\n",
       "      <td>480.0</td>\n",
       "      <td>522.166667</td>\n",
       "      <td>284.293420</td>\n",
       "      <td>19.000000</td>\n",
       "      <td>279.000000</td>\n",
       "      <td>539.000000</td>\n",
       "      <td>759.000000</td>\n",
       "      <td>999.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Facies</th>\n",
       "      <td>480.0</td>\n",
       "      <td>0.616667</td>\n",
       "      <td>0.486706</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Porosity</th>\n",
       "      <td>480.0</td>\n",
       "      <td>18.943989</td>\n",
       "      <td>3.170164</td>\n",
       "      <td>11.756196</td>\n",
       "      <td>16.588395</td>\n",
       "      <td>18.544339</td>\n",
       "      <td>21.651266</td>\n",
       "      <td>26.109068</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Perm</th>\n",
       "      <td>480.0</td>\n",
       "      <td>520.932093</td>\n",
       "      <td>1226.207190</td>\n",
       "      <td>0.005776</td>\n",
       "      <td>6.539988</td>\n",
       "      <td>49.451463</td>\n",
       "      <td>369.470756</td>\n",
       "      <td>10319.904849</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>AI</th>\n",
       "      <td>480.0</td>\n",
       "      <td>3758.879653</td>\n",
       "      <td>779.990582</td>\n",
       "      <td>1746.387548</td>\n",
       "      <td>3212.900121</td>\n",
       "      <td>3719.883000</td>\n",
       "      <td>4236.160395</td>\n",
       "      <td>6194.573653</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          count         mean          std          min          25%  \\\n",
       "X         480.0   430.187500   263.832692     0.000000   200.000000   \n",
       "Y         480.0   522.166667   284.293420    19.000000   279.000000   \n",
       "Facies    480.0     0.616667     0.486706     0.000000     0.000000   \n",
       "Porosity  480.0    18.943989     3.170164    11.756196    16.588395   \n",
       "Perm      480.0   520.932093  1226.207190     0.005776     6.539988   \n",
       "AI        480.0  3758.879653   779.990582  1746.387548  3212.900121   \n",
       "\n",
       "                  50%          75%           max  \n",
       "X          390.000000   630.000000    980.000000  \n",
       "Y          539.000000   759.000000    999.000000  \n",
       "Facies       1.000000     1.000000      1.000000  \n",
       "Porosity    18.544339    21.651266     26.109068  \n",
       "Perm        49.451463   369.470756  10319.904849  \n",
       "AI        3719.883000  4236.160395   6194.573653  "
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.describe().transpose()                               # summary table of sand only DataFrame statistics"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Set the Model Parameters\n",
    "\n",
    "See the the following model parameters:\n",
    "\n",
    "* **xmin**, **xmax**, **ymin** and **ymax** - extents of the dataset for plotting\n",
    "* **feature** and **feature_units** - feature of interest and associated units\n",
    "* **vmin** and **vmax** - minimum and maximum of the feature of interest"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "xmin = 0.0; xmax = 1000.0                                # spatial extents in x and y\n",
    "ymin = 0.0; ymax = 1000.0\n",
    "feature = 'Porosity'; feature_units = 'percentage'         # name and units of the feature of interest\n",
    "vmin = 0.0; vmax = 25.0                                  # min and max of the feature of interest\n",
    "cmap = plt.cm.inferno                                    # set the color map"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's transform the feature to standard normal (mean = 0.0, standard deviation = 1.0, Gaussian shape). This is required for sequential Gaussian simulation (common target for our variogram models) and the Gaussian transform assists with outliers and provides more interpretable variograms. \n",
    "\n",
    "Let's look at the inputs for the GeostatsPy nscore program.  Note the output include an ndarray with the transformed values (in the same order as the input data in Dataframe 'df' and column 'vcol'), and the transformation table in original values and also in normal score values. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function geostatspy.geostats.nscore(df, vcol, wcol=None, ismooth=False, dfsmooth=None, smcol=0, smwcol=0)>"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "geostats.nscore                                         # see the input parameters required by the nscore function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following command will transform the Porosity and Permeabilty to standard normal. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "#Transform to Gaussian by Facies\n",
    "df['N' + feature], tvPor, tnsPor = geostats.nscore(df, feature) # nscore transform for all facies porosity "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's look at the updated DataFrame to make sure that we now have the normal score porosity and permeability."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>X</th>\n",
       "      <th>Y</th>\n",
       "      <th>Facies</th>\n",
       "      <th>Porosity</th>\n",
       "      <th>Perm</th>\n",
       "      <th>AI</th>\n",
       "      <th>NPorosity</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>50.0</td>\n",
       "      <td>900.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>22.076146</td>\n",
       "      <td>140.021266</td>\n",
       "      <td>3413.063944</td>\n",
       "      <td>0.808592</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>50.0</td>\n",
       "      <td>850.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>23.715447</td>\n",
       "      <td>39.837129</td>\n",
       "      <td>3074.562617</td>\n",
       "      <td>1.403672</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>50.0</td>\n",
       "      <td>800.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>23.435152</td>\n",
       "      <td>84.992437</td>\n",
       "      <td>2292.783358</td>\n",
       "      <td>1.324260</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>50.0</td>\n",
       "      <td>750.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>24.455309</td>\n",
       "      <td>90.632307</td>\n",
       "      <td>2494.848885</td>\n",
       "      <td>1.720087</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>50.0</td>\n",
       "      <td>700.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>23.178661</td>\n",
       "      <td>811.547979</td>\n",
       "      <td>2522.063995</td>\n",
       "      <td>1.155424</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      X      Y  Facies   Porosity        Perm           AI  NPorosity\n",
       "0  50.0  900.0     1.0  22.076146  140.021266  3413.063944   0.808592\n",
       "1  50.0  850.0     1.0  23.715447   39.837129  3074.562617   1.403672\n",
       "2  50.0  800.0     1.0  23.435152   84.992437  2292.783358   1.324260\n",
       "3  50.0  750.0     1.0  24.455309   90.632307  2494.848885   1.720087\n",
       "4  50.0  700.0     1.0  23.178661  811.547979  2522.063995   1.155424"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df.head()                                               # preview sand DataFrame with nscore transforms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That looks good! One way to check is to see if the relative magnitudes of the normal score transformed values match the original values.  e.g. that the normal score transform of 0.10 porosity normal score is less than the normal score transform of 0.14 porsity.  Also, the normal score transform of values close to the mean value should be close to 0.0 \n",
    "\n",
    "Let's also check the original and transformed sand and shale porosity distributions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 59876 characters]",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.subplot(121)                                        # plot original sand and shale porosity histograms\n",
    "plt.hist(df[feature], facecolor='red',bins=np.linspace(vmin,vmax,1000),histtype=\"stepfilled\",alpha=0.2,density=True,cumulative=True,edgecolor='black')\n",
    "plt.xlim([vmin,vmax]); plt.ylim([0,1.0])\n",
    "plt.xlabel(feature + '(' + feature_units + ')'); plt.ylabel('Frequency'); plt.title('Porosity')\n",
    "plt.grid(True)\n",
    "\n",
    "plt.subplot(122)  \n",
    "plt.hist(df['N'+feature], facecolor='blue',bins=np.linspace(-3.0,3.0,1000),histtype=\"stepfilled\",alpha=0.2,density=True,cumulative=True,edgecolor='black')\n",
    "plt.xlim([-3.0,3.0]); plt.ylim([0,1.0])\n",
    "plt.xlabel('Gaussian Transformed ' + feature); plt.ylabel('Frequency'); plt.title('Guassian Transformed ' + feature)\n",
    "plt.grid(True)\n",
    "\n",
    "plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.2, wspace=0.2, hspace=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that the normal score transform has correctly transformed the feature to standard normal.\n",
    "\n",
    "#### Inspection of Posted Data\n",
    "\n",
    "Data visualization is very useful to detect patterns. Our brains are very good at pattern detection. I promote quantitative methods and recognize issues with cognitive bias, but it is important to recognize the value is expert intepretation based on data visualization.\n",
    "\n",
    "* This data visualization will also be important to assist with parameter selection for the variogram calculation search template.\n",
    "\n",
    "Let's plot the location maps of the original feature and the normal score transforms of the feature."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 358688 characters]",
      "text/plain": [
       "<Figure size 640x480 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.subplot(121)                                        # location map of normal score transform of porosity\n",
    "GSLIB.locmap_st(df,'X','Y',feature,0,1000,0,1000,vmin,vmax,feature,'X (m)','Y (m)',feature,cmap)\n",
    "\n",
    "plt.subplot(122)                                        # location map of normal score transform of porosity\n",
    "GSLIB.locmap_st(df,'X','Y','N'+feature,0,1000,0,1000,-3,3,'Gaussian Transformed ' + feature,'X (m)','Y (m)','Gaussian Transformed ' + feature,cmap)\n",
    "\n",
    "plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=1.0, wspace=0.5, hspace=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What do you see?  Here's my observations:\n",
    "\n",
    "* there is a high degree of spatial agreement between porosity and permeability, this is supported by the high correlation evident in the cross plot.\n",
    "* there are no discontinuities that could suggest that facies represent a distinct change, rather the porosity and permeability seem continuous and the assigned facies are a truncation of their continous behavoir, we doing 'ok' with no facies\n",
    "* suspect a 045 azimuth major direction of continuity (up - right)\n",
    "* there may be cycles in the 135 azimuth \n",
    "* there will not likely be a nugget effect, but there is an hint of some short scale discontinuity?\n",
    "\n",
    "**Do you agree?** If you have a different observations, drop me a line at mpyrcz@austin.utexas.edu and I'll add to this lesson with credit!\n",
    "\n",
    "#### Experimental Variograms\n",
    "\n",
    "We can use the location maps to help determine good variogram calculation parameters. For example:\n",
    "\n",
    "```p\n",
    "tmin = -9999.; tmax = 9999.; \n",
    "lag_dist = 100.0; lag_tol = 50.0; nlag = 7; bandh = 9999.9; azi = azi; atol = 22.5; isill = 1\n",
    "```\n",
    "* **tmin**, **tmax** are trimming limits - set to have no impact, no need to filter the data\n",
    "* **lag_dist**, **lag_tol** are the lag distance, lag tolerance - set based on the common data spacing (100m) and tolerance as 100% of lag distance for additonal smoothing\n",
    "* **nlag** is number of lags - set to extend just past 50 of the data extent\n",
    "* **bandh** is the horizontal band width - set to have no effect\n",
    "* **azi** is the azimuth -  it has not effect since we set atol, the azimuth tolerance, to 90.0\n",
    "* **isill** is a boolean to standardize the distribution to a variance of 1 - it has no effect since the previous nscore transform sets the variance to 1.0\n",
    "\n",
    "#### Dashboard for Interactive Variogram Calculation and Modeling\n",
    "\n",
    "Below we make a dashboard with the ipywidgets and matplotlib Python packages for calculating and modeling experimental variograms.\n",
    "\n",
    "* allowing you to calculate and model the variogram of the normal score transformed variogram interactively while changing (and exploring) the search template parameters.\n",
    "\n",
    "* first calculate the isotropic or directional variogram(s)\n",
    "\n",
    "* then fit the same isotropic or directional variogram(s)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "# interactive calculation of the experimental variogram\n",
    "l = widgets.Text(value='                              Variogram Calculation Interactive Demonstration, Michael Pyrcz, Associate Professor, The University of Texas at Austin',layout=Layout(width='950px', height='30px'))\n",
    "lag = widgets.FloatSlider(min = 10, max = 500, value = 10, step = 10, description = 'lag',orientation='vertical',layout=Layout(width='90px', height='200px'))\n",
    "lag.style.handle_color = 'gray'\n",
    "\n",
    "lag_tol = widgets.FloatSlider(min = 5, max = 500, value = 5, step = 10, description = 'lag tolerance',orientation='vertical',layout=Layout(width='90px', height='200px'))\n",
    "lag_tol.style.handle_color = 'gray'\n",
    "\n",
    "nlag = widgets.IntSlider(min = 1, max = 100, value = 100, step = 1, description = 'number of lags',orientation='vertical',layout=Layout(width='90px', height='200px'))\n",
    "nlag.style.handle_color = 'gray'\n",
    "\n",
    "azi = widgets.FloatSlider(min = 0, max = 360, value = 0, step = 5, description = 'azimuth',orientation='vertical',layout=Layout(width='90px', height='200px'))\n",
    "azi.style.handle_color = 'gray'\n",
    "\n",
    "azi_tol = widgets.FloatSlider(min = 10, max = 90, value = 10, step = 5, description = 'azimuth tolerance',orientation='vertical',layout=Layout(width='120px', height='200px'))\n",
    "azi_tol.style.handle_color = 'gray'\n",
    "\n",
    "bandwidth = widgets.FloatSlider(min = 100, max = 2000, value = 2000, step = 100, description = 'bandwidth',orientation='vertical',layout=Layout(width='90px', height='200px'))\n",
    "azi_tol.style.handle_color = 'gray'\n",
    "\n",
    "\n",
    "ui1 = widgets.HBox([lag,lag_tol,nlag,azi,azi_tol,bandwidth],) # basic widget formatting    \n",
    "ui = widgets.VBox([l,ui1],)\n",
    "\n",
    "def f_make(lag,lag_tol,nlag,azi,azi_tol,bandwidth):     # function to take parameters, calculate variogram and plot\n",
    "#    text_trap = io.StringIO()\n",
    "#    sys.stdout = text_trap\n",
    "    global lags,gammas,npps,lags2,gammas2,npps2\n",
    "    tmin = -9999.9; tmax = 9999.9\n",
    "    lags, gammas, npps = geostats.gamv(df,\"X\",\"Y\",\"N\"+feature,tmin,tmax,lag,lag_tol,nlag,azi,azi_tol,bandwidth,isill=1.0)\n",
    "    lags2, gammas2, npps2 = geostats.gamv(df,\"X\",\"Y\",\"N\"+feature,tmin,tmax,lag,lag_tol,nlag,azi+90.0,azi_tol,bandwidth,isill=1.0)\n",
    "    \n",
    "    plt.subplot(111)                                    # plot experimental variogram\n",
    "    plt.scatter(lags,gammas,color = 'black',s = npps*0.03,label = 'Major Azimuth ' +str(azi), alpha = 0.8)\n",
    "    plt.scatter(lags2,gammas2,color = 'red',s = npps*0.03,label = 'Minor Azimuth ' +str(azi+90.0), alpha = 0.8)\n",
    "    plt.plot([0,2000],[1.0,1.0],color = 'black')\n",
    "    plt.xlabel(r'Lag Distance $\\bf(h)$, (m)')\n",
    "    plt.ylabel(r'$\\gamma \\bf(h)$')\n",
    "    if azi_tol < 90.0:\n",
    "        plt.title('Directional NSCORE ' + feature + ' Variogram - Azi. ' + str(np.round(azi,0)) + ', Azi. Tol.' + str(azi_tol))\n",
    "    else: \n",
    "        plt.title('Omni Directional NSCORE ' + feature + ' Variogram ')\n",
    "    plt.xlim([0,1000]); plt.ylim([0,1.8])\n",
    "    plt.legend(loc=\"lower right\")\n",
    "    plt.grid(True)\n",
    "    \n",
    "    plt.subplots_adjust(left=0.0, bottom=0.0, right=1.5, top=1.0, wspace=0.3, hspace=0.3)\n",
    "    plt.show()\n",
    "    \n",
    "    return\n",
    "    \n",
    "# connect the function to make the samples and plot to the widgets    \n",
    "interactive_plot = widgets.interactive_output(f_make, {'lag':lag,'lag_tol':lag_tol,'nlag':nlag,'azi':azi,'azi_tol':azi_tol,'bandwidth':bandwidth})\n",
    "interactive_plot.clear_output(wait = True)               # reduce flickering by delaying plot updating"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interactive Variogram Calculation Demonstration\n",
    "\n",
    "* calculate omnidirectional and direction experimental variograms \n",
    "\n",
    "#### Michael Pyrcz, Associate Professor, University of Texas at Austin \n",
    "\n",
    "##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)\n",
    "\n",
    "### The Problem\n",
    "\n",
    "Calculate interpretable experimental variograms for sparse, irregularly-space spatial data. Note, size of the experimental point is scaled by the number of pairs.\n",
    "\n",
    "* **azimuth** is the azimuth of the lag vector\n",
    "\n",
    "* **azimuth tolerance** is the maximum allowable departure from the azimuth\n",
    "\n",
    "* **unit lag distance** the size of the bins in lag distance\n",
    "\n",
    "* **lag distance tolerance** - the allowable tolerance in lage distance\n",
    "\n",
    "* **number of lags** - number of lags in the experimental variogram\n",
    "\n",
    "* **bandwidth** - maximum departure from the lag vector"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0506d78df31d4b94a40385aede4ee476",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                              Variogram Calculation Interactive Demonstration, Mich…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "c90f38bee07c4757be140bc5d6a5fd50",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui, interactive_plot)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "# interactive calculation of the sample set (control of source parametric distribution and number of samples)\n",
    "l = widgets.Text(value='                              Variogram Modeling, Michael Pyrcz, Associate Professor, The University of Texas at Austin',layout=Layout(width='950px', height='30px'))\n",
    "nug = widgets.FloatSlider(min = 0, max = 1.0, value = 0.0, step = 0.1, description = r'$c_{nugget}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "nug.style.handle_color = 'gray'\n",
    "it1 = widgets.Dropdown(options=['Spherical', 'Exponential', 'Gaussian'],value='Spherical',\n",
    "    description=r'$Type_1$:',disabled=False,layout=Layout(width='200px', height='30px'))\n",
    "c1 = widgets.FloatSlider(min=0.0, max = 1.0, value = 0.1, description = r'$c_1$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "c1.style.handle_color = 'gray'\n",
    "hmaj1 = widgets.FloatSlider(min=0.01, max = 10000.0, value = 0.01, step = 25.0, description = r'$a_{1,maj}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "hmaj1.style.handle_color = 'black'\n",
    "hmin1 = widgets.FloatSlider(min = 0.01, max = 10000.0, value = 0.01, step = 25.0, description = r'$a_{1,min}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "hmin1.style.handle_color = 'red'\n",
    "\n",
    "it2 = widgets.Dropdown(options=['Spherical', 'Exponential', 'Gaussian'],value='Spherical',\n",
    "    description=r'$Type_2$:',disabled=False,layout=Layout(width='200px', height='30px'))\n",
    "c2 = widgets.FloatSlider(min=0.0, max = 1.0, value = 0.0, description = r'$c_2$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "c2.style.handle_color = 'gray'\n",
    "hmaj2 = widgets.FloatSlider(min=0.01, max = 10000.0, value = 0.01, step = 100.0, description = r'$a_{2,maj}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "hmaj2.style.handle_color = 'black'\n",
    "hmin2 = widgets.FloatSlider(min = 0.01, max = 10000.0, value = 0.01, step = 100.0, description = r'$a_{2,min}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "hmin2.style.handle_color = 'red'\n",
    "\n",
    "ui1 = widgets.HBox([nug,it1,c1,hmaj1,hmin1,it2,c2,hmaj2,hmin2],)                   # basic widget formatting   \n",
    "#ui2 = widgets.HBox([it2,c2,hmaj2,hmin2],)                   # basic widget formatting   \n",
    "ui = widgets.VBox([l,ui1],)\n",
    "\n",
    "def convert_type(it):\n",
    "    if it == 'Spherical': \n",
    "        return 1\n",
    "    elif it == 'Exponential':\n",
    "        return 2\n",
    "    else: \n",
    "        return 3\n",
    "\n",
    "def f_make(nug,it1,c1, hmaj1, hmin1, it2, c2, hmaj2, hmin2):                       # function to take parameters, make sample and plot\n",
    "    text_trap = io.StringIO()\n",
    "    sys.stdout = text_trap\n",
    "    \n",
    "    it1 = convert_type(it1); it2 = convert_type(it2)\n",
    "    if c2 > 0.0:\n",
    "        nst = 2\n",
    "    else:\n",
    "        nst = 1\n",
    "    \n",
    "    vario = GSLIB.make_variogram(nug,nst,it1,c1,0.0,hmaj1,hmin1,it2,c2,0.0,hmaj2,hmin2) # make model object\n",
    "    nlag = 100; xlag = 10;                                     \n",
    "    index_maj,h_maj,gam_maj,cov_maj,ro_maj = geostats.vmodel(nlag,xlag,0.0,vario)   # project the model in the major azimuth                                                  # project the model in the 135 azimuth\n",
    "    index_min,h_min,gam_min,cov_min,ro_min = geostats.vmodel(nlag,xlag,90.0,vario)   \n",
    "\n",
    "    plt.subplot(111)                                    # plot experimental variogram\n",
    "    plt.scatter(lags,gammas,color = 'black',s = npps*0.03,label = 'Major Azimuth ' +str(azi.value), alpha = 0.8)\n",
    "    plt.plot(h_maj,gam_maj,color = 'black')\n",
    "    plt.scatter(lags2,gammas2,color = 'red',s = npps*0.03,label = 'Minor Azimuth ' +str(azi.value+90.0), alpha = 0.8)\n",
    "    plt.plot(h_min,gam_min,color = 'red')\n",
    "    plt.plot([0,2000],[1.0,1.0],color = 'black')\n",
    "    plt.xlabel(r'Lag Distance $\\bf(h)$, (m)')\n",
    "    plt.ylabel(r'$\\gamma \\bf(h)$')\n",
    "    if azi_tol.value < 90.0:\n",
    "        plt.title('Directional NSCORE ' + feature + ' Variogram - Azi. ' + str(azi.value) + ', Azi. Tol.' + str(azi_tol.value))\n",
    "    else: \n",
    "        plt.title('Omni Directional NSCORE ' + feature + ' Variogram ')\n",
    "    plt.xlim([0,1000]); plt.ylim([0,1.8])\n",
    "    plt.legend(loc=\"lower right\")\n",
    "    plt.grid(True)\n",
    "    \n",
    "    plt.subplots_adjust(left=0.0, bottom=0.0, right=2.2, top=1.5, wspace=0.3, hspace=0.3)\n",
    "    plt.show()\n",
    "    \n",
    "# connect the function to make the samples and plot to the widgets    \n",
    "interactive_plot = widgets.interactive_output(f_make, {'nug':nug, 'it1':it1,'c1':c1, 'hmaj1':hmaj1, 'hmin1':hmin1, 'it2':it2, 'c2':c2, 'hmaj2':hmaj2, 'hmin2':hmin2})\n",
    "interactive_plot.clear_output(wait = True)               # reduce flickering by delaying plot updating"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interactive Nested Variogram Modeling Demostration\n",
    "\n",
    "* select the nested structures and their types, contributions and major and minor ranges \n",
    "\n",
    "#### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "\n",
    "##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)\n",
    "\n",
    "### The Problem\n",
    "\n",
    "Fit a positive definite variogram model based on the addition of multiple structures each describing spatial components of the feature variance \n",
    "\n",
    "* **nug**: nugget effect\n",
    "\n",
    "* **c1 / c2**: contributions of the sill\n",
    "\n",
    "* **hmaj1 / hmaj2**: range in the major direction\n",
    "\n",
    "* **hmin1 / hmin2**: range in the minor direction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "abdfcc4804da47d991ef9e45aa17b9be",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='                              Variogram Modeling, Michael Pyrcz, Associate Professo…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "bd5a22a0fba34d19923fe913fec99ea3",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui, interactive_plot)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "# interactive calculation of the sample set (control of source parametric distribution and number of samples)\n",
    "l = widgets.Text(value='               Variogram Modeling, Visualize Nested Structures and Azimuth Interpolation, Michael Pyrcz, Professor, The University of Texas at Austin',layout=Layout(width='950px', height='30px'))\n",
    "nug = widgets.FloatSlider(min = 0.0001, max = 1.0, value = 0.0001, step = 0.1, description = r'$c_{nugget}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "nug.style.handle_color = 'gray'\n",
    "it1 = widgets.Dropdown(options=['Spherical', 'Exponential', 'Gaussian'],value='Spherical',\n",
    "    description=r'$Type_1$:',disabled=False,layout=Layout(width='200px', height='30px'))\n",
    "# c1 = widgets.FloatSlider(min=0.0001, max = 1.0, value = 0.2, description = r'$c_1$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "# c1.style.handle_color = 'gray'\n",
    "hmaj1 = widgets.FloatSlider(min=0.01, max = 10000.0, value = 800.0, step = 25.0, description = r'$a_{1,maj}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "hmaj1.style.handle_color = 'black'\n",
    "hmin1 = widgets.FloatSlider(min = 0.01, max = 10000.0, value = 325.0, step = 25.0, description = r'$a_{1,min}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "hmin1.style.handle_color = 'red'\n",
    "\n",
    "it2 = widgets.Dropdown(options=['Spherical', 'Exponential', 'Gaussian'],value='Spherical',\n",
    "    description=r'$Type_2$:',disabled=False,layout=Layout(width='200px', height='30px'))\n",
    "c2 = widgets.FloatSlider(min=0.0001, max = 1.0, value = 0.0001, description = r'$c_2$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "c2.style.handle_color = 'gray'\n",
    "hmaj2 = widgets.FloatSlider(min=0.01, max = 10000.0, value = 800.0, step = 25.0, description = r'$a_{2,maj}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "hmaj2.style.handle_color = 'black'\n",
    "hmin2 = widgets.FloatSlider(min = 0.01, max = 10000.0, value = 325.0, step = 25.0, description = r'$a_{2,min}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "hmin2.style.handle_color = 'red'\n",
    "new_azimuth = widgets.FloatSlider(min = 0.0, max = 360.0, value = 45.0, step = 5.0, description = r'$azi_{inter}$',orientation='vertical',layout=Layout(width='60px', height='200px'))\n",
    "new_azimuth.style.handle_color = 'purple'\n",
    "\n",
    "ui9 = widgets.HBox([nug,it1,hmaj1,hmin1,it2,c2,hmaj2,hmin2,new_azimuth],)                   # basic widget formatting   \n",
    "#ui2 = widgets.HBox([it2,c2,hmaj2,hmin2],)                   # basic widget formatting   \n",
    "ui10 = widgets.VBox([l,ui9],)\n",
    "\n",
    "def convert_type(it):\n",
    "    if it == 'Spherical': \n",
    "        return 1\n",
    "    elif it == 'Exponential':\n",
    "        return 2\n",
    "    else: \n",
    "        return 3\n",
    "\n",
    "def f_make2(nug,it1,hmaj1,hmin1,it2,c2,hmaj2,hmin2,new_azimuth):                       # function to take parameters, make sample and plot\n",
    "    azimuth = azi.value\n",
    "    c1 = 1.0 - nug - c2\n",
    "    it1 = convert_type(it1); it2 = convert_type(it2)\n",
    "    if c2 > 0.0:\n",
    "        nst = 2\n",
    "    else:\n",
    "        nst = 1\n",
    "    \n",
    "    vario = GSLIB.make_variogram(nug,nst,it1,c1,0.0,hmaj1,hmin1,it2,c2,0.0,hmaj2,hmin2) # make model object\n",
    "    nlag = 100; xlag = 10;                                     \n",
    "    index_maj,h_maj,gam_maj,cov_maj,ro_maj = geostats.vmodel(nlag,xlag,0.0,vario)   # project the model in the major azimuth                                                  # project the model in the 135 azimuth\n",
    "    index_min,h_min,gam_min,cov_min,ro_min = geostats.vmodel(nlag,xlag,90.0,vario)\n",
    "    index_new,h_new,gam_new,cov_new,ro_new = geostats.vmodel(nlag,xlag,azimuth-new_azimuth,vario)\n",
    "    \n",
    "    _,h_maj0,gam_maj0,_,_ = vmodel_struct(nlag,xlag,0.0,vario,-1) \n",
    "    _,h_maj1,gam_maj1,_,_ = vmodel_struct(nlag,xlag,0.0,vario,0) \n",
    "    _,h_maj2,gam_maj2,_,_ = vmodel_struct(nlag,xlag,0.0,vario,1) \n",
    "    _,h_min0,gam_min0,_,_ = vmodel_struct(nlag,xlag,90.0,vario,-1) \n",
    "    _,h_min1,gam_min1,_,_ = vmodel_struct(nlag,xlag,90.0,vario,0) \n",
    "    _,h_min2,gam_min2,_,_ = vmodel_struct(nlag,xlag,90.0,vario,1) \n",
    "    _,h_new0,gam_new0,_,_ = vmodel_struct(nlag,xlag,azimuth-new_azimuth,vario,-1) \n",
    "    _,h_new1,gam_new1,_,_ = vmodel_struct(nlag,xlag,azimuth-new_azimuth,vario,0) \n",
    "    _,h_new2,gam_new2,_,_ = vmodel_struct(nlag,xlag,azimuth-new_azimuth,vario,1) \n",
    "    \n",
    "    plt.subplot(221)                                    # plot experimental variogram\n",
    "    plt.scatter(lags,gammas,color = 'black',s = npps*0.03,label = 'Major Azimuth ' +str(azimuth), alpha = 0.8,zorder=10)\n",
    "    plt.plot(h_maj,gam_maj,color = 'black',lw=3,zorder=10)\n",
    "    if nug > 0.0001: \n",
    "        plt.plot(h_maj0,gam_maj0,color = 'black',lw=1.5)\n",
    "        plt.fill_between(h_maj,gam_maj0,np.full(len(h_maj),0),color='grey',alpha=1.0,zorder=1,label = 'Nugget')\n",
    "    if c1 > 0.0001: \n",
    "        plt.plot(h_maj,gam_maj1+gam_maj0,color = 'black',lw=1.5)\n",
    "        plt.fill_between(h_maj,gam_maj1+gam_maj0,gam_maj0,color='darkorange',alpha=1.0,zorder=1,label = 'Structure #1')\n",
    "    \n",
    "    if c2 > 0.0001:   \n",
    "        plt.plot(h_maj,gam_maj2+gam_maj1+gam_maj0,color = 'black',lw=1.5)\n",
    "        plt.fill_between(h_maj,gam_maj2+gam_maj1+gam_maj0,gam_maj1+gam_maj0,color='deepskyblue',alpha=1.0,zorder=1,label='Structure #2')\n",
    "    \n",
    "    plt.plot([0,2000],[1.0,1.0],color = 'black',ls='--')\n",
    "    plt.xlabel(r'Lag Distance $\\bf(h)$, (m)'); plt.ylabel(r'$\\gamma \\bf(h)$')\n",
    "    if azi_tol.value < 90.0:\n",
    "        plt.title('Major Directional NSCORE ' + feature + ' Variogram - Azi. ' + str(azimuth))\n",
    "    else: \n",
    "        plt.title('Omni Directional NSCORE ' + feature + ' Variogram ')\n",
    "\n",
    "    if c1 > 0.0001:\n",
    "        plt.vlines(hmaj1,0,1.8,color='black',lw=1.5); \n",
    "        plt.annotate('Structure 1 Range',[hmaj1-30,1.3],rotation=90.0);\n",
    "    if c2 > 0.0001:\n",
    "        plt.vlines(hmaj2,0,1.8,color='black',lw=2.0)\n",
    "        plt.annotate('Structure 2 Range',[hmaj2-30,1.3],color='black',rotation=90.0)\n",
    "    plt.xlim([0,1000]); plt.ylim([0,1.8])\n",
    "    plt.legend(loc=\"upper left\")\n",
    "    plt.grid(True)\n",
    "    \n",
    "    plt.subplot(222)                                    # plot experimental variogram\n",
    "    plt.scatter(lags2,gammas2,color = 'red',s = npps*0.03,label = 'Minor Azimuth ' +str(azimuth+90.0), alpha = 0.8,zorder=10)\n",
    "    plt.plot(h_min,gam_min,color = 'red',lw=3)\n",
    "    if nug > 0.0001:\n",
    "        plt.plot(h_min0,gam_min0,color = 'red',lw=1.5)\n",
    "        plt.fill_between(h_min,gam_min0,np.full(len(h_maj),0),color='grey',alpha=1.0,zorder=1,label = 'Nugget')\n",
    "    if c1 > 0.0001:  \n",
    "        plt.plot(h_min,gam_min1+gam_min0,color = 'red',lw=1.5)\n",
    "        plt.fill_between(h_min,gam_min1+gam_min0,gam_min0,color='darkorange',alpha=1.0,zorder=1,label = 'Structure #1')\n",
    "    \n",
    "    if c2 > 0.0001:   \n",
    "        plt.plot(h_min,gam_min2+gam_min1+gam_min0,color = 'red',lw=1.5)\n",
    "        plt.fill_between(h_min,gam_min2+gam_min1+gam_min0,gam_min1+gam_min0,color='deepskyblue',alpha=1.0,zorder=1,label='Structure #2')\n",
    "    \n",
    "    plt.plot([0,2000],[1.0,1.0],color = 'black',ls='--')\n",
    "    plt.xlabel(r'Lag Distance $\\bf(h)$, (m)')\n",
    "    plt.ylabel(r'$\\gamma \\bf(h)$')\n",
    "    if azi_tol.value < 90.0:\n",
    "        plt.title('Minor Directional NSCORE ' + feature + ' Variogram - Azi. ' + str(azimuth + 90.0))\n",
    "    else: \n",
    "        plt.title('Omni Directional NSCORE ' + feature + ' Variogram ')\n",
    "    if c1 > 0.0001:      \n",
    "        plt.vlines(hmin1,0,1.8,color='red',lw=1.5)\n",
    "        plt.annotate('Structure 1 Range',[hmin1-30,1.3],color='red',rotation=90.0)\n",
    "    if c2 > 0.0001:  \n",
    "        plt.vlines(hmin2,0,1.8,color='red',lw=2.0)\n",
    "        plt.annotate('Structure 2 Range',[hmin2-30,1.3],color='red',rotation=90.0)\n",
    "    plt.xlim([0,1000]); plt.ylim([0,1.8])\n",
    "    plt.legend(loc=\"upper left\")\n",
    "    plt.grid(True)\n",
    "    \n",
    "    plt.subplot(223)                                    # plot experimental variogram\n",
    "    plt.plot(h_new,gam_new,color = 'purple',lw=3)\n",
    "    if nug > 0.0001:\n",
    "        plt.plot(h_new0,gam_new0,color = 'purple',lw=1.5)\n",
    "        plt.fill_between(h_new,gam_new0,np.full(len(h_maj),0),color='grey',alpha=1.0,zorder=1,label = 'Nugget')\n",
    "    if c1 > 0.0001:\n",
    "        plt.plot(h_new,gam_new1+gam_new0,color = 'purple',lw=1.5)\n",
    "        plt.fill_between(h_new,gam_new1+gam_new0,gam_new0,color='darkorange',alpha=1.0,zorder=1,label = 'Structure #1')\n",
    "    \n",
    "    if c2 > 0.0001:   \n",
    "        plt.plot(h_new,gam_new2+gam_new1+gam_new0,color = 'purple',lw=1.5)\n",
    "        plt.fill_between(h_new,gam_new2+gam_new1+gam_new0,gam_new1+gam_new0,color='deepskyblue',alpha=1.0,zorder=1,label='Structure #2')\n",
    "    \n",
    "    plt.plot([0,2000],[1.0,1.0],color = 'black',ls='--')\n",
    "    plt.xlabel(r'Lag Distance $\\bf(h)$, (m)')\n",
    "    plt.ylabel(r'$\\gamma \\bf(h)$')\n",
    "    if azi_tol.value < 90.0:\n",
    "        plt.title('Interpolated ' + feature + ' Variogram - Azi. ' + str(new_azimuth))\n",
    "    else: \n",
    "        plt.title('Omni Directional NSCORE ' + feature + ' Variogram ')\n",
    "        \n",
    "    if c1 > 0.0001:\n",
    "        plt.vlines(hmaj1,0,1.8,color='black',lw=1.5); \n",
    "\n",
    "    if c1 > 0.0001:      \n",
    "        plt.vlines(hmin1,0,1.8,color='red',lw=1.5)\n",
    "        \n",
    "    if c2 > 0.0001:\n",
    "        plt.vlines(hmaj2,0,1.8,color='black',lw=1.5); \n",
    "  \n",
    "    if c2 > 0.0001:      \n",
    "        plt.vlines(hmin2,0,1.8,color='red',lw=1.5)\n",
    "    \n",
    "    # plt.vlines(hmin1,0,1.8,color='black',lw=1.5); plt.vlines(hmin2,0,1.8,color='deepskyblue',lw=1.5)\n",
    "    # plt.annotate('Structure 1 Range',[hmin1-30,1.3],rotation=90.0); plt.annotate('Structure 2 Range',[hmin2-30,1.3],color='deepskyblue',rotation=90.0)\n",
    "    plt.xlim([0,1000]); plt.ylim([0,1.8])\n",
    "    plt.legend(loc=\"upper left\")\n",
    "    plt.grid(True)\n",
    "    \n",
    "    plt.subplot(224)\n",
    "    \n",
    "    plt.xlim([-2000,2000]); plt.ylim([-2000,2000]); plt.xlabel('X offset (m)'); plt.ylabel('Y offset (m)')\n",
    "    plt.plot([-2000,2000],[0,0],color='grey',lw=3,zorder=1); plt.plot([0,0],[-2000,2000],color='grey',lw=3,zorder=1)\n",
    "    plt.grid(True); plt.title('2D Variogram Structures - Ranges and Geometric Anisotropy')\n",
    "    \n",
    "    if c1 > 0.0001:\n",
    "        e1 = patches.Ellipse((0, 0), hmaj1*2, hmin1*2,angle=90-azimuth, linewidth=2, fill=True,facecolor='darkorange',edgecolor='black',alpha=1.0,zorder=4)\n",
    "        plt.gca().add_patch(e1)\n",
    "    \n",
    "    if c2 > 0.0001:\n",
    "        e2 = patches.Ellipse((0, 0), hmaj2*2, hmin2*2,angle=90-azimuth, linewidth=2, fill=True,facecolor='deepskyblue',edgecolor='black',alpha=1.0,zorder=2)\n",
    "        plt.gca().add_patch(e2)\n",
    "    \n",
    "    xarr,yarr = point_pos(0, 0, hmaj1, azimuth)\n",
    "    plt.plot([0,xarr],[0,yarr],color='black',zorder=13)\n",
    "    \n",
    "    xarr2,yarr2 = point_pos(0, 0, hmaj2, azimuth)\n",
    "    plt.plot([0,xarr2],[0,yarr2],color='black',zorder=10)\n",
    "    plt.annotate('Major',[xarr2,yarr2],zorder=10)\n",
    "    \n",
    "    xarr1,yarr1 = point_pos(0, 0, hmin1, azimuth+90.0)\n",
    "    plt.plot([0,xarr1],[0,yarr1],color='red',zorder=13)\n",
    "    \n",
    "    xarr2,yarr2 = point_pos(0, 0, hmin2, azimuth+90.0)\n",
    "    plt.plot([0,xarr2],[0,yarr2],color='red',zorder=10)\n",
    "    plt.annotate('Minor',[xarr2,yarr2],zorder=10,color='red')\n",
    "    \n",
    "    xarr_new,yarr_new = point_pos(0, 0, 1200, new_azimuth)\n",
    "    plt.plot([0,xarr_new],[0,yarr_new],color='purple',zorder=10,lw=3)\n",
    "    plt.annotate('Interpolated Azimuth',[xarr_new,yarr_new],color='purple',zorder=10)\n",
    "    \n",
    "    plt.subplots_adjust(left=0.0, bottom=0.0, right=2.0, top=2.5, wspace=0.2, hspace=0.2)\n",
    "    plt.show()\n",
    "    \n",
    "# connect the function to make the samples and plot to the widgets    \n",
    "interactive_plot2 = widgets.interactive_output(f_make2, {'nug':nug, 'it1':it1, 'hmaj1':hmaj1, 'hmin1':hmin1, 'it2':it2, 'c2':c2, 'hmaj2':hmaj2, 'hmin2':hmin2,'new_azimuth':new_azimuth})\n",
    "interactive_plot2.clear_output(wait = True)               # reduce flickering by delaying plot updating  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interactive Nested Variogram Modeling Demostration - Nested Structures and Azimuth Interpolation\n",
    "\n",
    "* select the nested structures and their types, contributions and major and minor ranges, visualize structures and azimuth interpolated variogram \n",
    "\n",
    "#### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "\n",
    "##### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1) | [GeostatsPy](https://github.com/GeostatsGuy/GeostatsPy)\n",
    "\n",
    "### The Problem\n",
    "\n",
    "Fit a positive definite variogram model based on the addition of multiple structures each describing spatial components of the feature variance \n",
    "\n",
    "* **nug**: nugget effect\n",
    "\n",
    "* **c1 / c2**: contributions of the sill - note, **c1** is set at 1.0 - **nug** - **c2**\n",
    "\n",
    "* **hmaj1 / hmaj2**: range in the major direction\n",
    "\n",
    "* **hmin1 / hmin2**: range in the minor direction"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "817727ded11345c798e8ebce29d2e66c",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Text(value='               Variogram Modeling, Visualize Nested Structures and Azimuth Interpol…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "fb786ab1885f41bfb5ddeb0d86bdec55",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Output()"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "display(ui10, interactive_plot2)                           # display the interactive plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Comments\n",
    "\n",
    "This was a basic demonstration / exercise of variogram calculation and modeling for spatial continuity analysis. Much more could be done, I have other demonstrations on the basics of working with DataFrames, ndarrays, univariate statistics, plotting data, declustering, data transformations and many other workflows available at https://github.com/GeostatsGuy/PythonNumericalDemos and https://github.com/GeostatsGuy/GeostatsPy. \n",
    "  \n",
    "#### The Author:\n",
    "\n",
    "### Michael Pyrcz, Professor, The University of Texas at Austin \n",
    "*Novel Data Analytics, Geostatistics and Machine Learning Subsurface Solutions*\n",
    "\n",
    "With over 17 years of experience in subsurface consulting, research and development, Michael has returned to academia driven by his passion for teaching and enthusiasm for enhancing engineers' and geoscientists' impact in subsurface resource development. \n",
    "\n",
    "For more about Michael check out these links:\n",
    "\n",
    "#### [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)\n",
    "\n",
    "#### Want to Work Together?\n",
    "\n",
    "I hope this content is helpful to those that want to learn more about subsurface modeling, data analytics and machine learning. Students and working professionals are welcome to participate.\n",
    "\n",
    "* Want to invite me to visit your company for training, mentoring, project review, workflow design and / or consulting? I'd be happy to drop by and work with you! \n",
    "\n",
    "* Interested in partnering, supporting my graduate student research or my Subsurface Data Analytics and Machine Learning consortium (co-PIs including Profs. Foster, Torres-Verdin and van Oort)? My research combines data analytics, stochastic modeling and machine learning theory with practice to develop novel methods and workflows to add value. We are solving challenging subsurface problems!\n",
    "\n",
    "* I can be reached at mpyrcz@austin.utexas.edu.\n",
    "\n",
    "I'm always happy to discuss,\n",
    "\n",
    "*Michael*\n",
    "\n",
    "Michael Pyrcz, Ph.D., P.Eng. Professor, Cockrell School of Engineering and The Jackson School of Geosciences, The University of Texas at Austin\n",
    "\n",
    "#### More Resources Available at: [Twitter](https://twitter.com/geostatsguy) | [GitHub](https://github.com/GeostatsGuy) | [Website](http://michaelpyrcz.com) | [GoogleScholar](https://scholar.google.com/citations?user=QVZ20eQAAAAJ&hl=en&oi=ao) | [Book](https://www.amazon.com/Geostatistical-Reservoir-Modeling-Michael-Pyrcz/dp/0199731446) | [YouTube](https://www.youtube.com/channel/UCLqEr-xV-ceHdXXXrTId5ig)  | [LinkedIn](https://www.linkedin.com/in/michael-pyrcz-61a648a1)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
