~madteam/mg5amcnlo/series2.0

« back to all changes in this revision

Viewing changes to madgraph/various/lhe_parser.py

  • Committer: olivier Mattelaer
  • Date: 2016-05-12 11:00:18 UTC
  • mfrom: (262.1.150 2.3.4)
  • Revision ID: olivier.mattelaer@uclouvain.be-20160512110018-sevb79f0wm4g8mpp
pass to 2.4.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
149
149
    """A class to allow to read both gzip and not gzip file"""
150
150
 
151
151
    def __new__(self, path, mode='r', *args, **opt):
152
 
 
153
 
        if  path.endswith(".gz"):
 
152
        
 
153
        if not path.endswith(".gz"):
 
154
            return file.__new__(EventFileNoGzip, path, mode, *args, **opt)
 
155
        elif mode == 'r' and not os.path.exists(path) and os.path.exists(path[:-3]):
 
156
            return EventFile.__new__(EventFileNoGzip, path[:-3], mode, *args, **opt)
 
157
        else:
154
158
            try:
155
159
                return gzip.GzipFile.__new__(EventFileGzip, path, mode, *args, **opt)
156
160
            except IOError, error:
159
163
                if mode == 'r':
160
164
                    misc.gunzip(path)
161
165
                return file.__new__(EventFileNoGzip, path[:-3], mode, *args, **opt)
162
 
        else:
163
 
            return file.__new__(EventFileNoGzip, path, mode, *args, **opt)
164
 
    
 
166
 
 
167
 
165
168
    def __init__(self, path, mode='r', *args, **opt):
166
169
        """open file and read the banner [if in read mode]"""
167
170
        
168
 
        super(EventFile, self).__init__(path, mode, *args, **opt)
 
171
        try:
 
172
            super(EventFile, self).__init__(path, mode, *args, **opt)
 
173
        except IOError:
 
174
            if '.gz' in path and isinstance(self, EventFileNoGzip) and\
 
175
                mode == 'r' and os.path.exists(path[:-3]):
 
176
                super(EventFile, self).__init__(path[:-3], mode, *args, **opt)
 
177
                
169
178
        self.banner = ''
170
179
        if mode == 'r':
171
180
            line = ''
466
475
    def apply_fct_on_event(self, *fcts, **opts):
467
476
        """ apply one or more fct on all event. """
468
477
        
469
 
        opt= {"print_step": 2000, "maxevent":float("inf")}
 
478
        opt= {"print_step": 2000, "maxevent":float("inf"),'no_output':False}
470
479
        opt.update(opts)
471
480
        
472
481
        nb_fct = len(fcts)
483
492
                else:
484
493
                    logger.info("currently at %s event" % nb_event)
485
494
            for i in range(nb_fct):
486
 
                out[i].append(fcts[i](event))
 
495
                if opts['no_output']:
 
496
                    fcts[i](event)
 
497
                else:
 
498
                    out[i].append(fcts[i](event))
487
499
            if nb_event > opt['maxevent']:
488
500
                break
489
501
        if nb_fct == 1:
492
504
            return out
493
505
 
494
506
    
 
507
    def update_Hwu(self, hwu, fct, name='lhe', keep_wgt=True):
 
508
        
 
509
        first=True
 
510
        def add_to_Hwu(event):
 
511
            """function to update the HwU on the flight"""
 
512
 
 
513
            value = fct(event)
 
514
            
 
515
            # initialise the curve for the first call
 
516
            if first:
 
517
                # register the variables
 
518
                if isinstance(value, dict):
 
519
                    hwu.add_line(value.keys())
 
520
                else:
 
521
                    hwu.add_line(name)
 
522
                    if keep_wgt is True:
 
523
                        hwu.add_line(['%s_%s' % (name, key)
 
524
                                                for key in event.reweight_data])
 
525
                first = False
 
526
            # Fill the histograms
 
527
            if isinstance(value, dict):
 
528
                hwu.addEvent(value)
 
529
            else:
 
530
                hwu.addEvent({name:value})
 
