# geotecha - A software suite for geotechncial engineering
# Copyright (C) 2018  Rohan T. Walker (rtrwalker@gmail.com)
#
# 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, either version 3 of the License, or
# (at your option) any later version.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see http://www.gnu.org/licenses/gpl.html.

"""
Zhu and Yin (2012) "Analysis and mathematical solutions for consolidation
of a soil layer with depth-dependent parameters under confined compression".

"""
from __future__ import print_function, division

import numpy as np
from matplotlib import pyplot as plt
import math
import textwrap
import scipy.special
import geotecha.piecewise.piecewise_linear_1d as pwise

from geotecha.mathematics.root_finding import find_n_roots


besselj = scipy.special.jv
bessely = scipy.special.yv


def zhuandyin2012(z, t, alpha, p, q, drn=0, tpor=None, H = 1, kv0 = 1, mv0 = 0.1, gamw = 10,
                ui = 1, nterms = 20, plot_eigs=False):
    """Analysis and mathematical solutions for consolidation of a soil layer
    with depth-dependent parameters under confined compression.

    An implementation of Zhu and Yin (2012) [1]_.

    Features:

     - Single layer.
     - Permeability and volume compressibility can vary with depth with a
       power lay relationship.
     - Instant load uniform with depth.
     - PTIB ansd PTPB drainage conditions.
     - Pore pressure vs depth at variaous times
     - Degree of consolidation based on surface settlement vs time.
     - Settlement vs time.


    Parameters
    ----------
    z : float or 1d array/list of float
        Depth.
    t : float or 1d array/list of float
        Time for degree of consolidation calcs.
    tpor : float or 1d array/list of float
        Time for pore pressure vs depth calcs.
    alpha, p, q : float
        Exponent in depth dependence of permeability and compressibility
        respectively. e.g. mv = mv0 * (1+alpha*z/H)**q. Note  p/q cannot be
        2; alpha cannot be zero.
    drn : [0,1], optional
        Drainage. drn=0 is pervious top pervious bottom.  drn=1 is pervious
        bottom, impervious bottom.  default drn=0.
    tpor : float or 1d array/list of float, optional
        Time values for pore pressure vs depth calcs.  Default tpor=None i.e.
        time values will be taken from `t`.
    H : float, optional
        Drainage path length.  default H=1.
    kv0 : float, optional
        Vertical coefficient of permeability.  Default kv=1.
    mv0 : float, optional
        Volume compressibility. Default mv=0.1.
    gamw : float, optional
        Unit weight of water. Default gamw=10.
    ui : float, optional
        Initial uniform pore water pressure. Default ui=1.
    nterms : int, optional
        maximum number of series terms. default nterms=20.
    plot_eigs : True/False, optional
        If True then a plot of the characteristic curve and associated
        eigenvalues will be created.  Use plt.show after runnning the program
        to display the curve.  Use this to assess if the eigenvalues are
        correct.  Default plot_eigs=False.


    Returns
    -------
    por : 2d array of float
        Pore pressure at depth and time.  ppress is an array of size
        (len(z), len(t)).
    doc : 1d array of float
        Degree of consolidation based on surface settlement.
    settlement : 1d array of float
        Surface settlement at time values


    Notes
    -----

    kv = kv0 * (1+alp*z/H)**p
    mv = mv0 * (1+alp*z/H)**q


    References
    ----------
    Code based on theroy in [1]_

    ..[1] Zhu, G., and J. Yin. 2012. 'Analysis and Mathematical Solutions
          for Consolidation of a Soil Layer with Depth-Dependent Parameters
          under Confined Compression'. International Journal of Geomechanics
          12 (4): 451-61.



    """

    def Zmu(eta, mu, nu, b):
        """Depth fn"""

        return (bessely(nu, eta) * besselj(mu, eta * b) -
                besselj(nu, eta) * bessely(mu, eta * b))

    def Zmu_(eta, mu, nu, b):
        """derivative of Zmu"""

        return ((besselj(mu - 1.0, eta * b)/2.0 -
                besselj(mu + 1.0, eta * b)/2.0)*bessely(nu, eta) -
                (bessely(mu - 1.0, eta * b)/2.0 -
            bessely(mu + 1.0, eta * b)/2.0)*besselj(nu, eta))


    def drn1root(eta, mu, nu, b, p, n):
        """eigenvalue function"""
        return (1-p)/(2-n)*Zmu(eta, nu, nu, b) + eta * b * Zmu_(eta, nu, nu, b)




    z = np.atleast_1d(z)
    t = np.atleast_1d(t)

    if tpor is None:
        tpor = t
    else:
        tpor = np.atleast_1d(tpor)

    #derived parameters
    n = p-q
    b = (1 + alpha)**(1 - n/2)

    nu = abs((1 - p) / (2 - n))

