=======================
Detailed Usage Examples
=======================


Each model, including the secondary phase models, implements all diagnostic functions. The following is a set of examples to highlight functionality.


.. code-block:: python

    from petbox import dca
    from data import rate as data_q, time as data_t
    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib as mpl

    plt.style.use('seaborn-v0_8-white')
    plt.rcParams['font.size'] = 16


.. code-block:: python

    # Setup time series for Forecasts and calculate cumulative production of data

    # We have this function handy
    t = dca.get_time(n=1001)

    # Calculate cumulative volume array of data
    data_N = np.cumsum(data_q * np.r_[data_t[0], np.diff(data_t)])

    # Calculate diagnostic functions D, beta, and b
    data_D = -dca.bourdet(data_q, data_t, L=0.35, xlog=False, ylog=True)
    data_beta = data_D * data_t
    data_b = dca.bourdet(1 / data_D, data_t, L=0.25, xlog=False, ylog=False)


Primary Phase Decline Curve Models
==================================

Modified Hyperbolic Model
-------------------------

*Robertson, S. 1988. Generalized Hyperbolic Equation. Available from SPE, Richardson, Texas, USA. SPE-18731-MS.*

.. code-block:: python

    mh = dca.MH(qi=725, Di=0.85, bi=0.6, Dterm=0.2)
    q_mh = mh.rate(t)
    N_mh = mh.cum(t)
    D_mh = mh.D(t)
    b_mh = mh.b(t)
    beta_mh = mh.beta(t)
    N_mh *= data_N[-1] / mh.cum(data_t[-1])


Transient Hyperbolic Model
--------------------------

*Fulford, D. S., and Blasingame, T. A. 2013. Evaluation of Time-Rate Performance of Shale Wells using the Transient Hyperbolic Relation. Presented at SPE Unconventional Resources Conference – Canada in Calgary, Alberta, Canda, 5–7 November. SPE-167242-MS. https://doi.org/10.2118/167242-MS.*

.. code-block:: python

    thm = dca.THM(qi=750, Di=.8, bi=2, bf=.5, telf=28)
    q_trans = thm.transient_rate(t)
    N_trans = thm.transient_cum(t)
    D_trans = thm.transient_D(t)
    b_trans = thm.transient_b(t)
    beta_trans = thm.transient_beta(t)
    N_trans *= data_N[-1] / thm.transient_cum(data_t[-1])


Transient Hyperbolic Model Analytic Approximation
-------------------------------------------------

*Fulford, D.S. 2018. A Model-Based Diagnostic Workflow for Time-Rate Performance of Unconventional Wells. Presented at Unconventional Resources Conference in Houston, Texas, USA, 23–25 July. URTeC-2903036. https://doi.org/10.15530/urtec-2018-2903036.*

.. code-block:: python

    q_thm = thm.rate(t)
    N_thm = thm.cum(t)
    D_thm = thm.D(t)
    b_thm = thm.b(t)
    beta_thm = thm.beta(t)
    N_thm *= data_N[-1] / thm.cum(data_t[-1])


Timing Comparison
~~~~~~~~~~~~~~~~~

If performance is a consideration, the approximation is much faster.

.. code-block:: python

    >>> %timeit thm.transient_rate(t)
    64.9 ms ± 5.81 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)


.. code-block:: python

    >>> %timeit thm.rate(t)
    86.9 µs ± 5.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)``


Power-Law Exponential Model
---------------------------

*Ilk, D., Perego, A. D., Rushing, J. A., and Blasingame, T. A. 2008. Exponential vs. Hyperbolic Decline in Tight Gas Sands – Understanding the Origin and Implications for Reserve Estimates Using Arps Decline Curves. Presented at SPE Annual Technical Conference and Exhibition in Denver, Colorado, USA, 21–24 September. SPE-116731-MS. https://doi.org/10.2118/116731-MS.*

*Ilk, D., Rushing, J. A., and Blasingame, T. A. 2009. Decline Curve Analysis for HP/HT Gas Wells: Theory and Applications. Presented at SPE Annual Technical Conference and Exhibition in New Orleands, Louisiana, USA, 4–7 October. SPE-125031-MS. https://doi.org/10.2118/125031-MS.*