531
                if keep_wgt:
 
532
                    event.parse_reweight()
 
533
                    if keep_wgt is True:
 
534
                        data = dict(('%s_%s' % (name, key),value)
 
535
                                                for key in event.reweight_data)
 
536
                        hwu.addEvent(data)
 
537
    
 
538
        
 
539
        
 
540
        self.apply_fct_on_event(add_to_Hwu, no_output=True)
 
541
        return hwu
 
542
    
495
543
    def create_syscalc_data(self, out_path, pythia_input=None):
496
544
        """take the lhe file and add the matchscale from the pythia_input file"""
497
545
        
791
839
        #define the weighting such that we have built-in the scaling
792
840
        
793
841
        if 'event_target' in opts and opts['event_target']:
794
 
            misc.sprint(opts['event_target'])
795
842
            new_wgt = sum(self.across)/opts['event_target']
796
843
            self.define_init_banner(new_wgt)
797
844
            self.written_weight = new_wgt
818
865
        self.aqcd = 0
819
866
        # Weight information
820
867
        self.tag = ''
 
868
        self.eventflag = {} # for information in <event > 
821
869
        self.comment = ''
822
870
        self.reweight_data = {}
823
871
        self.matched_scale_data = None
839
887
                self.comment += '%s\n' % line
840
888
                continue
841
889
            if "<event" in line:
 
890
                if '=' in line:
 
891
                    found = re.findall(r"""(\w*)=(?:(?:['"])([^'"]*)(?=['"])|(\S*))""",line)
 
892
                    #for '<event line=4 value=\'3\' error="5" test=" 1 and 2">\n'
 
893
                    #return [('line', '', '4'), ('value', '3', ''), ('error', '5', ''), ('test', ' 1 and 2', '')]
 
894
                    self.eventflag = dict((n, a1) if a1 else (n,a2) for n,a1,a2 in found)
 
895
                    # return {'test': ' 1 and 2', 'line': '4', 'value': '3', 'error': '5'}
842
896
                continue
843
897
            
844
898
            if 'first' == status:
946
1000
            self.tag = self.tag[:start] + self.tag[stop+7:]
947
1001
        return self.reweight_data
948
1002
    
 
1003
    def parse_nlo_weight(self):
 
1004
        """ """
 
1005
        if hasattr(self, 'nloweight'):
 
1006
            return self.nloweight
 
1007
        
 
1008
        start, stop = self.tag.find('<mgrwgt>'), self.tag.find('</mgrwgt>')
 
1009
        if start != -1 != stop :
 
1010
        
 
1011
            text = self.tag[start+8:stop]
 
1012
            self.nloweight = NLO_PARTIALWEIGHT(text, self)
 
1013
        
 
1014
            
949
1015
    def parse_matching_scale(self):
950
1016
        """Parse the line containing the starting scale for the shower"""
951
1017
        
1388
1454
    def __str__(self, event_id=''):
1389
1455
        """return a correctly formatted LHE event"""
1390
1456
                
1391
 
        out="""<event%(event_id)s>
 
1457
        out="""<event%(event_flag)s>