#    print('drn', drn)
#    print('p',p)
#    print('q', q)
#    print('n', n)
#    print('b', b)
#    print('nu', nu)
#    print('(1 - p) / (2 - n)',(1 - p) / (2 - n))
#    plot_eigs=True
    if drn==0:
        etam = find_n_roots(Zmu, args=(nu, nu, b), n = nterms, p=1.01)
        if plot_eigs:
            fig=plt.figure(figsize=(20,5))
            ax = fig.add_subplot('111')
            x = np.linspace(0.01,etam[-1],1000)
            y = Zmu(x, nu, nu, b)
            ax.plot(x, y, marker='.', markersize=3, ls='-')
            ax.plot(etam,np.zeros_like(etam), 'o', markersize=6, )
            ax.set_ylim(-0.3,0.3)
            ax.set_xlabel('$\eta$')
            ax.set_ylabel('characteristic curve')
            ax.grid()
            fig.tight_layout()

    elif drn==1:
        etam = find_n_roots(drn1root, args=(nu, nu, b, p, n), n = nterms, p=1.01)
        if plot_eigs:
            fig=plt.figure(figsize=(20,5))
            ax = fig.add_subplot('111')
            x = np.linspace(0.01,etam[-1],1000)
            y = drn1root(x, nu, nu, b, p, n)
            ax.plot(x, y, marker='.', markersize=3, ls='-')
            ax.plot(etam,np.zeros_like(etam), 'o', markersize=6, )
            ax.set_ylim(-0.3,0.3)
            ax.set_xlabel('$\eta$')
            ax.set_ylabel('characteristic curve')
            ax.grid()
            fig.tight_layout()

    eta0=etam[0]

    etam = etam[np.newaxis, np.newaxis, :]


    cv0 = kv0 / (mv0 * gamw)
    dTv = cv0 * (2 - n)**2 * alpha**2 * eta0**2 / (np.pi**2 * H**2)


    y = (1 + alpha * z / H)**(1-n/2)
    y = y[:, np.newaxis, np.newaxis]


    T = dTv * tpor
    T = T[np.newaxis, :, np.newaxis]
    Tm = np.exp(-np.pi**2 * etam**2 / (4 * eta0**2) * T)


#    if np.allclose(tpor, t):
#        Tm_ = Tm.copy()

    Zm = Zmu(etam, nu, nu, y)

    if drn == 0:
#        if -nu == (p - 1) / (2 - n):
        if np.allclose(-nu, (p - 1) / (2 - n)):

            numer = 2*np.pi * (2 + np.pi * etam * b**(1-nu) * Zmu(etam, nu-1, nu, b))
            denom = 4-(b * np.pi * etam * Zmu(etam, 1 + nu, nu, b))**2
            Cm = numer/denom
        else:

            numer = 2*np.pi * (2 - np.pi * etam * b**(1+nu) * Zmu(etam, 1+nu, nu, b))
            denom = 4-(b * np.pi * etam * Zmu(etam, 1 + nu, nu, b))**2
            Cm = numer/denom
    elif drn == 1:

        numer = 4 * np.pi
        denom = 4 - (b * np.pi * etam * Zmu(etam, nu, nu, b))**2
        Cm = numer / denom

    por = Cm*Zm*Tm
    por *= ui * y**((1 - p)/(2 - n))
    por = np.sum(por, axis=2)


    #degree of consolidation
    etam = etam[0, :, :]
#    if np.allclose(tpor, t):
#        Tm=Tm_[0,:,:]
#    else:
    T = dTv * t
    T = T[:, np.newaxis]

    Tm = np.exp(-np.pi**2 * etam**2 / (4 * eta0**2) * T)

    if drn == 0:
