import numpy as np
import pandas as pd
import sys
sys.path.insert(0, dirName) # dirName = location where the files listed here are located
from heatTransClass import HeatTrans

class HeatTransLiqUnc():
    '''Uncertainty calculations for heat transfer in liquid.'''
    hh = HeatTrans()
    
    def FtoC(self,T):
        '''Convert F to C.'''
        return 5/9*(T-32) # C
    
    def GPMtoSI(self,AV):
        '''Convert gpm to m3/s.'''
        return AV*0.00006309 # m3/s
    
    def SItoGPM(self,AV):
        '''Convert m3/s to gpm.'''
        return AV/0.00006309 # gpm
    
    def dqdr(self,AV,T1,T2,fluid,conc,unitsIn = 'IP'):
        '''Derivative of heat transfer with respect to density.'''
        T = (T1+T2)/2
        rho,cp,h = HeatTransLiqUnc.hh.fluidProps(T,unitsIn,fluid,conc) # kg/m3, kJ/kg-K
        if unitsIn == 'IP':
            T1C = self.FtoC(T1)
            T2C = self.FtoC(T2)
            AVsi = self.GPMtoSI(AV)
        else:
            T1C = T1
            T2C = T2
            AVsi = AV
        return AVsi*cp*(T2C-T1C),rho # kW m^3/kg
        
    def dqdAV(self,T1,T2,fluid,conc,unitsIn = 'IP'):
        '''Derivative of heat transfer with respect to flow rate.'''
        T = (T1+T2)/2
        rho,cp,h = HeatTransLiqUnc.hh.fluidProps(T,unitsIn,fluid,conc) # kg/m3, kJ/kg-K
        if unitsIn == 'IP':
            T1C = self.FtoC(T1)
            T2C = self.FtoC(T2)
        else:
            T1C = T1
            T2C = T2            
        return rho*cp*(T2C-T1C) # kJ/m3
    
    def dqdcp(self,AV,T1,T2,fluid,conc,unitsIn = 'IP'):
        '''Derivative of heat transfer with respect to specific heat.'''
        T = (T1+T2)/2
        rho,cp,h = HeatTransLiqUnc.hh.fluidProps(T,unitsIn,fluid,conc) # kg/m3, kJ/kg-K
        if unitsIn == 'IP':
            T1C = self.FtoC(T1)
            T2C = self.FtoC(T2)
            AVsi = self.GPMtoSI(AV)
        else:
            T1C = T1
            T2C = T2            
            AVsi = AV
        return rho*AVsi*(T2C-T1C),cp # C kg/s
    
    def dqdT(self,AV,T1,T2,fluid,conc,unitsIn = 'IP'):
        '''Derivative of heat transfer with respect to temperature.'''
        T = (T1+T2)/2
        rho,cp,h = HeatTransLiqUnc.hh.fluidProps(T,unitsIn,fluid,conc) # kg/m3, kJ/kg-K
        if unitsIn == 'IP':
            AVsi = self.GPMtoSI(AV)
        else:      
            AVsi = AV
        return rho*cp*AVsi # kW/K
    
    def uRho(self,rho,fluid = 'water',unitsIn = 'SI',unitsOut = 'SI'):
        '''Uncertainty of density estimate.'''
        if fluid == 'water':
            u = 0 # for water, neglect the uncertainty of the fluid properties
        else:
            u = 0.005*rho # probably kg/m3, but could be lbm/ft3
        if ((unitsIn == 'IP') & (unitsOut == 'SI')): # unlikely
            u = u/.062 # convert from lbm/ft3 to kg/m3
        elif ((unitsIn == 'SI') & (unitsOut == 'IP')): # unlikely
            u = u*0.062 # convert from kg/m3 to lbm/ft3            
        return u # probably kg/m3, but could be lbm/ft3
    
    def uCp(self,cp,fluid = 'water',unitsIn = 'SI',unitsOut = 'SI'):
        '''Uncertainty of specific heat estimate.'''
        if fluid == 'water':
            u = 0 # for water, neglect the uncertainty of the fluid properties
        else:
            u = 0.018*cp # probably kJ/kg-C, but could be BTU/lbm-R            
        if ((unitsIn == 'IP') & (unitsOut == 'SI')): # unlikely
            u = u/0.24 # convert from BTU/lbm-R to kJ/kg-C
        elif ((unitsIn == 'SI') & (unitsOut == 'IP')): # unlikely
            u = u*0.24 # convert from kJ/kg-C to BTU/lbm-R     
        return u # probably kJ/kg-C, but could be BTU/lbm-R
    
    def uAV(self,mData,name,AV,unitsIn = 'IP',unitsOut = 'SI'):
        '''Uncertainty of flow rate measurement.'''
        if name == 'cl_f':
            u = 0.05*AV
        elif name == 'pl_f':
            u = 0.0171*AV
        elif name == 'ts_f':
            u = 0.0151*AV
        else:
            if unitsIn == 'IP':
                u = float(mData.loc[name].accuracy_scaled) # in gpm
            else:
                u = self.GPMtoSI(float(mData.loc[name].accuracy_scaled)) # m3/s
        
        if ((unitsIn == 'IP') & (unitsOut == 'SI')):
            u = self.GPMtoSI(u) # most likely case
        elif ((unitsIn == 'SI') & (unitsOut == 'IP')):
            u = self.SItoGPM(u)
        return u # probably m3/s, but could be gpm
    
    def uT(self,mData,name,unitsOut = 'SI'):
        '''Uncertainty of temperature measurement.'''
        if unitsOut == 'IP':
            u = float(mData.loc[name].accuracy_scaled) # F
        else:
            u = float(mData.loc[name].accuracy_scaled)*5/9 # C          
        return u # probably C, but could be F
        
        
    def uTot(self,df,mData,t1n,t2n,fn,n,fluid = 'water',conc = 0.3,unitsIn = 'IP',unitsOut = 'SI'):
        '''Total combined uncertainty.'''
        AV = df[fn]
        T1 = df[t1n]
        T2 = df[t2n]
        
        # for these, unitsOut = SI
        dQdR,rho = self.dqdr(AV,T1,T2,fluid,conc,unitsIn)
        dQdAV = self.dqdAV(T1,T2,fluid,conc,unitsIn)
        dQdCp,cp = self.dqdcp(AV,T1,T2,fluid,conc,unitsIn)
        dQdT2 = self.dqdT(AV,T1,T2,fluid,conc,unitsIn)
        dQdT1 = self.dqdT(AV,T1,T2,fluid,conc,unitsIn)
        
        uRho1 = self.uRho(rho,fluid,unitsIn = 'SI',unitsOut = 'SI')
        uAV1 = self.uAV(mData,fn,AV,unitsIn = 'IP',unitsOut = 'SI')
        uCp1 = self.uCp(cp,fluid,unitsIn = 'SI',unitsOut = 'SI')
        uT2 = self.uT(mData,t2n,unitsOut = 'SI')
        uT1 = self.uT(mData,t1n,unitsOut = 'SI')    
        
        df['dqdr_'+n] = dQdR
        df['urho_'+n] = uRho1
        df['dqdAV_'+n] = dQdAV
        df['uAV_'+n] = uAV1
        df['dqdcp_'+n] = dQdCp
        df['ucp_'+n] = uCp1
        df['dqdT2_'+n] = dQdT2
        df['uT2_'+n] = uT2
        df['dqdT1_'+n] = dQdT1
        df['uT1_'+n] = uT1
        
        utot2 = (dQdR*uRho1)**2+(dQdAV*uAV1)**2+(dQdCp*uCp1)**2+(dQdT2*uT2)**2+(dQdT1*uT1)**2 # SI
        if unitsOut == 'IP':
            utot2 = (np.sqrt(utot2)*3412)**2 # kW to BTU/hr
            
        df['u_'+n] = np.sqrt(utot2)
        return df