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