.. code-block:: python

    ple = dca.PLE(qi=750, Di=.1, Dinf=.00001, n=.5)
    q_ple = ple.rate(t)
    N_ple = ple.cum(t)
    D_ple = ple.D(t)
    b_ple = ple.b(t)
    beta_ple = ple.beta(t)
    N_ple *= data_N[-1] / ple.cum(data_t[-1])


Stretched Exponential
---------------------

*Valkó, P. P. Assigning Value to Stimulation in the Barnett Shale: A Simultaneous Analysis of 7000 Plus Production Histories and Well Completion Records. 2009. Presented at SPE Hydraulic Fracturing Technology Conference in College Station, Texas, USA, 19–21 January. SPE-119369-MS. https://doi.org/10.2118/119369-MS.*

.. code-block:: python

    se = dca.SE(qi=715, tau=90.0, n=.5)
    q_se = se.rate(t)
    N_se = se.cum(t)
    D_se = se.D(t)
    b_se = se.b(t)
    beta_se = se.beta(t)
    N_se *= data_N[-1] / se.cum(data_t[-1])


Duong Model
-----------

*Duong, A. N. 2001. Rate-Decline Analysis for Fracture-Dominated Shale Reservoirs. SPE Res Eval & Eng 14 (3): 377–387. SPE-137748-PA. https://doi.org/10.2118/137748-PA.*

.. code-block:: python

    dg = dca.Duong(qi=715, a=2.8, m=1.4)
    q_dg = dg.rate(t)
    N_dg = dg.cum(t)
    D_dg = dg.D(t)
    b_dg = dg.b(t)
    beta_dg = dg.beta(t)
    N_dg *= data_N[-1] / dg.cum(data_t[-1])


Primary Phase Diagnostic Plots
==============================

Rate and Cumulative Production Plots
------------------------------------

.. code-block:: python

    # Rate vs Time
    fig = plt.figure(figsize=(15, 7.5))
    ax1 = fig.add_subplot(121)
    ax2 = fig.add_subplot(122)

    ax1.plot(data_t, data_q, 'o', mfc='w', label='Data')
    ax1.plot(t, q_trans, label='THM Transient')
    ax1.plot(t, q_thm, ls='--', label='THM Approx')
    ax1.plot(t, q_mh, label='MH')
    ax1.plot(t, q_ple, label='PLE')
    ax1.plot(t, q_se, label='SE')
    ax1.plot(t, q_dg, label='Duong')

    ax1.set(xscale='log', yscale='log', ylabel='Rate, BPD', xlabel='Time, Days')
    ax1.set(ylim=(1e0, 1e4), xlim=(1e0, 1e4))
    ax1.set_aspect(1)
    ax1.grid()
    ax1.legend()

    # Cumulative Volume vs Time
    ax2.plot(data_t, data_N, 'o', mfc='w', label='Data')
    ax2.plot(t, N_trans, label='THM Transient')
    ax2.plot(t, N_thm, ls='--', label='THM Approx')
    ax2.plot(t, N_mh, label='MH')
    ax2.plot(t, N_ple, label='PLE')
    ax2.plot(t, N_se, label='SE')
    ax2.plot(t, N_dg, label='Duong')

    ax2.set(xscale='log', yscale='log', ylim=(1e2, 1e6), xlim=(1e0, 1e4))
    ax2.set(ylabel='Cumulative Volume, MBbl', xlabel='Time, Days')
    ax2.set_aspect(1)
    ax2.grid()
    ax2.legend()

    plt.savefig(img_path / 'model.png')

.. image:: img/model.png

Diagnostic Function Plots
-------------------------