1392
1458
%(scale)s
1393
1459
%(particles)s
1394
1460
%(comments)s
1397
1463
</event>
1398
1464
""" 
1399
1465
        if event_id not in ['', None]:
1400
 
            event_id = ' event=%s' % event_id
1401
 
            
 
1466
            self.eventflag['event'] = str(event_id)
 
1467
 
 
1468
        if self.eventflag:
 
1469
            event_flag = ' %s' % ' '.join('%s="%s"' % (k,v) for (k,v) in self.eventflag.items())
 
1470
        else:
 
1471
            event_flag = ''
 
1472
 
1402
1473
        if self.nexternal:
1403
1474
            scale_str = "%2d %6d %+13.7e %14.8e %14.8e %14.8e" % \
1404
1475
            (self.nexternal,self.ievent,self.wgt,self.scale,self.aqed,self.aqcd)
1443
1514
            sys_str += "</mgrwt>\n"
1444
1515
            reweight_str = sys_str + reweight_str
1445
1516
        
1446
 
        out = out % {'event_id': event_id,
 
1517
        out = out % {'event_flag': event_flag,
1447
1518
                     'scale': scale_str, 
1448
1519
                      'particles': '\n'.join([str(p) for p in self]),
1449
1520
                      'tag': tag_str,
1579
1650
            py = obj.py
1580
1651
            pz = obj.pz
1581
1652
            E = obj.E
 
1653
        elif isinstance(obj, list):
 
1654
            assert len(obj) ==4
 
1655
            E = obj[0]
 
1656
            px = obj[1]
 
1657
            py = obj[2] 
 
1658
            pz = obj[3]
 
1659
        elif  isinstance(obj, str):
 
1660
            obj = [float(i) for i in obj.split()]
 
1661
            assert len(obj) ==4
 
1662
            E = obj[0]
 
1663
            px = obj[1]
 
1664
            py = obj[2] 
 
1665
            pz = obj[3]            
1582
1666
        else:
1583
1667
            E =obj
1584
1668
 
1585
1669
            
1586
 
        self.E = E
1587
 
        self.px = px
1588
 
        self.py = py
1589
 
        self.pz =pz
 
1670
        self.E = float(E)
 
1671
        self.px = float(px)
 
1672
        self.py = float(py)
 
1673
        self.pz = float(pz)
1590
1674
 
1591
1675
    @property
1592
1676
    def mass(self):
1593
1677
        """return the mass"""    
1594
1678
        return math.sqrt(self.E**2 - self.px**2 - self.py**2 - self.pz**2)
1595
1679
 
 
1680
    @property
1596
1681
    def mass_sqr(self):
1597
1682
        """return the mass square"""    
1598
1683
        return self.E**2 - self.px**2 - self.py**2 - self.pz**2
1627
1712
        self.py += obj.py
1628
1713
        self.pz += obj.pz
1629
1714
        return self
 
1715
 
 
1716
    def __sub__(self, obj):
 
1717
        
 
1718
        assert isinstance(obj, FourMomentum)
 
1719
        new = FourMomentum(self.E-obj.E,
 
1720
                           self.px - obj.px,
 
1721
                           self.py - obj.py,
 
1722
                           self.pz - obj.pz)
 
1723
        return new
 
1724
 
 
1725
    def __isub__(self, obj):
 
1726
        """update the object with the sum"""
 
1727
        self.E -= obj.E
 
1728
        self.px -= obj.px
 
1729
        self.py -= obj.py
 
1730
        self.pz -= obj.pz
 
1731
        return self
 
1732
    
 
1733
    def __mul__(self, obj):
 
1734
        if isinstance(obj, FourMomentum):
 
1735
            return self.E*obj.E - self.px *obj.px - self.py * obj.py - self.pz * obj.pz
 
1736
        elif isinstance(obj, (float, int)):
 
1737
            return FourMomentum(obj*self.E,obj*self.px,obj*self.py,obj*self.pz )
 
1738
        else:
 
1739
            raise NotImplemented
 
1740
    __rmul__ = __mul__
1630
1741
    
1631
1742
    def __pow__(self, power):
1632
1743
        assert power in [1,2]
1635
1746
            return FourMomentum(self)
1636
1747
        elif power == 2:
1637
1748
            return self.mass_sqr()
1638
 
        
 
1749
    
 
1750
    def __repr__(self):
 
1751
        return 'FourMomentum(%s,%s,%s,%s)' % (self.E, self.px, self.py,self.pz)
 
1752
    
1639
1753
    def boost(self, mom):
1640
1754
        """mom 4-momenta is suppose to be given in the rest frame of this 4-momenta.
1641
1755
        the output is the 4-momenta in the frame of this 4-momenta
1655
1769
            return FourMomentum(mom)
1656
1770
                
1657
1771
 
 
1772
class OneNLOWeight(object):
 
1773
        
 
1774
    def __init__(self, input):
 
1775
        """ """
 
