Source code for ACHP.Evaporator

from __future__ import division, print_function, absolute_import
from math import pi,log,exp

from scipy.optimize import brentq #solver to find roots (zero points) of functions
from scipy.interpolate import interp1d
import numpy as np

import CoolProp as CP
from CoolProp.CoolProp import PropsSI

from .Correlations import f_h_1phase_Tube,ShahEvaporation_Average, LockhartMartinelli,LMPressureGradientAvg,AccelPressureDrop,TwoPhaseDensity
from .FinCorrelations import WavyLouveredFins,FinInputs,IsFinsClass, HerringboneFins, PlainFins
from .DryWetSegment import DWSVals, DryWetSegment
from .ACHPTools import ValidateFields


[docs]class EvaporatorClass(): def __init__(self,**kwargs): self.__dict__.update(kwargs)
[docs] def Update(self,**kwargs): self.__dict__.update(kwargs)
[docs] def OutputList(self): """ Return a list of parameters for this component for further output It is a list of tuples, and each tuple is formed of items: [0] Description of value [1] Units of value [2] The value itself """ Output_List=[] #append optional parameters, if applicable if hasattr(self,'TestName'): Output_List.append(('Name','N/A',self.TestName)) if hasattr(self,'TestDescription'): Output_List.append(('Description','N/A',self.TestDescription)) if hasattr(self,'TestDetails'): Output_List.append(('Details','N/A',self.TestDetails)) Output_List_default=[ #default output list ('Volumetric flow rate','m^3/s',self.Fins.Air.Vdot_ha), ('Inlet Dry bulb temp','K',self.Tin_a), ('Inlet Air pressure','Pa',self.Fins.Air.p), ('Inlet Air Relative Humidity','-',self.Fins.Air.RH), ('Tubes per bank','-',self.Fins.Tubes.NTubes_per_bank), ('Number of banks','-',self.Fins.Tubes.Nbank), ('Number circuits','-',self.Fins.Tubes.Ncircuits), ('Length of tube','m',self.Fins.Tubes.Ltube), ('Tube OD','m',self.Fins.Tubes.OD), ('Tube ID','m',self.Fins.Tubes.ID), ('Tube Long. Pitch','m',self.Fins.Tubes.Pl), ('Tube Transverse Pitch','m',self.Fins.Tubes.Pt), ('Outlet superheat','K',self.Tout_r-self.Tdew_r), ('Fins per inch','1/in',self.Fins.Fins.FPI), ('Fin waviness pd','m',self.Fins.Fins.Pd), ('Fin waviness xf','m',self.Fins.Fins.xf), ('Fin thickness','m',self.Fins.Fins.t), ('Fin Conductivity','W/m-K',self.Fins.Fins.k_fin), ('Fins Type','-',self.FinsType), ('Q Total','W',self.Q), ('Q Superheat','W',self.Q_superheat), ('Q Two-Phase','W',self.Q_2phase), ('Inlet ref. temp','K',self.Tin_r), ('Outlet ref. temp','K',self.Tout_r), ('Outlet air temp','K',self.Tout_a), ('Evaporator P_sat in','Pa',self.psat_r), ('Evaporator inlet quality','-',self.xin_r), ('Evaporator ref. flowrate','kg/s',self.mdot_r), ('Pressure Drop Total','Pa',self.DP_r), ('Pressure Drop Superheat','Pa',self.DP_r_superheat), ('Pressure Drop Two-Phase','Pa',self.DP_r_2phase), ('Charge Total','kg',self.Charge), ('Charge Superheat','kg',self.Charge_superheat), ('Charge Two-Phase','kg',self.Charge_2phase), ('Mean HTC Superheat','W/m^2-K',self.h_r_superheat), ('Mean HTC Two-phase','W/m^2-K',self.h_r_2phase), ('Wetted Area Fraction Superheat','-',self.w_superheat), ('Wetted Area Fraction Two-phase','-',self.w_2phase), ('Mean Air HTC','W/m^2-K',self.Fins.h_a), ('Surface Effectiveness','-',self.Fins.eta_a), ('Air-side area (fin+tubes)','m^2',self.Fins.A_a), ('Mass Flow rate dry Air','kg/s',self.Fins.mdot_da), ('Mass Flow rate humid Air','kg/s',self.Fins.mdot_ha), ('Pressure Drop Air-side','Pa',self.Fins.dP_a), ('Sensible Heat Ratio','-',self.SHR), ('Bend Temperature profile','K',self.Tbends) ] for i in range(0,len(Output_List_default)): #append default parameters to output list Output_List.append(Output_List_default[i]) return Output_List
[docs] def AirSideCalcs(self): #Update with user FinType if self.FinsType == 'WavyLouveredFins': WavyLouveredFins(self.Fins) elif self.FinsType == 'HerringboneFins': HerringboneFins(self.Fins) elif self.FinsType == 'PlainFins': PlainFins(self.Fins)
[docs] def Initialize(self): #Input validation the first call of Initialize if False:#not hasattr(self,'IsValidated'): self.Fins.Validate() reqFields=[ ('Ref',str,None,None), ('psat_r',float,0.001,100000000), ('Fins',IsFinsClass,None,None), ('FinsType',str,None,None), ('hin_r',float,-100000,10000000), ('mdot_r',float,0.000001,10), ] optFields=['Verbosity','Backend'] d=self.__dict__ #Current fields in model ValidateFields(d,reqFields,optFields) self.IsValidated=True #AbstractState if hasattr(self,'Backend'): #check if backend is given AS = CP.AbstractState(self.Backend, self.Ref) else: #otherwise, use the defualt backend AS = CP.AbstractState('HEOS', self.Ref) self.AS = AS # Retrieve some parameters from nested structures # for code compactness self.ID=self.Fins.Tubes.ID self.OD=self.Fins.Tubes.OD self.Ltube=self.Fins.Tubes.Ltube self.NTubes_per_bank=self.Fins.Tubes.NTubes_per_bank self.Nbank=self.Fins.Tubes.Nbank self.Ncircuits=self.Fins.Tubes.Ncircuits self.Tin_a=self.Fins.Air.Tdb # Calculate an effective length of circuit if circuits are # not all the same length TotalLength=self.Ltube*self.NTubes_per_bank*self.Nbank self.Lcircuit=TotalLength/self.Ncircuits # Wetted area on the refrigerant side self.A_r_wetted=self.Ncircuits*pi*self.ID*self.Lcircuit self.V_r=self.Ncircuits*self.Lcircuit*pi*self.ID**2/4.0 #Average mass flux of refrigerant in circuit self.G_r = self.mdot_r/(self.Ncircuits*pi*self.ID**2/4.0) #[kg/m^2-s] ## Bubble and dew temperatures (same for fluids without glide) AS.update(CP.PQ_INPUTS, self.psat_r, 0.0) self.Tbubble_r=AS.T() #[K] h_l = AS.hmass() #[J/kg] AS.update(CP.PQ_INPUTS, self.psat_r, 1.0) self.Tdew_r=AS.T() #[K] h_v = AS.hmass() #[J/kg] ## Mean temperature for use in HT relationships self.Tsat_r=(self.Tbubble_r+self.Tdew_r)/2 # Latent heat self.h_fg=h_v - h_l #[J/kg] self.Fins.Air.RHmean=self.Fins.Air.RH #Update with user FinType if self.FinsType == 'WavyLouveredFins': WavyLouveredFins(self.Fins) elif self.FinsType == 'HerringboneFins': HerringboneFins(self.Fins) elif self.FinsType == 'PlainFins': PlainFins(self.Fins) self.mdot_ha=self.Fins.mdot_ha #[kg_ha/s] self.mdot_da=self.Fins.mdot_da #[kg_da/s]
[docs] def Calculate(self): #Initialize self.Initialize() AS = self.AS # Input and output thermodynamic properties AS.update(CP.QT_INPUTS, 0.0, self.Tbubble_r) ssatL=AS.smass() #[J/kg-K] hsatL=AS.hmass() #[J/kg] AS.update(CP.QT_INPUTS, 1.0, self.Tdew_r) ssatV=AS.smass() #[J/kg-K] hsatV=AS.hmass() #[J/kg] #if give enthalpy and pressure as inputs if hasattr(self,'hin_r'): self.xin_r=(self.hin_r-hsatL)/(hsatV-hsatL) self.sin_r=self.xin_r*ssatV+(1-self.xin_r)*ssatL self.Tin_r=self.xin_r*self.Tdew_r+(1-self.xin_r)*self.Tbubble_r elif hasattr(self,'xin_r'): #if given quality and pressure as inputs self.hin_r=self.xin_r*hsatV+(1-self.xin_r)*hsatL self.sin_r=self.xin_r*ssatV+(1-self.xin_r)*ssatL self.Tin_r=self.xin_r*self.Tdew_r+(1-self.xin_r)*self.Tbubble_r if self.xin_r>1.0: raise ValueError #Begin by assuming that you go all the way to saturated vapor at least self.xout_2phase=1.0 if self._TwoPhase_Forward(1.0)<0: # Evaporator outlet is in two-phase region, use all the area and find the outlet quality existsSuperheat=False self.w_2phase=1.0 def OBJECTIVE(xout): self.xout_2phase=xout Q_target=self.mdot_r*(xout-self.xin_r)*(hsatV-hsatL) self._TwoPhase_Forward(self.w_2phase) return self.Q_2phase-Q_target #Use a solver to find outlet quality brentq(OBJECTIVE,self.xin_r,1.0) self.w_superheat=0.0 self.Q_superheat=0.0 self.h_r_superheat=0.0 self.Re_r_superheat=0.0 self.Charge_superheat=0.0 self.Q_sensible_superheat=0.0 self.Tout_a_superheat=0.0 self.DP_r_superheat=0.0 self.fdry_superheat=0.0 else: existsSuperheat=True # Evaporator outlet is in superheated region, everything is ok self.w_2phase=brentq(self._TwoPhase_Forward,0.00000000001,0.9999999999) self._Superheat_Forward(1-self.w_2phase) self.Q=self.Q_superheat+self.Q_2phase self.Charge=self.Charge_superheat+self.Charge_2phase if self.Verbosity>4: print(self.Q,"Evaporator.Q") self.Capacity=self.Q-self.Fins.Air.FanPower #Sensible heat ratio [-] self.SHR=(self.Q_sensible_2phase+self.Q_sensible_superheat)/self.Q #Average air outlet temperature (area fraction weighted average) [K] self.Tout_a=self.w_superheat*self.Tout_a_superheat+self.w_2phase*self.Tout_a_2phase self.DP_r=self.DP_r_superheat+self.DP_r_2phase #Outlet enthalpy obtained from energy balance self.hout_r=self.hin_r+self.Q/self.mdot_r #Outlet entropy if existsSuperheat==True: AS.update(CP.PT_INPUTS, self.psat_r, self.Tout_r) self.sout_r=AS.smass() #[J/kg-K] else: xout_r=(self.hout_r-hsatL)/(hsatV-hsatL) self.sout_r=ssatV*xout_r+(1-xout_r)*ssatL #Outlet superheat an temperature (in case of two phase) if existsSuperheat: self.DT_sh_calc=self.Tout_r-self.Tdew_r else: AS.update(CP.QT_INPUTS, 1.0, self.Tdew_r) cp_sh = AS.cpmass() #[J/kg-K] self.DT_sh_calc=(self.hout_r-hsatV)/cp_sh #Effective superheat AS.update(CP.PQ_INPUTS, self.psat_r+self.DP_r, xout_r) self.Tout_r=AS.T() #saturated temperature at outlet quality [K] self.hmean_r=self.w_2phase*self.h_r_2phase+self.w_superheat*self.h_r_superheat self.UA_r=self.hmean_r*self.A_r_wetted self.UA_a=self.Fins.h_a*self.Fins.A_a*self.Fins.eta_a #Build a vector of temperatures at each point where there is a phase transition along the averaged circuit if existsSuperheat: #Insert the shoulder point Tv=[self.Tin_r,self.Tdew_r,self.Tout_r] x=[0,self.w_2phase,1] else: Tv=[self.Tin_r,xout_r*self.Tdew_r+(1-xout_r)*self.Tbubble_r] x=[0,1] #Determine each bend temperature by interpolation #------------------------------------------------ #Number of bends (including inlet and outlet of coil) Nbends=1+self.Lcircuit/self.Ltube #x-position of each point xv=np.linspace(0,1,Nbends) self.Tbends=interp1d(x,Tv)(xv)
def _TwoPhase_Forward(self,w_2phase): DWS=DWSVals() #DryWetSegment structure # Store temporary values to be passed to DryWetSegment DWS.Fins=self.Fins DWS.FinsType = self.FinsType DWS.A_a=self.Fins.A_a*w_2phase DWS.cp_da=self.Fins.cp_da DWS.eta_a=self.Fins.eta_a DWS.h_a=self.Fins.h_a #Heat transfer coefficient, not enthalpy DWS.mdot_da=self.mdot_da*w_2phase DWS.pin_a=self.Fins.Air.p DWS.Tdew_r=self.Tdew_r DWS.Tbubble_r=self.Tbubble_r DWS.Tin_a=self.Tin_a DWS.RHin_a=self.Fins.Air.RH DWS.Tin_r=self.Tsat_r DWS.A_r=self.A_r_wetted*w_2phase DWS.cp_r=1.0e15 #In the two-phase region the cp is infinite, use 1e15 as a big number; DWS.pin_r=self.psat_r DWS.mdot_r=self.mdot_r DWS.IsTwoPhase=True #Target heat transfer to go from inlet quality to saturated vapor Q_target=self.mdot_r*(self.xout_2phase-self.xin_r)*self.h_fg if Q_target<0: raise ValueError('Q_target in Evaporator must be positive') # Average Refrigerant heat transfer coefficient DWS.h_r=ShahEvaporation_Average(self.xin_r,self.xout_2phase,self.AS,self.G_r,self.ID,self.psat_r,Q_target/DWS.A_r,self.Tbubble_r,self.Tdew_r) #Run the DryWetSegment to carry out the heat and mass transfer analysis DryWetSegment(DWS) self.Q_2phase=DWS.Q self.Q_sensible_2phase=DWS.Q_sensible self.h_r_2phase=DWS.h_r self.fdry_2phase=DWS.f_dry self.Tout_a_2phase=DWS.Tout_a rho_average=TwoPhaseDensity(self.AS,self.xin_r,self.xout_2phase,self.Tdew_r,self.Tbubble_r,slipModel='Zivi') self.Charge_2phase = rho_average * w_2phase * self.V_r #Frictional pressure drop component DP_frict=LMPressureGradientAvg(self.xin_r,self.xout_2phase,self.AS,self.G_r,self.ID,self.Tbubble_r,self.Tdew_r)*self.Lcircuit*w_2phase #Accelerational pressure drop component DP_accel=AccelPressureDrop(self.xin_r,self.xout_2phase,self.AS,self.G_r,self.Tbubble_r,self.Tdew_r)*self.Lcircuit*w_2phase self.DP_r_2phase=DP_frict+DP_accel; if self.Verbosity>7: print(w_2phase,DWS.Q,Q_target,self.xin_r,"w_2phase,DWS.Q,Q_target,self.xin_r") return DWS.Q-Q_target def _Superheat_Forward(self,w_superheat): self.w_superheat=w_superheat DWS=DWSVals() #DryWetSegment structure AS=self.AS #AbstractState # Store temporary values to be passed to DryWetSegment DWS.A_a=self.Fins.A_a*w_superheat DWS.cp_da=self.Fins.cp_da DWS.eta_a=self.Fins.eta_a DWS.h_a=self.Fins.h_a #Heat transfer coefficient DWS.mdot_da=self.mdot_da*w_superheat DWS.pin_a=self.Fins.Air.p DWS.Fins=self.Fins DWS.FinsType = self.FinsType # Inputs on the air side to two phase region are inlet air again DWS.Tin_a=self.Tin_a DWS.RHin_a=self.Fins.Air.RH DWS.Tin_r=self.Tdew_r DWS.A_r=self.A_r_wetted*w_superheat AS.update(CP.PT_INPUTS, self.psat_r, self.Tdew_r+2.5) DWS.cp_r=AS.cpmass() #Use a guess value of 6K superheat to calculate cp [J/kg-K] DWS.pin_r=self.psat_r DWS.mdot_r=self.mdot_r DWS.IsTwoPhase=False #Use a guess value of 6K superheat to calculate the properties self.f_r_superheat, self.h_r_superheat, self.Re_r_superheat=f_h_1phase_Tube(self.mdot_r / self.Ncircuits, self.ID, self.Tdew_r+3, self.psat_r, self.AS, "Single"); # Average Refrigerant heat transfer coefficient DWS.h_r=self.h_r_superheat #Run DryWetSegment DryWetSegment(DWS) AS.update(CP.PT_INPUTS, self.psat_r, (DWS.Tout_r+self.Tdew_r)/2.0) rho_superheat=AS.rhomass() #[kg/m^3] self.Charge_superheat = w_superheat * self.V_r * rho_superheat #Pressure drop calculations for superheated refrigerant v_r=1/rho_superheat #Pressure gradient using Darcy friction factor dpdz_r=-self.f_r_superheat*v_r*self.G_r**2/(2*self.ID) #Pressure gradient self.DP_r_superheat=dpdz_r*self.Lcircuit*self.w_superheat #Set values self.Q_superheat=DWS.Q self.Q_sensible_superheat=DWS.Q_sensible self.fdry_superheat=DWS.f_dry self.Tout_a_superheat=DWS.Tout_a self.Tout_r=DWS.Tout_r
if __name__=='__main__': #Example usage for a parametric study import pylab num_points= 101 T_dews= np.linspace(270,299.7,num_points) TT= np.empty(num_points) Q_2p= np.empty(num_points) w_2p= np.empty(num_points) w_sh= np.empty(num_points) Q_tot= np.empty(num_points) h_2p= np.empty(num_points) h_sh= np.empty(num_points) FinsTubes=FinInputs() FinsTubes.Tubes.NTubes_per_bank=32 FinsTubes.Tubes.Ncircuits=5 FinsTubes.Tubes.Nbank=3 FinsTubes.Tubes.Ltube=0.452 FinsTubes.Tubes.OD=0.009525 FinsTubes.Tubes.ID=0.0089154 FinsTubes.Tubes.Pl=0.0254 FinsTubes.Tubes.Pt=0.0219964 FinsTubes.Fins.FPI=14.5 FinsTubes.Fins.Pd=0.001 FinsTubes.Fins.xf=0.001 FinsTubes.Fins.t=0.00011 FinsTubes.Fins.k_fin=237 FinsTubes.Air.Vdot_ha=0.5663 FinsTubes.Air.Tmean=299.9 FinsTubes.Air.Tdb=299.9 FinsTubes.Air.p=101325 FinsTubes.Air.RH=0.51 FinsTubes.Air.RHmean=0.51 FinsTubes.Air.FanPower=438 kwargs={'Ref': 'R410A', 'mdot_r': 0.0708, 'psat_r': PropsSI('P','T',T_dews[0],'Q',1.0,'R410A'), 'Fins': FinsTubes, 'FinsType': 'WavyLouveredFins', #Choose fin Type: 'WavyLouveredFins' or 'HerringboneFins'or 'PlainFins' 'hin_r': PropsSI('H','P',PropsSI('P','T',282,'Q',1.0,'R410A'),'Q',0.15,'R410A'), 'Verbosity': 0, 'Backend':'HEOS' #choose between: 'HEOS','TTSE&HEOS','BICUBIC&HEOS','REFPROP','SRK','PR' } Evap=EvaporatorClass(**kwargs) #generate new evaporator instance and update kwargs for i in range(0, len(T_dews)): kwargs={'psat_r': PropsSI('P','T',T_dews[i],'Q',1.0,'R410A')} Evap.Update(**kwargs) Evap.Calculate() Q_tot[i] = Evap.Q Q_2p[i]= Evap.Q_2phase w_2p[i]= Evap.w_2phase w_sh[i]= Evap.w_superheat h_2p[i]= Evap.h_r_2phase h_sh[i]= Evap.h_r_superheat print("Demonstrate output list") print(Evap.OutputList()) pylab.plot(T_dews,Q_2p,T_dews,Q_tot) pylab.title('Parametric Study With Fixed flowrates - Capacity') pylab.legend(['two-phase','total'],loc='best') pylab.title('Parametric Study With Fixed flowrates - Capacity') pylab.xlabel('Evaporation Dew Temperature in Kelvin') pylab.ylabel('Capacity in Watt') pylab.savefig('Evaporator_py_capacity.pdf') pylab.show() pylab.plot(T_dews,h_2p,T_dews, h_sh) pylab.title('Parametric Study with fixed Flowrates - Heat Transfer Coefficients') pylab.legend(['two-phase','superheat'],loc='best') pylab.xlabel('Evaporation Dew Temperature in Kelvin') pylab.ylabel('Heat Transfer Coefficient in W/m2-K') pylab.savefig('Evaporator_py_HTC.pdf') pylab.show() pylab.plot(T_dews,w_2p, T_dews, w_sh) pylab.title('Parametric Study with fixed Flowrates - Area Fraction') pylab.legend(['two-phase', 'superheat'],loc='best') pylab.xlabel('Evaporation Dew Temperature in Kelvin') pylab.ylabel('Two-phase Wetted Area Fraction') pylab.ylim(-0.01,1.01) pylab.savefig('Evaporator_py_wetted_area.pdf') pylab.show()