.. code-block:: python

    fig = plt.figure(figsize=(15, 15))
    ax1 = fig.add_subplot(221)
    ax2 = fig.add_subplot(222)
    ax3 = fig.add_subplot(223)
    ax4 = fig.add_subplot(224)

    # D-parameter vs Time
    ax1.plot(data_t, data_D, 'o', mfc='w', label='Data')
    ax1.plot(t, D_trans, label='THM Transient')
    ax1.plot(t, D_thm, ls='--', label='THM Approx')
    ax1.plot(t, D_mh, label='MH')
    ax1.plot(t, D_ple, label='PLE')
    ax1.plot(t, D_se, label='SE')
    ax1.plot(t, D_dg, label='Duong')
    ax1.set(xscale='log', yscale='log', ylim=(1e-4, 1e0))
    ax1.set(ylabel='$D$-parameter, Days$^{-1}$', xlabel='Time, Days')

    # beta-parameter vs Time
    ax2.plot(data_t, data_D * data_t, 'o', mfc='w', label='Data')
    ax2.plot(t, beta_trans, label='THM Transient')
    ax2.plot(t, beta_thm, ls='--', label='THM Approx')
    ax2.plot(t, beta_mh, label='MH')
    ax2.plot(t, beta_ple, label='PLE')
    ax2.plot(t, beta_se, label='SE')
    ax2.plot(t, beta_dg, label='Duong')
    ax2.set(xscale='log', yscale='log', ylim=(1e-2, 1e2))
    ax2.set(ylabel=r'$\beta$-parameter, Dimensionless', xlabel='Time, Days')

    # b-parameter vs Time
    ax3.plot(data_t, data_b, 'o', mfc='w', label='Data')
    ax3.plot(t, b_trans, label='THM Transient')
    ax3.plot(t, b_thm, ls='--', label='THM Approx')
    ax3.plot(t, b_mh, label='MH')
    ax3.plot(t, b_ple, label='PLE')
    ax3.plot(t, b_se, label='SE')
    ax3.plot(t, b_dg, label='Duong')
    ax3.set(xscale='log', yscale='linear', ylim=(0., 4.))
    ax3.set(ylabel='$b$-parameter, Dimensionless', xlabel='Time, Days')

    # q/N vs Time
    ax4.plot(data_t, data_q / data_N, 'o', mfc='w', label='Data')
    ax4.plot(t, q_trans / N_trans, label='THM Transient')
    ax4.plot(t, q_thm / N_thm, ls='--', label='THM Approx')
    ax4.plot(t, q_mh / N_mh, label='MH')
    ax4.plot(t, q_ple / N_ple, label='PLE')
    ax4.plot(t, q_se / N_se, label='SE')
    ax4.plot(t, q_dg / N_dg, label='Duong')
    ax4.set(xscale='log', yscale='log', ylim=(1e-7, 1e0), xlim=(1e0, 1e7))
    ax4.set(ylabel='$q_o / N_p$, Days$^{-1}$', xlabel='Time, Days')

    for ax in [ax1, ax2, ax3, ax4]:
        if ax != ax4:
            ax.set(xlim=(1e0, 1e4))
        if ax != ax3:
            ax.set_aspect(1)
        ax.grid()
        ax.legend()


    plt.savefig(img_path / 'diagnostics.png')


.. image:: img/diagnostics.png


Secondary Phase Decline Curve Models
====================================

Power-Law GOR/CGR Model
-----------------------

*Fulford, D.S. 2018. A Model-Based Diagnostic Workflow for Time-Rate Performance of Unconventional Wells. Presented at Unconventional Resources Conference in Houston, Texas, USA, 23–25 July. URTeC-2903036. https://doi.org/10.15530/urtec-2018-2903036.*

.. code-block:: python

    thm = dca.THM(qi=750, Di=.8, bi=2, bf=.5, telf=28)
    thm.add_secondary(dca.PLYield(c=1000, m0=-0.1, m=0.8, t0=2 * 365.25 / 12, max=10_000))


Secondary Phase Diagnostic Plots
================================

Rate and Cumluative Production Plots
------------------------------------

Numeric calculation provided to verify analytic relationships