1776
 
 
1777
        if isinstance(input, str):
 
1778
            self.parse(input)
 
1779
        
 
1780
    def parse(self, text):
 
1781
        """parse the line and create the related object"""
 
1782
        #0.546601845792D+00 0.000000000000D+00 0.000000000000D+00 0.119210435309D+02 0.000000000000D+00  5 -1 2 -11 12 21 0 0.24546101D-01 0.15706890D-02 0.12586055D+04 0.12586055D+04 0.12586055D+04  1  2  2  2  5  2  2 0.539995789976D+04
 
1783
        # below comment are from Rik description email
 
1784
        
 
1785
        data = text.split()
 
1786
        # 1. The first three doubles are, as before, the 'wgt', i.e., the overall event of this
 
1787
        # contribution, and the ones multiplying the log[mu_R/QES] and the log[mu_F/QES]
 
1788
        # stripped of alpha_s and the PDFs.
 
1789
        self.pwgt = [float(f) for f in data[:3]]
 
1790
        # 2. The next two doubles are the values of the (corresponding) Born and 
 
1791
        #    real-emission matrix elements. You can either use these values to check 
 
1792
        #    that the newly computed original matrix element weights are correct, 
 
1793
        #    or directly use these so that you don't have to recompute the original weights. 
 
1794
        #    For contributions for which the real-emission matrix elements were 
 
1795
        #    not computed, the 2nd of these numbers is zero. The opposite is not true, 
 
1796
        #    because each real-emission phase-space configuration has an underlying Born one 
 
1797
        #    (this is not unique, but on our code we made a specific choice here). 
 
1798
        #    This latter information is useful if the real-emission matrix elements 
 
1799
        #    are unstable; you can then reweight with the Born instead. 
 
1800
        #    (see also point 9 below, where the momentum configurations are assigned). 
 
1801
        #    I don't think this instability is real problem when reweighting the real-emission 
 
1802
        #    with tree-level matrix elements (as we generally would do), but is important 
 
1803
        #    when reweighting with loop-squared contributions as we have been doing for gg->H. 
 
1804
        #    (I'm not sure that reweighting tree-level with loop^2 is something that 
 
1805
        #    we can do in general, because we don't really know what to do with the 
 
1806
        #    virtual matrix elements because we cannot generate 2-loop diagrams.)
 
1807
        self.born = float(data[3])
 
1808
        self.real = float(data[4])
 
1809
        # 3. integer: number of external particles of the real-emission configuration  (as before)
 
1810
        self.nexternal = int(data[5])
 
1811
        # 4. PDG codes corresponding to the real-emission configuration (as before)
 
1812
        self.pdgs = [int(i) for i in data[6:6+self.nexternal]]
 
1813
        flag = 6+self.nexternal # new starting point for the position
 
1814
        # 5. next integer is the power of g_strong in the matrix elements (as before)
 
1815
        self.qcdpower = int(data[flag])
 
1816
        # 6. 2 doubles: The bjorken x's used for this contribution (as before)
 
1817
        self.bjks = [float(f) for f in data[flag+1:flag+3]]
 
1818
        # 7. 3 doubles: The Ellis-sexton scale, the renormalisation scale and the factorisation scale, all squared, used for this contribution (as before)
 
1819
        self.scales2 = [float(f) for f in data[flag+3:flag+6]]
 
1820
        # 8.the value of g_strong
 
1821
        self.gs = float(data[flag+6])
 
1822
        # 9. 2 integers: the corresponding Born and real-emission type kinematics. (in the list of momenta)
 
1823
        #    Note that also the Born-kinematics has n+1 particles, with, in general, 
 
1824
        #    one particle with zero momentum (this is not ALWAYS the case, 
 
1825
        #    there could also be 2 particles with perfectly collinear momentum). 
 
1826
        #    To convert this from n+1 to a n particles, you have to sum the momenta 
 
1827
        #    of the two particles that 'merge', see point 12 below.
 
1828
        self.born_related = int(data[flag+7])
 
1829
        self.real_related = int(data[flag+8])
 
