<jupyter_start><jupyter_text>ngl\_resum: a package to resum non-global logarithms at leading logarithmic accuracy If you use the package ngl\_resum, please cite [doi:10.1007/JHEP09(2020)029](https://inspirehep.net/literature/1798660). In this documentation we show some features of ngl\_resum. In particular, we want to visit each of the classes defined in the module and explain their main purposes. We suggest this notebook to be used in Binder:[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/MarcelBalsiger/ngl_resum/master?filepath=%2Fdocs%2Fnglresum.ipynb)  To have this example working as a jupyter notebook, one needs to have the packages numpy, physt and - obviously - ngl\_resum installed. The easiest way to do this is to use pip install ngl_resum. Details may be found here: [https://packaging.python.org/tutorials/installing-packages/use-pip-for-installing](https://packaging.python.org/tutorials/installing-packages/use-pip-for-installing)  Imports We start by importing the package ngl\_resum and numpy:<jupyter_code>import ngl_resum as ngl
import numpy as np<jupyter_output><empty_output><jupyter_text>FourVector We start with the FourVector class. As its name suggests, this class is used to describe fourvectors and contains some information specifically used in collider physics. To instantiate a FourVector, we have to feed all four components of it. Let us define fvA with energy $e$ energyFvA and momenta $p_i$ iMomFvA:<jupyter_code>energyFvA=6.9
xMomFvA=4.2
yMomFvA=3.5
zMomFvA=1.2
fvA=ngl.FourVector(energyFvA,xMomFvA,yMomFvA,zMomFvA)
print(fvA)<jupyter_output>[6.9,4.2,3.5,1.2]<jupyter_text>Attributes Of course, we can access the four individual coordinates:<jupyter_code>print("energy: ",fvA.e,"\nx-mom.: ",fvA.px,\
              "\ny-mom.: ",fvA.py,"\nz-mom.: ",fvA.pz)<jupyter_output>energy:  6.9 
x-mom.:  4.2 
y-mom.:  3.5 
z-mom.:  1.2<jupyter_text>In some cases, it may become useful to have the momentum vector displayed as a numpy-array:<jupyter_code>print("numpy-array: ",fvA.vec)<jupyter_output>numpy-array:  [6.9 4.2 3.5 1.2]<jupyter_text>The two angles $\theta$ (angle between the z-axis or beam-axis)and $\phi$ (angle in the x-y-plane) of the three-vector are attributes:<jupyter_code>print("theta: ",fvA.theta,"\nphi: ",fvA.phi)<jupyter_output>theta:  1.3547308176908472 
phi:  0.6947382761967031<jupyter_text>We can also access the mass and the velocity of the particle:<jupyter_code>print("mass: ",fvA.m,"\nvelocity: ",fvA.beta)<jupyter_output>mass:  4.034848200366403 
velocity:  0.811205911255451<jupyter_text>The length of the spatial vector $\sqrt{p_x^2+p_y^2+p_z^2}$ can also be accessed:<jupyter_code>print("np.sqrt(px*px+py*py+pz*pz): ",fvA.absSpace)<jupyter_output>np.sqrt(px*px+py*py+pz*pz):  5.597320787662612<jupyter_text>Collider-physics specific attributes are the transverse energy $E_T$, the transverse momentum $p_T$, the rapidity $y=\frac{1}{2}\ln\frac{e+p_z}{e-p_z}$ and the pseudorapidity $\eta=-\ln\left(\tan\frac{\theta}{2}\right)$:<jupyter_code>print("transverse energy: ",fvA.eT,"\ntransverse momentum: ",fvA.pT,\
              "\nrapidity: ",fvA.rap,"\npseudorapidity: ",fvA.pseudorap)<jupyter_output>transverse energy:  6.7395647606579505 
transverse momentum:  5.4671747731346585 
rapidity:  0.1756989434189443 
pseudorapidity:  0.2177665445807071<jupyter_text>Operations Let us instantiate another FourVector:<jupyter_code>fvB=ngl.FourVector(5,4,3,0)<jupyter_output><empty_output><jupyter_text>We can now add and subtract the fourvectors with the usual operators:<jupyter_code>print("fvA+fvB:",fvA+fvB,"\nfvA-fvB: ",fvA-fvB)<jupyter_output>fvA+fvB: [11.9,8.2,6.5,1.2] 
fvA-fvB:  [1.9000000000000004,0.20000000000000018,0.5,1.2]<jupyter_text>The multiplication operator $*$ can be used to give a scalar product of two fourvectors or as the multiplication of a scalar. Note that we use te mostly-minus metric (+ - - -):<jupyter_code>print("fvA*fvB:",fvA*fvB,"\n10*fvA: ",10*fvA)<jupyter_output>fvA*fvB: 7.199999999999999 
10*fvA:  [69.0,42.0,35.0,12.0]<jupyter_text>The division by a scalar does work, too:<jupyter_code>print("fvA/10:",fvA/10)<jupyter_output>fvA/10: [0.6900000000000001,0.42000000000000004,0.35,0.12]<jupyter_text>Methods To measure the squared angular distance $\Delta R^2=\Delta\phi^2+\Delta\eta^2=|\phi_A-\phi_B|^2+|\eta_A-\eta_B|^2$ between two fourvectors, we can use<jupyter_code>print("deltaR^2:",fvA.R2(fvB)," or ",fvB.R2(fvA))<jupyter_output>deltaR^2: 0.050047515262147006  or  0.050047515262147006<jupyter_text>while the cosine of the spatial angle between two fourvectors can be accessed by<jupyter_code>print("cosTheta:",fvA.costheta(fvB)," or ",fvB.costheta(fvA))<jupyter_output>cosTheta: 0.9754666932856004  or  0.9754666932856004<jupyter_text>We can check, whether the FourVector is massive or massless. A FourVector a is treated as massive (or time-like), if a*a is larger than $10^{-7}$, otherwise it is treated as massless (or light-like):<jupyter_code>fvA.isMassive()
fvA.isMassless()
fvB.isMassive()
fvB.isMassless()<jupyter_output><empty_output><jupyter_text>We can check whether two FourVectors are the same. We consider two FourVectors a and b to be the same, if (a-b).e^2+(a-b).px^2+(a-b).py^2+(a-b).pz^2 is smaller than $10^{-10}$ to account for rounding errors. To apply this check, we can use<jupyter_code>fvA.isSame(fvB)
fvA.isSame(fvA+ngl.FourVector(0.000000001,0.000000001,0.000000001,0))<jupyter_output><empty_output><jupyter_text>One last method of the  FourVector class is going to be the tensor product fvA$_\mu$fvB$_\nu$ (which is probably not going to be used that often), given by<jupyter_code>print("fvA.metric.fvB:\n",fvA.tensorProd(fvB))<jupyter_output>fvA.metric.fvB:
 [[ 34.5 -27.6 -20.7  -0. ]
 [ 21.  -16.8 -12.6  -0. ]
 [ 17.5 -14.  -10.5  -0. ]
 [  6.   -4.8  -3.6  -0. ]]<jupyter_text>Boost The Boost class takes care of the boosting procedure as described in Section 3.1 [doi:10.1007/JHEP09(2020)029](https://inspirehep.net/literature/1798660). It takes two arbitrary momentum vectors from the lab frame and creates the boost from lab frame to the frame where these two fourvectors are back-to-back alongside the z-axis. For transparency we take the first two vectors of the event from Appendix A of above article, as given in (A.3):<jupyter_code>p1=ngl.FourVector(504.7,125.6,82.44,-450.4)
u1=p1/p1.e
u2=ngl.FourVector(1,0,0,-1)<jupyter_output><empty_output><jupyter_text>Now to get the boost accounting for the boost from the lab frame to the frame where u1 is alongside the positive z-axis and u2 alongside the negative z-axis, we just have to instantiate a Boost with the two fourvectors as arguments:<jupyter_code>bst=ngl.Boost(u1,u2)<jupyter_output><empty_output><jupyter_text>Attributes We have access to each single transformation $X$, $B$ and $Z$ as defined in Section 3.1 and thoroughly explained with an example in the pages after (A.3) of [doi:10.1007/JHEP09(2020)029](https://inspirehep.net/literature/1798660). Note, that each of these matrices are 4x4 numpy arrays. We start out with the boost $X$, which is a rotation that puts the added two initial fourvectors along the x-axis:<jupyter_code>np.dot(bst.X,(u1+u2).vec)<jupyter_output><empty_output><jupyter_text>Now we apply the boost $B$, which removes the spatial component of above fourvector:<jupyter_code>np.dot(bst.B,np.dot(bst.X,(u1+u2).vec))<jupyter_output><empty_output><jupyter_text>Finally, we apply a second rotation $Z$ that puts the two initial vectors alongside the z-axis, with u1 in the positive direction:<jupyter_code>np.dot(bst.Z,np.dot(bst.B,np.dot(bst.X,u1.vec)))
np.dot(bst.Z,np.dot(bst.B,np.dot(bst.X,u2.vec)))<jupyter_output><empty_output><jupyter_text>As we can see, the two fourvectors are now nicely aligned back-to-back. The full boost is also stored in an attribute:<jupyter_code>u1prime=np.dot(bst.LABtoCMS,u1.vec)
u1prime<jupyter_output><empty_output><jupyter_text>The same goes for the inverse boost:<jupyter_code>u1BoostedBack=np.dot(bst.CMStoLAB,u1prime)
u1BoostedBack
u1BoostedBack-u1.vec<jupyter_output><empty_output><jupyter_text>Methods Of course, the numpy arrays are not easy to handle as we have to keep in mind which variable is a FourVector and which one a numpy array. We have a shortcut to erase this problem. We can take the boost and apply it on any FourVector and get back the FourVector in the new frame as follows:<jupyter_code>fvu1prime=bst.boostLABtoCMS(u1)
fvu1prime
fvu1BoostedBack=bst.boostCMStoLAB(fvu1prime)
fvu1BoostedBack
fvu1BoostedBack.isSame(u1)<jupyter_output><empty_output><jupyter_text>Hist The Hist class acts as an adapter to the physt package and is immensly based on it. It accounts for the histograms $R(t)$ that is the result of the resummation (see (4.3) of [doi:10.1007/JHEP09(2020)029](https://inspirehep.net/literature/1798660)). When initializing a Hist, we at least need to provide a number of bins nbins and a maximal value for $t$, tmax:<jupyter_code>nbins=10
tmax=0.1
hst=ngl.Hist(nbins, tmax)
hst<jupyter_output><empty_output><jupyter_text>Another possibility is to also calculate an error estimate of each bin:<jupyter_code>hstErr=ngl.Hist(nbins,tmax,errorHistCalc=True)
hstErr<jupyter_output><empty_output><jupyter_text>Compared to the rest of the classes we switch the order and postpone the discussion of the attributes to after the methods and operations. This is due to the fact that the methods are mainly used to populate the histograms to avoid looking at a wall of zeroes in the discussion of the attributes. For the sake of streamlinedness, we will stop discussing the more involved case of the error estimation at this point. The discussion thereof is quite involved and provides little insight. We will look at the extraction of the error from the histogram later.  Methods We have two functions that can be used to set a whole histogram to zero or to one:<jupyter_code>hst.setOne()
hst
hst.setZero()
hst<jupyter_output><empty_output><jupyter_text>To populate the histogram, we can use addToBin by specifying the value of t at which we add a weight w:<jupyter_code>tVal=0.053
w=0.696969
hst.addToBin(tVal,w)
hst<jupyter_output><empty_output><jupyter_text>Operators Let us start with a disclaimer - we assume the two histograms to be initialized with the same number of bin nbins and the same maximal $t$ value tmax. If this is not the case, anything might happen. To show the use of some operators, we will populate two histograms with some random numbers:<jupyter_code>hst1=ngl.Hist(nbins, tmax)
hst2=ngl.Hist(nbins, tmax)
for i in range(0,50):
    hst1.addToBin(tmax*np.random.random_sample(),np.random.random_sample())
    hst2.addToBin(tmax*np.random.random_sample(),np.random.random_sample())<jupyter_output><empty_output><jupyter_text>Let us have a look at how these historgams are populated:<jupyter_code>hst1
hst2<jupyter_output><empty_output><jupyter_text>We can add and subtract the histograms. This sums (or subtracts) the entry of each bin:<jupyter_code>hst1+hst2
hst1-hst2<jupyter_output><empty_output><jupyter_text>The multiplication of two Hists multiplies the entry of each bin, and we can also multiply with a scalar (which multiplicates each entry by the scalar):<jupyter_code>hst1*hst2
10*hst1<jupyter_output><empty_output><jupyter_text>Division by a scalar is possible as well:<jupyter_code>hst1/10<jupyter_output><empty_output><jupyter_text>Attributes Let us finally look at how to access the data in Hist. The Histograms certainly knows about the number of bins and maximal $t$:<jupyter_code>print("nbins: ",hst1.nbins,"\ntmax: ",hst1.tmax)<jupyter_output>nbins:  10 
tmax:  0.1<jupyter_text>To access the entries of the histogram, we can do so:<jupyter_code>hst1.entries<jupyter_output><empty_output><jupyter_text>The bin values can be read out as well. As it is sometimes useful to have the lower or upper bin boundary or the central value, we have created access to all of them:<jupyter_code>print("central bin values: ",hst1.centerBinValue)
print("lower bin boundary: ",hst1.lowerBinBoundary)
print("upper bin boundary: ",hst1.upperBinBoundary)<jupyter_output>central bin values:  [0.005, 0.015, 0.025, 0.035, 0.045, 0.055, 0.065, 0.07500000000000001, 0.08499999999999999, 0.095]
lower bin boundary:  [0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09]
upper bin boundary:  [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1]<jupyter_text>Now let us have a quick glance at the error estimations of the bins. Note, that while the representation of the histogram comes with the error itself, due to intricacies of the error computation when multiplying histograms (which is used in the showering procedure), we are only able to access the squared of the error estimate. To illustrate this, let us first populize the hstErr from above and show its representation:<jupyter_code>for i in range(0,50):
    hstErr.addToBin(tmax*np.random.random_sample(),np.random.random_sample())
hstErr<jupyter_output><empty_output><jupyter_text>Now we will access the squared error of the bins:<jupyter_code>hstErr.squaredError<jupyter_output><empty_output><jupyter_text>This might seem a little bit odd. For more insight into the error handling we refer to the documentation of the example codes.   Event Now we get to the core of the package. An instance of Event contains all the reevant information to start one showering. We will not go too deep into details here, but mainly refer to the two example codes. To instantiate an Event, one can either feed it * a dipole (using the feedDipole parameter), or* an pylhe.LHEEvent read-in via pylhe.readLHE (using the eventFromFile parameter)To each of those we have an example code.While we will not explain every attribute and method of this class in detail, we still want to give an overview of some intricacies.  First of all we want to discuss the feedDipole feature. Via Event(feedDipole=dipole) we can set up the showering of one single dipole, consisting of two fourVectors in an array:<jupyter_code>leg1=ngl.FourVector(1,0,0,0.5)
leg2=ngl.FourVector(1,0,0,-0.5)
dipole=[leg1,leg2]
evDip=ngl.Event(feedDipole=dipole)<jupyter_output><empty_output><jupyter_text>This feature is straightforward enough. We have set up this dipole for showering. The other feature of Event is the more intricate showering of an event read in from a .lhe-file. After feeding the pylhe.LHEEvent into the parameter eventFromFile we have two additional options, namely* whether we want to form the color-connected dipoles between the incoming and outgoing particles of the event or between the incoming and intermediate ones, and* whether we also want to account for the decay dipoles between the intermediate and the outgoing particles.An example where we shower both the dipoles formed by the incoming-intermediate particles and the intermediate-outgoing particles is given for example in Section 5 of [doi:10.1007/JHEP09(2020)029](https://inspirehep.net/literature/1798660) . To keep this documentation simple, we will not actually read in a .lhe-file, and therefore can not go hands-on here. If you want to play around with this feature, we suggest you move to the example code. To instantiate an Event by an event=pylhe.LHEEvent, we have to use evLHE=ngl.Event(eventFromFile="pylhe.LHEEvent").It sets up the Event with the default case of color-sorting the incoming and outgoing particles. To set up the showering of the incoming-intermediate particle dipoles, we have to use evLHE=ngl.Event(eventFromFile=pylhe.LHEEvent, productionDipoles='intermediate'),and if we not only want to have the production dipoles showered, but also the dipoles associated to the decay, we have to use evLHE=ngl.Event(eventFromFile=pylhe.LHEEvent,productionDipoles='intermediate',decayDipoles=True). Note that you will probably seldom use these additional features and most oftenly only have to use evLHE=ngl.Event(eventFromFile="pylhe.LHEEvent"), except if you work with top quarks. Note that if you instantiate an Event using a pylhe.LHEEvent, you have access to the weight as well as an array of the FourVector of each kind of particles in the form of an attribute. You can access the weight of the event via ev=ngl.Event(eventFromFile="pylhe.LHEEvent")ev.weightIf you want the fourvectors of all incoming up-type quarks and antiquarks, you can access them via  ev=ngl.Event(eventFromFile="pylhe.LHEEvent")ev.incomingUp.In the same way, you can access ev.statusType, with status being incoming,intermediate or outgoing, and Type being Down, Up, Strange, Charm, Bottom, Top, Electron, ENeutrino, Muon, MNeutrino, Tau, TNeutrino, Gluon, Photon, ZBoson, WBoson or Higgs.  OutsideRegion Just as the Event class, OutsideRegion is very specific to the observable you are considering. Its whole purpose is to tell the Shower, whether a FourVector is pointing into the region where it gets vetoed. The nomenclature of **outside** comes from the textbook example of the interjet energy flow, where radiation that is not inside the jets gets vetoed.  We can initiate the OutsideRegion with or without an Event. Whether you should feed it an Event or not depends on whether the region where you want to  veto radiation depends on the distribution of the outgoing particles. We have one example code each for the usage with and without an Event. An instance of OutsideRegion doesn't do anything. It containes the stub of a method outside(self,v) which needs to be implemented by you. To do so, you need to write a method<jupyter_code>def _outside(self,v):
    #
    # Code that checks whether v is a FourVector
    # landing outside. 
    #
    retVal=True  # if v outside
    retVal=False # if v not outside
    return (retVal)<jupyter_output><empty_output><jupyter_text>and - after creating an instance of OutsideRegion<jupyter_code>outsideRegion=ngl.OutsideRegion()<jupyter_output><empty_output><jupyter_text>exchange the stub of outside(self,v) of your instance outsideRegion to your method by invoking<jupyter_code>outsideRegion.outside = _outside.__get__(outsideRegion,ngl.OutsideRegion)<jupyter_output><empty_output><jupyter_text>For more details we refer to the two example codes which show the handling of the OutsideRegion class.  Shower Let us now get to the core of our resummation precedure, the showering of an Event. To instantiate a Shower, we feed it the following parameters (most of which come with a default choice):<jupyter_code># event: Event,
# outsideRegion: OutsideRegion,
# nsh: int=50, 
# nbins: int=100, 
# tmax: float=0.1, 
# cut: float=5.0, 
# fixedOrderExpansion: bool=True,
# virtualSubtracted: bool=False<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09406392694063927, 'fraction_numerical': 0.045769540692989524, 'mean_word_length': 5.749818709209572, 'pattern_counts': {'":': 0, '<': 162, '<?xml version=': 0, '>': 162, 'https://': 9, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 10, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '44', 'dir': 'jupyter-structured-clean-dedup', 'id': '62178', 'license': 'MIT', 'n_tokens_mistral': 6911, 'n_tokens_neox': 6198, 'n_words': 2082, 'path': 'docs/nglresum.ipynb', 'repo_name': 'MarcelBalsiger/ngl_resum', 'size': '18615'}