.. code-block:: python

    fig = plt.figure(figsize=(15, 15))
    ax1 = fig.add_subplot(221)
    ax2 = fig.add_subplot(222)
    ax3 = fig.add_subplot(223)
    ax4 = fig.add_subplot(224)


    # Rate vs Time
    q = thm.rate(t)
    g = thm.secondary.rate(t) / 1000.0
    y = thm.secondary.gor(t)

    ax1.plot(t, q, c='C2', label='Oil')
    ax1.plot(t, g, c='C3', label='Gas')
    ax1.plot(t, y, c='C1', label='GOR')
    ax1.set(xscale='log', yscale='log', xlim=(1e0, 1e5), ylim=(1e0, 1e5))
    ax1.set(ylabel='Rate or GOR, BPD, MCFD, or scf/Bbl', xlabel='Time, Days')


    # Cumulative Volume vs Time
    q_N = thm.cum(t)
    g_N = thm.secondary.cum(t) / 1000.0
    _g_N = np.cumsum(g * np.diff(t, prepend=0))

    ax2.plot(t, q_N, c='C2', label='Oil')
    ax2.plot(t, g_N, c='C3', label='Gas')
    ax2.plot(t, _g_N, c='k', ls=':', label='Gas (numeric)')
    ax2.plot(t, y, c='C1', label='GOR')
    ax2.set(xscale='log', yscale='log', xlim=(1e0, 1e5), ylim=(1e2, 1e7))
    ax2.set(ylabel='Rate, Dimensionless', xlabel='Time, Days')
    ax2.set(ylabel='Cumulative Volume or GOR, MBbl, MMcf, or scf/Bbl', xlabel='Time, Days')


    # Time vs Monthly Volume
    q_MN = thm.monthly_vol_equiv(t)
    g_MN = thm.secondary.monthly_vol_equiv(t) / 1000.0
    _g_MN = np.diff(np.cumsum(g * np.diff(t, prepend=0)), prepend=0) \
        / np.diff(t, prepend=0) * dca.DAYS_PER_MONTH

    ax3.plot(t, q_MN, c='C2', label='Oil')
    ax3.plot(t, g_MN, c='C3', label='Gas')
    ax3.plot(t, _g_MN, c='k', ls=':', label='Gas (numeric)')
    ax3.plot(t, y, c='C1', label='GOR')
    ax3.set(xscale='log', yscale='log', xlim=(1e0, 1e5), ylim=(1e0, 1e5))
    ax3.set(ylabel='Monthly Volume or GOR, MBbl, MMcf, or scf/Bbl', xlabel='Time, Days')


    # Time vs Interval Volume
    q_IN = thm.interval_vol(t, t0=0.0)
    g_IN = thm.secondary.interval_vol(t, t0=0.0) / 1000.0
    _g_IN = np.diff(np.cumsum(g * np.diff(t, prepend=0)), prepend=0)

    ax4.plot(t, q_IN, c='C2', label='Oil')
    ax4.plot(t, g_IN, c='C3', label='Gas')
    ax4.plot(t, _g_IN, c='k', ls=':', label='Gas (numeric)')
    ax4.plot(t, y, c='C1', label='GOR')
    ax4.set(xscale='log', yscale='log', xlim=(1e0, 1e5), ylim=(1e0, 1e5))
    ax4.set(ylabel='$\Delta$Volume or GOR, MBbl, MMcf, or scf/Bbl', xlabel='Time, Days')

    for ax in [ax1, ax2, ax3, ax4]:
        ax.set_aspect(1)
        ax.grid()
        ax.legend()

    plt.savefig(img_path / 'secondary_model.png')


.. image:: img/secondary_model.png


Diagnostic Function Plots
-------------------------

.. code-block:: python

    fig = plt.figure(figsize=(15, 15))
    ax1 = fig.add_subplot(221)
    ax2 = fig.add_subplot(222)
    ax3 = fig.add_subplot(223)
    ax4 = fig.add_subplot(224)

    # D-parameter vs Time
    q_D = thm.D(t)
    g_D = thm.secondary.D(t)
    _g_D = -np.gradient(np.log(thm.secondary.rate(t) / 1000.0), t)

    ax1.plot(t, q_D, c='C2', label='Oil')
    ax1.plot(t, g_D, c='C3', label='Gas')
    ax1.plot(t, _g_D, c='k', ls=':', label='Gas (numeric)')
    ax1.set(xscale='log', yscale='log', xlim=(1e0, 1e4), ylim=(1e-4, 1e0))
    ax1.set(ylabel='$D$-parameter, Days$^{-1}$', xlabel='Time, Days')

    # beta-parameter vs Time
    q_beta = thm.beta(t)
    g_beta = thm.secondary.beta(t)
    _g_beta = _g_D * t

    ax2.plot(t, q_beta, c='C2', label='Oil')
    ax2.plot(t, g_beta, c='C3', label='Gas')
    ax2.plot(t, _g_beta, c='k', ls=':', label='Gas (numeric)')
    ax2.set(xscale='log', yscale='log', xlim=(1e0, 1e4), ylim=(1e-2, 1e2))
    ax2.set(ylabel=r'$\beta$-parameter, Dimensionless', xlabel='Time, Days')

    # b-parameter vs Time
    q_b = thm.b(t)
    g_b = thm.secondary.b(t)
    _g_b = np.gradient(1.0 / _g_D, t)

    ax3.plot(t, q_b, c='C2', label='Oil')
    ax3.plot(t, g_b, c='C3', label='Gas')
    ax3.plot(t, _g_b, c='k', ls=':', label='Gas (numeric)')
    ax3.set(xscale='log', yscale='linear', xlim=(1e0, 1e4), ylim=(-2, 4))
    ax3.set(ylabel='$b$-parameter, Dimensionless', xlabel='Time, Days')

    # q/N vs Time
    q_Ng = thm.rate(t) / thm.cum(t)
    g_Ng = thm.secondary.rate(t) / thm.secondary.cum(t)
    _g_Ng = thm.secondary.rate(t) / np.cumsum(thm.secondary.rate(t) * np.diff(t, prepend=0))

    ax4.plot(t, q_Ng, c='C2', label='Oil')
    ax4.plot(t, g_Ng, c='C3', ls='--', label='Gas')
    ax4.plot(t, _g_Ng, c='k', ls=':', label='Gas (numeric)')
    ax4.set(xscale='log', yscale='log', ylim=(1e-7, 1e0), xlim=(1e0, 1e7))
    ax4.set(ylabel='$q_o / N_p$, Days$^{-1}$', xlabel='Time, Days')

    for ax in [ax1, ax2, ax3, ax4]:
        if ax != ax3:
            ax.set_aspect(1)
        ax.grid()
        ax.legend()

    plt.savefig(img_path / 'sec_diagnostic_funs.png')