1830
        # 10. 1 integer: the 'type'. This is the information you should use to determine 
 
1831
        #     if to reweight with Born, virtual or real-emission matrix elements. 
 
1832
        #     (Apart from the possible problems with complicated real-emission matrix elements
 
1833
        #     that need to be computed very close to the soft/collinear limits, see point 2 above. 
 
1834
        #     I guess that for tree-level this is always okay, but when reweighting 
 
1835
        #     a tree-level contribution with a one-loop squared one, as we do 
 
1836
        #     for gg->Higgs, this is important). 
 
1837
        #     type=1 : real-emission:     
 
1838
        #     type=2 : Born: 
 
1839
        #     type=3 : integrated counter terms: 
 
1840
        #     type=4 : soft counter-term            : 
 
1841
        #     type=5 : collinear counter-term     : 
 
1842
        #     type=6 : soft-collinear counter-term: 
 
1843
        #     type=7 : O(alphaS) expansion of Sudakov factor for NNLL+NLO :  
 
1844
        #     type=8 : soft counter-term (with n+1-body kin.):     
 
1845
        #     type=9 : collinear counter-term (with n+1-body kin.): 
 
1846
        #     type=10: soft-collinear counter-term (with n+1-body kin.): 
 
1847
        #     type=11: real-emission (with n-body kin.): 
 
1848
        #     type=12: MC subtraction with n-body kin.: 
 
1849
        #     type=13: MC subtraction with n+1-body kin.: 
 
1850
        #     type=14: virtual corrections minus approximate virtual
 
1851
        #     type=15: approximate virtual corrections: 
 
1852
        self.type = int(data[flag+9])
 
1853
        # 11. 1 integer: The FKS configuration for this contribution (not really 
 
1854
        #     relevant for anything, but is used in checking the reweighting to 
 
1855
        #     get scale & PDF uncertainties). 
 
1856
        self.nfks = int(data[flag+10])
 
1857
        # 12. 2 integers: the two particles that should be merged to form the 
 
1858
        #     born contribution from the real-emission one. Remove these two particles
 
1859
        #     from the (ordered) list of PDG codes, and insert a newly created particle
 
1860
        #     at the location of the minimum of the two particles removed. 
 
1861
        #     I.e., if you merge particles 2 and 4, you have to insert the new particle 
 
1862
        #     as the 2nd particle. And particle 5 and above will be shifted down by one.
 
1863
        self.to_merge_pdg = [int (f) for f in data[flag+11:flag+13]]
 
1864
        # 13. 1 integer: the PDG code of the particle that is created after merging the two particles at point 12.
 
1865
        self.merge_new_pdg = int(data[flag+13])
 
1866
        # 14. 1 double: the reference number that one should be able to reconstruct 
 
1867
        #     form the weights (point 1 above) and the rest of the information of this line. 
 
1868
        #     This is really the contribution to this event as computed by the code 
 
1869
        #     (and is passed to the integrator). It contains everything. 
 
1870
        self.ref_wgt = float(data[flag+14])
 
1871
 
 
1872
        #check the momenta configuration linked to the event
 
1873
        if self.type in [1,11]:
 
1874
            self.momenta_config = self.real_related
 
1875
        else:
 
1876
            self.momenta_config = self.born_related
 
1877
 
 
1878
 
 
1879
class NLO_PARTIALWEIGHT(object):
 
1880
 
 
1881
    class BasicEvent(list):
 
1882
        
 
1883
        def __init__(self, momenta, wgts, event):
 
1884
            list.__init__(self, momenta)
 
1885
            
 
1886
            assert self
 
1887
            self.wgts = wgts
 
1888
            self.pdgs = list(wgts[0].pdgs)
 
1889
            self.event = event
 
1890
            
 
1891
            if wgts[0].momenta_config == wgts[0].born_related:
 
1892
                # need to remove one momenta.
 
1893
                ind1, ind2 = [ind-1 for ind in wgts[0].to_merge_pdg] 
 
1894
                if ind1> ind2: 
 
