1
2
3 import sys
4 import logging
5 import os
6 import stat
7 import re
8
9 logger = logging.getLogger('madweight.diagram_class')
10
11 try:
12 import madgraph.madweight.Cards as Cards
13 import madgraph.madweight.particle_class as particle_class
14 import madgraph.madweight.substructure_class as substructure_class
15 import madgraph.madweight.blob_solution as blob_solution
16 import madgraph.madweight.proc_info as proc_info
17 import madgraph.madweight.MW_fct as MW_fct
18 import madgraph.madweight.MW_info as MW_param
19 import madgraph.various.misc as misc
20 except ImportError:
21 import internal.madweight.Cards as Cards
22 import internal.madweight.particle_class as particle_class
23 import internal.madweight.substructure_class as substructure_class
24 import internal.madweight.blob_solution as blob_solution
25 import internal.madweight.proc_info as proc_info
26 import internal.madweight.MW_fct as MW_fct
27 import internal.madweight.MW_info as MW_param
28 import internal.misc as misc
29 Particle = particle_class.Particle
30 propagator = particle_class.propagator
31 external_part = particle_class.external_part
32 diagram = substructure_class.diagram
33 ECS_sector = substructure_class.ECS_sector
34 blob_sector = substructure_class.blob_sector
35 Block_sector = blob_solution.Block_sector
36 Decay_info = proc_info.Decay_info
37 Multi_list = MW_fct.Multi_list
38
39
40
42
43 - def __init__(self,dir_file,config,opt='default'):
56
57 - def organize_particle_content(self,param_card,tf_file):
58 """ define production area and organize all the needed information """
59
60
61 self.import_process(self.directory,self.config)
62 process_tag=re.compile(r'''P(?P<tag>\d*)''')
63 cond=process_tag.search(self.directory).group('tag')
64 ParticlesFile=''
65 if hasattr(self, 'ParticlesFile'):
66 ParticlesFile=self.ParticlesFile
67 proc_decay=Decay_info(self.directory,cond,ParticlesFile=ParticlesFile)
68 self.identify_production_area(proc_decay)
69
70
71 self.define_parameter(param_card)
72 self.define_neutrino_stuff()
73 self.define_tf_width(tf_file)
74 self.define_level()
75 self.order_in_level()
76
77
78
79
80
81
82
83
95
97 "definis le diagramme soit via un fichier, soit via un autre diagram"
98 self.config=config
99
100
101
102
103
104
105 new_config=re.compile(r'''^\s*\*\s*(?P<iconfig>\d+)\s+(?P<igraph>\d+)''')
106 propa_des=re.compile(r'''^\s+ #begin line
107 (?P<propa>-?\d+)\s+ #desintegrated propa
108 (?P<des1>-?\d+)\s+ #product of desintegration
109 (?P<des2>-?\d+)\s+ #product of desintegration 2
110 (?P<mass>\w*)\s* #text for the mass ot the part
111 (?P<width>\w*)\s* #text for the width ot the part
112 (?P<channel>[ST]*)\s* # S or T propagator
113 (?P<pid>-?\d*) # pid of the propa
114 ''',re.VERBOSE)
115
116 ff=open(dir_file+'/configs.inc','r')
117
118
119 read=0
120 while 1:
121 line=ff.readline()
122 if line=='':
123 break
124
125
126 if new_config.search(line):
127 if(new_config.search(line).group('iconfig')!=str(config)):
128 if read:
129 break
130 else:
131 continue
132 else:
133 read=1
134 elif(not read):
135 continue
136
137
138 pattern=propa_des.search(line)
139 if not pattern:
140 continue
141
142
143
144 i=int(pattern.group('propa'))
145 propa=propagator(i,pattern.group('pid'),pattern.group('channel'))
146 self.add_content(i,propa)
147 self.content[i].def_desintegation(self.content[int(pattern.group('des1'))])
148 self.content[i].def_desintegation(self.content[int(pattern.group('des2'))])
149
150
152 """define mass+width of all particle"""
153
154
155 info=MW_param.read_card(param_card)
156
157 for obj in self.content.values():
158 obj.def_mass(info)
159
161 """ assign width of the TF function for all external particle """
162
163 if not hasattr(self,'ParticlesFile'):
164 self.ParticlesFile=Cards.Particles_file('./Source/MODEL/particles.dat')
165 dico_pid_to_label=self.ParticlesFile.give_pid_to_label()
166
167 dico_type_to_tflevel={}
168 has_theta_tf, has_phi_tf = [] , []
169 ff=open(file,'r')
170
171 pattern=re.compile(r'''^\s*(?P<width>\w+)\s+(?P<type>[\w,01]*)''',re.VERBOSE)
172
173 while 1:
174 line=ff.readline()
175 if line=='':
176 break
177
178 if pattern.search(line):
179 re_obj=pattern.search(line)
180 if re_obj.group('width').lower()=='delta':
181 tf_level=0
182 elif(re_obj.group('width').lower()=='thin'):
183 tf_level=1
184 elif(re_obj.group('width').lower()=='x'):
185 self.x_constrained=int(re_obj.group('type'))
186 continue
187 elif(re_obj.group('width').lower()=='theta'):
188 list=re_obj.group('type').split(',')
189 for tag in list:
190 has_theta_tf.append(tag)
191 continue
192 elif(re_obj.group('width').lower()=='phi'):
193 list=re_obj.group('type').split(',')
194 for tag in list:
195 has_phi_tf.append(tag)
196 continue
197 else:
198 tf_level=2
199 list=re_obj.group('type').split(',')
200 for tag in list:
201 dico_type_to_tflevel[tag]=tf_level
202
203
204 for part in self.ext_content:
205 try:
206 label=dico_pid_to_label[abs(part.pid)]
207 except KeyError:
208 logger.info('particle with pdg %s has no transfer function define: Treated as missing energy' % part.pid)
209 label = None
210 if not part.neutrino:
211 if dico_type_to_tflevel.has_key(label):
212 part.tf_level=dico_type_to_tflevel[label]
213 else:
214 part.tf_level=0
215 else:
216 part.tf_level=3
217 if label in has_theta_tf:
218 part.has_theta_tf = True
219 if label in has_phi_tf:
220 part.has_phi_tf = True
221
233
234
236 "find the number of neutrino in the decay chain for each propa"
237
238
239 for Propa in self.prop_content:
240 num_neut=0
241 for j in range(0,2):
242 if Propa.des[j].external:
243 if Propa.des[j].neutrino:
244 num_neut+=1
245 else:
246 num_neut+=Propa.des[j].NeutInDecay
247 Propa.NeutInDecay=num_neut
248
250 "detect propagator decaying in fully invisible particle and treat this case"
251
252 for neut in self.neut_content:
253 if neut.twin in self.neut_content:
254
255 self.neut_content.remove(neut)
256 self.neut_content.remove(neut.twin)
257 mother = external_part(neut.mother.MG, neut.mother.pid)
258 self.neut_content.append(mother)
259
260 self.ext_content.remove(neut)
261 self.ext_content.remove(neut.twin)
262 self.ext_content.append(mother)
263
264 self.prop_content.remove(neut.mother)
265 self.num_propa-=1
266
267
268 mother.neutrino=1
269 mother.level = neut.mother.level -1
270
271 mother.tf_level=3
272
273
274
275 obj=neut
276 while 1:
277 try:
278 obj=obj.mother
279 obj.NeutInDecay+=-1
280 except:
281 break
282
283
284 self.detect_invisible_propa()
285 break
286
288 """ not use anymore: define the twin for all particle in the diagram """
289 print 'WARNING: MG_diagram.define_twin() is an old routine. This routine has not been updated since 1/2/08'
290
291 for particle in self.content.values():
292 particle.def_twin()
293
294
295
296
297
298
299
300
302 """ identify (and tag) production part from the information decay of the proc_card
303
304 Step: 1) -> find initial particle in this process (no mother or S channel with T channel mother)
305 2) -> launch (self-iterative) check for the decay
306 3) -> tag particle
307 """
308
309 init_list=[]
310 for particle in self.ext_content:
311 if particle.mother==0 or particle.mother.channel=='T':
312 init_list.append(particle)
313 for particle in self.prop_content:
314 if particle.mother==0 and particle.channel=='S':
315 init_list.append(particle)
316 elif particle.channel=='S':
317 if particle.mother.channel=='T':
318 init_list.append(particle)
319
320
321
322 init_propa=self.check_decay(init_list,proc_decay.decay_diag)
323
324 if not init_propa:
325 return
326 raise Exception('No matching between process information and feynman diagram information')
327
328
329 for particle in init_propa:
330
331 motherX=particle
332 while 1:
333 motherX=motherX.mother
334 if motherX==0:
335 break
336 motherX.channel='T'
337
338
339
341 """ check if the Particle_list is equivalent to the Decay_list if not return the list of correct Particle
342
343 Step 1) check if we have correct number of particle ->if not decay one or the other and restart
344 2) check if we have correct pid
345 3) check for decay in each case
346 """
347
348
349 if len(particle_list)<len(decay_list):
350 init_list=list(particle_list)
351 for particle in init_list:
352 particle_list=list(init_list)
353 if particle.external:
354 continue
355 particle_list.remove(particle)
356 particle_list+=particle.des
357 out=self.check_decay(particle_list,decay_list)
358 if out:
359 return out
360
361 print "no config found"
362 return 0
363
364
365
366 particle_list_pid=[]
367 decay_Mlist_pid=Multi_list()
368 decay_list_pid=[]
369 for particle in particle_list:
370 particle_list_pid.append(particle.pid)
371 for particle in decay_list:
372 decay_Mlist_pid.append(particle.pid)
373 decay_list_pid+=particle.pid
374
375
376 for pid in particle_list_pid:
377 if pid not in decay_list_pid:
378 return 0
379
380
381
382 all_order_particle=decay_Mlist_pid.give_list_possiblity(particle_list,'pid')
383 for order_particle in all_order_particle:
384 for i in range(0,len(order_particle)):
385 if not order_particle[i].external:
386 out=self.check_decay(order_particle[i].des,decay_list[i].des)
387 if not out:
388 break
389 if i==len(order_particle)-1:
390 return order_particle
391
392 print 'error 392'
393 return 0
394
395
397 """ detect the non resonant part of the diagram """
398
399
400 nb_sigma=3
401 detect=[]
402 for particle in self.prop_content:
403 if particle.channel!='S':
404 continue
405
406 value=particle.mass+nb_sigma*particle.width
407 for daughter in particle.des:
408 value-=daughter.mass-nb_sigma*daughter.width
409
410 if value<0:
411 detect.append([particle]+particle.des)
412
413
414
415 nb_sigma=3
416 nb2_sigma=5
417 unaligned=Multi_list()
418 for ambiguous_area in detect:
419 deviation={}
420 for i in range(0,len(ambiguous_area)):
421 value=0
422 if ambiguous_area[i].external:
423 continue
424
425
426 for j in range(0,len(ambiguous_area)):
427 if i!=j and j!=0:
428 value+=ambiguous_area[j].mass-nb_sigma*ambiguous_area[j].width
429 elif i!=j:
430 value-=ambiguous_area[j].mass-nb_sigma*ambiguous_area[j].width
431 else:
432 value-=ambiguous_area[j].mass
433 if ambiguous_area[i].width:
434 value=abs(value/ambiguous_area[i].width)
435 else:
436 value=1e500
437 if not deviation.has_key(value):
438 deviation[value]=[ambiguous_area[i]]
439 else:
440 deviation[value].append(ambiguous_area[i])
441
442 unaligned_here=[]
443 minimal_sigma=1e500
444 for sigma in deviation.keys():
445 if sigma<minimal_sigma:
446 minimal_sigma=sigma
447
448 if minimal_sigma<nb2_sigma:
449 gap=2
450 else:
451 gap=1
452
453 for sigma in deviation.keys():
454 if sigma<minimal_sigma+gap:
455 unaligned_here+=deviation[sigma]
456
457 unaligned.append(unaligned_here)
458
459 return unaligned
460
461
463 """ store information that this propagator cann't be generated following BW
464 restore the other propagator
465 """
466
467 for particle in self.prop_content:
468 if particle in unaligned:
469 particle.channel='S_flat'
470 elif particle.channel=='S_flat':
471 particle.channel='S'
472
473
475 """
476 supress all data from the solution in order to restart a new solution possibility
477 """
478 self.ECS_sol=[]
479 self.blob_content={}
480
481
482
483
484
485
486
503
505 """define ECS for a new (or a list) of new solution(s) for the ECS change of variable"""
506
507 if type(ECS_list)!=list:
508 ECS_list=[ECS_list]
509 self.ECS_sol+=ECS_list
510
511
512
513 for ECS in ECS_list:
514 ECS.define_blob(self)
515
516
518 """ select the best(s) ECS (minimizing unfactorized propagator \
519 define solution if define==1"""
520
521
522
523
524
525
526 authorize_ecs=list(self.opt.ecs_on)
527 if authorize_ecs.count('b') ^ authorize_ecs.count('c'):
528 if authorize_ecs.count('b'):
529 authorize_ecs.append('c')
530 else:
531 authorize_ecs.append('b')
532 if authorize_ecs.count('f'):
533 authorize_ecs.append('g')
534
535
536
537 lowest_value=100
538 for ECS in ECS_list:
539 if ECS.chgt_var not in authorize_ecs:
540 continue
541 ECS.update_unaligned_with_blob()
542 if ECS.unaligned<lowest_value:
543 lowest_value=ECS.unaligned
544
545
546 solution=[]
547 at_least_one_sol=0
548 num_sol=0
549 for ECS in ECS_list:
550 if num_sol>=self.opt.max_sol:
551 break
552 if ECS.unaligned==lowest_value:
553 ECS_equi_list=ECS.equivalent_ECS()
554 for ECS in ECS_equi_list:
555 at_least_one_sol=1
556 if define_solution:
557 num_sol+=1
558 self.define_ECS_as_solution(ECS_equi_list)
559 else:
560 num_sol+=1
561 solution+=ECS_equi_list
562
563 if not at_least_one_sol:
564 print self
565 sys.exit('FATAL ERROR: NO CHANGE OF VARIABLE\n\
566 you probably put bad option in your card')
567
568
569 if len(solution)>self.opt.max_sol:
570 solution=solution[:self.opt.max_sol]
571
572 return solution
573
574
575
577 """ find ECS containing no neutrino """
578
579
580
581
582 min_tf_level=2
583 if force==2:
584 min_tf_level=1
585 elif force==3:
586 min_tf_level=0
587
588 select=[]
589 for part in self.ext_content:
590
591 if part.tf_level>=min_tf_level:
592 select.append(part)
593 level=part.level
594
595 if len(select)<2 and force:
596 force+=force
597 if force>3:
598 sys.exit('too tiny transfer function on all particle')
599 ECS=self.find_ECS_0neut(force)
600 return ECS
601
602
603 sol=[]
604 limit=min(6+self.ext_content[0].level,10)
605 for i in range(0,len(select)):
606 part1=select[i]
607 if part1.level>limit/2+1:
608 break
609 for j in range(i+1,len(select)):
610 part2=select[j]
611 if part2.level>limit:
612 break
613
614 if part1.unaligned_propa(part2)<limit:
615 limit=part1.unaligned_propa(part2)
616 sol=[[part1,part2]]
617 elif part1.unaligned_propa(part2)==limit:
618 sol.append([part1,part2])
619
620 ECS_list=[]
621 for soluce in sol:
622 ECS_sol=ECS_sector(self,'a',soluce,limit+2)
623 ECS_list.append(ECS_sol)
624 return ECS_list
625
626
627
629 """ find the lowest(s) neutrino and define ECS """
630
631 lowest_neut=self.find_lowest_particle(lowest_level=1,out_num=1)
632 ECS_list=[]
633 for neut in lowest_neut:
634 ECS_sol=ECS_sector(self,'b',neut,neut.level-1)
635 ECS_list.append(ECS_sol)
636 return ECS_list
637
638
640 """ return best 2 neutrino ECS
641 The way to solve this problem is globally the following:
642 - A: find the two lowest neutrino
643 - B: if both are at level 1 -> return F/G depending on TF on x1,x2
644 - C: check if both have two propagator free before them -> return D
645 - D: check if they are linked by a S-channel -> return E
646 - E: else return nothing (always best in one neutrino)
647 """
648 ECS_list=[]
649
650 lowest_neutrino=self.find_lowest_particle(lowest_level=1)
651 if len(lowest_neutrino) < 2:
652 return ECS_list
653
654
655 if lowest_neutrino[0].level==1 and lowest_neutrino[1].level==1:
656
657 for i in range(0,len(lowest_neutrino)):
658 for j in range(i+1,len(lowest_neutrino)):
659 if self.x_constrained:
660 ECS_sol=ECS_sector(self,'f',[lowest_neutrino[i],lowest_neutrino[j]],2)
661 else:
662 ECS_sol=ECS_sector(self,'g',[lowest_neutrino[i],lowest_neutrino[j]],2)
663 ECS_list.append(ECS_sol)
664 return ECS_list
665
666
667 for i in range(0,len(lowest_neutrino)):
668 for j in range(i+1,len(lowest_neutrino)):
669 total_propa,free1,free2=lowest_neutrino[i].unaligned_propa(lowest_neutrino[j],0)
670 if free1>1 and free2>1:
671 ECS_sol=ECS_sector(self,'d',[lowest_neutrino[i],lowest_neutrino[j]],total_propa-4)
672 ECS_list.append(ECS_sol)
673
674 if free1!=lowest_neutrino[i].level and free2!=lowest_neutrino[j].level:
675
676 if free1 != 0 and free2 != 0:
677
678 ECS_sol=ECS_sector(self,'e',[lowest_neutrino[i],lowest_neutrino[j]],total_propa-3)
679 ECS_list.append(ECS_sol)
680
681
682 return ECS_list
683
684
685
686 - def find_lowest_particle(self,neutrino=1,lowest_level=0,out_num=2):
687 "find the one/two lowest (lower level) neutrino/particle, if the are ambiguity return more than two"
688
689 if neutrino:
690 list=self.neut_content
691 else:
692 list=self.ext_content
693
694
695 min_level1=1000
696 min_level2=1000
697 for neut in list:
698 if(neut.level>=lowest_level and neut.level<=min_level1):
699 min_level2=min_level1
700 min_level1=neut.level
701 if out_num==1:
702 min_level2=min_level1
703 elif(neut.level>=lowest_level and neut.level<min_level2):
704 min_level2=neut.level
705
706
707 lowest=[]
708 for neut in list:
709 if(neut.level>=lowest_level and neut.level<=min_level2):
710 lowest.append(neut)
711
712 return lowest
713
714
715
716
717
718
719
720
721
723 """ resolve the change of variable for blob """
724
725 for blob in self.blob_content.values():
726 blob.find_solutions()
727
729 """ store the different option linked to the generation of this MG_diagram """
730
731 self.opt=Option(info)
732
733
735 try:
736 text='structure of the configuration '+str(self.config)+':\n'
737 for i in self.ext_content:
738 text+=str(i)+'\n'
739 for i in self.prop_content:
740 text+=str(i)+'\n'
741 text+=str(len(self.ECS_sol))+' ECS(\'s) '+str(self.num_propa)+' propagator(s) '+str(self.num_neut)+' missing particles(s)\n'
742
743 text+='detail :\n'
744 for ECS in self.ECS_sol:
745 text+=str(ECS.chgt_var)+'('+str(len(ECS.blob_content))+')'+'\t'
746 text+='\n'+str(len(self.blob_content))+' blob(s) associated\n'
747 for blob in self.blob_content.values():
748 text+=str(blob)
749
750 return text
751 except:
752 text='not supportted string in not full mode: organize_particle_content is needed fisrt'
753 return text
754
755
756
758 """ return output containing the number of muon/electron/jet/bjet/invisible_part """
759
760 mu_m_list=[13]
761 el_m_list=[11]
762 ta_m_list=[15]
763 mu_p_list=[-13]
764 el_p_list=[-11]
765 ta_p_list=[-15]
766 photon_list = [22]
767
768 jet_list=[1,2,3,4,21]
769 bjet_list=[5]
770 inv_list=[12,14,16,18]
771 inv_list+=[1000012,1000014,100016,1000022,1000023,1000024,1000025,1000035,1000037]
772
773 content=[0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0]
774 list=[jet_list,bjet_list,el_m_list,el_p_list,mu_m_list,mu_p_list,ta_m_list,ta_p_list,inv_list,photon_list]
775 auth_sign=[1 ,1 ,0 ,0 ,0 ,0 ,0 ,0 ,1 ,0]
776
777 for i in range(0,len(list)):
778 for particle in self.ext_content:
779 if auth_sign[i] and abs(particle.pid) in list[i]:
780 content[i]+=1
781 elif particle.pid in list[i]:
782 content[i]+=1
783 return content
784
785
786
787
789
791 "initialize option"
792 if isinstance(info, basestring) and info!='default':
793 info=MW_param.read_card(info)
794
795 self.ecs_fuse=1
796 self.blob_fuse=1
797 self.use_sol_type_1=1
798 self.use_sol_type_2=1
799 self.use_sol_type_3=1
800 self.max_sol=10
801 self.use_ecs_a=1
802 self.use_ecs_b=1
803 self.use_ecs_c=1
804 self.use_ecs_d=1
805 self.use_ecs_e=1
806 self.use_ecs_f=1
807 self.foce_nwa = 1e-9
808
809 if info=='default':
810 return
811
812 tag_to_genvar={'1':'self.ecs_fuse',
813 '2':'self.blob_fuse',
814 '3':'self.max_sol',
815 '4':'self.use_sol_type_1',
816 '5':'self.use_sol_type_2',
817 '6':'self.use_sol_type_3',
818 '10':'self.use_ecs_a',
819 '11':'self.use_ecs_b',
820 '12':'self.use_ecs_c',
821 '13':'self.use_ecs_d',
822 '14':'self.use_ecs_e',
823 '15':'self.use_ecs_f',
824 'force_nwa':'self.force_nwa'}
825
826
827 for key, value in info['mw_gen'].items():
828 if key in tag_to_genvar:
829 exec('%s = %s' % (tag_to_genvar[key],value))
830
831 self.ecs_on=[]
832 for letter in 'abcdef':
833 cond='self.use_ecs_'+letter
834 if eval(cond):
835 self.ecs_on.append(letter)
836
837
838 self.force_nwa = max(self.force_nwa, float(info['mw_run']['nwa']))
839
840
841
842
844 text=' *** OPTION ***\n\n'
845 text='self.ecs_fuse='+str(self.ecs_fuse)+'\n'
846 text+='self.blob_fuse='+str(self.blob_fuse)+'\n'
847 text+='self.use_sol_type_1='+str(self.use_sol_type_1)+'\n'
848 text+='self.use_sol_type_2='+str(self.use_sol_type_2)+'\n'
849 text+='self.use_sol_type_3='+str(self.use_sol_type_3)+'\n'
850 text+='self.max_sol='+str(self.max_sol)+'\n'
851 text+='self.use_ecs_a='+str(self.use_ecs_a)+'\n'
852 text+='self.use_ecs_b='+str(self.use_ecs_b)+'\n'
853 text+='self.use_ecs_c='+str(self.use_ecs_c)+'\n'
854 text+='self.use_ecs_d='+str(self.use_ecs_d)+'\n'
855 text+='self.use_ecs_e='+str(self.use_ecs_e)+'\n'
856 text+='self.use_ecs_f='+str(self.use_ecs_f)+'\n'
857
858 return text
859
860
861
862 if(__name__=="__main__"):
863 """test"""
864 os.chdir('..')
865 for i in range(1,2):
866 diag=MG_diagram('./SubProcesses/MW_P_mu+mu-_mu+mu-e+e-/','param_card.dat','./Source/transfer_function/ordering_file.inc',i)
867 diag.define_Constraint_sector()
868 diag.solve_blob_sector()
869 print diag
870