return str(self.name)+", symbol "+self.symbol+", atomic number "+str(self.z)+", average mass "+"{:.3f}".format(self.average_mass)+", "+str(self.isotopes.size)+" isotopes."
def read_formula(formula):
pos = [i for i,e in enumerate(formula+'A') if e.isupper()]
parts = [formula[pos[j]:pos[j+1]] for j in range(len(pos)-1)]
lengths=[len([t for t in p if not t.isdigit()]) for p in parts]
symbols=[p[:l] for p,l in zip(parts,lengths)]
stoichiometrics_strings=[p[l:] for p,l in zip(parts,lengths)]
stoichiometrics_strings_final=[x if x else '1' for x in stoichiometrics_strings]
stoichiometrics=[int(s) for s in stoichiometrics_strings_final]
'''Prints a string with the hamiltonian in Latex form.'''
#return '+'.join([r'$c_{'+print_latex_coupling(c)+'}'+str(inspect.signature(self.wilson_coefficients[c]))+'\\times {\\cal O}_{'+print_latex_coupling_short(c)+'}$\n' for c in self.couplings])
prime=[]
for c in self.couplings:
if 'q' in self.arguments[c]:
prime.append("'(q)")
else:
prime.append("")
return '+'.join([r'$c_{'+print_latex_coupling(c)+'}'+str(inspect.signature(self.wilson_coefficients[c]))+'\\times {\\cal O}_{'+print_latex_coupling(c)+'}$'+pp+'\n' if ws.__name__=='<lambda>' else ws.__name__+str(inspect.signature(self.wilson_coefficients[c]))+'$\\times {\\cal O}_{'+print_latex_coupling(c)+'}$'+pp+'\n' for pp,ws,c in zip(prime,self.wilson_coefficients.values(),self.couplings)])
def print_hamiltonian(self):
'''Prints a string with the hamiltonian'''
prime=[]
for c in self.couplings:
if 'q' in self.arguments[c]:
prime.append("'(q)")
else:
prime.append("")
return '+'.join(['c_'+print_coupling(c)+str(inspect.signature(self.wilson_coefficients[c]))+'* O_'+print_coupling(c)+pp if ws.__name__=='<lambda>' else ws.__name__+str(inspect.signature(self.wilson_coefficients[c]))+'* O_'+print_coupling(c)+pp for pp,ws,c in zip(prime,self.wilson_coefficients.values(),self.couplings)])
def __init__(self,name,wilson_coefficients):
self.name=name
'''
path='WimPyDD_models/'+self.name
if not os.path.isdir(path):
p = subprocess.call("mkdir -p "+path, stdout=subprocess.PIPE, shell=True)
if not os.path.isfile(os.getcwd()+'/WimPyDD_models/__init__.py'):
self.hamiltonian='+'.join(['c_'+print_coupling(c)+'*O_'+print_coupling_short(c) for c in self.couplings])
def coeff_squared(__c1__,__c2__,**args):
input_args={}
input_args.update({k:1 for k in {e for e in self.global_arguments}-{t for t in list(args.keys())} if k not in list(self.global_default_args.keys()) and k!='mchi' and k!='delta'})
input_args.update({k:v for k,v in self.global_default_args.items() if k not in list(args.keys())})
input_args.update(args)
flatten=False
if flatten:
return np.kron([1,1],[1,1]).reshape(2,2)
global_arguments=np.unique(np.array([e for t in list(self.arguments.values()) for e in t]))
#args.update({k:1 for k in {e for e in global_arguments}-{t for t in list(args.keys())}})
#args.update({k:1 for k in {e for e in global_arguments}-{t for t in list(args.keys())}-{l for l in list(self.global_default_args.keys())}})
input_args.update({k:1 for k in {e for e in global_arguments}-{t for t in list(input_args.keys())}-{l for l in list(self.global_default_args.keys())}})
args1={}
if 'q' in self.arguments[__c1__]:
if any([s.startswith('q0') for s in self.default_args[__c1__].keys()]):
q_norm=self.default_args[__c1__][list(self.default_args[__c1__].keys())[np.where([s.startswith('q0') for s in self.default_args[__c1__].keys()])[0][0]]]
args1['q']=q_norm
else:
args1['q']=1.
for arg in set(self.arguments[__c1__])-{'q'}-{s for s in self.default_args[__c1__].keys() if s.startswith('q0')}:
args1[arg]=input_args[arg]
args2={}
if 'q' in self.arguments[__c2__]:
if any([s.startswith('q0') for s in self.default_args[__c2__].keys()]):
q_norm=self.default_args[__c2__][list(self.default_args[__c2__].keys())[np.where([s.startswith('q0') for s in self.default_args[__c2__].keys()])[0][0]]]
args2['q']=q_norm
else:
args2['q']=1.
for arg in set(self.arguments[__c2__])-{'q'}-{s for s in self.default_args[__c2__].keys() if s.startswith('q0')}:
diff_response_functions_documentation='Dictionary containing the differential response functions as defined in ...\nKeys: [model_name,spin,coupling1,coupling2,element]\nOutput: numpy array of functions with shape (2,2,n_isotopes,4).'
if isinstance(list(hamiltonian.wilson_coefficients.keys())[0],int) and j_chi>1:
print('NO RESPONSE FUNCTION LOADED')
print('Hamiltonian "'+hamiltonian.name+'" has NR couplings in the notation of PHYSICAL REVIEW C 89, 065501 (2014).\n For a WIMP spin higher that 1 use hamiltonian with couplings in all spins format (X, l,s).')
return
if isinstance(list(hamiltonian.wilson_coefficients.keys())[0],tuple):
if isinstance(list(hamiltonian.wilson_coefficients.keys())[0][0],int) and j_chi>1:
print('NO RESPONSE FUNCTION LOADED')
print('Hamiltonian "'+hamiltonian.name+'" has NR couplings in the notation of PHYSICAL REVIEW C 89, 065501 (2014).\n For a WIMP spin higher that 1 use hamiltonian with couplings in all spins format (X, l,s).')
return
j_chi_out=print_spin(j_chi)
response_functions_output=()
diff_response_functions_output={}
for __c1__,__c2__ in hamiltonian.coeff_squared_list:
#put indetation if we valid it'''Definition from Phys.Rev.C 89 (2014) 6, 065501 (e-Print: 1308.6288[hep-ph]) used\nfor nuclear W functions as default.'''
#eft_vec=np.array([np.sum(4.e0*np.pi/(2.e0*j_nucleus+1.e0)*amplitude[([inter[int] for int in interference],)]*element.func_w[isotope](q)[([inter[int] for int in interference],)]) for interference in [interference1,interference2,no_interference]])
if np.size(self.exposure)>1 and np.size(self.exposure) != len(self.data):
print('WARNING!: exposure provided as '+str(np.size(self.exposure))+'-dimensional array. If provided as array its\ndimension must be the same of the number of data points ('+str(len(self.data))+').\n All values besides the first('+str(self.exposure[0])+') ignored.')
#return "==================================================\n"+self.name+" uses "+str(self.exposure)+" kg*day of "+str(self.target.formula)+"\nAvaliable output:\nEnergy resolution function: "+self.name+".resolution(e_prime,e_ee)\nQuenching factor for each element: "+self.name+".target.element[i].quenching(er)\nEfficiency: "+self.name+".efficiency(eprime)\nData bins: "+self.name+".data\nDifferential response function for each element i and isotope j:\n"+self.name+".target.element[i].response_function[j](er,eprime)\nTables of integrated response functions Rbar vs. Er in tuple\n "+self.name+".response_functions[i_exp_point][i_element][0]-"+self.name+".response_functions[i_exp_point][i_element][i_isotope+1]\nAll indices start from 0 so first isotope correspond to 0 index etc.\n"+"===================================="
if get_short_coupling(convert_to_all_spins(n_coeff1))!=get_short_coupling(convert_to_all_spins(n_coeff2)) and not symmetric_interference(convert_to_all_spins(n_coeff1),convert_to_all_spins(n_coeff2)) and not non_symmetric_interference(convert_to_all_spins(n_coeff1),convert_to_all_spins(n_coeff2)):
# all response functions are zero. fills in a tuple with the correct stucture containing all zeros
for (string_old,string_new) in zip(info_old,info_new):
if string_old!=string_new:
n_count+=1
print(10*'-'+str(n_count)+10*'-')
print('info from response function file '+inputfile+':')
print(string_old)
print('info from '+path_exp+':')
print(string_new)
print(20*'*'+'Warning'+20*'*'+'\nThe documentation collected from the directory '+path_exp+' does not match with that contained in the file '+path+'/'+inputfile+'.\nIf any modification in the input of the '+path_exp+' directory is more recent than the file '+path+'/'+inputfile+' you need to recalculate it. If this is not the case consider to use update_exp_info to update the documentation in '+inputfile+' to stop this warning.')
#if not is_last_modified_file(path_exp,path+'/'+inputfile,exceptions=get_exceptions_to_last_modified_path_interf(n_coeff1,n_coeff2)) or time_w_func>time_response_functions:
if not last or time_w_func>time_response_functions:
#if not is_last_modified_file(path_exp,path+'/'+inputfile,exceptions=get_exceptions_to_last_modified_path_interf(n_coeff1,n_coeff2)):
if not last:
#print('in '+path_exp+' the file '+last_modified_file(path_exp,path+'/'+inputfile,exceptions=get_exceptions_to_last_modified_path_interf(n_coeff1,n_coeff2))[0]+' is more recent than '+inputfile)
print('in '+path_exp+' the file '+latest_file+' is more recent than '+inputfile)
if time_w_func>time_response_functions:
print('a custom nuclear form factor for one of the targets is more recent then '+inputfile)
print('if response functions need not to be updated use update_time_stamp=True ')
print('when calling load_response functions to avoid this warning in the future')
reply=int(delayed_input(10,'Type 1 if you want to recalculate the response functions, 2 if you want to use the response functions stored in '+path+'/'+inputfile+', 0 otherwise (default answer is 2 after 10 seconds)',2))
response_functions_tau_prime+=(primitive_table_interf(exp,tau,tau_prime,[exp.target.element[i].response_function_interf[:,:,:,n_vel] for i in range(exp.target.n_targets)],n_sampling=n_sampling,verbose=verbose),)
print('Response functions for '+exp.name+' have not been loaded and '+exp.name+'.response_functions has been set to the empty tuple (). Also the info in the file '+inputfile+' has not been updated.')
reply=int(delayed_input(10,inputfile+' not available for '+exp.name+' in '+path+'. Type 1 if you want to calculate the response functions, 0 otherwise (default answer is 1 after 10 seconds)',1))
else:
reply=int(delayed_input(10,'Response functions not available for '+inputfile+' in '+path+'. Type 1 if you want to calculate the response functions, 0 otherwise (default answer is 1 after 10 seconds)',1))
#reply=input('response functions not available for '+name+' and c'+str(n_coeff+1)+' in '+path+'. Type 1 if you want to calculate them, 0 otherwise.\n')
response_functions_tau_prime+=(primitive_table_interf(exp,tau,tau_prime,[exp.target.element[i].response_function_interf[:,:,:,n_vel] for i in range(exp.target.n_targets)],n_sampling=n_sampling,verbose=verbose),)
# puts to one or to their default values all arguments not used in the function call
input_args={}
input_args.update({k:1 for k in {e for e in hamiltonian.global_arguments}-{t for t in list(args.keys())} if k not in list(hamiltonian.global_default_args.keys()) and k!='mchi' and k!='delta'})
input_args.update({k:v for k,v in hamiltonian.global_default_args.items() if k not in list(args.keys())})
input_args.update(args)
if not flatten:
response_function_index=hamiltonian,j_chi
if response_function_index in list(exp.response_functions.keys()) and reset_response_functions:
del exp.response_functions[hamiltonian,j_chi]
coeff_squared_list=hamiltonian.coeff_squared_list
if response_function_index not in list(exp.response_functions.keys()):
if not (eft_hamiltonian.name, j_chi, __c1__, __c2__, exp.target.element[n_element].name) in exp.diff_response_functions.keys():
print('Use load_response_functions to load the differential response functions for '+exp.name+', '+eft_hamiltonian.name+' and j_chi='+str(j_chi)+' (you do not need to calculate the integrated response functions if missing)')
# puts to one or to their default values all arguments not used in the function call
input_args={}
input_args.update({key:1 for key in {e for e in hamiltonian.global_arguments}-{t for t in list(args.keys())} if key not in list(hamiltonian.global_default_args.keys()) and key!='mchi' and key!='delta'})
input_args.update({key:value for key,value in hamiltonian.global_default_args.items() if key not in list(args.keys())})
# puts to one or to their default values all arguments not used in the function call
input_args={}
input_args.update({k:1 for k in {e for e in hamiltonian.global_arguments}-{t for t in list(args.keys())} if k not in list(hamiltonian.global_default_args.keys()) and k!='mchi' and k!='delta'})
input_args.update({k:v for k,v in hamiltonian.global_default_args.items() if k not in list(args.keys())})
'''Calculates the 90%C.L. upper bound of the conventional WIMP-nucleon
cross section sigma_p=c^2*mu*2/pi (in cm^2) as a function of the
WIMP mass (in GeV) for the the halo function delta_eta calculated by
streamed_halo_function. If mchi_scale='log' a log scale is used
for mchi. If the array bin_vec=[] is
provided only the corresponding bins are used to calculate the
bound.
'''
flatten=False
if wimp_dd_rate_function is None:
wimp_dd_rate_function=wimp_dd_rate
# puts to one or to their default values all arguments not used in the function call
input_args={}
input_args.update({k:1 for k in {e for e in hamiltonian.global_arguments}-{t for t in list(args.keys())} if k not in list(hamiltonian.global_default_args.keys()) and k!='mchi' and k!='delta'})
input_args.update({k:v for k,v in hamiltonian.global_default_args.items() if k not in list(args.keys())})
print('modulation maximum phase='+str(np.degrees(lambda_0)*365/360)+' from vernal equinox, i.e. day no.'+str(80.+np.degrees(lambda_0)*365/360)+' of the year')
integral = tplquad(lambda v,cos_theta, phi: v**2*earth_velocity_distribution_spherical(v,cos_theta,phi)/(v*norm),0.,2.*np.pi,-1., cos_theta_max, v_lab, vmax)
eta0_num=np.append(eta0_num,integral[0])
s2_num=timer()
n_tot-=1
print('vmin='+str(v_lab)+' and lambda0='+str(lambda0)+' completed in '+str(s2_num-s1_num)+' sec with value '+str(integral[0])+' and error '+str(integral[1]))
+ N.B. The output matrix M of wimp_dd_matrix is always in the isospin base. The mapping in the proton-neutron base can be used after rotating M to the matrix M_pn with:
+
+ U=WD.rotation_from_isospin_to_pn(hamiltonian)
+ M_pn=np.dot(U,np.dot(M,U))
+ '''
+
+ mapping={}
+ n=0
+ for c in hamiltonian.couplings:
+ mapping[c,0]=n
+ n+=1
+ mapping[c,1]=n
+ n+=1
+ if pn:
+ mapping={((c,'p') if tau==0 else (c,'n')):v for (c,tau),v in mapping.items()}