1895
                    ind1, ind2 = ind2, ind1
 
1896
                if ind1 >= sum(1 for p in event if p.status==-1):
 
1897
                    new_p = self[ind1] + self[ind2]
 
1898
                else:
 
1899
                    new_p = self[ind1] - self[ind2]
 
1900
                self.pop(ind1) 
 
1901
                self.insert(ind1, new_p)
 
1902
                self.pop(ind2)
 
1903
                self.pdgs.pop(ind1) 
 
1904
                self.pdgs.insert(ind1, wgts[0].merge_new_pdg )
 
1905
                self.pdgs.pop(ind2)                 
 
1906
                # DO NOT update the pdgs of the partial weight!
 
1907
            elif any(w.type in [1,11] for w in wgts):
 
1908
                if any(w.type not in [1,11] for w in wgts):
 
1909
                    raise Exception
 
1910
                # check if this is too soft/colinear if so use the born
 
1911
                ind1, ind2 = [ind-1 for ind in wgts[0].to_merge_pdg] 
 
1912
                if ind1> ind2: 
 
1913
                    ind1, ind2 = ind2, ind1                
 
1914
                if ind1 >= sum(1 for p in event if p.status==-1):
 
1915
                    new_p = self[ind1] + self[ind2]
 
1916
                else:
 
1917
                    new_p = self[ind1] - self[ind2]
 
1918
 
 
1919
                if __debug__:
 
1920
                    ptot = FourMomentum()
 
1921
                    for i in xrange(len(self)):
 
1922
                        if i <2:
 
1923
                            ptot += self[i]
 
1924
                        else:
 
1925
                            ptot -= self[i]
 
1926
                    if ptot.mass_sqr > 1e-16:
 
1927
                        misc.sprint(ptot, ptot.mass_sqr)
 
1928
                
 
1929
                inv_mass = new_p.mass_sqr
 
1930
                shat = (self[0]+self[1]).mass_sqr
 
1931
                if (abs(inv_mass)/shat < 1e-6):
 
1932
#                    misc.sprint(abs(inv_mass)/shat)
 
1933
                    self.pop(ind1) 
 
1934
                    self.insert(ind1, new_p)
 
1935
                    self.pop(ind2)
 
1936
                    self.pdgs.pop(ind1) 
 
1937
                    self.pdgs.insert(ind1, wgts[0].merge_new_pdg )
 
1938
                    self.pdgs.pop(ind2)                 
 
1939
                    # DO NOT update the pdgs of the partial weight!                    
 
1940
                
 
1941
                if __debug__:
 
1942
                    ptot = FourMomentum()
 
1943
                    for i in xrange(len(self)):
 
1944
                        if i <2:
 
1945
                            ptot += self[i]
 
1946
                        else:
 
1947
                            ptot -= self[i]
 
1948
                    if ptot.mass_sqr > 1e-16:
 
1949
                        misc.sprint(ptot, ptot.mass_sqr)
 
1950
#                            raise Exception
 
1951
 
 
1952
        def get_pdg_code(self):
 
1953
            return self.pdgs
 
1954
        
 
1955
        def get_tag_and_order(self):
 
1956
            """ return the tag and order for this basic event""" 
 
1957
            (initial, _), _ = self.event.get_tag_and_order()
 
1958
            order = self.get_pdg_code()
 
1959
            
 
1960
            
 
1961
            initial, out = order[:len(initial)], order[len(initial):]
 
1962
            initial.sort()
 
1963
            out.sort()
 
1964
            return (tuple(initial), tuple(out)), order
 
1965
        
 
1966
        def get_momenta(self, get_order, allow_reversed=True):
 
1967
            """return the momenta vector in the order asked for"""
 
1968
        
 
1969
            #avoid to modify the input
 
1970
            order = [list(get_order[0]), list(get_order[1])] 
 
1971
            out = [''] *(len(order[0])+len(order[1]))
 
1972
            pdgs = self.get_pdg_code()
 
1973
            for pos, part in enumerate(self):
 