#        if -nu == (p - 1) / (2 - n):
        if np.allclose(-nu, (p - 1) / (2 - n)):

            numer = (2 + np.pi * etam * b**(1-nu) * Zmu(etam, nu-1, nu, b))**2
            denom = etam**2 * ((b * np.pi * etam * Zmu(etam, 1 + nu, nu, b))**2-4)

            Cm = numer/denom
        else:
            print('got here')
            numer = (2 - np.pi * etam * b**(1+nu) * Zmu(etam, 1 + nu, nu, b))**2
            denom = etam**2 * ((b * np.pi * etam * Zmu(etam, 1 + nu, nu, b))**2-4)

            Cm = numer/denom
    elif drn == 1:
        numer = 1.0
        denom = etam**2*((b * np.pi * etam * Zmu(etam, nu, nu, b))**2-4)

        Cm = numer / denom

    doc = np.sum(Cm*Tm,axis=1)

    if drn==0:
        numer = 4*(1+q)
#        numer_settle = 4
    elif drn==1:
        numer = 16*(1+q)
#        numer_settle = 16
    denom = (2 - n) * ((1+alpha)**(1 + q) - 1)
#    denom_settle = (2 - n)

#    settle = mv0*ui*(1 - (H/alpha*numer_settle / denom_settle) * doc)

    fr = numer / denom
    doc*= -fr
    doc += 1

    settle = doc * ui* mv0*((1+alpha)**(1 + q) - 1)/(1+q)*H/alpha

#    print(",".join(['{:.3g}']*4).format((1 - p) / (2 - n), nu, t[0], doc[0]))
    return por, doc, settle

if __name__ == '__main__':
    import geotecha.plotting.one_d
    from geotecha.plotting.one_d import plot_vs_depth
    from geotecha.plotting.one_d import plot_vs_time

    nus=np.linspace(-5,5,30)
#    nus=np.linspace(2.1,4,30)
#    print(nus)
    nus=[3.0]
    for i in nus:




        ui = 100
        drn = 1
        nterms = 50
        mv0 = 1.2
        kv0 = 1.6
        H = 2.5
        alpha = 0.5
        q = 4
#        nu = i
#        p=((2+q)*nu-1)/(nu-1)
        p = -2



        z = np.linspace(0,H,20)
#        t = np.logspace(-2,-0.5,15)
        t = np.linspace(0,15,16)
#        tpor = np.array([0.3, 0.5, 0.7])
        tpor=t[np.array([2,4,9,13])]
#        tpor=t

        plot_eigs=False
        por, doc, settle = zhuandyin2012(
            z=z, t=t, alpha=alpha, p=p, q=q, drn=drn, tpor=tpor, H = H, kv0 = kv0, mv0 = mv0, gamw = 10,
                ui = 100, nterms = nterms, plot_eigs=plot_eigs)
#            z, t, alpha = alpha, p=p, q=q, drn=drn,tpor=tpor, nterms=nterms, plot_eigs=plot_eigs)
        print('z',repr(z))
        print('t', repr(t))
        print('por',repr(por))
        print('doc',repr(doc))
        print('set',repr(settle))
        x = np.linspace(0,1,11)
        kv = (1+alpha*x)**p
        mv = (1+alpha*x)**q
#        print('x', repr(x))
#        print('kv', repr(kv))
#        print('mv', repr(mv))
        fig=plt.figure()
        ax= fig.add_subplot('111')
        ax.plot(kv, x, label='kv', ls='-')
        ax.plot(mv, x, label='mv', ls='-')
        ax.legend()
        ax.invert_yaxis()

        fig_por=plot_vs_depth(por, z, line_labels=['{0:.3g}'.format(v) for v in tpor],
                                           prop_dict={'xlabel': 'Pore Pressure'})
        fig_por.get_axes()[0].grid()
#        fig_por.get_axes()[0].set_xlim(0,1.3)
        fig_doc=plot_vs_time(t,doc, prop_dict={'ylabel': "Degree of consolidation"})
        fig_doc.gca().invert_yaxis()
        fig_set=plot_vs_time(t,settle, prop_dict={'ylabel': "Settlement"})
        fig_set.gca().invert_yaxis()
    plt.show()