def getCp(self, T): if self.useConstantProperties: if self.constantCpIsSet == False: print( "FATAL ERROR setConstartCp must be defined if constant properties are used" ) sys.exit(0) else: return self.constantCp else: try: if newVersion == True: return PropsSI("C", "T", T + 273.15, "P", self.pressure, self.name) else: return Props("C", "T", T + 273.15, "P", self.pressure, self.name) * 1000.0 except: if newVersion == True: return PropsSI("C", "T", T + 0.01 + 273.15, "P", self.pressure, self.name) else: try: return Props("C", "T", T + 273.15, "P", self.pressure, self.name) * 1000.0 except: return Props("C", "T", T + 0.01 + 273.15, "P", self.pressure, self.name) * 1000.0
def WyattPHEHX(): Tdew=Props('T','P',962.833,'Q',1.0,'R134a') params={ 'Ref_c':'R134a', 'mdot_c':0.073, 'pin_c':962.833, 'hin_c':Props('H','T',Tdew,'Q',0.0,'R134a')*1000, 'xin_c':0.0, 'Ref_h':'Water', 'mdot_h':100.017, 'pin_h':Props('P','T',115.5+273.15,'Q',1,'Water'), 'hin_h':Props('H','T',115.5+273.15,'Q',1,'Water')*1000, #Geometric parameters 'Bp' : 0.119, 'Lp' : 0.526, #Center-to-center distance between ports 'Nplates' : 110, 'PlateAmplitude' : 0.00102, #[m] 'PlateThickness' : 0.0003, #[m] 'PlateWavelength' : 0.0066, #[m] 'InclinationAngle' : pi/3,#[rad] 'PlateConductivity' : 15.0, #[W/m-K] 'MoreChannels' : 'Hot', #Which stream gets the extra channel, 'Hot' or 'Cold' 'Verbosity':10 } PHE=PHEHXClass(**params) PHE.Calculate()
def SWEPVariedmdot(): Tin=8+273.15 for mdot_h in [0.4176,0.5013,0.6267,0.8357,1.254,2.508]: params={ 'Ref_c':'R290', 'mdot_c':0.03312, 'pin_c':Props('P','T',Tin,'Q',1.0,'R290'), 'hin_c':Props('H','T',Tin,'Q',0.15,'R290')*1000, 'Ref_h':'Water', 'mdot_h':mdot_h, 'pin_h':200, 'hin_h':Props('H','T',15+273.15,'P',200,'Water')*1000, #Geometric parameters 'Bp' : 0.101, 'Lp' : 0.455, #Center-to-center distance between ports 'Nplates' : 46, 'PlateAmplitude' : 0.00102, #[m] 'PlateThickness' : 0.0003, #[m] 'PlateWavelength' : 0.00626, #[m] 'InclinationAngle' : 65/180*pi,#[rad] 'PlateConductivity' : 15.0, #[W/m-K] 'MoreChannels' : 'Hot', #Which stream gets the extra channel, 'Hot' or 'Cold' 'Verbosity':0 } PHE=PHEHXClass(**params) PHE.Calculate() print PHE.Q,',',PHE.h_subcooled_h,',',-PHE.DP_h/1000
def getCp(self, T): if (self.useConstantProperties): if (self.constantCpIsSet == False): print( "FATAL ERROR setConstartCp must be defined if constant properties are used" ) sys.exit(0) else: return self.constantCp else: try: if (newVersion == True): return (PropsSI('C', 'T', T + 273.15, 'P', self.pressure, self.name)) else: return (Props('C', 'T', T + 273.15, 'P', self.pressure, self.name) * 1000.) except: if (newVersion == True): return (PropsSI('C', 'T', T + 0.01 + 273.15, 'P', self.pressure, self.name)) else: try: return (Props('C', 'T', T + 273.15, 'P', self.pressure, self.name) * 1000.) except: return (Props('C', 'T', T + 0.01 + 273.15, 'P', self.pressure, self.name) * 1000.)
def getLambda(self, T): if (self.useConstantProperties): if (self.constantMuIsSet == False): print( "FATAL ERROR setConstartMu must be defined if constant properties are used" ) sys.exit(0) else: return self.constantMu else: try: if (newVersion == True): return (PropsSI('L', 'T', T + 273.15, 'P', self.pressure, self.name)) else: return (Props('L', 'T', T + 273.15, 'P', self.pressure, self.name)) except: print("Error in Lambda T:%f" % T) if (newVersion == True): return (PropsSI('L', 'T', T + 0.1 + 273.15, 'P', self.pressure, self.name) * 1000.) else: return (Props('L', 'T', T + 0.1 + 273.15, 'P', self.pressure, self.name) * 1000.)
def TwoPhaseDensity(Ref, xmin, xmax, Tdew, Tbubble, slipModel='Zivi'): rhog = Props('D', 'T', Tdew, 'Q', 1, Ref) rhof = Props('D', 'T', Tbubble, 'Q', 0, Ref) if slipModel == 'Zivi': S = pow(rhof / rhog, 0.3333) elif slipModel == 'Homogeneous': S = 1 else: raise ValueError("slipModel must be either 'Zivi' or 'Homogeneous'") C = S * rhog / rhof if xmin + 5 * machine_eps < 0 or xmax - 5 * machine_eps > 1.0: raise ValueError('Quality must be between 0 and 1') #Avoid the zero and one qualities (undefined integral) if xmin == xmax: alpha_average = 1 / (1 + C * (1 - xmin) / xmin) else: if xmin >= 1.0: alpha_average = 1.0 elif xmax <= 0.0: alpha_average = 0.0 else: alpha_average = -(C * (log( ((xmax - 1.0) * C - xmax) / ((xmin - 1.0) * C - xmin)) + xmax - xmin) - xmax + xmin) / ( C**2 - 2 * C + 1) / (xmax - xmin) return alpha_average * rhog + (1 - alpha_average) * rhof
def getLambda(self, T): if self.useConstantProperties: if self.constantMuIsSet == False: print( "FATAL ERROR setConstartMu must be defined if constant properties are used" ) sys.exit(0) else: return self.constantMu else: try: if newVersion == True: return PropsSI("L", "T", T + 273.15, "P", self.pressure, self.name) else: return Props("L", "T", T + 273.15, "P", self.pressure, self.name) except: print("Error in Lambda T:%f" % T) if newVersion == True: return PropsSI("L", "T", T + 0.1 + 273.15, "P", self.pressure, self.name) * 1000.0 else: return Props("L", "T", T + 0.1 + 273.15, "P", self.pressure, self.name) * 1000.0
def __init__(self, Ref, IPF): self.Ref = Ref self.IPF = IPF self.RefString, self.N0, self.T0, self.D0, self.L0 = get_fluid_constants(Ref) self.Tc = Props(self.RefString,'Tcrit') self.rhoc = Props(self.RefString,'rhocrit') molemass = Props(self.RefString,'molemass') self.R = 8.314472/ molemass
def fit_hs(fluid): T = np.linspace(Props(fluid, 'Tmin'), Props(fluid, 'Tcrit') - 2) sL = Props('S', 'T', T, 'Q', 0, fluid) hL = Props('H', 'T', T, 'Q', 0, fluid) a = np.polyfit(sL, hL, 4) n = range(4, -1, -1) d = dict(a_hs_satL=list(a), n_hs_satL=list(n)) return d
def _OnePhaseH_OnePhaseC_Qimposed(self,Inputs): """ Single phase on both sides Inputs is a dict of parameters """ #Calculate the mean temperature Tmean_h=Inputs['Tmean_h'] Tmean_c=Inputs['Tmean_c'] #Evaluate heat transfer coefficient for both fluids h_h,cp_h,PlateOutput_h=self.PlateHTDP(self.Ref_h, Tmean_h, Inputs['pin_h'],self.mdot_h/self.NgapsHot) h_c,cp_c,PlateOutput_c=self.PlateHTDP(self.Ref_c, Tmean_c, Inputs['pin_c'],self.mdot_c/self.NgapsCold) #Use cp calculated from delta h/delta T cp_h=Inputs['cp_h'] cp_c=Inputs['cp_c'] #Evaluate UA [W/K] if entire HX was in this section UA_total=1/(1/(h_h*self.A_h_wetted)+1/(h_c*self.A_c_wetted)+self.PlateThickness/(self.PlateConductivity*(self.A_c_wetted+self.A_h_wetted)/2.)) #Get Ntu [-] C=[cp_c*self.mdot_c,cp_h*self.mdot_h] Cmin=min(C) Cr=Cmin/max(C) #Effectiveness [-] Q=Inputs['Q'] Qmax=Cmin*(Inputs['Tin_h']-Inputs['Tin_c']) epsilon = Q/Qmax #Pure counterflow with Cr<1 (Incropera Table 11.4) NTU=1/(Cr-1)*log((epsilon-1)/(epsilon*Cr-1)) #Required UA value UA_req=Cmin*NTU #w is required part of heat exchanger for this duty w=UA_req/UA_total #Determine both charge components rho_h=Props('D','T',Tmean_h, 'P', self.pin_h, self.Ref_h) Charge_h = w * self.V_h * rho_h rho_c=Props('D','T',Tmean_c, 'P', self.pin_c, self.Ref_c) Charge_c = w * self.V_c * rho_c #Pack outputs Outputs={ 'w': w, 'Tout_h': Inputs['Tin_h']-Q/(self.mdot_h*cp_h), 'Tout_c': Inputs['Tin_c']-Q/(self.mdot_c*cp_c), 'Charge_c': Charge_c, 'Charge_h': Charge_h, 'DP_h': -PlateOutput_h['DELTAP'], 'DP_c': -PlateOutput_c['DELTAP'], 'h_h':h_h, 'h_c':h_c, } return dict(Inputs.items()+Outputs.items())
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,1e-6,100000), ('Fins',IsFinsClass,None,None), ('hin_r',float,-100000,10000000), ('mdot_r',float,0.000001,10), ] optFields=['Verbosity'] d=self.__dict__ #Current fields in model ValidateFields(d,reqFields,optFields) self.IsValidated=True # 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] """ Tsat() is a relatively slow function since it does a Dekker solve over the full two-phase region. So store the value in order to cut down on computational work. """ ## Bubble and dew temperatures (same for fluids without glide) self.Tbubble_r=Props('T','P',self.psat_r,'Q',0,self.Ref) self.Tdew_r=Props('T','P',self.psat_r,'Q',1,self.Ref) ## Mean temperature for use in HT relationships self.Tsat_r=(self.Tbubble_r+self.Tdew_r)/2 # Latent heat self.h_fg=(Props('H','T',self.Tdew_r,'Q',1.0,self.Ref)-Props('H','T',self.Tbubble_r,'Q',0.0,self.Ref))*1000. #[J/kg] self.Fins.Air.RHmean=self.Fins.Air.RH self.Fins.h_tp_tuning= self.h_tp_tuning #pass tuning factor WavyLouveredFins(self.Fins) self.mdot_ha=self.Fins.mdot_ha #[kg_ha/s] self.mdot_da=self.Fins.mdot_da #[kg_da/s]
def _Superheat_Forward(self, w_superheat): self.w_superheat = w_superheat DWS = DWSVals() #DryWetSegment structure # 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 # 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 DWS.cp_r = Props( 'C', 'T', self.Tdew_r + 2.5, 'P', self.psat_r, self.Ref ) * 1000 #Use a guess value of 6K superheat to calculate cp 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.Ref, "Single") # Average Refrigerant heat transfer coefficient DWS.h_r = self.h_r_superheat #Run DryWetSegment DryWetSegment(DWS) rho_superheat = Props('D', 'T', (DWS.Tout_r + self.Tdew_r) / 2.0, 'P', self.psat_r, self.Ref) self.Charge_superheat = w_superheat * self.V_r * rho_superheat #Pressure drop calculations for subcooled 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
def __init__(self, Ref): self.Ref = Ref self.RefString, N0, T0, D0, L0 = get_fluid_constants(Ref) self.molemass = Props(self.RefString, 'molemass') self.Tc = Props(self.RefString, 'Tcrit') self.rhoc = Props(self.RefString, 'rhocrit') self.pc = Props(self.RefString, 'pcrit') self.T = np.linspace(100, 450, 200) self.tau = self.Tc / self.T self.C = Props('C', 'T', self.T, 'D', 1e-15, self.RefString) R = 8.314472 / self.molemass self.cp0_R = self.C / R
def DetermineHTBounds(self): # See if each phase could change phase if it were to reach the # inlet temperature of the opposite phase #Inlet phases self.Tin_h,rhoin_h,Phasein_h=TrhoPhase_ph(self.Ref_h,self.pin_h,self.hin_h,self.Tbubble_h,self.Tdew_h,self.rhosatL_h,self.rhosatV_h) self.Tin_c,rhoin_c,Phasein_c=TrhoPhase_ph(self.Ref_c,self.pin_c,self.hin_c,self.Tbubble_c,self.Tdew_c,self.rhosatL_c,self.rhosatV_c) # Find the maximum possible rate of heat transfer as the minimum of # taking each stream to the inlet temperature of the other stream hout_h=Props('H','T',self.Tin_c,'P',self.pin_h,self.Ref_h)*1000 hout_c=Props('H','T',self.Tin_h,'P',self.pin_c,self.Ref_c)*1000 Qmax=min([self.mdot_c*(hout_c-self.hin_c),self.mdot_h*(self.hin_h-hout_h)]) if Qmax<0: raise ValueError('Qmax in PHE must be > 0') # Now we need to check for internal pinch points where the temperature # profiles would tend to overlap given the "normal" definitions of # maximum heat transfer of taking each stream to the inlet temperature # of the other stream # # First we build the same vectors of enthalpies like below EnthalpyList_c,EnthalpyList_h=self.BuildEnthalpyLists(Qmax) # Then we find the temperature of each stream at each junction TList_c=np.zeros_like(EnthalpyList_c) TList_h=np.zeros_like(EnthalpyList_h) if not len(EnthalpyList_h)==len(EnthalpyList_c): raise ValueError('Length of enthalpy lists for both fluids must be the same') #Make the lists of temperatures of each fluid at each cell boundary for i in range(len(EnthalpyList_h)): TList_c[i]=TrhoPhase_ph(self.Ref_c,self.pin_c,EnthalpyList_c[i],self.Tbubble_c,self.Tdew_c,self.rhosatL_c,self.rhosatV_c)[0] TList_h[i]=TrhoPhase_ph(self.Ref_h,self.pin_h,EnthalpyList_h[i],self.Tbubble_h,self.Tdew_h,self.rhosatL_h,self.rhosatV_h)[0] # #Double-check that the edges are not pinched # if TList_c[0]-1e-9>TList_h[0] or TList_c[-1]-1e-9>TList_h[-1]: # raise ValueError('Outlet or inlet of PHE is pinching. Why?') #TODO: could do with more generality if both streams can change phase #Check if any internal points are pinched if np.sum(TList_c>TList_h)>0: #Loop over the internal cell boundaries for i in range(1,len(TList_c)-1): #If cold stream is hotter than the hot stream if TList_c[i]-1e-9>TList_h[i]: #Find new enthalpy of cold stream at the hot stream cell boundary hpinch=Props('H','T',TList_h[i],'P',self.pin_c,self.Ref_c)*1000 #Find heat transfer of hot stream in right-most cell Qextra=self.mdot_h*(EnthalpyList_h[i+1]-EnthalpyList_h[i]) Qmax=self.mdot_c*(hpinch-self.hin_c)+Qextra return Qmax
def Calculate(self): if not IsFluidType(self.Ref, 'Brine'): #Figure out the inlet state self.Tbubble = Props('T', 'P', self.pin, 'Q', 0.0, self.Ref) self.Tdew = Props('T', 'P', self.pin, 'Q', 1.0, self.Ref) else: #It is a brine self.Tbubble = None self.Tdew = None self.Tin, self.rhoin, self.Phasein = TrhoPhase_ph( self.Ref, self.pin, self.hin, self.Tbubble, self.Tdew) self.f_fluid, self.h_fluid, self.Re_fluid = f_h_1phase_Tube( self.mdot, self.ID, self.Tin, self.pin, self.Ref) # Specific heat capacity [J/kg-K] cp = Props('C', 'T', self.Tin, 'P', self.pin, self.Ref) * 1000 # Density [kg/m^3] rho = Props('D', 'T', self.Tin, 'P', self.pin, self.Ref) #Thermal resistance of tube R_tube = log(self.OD / self.ID) / (2 * pi * self.L * self.k_tube) #Thermal resistance of insulation R_insul = log((self.OD + 2.0 * self.t_insul) / self.OD) / (2 * pi * self.L * self.k_insul) #Convective UA for inside the tube UA_i = pi * self.ID * self.L * self.h_fluid #Convective UA for the air-side UA_o = pi * (self.OD + 2 * self.t_insul) * self.L * self.h_air #Avoid the possibility of division by zero if h_air is zero if UA_o < 1e-12: UA_o = 1e-12 #Overall UA value UA = 1 / (1 / UA_i + R_tube + R_insul + 1 / UA_o) #Outlet fluid temperature [K] self.Tout = self.T_air - exp( -UA / (self.mdot * cp)) * (self.T_air - self.Tin) #Overall heat transfer rate [W] self.Q = self.mdot * cp * (self.Tout - self.Tin) self.hout = self.hin + self.Q / self.mdot #Pressure drop calculations for superheated refrigerant v = 1. / rho G = self.mdot / (pi * self.ID**2 / 4.0) #Pressure gradient using Darcy friction factor dpdz = -self.f_fluid * v * G**2 / (2. * self.ID) #Pressure gradient self.DP = dpdz * self.L #Charge in Line set [kg] self.Charge = pi * self.ID**2 / 4.0 * self.L * rho
def f_h_1phase_Channel(mdot, W, H, T, p, Fluid, Phase): if Phase == "SatVap": mu = Props('V', 'T', T, 'Q', 1, Fluid) #kg/m-s cp = Props('C', 'T', T, 'Q', 1, Fluid) * 1000. #J/kg-K k = Props('L', 'T', T, 'Q', 1, Fluid) * 1000. #W/m-K rho = Props('D', 'T', T, 'Q', 1, Fluid) #kg/m^3 else: mu = Props('V', 'T', T, 'P', p, Fluid) ##kg/m-s cp = Props('C', 'T', T, 'P', p, Fluid) * 1000. #J/kg-K k = Props('L', 'T', T, 'P', p, Fluid) * 1000. #W/m-K rho = Props('D', 'T', T, 'P', p, Fluid) #kg/m^3 Pr = cp * mu / k #[-] Dh = 2 * H * W / (H + W) Area = W * H u = mdot / (Area * rho) Re = rho * u * (Dh) / mu # Friction factor of Churchill (Darcy Friction factor where f_laminar=64/Re) e_D = 0 A = ((-2.457 * log((7.0 / Re)**(0.9) + 0.27 * e_D)))**16 B = (37530.0 / Re)**16 f = 8 * ((8 / Re)**12.0 + 1 / (A + B)**(1.5))**(1 / 12) # Heat Transfer coefficient of Gnielinski for high Re, if Re > 1000: Nu = (f / 8) * (Re - 1000.) * Pr / (1 + 12.7 * sqrt(f / 8) * (Pr**(0.66666) - 1)) #[-] else: Nu = 3.66 h = k * Nu / Dh #W/m^2-K return (f, h, Re)
def f_h_1phase_Annulus(mdot, OD, ID, T, p, Fluid, Phase='Single'): """ """ if Phase == "SatVap": mu = Props('V', 'T', T, 'Q', 1, Fluid) #kg/m-s cp = Props('C', 'T', T, 'Q', 1, Fluid) * 1000. #J/kg-K k = Props('L', 'T', T, 'Q', 1, Fluid) * 1000. #W/m-K rho = Props('D', 'T', T, 'Q', 1, Fluid) #kg/m^3 else: mu = Props('V', 'T', T, 'P', p, Fluid) #kg/m-s cp = Props('C', 'T', T, 'P', p, Fluid) * 1000. #J/kg-K k = Props('L', 'T', T, 'P', p, Fluid) * 1000. #W/m-K rho = Props('D', 'T', T, 'P', p, Fluid) #kg/m^3 Pr = cp * mu / k #[-] Dh = OD - ID Area = pi * (OD**2 - ID**2) / 4.0 u = mdot / (Area * rho) Re = rho * u * Dh / mu # Friction factor of Churchill (Darcy Friction factor where f_laminar=64/Re) e_D = 0 A = ((-2.457 * log((7.0 / Re)**(0.9) + 0.27 * e_D)))**16 B = (37530.0 / Re)**16 f = 8 * ((8 / Re)**12.0 + 1 / (A + B)**(1.5))**(1 / 12) # Heat Transfer coefficient of Gnielinski Nu = (f / 8) * (Re - 1000) * Pr / (1 + 12.7 * sqrt(f / 8) * (Pr**(0.66666) - 1)) #[-] h = k * Nu / Dh #W/m^2-K return (f, h, Re)
def LMPressureGradientAvg(x_min, x_max, Ref, G, D, Tbubble, Tdew, C=None, satTransport=None): """ Returns the average pressure gradient between qualities of x_min and x_max. To obtain the pressure gradient for a given value of x, pass it in as x_min and x_max Required parameters: * x_min : The minimum quality for the range [-] * x_max : The maximum quality for the range [-] * Ref : String with the refrigerant name * G : Mass flux [kg/m^2/s] * D : Diameter of tube [m] * Tbubble : Bubblepoint temperature of refrigerant [K] * Tdew : Dewpoint temperature of refrigerant [K] Optional parameters: * C : The coefficient in the pressure drop * satTransport : A dictionary with the keys 'mu_f','mu_g,'v_f','v_g' for the saturation properties. So they can be calculated once and passed in for a slight improvement in efficiency """ def LMFunc(x): dpdz, alpha = LockhartMartinelli(Ref, G, D, x, Tbubble, Tdew, C, satTransport) return dpdz ## Use Simpson's Rule to calculate the average pressure gradient ## Can't use adapative quadrature since function is not sufficiently smooth ## Not clear why not sufficiently smooth at x>0.9 if x_min == x_max: return LMFunc(x_min) else: #Calculate the tranport properties once satTransport = {} satTransport['v_f'] = 1 / Props('D', 'T', Tbubble, 'Q', 0.0, Ref) satTransport['v_g'] = 1 / Props('D', 'T', Tdew, 'Q', 1.0, Ref) satTransport['mu_f'] = Props('V', 'T', Tbubble, 'Q', 0.0, Ref) satTransport['mu_g'] = Props('V', 'T', Tdew, 'Q', 1.0, Ref) xx = np.linspace(x_min, x_max, 30) DP = np.zeros_like(xx) for i in range(len(xx)): DP[i] = LMFunc(xx[i]) return -simps(DP, xx) / (x_max - x_min)
def test_subcrit_twophase_consistency(): for Fluid in reversed(sorted(CoolProp.__fluids__)): Tmin = Props(Fluid, 'Tmin') Tcrit = Props(Fluid, 'Tcrit') for T in [Tmin + 1, (Tmin + Tcrit) / 2.0, 0.95 * Tcrit]: for mode in modes: rhoL = Props('D', 'T', T, 'Q', 0, Fluid) rhoV = Props('D', 'T', T, 'Q', 1, Fluid) for Q in [0.0, 0.5, 1.0]: rho = 1 / ((1 - Q) / rhoL + Q / rhoV) for inputs in twophase_inputs: for unit_system in ['kSI', 'SI']: yield check_consistency, Fluid, mode, unit_system, T, rho, inputs
def LongoCondensation(x_avg, G, dh, Ref, TsatL, TsatV): rho_L = Props('D', 'T', TsatL, 'Q', 0, Ref) #kg/m^3 rho_V = Props('D', 'T', TsatV, 'Q', 1, Ref) #kg/m^3 mu_L = Props('V', 'T', TsatL, 'Q', 0, Ref) #kg/m-s cp_L = Props('C', 'T', TsatL, 'Q', 0, Ref) * 1000 #J/kg-K k_L = Props('L', 'T', TsatV, 'Q', 0, Ref) * 1000 #W/m-K Pr_L = cp_L * mu_L / k_L #[-] Re_eq = G * ((1 - x_avg) + x_avg * sqrt(rho_L / rho_V)) * dh / mu_L if Re_eq < 1750: Nu = 60 * Pr_L**(1 / 3) else: Nu = ((75 - 60) / (3000 - 1750) * (Re_eq - 1750) + 60) * Pr_L**(1 / 3) h = Nu * k_L / dh return h
def hsatVmax(fluid): Tmin = Props(fluid, 'Tmin') Tmax = Props(fluid, 'Tcrit') def OBJECTIVE(T): return -Props('H', 'T', T, 'Q', 1, fluid) T = scipy.optimize.minimize_scalar(OBJECTIVE, bounds=(Tmin, Tmax), method='Bounded').x h = Props('H', 'T', T, 'Q', 1, fluid) s = Props('S', 'T', T, 'Q', 1, fluid) rho = Props('D', 'T', T, 'Q', 1, fluid) return h * 1000, T, s * 1000, rho
def _Superheat_Forward(self,w_superheat,T_inlet_r=-99): if T_inlet_r==-99: #catch if default case is used T_inlet_r=self.Tdew_r self.w_superheat=w_superheat DWS=DWSVals() #DryWetSegment structure # 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 # 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=T_inlet_r #default is dew temperature; can change to consider for initial superheat DWS.A_r=self.A_r_wetted*w_superheat DWS.cp_r=self.cp_r 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.Ref, "Single"); # Average Refrigerant heat transfer coefficient DWS.h_r=self.h_r_superheat #Run DryWetSegment DryWetSegment(DWS) try: rho_superheat=Props('D','T',(DWS.Tout_r+self.Tdew_r)/2.0, 'P', self.psat_r, self.Ref) #members = [attr for attr in dir(DWS()) if not callable(attr) and not attr.startswith("__")] #print members except: print "error in Evaporator, unreasonable inputs?","Inputs to calculate density are DWS.Tout_r,self.Tdew_r,self.psat_r",DWS.Tout_r,self.Tdew_r,self.psat_r,"<<" print "DWS values are", 'DWS.A_a',DWS.A_a,'DWS.Tin_r',DWS.Tin_r,'DWS.mdot_da',DWS.mdot_da,"DWS.mdot_r",DWS.mdot_r,'DWS.mdot_da',DWS.mdot_da,'DWS.mdot_r',DWS.mdot_r print "plot DWS vals to figure out what is going on" raise() self.Charge_superheat = w_superheat * self.V_r * rho_superheat #Pressure drop calculations for subcooled 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 self.omega_out_superheat=DWS.omega_out
def getTemperature(self, h): if newVersion == True: return PropsSI("T", "H", h / 1000.0, "P", self.pressure, self.name) - 273.15 else: return Props("T", "H", h / 1000.0, "P", self.pressure, self.name) - 273.15
def __init__(self, num_samples = 80, # Have 60 chromos in the sample group num_selected = 20, # Have 10 chromos in the selected group mutation_factor = 2, # Randomly mutate 1/n of the chromosomes num_powers = 4, # How many powers in the fit Ref = 'REFPROP-R134a', value = 'rhoV', addTr = False ): self.num_samples = num_samples self.num_selected = num_selected self.mutation_factor = mutation_factor self.num_powers = num_powers self.addTr = addTr self.value = value self.Ref = Ref #THermodynamics from CoolProp.CoolProp import Props self.Tc = Props(Ref,'Tcrit') self.pc = Props(Ref,'pcrit') self.rhoc = Props(Ref,'rhocrit') self.Tmin = Props(Ref,'Tmin') self.T = np.linspace(self.Tmin+1e-6, self.Tc-0.000001,200) self.p = [Props('P','T',T,'Q',0,Ref) for T in self.T] self.rhoL = [Props('D','T',T,'Q',0,Ref) for T in self.T] self.rhoV = [Props('D','T',T,'Q',1,Ref) for T in self.T] self.logppc = (np.log(self.p)-np.log(self.pc)) self.rhoLrhoc = np.array(self.rhoL)/self.rhoc self.rhoVrhoc = np.array(self.rhoV)/self.rhoc self.logrhoLrhoc = np.log(self.rhoL)-np.log(self.rhoc) self.logrhoVrhoc = np.log(self.rhoV)-np.log(self.rhoc)
def getTemperature(self, h): if (newVersion == True): return ( PropsSI('T', 'H', h / 1000., 'P', self.pressure, self.name) - 273.15) else: return (Props('T', 'H', h / 1000., 'P', self.pressure, self.name) - 273.15)
def evaluate_REFPROP(self, Ref, T, rho): p,cp,cv,w = [],[],[],[] R = 8.314472/Props(Ref,'molemass') for _T,_rho in zip(T, rho): p.append(Props("P",'T',_T,'D',_rho,Ref)) cp.append(Props("C",'T',_T,'D',_rho,Ref)) cv.append(Props("O",'T',_T,'D',_rho,Ref)) w.append(Props("A",'T',_T,'D',_rho,Ref)) class stub: pass PPF = stub() PPF.p = np.array(p, ndmin = 1).T PPF.cp = np.array(cp, ndmin = 1).T PPF.cv = np.array(cv, ndmin = 1).T PPF.w = np.array(w, ndmin = 1).T return PPF
def AccelPressureDrop(x_min, x_max, Ref, G, Tbubble, Tdew, rhosatL=None, rhosatV=None, slipModel='Zivi'): """ Accelerational pressure drop From -dpdz|A=G^2*d[x^2v_g/alpha+(1-x)^2*v_f/(1-alpha)^2]/dz Integrating over z from 0 to L where x=x_1 at z=0 and x=x_2 at z=L Maxima code: alpha:1/(1+S*rho_g/rho_f*(1-x)/x)$ num1:x^2/rho_g$ num2:(1-x)^2/rho_f$ subst(num1/alpha+num2/(1-alpha),x,1); subst(num1/alpha+num2/(1-alpha),x,0); """ if rhosatL == None or rhosatV == None: rhosatL = Props('D', 'T', Tbubble, 'Q', 0.0, Ref) rhosatV = Props('D', 'T', Tdew, 'Q', 1.0, Ref) def f(x, rhoL, rhoV): if abs(x) < 1e-12: return 1 / rhosatL elif abs(1 - x) < 1e-12: return 1 / rhosatV else: if slipModel == 'Zivi': S = pow(rhoL / rhoV, 1 / 3) elif slipModel == 'Homogeneous': S = 1 else: raise ValueError( "slipModel must be either 'Zivi' or 'Homogeneous'") alpha = 1 / (1 + S * rhoV / rhoL * (1 - x) / x) return x**2 / rhoV / alpha + (1 - x)**2 / rhoL / (1 - alpha) return G**2 * (f(x_min, rhosatL, rhosatV) - f(x_max, rhosatL, rhosatV))
def saturation_pressure_brute(Ref, ClassName): Tc = Props(Ref, 'Tcrit') pc = Props(Ref, 'pcrit') rhoc = Props(Ref, 'rhocrit') Tmin = Props(Ref, 'Tmin') TT = np.linspace(Tmin + 1e-6, Tc - 0.00001, 300) p = np.array([Props('P', 'T', T, 'Q', 0, Ref) for T in TT]) rhoL = np.array([Props('D', 'T', T, 'Q', 0, Ref) for T in TT]) rhoV = np.array([Props('D', 'T', T, 'Q', 1, Ref) for T in TT]) Np = 10 max_abserror = 99999 bbest = [] x = 1.0 - TT / Tc y = (np.log(rhoL) - np.log(rhoc)) * TT / Tc def f_p(B, x): # B is a vector of the parameters. # x is an array of the current x values. return sum([_B * x**(_n) for _B, _n in zip(B, b)]) linear = Model(f_p) mydata = Data(x, y) for attempt in range(300): n = [i / 6.0 for i in range(1, 100)] + [ 0.35 + i / 200.0 for i in range(1, 70) ] + [0.05 + 0.01 * i for i in range(1, 70)] b = [] for _ in range(6): i = random.randint(0, len(n) - 1) b.append(n.pop(i)) myodr = ODR(mydata, linear, beta0=[1] * len(b)) myoutput = myodr.run() b = np.array(b) keepers = np.abs(myoutput.sd_beta / myoutput.beta) < 0.1 if any(keepers): b = b[keepers] myodr = ODR(mydata, linear, beta0=[1] * len(b)) myoutput = myodr.run() rho_fit = np.exp(f_p(myoutput.beta, x) * Tc / TT) * rhoc abserror = np.max(np.abs(rho_fit / rhoL - 1)) * 100 print('.') if abserror < max_abserror: max_abserror = abserror bbest = b betabest = myoutput.beta print("%s %s %s" % (abserror, myoutput.sum_square, myoutput.sd_beta / myoutput.beta))
def check_consistency(Fluid, mode, unit_system, T, rho, inputs): if unit_system == 'SI': set_standard_unit_system(unit_systems_constants.UNIT_SYSTEM_SI) elif unit_system == 'kSI': set_standard_unit_system(unit_systems_constants.UNIT_SYSTEM_KSI) else: raise ValueError('invalid unit_system:' + str(unit_system)) if get_REFPROPname(Fluid) == 'N/A': return if mode == 'REFPROP': Fluid = 'REFPROP-' + get_REFPROPname(Fluid) if mode == 'pure' and not IsFluidType(Fluid, 'PureFluid'): return # Evaluate the inputs; if inputs is ('T','P'), calculate the temperature and the pressure Input1 = Props(inputs[0], 'T', T, 'D', rho, Fluid) Input2 = Props(inputs[1], 'T', T, 'D', rho, Fluid) # Evaluate using the inputs given --> back to T,rho TEOS = Props('T', inputs[0], Input1, inputs[1], Input2, Fluid) DEOS = Props('D', inputs[0], Input1, inputs[1], Input2, Fluid) print 'T', inputs[0], Input1, inputs[1], Input2, Fluid # Check they are consistent if abs(TEOS - T) > 1e-1 or abs(DEOS / rho - 1) > 0.05: raise AssertionError( "{T:g} K {D:g} kg/m^3 inputs: \"D\",'{ins1:s}',{in1:.12g},'{ins2:s}',{in2:.12g},\"{fluid:s}\" || T: {TEOS:g} D: {DEOS:g}" .format(T=T, D=rho, TEOS=TEOS, DEOS=DEOS, inputs=str(inputs), in1=Input1, in2=Input2, ins1=inputs[0], ins2=inputs[1], fluid=Fluid))
def _Superheat_Forward(self): # ********************************************************************** # SUPERHEATED PART # ********************************************************************** #Dew temperature for constant pressure cooling to saturation Tdew = self.Tdew # Average fluid temps are used for the calculation of properties # Average temp of refrigerant is average of sat. temp and outlet temp # Secondary fluid is air over the fins self.f_r_superheat, self.h_r_superheat, self.Re_r_superheat = f_h_1phase_Tube( self.mdot_r / self.Ncircuits, self.ID, (Tdew + self.Tin_r) / 2.0, self.psat_r, self.Ref, "Single") cp_r = Props('C', 'T', (Tdew + self.Tin_r) / 2, 'P', self.psat_r, self.Ref) * 1000. #//[J/kg-K] WavyLouveredFins(self.Fins) self.mdot_da = self.Fins.mdot_da # Cross-flow in the superheated region. # Using effectiveness-Ntu relationships for cross flow with non-zero Cr. UA_overall = 1. / (1. / (self.Fins.eta_a * self.Fins.h_a * self.Fins.A_a) + 1. / (self.h_r_superheat * self.A_r_wetted)) epsilon_superheat = (Tdew - self.Tin_r) / (self.Tin_a - self.Tin_r) Ntu = UA_overall / (self.mdot_da * self.Fins.cp_da) if epsilon_superheat > 1.0: epsilon_superheat = 1.0 - 1e-12 self.w_superheat = -log(1 - epsilon_superheat) * self.mdot_r * cp_r / ( (1 - exp(-Ntu)) * self.mdot_da * self.Fins.cp_da) # Positive Q is heat input to the refrigerant, negative Q is heat output from refrigerant. # Heat is removed here from the refrigerant since it is being cooled self.Q_superheat = self.mdot_r * cp_r * (Tdew - self.Tin_r) rho_superheat = Props('D', 'T', (self.Tin_r + Tdew) / 2.0, 'P', self.psat_r, self.Ref) #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 self.Charge_superheat = self.w_superheat * self.V_r * rho_superheat #Latent heat needed for pseudo-quality calc Tbubble = Props('T', 'P', self.psat_r, 'Q', 0, self.Ref) Tdew = Props('T', 'P', self.psat_r, 'Q', 1, self.Ref) h_fg = (Props('H', 'T', Tdew, 'Q', 1, self.Ref) - Props('H', 'T', Tbubble, 'Q', 0, self.Ref)) * 1000 #J/kg self.hin_r = Props('H', 'T', self.Tin_r, 'P', self.psat_r, self.Ref) * 1000 self.xin_r = 1.0 + cp_r * (self.Tin_r - Tdew) / h_fg
def __init__( self, num_samples=100, # Have this many chromos in the sample group num_selected=20, # Have this many chromos in the selected group mutation_factor=2, # Randomly mutate 1/n of the chromosomes num_powers=5, # How many powers in the fit Ref="R407C", value="rhoV", addTr=True, values=None, Tlims=None, ): self.num_samples = num_samples self.num_selected = num_selected self.mutation_factor = mutation_factor self.num_powers = num_powers self.addTr = addTr self.value = value self.Ref = Ref # Thermodynamics from CoolProp.CoolProp import Props if values is None: self.Tc = Props(Ref, "Tcrit") self.pc = Props(Ref, "pcrit") self.rhoc = Props(Ref, "rhocrit") self.Tmin = Props(Ref, "Tmin") if Tlims is None: self.T = np.append( np.linspace(self.Tmin + 1e-14, self.Tc - 1, 150), np.logspace(np.log10(self.Tc - 1), np.log10(self.Tc) - 1e-15, 40), ) else: self.T = np.linspace(Tlims[0], Tlims[1]) self.pL = Props("P", "T", self.T, "Q", 0, Ref) self.pV = Props("P", "T", self.T, "Q", 1, Ref) self.rhoL = Props("D", "T", self.T, "Q", 0, Ref) self.rhoV = Props("D", "T", self.T, "Q", 1, Ref) else: self.Tc = values["Tcrit"] self.pc = values["pcrit"] self.rhoc = values["rhocrit"] self.Tmin = values["Tmin"] self.T = values["T"] self.p = values["p"] self.rhoL = values["rhoL"] self.rhoV = values["rhoV"] self.logpLpc = np.log(self.pL) - np.log(self.pc) self.logpVpc = np.log(self.pV) - np.log(self.pc) self.rhoLrhoc = np.array(self.rhoL) / self.rhoc self.rhoVrhoc = np.array(self.rhoV) / self.rhoc self.logrhoLrhoc = np.log(self.rhoL) - np.log(self.rhoc) self.logrhoVrhoc = np.log(self.rhoV) - np.log(self.rhoc) self.x = 1.0 - self.T / self.Tc MM = Props(Ref, "molemass") self.T_r = self.Tc if self.value == "pL": self.LHS = self.logpLpc.copy() self.EOS_value = self.pL.copy() if self.addTr == False: self.description = "p' = pc*exp(sum(n_i*theta^t_i))" else: self.description = "p' = pc*exp(Tc/T*sum(n_i*theta^t_i))" self.reducing_value = self.pc * 1000 elif self.value == "pV": self.LHS = self.logpVpc.copy() self.EOS_value = self.pV.copy() if self.addTr == False: self.description = "p'' = pc*exp(sum(n_i*theta^t_i))" else: self.description = "p'' = pc*exp(Tc/T*sum(n_i*theta^t_i))" self.reducing_value = self.pc * 1000 elif self.value == "rhoL": self.LHS = self.logrhoLrhoc.copy() self.EOS_value = self.rhoL if self.addTr == False: self.description = "rho' = rhoc*exp(sum(n_i*theta^t_i))" else: self.description = "rho' = rhoc*exp(Tc/T*sum(n_i*theta^t_i))" self.reducing_value = self.rhoc / MM * 1000 elif self.value == "rhoV": self.LHS = self.logrhoVrhoc.copy() self.EOS_value = self.rhoV if self.addTr == False: self.description = "rho'' = rhoc*exp(sum(n_i*theta^t_i))" else: self.description = "rho'' = rhoc*exp(Tc/T*sum(n_i*theta^t_i))" self.reducing_value = self.rhoc / MM * 1000 elif self.value == "rhoLnoexp": self.LHS = (self.rhoLrhoc - 1).copy() self.EOS_value = self.rhoL self.description = "rho' = rhoc*(1+sum(n_i*theta^t_i))" self.reducing_value = self.rhoc / MM * 1000 else: raise ValueError if self.value == "rhoLnoexp" and self.addTr: raise ValueError("Invalid combination") if self.addTr: self.LHS *= self.T / self.Tc
def saturation_pressure(Ref, ClassName, fName=None, LV=None): if fName is None: Tc = Props(Ref, 'Tcrit') pc = Props(Ref, 'pcrit') rhoc = Props(Ref, 'rhocrit') Tmin = Props(Ref, 'Tmin') TT = np.linspace(Tmin + 1e-6, Tc - 0.00001, 300) pL = Props('P', 'T', TT, 'Q', 0, Ref) pV = Props('P', 'T', TT, 'Q', 1, Ref) rhoL = Props('D', 'T', TT, 'Q', 0, Ref) rhoV = Props('D', 'T', TT, 'Q', 1, Ref) else: Tc = 423.27 pc = 3533 rhoc = 470 Tmin = 273 lines = open(fName, 'r').readlines() TT, p, rhoL, rhoV = [], [], [], [] for line in lines: _T, _p, _rhoL, _rhoV = line.split(' ') TT.append(float(_T)) p.append(float(_p)) rhoL.append(float(_rhoL)) rhoV.append(float(_rhoV)) TT = np.array(TT) Np = 60 n = range(1, Np) max_abserror = 0 while len(n) > 3: def f_p(B, x): # B is a vector of the parameters. # x is an array of the current x values. return sum([_B * x**(_n / 2.0) for _B, _n in zip(B, n)]) x = 1.0 - TT / Tc if LV == 'L': y = (np.log(pL) - np.log(pc)) * TT / Tc elif LV == 'V' or LV is None: y = (np.log(pV) - np.log(pc)) * TT / Tc linear = Model(f_p) mydata = Data(x, y) myodr = ODR(mydata, linear, beta0=[0] * len(n)) myoutput = myodr.run() beta = myoutput.beta sd = myoutput.sd_beta p_fit = np.exp(f_p(myoutput.beta, x) * Tc / TT) * pc if LV == 'L': max_abserror = np.max(np.abs((p_fit / pL) - 1) * 100) elif LV == 'V' or LV is None: max_abserror = np.max(np.abs((p_fit / pV) - 1) * 100) print(max_abserror) psat_error = max_abserror dropped_indices = [i for i in range(len(n)) if abs(sd[i]) < 1e-15] if dropped_indices: # for i in reversed(dropped_indices): # randomly drop one of them n.pop(random.choice(dropped_indices)) continue if max_abserror < 0.5: # Max error is 0.5% Ncoeffs = str(list(myoutput.beta)).lstrip('[').rstrip(']') tcoeffs = str(n).lstrip('[').rstrip(']') maxerror = max_abserror N = len(n) else: break # Find the least significant entry (the one with the largest standard error) # and remove it n.pop(np.argmax(sd)) # Remove elements that are not import textwrap template = textwrap.dedent( """ double {name:s}Class::psat{LV:s}(double T) {{ // Maximum absolute error is {psat_error:f} % between {Tmin:f} K and {Tmax:f} K const double t[]={{0, {tcoeffs:s}}}; const double N[]={{0, {Ncoeffs:s}}}; double summer=0,theta; theta=1-T/reduce.T; for (int i=1;i<={N:d};i++) {{ summer += N[i]*pow(theta,t[i]/2); }} return reduce.p.Pa*exp(reduce.T/T*summer); }} """) the_string = template.format(N=len(n) + 1, tcoeffs=tcoeffs, Ncoeffs=Ncoeffs, name=ClassName, Tmin=Tmin, Tmax=TT[-1], psat_error=maxerror, LV=LV if LV in ['L', 'V'] else '' ) f = open('anc.txt', 'a') f.write(the_string) f.close() return the_string
def saturation_density(Ref, ClassName, form='A', LV='L', perc_error_allowed=0.3, fName=None, add_critical=True): """ Parameters ---------- Ref : string The fluid name for the fluid that will be used to generate the saturation data ClassName : The name of the class that will be used in the C++ code form : string If ``'A'``, use a term of the form """ if fName is None: Tc = Props(Ref, 'Tcrit') pc = Props(Ref, 'pcrit') rhoc = Props(Ref, 'rhocrit') Tmin = Props(Ref, 'Tmin') print("%s %s %s" % (Ref, Tmin, Props(Ref, 'Ttriple'))) TT = np.linspace(Tmin, Tc - 1, 1000) TT = list(TT) # Add temperatures around the critical temperature if add_critical: for dT in [1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1]: TT.append(Tc - dT) TT = np.array(sorted(TT)) p = Props('P', 'T', TT, 'Q', 0, Ref) rhoL = Props('D', 'T', TT, 'Q', 0, Ref) rhoV = Props('D', 'T', TT, 'Q', 1, Ref) else: Tc = 423.27 pc = 3533 rhoc = 470 Tmin = 273 lines = open(fName, 'r').readlines() TT, p, rhoL, rhoV = [], [], [], [] for line in lines: _T, _p, _rhoL, _rhoV = line.split(' ') TT.append(float(_T)) p.append(float(_p)) rhoL.append(float(_rhoL)) rhoV.append(float(_rhoV)) TT = np.array(TT) # Start with a large library of potential powers n = [i / 6.0 for i in range(1, 200)] # +[0.35+i/200.0 for i in range(1,70)]+[0.05+0.01*i for i in range(1,70)] x = 1.0 - TT / Tc if LV == 'L': rho_EOS = rhoL elif LV == 'V': rho_EOS = rhoV else: raise ValueError if form == 'A': y = np.array(rho_EOS) / rhoc - 1 elif form == 'B': y = (np.log(rho_EOS) - np.log(rhoc)) * TT / Tc else: raise ValueError max_abserror = 0 while len(n) > 3: print("%s %s" % (max_abserror, len(n))) def f_p(B, x): # B is a vector of the parameters. # x is an array of the current x values. return sum([_B * x**(_n) for _B, _n in zip(B, n)]) linear = Model(f_p) mydata = Data(x, y) myodr = ODR(mydata, linear, beta0=[0] * len(n)) myoutput = myodr.run() beta = myoutput.beta sd = myoutput.sd_beta if form == 'A': rho_fit = (f_p(myoutput.beta, x) + 1) * rhoc elif form == 'B': rho_fit = np.exp(f_p(myoutput.beta, x) * Tc / TT) * rhoc else: raise ValueError print('first,last %s %s %s %s %s %s' % (TT[0], TT[-1], rho_fit[0], rho_fit[-1], rho_EOS[0], rho_EOS[-1])) max_abserror = np.max(np.abs(rho_fit / rho_EOS - 1)) * 100 dropped_indices = [i for i in range(len(n)) if abs(sd[i]) < 1e-15] if dropped_indices: for i in reversed(sorted(dropped_indices)): n.pop(i) print('popping... %s terms remaining' % len(n)) continue if max_abserror > perc_error_allowed: break # The last good run will be used else: print(max_abserror) Ncoeffs = str(list(myoutput.beta)).lstrip('[').rstrip(']') tcoeffs = str(n).lstrip('[').rstrip(']') maxerror = max_abserror if form == 'A': code_template = textwrap.dedent( """ for (int i=1; i<={count:d}; i++) {{ summer += N[i]*pow(theta,t[i]); }} return reduce.rho*(summer+1); """.format(count=len(n)) ) elif form == 'B': code_template = textwrap.dedent( """ for (int i=1; i<={count:d}; i++) {{ summer += N[i]*pow(theta,t[i]); }} return reduce.rho*exp(reduce.T/T*summer); """.format(count=len(n)) ) else: raise ValueError # Find the least significant entry (the one with the largest relative standard error) # and remove it n.pop(np.argmax(np.abs(sd / beta))) # Remove elements that are not template = textwrap.dedent( """ double {name:s}Class::rhosat{LV:s}(double T) {{ // Maximum absolute error is {error:f} % between {Tmin:f} K and {Tmax:f} K const double t[] = {{0, {tcoeffs:s}}}; const double N[] = {{0, {Ncoeffs:s}}}; double summer=0,theta; theta=1-T/reduce.T; \t{code:s} }} """) the_string = template.format(tcoeffs=tcoeffs, Ncoeffs=Ncoeffs, name=ClassName, Tmin=Tmin, Tmax=TT[-1], error=maxerror, code=code_template, LV=LV ) f = open('anc.txt', 'a') f.write(the_string) f.close() return the_string
class GeneticAncillaryFitter(object): def __init__( self, num_samples=100, # Have this many chromos in the sample group num_selected=20, # Have this many chromos in the selected group mutation_factor=2, # Randomly mutate 1/n of the chromosomes num_powers=5, # How many powers in the fit Ref="R407C", value="rhoV", addTr=True, values=None, Tlims=None, ): self.num_samples = num_samples self.num_selected = num_selected self.mutation_factor = mutation_factor self.num_powers = num_powers self.addTr = addTr self.value = value self.Ref = Ref # Thermodynamics from CoolProp.CoolProp import Props if values is None: self.Tc = Props(Ref, "Tcrit") self.pc = Props(Ref, "pcrit") self.rhoc = Props(Ref, "rhocrit") self.Tmin = Props(Ref, "Tmin") if Tlims is None: self.T = np.append( np.linspace(self.Tmin + 1e-14, self.Tc - 1, 150), np.logspace(np.log10(self.Tc - 1), np.log10(self.Tc) - 1e-15, 40), ) else: self.T = np.linspace(Tlims[0], Tlims[1]) self.pL = Props("P", "T", self.T, "Q", 0, Ref) self.pV = Props("P", "T", self.T, "Q", 1, Ref) self.rhoL = Props("D", "T", self.T, "Q", 0, Ref) self.rhoV = Props("D", "T", self.T, "Q", 1, Ref) else: self.Tc = values["Tcrit"] self.pc = values["pcrit"] self.rhoc = values["rhocrit"] self.Tmin = values["Tmin"] self.T = values["T"] self.p = values["p"] self.rhoL = values["rhoL"] self.rhoV = values["rhoV"] self.logpLpc = np.log(self.pL) - np.log(self.pc) self.logpVpc = np.log(self.pV) - np.log(self.pc) self.rhoLrhoc = np.array(self.rhoL) / self.rhoc self.rhoVrhoc = np.array(self.rhoV) / self.rhoc self.logrhoLrhoc = np.log(self.rhoL) - np.log(self.rhoc) self.logrhoVrhoc = np.log(self.rhoV) - np.log(self.rhoc) self.x = 1.0 - self.T / self.Tc MM = Props(Ref, "molemass") self.T_r = self.Tc if self.value == "pL": self.LHS = self.logpLpc.copy() self.EOS_value = self.pL.copy() if self.addTr == False: self.description = "p' = pc*exp(sum(n_i*theta^t_i))" else: self.description = "p' = pc*exp(Tc/T*sum(n_i*theta^t_i))" self.reducing_value = self.pc * 1000 elif self.value == "pV": self.LHS = self.logpVpc.copy() self.EOS_value = self.pV.copy() if self.addTr == False: self.description = "p'' = pc*exp(sum(n_i*theta^t_i))" else: self.description = "p'' = pc*exp(Tc/T*sum(n_i*theta^t_i))" self.reducing_value = self.pc * 1000 elif self.value == "rhoL": self.LHS = self.logrhoLrhoc.copy() self.EOS_value = self.rhoL if self.addTr == False: self.description = "rho' = rhoc*exp(sum(n_i*theta^t_i))" else: self.description = "rho' = rhoc*exp(Tc/T*sum(n_i*theta^t_i))" self.reducing_value = self.rhoc / MM * 1000 elif self.value == "rhoV": self.LHS = self.logrhoVrhoc.copy() self.EOS_value = self.rhoV if self.addTr == False: self.description = "rho'' = rhoc*exp(sum(n_i*theta^t_i))" else: self.description = "rho'' = rhoc*exp(Tc/T*sum(n_i*theta^t_i))" self.reducing_value = self.rhoc / MM * 1000 elif self.value == "rhoLnoexp": self.LHS = (self.rhoLrhoc - 1).copy() self.EOS_value = self.rhoL self.description = "rho' = rhoc*(1+sum(n_i*theta^t_i))" self.reducing_value = self.rhoc / MM * 1000 else: raise ValueError if self.value == "rhoLnoexp" and self.addTr: raise ValueError("Invalid combination") if self.addTr: self.LHS *= self.T / self.Tc def generate_random_chromosomes(self,): """ Create a list of random chromosomes to seed our alogrithm. """ chromos = [] while len(chromos) < self.num_samples: chromos.append(Sample(sorted(random.sample(LIBRARY, self.num_powers)))) return chromos def fitness(self, chromo): """ Fitness of a chromo is the sum of the squares of the error of the correlation """ # theta^t where the i-th row is equal to theta^t[i] # We need these terms later on to build the A and b matrices theta_t = (self.x.reshape(-1, 1) ** chromo.v).T # TODO: more numpy broadcasting should speed this up even more # Another few times faster ought to be possible I = len(chromo.v) A = np.zeros((I, I)) b = np.zeros((I, 1)) for i in range(I): for j in range(I): A[i, j] = np.sum(theta_t[i] * theta_t[j]) b[i] = np.sum(theta_t[i] * self.LHS) # If you end up with a singular matrix, quit this run try: n = np.linalg.solve(A, b).T except np.linalg.linalg.LinAlgError as E: chromo.fitness = 1e99 return chromo.beta = n RHS = np.sum(n * self.x.reshape(-1, 1) ** chromo.v, axis=1) if self.addTr: RHS *= self.Tc / self.T if self.value in ["pL", "pV"]: fit_value = np.exp(RHS) * self.pc elif self.value in ["rhoL", "rhoV"]: fit_value = np.exp(RHS) * self.rhoc elif self.value == "rhoLnoexp": fit_value = self.rhoc * (1 + RHS) else: raise ValueError max_abserror = np.max(np.abs((fit_value / self.EOS_value) - 1) * 100) chromo.fitness = max_abserror chromo.fit_value = fit_value chromo.max_abserror = max_abserror return chromo.fitness def tourny_select_chromo(self, samples): """ Randomly select two chromosomes from the samples, then return the one with the best fitness. """ a = random.choice(samples) b = random.choice(samples) if a.fitness < b.fitness: return a else: return b def breed(self, a, b): """ Breed two chromosomes by splicing them in a random spot and combining them together to form two new chromos. """ splice_pos = random.randrange(len(a.v)) new_a = a.v[:splice_pos] + b.v[splice_pos:] new_b = b.v[:splice_pos] + a.v[splice_pos:] return Sample(sorted(new_a)), Sample(sorted(new_b)) def mutate(self, chromo): """ Mutate a chromosome by changing one of the parameters, but only if it improves the fitness """ v = chromo.v if hasattr(chromo, "fitness"): old_fitness = chromo.fitness else: old_fitness = self.fitness(chromo) for i in range(10): pos = random.randrange(len(chromo.v)) chromo.v[pos] = random.choice(LIBRARY) new_fitness = self.fitness(chromo) if new_fitness < old_fitness: return chromo else: return Sample(sorted(v)) def run(self): # Create a random sample of chromos samples = self.generate_random_chromosomes() # Calculate the fitness for the initial chromosomes for chromo in samples: self.fitness(chromo) # print '#' decorated = [(sample.fitness, sample) for sample in samples] decorated.sort() samples = [s for sv, s in decorated] values = [sv for sv, s in decorated] plt.plot(values[0 : len(values) // 2]) plt.close() # Main loop: each generation select a subset of the sample and breed from # them. generation = -1 while generation < 0 or samples[0].fitness > 0.02 or (generation < 3 and generation < 15): generation += 1 # Generate the selected group from sample- take the top 10% of samples # and tourny select to generate the rest of selected. ten_percent = int(len(samples) * 0.1) selected = samples[:ten_percent] while len(selected) < self.num_selected: selected.append(self.tourny_select_chromo(samples)) # Generate the solution group by breeding random chromos from selected solution = [] while len(solution) < self.num_samples: solution.extend(self.breed(random.choice(selected), random.choice(selected))) # Apply a mutation to a subset of the solution set mutate_indices = random.sample(range(len(solution)), len(solution) // self.mutation_factor) for i in mutate_indices: solution[i] = self.mutate(solution[i]) for chromo in solution: self.fitness(chromo) # print '#' decorated = [(sample.fitness, sample) for sample in solution] decorated.sort() samples = [s for sv, s in decorated] # print '------------------ Top 10 values ---------------' # for sample in samples[0:10]: # print sample.v, sample.fitness, sample.max_abserror # print '// Max error is ',samples[0].max_abserror,'% between',np.min(self.T),'and',np.max(self.T),'K' # print str(samples[0].v), samples[0].beta.tolist() # Print useful stats about this generation (min, median, max) = [samples[0].fitness, samples[len(samples) // 2].fitness, samples[-1].fitness] # print("{0} best value: {1}. fitness: best {2}, median {3}, worst {4}".format(generation, samples[0].v, min, median, max)) # If the string representations of all the chromosomes are the same, stop if len(set([str(s.v) for s in samples[0:5]])) == 1: break self.fitness(samples[0]) print self.value print "// Max error is ", samples[0].max_abserror, "% between", np.min(self.T), "and", np.max(self.T), "K" # print str(samples[0].v), samples[0].beta.tolist() j = dict() j["n"] = samples[0].beta.squeeze().tolist() j["t"] = samples[0].v j["Tmin"] = np.min(self.T) j["Tmax"] = np.max(self.T) j["type"] = self.value j["using_tau_r"] = self.addTr j["reducing_value"] = self.reducing_value j["T_r"] = self.Tc # Informational, not used j["max_abserror_percentage"] = samples[0].max_abserror j["description"] = self.description return j