# J. Duriez (jerome.duriez@irstea.fr)

# to import with yade/python/ipython solveLaplace_uc.py, or with execfile('solveLaplace_uc.py',globals()) once inside a yade/python/ipython session

from past.builtins import execfile

from builtins import range
execfile('solveLiqBridge.py',globals())

def solveLaplace_uc(theta,rRatio,uStar,delta1Cons,deltaZ,save):
    ''' Solves Laplace-Young equation for given r=rRatio, uc*=uStar and theta.\n
    Making recursively use of solveLiqBridge function, the present function identifies all possible bridges configurations for given r, uc*, and theta

    Input attributes are (see also solveLiqBridge function):
    :param theta: contact angle (deg)
    :param rRatio: rMax / rMin for the considered particles pair
    :param uStar: dimensionless capillary pressure
    :param delta1Cons: numpy.array of delta1 (see solveLiqBridge) values to consider
    :param deltaZ: see solveLiqBridge
    :param save: 0/1 to save text files including the capillary bridges data (if 1)

    :returns: a 2D numpy.array of capillary bridges data, possibly (in case save=1) also written in text files. Each data is a tuple with:
    \tinterparticle distance,
    \tuStar value,
    \tdimensionless volume (= vol /R2^3),
    \tdimensionless force = force/(2*pi*gamma*R2),
    \tdelta1/2 = filling angles on the 2 particles (degrees),
    \teStar = free energy/(gamma* R2^2),
    \tnn11 = 11-term of integral(ni nj dS) / R2^2 = 22-term,
    \tnn33 = 33-term, with 3 axis = the meniscus orientation (NB: surface / R2^2 = 2*nn11 + nn33)
    '''



    # Removal of possible d1=0 since such bridge is physically impossible, and
    # d1 = 0 might make trouble if theta = 0 (leads to inf. initial rhoPrime):
    delta1Cons = delta1Cons[delta1Cons!=0]
    # And of d1 values such that d1 + theta > 90
    delta1Cons = delta1Cons[delta1Cons + theta <= 90]


    #------------ All possible bridges for these r, uc*, theta: ---------------

    # initialization of bridge configurations data: as many lines as configurations
    # and 9 columns being [dist uc(=cst) V F delta1 delta2 E nn11 nn33]
    sol_u = -numpy.ones((len(delta1Cons),9))
    # list [[- 1 for j in range(9)] for j in range(len(delta1Cons)) ] would be a better choice than arrays in terms of sys.getsizeof (list of 100000*9 lines*columns~0.1 * numpy.array of 100000*9 lines*columns)
    # but seems much less convenient for indexing and testing signs below (list[4][:] is the same as list[:][4]....)

    minDzUsed = deltaZ
    # timeStartLoop = time.clock() 
    for i in range(0,len(delta1Cons)):
        dzUsed = deltaZ
        delta1 = delta1Cons[i]
        [dist,vol,force,delta1,delta2,eStar,nn11,nn33,out] = solveLiqBridge(rRatio,theta,uStar,delta1,dzUsed,0,0,0)
        while out == 0:
            dzUsed = dzUsed / 2.
            minDzUsed = min(dzUsed,minDzUsed)
            [dist,vol,force,delta1,delta2,eStar,nn11,nn33,out] = solveLiqBridge(rRatio,theta,uStar,delta1,dzUsed,0,0,0)
        sol_u[i,:] = [dist,uStar,vol,force,delta1,delta2,eStar,nn11,nn33]
    # timeEndLoop = time.clock() # gives the same measurements than time.time()
    # print 'Here, loop took '+str(timeEndLoop-timeStartLoop)+' s'

    # Get rid of unphysical solutions with negativ distances and/or volume:
    physSol = sol_u[ numpy.logical_and(sol_u[:,0]>=0,sol_u[:,2]>=0),: ]

    # Get rid of unstable physical solutions:
    # We use volume values for this purpose, see e.g. Duriez2017
    distRupt = max(physSol[:,0])
    # eRupt = physSol(physSol(:,1)==distRupt,7) # would not work since eRupt can be a global maximum
    # (when two branches are increasing with d*)
    vRupt = physSol[physSol[:,0]==distRupt,2]
    stabSol = physSol[physSol[:,2]>=vRupt,:]


    # ----------- Save of data in text files -----------
    # three text files are generated: one, "Raw", includes all computed data;
    # another, "Phys", restricts to physical solutions (with positiv distance
    # and volume); the last one, "Stab" restricts to the stable physical solutions
    if save !=0:
        if minDzUsed == deltaZ:
            strDz = 'With deltaZ = ' + str(deltaZ)
        else:
            strDz = 'With deltaZ between '+str(deltaZ)+' and '+str(minDzUsed)
        # Raw data: all liquid bridge configurations that have been computed
        nameFile = 'capDataRaw_r'+str(rRatio)+'_theta'+str(theta)+'_ucStar'+str(uStar)+'.txt'
        head_wE = 'dist*\tuc*\tV*\tF*\tdelta1 (deg)\tdelta2(deg)\tE* (-)\tn11 (-)\tn33 (-)\t' + strDz + '\n'
        writeFile(nameFile,head_wE,sol_u)

        # Physical data: after removing the configurations with negative distance or volume
        nameFile = 'capDataPhys_r'+str(rRatio)+'_theta'+str(theta)+'_ucStar'+str(uStar)+'.txt'
        writeFile(nameFile,head_wE,physSol)

        # Stab data: only the stable configurations, i.e. biggest volumes
        nameFile = 'capDataStab_r'+ str(rRatio)+'_theta'+str(theta)+'_ucStar'+str(uStar)+'.txt'
        head_woE = 'dist*\tuc*\tV*\tF*\tdelta1 (deg)\tdelta2(deg)\tn11 (-)\tn33 (-)\t'+strDz+'\n'
        columns = list(range(6)) # just an intermediate variable for below, as range(5).extend([7,8]) is impossible..
        columns.extend([7,8]) # columns.extend() as an argument of numpy.ix() would fail
        writeFile(nameFile,head_woE,stabSol[ numpy.ix_( list(range(stabSol.shape[0])) , columns ) ])

    # --------------------------------------------------
    return stabSol


def writeFile(nomFic,headLine,data):

    fic = open(nomFic,'w')
    numpy.savetxt(fic,data,delimiter='\t',header=headLine)
    fic.close()
