.. _sec_structure: Structure of McMule =================== McMule is written in Fortran 95 with helper and analysis tools written in ``python`` [1]_. The code is written with two kinds of applications in mind. First, several processes are implemented, some at :term:`NLO`, some at :term:`NNLO`. For these, the user can define an arbitrary (infrared safe), fully differential observable and compute cross sections and distributions. Second, the program is set up such that additional processes can be implemented by supplying the relevant matrix elements. The main part of McMule is the ``libmcmule`` library which consists of several modules with a simple, mostly hierarchic structure. The ``mcmule`` executable loads this library which in turn loads the :term:`measurement function` defined by the user. The user function ----------------- For a user of the code who wants to run for an already implemented process, this is the only relevant part. The user function is defined in a (usually dynamically loaded) library and defines the :term:`measurement function`. At the beginning of the module, the user has to specify the number of quantities to be computed, :f:var:`user/nq`, the number of bins in the histogram, :f:var:`user/nbins`, as well as their lower and upper boundaries, :f:var:`user/min_val` and :f:var:`user/max_val`. The last three quantities are arrays of length :f:var:`user/nq`. The quantities themselves, i.e. the measurement function, is to be defined by the user in terms of the momenta of the particles in :f:func:`user/quant`. Cuts can be applied by setting the logical variable :f:var:`mcmule/pass_cut` to false [2]_. Some auxiliary functions like (pseudo)rapidity, transverse momentum etc. are predefined in :f:mod:`mcmule` as documented in Section :ref:`sec_libmcmule`. Each quantity has to be given a name through the array :f:var:`mcmule/names`. Further, ``user`` contains a subroutine called :f:subr:`quant/inituser`. This allows the user to read additional input at runtime, for example which of multiple cuts should be calculated. It also allows the user to print some information on the configuration implemented. Needless to say that it is good idea to do this for documentation purposes. Once written (or more likely adapted from a `template `__), the user function is compiled so it can be loaded by ``libmcmule`` .. code:: bash $ gfortran -fPIC --shared -o user.so user.f95 It is possible to write a user function in languages other the Fortran and a ``mcmule.h`` header file is provided for C and C++. However, many of the quality-of-life functions of :f:mod:`mcmule` are only available in Fortran (cf. Section :ref:`sec_libmcmule_c`) Modular structure of ``libmcmule`` ---------------------------------- The relation between the most important Fortran modules is depicted in :numref:`fig_structure`. A solid arrow indicates "using" the full module, whereas a dashed arrow is indicative of partial use. In what follows we give a brief description of the various modules and mention some variables that play a prominent role in the interplay between the modules. .. _fig_structure: .. mermaid:: figures/structure.mmd :caption: The structure of McMule ``global_def``: This module simply provides some parameters such as fermion masses that are needed throughout the code. It also defines :f:type:`real(kind=prec)` as a generic type for the precision used. [3]_ Currently, this simply corresponds to double precision. ``functions``: This module is a library of basic functions that are needed at various points in the code. This includes dot products, eikonal factors, the integrated eikonal, and an interface for scalar integral functions among others. ``collier``: This is an external module :cite:`Denner:2016kdg, Denner:2010tr, Denner:2005nn, Denner:2002ii`. It will be linked to McMule during compilation and provides the numerical evaluations of the scalar and in some cases tensor integral functions in ``functions``. ``phase_space``: The routines for generating phase-space points and their weights are collected in this module. Phase-space routines ending with ``FKS`` are prepared for the :term:`FKS` subtraction procedure with a single unresolved photon. In the weight of such routines a factor :math:`\xi_1` is omitted to allow the implementation of the distributions in the :term:`FKS` method. This corresponds to a global variable :f:var:`xiout1`. This factor has to be included in the integrand of the module ``integrands``. Also the variable :f:var:`qsoft` is provided that corresponds to the photon momentum without the (vanishing) energy factor :math:`\xi_1`. Routines ending with ``FKSS`` are routines with two unresolved photons. Correspondingly, a factor :math:`\xi_1\,\xi_2` is missing in the weight and :f:var:`xioutA` and :f:var:`xioutB`, as well as :f:var:`qsoftA`` and :f:var:`qsoftB` are provided. To ensure numerical stability it is often required to tune the phase-space routine to a particular kinematic situation. ``openloops``: This is the external OpenLoops library :cite:`Buccioni:2017yxi,Buccioni:2019sur` that we use for some real-virtual matrix elements. It is pulled as a ``git`` submodule and linked to McMule during compilation. ``olinterface``: This connects ``openloops`` to the rest of McMule by initialising OpenLoops for the process under consideration and converting to and from the OpenLoops conventions which are slightly different than the ones used by McMule. ``{pg}_mat_el``: Matrix elements are grouped into :term:`process groups` such as muon decay (``mudec``) or :math:`\mu`-:math:`e` and :math:`\mu`-:math:`p` scattering (``mue``). Each :term:`process group` contains a ``mat_el`` module that provides all matrix elements for its group. Simple matrix elements are coded directly in this module. More complicated results are imported from sub-modules not shown in :numref:`fig_structure`. A matrix element starting with ``P`` contains a polarised initial state. A matrix element ending in ``av`` is averaged over a neutrino pair in the final state. ``{pg}``: In this module the soft limits of all applicable matrix elements of a :term:`process group` are provided to allow for the soft subtractions required in the :term:`FKS` scheme. These limits are simply the eikonal factor evaluated with :f:var:`qsoft` from ``phase_space`` times the reduced matrix element, provided through ``mat_el``. This module also functions as the interface of the :term:`process group`, exposing all necessary functions that are imported by ``mat_el``, which collects all matrix elements as well as their particle labelling or :term:`PID`. ``user_dummy``: This interfaces to the (usually dynamically loaded) :term:`measurement function` defined by the user. ``vegas``: As the name suggests this module contains the adaptive Monte Carlo routine ``vegas`` :cite:`Lepage:1980jk` . The binning routine ``bin_it`` is also in this module, hence the need for the binning metadata, i.e. the number of bins and histograms as well as their bounds (:f:var:`min_val` and :f:var:`max_val`) and names, from ``user_dummy``. ``integrands``: In this module the functions that are to be integrated by ``vegas`` are coded. There are three types of integrands: non-subtracted, single-subtracted, and double-subtracted integrands, corresponding to the three parts of the :math:`{\rm FKS}^2` scheme :cite:`Engel:2019nfw,Ulrich:2020phd`. The matrix elements to be evaluated and the phase-space routines used are set using function pointers through a subroutine ``initpiece``. The factors :math:`\xi_i` that were omitted in the phase-space weight have to be included here for the single- and double-subtracted integrands. ``mcmule_header``: This is the main part of the library that initialised and runs McMule with configurations passed by the main executable. It also defines the Module :f:mod:`mcmule` which contains :f:subr:`runmcmule`. ``test``: For developing purposes, a separate main program exists that is used to validate the code after each change. Reference values for matrix elements and results of short integrations are stored here and compared against. ``mcmule``: This is the main executable, but actually does little else than read the inputs and call ``mcmule_header``. The library of matrix elements deserves a few comments. As matrix elements quickly become very large, we store them separately from the main code. This makes it also easy to extend the program by minimising the code that needs to be changed. We group matrix elements into :term:`process groups`, :term:`generic processes`, and :term:`generic pieces` as indicated in Appendix :ref:`sec_pieces`. The generic process is a prototype for the physical process such as :math:`\ell p\to\ell p` where the flavour of the lepton :math:`\ell` is left open. The generic piece describes a part of the calculation such as the real or virtual corrections, i.e. the different pieces of :eq:`eq_nlo` (or correspondingly :eq:`eq_nnlo_n` at :term:`NNLO`), that themselves may be further subdivided as is convenient. In particular, in some cases a generic piece is split into various partitions (cf. Section :ref:`sec_ps` for details on why that is important). What happens when running ------------------------- In the following we discuss what happens behind the scene when asking McMule to perform the calculation of ``m2enng0`` in Section :ref:`sec_getting_started_lo`. 0. When started, the first thing ``mcmule`` does is load the :term:`measurement function` as from a :term:`shared library` using the :f:subr:`load_quant` routine. 1. When started, ``mcmule`` reads options from ``stdin`` as specified in :numref:`tab_mcmuleinput`. It then passes these values to the :f:subr:`runmcmule` function of :f:mod:`mcmule`. 2. Once McMule knows its configuration it associates the numerical values of the masses, as specified through ``flavour`` as set by the user. In particular, we set the generic masses ``Mm`` and ``Me`` to ``Mtau`` and ``Mel``. This is done in :f:subr:`initflavour`, defined in :f:mod:`global_def`. For other processes this might also involve setting e.g. centre-of-mass energies ``scms`` to default values. 3. Next, the function to be integrated by ``vegas`` is determined. This is a function stored in ``integrands``. There are basically three types of integrands: a standard, non-subtracted integrand, ``sigma_0``, a single-subtracted integrand needed beyond :term:`LO`, ``sigma_1``, and a double-subtracted integrand needed beyond :term:`NLO`, ``sigma_2``. Which integrand is needed and what matrix elements and phase-space it depends on is determined by calling the function ``init_piece`` which uses the variable :f:var:`which_piece` to point function pointers at the necessary procedures. For our :term:`LO` case, ``init_piece`` sets the integrand to ``sigma_0`` and fixes the dimension of the integration to :math:`{\tt ndim}=8`. 4. The matrix element pointer is assigned to the matrix element that needs to be called, ``Pm2enngAV(q1,n1,q2,q3,q4,q5)``. The name of the function suggests we compute :math:`\mu(q_1,n_1)\to [\nu(q3) \bar\nu(q4)]e(q_2) \gamma(q_5)` with the polarisation vector ``n1`` of the initial lepton. Even though we average over the neutrinos, their momenta are still given for completeness. 5. The interplay between the function ``sigma_0(x,wgt,ndim)`` and ``vegas`` is as usual, through an array of random numbers ``x`` of length ``ndim`` that corresponds to the dimension of the integration. In addition there is the ``vegas`` weight of the event, ``wgt`` due to the Jacobian introduced by the importance sampling. The function ``sigma_0`` simply evaluates the complete weight ``wg`` of a particular event by combining ``wgt`` with the matrix element supplemented by symmetry, flux, and phase-space factors. 1. In a first step a phase-space routine of ``phase_space`` is called. For our :term:`LO` calculation, ``init_piece`` pointed a pointer to the phase-space routine :f:subr:`psd5_25`, a phase-space routine optimised for radiative lepton decays (cf. Section :ref:`sec_ps`). This will be called as a first step in the integrand to generate the momenta with correct masses as well as the phase-space weight ``weight``. 2. With these momenta the observables to be computed are evaluated with a call to :f:func:`user/quant`. If one of them passes the cuts, the variable :f:var:`pass_cut` is set to true. 3. This triggers the computation of the matrix element and the assembly of the full weight. 4. In a last step, the routine ``bin_it``, stored in ``vegas``, is called to put the weight into the correct bins of the various distributions. If the variable under- or overshoots the bounds specified by :f:var:`user/min_val` and :f:var:`user/max_val`, the event is placed into dedicated, infinitely big under- and overflow bins. These steps are done for all events and those after pre-conditioning are used to obtain the final distributions. 6. After preconditioning the state of the integrator is reset, as is usual. 7. During the main run, the code generates a statefile from which the full state of the integrator can be reconstructed should the integration be interrupted (cf. Section :ref:`sec_vegasff` for details). This makes the statefile ideal to also store results in a compact format. 8. The value and error estimate of the integration is printed to ``stdout``. To analyse these results, we provide a python tool pymule, additionally to the main code for McMule. pymule uses ``numpy`` :cite:`Walt:2011np` for data storage and ``matplotlib`` for plotting :cite:`Hunter:2007mp`. While pymule works with any python interpreter, ``IPython`` :cite:`Perez:2007ip` is recommended. We will encountered pymule in Section :ref:`sec_analyse` when we discuss how to use it to analyse results. A full list of functions provided can be found in Appendix :ref:`sec_pymule`. .. [1] Additionally to the ``python`` tool a Mathematica tool is available. .. [3] Technically, :f:var:`pass_cut` is a list of length :f:var:`nr_q`, allowing to decide whether to cut for each histogram separately. .. [2] For quad precision ``prec=16`` and the compiler flag ``-fdefault-real-16`` is required.