def full_EoS_nS0(T,muB,**kwargs): """ full EoS (matching between lQCD and HRG) at a fixed T,muB <n_S> = 0 <n_Q> = factQB*<n_B> """ return EoS_nS0(full_EoS,T,muB,**kwargs)
def system(Tmu): """ Define the system to be solved e_nS0(T,muB) = e_input n_B_nS0(T,muB) = n_B_input """ thermo = EoS_nS0(full_EoS,Tmu[0],Tmu[1],**kwargs) # this is unitless return [thermo['e']*Tmu[0]**4./(hbarc**3.) - e, thermo['n_B']*Tmu[0]**3./(hbarc**3.) - nB]
def getmax(): """ get max value of lattice data in the range T < Tmax """ ymin = np.zeros(len(list_quant)) ymax = np.zeros(len(list_quant)) for muB, _ in tab: if (not muBoT): xmuB = muB elif (muBoT): xmuB = muB * xtemp # fixed value of muB/T if (EoS == 'muB'): paramdata = param(xtemp, xmuB, 0., 0.) elif (EoS == 'nS0'): paramdata = EoS_nS0(param, xtemp, xmuB) for iq, quant in enumerate(list_quant): dlQCD = paramdata[quant] if (dlQCD.max() > ymax[iq]): ymax[iq] = dlQCD.max() if (dlQCD.min() < ymax[iq]): ymin[iq] = dlQCD.min() return dict(zip(list_quant, ymin * 1.1)), dict(zip(list_quant, ymax * 1.1))
def fit_freezeout(dict_yield,**kwargs): """ Extract freeze out parameters by fitting final heavy ion data (dN/dy) given in dict_yield. Construct ratios of different particles. """ # additional plots of chi^2 (takes time) try: chi2_plot = kwargs['chi2_plot'] except: chi2_plot = False # default # consider decay from unstable particles in the calculation of densities? try: freezeout_decay = kwargs['freezeout_decay'] except: freezeout_decay = True # apply fit to both yields and ratios? try: method = kwargs['method'] except: method = 'all' # consider integration over mass? try: offshell = kwargs['offshell'] except: offshell = False # default # evaluate freeze out parameters for which EoS? full or strangeness neutrality ns0 ? try: EoS = kwargs['EoS'] except: EoS = 'all' # default # we fit the HRG EoS to the ratios of particle yields as list_part1/list_part2 # as in BES STAR paper: PHYSICAL REVIEW C 96, 044904 (2017) list_part1 = ['pi-','K-','p~','Lambda~','Xi~+','K-','p~','Lambda','Xi~+'] list_part2 = ['pi+','K+','p','Lambda','Xi-','pi-','pi-','pi-','pi-'] # unique list of particles (for the yields) list_part = ['pi+','pi-','K+','K-','p','p~','Lambda','Lambda~','Xi-','Xi~+'] # construct yields from input data_yields = [] err_yields = [] final_part = [] for part in list_part: try: # check if the particles are given in dict_yield if(dict_yield[part]!=None and dict_yield[part]>0.): data_yields.append(dict_yield[part]) err_yields.append(dict_yield[part+'_err']) final_part.append(part) except: pass # construct ratios from input yields data_ratios = [] err_ratios = [] final_part1 = [] final_part2 = [] # loop over particles in list_part1 and list_part2 for part1,part2 in zip(list_part1,list_part2): try: # check if the particles are given in dict_yield if(dict_yield[part1]!=None and dict_yield[part1]>0. and dict_yield[part2]!=None and dict_yield[part2]>0.): ratio = dict_yield[part1]/dict_yield[part2] data_ratios.append(ratio) err_ratios.append(abs(ratio)*np.sqrt((dict_yield[part1+'_err']/dict_yield[part1])**2.+(dict_yield[part2+'_err']/dict_yield[part2])**2.)) final_part1.append(part1) final_part2.append(part2) except: pass def f_yields(x,T,muB,muQ,muS,gammaS,dVdy): """ Calculate the particle yields for fixed T,muB,muQ,muS,gammaS,volume x is a dummy argument """ result = np.zeros(len(final_part)) # calculate all densities result_HRG = HRG_freezout(T,muB,muQ,muS,gammaS,EoS='full',**kwargs) # loop over particles for i,part in enumerate(final_part): yval = result_HRG[part] #print('part,yval=',part,yval) # if no decays, then Sigma0 should be added to Lambda # if decays are activated, then Sigma0 decays to Lambda if(not(freezeout_decay)): # include Sigma0 with Lambda if(part=='Lambda'): yval += result_HRG['Sigma0'] # include Sigma~0 with Lambda~ elif(part=='Lambda~'): yval += result_HRG['Sigma~0'] # number of particles result[i] = yval*T**3.*dVdy/(0.197**3.) # return the list of yields return result def f_yields_nS0(x,T,muB,gammaS,dVdy): """ Calculate the particle yields for fixed T,muB,gammaS,volume x is a dummy argument """ result = np.zeros(len(final_part)) # calculate all densities result_HRG = HRG_freezout(T,muB,0.,0.,gammaS,EoS='nS0',**kwargs) # loop over particles for i,part in enumerate(final_part): yval = result_HRG[part] #print('part,yval=',part,yval) # if no decays, then Sigma0 should be added to Lambda # if decays are activated, then Sigma0 decays to Lambda if(not(freezeout_decay)): # include Sigma0 with Lambda if(part=='Lambda'): yval += result_HRG['Sigma0'] # include Sigma~0 with Lambda~ elif(part=='Lambda~'): yval += result_HRG['Sigma~0'] # number of particles result[i] = yval*T**3.*dVdy/(0.197**3.) # return the list of yields return result def f_ratios(x,T,muB,muQ,muS,gammaS): """ Calculate the ratios of particle yields for fixed T,muB,muQ,muS,gammaS x is a dummy argument """ result = np.zeros(len(data_ratios)) # calculate all densities result_HRG = HRG_freezout(T,muB,muQ,muS,gammaS,EoS='full',**kwargs) # loop over different ratios for i,(part1,part2) in enumerate(zip(final_part1,final_part2)): yval1 = result_HRG[part1] yval2 = result_HRG[part2] #print('part,yval1=',part1,yval1) #print('part,yval2=',part2,yval2) # if no decays, then Sigma0 should be added to Lambda # if decays are activated, then Sigma0 decays to Lambda if(not(freezeout_decay)): # include Sigma0 with Lambda if(part1=='Lambda'): yval1 += result_HRG['Sigma0'] # include Sigma~0 with Lambda~ elif(part1=='Lambda~'): yval1 += result_HRG['Sigma~0'] # include Sigma0 with Lambda if(part2=='Lambda'): yval2 += result_HRG['Sigma0'] # include Sigma~0 with Lambda~ elif(part2=='Lambda~'): yval2 += result_HRG['Sigma~0'] # ratio of particle1/particle2 result[i] = yval1/yval2 # return the list of ratios return result def f_ratios_nS0(x,T,muB,gammaS): """ Calculate the ratios of particle yields for fixed T,muB,gammaS x is a dummy argument """ result = np.zeros(len(data_ratios)) # calculate all densities result_HRG = HRG_freezout(T,muB,0.,0.,gammaS,EoS='nS0',**kwargs) # loop over different ratios for i,(part1,part2) in enumerate(zip(final_part1,final_part2)): yval1 = result_HRG[part1] yval2 = result_HRG[part2] #print('part,yval1=',part1,yval1) #print('part,yval2=',part2,yval2) # if no decays, then Sigma0 should be added to Lambda # if decays are activated, then Sigma0 decays to Lambda if(not(freezeout_decay)): # include Sigma0 with Lambda if(part1=='Lambda'): yval1 += result_HRG['Sigma0'] # include Sigma~0 with Lambda~ elif(part1=='Lambda~'): yval1 += result_HRG['Sigma~0'] # include Sigma0 with Lambda if(part2=='Lambda'): yval2 += result_HRG['Sigma0'] # include Sigma~0 with Lambda~ elif(part2=='Lambda~'): yval2 += result_HRG['Sigma~0'] # ratio of particle1/particle2 result[i] = yval1/yval2 # return the list of ratios return result # initialize the parameters # which parameters to be fixed? fix_T=False fix_muB=False fix_muQ=False fix_muS=False fix_gammaS=False fix_dVdy=False # first guesses for T,muB,muQ,muS,gammaS,dVdy guess = (0.150, 0.05, 0., 0.05, 1., 2000.) # bounds bounds = ((0.100, 0.200), (0, 0.6), (-0.2,0.2), (0,0.2), (0.0,1.2), (100.,10000.)) # fit with yields if((EoS=='all' or EoS=='full') and (method=='all' or method=='yields')): # x-values, just the indexes of ratios [1,2,...,N_particles] xyields = np.arange(len(final_part)) # initialize Minuit least_squares class least_squares = LeastSquares(xyields, data_yields, err_yields, f_yields) m = Minuit(least_squares, T=guess[0], muB=guess[1], muQ=guess[2], muS=guess[3], gammaS=guess[4], dVdy=guess[5], limit_T=bounds[0],limit_muB=bounds[1],limit_muQ=bounds[2],limit_muS=bounds[3],limit_gammaS=bounds[4],limit_dVdy=bounds[5], fix_T=fix_T,fix_muB=fix_muB,fix_muQ=fix_muQ,fix_muS=fix_muS,fix_gammaS=fix_gammaS,fix_dVdy=fix_dVdy) m.migrad() # finds minimum of least_squares function m.hesse() # computes errors #print(m.params) # minuit output # display values and errors popt1 = m.values.values() perr1 = m.errors.values() print('\nfit from yields, full EoS:') fit_string1 = f'$T_{{ch}}={popt1[0]:.4f} \pm {perr1[0]:.4f}\ GeV$\ \n$\mu_{{B}}={popt1[1]:.4f} \pm {perr1[1]:.4f}\ GeV$\ \n$\mu_{{Q}}={popt1[2]:.4f} \pm {perr1[2]:.4f}\ GeV$\ \n$\mu_{{S}}={popt1[3]:.4f} \pm {perr1[3]:.4f}\ GeV$\ \n$\gamma_{{S}}={popt1[4]:.2f} \pm {perr1[4]:.2f}$\ \n$dV/dy={popt1[5]:.1f} \pm {perr1[5]:.1f} \ fm^3$' print(fit_string1) thermo = HRG(popt1[0],popt1[1],popt1[2],popt1[3],gammaS=popt1[4],offshell=offshell) snB1 = thermo['s']/thermo['n_B'] snB1_err = 0. # derivative wrt T thermoT1 = HRG(popt1[0]+perr1[0]/2.,popt1[1],popt1[2],popt1[3],gammaS=popt1[4],offshell=offshell) thermoT2 = HRG(popt1[0]-perr1[0]/2.,popt1[1],popt1[2],popt1[3],gammaS=popt1[4],offshell=offshell) if(thermoT1['n_B']!=0. and thermoT2['n_B']!=0.): snB1_err += (thermoT1['s']/thermoT1['n_B']-thermoT2['s']/thermoT2['n_B'])**2. # derivative wrt mu_B thermomuB1 = HRG(popt1[0],popt1[1]+perr1[1]/2.,popt1[2],popt1[3],gammaS=popt1[4],offshell=offshell) thermomuB2 = HRG(popt1[0],popt1[1]-perr1[1]/2.,popt1[2],popt1[3],gammaS=popt1[4],offshell=offshell) if(thermomuB1['n_B']!=0. and thermomuB2['n_B']!=0.): snB1_err += (thermomuB1['s']/thermomuB1['n_B']-thermomuB2['s']/thermomuB2['n_B'])**2. # derivative wrt mu_Q thermomuQ1 = HRG(popt1[0],popt1[1],popt1[2]+perr1[2]/2.,popt1[3],gammaS=popt1[4],offshell=offshell) thermomuQ2 = HRG(popt1[0],popt1[1],popt1[2]-perr1[2]/2.,popt1[3],gammaS=popt1[4],offshell=offshell) if(thermomuQ1['n_B']!=0. and thermomuQ2['n_B']!=0.): snB1_err += (thermomuQ1['s']/thermomuQ1['n_B']-thermomuQ2['s']/thermomuQ2['n_B'])**2. # derivative wrt mu_S thermomuS1 = HRG(popt1[0],popt1[1],popt1[2],popt1[3]+perr1[3]/2.,gammaS=popt1[4],offshell=offshell) thermomuS2 = HRG(popt1[0],popt1[1],popt1[2],popt1[3]-perr1[3]/2.,gammaS=popt1[4],offshell=offshell) if(thermomuS1['n_B']!=0. and thermomuS2['n_B']!=0.): snB1_err += (thermomuS1['s']/thermomuS1['n_B']-thermomuS2['s']/thermomuS2['n_B'])**2. # derivative wrt gamma_S thermogammaS1 = HRG(popt1[0],popt1[1],popt1[2],popt1[3],gammaS=popt1[4]+perr1[4]/2.,offshell=offshell) thermogammaS2 = HRG(popt1[0],popt1[1],popt1[2],popt1[3],gammaS=popt1[4]-perr1[4]/2.,offshell=offshell) if(thermogammaS1['n_B']!=0. and thermogammaS2['n_B']!=0.): snB1_err += (thermogammaS1['s']/thermogammaS1['n_B']-thermogammaS2['s']/thermogammaS2['n_B'])**2. # error as sqrt((df/dT)**2. dT+(df/dmuB)**2.+...) with f = s/n_B snB1_err = np.sqrt(snB1_err) print(f's/n_B = {snB1} \pm {snB1_err}') # evaluate the chi^2 values for each parameter if(chi2_plot): dT, fT = m.profile('T') dmuB, fmuB = m.profile('muB') dmuQ, fmuQ = m.profile('muQ') dmuS, fmuS = m.profile('muS') dgammaS, fgammaS = m.profile('gammaS') ddVdy, fdVdy = m.profile('dVdy') output_chi21 = [[dT,fT],[dmuB,fmuB],[dmuQ,fmuQ],[dmuS,fmuS],[dgammaS,fgammaS],[ddVdy,fdVdy]] else: output_chi21 = None output_yields = {'fit_yields':np.array(list(zip(popt1,perr1))),\ 'fit_string_yields':fit_string1,\ 'result_yields':f_yields(xyields,*popt1),\ 'data_yields':np.array(list(zip(data_yields,err_yields))),\ 'particle_yields':list(latex(final_part)),\ 'chi2_yields':output_chi21,\ 'snB_yields':np.array([snB1,snB1_err])} else: output_yields = {} # fit with yields # strangeness neutrality if((EoS=='all' or EoS=='nS0') and (method=='all' or method=='yields')): # x-values, just the indexes of ratios [1,2,...,N_particles] xyields = np.arange(len(final_part)) # initialize Minuit least_squares class least_squares = LeastSquares(xyields, data_yields, err_yields, f_yields_nS0) m = Minuit(least_squares, T=guess[0], muB=guess[1], gammaS=guess[4], dVdy=guess[5], limit_T=bounds[0],limit_muB=bounds[1],limit_gammaS=bounds[4],limit_dVdy=bounds[5], fix_T=fix_T,fix_muB=fix_muB,fix_gammaS=fix_gammaS,fix_dVdy=fix_dVdy) m.migrad() # finds minimum of least_squares function m.hesse() # computes errors #print(m.params) # minuit output # display values and errors popt1 = m.values.values() perr1 = m.errors.values() thermo = EoS_nS0(HRG,popt1[0],popt1[1],gammaS=popt1[2],offshell=offshell) print('\nfit from yields, nS0 EoS:') fit_string1 = f'$T_{{ch}}={popt1[0]:.4f} \pm {perr1[0]:.4f}\ GeV$\ \n$\mu_{{B}}={popt1[1]:.4f} \pm {perr1[1]:.4f}\ GeV$\ \n$\gamma_{{S}}={popt1[2]:.2f} \pm {perr1[2]:.2f}$\ \n$dV/dy={popt1[3]:.1f} \pm {perr1[3]:.1f} \ fm^3$\ \n$\mu_{{Q}}={thermo["muQ"]:.4f}\ GeV$\ \n$\mu_{{S}}={thermo["muS"]:.4f}\ GeV$' print(fit_string1) snB1 = thermo['s']/thermo['n_B'] snB1_err = 0. # derivative wrt T thermoT1 = EoS_nS0(HRG,popt1[0]+perr1[0]/2.,popt1[1],gammaS=popt1[2],offshell=offshell) thermoT2 = EoS_nS0(HRG,popt1[0]-perr1[0]/2.,popt1[1],gammaS=popt1[2],offshell=offshell) if(thermoT1['n_B']!=0. and thermoT2['n_B']!=0.): snB1_err += (thermoT1['s']/thermoT1['n_B']-thermoT2['s']/thermoT2['n_B'])**2. # derivative wrt mu_B thermomuB1 = EoS_nS0(HRG,popt1[0],popt1[1]+perr1[1]/2.,gammaS=popt1[2],offshell=offshell) thermomuB2 = EoS_nS0(HRG,popt1[0],popt1[1]-perr1[1]/2.,gammaS=popt1[2],offshell=offshell) if(thermomuB1['n_B']!=0. and thermomuB2['n_B']!=0.): snB1_err += (thermomuB1['s']/thermomuB1['n_B']-thermomuB2['s']/thermomuB2['n_B'])**2. # derivative wrt gamma_S thermogammaS1 = EoS_nS0(HRG,popt1[0],popt1[1],gammaS=popt1[2]+perr1[2]/2.,offshell=offshell) thermogammaS2 = EoS_nS0(HRG,popt1[0],popt1[1],gammaS=popt1[2]-perr1[2]/2.,offshell=offshell) if(thermogammaS1['n_B']!=0. and thermogammaS2['n_B']!=0.): snB1_err += (thermogammaS1['s']/thermogammaS1['n_B']-thermogammaS2['s']/thermogammaS2['n_B'])**2. # error as sqrt((df/dT)**2. dT+(df/dmuB)**2.+...) with f = s/n_B snB1_err = np.sqrt(snB1_err) print(f's/n_B = {snB1} \pm {snB1_err}') # evaluate the chi^2 values for each parameter if(chi2_plot): dT, fT = m.profile('T') dmuB, fmuB = m.profile('muB') dgammaS, fgammaS = m.profile('gammaS') ddVdy, fdVdy = m.profile('dVdy') output_chi21 = [[dT,fT],[dmuB,fmuB],[dgammaS,fgammaS],[ddVdy,fdVdy]] else: output_chi21 = None result_yields_nS0 = f_yields_nS0(xyields,*popt1) Tch,muB,gammaS,dVdy = popt1 Tch_err,muB_err,gammaS_err,dVdy_err = perr1 popt1 = np.array([Tch,muB,thermo['muQ'],thermo['muS'],gammaS,dVdy]) perr1 = np.array([Tch_err,muB_err,0.,0.,gammaS_err,dVdy_err]) output_yields_nS0 = {'fit_yields_nS0':np.array(list(zip(popt1,perr1))),\ 'fit_string_yields_nS0':fit_string1,\ 'result_yields_nS0':result_yields_nS0,\ 'data_yields':np.array(list(zip(data_yields,err_yields))),\ 'particle_yields':list(latex(final_part)),\ 'chi2_yields_nS0':output_chi21,\ 'snB_yields_nS0':np.array([snB1,snB1_err])} else: output_yields_nS0 = {} # fit with ratios if((EoS=='all' or EoS=='full') and (method=='all' or method=='ratios')): # x-values, just the indexes of ratios [1,2,...,N_ratios] xratios = np.arange(len(data_ratios)) # initialize Minuit least_squares class least_squares = LeastSquares(xratios, data_ratios, err_ratios, f_ratios) m = Minuit(least_squares, T=guess[0], muB=guess[1], muQ=guess[2], muS=guess[3], gammaS=guess[4], limit_T=bounds[0],limit_muB=bounds[1],limit_muQ=bounds[2],limit_muS=bounds[3],limit_gammaS=bounds[4], fix_T=fix_T,fix_muB=fix_muB,fix_muQ=fix_muQ,fix_muS=fix_muS,fix_gammaS=fix_gammaS) m.migrad() # finds minimum of least_squares function m.hesse() # computes errors #print(m.params) # minuit output # display values and errors popt2 = m.values.values() perr2 = m.errors.values() print('\nfit from ratios, full EoS:') fit_string2 = f'$T_{{ch}}={popt2[0]:.4f} \pm {perr2[0]:.4f}\ GeV$\ \n$\mu_{{B}}={popt2[1]:.4f} \pm {perr2[1]:.4f}\ GeV$\ \n$\mu_{{Q}}={popt2[2]:.4f} \pm {perr2[2]:.4f}\ GeV$\ \n$\mu_{{S}}={popt2[3]:.4f} \pm {perr2[3]:.4f}\ GeV$\ \n$\gamma_{{S}}={popt2[4]:.2f} \pm {perr2[4]:.2f}$' print(fit_string2) thermo = HRG(popt2[0],popt2[1],popt2[2],popt2[3],gammaS=popt2[4],offshell=offshell) snB2 = thermo['s']/thermo['n_B'] snB2_err = 0. # derivative wrt T thermoT1 = HRG(popt2[0]+perr2[0]/2.,popt2[1],popt2[2],popt2[3],gammaS=popt2[4],offshell=offshell) thermoT2 = HRG(popt2[0]-perr2[0]/2.,popt2[1],popt2[2],popt2[3],gammaS=popt2[4],offshell=offshell) if(thermoT1['n_B']!=0. and thermoT2['n_B']!=0.): snB2_err += (thermoT1['s']/thermoT1['n_B']-thermoT2['s']/thermoT2['n_B'])**2. # derivative wrt mu_B thermomuB1 = HRG(popt2[0],popt2[1]+perr2[1]/2.,popt2[2],popt2[3],gammaS=popt2[4],offshell=offshell) thermomuB2 = HRG(popt2[0],popt2[1]-perr2[1]/2.,popt2[2],popt2[3],gammaS=popt2[4],offshell=offshell) if(thermomuB1['n_B']!=0. and thermomuB2['n_B']!=0.): snB2_err += (thermomuB1['s']/thermomuB1['n_B']-thermomuB2['s']/thermomuB2['n_B'])**2. # derivative wrt mu_Q thermomuQ1 = HRG(popt2[0],popt2[1],popt2[2]+perr2[2]/2.,popt2[3],gammaS=popt2[4],offshell=offshell) thermomuQ2 = HRG(popt2[0],popt2[1],popt2[2]-perr2[2]/2.,popt2[3],gammaS=popt2[4],offshell=offshell) if(thermomuQ1['n_B']!=0. and thermomuQ2['n_B']!=0.): snB2_err += (thermomuQ1['s']/thermomuQ1['n_B']-thermomuQ2['s']/thermomuQ2['n_B'])**2. # derivative wrt mu_S thermomuS1 = HRG(popt2[0],popt2[1],popt2[2],popt2[3]+perr2[3]/2.,gammaS=popt2[4],offshell=offshell) thermomuS2 = HRG(popt2[0],popt2[1],popt2[2],popt2[3]-perr2[3]/2.,gammaS=popt2[4],offshell=offshell) if(thermomuS1['n_B']!=0. and thermomuS2['n_B']!=0.): snB2_err += (thermomuS1['s']/thermomuS1['n_B']-thermomuS2['s']/thermomuS2['n_B'])**2. # derivative wrt gamma_S thermogammaS1 = HRG(popt2[0],popt2[1],popt2[2],popt2[3],gammaS=popt2[4]+perr2[4]/2.,offshell=offshell) thermogammaS2 = HRG(popt2[0],popt2[1],popt2[2],popt2[3],gammaS=popt2[4]-perr2[4]/2.,offshell=offshell) if(thermogammaS1['n_B']!=0. and thermogammaS2['n_B']!=0.): snB2_err += (thermogammaS1['s']/thermogammaS1['n_B']-thermogammaS2['s']/thermogammaS2['n_B'])**2. # error as sqrt((df/dT)**2. dT+(df/dmuB)**2.+...) with f = s/n_B snB2_err = np.sqrt(snB2_err) print(f's/n_B = {snB2} \pm {snB2_err}') # evaluate the chi^2 values for each parameter if(chi2_plot): dT, fT = m.profile('T') dmuB, fmuB = m.profile('muB') dmuQ, fmuQ = m.profile('muQ') dmuS, fmuS = m.profile('muS') dgammaS, fgammaS = m.profile('gammaS') output_chi22 = [[dT,fT],[dmuB,fmuB],[dmuQ,fmuQ],[dmuS,fmuS],[dgammaS,fgammaS]] else: output_chi22 = None output_ratios = {'fit_ratios':np.array(list(zip(popt2,perr2))),\ 'fit_string_ratios':fit_string2,\ 'result_ratios':f_ratios(xratios,*popt2),\ 'data_ratios':np.array(list(zip(data_ratios,err_ratios))),\ 'particle_ratios':list(zip(latex(final_part1),latex(final_part2))),\ 'chi2_ratios':output_chi22,\ 'snB_ratios':np.array([snB2,snB2_err])} else: output_ratios = {} # fit with ratios if((EoS=='all' or EoS=='nS0') and (method=='all' or method=='ratios')): # x-values, just the indexes of ratios [1,2,...,N_ratios] xratios = np.arange(len(data_ratios)) # initialize Minuit least_squares class least_squares = LeastSquares(xratios, data_ratios, err_ratios, f_ratios_nS0) m = Minuit(least_squares, T=guess[0], muB=guess[1], gammaS=guess[4], limit_T=bounds[0],limit_muB=bounds[1],limit_gammaS=bounds[4], fix_T=fix_T,fix_muB=fix_muB,fix_gammaS=fix_gammaS) m.migrad() # finds minimum of least_squares function m.hesse() # computes errors #print(m.params) # minuit output # display values and errors popt2 = m.values.values() perr2 = m.errors.values() thermo = EoS_nS0(HRG,popt2[0],popt2[1],gammaS=popt2[2],offshell=offshell) print('\nfit from ratios, nS0 EoS:') fit_string2 = f'$T_{{ch}}={popt2[0]:.4f} \pm {perr2[0]:.4f}\ GeV$\ \n$\mu_{{B}}={popt2[1]:.4f} \pm {perr2[1]:.4f}\ GeV$\ \n$\gamma_{{S}}={popt2[2]:.2f} \pm {perr2[2]:.2f}$\ \n$\mu_{{Q}}={thermo["muQ"]:.4f}\ GeV$\ \n$\mu_{{S}}={thermo["muS"]:.4f}\ GeV$' print(fit_string2) snB2 = thermo['s']/thermo['n_B'] snB2_err = 0. # derivative wrt T thermoT1 = EoS_nS0(HRG,popt2[0]+perr2[0]/2.,popt2[1],gammaS=popt2[2],offshell=offshell) thermoT2 = EoS_nS0(HRG,popt2[0]-perr2[0]/2.,popt2[1],gammaS=popt2[2],offshell=offshell) if(thermoT1['n_B']!=0. and thermoT2['n_B']!=0.): snB2_err += (thermoT1['s']/thermoT1['n_B']-thermoT2['s']/thermoT2['n_B'])**2. # derivative wrt mu_B thermomuB1 = EoS_nS0(HRG,popt2[0],popt2[1]+perr2[1]/2.,gammaS=popt2[2],offshell=offshell) thermomuB2 = EoS_nS0(HRG,popt2[0],popt2[1]-perr2[1]/2.,gammaS=popt2[2],offshell=offshell) if(thermomuB1['n_B']!=0. and thermomuB2['n_B']!=0.): snB2_err += (thermomuB1['s']/thermomuB1['n_B']-thermomuB2['s']/thermomuB2['n_B'])**2. # derivative wrt gamma_S thermogammaS1 = EoS_nS0(HRG,popt2[0],popt2[1],gammaS=popt2[2]+perr2[2]/2.,offshell=offshell) thermogammaS2 = EoS_nS0(HRG,popt2[0],popt2[1],gammaS=popt2[2]-perr2[2]/2.,offshell=offshell) if(thermogammaS1['n_B']!=0. and thermogammaS2['n_B']!=0.): snB2_err += (thermogammaS1['s']/thermogammaS1['n_B']-thermogammaS2['s']/thermogammaS2['n_B'])**2. # error as sqrt((df/dT)**2. dT+(df/dmuB)**2.+...) with f = s/n_B snB2_err = np.sqrt(snB2_err) print(f's/n_B = {snB2} \pm {snB2_err}') # evaluate the chi^2 values for each parameter if(chi2_plot): dT, fT = m.profile('T') dmuB, fmuB = m.profile('muB') dgammaS, fgammaS = m.profile('gammaS') output_chi22 = [[dT,fT],[dmuB,fmuB],[dgammaS,fgammaS]] else: output_chi22 = None result_ratios_nS0 = f_ratios_nS0(xratios,*popt2) Tch,muB,gammaS = popt2 Tch_err,muB_err,gammaS_err = perr2 popt2 = np.array([Tch,muB,thermo['muQ'],thermo['muS'],gammaS]) perr2 = np.array([Tch_err,muB_err,0.,0.,gammaS_err]) output_ratios_nS0 = {'fit_ratios_nS0':np.array(list(zip(popt2,perr2))),\ 'fit_string_ratios_nS0':fit_string2,\ 'result_ratios_nS0':result_ratios_nS0,\ 'data_ratios':np.array(list(zip(data_ratios,err_ratios))),\ 'particle_ratios':list(zip(latex(final_part1),latex(final_part2))),\ 'chi2_ratios_nS0':output_chi22,\ 'snB_ratios_nS0':np.array([snB2,snB2_err])} else: output_ratios_nS0 = {} output = {} output.update(output_yields) output.update(output_ratios) output.update(output_yields_nS0) output.update(output_ratios_nS0) return output
def HRG_freezout(T,muB,muQ,muS,gammaS,EoS='full',**kwargs): """ Calculate all particle number densities from HRG Includes decays as well. """ # list of particles to consider list_particles = HRG_mesons + HRG_baryons + to_antiparticle(HRG_mesons) + to_antiparticle(HRG_baryons) # particles are listed from the heaviest to the lightest # that's for the decays list_particles.sort(reverse=True,key=lambda part: mass(part)) # for tests only #for part in list_particles: # print_decays(part) #input('pause') # consider integration over mass? try: offshell = kwargs['offshell'] except: offshell = False # default # which particles are corrected for feed-down weak decays? try: no_feeddown = kwargs['no_feeddown'] # if all particles are corrected for feed-down weak decays if(no_feeddown=='all'): no_feeddown = [part.name for part in list_particles] except: # default (like BES data, pions and lambda are corrected, protons are inclusive) no_feeddown = ['pi+','pi-','Lambda','Lambda~']#,'p','p~'] # consider decay from unstable particles in the calculation of densities? try: freezeout_decay = kwargs['freezeout_decay'] # in PHSD, the eta and strange baryons haven't decayed in the final output if(freezeout_decay=='PHSD'): # stable particles stables = ['eta'] freezeout_decay = True # all particles should be corrected for feed-down weak decays no_feeddown = [part.name for part in list_particles] except: freezeout_decay = True # default stables = [] # all particles decay # initial densities of particles init_dens = {} # dictionnary containing the densities from weak decays feeddown_dens = {} # dictionnary containing final densities (incuding possible decays) from HRG final_dens = {} # fill dictionnaries with densities if(EoS=='nS0'): init_EoS = EoS_nS0(HRG,T,muB,gammaS=gammaS,offshell=offshell) # keep the values of muQ and muS for later muQ = init_EoS['muQ'] muS = init_EoS['muS'] for part in list_particles: part_dens = HRG(T,muB,muQ,muS,gammaS=gammaS,offshell=offshell,species=part.name)['n'] init_dens[part.name] = part_dens feeddown_dens[part.name] = 0. final_dens[part.name] = part_dens # particles which contribute to feed-down weak decays weak_decays = ['K0','Lambda','Sigma+','Sigma-','Xi-','Xi0','Omega-'] # add their corresponding antiparticles weak_decays += [to_antiparticle(to_particle(part)).name for part in weak_decays] # decay of unstable particles, which can be added to the density # loop over all the considered particles if(freezeout_decay): for parent in list_particles: # see if this particle can decay list_decays = part_decay(parent) #print_decays(parent) # if a particle is considered stable, skip if(parent.name in stables): continue # half of the K0 & K~0 go to K(S)0 if(parent.name=='K0' or parent.name=='K~0'): list_decays = part_decay(to_particle('K(S)0')) # half of the K0 & K~0 go to K(S)0 # so divide the K(S)0 branchings by 2 fact_br = 0.5 else: fact_br = 1. #print_decays(to_particle('K(S)0')) # loop over decay channels if not None if(list_decays!=None): for decay in list_decays: br = fact_br*decay[0] # branching children = decay[1] # loop over child particles for child in children: # try if child particle is in dictionnary try: final_dens[child.name] += br*final_dens[parent.name] # count feed-down weak decays for this particle if(parent.name in weak_decays): feeddown_dens[child.name] += br*final_dens[parent.name] except: pass # correct (substract) densities from weak decays for particles in list no_feeddown for part in no_feeddown: final_dens[part] -= feeddown_dens[part] # output to compare final and initial densities #for part in list_particles: # print(part.name,init_dens[part.name],final_dens[part.name],'from decays [%]:',(final_dens[part.name]-init_dens[part.name])*100./final_dens[part.name]) #input('pause') return final_dens
def main(EoS, tab, muBoT=False): ############################################################################### __doc__ = """Produce plots for the HRG EoS defined in EoS_HRG.HRG and compare with lQCD data from EoS_HRG.fit_lattice for two different settings: - 'muB' refers to the EoS with the condition \mu_Q = \mu_S = 0 - 'nS0' refers to the EoS with the condition <n_S> = 0 & <n_Q> = 0.4 <n_B> """ parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('--Tmin', type=float, default=.1, help='minimum temperature [GeV]') parser.add_argument('--Tmax', type=float, default=.2, help='maximum temperature [GeV]') parser.add_argument('--Npoints', type=int, default=15, help='Number of points to plot') parser.add_argument( '--species', choices=['all', 'baryons', 'mesons'], default='all', help='include only baryons, mesons, or everything in the HRG EoS') parser.add_argument( '--offshell', type=str2bool, default=True, help='account for finite width of resonances and integrate over mass') parser.add_argument('--output', type=str2bool, default=False, help='write the HRG results in output file') args = parser.parse_args() # create new directory to save plots if it doesn't already exist if not os.path.exists(f'{dir_path}/plot_HRG/'): os.makedirs(f'{dir_path}/plot_HRG/') Tmin = args.Tmin Tmax = args.Tmax species_out = '' if (args.species != 'all'): species_out = "_" + args.species print(EoS) # quantities to plot list_quant = ['P', 'n_B', 'n_S', 's', 'e', 'I'] # initialize plots with lattice data dict_plots = plot_lattice(EoS, tab, list_quant, all_labels=False, muBoT=muBoT) # initialize values of T to evaluate xtemp = np.linspace(Tmin, Tmax, args.Npoints) for muB, color in tab: if (not muBoT): print(' muB = ', muB, ' GeV') elif (muBoT): print(' muB/T = ', muB) if (not muBoT): xmuB = muB elif (muBoT): xmuB = muB * xtemp # fixed value of muB/T if (EoS != 'nS0'): yval = HRG(xtemp, xmuB, 0., 0., species=args.species, offshell=args.offshell) else: yval = EoS_nS0(HRG, xtemp, xmuB, species=args.species, offshell=args.offshell) for quant in list_quant: if (quant == 'n_B' and muB == 0): # don't plot n_B when \mu_B = 0 continue # plot HRG EoS with solid line below Tc if (not muBoT): cond = xtemp <= Tc_lattice(muB) label = r'$ \mu_B = $' + str(muB) + ' GeV' elif (muBoT): cond = xtemp <= Tc_lattice_muBoT(muB) label = r'$ \mu_B/T = $' + str(muB) dict_plots[quant][1].plot(xtemp[cond], yval[quant][cond], color=color, linewidth='2.5', label=label) # plot HRG EoS with lighter line above Tc dict_plots[quant][1].plot(xtemp, yval[quant], color=color, linewidth='2.5', alpha=0.5) dict_plots[quant][1].legend(bbox_to_anchor=(0.05, 0.75), title='PHSD HRG', title_fontsize='25', loc='center left', borderaxespad=0., frameon=False) # output data if (args.output): # output data for each \mu_B or \mu_B/T if (not muBoT): filename = f"{dir_path}/plot_HRG/HRG_muB{int(muB*10):02d}_{EoS}{species_out}.dat" elif (muBoT): filename = f"{dir_path}/plot_HRG/HRG_muBoT{muB}_{EoS}{species_out}.dat" with open(filename, 'w') as outfile: outfile.write(",".join(yval.keys())) outfile.write('\n') for i, _ in enumerate(yval['T']): outfile.write(",".join( map(lambda x: f"{yval[x][i]:5.3E}", yval.keys()))) outfile.write('\n') def getmax(): """ get max value of lattice data in the range T < Tmax """ ymin = np.zeros(len(list_quant)) ymax = np.zeros(len(list_quant)) for muB, _ in tab: if (not muBoT): xmuB = muB elif (muBoT): xmuB = muB * xtemp # fixed value of muB/T if (EoS == 'muB'): paramdata = param(xtemp, xmuB, 0., 0.) elif (EoS == 'nS0'): paramdata = EoS_nS0(param, xtemp, xmuB) for iq, quant in enumerate(list_quant): dlQCD = paramdata[quant] if (dlQCD.max() > ymax[iq]): ymax[iq] = dlQCD.max() if (dlQCD.min() < ymax[iq]): ymin[iq] = dlQCD.min() return dict(zip(list_quant, ymin * 1.1)), dict(zip(list_quant, ymax * 1.1)) min_val, max_val = getmax() # for each plot, adapt range in (x,y) and export for quant in list_quant: if (EoS == 'nS0' and quant == 'n_S'): continue if (min_val[quant] > 0.): min_val[quant] = 0. if (max_val[quant] < 0.): max_val[quant] = 0. dict_plots[quant][1].set_xlim(Tmin, Tmax) dict_plots[quant][1].set_ylim(min_val[quant], max_val[quant]) if (not muBoT): dict_plots[quant][0].savefig( f"{dir_path}/plot_HRG/HRG_{quant}_T_muB_{EoS}{species_out}.png" ) elif (muBoT): dict_plots[quant][0].savefig( f"{dir_path}/plot_HRG/HRG_{quant}_T_muBoT_{EoS}{species_out}.png" ) dict_plots[quant][0].clf() pl.close(dict_plots[quant][0]) def plot_species(EoS, muB): """ Plot the contribution to the HRG EoS from different individual particles """ # quantities to plot list_quant = ['P'] #,'n_B','s','e','n'] # initialize plot plots = np.array( [pl.subplots(figsize=(10, 7)) for x in np.arange(len(list_quant))]) f = plots[:, 0] ax = plots[:, 1] # initialize values of T to evaluate xtemp = np.linspace(Tmin, Tmax, args.Npoints) if (not muBoT): xmuB = muB elif (muBoT): xmuB = muB * xtemp # fixed value of muB/T # EoS including all particles if (EoS != 'nS0'): yval = HRG(xtemp, xmuB, 0., 0., species='all', offshell=args.offshell) else: yval = EoS_nS0(HRG, xtemp, xmuB, species='all', offshell=args.offshell) # keep the values of muQ and muS as a function of T for later list_muQ = yval['muQ'] list_muS = yval['muS'] for ipl, quant in enumerate(list_quant): ax[ipl].plot(xtemp, yval[quant], linewidth='2.5', color='k', label='all particles') ax[ipl].legend(title='PHSD HRG', title_fontsize='20', loc='best', borderaxespad=0., frameon=False) list_part = ['pi+', 'pi-', 'K+', 'K-', 'p', 'n', 'p~', 'n~'] line = '-' for part in list_part: if (EoS != 'nS0'): yval = HRG(xtemp, xmuB, 0., 0., species=part, offshell=args.offshell) else: # use stored values of muQ and muS as a function of T yval = HRG(xtemp, xmuB, list_muQ, list_muS, species=part, offshell=args.offshell) for ipl, quant in enumerate(list_quant): ax[ipl].plot(xtemp, yval[quant], line, linewidth='2.5', label=r'$' + latex(part) + '$') ax[ipl].legend(title='PHSD HRG', title_fontsize='20', loc='best', borderaxespad=0., frameon=False) if (line == '-'): line = '--' elif (line == '--'): line = '-' # for each plot, adapt range in (x,y) and export for ipl, quant in enumerate(list_quant): if (quant == 'n_B' or quant == 's' or quant == 'n'): ylabel = '$' + quant + '/T^3$' else: ylabel = '$' + quant + '/T^4$' if (EoS != 'nS0'): if (not muBoT): title = f'$\mu_B =$ {muB} GeV; $\mu_Q = \mu_S = 0$' elif (muBoT): title = f'$\mu_B/T =$ {muB}; $\mu_Q = \mu_S = 0$' else: if (not muBoT): title = rf'$\mu_B =$ {muB} GeV; $\langle n_S \rangle = 0$ & $\langle n_Q \rangle = 0.4 \langle n_B \rangle$' elif (muBoT): title = rf'$\mu_B/T =$ {muB}; $\langle n_S \rangle = 0$ & $\langle n_Q \rangle = 0.4 \langle n_B \rangle$' ax[ipl].set(xlabel='T [GeV]', ylabel=ylabel, title=title) ax[ipl].set_xlim(Tmin, Tmax) ax[ipl].set_yscale('log') if (not muBoT): f[ipl].savefig( f"{dir_path}/plot_HRG/HRG_{quant}_T_muB{int(muB*10):02d}_{EoS}_species.png" ) elif (muBoT): f[ipl].savefig( f"{dir_path}/plot_HRG/HRG_{quant}_T_muBoT{muB}_{EoS}_species.png" ) f[ipl].clf() pl.close(f[ipl]) # call function plot_species here for muB, _ in tab: if (not muBoT): print(' species - muB = ', muB, ' GeV') elif (muBoT): print(' species - muB/T = ', muB) plot_species(EoS, muB)
def plot_species(EoS, muB): """ Plot the contribution to the HRG EoS from different individual particles """ # quantities to plot list_quant = ['P'] #,'n_B','s','e','n'] # initialize plot plots = np.array( [pl.subplots(figsize=(10, 7)) for x in np.arange(len(list_quant))]) f = plots[:, 0] ax = plots[:, 1] # initialize values of T to evaluate xtemp = np.linspace(Tmin, Tmax, args.Npoints) if (not muBoT): xmuB = muB elif (muBoT): xmuB = muB * xtemp # fixed value of muB/T # EoS including all particles if (EoS != 'nS0'): yval = HRG(xtemp, xmuB, 0., 0., species='all', offshell=args.offshell) else: yval = EoS_nS0(HRG, xtemp, xmuB, species='all', offshell=args.offshell) # keep the values of muQ and muS as a function of T for later list_muQ = yval['muQ'] list_muS = yval['muS'] for ipl, quant in enumerate(list_quant): ax[ipl].plot(xtemp, yval[quant], linewidth='2.5', color='k', label='all particles') ax[ipl].legend(title='PHSD HRG', title_fontsize='20', loc='best', borderaxespad=0., frameon=False) list_part = ['pi+', 'pi-', 'K+', 'K-', 'p', 'n', 'p~', 'n~'] line = '-' for part in list_part: if (EoS != 'nS0'): yval = HRG(xtemp, xmuB, 0., 0., species=part, offshell=args.offshell) else: # use stored values of muQ and muS as a function of T yval = HRG(xtemp, xmuB, list_muQ, list_muS, species=part, offshell=args.offshell) for ipl, quant in enumerate(list_quant): ax[ipl].plot(xtemp, yval[quant], line, linewidth='2.5', label=r'$' + latex(part) + '$') ax[ipl].legend(title='PHSD HRG', title_fontsize='20', loc='best', borderaxespad=0., frameon=False) if (line == '-'): line = '--' elif (line == '--'): line = '-' # for each plot, adapt range in (x,y) and export for ipl, quant in enumerate(list_quant): if (quant == 'n_B' or quant == 's' or quant == 'n'): ylabel = '$' + quant + '/T^3$' else: ylabel = '$' + quant + '/T^4$' if (EoS != 'nS0'): if (not muBoT): title = f'$\mu_B =$ {muB} GeV; $\mu_Q = \mu_S = 0$' elif (muBoT): title = f'$\mu_B/T =$ {muB}; $\mu_Q = \mu_S = 0$' else: if (not muBoT): title = rf'$\mu_B =$ {muB} GeV; $\langle n_S \rangle = 0$ & $\langle n_Q \rangle = 0.4 \langle n_B \rangle$' elif (muBoT): title = rf'$\mu_B/T =$ {muB}; $\langle n_S \rangle = 0$ & $\langle n_Q \rangle = 0.4 \langle n_B \rangle$' ax[ipl].set(xlabel='T [GeV]', ylabel=ylabel, title=title) ax[ipl].set_xlim(Tmin, Tmax) ax[ipl].set_yscale('log') if (not muBoT): f[ipl].savefig( f"{dir_path}/plot_HRG/HRG_{quant}_T_muB{int(muB*10):02d}_{EoS}_species.png" ) elif (muBoT): f[ipl].savefig( f"{dir_path}/plot_HRG/HRG_{quant}_T_muBoT{muB}_{EoS}_species.png" ) f[ipl].clf() pl.close(f[ipl])