1974
                if pos < len(get_order[0]): #initial
 
1975
                    try:
 
1976
                        ind = order[0].index(pdgs[pos])
 
1977
                    except ValueError, error:
 
1978
                        if not allow_reversed:
 
1979
                            raise error
 
1980
                        else:
 
1981
                            order = [[-i for i in get_order[0]],[-i for i in get_order[1]]]
 
1982
                            try:
 
1983
                                return self.get_momenta(order, False)
 
1984
                            except ValueError:
 
1985
                                raise error   
 
1986
                            
 
1987
                                                 
 
1988
                    position =  ind
 
1989
                    order[0][ind] = 0             
 
1990
                else: #final   
 
1991
                    try:
 
1992
                        ind = order[1].index(pdgs[pos])
 
1993
                    except ValueError, error:
 
1994
                        if not allow_reversed:
 
1995
                            raise error
 
1996
                        else:
 
1997
                            order = [[-i for i in get_order[0]],[-i for i in get_order[1]]]
 
1998
                            try:
 
1999
                                return self.get_momenta(order, False)
 
2000
                            except ValueError:
 
2001
                                raise error     
 
2002
                    position = len(order[0]) + ind
 
2003
                    order[1][ind] = 0   
 
2004
    
 
2005
                out[position] = (part.E, part.px, part.py, part.pz)
 
2006
                
 
2007
            return out
 
2008
            
 
2009
            
 
2010
        def get_helicity(self, *args):
 
2011
            return [9] * len(self)
 
2012
        
 
2013
        @property
 
2014
        def aqcd(self):
 
2015
            return self.event.aqcd
 
2016
            
 
2017
    def __init__(self, input, event):
 
2018
        
 
2019
        self.event = event
 
2020
        if isinstance(input, str):
 
2021
            self.parse(input)
 
2022
            
 
2023
        
 
2024
    def parse(self, text):
 
2025
        """create the object from the string information (see example below)"""
 
2026
#0.2344688900d+00    8    2    0
 
2027
#0.4676614699d+02 0.0000000000d+00 0.0000000000d+00 0.4676614699d+02
 
2028
#0.4676614699d+02 0.0000000000d+00 0.0000000000d+00 -.4676614699d+02
 
2029
#0.4676614699d+02 0.2256794794d+02 0.4332148227d+01 0.4073073437d+02
 
2030
#0.4676614699d+02 -.2256794794d+02 -.4332148227d+01 -.4073073437d+02
 
2031
#0.0000000000d+00 -.0000000000d+00 -.0000000000d+00 -.0000000000d+00
 
2032
#0.4780341163d+02 0.0000000000d+00 0.0000000000d+00 0.4780341163d+02
 
2033
#0.4822581633d+02 0.0000000000d+00 0.0000000000d+00 -.4822581633d+02
 
2034
#0.4729127470d+02 0.2347155377d+02 0.5153455534d+01 0.4073073437d+02
 
2035
#0.4627255267d+02 -.2167412893d+02 -.3519736379d+01 -.4073073437d+02
 
2036
#0.2465400591d+01 -.1797424844d+01 -.1633719155d+01 -.4224046944d+00
 
2037
#0.473706252575d-01 0.000000000000d+00 0.000000000000d+00  5 -3 3 -11 11 21 0 0.11849903d-02 0.43683926d-01 0.52807978d+03 0.52807978d+03 0.52807978d+03  1  2  1 0.106660059627d+03
 
2038
#-.101626389492d-02 0.000000000000d+00 -.181915673961d-03  5 -3 3 -11 11 21 2 0.11849903d-02 0.43683926d-01 0.52807978d+03 0.52807978d+03 0.52807978d+03  1  3  1 -.433615206719d+01
 
2039
#0.219583436285d-02 0.000000000000d+00 0.000000000000d+00  5 -3 3 -11 11 21 2 0.11849903d-02 0.43683926d-01 0.52807978d+03 0.52807978d+03 0.52807978d+03  1 15  1 0.936909375537d+01
 
