diff --git a/examples/borehole/borehole.py b/examples/borehole/borehole.py
new file mode 100644
index 0000000000000000000000000000000000000000..43dd71f21904a7ca7bec4e45a48fd3a37cc33879
--- /dev/null
+++ b/examples/borehole/borehole.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Nov 19 08:56:21 2018
+
+@author: farid
+"""
+import numpy as np
+
+
+def borehole(xx, *args):
+    """
+    BOREHOLE FUNCTION
+
+    Authors: Sonja Surjanovic, Simon Fraser University
+             Derek Bingham, Simon Fraser University
+    Questions/Comments: Please email Derek Bingham at dbingham@stat.sfu.ca.
+
+    Copyright 2013. Derek Bingham, Simon Fraser University.
+
+    THERE IS NO WARRANTY, EXPRESS OR IMPLIED. WE DO NOT ASSUME ANY LIABILITY
+    FOR THE USE OF THIS SOFTWARE.  If software is modified to produce
+    derivative works, such modified software should be clearly marked.
+    Additionally, this program is free software; you can redistribute it
+    and/or modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; version 2.0 of the License.
+    Accordingly, this program is distributed in the hope that it will be
+    useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+    of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    General Public License for more details.
+
+    For function details and reference information, see:
+        https://www.sfu.ca/~ssurjano/ishigami.html
+
+    Parameters
+    ----------
+    xx : array of shape (n_samples, n_params)
+        Input parameter sets. [rw, L, kw, Tu, Tl, Hu, Hl, r]
+
+    Returns
+    -------
+    output: dict
+        Water flow rate.
+
+    """
+    rw = xx[:, 0]
+    L = xx[:, 1]
+    Kw = xx[:, 2]
+    Tu = xx[:, 3]
+    Tl = xx[:, 4]
+    Hu = xx[:, 5]
+    Hl = xx[:, 6]
+    r = xx[:, 7]
+
+    frac1 = 2 * np.pi * Tu * (Hu-Hl)
+
+    frac2a = 2*L*Tu / (np.log(r/rw)*(rw**2)*Kw)
+    frac2b = Tu / Tl
+    frac2 = np.log(r/rw) * (1 + frac2a + frac2b)
+
+    y = frac1 / frac2
+
+    # Prepare output dict using standard bayesvalidrox format
+    output = {'x_values': np.zeros(1), 'flow rate [m$^3$/yr]': y}
+
+    return output
diff --git a/examples/borehole/data/valid_outputs.npy b/examples/borehole/data/valid_outputs.npy
new file mode 100644
index 0000000000000000000000000000000000000000..076320cebb087989776523cbfd6e1dd122c84b63
Binary files /dev/null and b/examples/borehole/data/valid_outputs.npy differ
diff --git a/examples/borehole/data/valid_samples.npy b/examples/borehole/data/valid_samples.npy
new file mode 100644
index 0000000000000000000000000000000000000000..27564f822ee863e5d75d7b8c71fcd8a763d5bc63
Binary files /dev/null and b/examples/borehole/data/valid_samples.npy differ
diff --git a/examples/borehole/test_borehole.py b/examples/borehole/test_borehole.py
new file mode 100644
index 0000000000000000000000000000000000000000..3097b78bc8fd221d18f254b2426b9fdff00987e8
--- /dev/null
+++ b/examples/borehole/test_borehole.py
@@ -0,0 +1,240 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+This test deals with the surrogate modeling of a Ishigami function.
+
+You will see how to:
+    Check the quality of your regression model
+    Perform sensitivity analysis via Sobol Indices
+
+Author: Farid Mohammadi, M.Sc.
+E-Mail: farid.mohammadi@iws.uni-stuttgart.de
+Department of Hydromechanics and Modelling of Hydrosystems (LH2)
+Institute for Modelling Hydraulic and Environmental Systems (IWS), University
+of Stuttgart, www.iws.uni-stuttgart.de/lh2/
+Pfaffenwaldring 61
+70569 Stuttgart
+
+Created on Wed Jul 10 2019
+
+"""
+
+import numpy as np
+import joblib
+
+# import bayesvalidrox
+# Add BayesValidRox path
+import sys
+sys.path.append("../../src/bayesvalidrox/")
+
+from pylink.pylink import PyLinkForwardModel
+from surrogate_models.inputs import Input
+from surrogate_models.surrogate_models import MetaModel
+from post_processing.post_processing import PostProcessing
+from bayes_inference.bayes_inference import BayesInference
+from bayes_inference.discrepancy import Discrepancy
+import matplotlib
+matplotlib.use('agg')
+
+if __name__ == "__main__":
+
+    # =====================================================
+    # =============   COMPUTATIONAL MODEL  ================
+    # =====================================================
+    Model = PyLinkForwardModel()
+
+    # Define model options
+    Model.link_type = 'Function'
+    Model.py_file = 'borehole'
+    Model.name = 'borehole'
+
+    Model.Output.names = ['flow rate [m$^3$/yr]']
+
+    # =====================================================
+    # =========   PROBABILISTIC INPUT MODEL  ==============
+    # =====================================================
+    Inputs = Input()
+
+    # borehole radius
+    Inputs.add_marginals()
+    Inputs.Marginals[0].name = '$r_w$'
+    Inputs.Marginals[0].dist_type = 'normal'
+    Inputs.Marginals[0].parameters = [0.10, 0.0161812]
+
+    # borehole length
+    Inputs.add_marginals()
+    Inputs.Marginals[1].name = '$L$'
+    Inputs.Marginals[1].dist_type = 'unif'
+    Inputs.Marginals[1].parameters = [1120, 1680]
+
+    # borehole hydraulic conductivity
+    Inputs.add_marginals()
+    Inputs.Marginals[2].name = '$K_w$'
+    Inputs.Marginals[2].dist_type = 'unif'
+    Inputs.Marginals[2].parameters = [9855, 12045]
+
+    # transmissivity of upper aquifer
+    Inputs.add_marginals()
+    Inputs.Marginals[3].name = '$T_u$'
+    Inputs.Marginals[3].dist_type = 'unif'
+    Inputs.Marginals[3].parameters = [63070, 115600]
+
+    # transmissivity of lower aquifer
+    Inputs.add_marginals()
+    Inputs.Marginals[4].name = '$T_l$'
+    Inputs.Marginals[4].dist_type = 'unif'
+    Inputs.Marginals[4].parameters = [63.1, 116]
+
+    # potentiometric head of upper aquifer
+    Inputs.add_marginals()
+    Inputs.Marginals[5].name = '$H_u$'
+    Inputs.Marginals[5].dist_type = 'unif'
+    Inputs.Marginals[5].parameters = [990, 1110]
+
+    # potentiometric head of lower aquifer
+    Inputs.add_marginals()
+    Inputs.Marginals[6].name = '$H_l$'
+    Inputs.Marginals[6].dist_type = 'unif'
+    Inputs.Marginals[6].parameters = [700, 820]
+
+    # radius of influence
+    Inputs.add_marginals()
+    Inputs.Marginals[7].name = '$r$'
+    Inputs.Marginals[7].dist_type = 'lognorm'
+    Inputs.Marginals[7].parameters = [7.71, 1.0056]
+
+    # =====================================================
+    # ======  POLYNOMIAL CHAOS EXPANSION METAMODELS  ======
+    # =====================================================
+    MetaModelOpts = MetaModel(Inputs, Model)
+
+    # Select your metamodel method
+    # 1) PCE (Polynomial Chaos Expansion) 2) aPCE (arbitrary PCE)
+    # 3) GPE (Gaussian Process Emulator)
+    MetaModelOpts.meta_model_type = 'aPCE'
+
+    # ------------------------------------------------
+    # ------------- PCE Specification ----------------
+    # ------------------------------------------------
+    # Select the sparse least-square minimization method for
+    # the PCE coefficients calculation:
+    # 1)OLS: Ordinary Least Square  2)BRR: Bayesian Ridge Regression
+    # 3)LARS: Least angle regression  4)ARD: Bayesian ARD Regression
+    # 5)FastARD: Fast Bayesian ARD Regression
+    # 6)BCS: Bayesian Compressive Sensing
+    # 7)OMP: Orthogonal Matching Pursuit
+    # 8)VBL: Variational Bayesian Learning
+    # 9)EBL: Emperical Bayesian Learning
+    MetaModelOpts.pce_reg_method = 'OMP'
+
+    # Bootstraping
+    # 1) normal 2) fast
+    MetaModelOpts.bootstrap_method = 'fast'
+    MetaModelOpts.n_bootstrap_itrs = 1#00
+
+    # Specify the max degree to be compared by the adaptive algorithm:
+    # The degree with the lowest Leave-One-Out cross-validation (LOO)
+    # error (or the highest score=1-LOO)estimator is chosen as the final
+    # metamodel. pce_deg accepts degree as a scalar or a range.
+    MetaModelOpts.pce_deg = 5
+
+    # q-quasi-norm 0<q<1 (default=1)
+    MetaModelOpts.pce_q_norm = 1.0
+
+    # Print summary of the regression results
+    # MetaModelOpts.verbose = True
+
+    # ------------------------------------------------
+    # ------ Experimental Design Configuration -------
+    # ------------------------------------------------
+    MetaModelOpts.add_ExpDesign()
+
+    # One-shot (normal) or Sequential Adaptive (sequential) Design
+    MetaModelOpts.ExpDesign.method = 'sequential'
+    MetaModelOpts.ExpDesign.n_init_samples = 50
+
+    # Sampling methods
+    # 1) random 2) latin_hypercube 3) sobol 4) halton 5) hammersley 6) korobov
+    # 7) chebyshev(FT) 8) grid(FT) 9) nested_grid(FT) 10)user
+    MetaModelOpts.ExpDesign.sampling_method = 'latin_hypercube'
+
+    # Provide the experimental design object with a hdf5 file
+    # MetaModelOpts.ExpDesign.hdf5_file = 'ExpDesign_borehole.hdf5'
+
+    # Sequential experimental design (needed only for sequential ExpDesign)
+    MetaModelOpts.ExpDesign.n_new_samples = 1
+    MetaModelOpts.ExpDesign.n_max_samples = 300  # 150
+    MetaModelOpts.ExpDesign.mod_LOO_threshold = 1e-16
+
+    # ------------------------------------------------
+    # ------- Sequential Design configuration --------
+    # ------------------------------------------------
+    # 1) None 2) 'equal' 3)'epsilon-decreasing' 4) 'adaptive'
+    MetaModelOpts.ExpDesign.tradeoff_scheme = None
+    # MetaModelOpts.ExpDesign.n_replication = 50
+    # -------- Exploration ------
+    # 1)'Voronoi' 2)'random' 3)'latin_hypercube' 4)'dual annealing'
+    MetaModelOpts.ExpDesign.explore_method = 'latin_hypercube'
+
+    # Use when 'dual annealing' chosen
+    MetaModelOpts.ExpDesign.max_func_itr = 200
+
+    # Use when 'Voronoi' or 'random' or 'latin_hypercube' chosen
+    MetaModelOpts.ExpDesign.n_canddidate = 5000
+    MetaModelOpts.ExpDesign.n_cand_groups = 4
+
+    # -------- Exploitation ------
+    # 1)'BayesOptDesign' 2)'VarOptDesign' 3)'alphabetic' 4)'Space-filling'
+    MetaModelOpts.ExpDesign.exploit_method = 'Space-filling'
+
+    # BayesOptDesign -> when data is available
+    # 1)DKL (Kullback-Leibler Divergence) 2)DPP (D-Posterior-percision)
+    # 3)APP (A-Posterior-percision)
+    # MetaModelOpts.ExpDesign.util_func = 'DKL'
+
+    # VarBasedOptDesign -> when data is not available
+    # Only with Vornoi >>> 1)Entropy 2)EIGF, 3)ALM, 4)LOOCV
+    MetaModelOpts.ExpDesign.util_func = 'ALM'
+
+    # alphabetic
+    # 1)D-Opt (D-Optimality) 2)A-Opt (A-Optimality)
+    # 3)K-Opt (K-Optimality)
+    # MetaModelOpts.ExpDesign.util_func = 'D-Opt'
+
+    MetaModelOpts.valid_samples = np.load("data/valid_samples.npy")
+    MetaModelOpts.valid_model_runs = {
+        'flow rate [m$^3$/yr]': np.load("data/valid_outputs.npy")
+        }
+    # >>>>>>>>>>>>>>>>>>>>>> Build Surrogate <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+    # Adaptive sparse arbitrary polynomial chaos expansion
+    # PCEModel = MetaModelOpts.create_metamodel()
+    from surrogate_models.meta_model_engine import MetaModelEngine
+    meta_model_engine = MetaModelEngine(MetaModelOpts)
+    meta_model_engine.run()
+    PCEModel = meta_model_engine.MetaModel
+
+    # Save PCE models
+    with open(f'PCEModel_{Model.name}.pkl', 'wb') as output:
+        joblib.dump(PCEModel, output, 2)
+
+    # =====================================================
+    # =========  POST PROCESSING OF METAMODELS  ===========
+    # =====================================================
+    PostPCE = PostProcessing(PCEModel)
+
+    # Plot to check validation visually.
+    PostPCE.valid_metamodel(n_samples=200)
+
+    # Check the quality of your regression model
+    PostPCE.check_reg_quality()
+
+    # PostPCE.eval_PCEmodel_3D()
+    # Compute and print RMSE error
+    PostPCE.check_accuracy(n_samples=3000)
+
+    # Plot the evolution of the KLD,BME, and Modified LOOCV error
+    if MetaModelOpts.ExpDesign.method == 'sequential':
+        PostPCE.plot_seq_design_diagnostics()
+
+    # Plot the sobol indices
+    total_sobol = PostPCE.sobol_indices(plot_type='bar')