def FastScatLimit(exp, x_in, Lim_in, Del_in, Del, geff, optype="V"): ##### Assuming the splitting to be irrelevant --> upscattering easy to get using the beam energy gu, gd, ge, gs = Fillg(geff) if Del > 0.5: print( "Warning: recasting of scattering limits only implemented for small or zero splitting" ) M2tildeToM1 = (1 + 1 / (1 + Del)) / (2 + Del_in) xProd_DPtmp, NProd_DP = br.NProd_DP(exp) xProd_DP = xProd_DPtmp / 1.0 # Switching to zero splitting to avoid problems at the resonance mymin = np.min(xProd_DP) / M2tildeToM1 # Sending M1 to M2tilde mymax = np.max(xProd_DP) / M2tildeToM1 xi = uf.log_sample(mymin, mymax, 200) Lam1TeV = np.full(np.size(xi), 1000) xProd_new, Prod_new = br.NProd(Del, exp, geff, optype) Nnew = np.interp(xi, xProd_new * (1 + Del), Prod_new) xProd, NProd = br.NProd(Del, exp, geff, optype) xi, ratio, LimDP = uf.GetRatio(NProd, xProd, NProd_DP, xProd_DP, Lim_in, x_in) gscat = (np.abs(gu) + np.abs(gd)) EffLim = 0.013 * np.sqrt(xi) / np.sqrt(LimDP) * np.power( ratio, 1 / 8.) * 1000 * np.sqrt(gscat) return xi, EffLim
def FastMonoJet(exp, g_in, Lim_Up_in, Delini, Del, geff, optype="V"): gu, gd, ge, gs = Fillg(geff) xi_basic = uf.log_sample(0.005, 5, 200) gef = np.sqrt(2 * gu**2 + gd**2) if gef < np.min(g_in): Lim_u_out = 0 else: Lim_u_out = np.interp(gef, g_in, Lim_Up_in) Lim_full = np.full(200, Lim_u_out) return xi_basic, Lim_full
def Stretchfeff(xi, feffin, DelProd, Del): thrup = br.MPi / 3 xlow = np.min(xi[feffin > 0]) fefftoStretch = feffin[np.logical_and(xi < thrup, xi > 1.2 * xlow)] xefftoStretch = xi[np.logical_and(xi < thrup, xi > 1.2 * xlow)] xilow = xi[xi < thrup] xiInterp = uf.log_sample(2 * me / Del * (2 + Del), thrup, len(xefftoStretch)) # print(xiInterp) fEffinM2Fit = np.interp(xilow, xiInterp, fefftoStretch) res = feffin # print(res[np.logical_and(xi>2*me/Del*(2+Del),xi<thrup)]) res[np.logical_and(xi > 2 * me / Del * (2 + Del), xi < thrup)] = fEffinM2Fit[np.logical_and( xilow > 2 * me / Del * (2 + Del), xilow < thrup)] return res
def DetEff(xNProd, NProd, xlim, Lamlim, Del, DelIni, exp, geff, optype="V"): # First we get the production ratio # We need to shift the masses, making sure that the invariant mass is equal: M1 + M2 = M1tilde + M2tilde M2tildeToM1 = (1 + 1 / (1 + Del)) / (2 + DelIni) # print("Production function: ", xNProd,NProd) M1ToX = (2 + DelIni) xmin = np.min(xNProd) * M1ToX # Sending M1 to X=M1+M2 xmax = np.max(xNProd) * M1ToX xi = uf.log_sample(xmin, xmax, 200) Lam1TeV = np.full(np.size(xi), 1000) # print("Limlim: ", Lamlim[xlim*M1ToX>0.01]) LamliminX = np.interp(xi, xlim * M1ToX, Lamlim) # print("Laimlim: ", LamliminX[xi>0.01]) NprodinX = np.interp(xi, xNProd * M1ToX, NProd) GammaDecayinX = am.GamHDSee(xi / M1ToX, DelIni, Lam1TeV, geff, optype) Res = np.power(LamliminX, 8) / NprodinX / GammaDecayinX # if exp=="faser" :print("Res: ", np.nan_to_num(Res)*(GammaDecayinX>0)) return xi, np.nan_to_num(Res) * (GammaDecayinX > 0 ) # We output as fnction of M1+M2
def GetNaiveDecayLimits(Del, exp, Nexp, geff, optype="V", HeavyMeson=False): Dexp, Lexp, beamtype = GeomForExp(exp) xNProd, NProd = br.NProd(Del, exp, geff, optype, HeavyMeson) MinvFac = (1 + Del) mymin = np.min(xNProd * MinvFac) mymax = np.max(xNProd * MinvFac) xi = uf.log_sample(mymin, mymax, 500) Lam1TeV = np.full(np.size(xi), 1000.) ctaugamma = 1 / am.GamHDSll(xi / (1 + Del), Del, Lam1TeV, geff, optype) * BoostFact( xi, Del, beamtype) * (3e8 * 6.5875e-25) Limnew = 1000 * np.power( np.interp(xi, xNProd * MinvFac, NProd) * Lexp / ctaugamma / Nexp, 1 / 8.) if (beamtype == "LHC"): Limnew = ReduceLimLHC(Limnew) return xi / ( 1 + Del ), Limnew # We need to export as M1 to match with the other imported limits
def FastSN1987Limit(limlist, Del, geff, optype="V", upperlimit=True): xi_basic = uf.log_sample(0.005, 0.3, 400) ##### Currently just test the different operator and apply a naive proton scattering scaling, except from the AV case where the upper limit derives from the pi0 branching ratio gu, gd, ge, gs = Fillg(geff) M2tildeToM1 = (1 + 1 / (1 + Del)) / (2) ### Change for scaling dle=0 initially x_in, Lim_in = limlist[optype] if upperlimit: if optype == "V": Lim_out = Lim_in * np.sqrt((np.abs(gu) + np.abs(gd) + np.abs(ge)) / 2) # Scaling based on e+e- annihilation return xi_basic, np.interp( xi_basic, x_in / M2tildeToM1, Lim_out ) # we include the possibility of production from electrons just incase -- very rough else: # Gam1=br.GamAV_MestoXX(x_inAV,x_inAV*(1),br.MPi,1,1000.) # Gam2=br.GamAV_MestoXX(x_inAV,x_inAV*(1+Del),br.MPi,1,1000.) xf = x_in / M2tildeToM1 Gam1 = am.GamAV_MestoXX(x_in, x_in * (1 + 0), br.MPi, 1, 1000.) Gam2 = am.GamAV_MestoXX(xf / (1 + Del), xf, br.MPi, 1, 1000.) Lim_out = Lim_in * np.power(gu - gd, 1 / 2.) * np.power( Gam2 / Gam1, 1 / 8.) # Limits from invisible pi0 decay # print("x for SN, ",M2tildeToM1, x_inAV , Lim_inAV, Lim_out) return xi_basic, np.interp(xi_basic, x_in / M2tildeToM1, Lim_out) else: if optype == "V": Lim_out = Lim_in * np.sqrt( (np.abs(gu) + np.abs(gd) )) # we include the possibility of scattering from nuclei return xi_basic, np.interp(xi_basic, x_in / M2tildeToM1, Lim_out) else: Lim_out = Lim_in * np.sqrt((np.abs(gu) + np.abs(gd))) return xi_basic, np.interp( xi_basic, x_in / M2tildeToM1, Lim_out ) # we include the possibility of scattering from nuclei
def ShiftLimDel(xNProd, NProd, xlim, Lamlim, Del, DelIni, exp, optypetmp, gefftmp, HeavyMeson=False): # We need to shift the masses, making sure that the invariant mass is equal: M1 + M2 = M1tilde + M2tilde M2tildeToM1 = (1 + 1 / (1 + Del)) / (2 + DelIni) mymin = np.min(xNProd) / M2tildeToM1 # Sending M1 to M2tilde mymax = np.max(xNProd) / M2tildeToM1 xi = uf.log_sample(mymin, mymax, 200) Lam1TeV = np.full(np.size(xi), 1000) # We tolerate having both operator type for this function (along with both effective coupling), still a bit experimental though # The first operator is the one use for production, then the other are used to get the decay if np.size(optypetmp) > 1: if np.size(gefftmp) < 2: print( "Please provide the effective couplings for each type of effective operators (Vector or Axial-Vector" ) return LamLim1 * 0. geffandOp = tuple(zip(gefftmp, optypetmp)) GamNew = sum( am.GamHDSll(xi / (1 + Del), Del, Lam1TeV, ge, op) for (ge, op) in geffandOp) geff = gefftmp[0] optype = optypetmp[0] else: geff = gefftmp optype = optypetmp GamNew = am.GamHDSll(xi / (1 + Del), Del, Lam1TeV, geff, optype) # First we get the production ratio xProd_new, Prod_new = br.NProd(Del, exp, geff, optype, HeavyMeson) Nnew = np.interp( xi, xProd_new * (1 + Del), Prod_new ) ## Getting the Production for M2 and new splitting with the same invariant mass M1 + M2 as the original splitting # if (exp == "faser") : print("Faser prod " , xi , Nnew[xi>0.01]) ####### ----- Recasting the detection rate M12ToM2 = (1 + Del) / (Del + 2) # print("Effective Gamma",xi,GamNew) xM1M2, fEffinX = DetEff(xNProd, NProd, xlim, Lamlim, Del, DelIni, exp, geff, optype) # fEffinM2=np.interp(xi, xM1M2*M12ToM2, fEffinX) # We stretch the function to account from the fact that the width of chi2 has a smaller lower bound (due to ee threshold) if Del > DelIni: fEfftmp = Stretchfeff(xi, fEffinX, DelIni, Del) fEffFinal = np.interp(xi, xi * M12ToM2, fEfftmp) else: fEffFinal = np.interp(xi, xM1M2 * M12ToM2, fEffinX) # if (exp == "faser") : print("Faser xlow" , np.min(xi[fEffFinal>0])) # if (exp == "faser") : print("Faser xlow2 " , np.min(xi[fEffinM2>0])) # if (exp == "faser") : print("Faser fEffFinal" , fEffFinal[xi>0.006]) Limnew = np.power(fEffFinal * GamNew * Nnew, 1 / 8.) ####### ---------- If the limits is decreased due to EFT limitation at LHC if (exp == "faser") or (exp == "mathusla"): # print("Limit second method ",xi, Limnew) Limnew = ReduceLimLHC(Limnew) # print("Limit second method ",xi, Limnew) return xi, Limnew # We output as function of M2
def FastCRLimit(exp, Del, geff, optype="AV", SaveToFile=True, ReadFromFile=False, filename='Output/Lim_superK_cosmicrays.dat'): if ReadFromFile: filedat = np.loadtxt(filename) mxdellist, LimLowList, LimHighList = filedat return mxdellist, LimLowList, LimHighList Nt2klim = 70 #note mx is the *light* chi mass. heavy chi mass = mx*(1+Del) minmx = me * 2.001 / (Del) * 4 # maxmx = 0.5*br.MEta/(1+Del) - 0.001 #production threshold for *heavy* chi mass at half the meson mass minus twice electron mass maxmx = br.MEta * 0.999 / (2 + Del) numpts = 20 # mxlist = np.linspace(minmx, maxmx, numpts) mxlist = uf.log_sample(minmx, maxmx, numpts) res = 40 #resolution in GeV for finding limit lamlist = uf.log_sample(5, 1000, res) LimLowList = [] LimHighList = [] for mx in mxlist: #find Lambda corresponding to Nt2klim number of events. Note that there are in general two solutions. limlow = -1 limhigh = -1 for Lam in lamlist: #range(5, 1000, res): Nprod = Ndecayperyear_cosmicrays(Lam, mx, mx * (1 + Del), Del, geff, optype, "t2k") print("--------------") print("Lambda = ", Lam) print("Nprod = ", Nprod) print("--------------") #passed first threshold for lower limit if Nprod >= Nt2klim and limlow == -1: limlow = Lam #0.5*((Lam-res) + Lam) #passed second threshold for upper limit elif Nprod <= Nt2klim and limhigh == -1 and limlow != -1: limhigh = Lam #0.5*((Lam-res) + Lam) break LimLowList.append(limlow) LimHighList.append(limhigh) if limlow == -1 and limhigh == -1: # Limits typically stop after this point, no need to take time searching break print("**************************************************") print(LimLowList) print(LimHighList) print("**************************************************") LimLowList = np.array(LimLowList) LimHighList = np.array(LimHighList) #for testing ''' mxlist = np.array([0.00449904, 0.00759627, 0.01282572, 0.02165522, 0.03656314, 0.061734, 0.10423302, 0.17598929, 0.29714414, 0.50170463]) LimLowList = np.array([ 5, 5, 5, 25, 25, 45, 85, -1, -1, -1]) LimHighList = np.array([ 25, 45, 85, 125, 205, 325, 445, -1, -1, -1]) ''' #remove trailing -1's from list LimLowList, LimHighList = np.transpose([ (low, high) for (low, high) in zip(LimLowList, LimHighList) if low != -1 and high != -1 ]) mxlist = mxlist[:-(len(mxlist) - len(LimLowList))] #replicate last entry identically (with small x-axis offset) for uf.CombineUpDown to include last point correctly, #otherwise it joins up last entry of lowlist with second-last entry of highlist mxlist = np.append(mxlist, mxlist[len(mxlist) - 1] + 0.000001) LimLowList = np.append(LimLowList, LimLowList[len(LimLowList) - 1]) LimHighList = np.append(LimHighList, LimHighList[len(LimHighList) - 1]) #save to file for saving time when plotting if SaveToFile: np.savetxt(filename, (mxlist * (1 + Del), LimLowList, LimHighList)) return mxlist * (1 + Del), LimLowList, LimHighList