from CoolProp import CoolProp as CP
from PDSim.misc.datatypes import Collector
import numpy as np
import matplotlib.pyplot as plt
from scipy.odr import *
import textwrap

#
#fluid = 'Propane'
#Rfluid = 'REFPROP-propane'
#e_k = 263.88
#sigma = 0.49748
#
fluid = 'DimethylEther'
Rfluid = 'REFPROP-DME'
e_k = 329.72
sigma = 0.5529
molemass = CP.Props(fluid,'molemass')

Ttriple = CP.Props(fluid,'Ttriple')
Tcrit = CP.Props(fluid,'Tcrit')
rhocrit = CP.Props(fluid,'rhocrit')
n = 6
m = 3
NP = 1
Nb = 0
N = (n-1)*(m+1)+3+Nb

mu,mu_dilute,RHO,TTT = Collector(),Collector(),Collector(),Collector()

rhomax = CP.Props('D','T',Ttriple,'Q',0,fluid)
#Build a database of "experimental" data
for T in np.linspace(Ttriple,Tcrit+30,400):
    for rho in np.linspace(1e-10,rhomax,400):
        muval = CP.Props('V','T',T,'D',rho,Rfluid)
        mudilute = CP.viscosity_dilute(fluid,T,rho,e_k,sigma)
        
        #Want positive value, and single-phase
        if (muval > 0 and T > Tcrit or rho > CP.rhosatL_anc(fluid,T) or rho < CP.rhosatV_anc(fluid,T)):
            mu << muval
            mu_dilute << mudilute
            TTT << T
            RHO << rho

from CoolProp.Plots.Plots import Trho
Trho(fluid)
plt.plot(RHO.vec,TTT.vec,'.')
plt.show()

#tau = np.array(TTT.vec)/Tcrit
tau = np.array(TTT.vec)/Tcrit
delta = np.array(RHO.vec)/rhocrit
Tstar = np.array(TTT.vec)/e_k

#Define the objective function
def OBJECTIVE_fit(c,x):
    tau = x[0,:]
    delta = x[1,:]
    #Unpack the inputs into e matrix and f vector
    e = np.zeros((n+1,m+1))
    
    sum = 0
    k = 0
    for i in range(2,n+1):
        for j in range(0,m+1):
            e[i][j] = c[k]
            sum += e[i][j]*delta**i/tau**j
            k += 1
            
    for o in range(0,NP):
        f1 = c[k+o*3]
        g1 = c[k+1+o*3]
        g2 = c[k+2+o*3]
    
    delta_0 = g1*(1+g2*tau**0.5)
    sum += f1*(delta/(delta_0-delta)-delta/delta_0)
    return sum + np.array(mu_dilute.vec)
    
print 'starting fit'
XXX = np.r_[np.array(tau,ndmin = 2), np.array(delta,ndmin=2)]
mod = Model(OBJECTIVE_fit)
mydata = Data(XXX.copy(), np.array(mu.vec))
beta0  = [1 for _ in range(N)]
myodr = ODR(mydata, mod, beta0=beta0)
myoutput = myodr.run()
myoutput.pprint()
print myoutput.sum_square
YFIT = OBJECTIVE_fit(myoutput.beta,XXX)
plt.plot(np.array(mu.vec),YFIT/np.array(mu.vec),'o')
plt.show()

rel_error = (YFIT)/np.array(mu.vec)-1
MAE = np.mean(np.abs(rel_error))*100
SSE = np.sum(np.power(YFIT-np.array(mu.vec),2))
print SSE

def write_output(c):
    e = np.zeros((n+1,m+1))
    k = 0
    edata = ''
    for i in range(2,n+1):
        erow = ''
        for j in range(0,m+1):
            e[i][j] = c[k]
            erow += 'e[{i:d}][{j:d}] = {val:0.16g}; '.format(val = e[i][j],i=i,j=j)
            k += 1
        edata += erow + '\n'
    f1 = c[k]
    g1 = c[k+1]
    g2 = c[k+2]
        
    template = textwrap.dedent(
    """
    double {name:s}Class::viscosity_Trho(double T, double rho)
    {{
        // This function was generated by fitting REFPROP ECS data
        // to the functional form of Vogel, 1998 (propane viscosity)
        // The script entitled dev/fit_avoid_ECS.py was used to make this 
        // function.  The mean absolute error of the fit is equal to
        // {MAE:g} % 
        
        double delta_0, sum, DELTA_H_eta, e_k, sigma, tau, delta;
        double e[{n:d}+1][{m:d}+1];
        
        tau = T/reduce.T; //[Opposite to normal definition]
        delta = rho/reduce.rho;
        
        // Load the coefficients
        double f1 = {f1:0.16g}, g1 = {g1:0.16g}, g2 = {g2:0.16g};
        for (int i=0;i<={n:d};i++){{ for(int j=0;j<={m:d};j++){{ e[i][j]=0.0; }} }}
        {edata:s}
        delta_0=g1*(1+g2*sqrt(tau)); //[no units]
        sum=0;
        for (int i=2;i<={n:d};i++){{
            for (int j=0;j<={m:d};j++){{
                sum += e[i][j]*pow(delta,i)/pow(tau,j);
            }}
        }}
        DELTA_H_eta = sum + f1*(delta/(delta_0-delta)-delta/delta_0); //[Pa-s]
        
        try{{
            // Get the ECS params for the fluid if it has them
            ECSParams(&e_k,&sigma);
        }}
        catch(NotImplementedError)
        {{
            throw ValueError(format("Your fluid does not implement ECSParams"));
        }}
        
        return viscosity_dilute(T,e_k,sigma) + DELTA_H_eta;
    }} 
    """
    )
    
    values = dict(f1 = f1,
                  g1 = g1,
                  g2 = g2,
                  n = n,
                  m = m,
                  name = fluid,
                  edata = edata,
                  MAE = MAE)
    
    print template.format(**values)

write_output(myoutput.beta)
