Implement a Process

Normally users should not try to implement new processes internally inside Pythia, but rather use the Les Houches Accord to input your own events for processing by Pythia. However, in case you do want to implement a new process at the same level as the internal Pythia ones, here is a brief summary. For the details, of course, it always makes sense to take a similar already-implemented process as a starting point.

There are two steps involved in implementing a process:
1) implementing the matrix elements, including information on incoming and outgoing flavours and colours, and
2) making the process available to the rest of the program, so that it can be switched on by a user.
We consider these two aspects in turn.

Matrix Elements

The matrix-element information have to be stored in a matched pair of .cc and .h files, in the src and include directories, respectively. These could be one set of existing files (SigmaQCD, SigmaEW and SigmaSUSY), if it belongs there, or entirely new files, say SigmaCompositeness, where the name attaches to the scenario studied.

Header

The .h file is used to define the class for the new process. This class has to be derived either from Sigma1Process, for 2 -> 1 processes, or from Sigma2Process, for 2 -> 2 ones. (The Sigma0Process class is used for elastic, diffractive and minimum-bias events, and should not be used.) Both Sigma1Process and Sigma2Process are in their turn derived from the SigmaProcess base class.

The header information of a new class must contain a constructor and a destructor. The constructor can take arguments to allow a set of related processes to share common code. For instance, g g -> Q Qbar, Q = c or b, is only coded once, and then the constructor takes the quark code (4 or 5) as argument, to allow the proper amount of differentiation.

A few simple methods are used to encapsulate information on the particular process:
* name() returns the name of the process, as it will be shown in listings.
* code() returns an integer identifier of the process. This has no internal function, but is only intended as a service for the user to rapidly identify which process occured in a given event.
* id3Mass(), id4Mass() are the one or two final-state flavours, where masses are to be selected before the matrix elements are evaluated. Only the absolute value need to be given. For massless particles, like gluons and photons, one need not give anything, i.e. one defaults to 0. The same goes for normal light quarks, where masses presumably are not implemented in the matrix elements. Later on, these quarks can still (automatically) obtain constituent masses, once a u, d or s flavour has been selected.
* resonanceA(), resonanceB() are the codes of up to two s-channel resonances contributing to the matrix elements. These are used by the program to improve the phase-space selection efficiency, by partly sampling according to the relevant Breit-Wigner. Massless resonances need not be specified.

In addition, the initProc(), sigmaHat() and setIdColAcol() methods have to be implemented. This normally requires more code, and so the implementation belongs in the .cc file.

Initialization

initProc() at the very least has to specify the combinations of incoming partons that are allowed for the process under consideration. This is obtained by creating a pointer to an InFlux object. Currently allowed options are
* InFluxgg: g g ,
* InFluxqg: q g or qbar g,
* InFluxqqbarqqDiff: q q', q qbar' or qbar qbar', q and q' different flavours,
* InFluxqqDiff: q q' or qbar qbar', q and q' different flavours,
* InFluxqqSame: q q or qbar qbar, same flavour,
* InFluxqqbarDiff: q qbar', q and q' different flavours,
* InFluxqqbarSame: q qbar, same flavour,
* InFluxff: f f', f fbar' or fbar fbar', where f and f' may be same or different,
* InFluxffbarSame: f fbar, same flavour,
* InFluxffbarChg: f fbar', where the combination has charge +-1 (e.g. u dbar, e+ nu_e).
For a hadron beam, the two generic fermion alternatives default back to quarks only.

After the allowed channels have been set up, it is possible to specify that the channels come with a different weight, apart from the automatic convolution with the respective parton densities
* weightCharge2(): weights by the squared quark charge (also works e.g. for q g states, but not for a quark pair of different charge).
* weightCKM2(): weights by the squared of the CKM mixing-matrix element, vanishing when no such element exists.
* weightCKM2sum(int mode, int idQ): provides the CKM-related weight for a number of different situations.
1) Sum of CKM weights for transformation of an incoming flavour to an outgoing one, excluding top (used e.g. for f g -> f' W).
2) Ditto, but product of both incoming sides (not used currently).
3) Coupled CKM weights of the two sides, as consistent with t-channel W exchange (used e.g. for q_1 q_2 -> q_3 q_4).
4) Coupled CKM weights of the two sides, but on one side to specified flavour idQ (used e.g. for q_1 q_2 -> t q_3).
* weightInvCol(): factor 1/3 for an incoming qqbar pair and 1/8 for an incoming g g one.
* weightNeutrinoSpin(): multiply by factor 2 for an incoming neutrino, since they always are lefthanded and so are not averaged over incoming spin.
* weightFixed(double nowWeight): weights all channels by nowWeight.
* weightFixed(int id1, int id2, double nowWeight, bool flipSide = true, bool conjugate = true, bool allGen = true): weights by nowWeight for the specific incoming state consisting of id1 and id2. If flipside then also apply this weight when the two incoming sides are interchanged (but only once for id1 = id2, of course). If conjugate then also apply it when quarks and leptons are replaced by their antiparticles. If allGen then apply weights provided for first-generation fermions also to equivalents in the second and third generation (i.e. u dbar is also applied to u sbar, u bbar, c dbar, c sbar and c bbar).