.. image:: img/sec_diagnostic_funs.png


Additional Diagnostic Plots
---------------------------

Numeric calculation provided to verify analytic relationships


.. code-block:: python

    fig = plt.figure(figsize=(15, 15))
    ax1 = fig.add_subplot(221)
    ax2 = fig.add_subplot(222)
    ax3 = fig.add_subplot(223)

    # D-parameter vs Time
    q_D = thm.D(t)
    g_D = thm.secondary.D(t)
    _g_D = -np.gradient(np.log(thm.secondary.rate(t)), t)

    ax1.plot(t, q_D, c='C2', label='Oil')
    ax1.plot(t, g_D, c='C3', label='Gas')
    ax1.plot(t, _g_D, c='k', ls=':', label='Gas(numeric)')
    ax1.set(xscale='log', yscale='linear', xlim=(1e0, 1e5), ylim=(None, None))
    ax1.set(ylabel='$D$-parameter, 1 / Days', xlabel='Time, Days')

    # Secant Effective Decline vs Time
    secant_from_nominal = dca.MultisegmentHyperbolic.secant_from_nominal
    dpy = dca.DAYS_PER_YEAR

    q_Dn = [secant_from_nominal(d * dpy, b) for d, b in zip(q_D, thm.b(t))]
    g_Dn = [secant_from_nominal(d * dpy, b) for d, b in zip(g_D, thm.secondary.b(t))]
    _g_Dn = [secant_from_nominal(d * dpy, b) for d, b in zip(_g_D, np.gradient(1 / _g_D, t))]

    ax2.plot(t, q_Dn, c='C2', label='Oil')
    ax2.plot(t, g_Dn, c='C3', label='Gas')
    ax2.plot(t, _g_Dn, c='k', ls=':', label='Gas (numeric)')
    ax2.set(xscale='log', yscale='linear', xlim=(1e0, 1e5), ylim=(-.5, 1.025))
    ax2.yaxis.set_major_formatter(mpl.ticker.PercentFormatter(xmax=1))
    ax2.set(ylabel='Secant Effective Decline, % / Year', xlabel='Time$ Days')

    # Tangent Effective Decline vs Time
    ax3.plot(t, 1 - np.exp(-q_D * dpy), c='C2', label='Oil')
    ax3.plot(t, 1 - np.exp(-g_D * dpy), c='C3', label='Gas')
    ax3.plot(t, 1 - np.exp(-_g_D * dpy), c='k', ls=':', label='Gas (numeric)')
    ax3.set(xscale='log', yscale='linear', xlim=(1e0, 1e5), ylim=(-1.025, 1.025))
    ax3.yaxis.set_major_formatter(mpl.ticker.PercentFormatter(xmax=1))
    ax3.set(ylabel='Tangent Effective Decline, % / Day', xlabel='Time, Days')

    for ax in [ax1, ax2, ax3]:
        ax.grid()
        ax.legend()

    plt.savefig(img_path / 'sec_decline_diagnostics.png')


.. image:: img/sec_decline_diagnostics.png
