Example #1
0
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)
Example #2
0
 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]
Example #3
0
    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))
Example #4
0
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
Example #5
0
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
Example #6
0
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)
Example #7
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])