You can see the list of allowed channels and their respective fixed weights by switching on the InFlux:showChannels flag. Note that channels which are assigned a vanishing weight during the initialization step are pruned from the channel list.

In addition, initProc() is the place where all other types of process-specific initialization calculations could be put.

Event weight

sigmaHat() encodes the relevant matrix element. For a 2 -> 1 process, what is to be provided is sigmaHat(sHat), where sH and sH2 are available to be used. For a 2 -> 2 process, instead d(sigmaHat)/d(tHat) should be calculated, based on provided sH, sH2, tH, tH2, uH, uH2, m3, s3, m4, s4 and pT2 values (s3 = m3*m3). In either case, alpha_s and alpha_em have also been calculated, and are stored in alpS and alpEM. Also other standard variables may be used, like CoupEW::sin2thetaW(), and related flavour-dependent vector and axial couplings in CoupEW and CKM combinations in VCKM. Note that, normally the cross sections would come in dimensions of inverse GeV to second or fourth power, respectively. To translate it to the compulsory mb or mb/GeV^2 dimensions, you need to multiply by the CONVERT2MB constant.

A further machinery exists when event-by-event channel-dependent weights are required. A typical example would be that, for q qbar -> gamma*/Z0, the relative weight of incoming u-type and d-type quarks depends on the relative admixture of gamma* and of Z0, which depends on the subprocess energy, and so varies from one event to the next. Therefore, in addition to the fixed weight above, to be set at initialization, each channel contains a variable weight factor, and the total weight of a channel is a product of the two. The variable weight is set to 1 at the creation of a new channel, so if not touched it makes no difference. It is not changed between events, however, i.e. not automatically reset to 1. It can be manipulated by two methods:
* weightInState(double nowWeight = 1.): set all variable weights to the value specified (usually not necessary).
* weightInState(int id1, int id2, double nowWeight, bool flipSide = true, bool conjugate = true, bool allGen = true): set the variable weight factor for the incoming state defined by id1 and id2 to be varWeight Se above for an explanation of the flipside, conjugate and allGen options.

Normally, only one cross section is returned from sigmaHat(), and the standard machinery takes care of weighting it with parton densities and the other factors specified above. However, if you use variable weights, you should use the weightInState(...) method above to specify the weight for each incoming flavour combination. Note that, when weights are used, there is a complete freedom how you shuffle contributions between the individual channels and the overall weight returned by sigmaHat(), since only the product is relevant. For sanity, as much as possible should be put in the sigmaHat(), with weightFixed(...) and weightInState(...) only providing dimensionless extrafactors of order unity.

Finalize event

setIdColAcol() is called when a kinematical configuration has been accepted and flavours and colours need be provided. The first part is handled by the setId method. The two incoming flavours have already been set in id1 and id2, whereas the one or two outgoing ones either are fixed for a given process or can be determined from the instate (e.g. whether a W+ or W- was produced). The colours are handled by the setColAcol method. Les Houches style colour tags are used, but starting with number 1. The input is grouped particle by particle, with the colour index before the anticolour one. You may need to select colour flow dynamically, depending on the kinematics, when several distinct possibilities exist. Trivial operations, like swapping colours and anticolours, can be done with existing methods. There is also a standard method to pick a final flavour from an iniial one with CKM mixing.

When the id3Mass() and id4Mass() methods have been used, the order of the outgoing particles may be inconsistent with the way the tHat and uHat variables have been defined. A typical example would be a process like q g -> q' W with tHat defined between incoming and outgoing quark, but where id3Mass() = 24 and so the process is to be stored as q g -> W q'. One should then put the variable swapTU = true for each event where the tHat and uHat variables should be swapped before the event kinematics is reconstructed. This variable is automatically restored to false for each new event.

Access

A flag has to be defined, that allows the process to be switched on; by default it should always be off. The name of the flag should be chosen of the type model:process. Here the model would be related to the general scenario considered, e.g. Compositeness, while process would specify instate and outstate, separated by a 2 (= to), e.g. ug2u*g. When several processes are implemented and "belong together" it is also useful to define a model:all switch that affects all the separate processes.

The flags should normally be stored in the Processes.xml file. This is to make them easily found by users. You could create and use your own .xml file, so long as you then add that name to the list of files in the Index.xml file. (If not, the flags would never be created and the program would not work.)

In the ProcessContainer.c file, the final SetupContainers::init routine needs to be expanded to create instances of the processes switched on. This code is fairly repetitive, and should be easy to copy and modify from the code already there. The basic structure is
(i) check whether a process is requested by the user and, if so,
(ii) create an instance of the matrix-element class,
(iii)create a container for the matrix element and its associated phase-space handling, and
(iv) add the container to the existing process list.

Two minor variations are possible. One is that a set of related processes are lumped inside the the same initial check, i.e. are switched on all together. The second is that the matrix-element constructor may take arguments, as specified by you (see above). If so, the same basic matrix element may be recycled for a set of related processes, e.g. one for a composite u and one for a composite d. Obviously these variations may be combined.