2040
#0.290043597283d-03 0.000000000000d+00 0.000000000000d+00  5 -3 3 -11 11 21 2 0.12292838d-02 0.43683926d-01 0.58606724d+03 0.58606724d+03 0.58606724d+03  1 12  1 0.118841547979d+01
 
2041
#-.856330613460d-01 0.000000000000d+00 0.000000000000d+00  5 -3 3 -11 11 21 2 0.11849903d-02 0.43683926d-01 0.52807978d+03 0.52807978d+03 0.52807978d+03  1  4  1 -.365375546483d+03
 
2042
#0.854918237609d-01 0.000000000000d+00 0.000000000000d+00  5 -3 3 -11 11 21 2 0.12112732d-02 0.45047393d-01 0.58606724d+03 0.58606724d+03 0.58606724d+03  2 11  1 0.337816057347d+03
 
2043
#0.359257891118d-05 0.000000000000d+00 0.000000000000d+00  5 21 3 -11 11 3 2 0.12292838d-02 0.43683926d-01 0.58606724d+03 0.58606724d+03 0.58606724d+03  1 12  3 0.334254554762d+00
 
2044
#0.929944817736d-03 0.000000000000d+00 0.000000000000d+00  5 21 3 -11 11 3 2 0.12112732d-02 0.45047393d-01 0.58606724d+03 0.58606724d+03 0.58606724d+03  2 11  3 0.835109616010d+02
 
2045
        
 
2046
        text = text.lower().replace('d','e')
 
2047
        all_line = text.split('\n')
 
2048
        #get global information
 
2049
        first_line =''
 
2050
        while not first_line.strip():
 
2051
            first_line = all_line.pop(0)
 
2052
            
 
2053
        wgt, nb_wgt, nb_event, _ = first_line.split()
 
2054
        nb_wgt, nb_event = int(nb_wgt), int(nb_event)
 
2055
        
 
2056
        momenta = []
 
2057
        wgts = []
 
2058
        for line in all_line:
 
2059
            data = line.split()
 
2060
            if len(data) == 4:
 
2061
                p = FourMomentum(data)
 
2062
                momenta.append(p)
 
2063
            elif len(data)>0:
 
2064
                wgt = OneNLOWeight(line)
 
2065
                wgts.append(wgt)
 
2066
        
 
2067
        assert len(wgts) == int(nb_wgt)
 
2068
        
 
2069
        get_weights_for_momenta = {}
 
2070
        size_momenta = 0
 
2071
        for wgt in wgts:
 
2072
            if wgt.momenta_config in get_weights_for_momenta:
 
2073
                get_weights_for_momenta[wgt.momenta_config].append(wgt)
 
2074
            else: 
 
2075
                if size_momenta == 0: size_momenta = wgt.nexternal
 
2076
                assert size_momenta == wgt.nexternal
 
2077
                get_weights_for_momenta[wgt.momenta_config] = [wgt]
 
2078
    
 
2079
        assert sum(len(c) for c in get_weights_for_momenta.values()) == int(nb_wgt), "%s != %s" % (sum(len(c) for c in get_weights_for_momenta.values()), nb_wgt)
 
2080
    
 
2081
         
 
2082
    
 
2083
        self.cevents = []   
 
2084
        for key in range(1, nb_event+1): 
 
2085
            if key in get_weights_for_momenta:
 
2086
                wgt = get_weights_for_momenta[key]
 
2087
                evt = self.BasicEvent(momenta[:size_momenta], get_weights_for_momenta[key], self.event) 
 
2088
                self.cevents.append(evt)
 
2089
            momenta = momenta[size_momenta:]
 
2090
           
 
2091
        nb_wgt_check = 0 
 
2092
        for cevt in self.cevents:
 
2093
            nb_wgt_check += len(cevt.wgts)
 
2094
        assert nb_wgt_check == int(nb_wgt)
 
2095
            
 
2096
            
 
2097
 
1658
2098
if '__main__' == __name__:   
1659
2099
    
1660
2100
    # Example 1: adding some missing information to the event (here distance travelled)