################################################################################ # # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors # # This file is a part of the MadGraph5_aMC@NLO project, an application which # automatically generates Feynman diagrams and matrix elements for arbitrary # high-energy processes in the Standard Model and beyond. # # It is subject to the MadGraph5_aMC@NLO license which should accompany this # distribution. # # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch # ################################################################################ """A user friendly command line interface to access MadGraph5_aMC@NLO features at LO. Uses the cmd package for command interpretation and tab completion. """ from __future__ import division import atexit import collections import cmath import glob import logging import optparse import os import pydoc import random import re import signal import subprocess import copy import sys import shutil import StringIO import traceback import time import inspect import urllib import random #useful shortcut pjoin = os.path.join try: import readline GNU_SPLITTING = ('GNU' in readline.__doc__) except: GNU_SPLITTING = True import aloha import madgraph from madgraph import MG4DIR, MG5DIR, MadGraph5Error import madgraph.core.base_objects as base_objects import madgraph.core.diagram_generation as diagram_generation import madgraph.loop.loop_diagram_generation as loop_diagram_generation import madgraph.loop.loop_base_objects as loop_base_objects import madgraph.core.drawing as draw_lib import madgraph.core.helas_objects as helas_objects import madgraph.iolibs.drawing_eps as draw import madgraph.iolibs.export_cpp as export_cpp import madgraph.iolibs.export_v4 as export_v4 import madgraph.iolibs.helas_call_writers as helas_call_writers import madgraph.iolibs.file_writers as writers import madgraph.iolibs.files as files import madgraph.iolibs.group_subprocs as group_subprocs import madgraph.iolibs.import_v4 as import_v4 import madgraph.iolibs.save_load_object as save_load_object import madgraph.interface.extended_cmd as cmd import madgraph.interface.tutorial_text as tutorial_text import madgraph.interface.tutorial_text_nlo as tutorial_text_nlo import madgraph.interface.tutorial_text_madloop as tutorial_text_madloop import madgraph.interface.launch_ext_program as launch_ext import madgraph.interface.madevent_interface as madevent_interface import madgraph.interface.amcatnlo_run_interface as amcatnlo_run import madgraph.loop.loop_exporters as loop_exporters import madgraph.loop.loop_helas_objects as loop_helas_objects import madgraph.various.process_checks as process_checks import madgraph.various.banner as banner_module import madgraph.various.misc as misc import madgraph.various.cluster as cluster import models as ufomodels import models.import_ufo as import_ufo import models.write_param_card as param_writer import models.check_param_card as check_param_card import models.model_reader as model_reader import aloha.aloha_fct as aloha_fct import aloha.create_aloha as create_aloha import aloha.aloha_lib as aloha_lib import mg5decay.decay_objects as decay_objects # Special logger for the Cmd Interface logger = logging.getLogger('cmdprint') # -> stdout logger_check = logging.getLogger('check') # -> stdout logger_mg = logging.getLogger('madgraph.interface') # -> stdout logger_stderr = logging.getLogger('fatalerror') # ->stderr logger_tuto = logging.getLogger('tutorial') # -> stdout include instruction in #order to learn MG5 logger_tuto_nlo = logging.getLogger('tutorial_aMCatNLO') # -> stdout include instruction in #order to learn aMC@NLO logger_tuto_madloop = logging.getLogger('tutorial_MadLoop') # -> stoud for MadLoop tuto #=============================================================================== # CmdExtended #=============================================================================== class CmdExtended(cmd.Cmd): """Particularisation of the cmd command for MG5""" #suggested list of command next_possibility = { 'start': ['import model ModelName', 'import command PATH', 'import proc_v4 PATH', 'tutorial'], 'import model' : ['generate PROCESS','define MULTIPART PART1 PART2 ...', 'display particles', 'display interactions'], 'define': ['define MULTIPART PART1 PART2 ...', 'generate PROCESS', 'display multiparticles'], 'generate': ['add process PROCESS','output [OUTPUT_TYPE] [PATH]','display diagrams'], 'add process':['output [OUTPUT_TYPE] [PATH]', 'display processes'], 'output':['launch','open index.html','history PATH', 'exit'], 'display': ['generate PROCESS', 'add process PROCESS', 'output [OUTPUT_TYPE] [PATH]'], 'import proc_v4' : ['launch','exit'], 'launch': ['open index.html','exit'], 'tutorial': ['generate PROCESS', 'import model MODEL', 'help TOPIC'] } debug_output = 'MG5_debug' error_debug = 'Please report this bug on https://bugs.launchpad.net/mg5amcnlo\n' error_debug += 'More information is found in \'%(debug)s\'.\n' error_debug += 'Please attach this file to your report.' config_debug = 'If you need help with this issue please contact us on https://answers.launchpad.net/mg5amcnlo\n' keyboard_stop_msg = """stopping all operation in order to quit mg5 please enter exit""" # Define the Error Class # Define how error are handle InvalidCmd = madgraph.InvalidCmd ConfigurationError = MadGraph5Error intro_banner = "************************************************************\n" + \ "* *\n" + \ "* W E L C O M E to *\n" + \ "* M A D G R A P H 5 _ a M C @ N L O *\n" + \ "* *\n" + \ "* *\n" + \ "* * * *\n" + \ "* * * * * *\n" + \ "* * * * * 5 * * * * *\n" + \ "* * * * * *\n" + \ "* * * *\n" + \ "* *\n" + \ "%s" + \ "* *\n" + \ "* The MadGraph5_aMC@NLO Development Team - Find us at *\n" + \ "* https://server06.fynu.ucl.ac.be/projects/madgraph *\n" + \ "* and *\n" + \ "* http://amcatnlo.web.cern.ch/amcatnlo/ *\n" + \ "* *\n" + \ "* Type 'help' for in-line help. *\n" + \ "* Type 'tutorial' to learn how MG5 works *\n" + \ "* Type 'tutorial aMCatNLO' to learn how aMC@NLO works *\n" + \ "* Type 'tutorial MadLoop' to learn how MadLoop works *\n" + \ "* *\n" + \ "************************************************************" def __init__(self, *arg, **opt): """Init history and line continuation""" # If possible, build an info line with current version number # and date, from the VERSION text file info = misc.get_pkg_info() info_line = "" if info.has_key('version') and info.has_key('date'): len_version = len(info['version']) len_date = len(info['date']) if len_version + len_date < 30: info_line = "#* VERSION %s %s %s *\n" % \ (info['version'], (30 - len_version - len_date) * ' ', info['date']) if os.path.exists(pjoin(MG5DIR, '.bzr')): proc = subprocess.Popen(['bzr', 'nick'], stdout=subprocess.PIPE,cwd=MG5DIR) bzrname,_ = proc.communicate() proc = subprocess.Popen(['bzr', 'revno'], stdout=subprocess.PIPE,cwd=MG5DIR) bzrversion,_ = proc.communicate() bzrname, bzrversion = bzrname.strip(), bzrversion.strip() len_name = len(bzrname) len_version = len(bzrversion) info_line += "#* BZR %s %s %s *\n" % \ (bzrname, (34 - len_name - len_version) * ' ', bzrversion) # Create a header for the history file. # Remember to fill in time at writeout time! self.history_header = banner_module.ProcCard.history_header % {'info_line': info_line} banner_module.ProcCard.history_header = self.history_header if info_line: info_line = info_line.replace("#*","*") logger.info(self.intro_banner % info_line) cmd.Cmd.__init__(self, *arg, **opt) self.history = banner_module.ProcCard() def default(self, line): """Default action if line is not recognized""" # Faulty command log=True if line.startswith('p') or line.startswith('e'): logger.warning("Command %s not recognized. Did you mean \'generate %s\'?. Please try again" % (line.split()[0], line)) log=False return super(CmdExtended,self).default(line, log=log) def postcmd(self,stop, line): """ finishing a command This looks if the command add a special post part. This looks if we have to write an additional text for the tutorial.""" stop = super(CmdExtended, self).postcmd(stop, line) # Print additional information in case of routines fails if stop == False: return False args=line.split() # Return for empty line if len(args)==0: return stop # try to print linked to the first word in command #as import_model,... if you don't find then try print with only #the first word. if len(args)==1: command=args[0] else: command = args[0]+'_'+args[1].split('.')[0] try: logger_tuto.info(getattr(tutorial_text, command).replace('\n','\n\t')) except Exception: try: logger_tuto.info(getattr(tutorial_text, args[0]).replace('\n','\n\t')) except Exception: pass try: logger_tuto_nlo.info(getattr(tutorial_text_nlo, command).replace('\n','\n\t')) except Exception: try: logger_tuto_nlo.info(getattr(tutorial_text_nlo, args[0]).replace('\n','\n\t')) except Exception: pass try: logger_tuto_madloop.info(getattr(tutorial_text_madloop, command).replace('\n','\n\t')) except Exception: try: logger_tuto_madloop.info(getattr(tutorial_text_madloop, args[0]).replace('\n','\n\t')) except Exception: pass return stop def get_history_header(self): """return the history header""" return self.history_header % misc.get_time_info() #=============================================================================== # HelpToCmd #=============================================================================== class HelpToCmd(cmd.HelpCmd): """ The Series of help routine for the MadGraphCmd""" def help_save(self): logger.info("syntax: save %s FILENAME" % "|".join(self._save_opts),'$MG:color:BLUE') logger.info("-- save information as file FILENAME",'$MG:color:BLACK') logger.info(" FILENAME is optional for saving 'options'.") logger.info(' By default it uses ./input/mg5_configuration.txt') logger.info(' If you put "global" for FILENAME it will use ~/.mg5/mg5_configuration.txt') logger.info(' If this files exists, it is uses by all MG5 on the system but continues') logger.info(' to read the local options files.') def help_load(self): logger.info("syntax: load %s FILENAME" % "|".join(self._save_opts),'$MG:color:BLUE') logger.info("-- load information from file FILENAME",'$MG:color:BLACK') def help_import(self): logger.info("syntax: import " + "|".join(self._import_formats) + \ " FILENAME",'$MG:color:BLUE') logger.info("-- imports file(s) in various formats",'$MG:color:GREEN') logger.info("") logger.info(" import model MODEL[-RESTRICTION] [OPTIONS]:",'$MG:color:BLACK') logger.info(" Import a UFO model.") logger.info(" MODEL should be a valid UFO model name") logger.info(" Model restrictions are specified by MODEL-RESTRICTION") logger.info(" with the file restrict_RESTRICTION.dat in the model dir.") logger.info(" By default, restrict_default.dat is used.") logger.info(" Specify model_name-full to get unrestricted model.") logger.info(" '--modelname' keeps the original particle names for the model") logger.info("") logger.info(" import model_v4 MODEL [--modelname] :",'$MG:color:BLACK') logger.info(" Import an MG4 model.") logger.info(" Model should be the name of the model") logger.info(" or the path to theMG4 model directory") logger.info(" '--modelname' keeps the original particle names for the model") logger.info("") logger.info(" import proc_v4 [PATH] :",'$MG:color:BLACK') logger.info(" Execute MG5 based on a proc_card.dat in MG4 format.") logger.info(" Path to the proc_card is optional if you are in a") logger.info(" madevent directory") logger.info("") logger.info(" import command PATH :",'$MG:color:BLACK') logger.info(" Execute the list of command in the file at PATH") logger.info("") logger.info(" import banner PATH [--no_launch]:",'$MG:color:BLACK') logger.info(" Rerun the exact same run define in the valid banner.") def help_install(self): logger.info("syntax: install " + "|".join(self._install_opts),'$MG:color:BLUE') logger.info("-- Download the last version of the program and install it") logger.info(" locally in the current MadGraph5_aMC@NLO version. In order to have") logger.info(" a successful installation, you will need to have an up-to-date") logger.info(" F77 and/or C and Root compiler.") logger.info(" ") logger.info(" When installing any of the following programs:") logger.info(" %s"%(', '.join(self._advanced_install_opts))) logger.info(" The following options are available:") logger.info(" --force Overwrite without asking any existing installation.") logger.info(" --keep_source Keep a local copy of the sources of the tools MG5_aMC installed from.") logger.info(" ") logger.info(" \"install update\"",'$MG:color:BLACK') logger.info(" check if your MG5 installation is the latest one.") logger.info(" If not it load the difference between your current version and the latest one,") logger.info(" and apply it to the code. Two options are available for this command:") logger.info(" -f: didn't ask for confirmation if it founds an update.") logger.info(" --timeout=: Change the maximum time allowed to reach the server.") def help_display(self): logger.info("syntax: display " + "|".join(self._display_opts),'$MG:color:BLUE') logger.info("-- display a the status of various internal state variables") logger.info(" for particles/interactions you can specify the name or id of the") logger.info(" particles/interactions to receive more details information.") logger.info(" Example: display particles e+.",'$MG:color:GREEN') logger.info(" > For \"checks\", can specify only to see failed checks.") logger.info(" > For \"diagrams\", you can specify where the file will be written.") logger.info(" Example: display diagrams ./",'$MG:color:GREEN') def help_launch(self): """help for launch command""" # Using the built-in parser help is not convenient when one wants to use # color schemes. #_launch_parser.print_help() logger.info("syntax: launch ",'$MG:color:BLUE') logger.info("-- execute the aMC@NLO/madevent/standalone/pythia8 output present in dir_path",'$MG:color:BLACK') logger.info("By default, dir_path points to the last created directory.") logger.info("(for pythia8, it should be the Pythia 8 main directory)") logger.info("") logger.info("Launch on madevent/pythia8/standalone outputs:",'$MG:color:BLACK') logger.info(" o Example: launch PROC_sm_1 --name=run2",'$MG:color:GREEN') logger.info(" o Example: launch ../pythia8",'$MG:color:GREEN') logger.info(" > Options:") logger.info(" -h, --help show this help message and exit") logger.info(" -f, --force Use the card present in the directory in order") logger.info(" to launch the different program") logger.info(" -n NAME, --name=NAME Provide a name to the run (for madevent run)") logger.info(" -c, --cluster submit the job on the cluster") logger.info(" -m, --multicore submit the job on multicore core") logger.info(" -i, --interactive Use Interactive Console [if available]") logger.info(" -s LASTSTEP, --laststep=LASTSTEP") logger.info(" last program run in MadEvent run.") logger.info(" [auto|parton|pythia|pgs|delphes]") logger.info("") logger.info("Launch on MadLoop standalone output:",'$MG:color:BLACK') logger.info(" o Example: launch PROC_loop_sm_1 -f",'$MG:color:GREEN') logger.info(" > Simple check of a single Phase-space points.") logger.info(" > You will be asked whether you want to edit the MadLoop ") logger.info(" and model param card as well as the PS point, unless ") logger.info(" the -f option is specified. All other options are ") logger.info(" irrelevant for this kind of launch.") logger.info("") logger.info("Launch on aMC@NLO output:",'$MG:color:BLACK') logger.info(" > launch ",'$MG:color:BLUE') logger.info(" o Example: launch MyProc aMC@NLO -f -p",'$MG:color:GREEN') def help_tutorial(self): logger.info("syntax: tutorial [" + "|".join(self._tutorial_opts) + "]",'$MG:color:BLUE') logger.info("-- start/stop the MG5 tutorial mode (or stop any other mode)") logger.info("-- aMCatNLO: start aMC@NLO tutorial mode") logger.info("-- MadLoop: start MadLoop tutorial mode") def help_open(self): logger.info("syntax: open FILE ",'$MG:color:BLUE') logger.info("-- open a file with the appropriate editor.",'$MG:color:BLACK') logger.info(' If FILE belongs to index.html, param_card.dat, run_card.dat') logger.info(' the path to the last created/used directory is used') logger.info(' The program used to open those files can be chosen in the') logger.info(' configuration file ./input/mg5_configuration.txt') def help_customize_model(self): logger.info("syntax: customize_model --save=NAME",'$MG:color:BLUE') logger.info("-- Open an invite where you options to tweak the model.",'$MG:color:BLACK') logger.info(" If you specify the option --save=NAME, this tweak will be") logger.info(" available for future import with the command 'import model XXXX-NAME'") def help_output(self): logger.info("syntax: output [" + "|".join(self._export_formats) + \ "] [path|.|auto] [options]",'$MG:color:BLUE') logger.info("-- Output any generated process(es) to file.",'$MG:color:BLACK') logger.info(" Default mode is madevent. Default path is \'.\' or auto.") logger.info(" mode:",'$MG:color:BLACK') logger.info(" - For MadLoop and aMC@NLO runs, there is only one mode and") logger.info(" it is set by default.") logger.info(" - If mode is madevent, create a MadEvent process directory.") logger.info(" - If mode is standalone, create a Standalone directory") logger.info(" - If mode is matrix, output the matrix.f files for all") logger.info(" generated processes in directory \"path\".") logger.info(" - If mode is standalone_cpp, create a standalone C++") logger.info(" directory in \"path\".") logger.info(" - If mode is pythia8, output all files needed to generate") logger.info(" the processes using Pythia 8. The files are written in") logger.info(" the Pythia 8 directory (default).") logger.info(" NOTE: The Pythia 8 directory is set in the ./input/mg5_configuration.txt") logger.info(" - If mode is aloha: Special syntax output:") logger.info(" syntax: aloha [ROUTINE] [--options]" ) logger.info(" valid options for aloha output are:") logger.info(" --format=Fortran|Python|Cpp : defining the output language") logger.info(" --output= : defining output directory") logger.info(" path: The path of the process directory.",'$MG:color:BLACK') logger.info(" If you put '.' as path, your pwd will be used.") logger.info(" If you put 'auto', an automatic directory PROC_XX_n will be created.") logger.info(" options:",'$MG:color:BLACK') logger.info(" -f: force cleaning of the directory if it already exists") logger.info(" -d: specify other MG/ME directory") logger.info(" -noclean: no cleaning performed in \"path\".") logger.info(" -nojpeg: no jpeg diagrams will be generated.") logger.info(" -name: the postfix of the main file in pythia8 mode.") logger.info(" Examples:",'$MG:color:GREEN') logger.info(" output",'$MG:color:GREEN') logger.info(" output standalone MYRUN -f",'$MG:color:GREEN') logger.info(" output pythia8 ../pythia8/ -name qcdprocs",'$MG:color:GREEN') def help_check(self): logger.info("syntax: check [" + "|".join(self._check_opts) + "] [param_card] process_definition [--energy=] [--split_orders=] [--reduction=]",'$MG:color:BLUE') logger.info("-- check a process or set of processes.",'$MG:color:BLACK') logger.info("General options:",'$MG:color:BLACK') logger.info("o full:",'$MG:color:GREEN') logger.info(" Perform all four checks described below:") logger.info(" permutation, brs, gauge and lorentz_invariance.") logger.info("o permutation:",'$MG:color:GREEN') logger.info(" Check that the model and MG5 are working properly") logger.info(" by generating permutations of the process and checking") logger.info(" that the resulting matrix elements give the same value.") logger.info("o gauge:",'$MG:color:GREEN') logger.info(" Check that processes are gauge invariant by ") logger.info(" comparing Feynman and unitary gauges.") logger.info(" This check is, for now, not available for loop processes.") logger.info("o brs:",'$MG:color:GREEN') logger.info(" Check that the Ward identities are satisfied if the ") logger.info(" process has at least one massless gauge boson as an") logger.info(" external particle.") logger.info("o lorentz_invariance:",'$MG:color:GREEN') logger.info(" Check that the amplitude is lorentz invariant by") logger.info(" comparing the amplitiude in different frames") logger.info("o cms:",'$MG:color:GREEN') logger.info(" Check the complex mass scheme consistency by comparing") logger.info(" it to the narrow width approximation in the off-shell") logger.info(" region of detected resonances and by progressively") logger.info(" decreasing the width. Additional options for this check are:") logger.info(" --offshellness=f : f is a positive or negative float specifying ") logger.info(" the distance from the pole as f*particle_mass. Default is 10.0") logger.info(" --seed=i : to force a specific RNG integer seed i (default is fixed to 0)") logger.info(" --cms=order1&order2;...,p1->f(p,lambdaCMS)&p2->f2(p,lambdaCMS);...") logger.info(" 'order_i' specifies the expansion orders considered for the test.") logger.info(" The substitution lists specifies how internal parameter must be modified") logger.info(" with the width scaling 'lambdaCMS'. The default value for this option is:") logger.info(" --cms=QED&QCD,aewm1->10.0/lambdaCMS&as->0.1*lambdaCMS ") logger.info(" The number of order and parameters don't have to be the same.") logger.info(" The scaling must be specified so that one occurrence of the coupling order.") logger.info(" brings in exactly one power of lambdaCMS.") logger.info(" --recompute_width= never|first_time|always|auto") logger.info(" Decides when to use MadWidth to automatically recompute the width") logger.info(" 'auto' (default) let MG5 chose the most appropriate behavior.") logger.info(" 'never' uses the default width value for lambdaCMS=1.0.") logger.info(" 'first_time' uses MadWidth to compute the width for lambdaCMS=1.0.") logger.info(" 'first_time' and 'never' assume linear scaling of the widths with lambdaCMS") logger.info(" 'always' uses MadWidth to compute the widths for all values of lambdaCMS") logger.info(" the test relies on linear scaling of the width, so 'always' is ") logger.info(" only for double-checks") logger.info(" --lambdaCMS = : specifies the list of lambdaCMS values to ") logger.info(" use for the test. For example: '[(1/2.0)**exp\ for\ exp\ in\ range(0,20)]'") logger.info(" In the list expression, you must escape spaces. Also, this option") logger.info(" *must* appear last in the otpion list. Finally, the default value is '1.0e-6'") logger.info(" for which an optimal list of progressive values is picked up to 1.0e-6") logger.info(" --show_plot = True or False: Whether to show plot during analysis (default is True)") logger.info(" --report = concise or full: Whether return a concise or full report.") logger.info("Comments",'$MG:color:GREEN') logger.info(" > If param_card is given, that param_card is used ") logger.info(" instead of the default values for the model.") logger.info(" If that file is an (LHE) event file. The param_card of the banner") logger.info(" is used and the first event compatible with the requested process") logger.info(" is used for the computation of the square matrix elements") logger.info(" > \"--energy=\" allows to change the default value of sqrt(S).") logger.info(" > Except for the 'gauge' test, all checks above are also") logger.info(" available for loop processes with ML5 ('virt=' mode)") logger.info("Example: check full p p > j j",'$MG:color:GREEN') logger.info("Options for loop processes only:",'$MG:color:BLACK') logger.info("o timing:",'$MG:color:GREEN') logger.info(" Generate and output a process and returns detailed") logger.info(" information about the code and a timing benchmark.") logger.info("o stability:",'$MG:color:GREEN') logger.info(" Generate and output a process and returns detailed") logger.info(" statistics about the numerical stability of the code.") logger.info("o profile:",'$MG:color:GREEN') logger.info(" Performs both the timing and stability analysis at once") logger.info(" and outputs the result in a log file without prompting") logger.info(" it to the user.") logger.info("Comments",'$MG:color:GREEN') logger.info(" > These checks are only available for ML5 ('virt=' mode)") logger.info(" > For the 'profile' and 'stability' checks, you can chose") logger.info(" how many PS points should be used for the statistic by") logger.info(" specifying it as an integer just before the [param_card]") logger.info(" optional argument.") logger.info(" > Notice multiparticle labels cannot be used with these checks.") logger.info(" > \"--reduction=\" allows to change what reduction methods should be used.") logger.info(" > \"--split_orders=\" allows to change what specific combination of coupling orders to consider.") logger.info(" > For process syntax, please see help generate.") logger.info(" > In order to save the directory generated or the reuse an existing one") logger.info(" previously generated with the check command, one can add the '-reuse' ") logger.info(" keyword just after the specification of the type of check desired.") logger.info("Example: check profile g g > t t~ [virt=QCD]",'$MG:color:GREEN') def help_generate(self): logger.info("-- generate diagrams for a given process",'$MG:color:BLUE') logger.info("General leading-order syntax:",'$MG:color:BLACK') logger.info(" o generate INITIAL STATE > REQ S-CHANNEL > FINAL STATE $ EXCL S-CHANNEL / FORBIDDEN PARTICLES COUP1=ORDER1 COUP2^2=ORDER2 @N") logger.info(" o Example: generate l+ vl > w+ > l+ vl a $ z / a h QED<=3 QCD=0 @1",'$MG:color:GREEN') logger.info(" > Alternative required s-channels can be separated by \"|\":") logger.info(" b b~ > W+ W- | H+ H- > ta+ vt ta- vt~") logger.info(" > If no coupling orders are given, MG5 will try to determine") logger.info(" orders to ensure maximum number of QCD vertices.") logger.info(" > Desired coupling orders combination can be specified directly for") logger.info(" the squared matrix element by appending '^2' to the coupling name.") logger.info(" For example, 'p p > j j QED^2==2 QCD^==2' selects the QED-QCD") logger.info(" interference terms only. The other two operators '<=' and '>' are") logger.info(" supported. Finally, a negative value COUP^2==-I refers to the") logger.info(" N^(-I+1)LO term in the expansion of the COUP order.") logger.info(" > allowed coupling operator are: \"==\", \"=\", \"<=\" and \">\".") logger.info(" \"==\" request exactly that number of coupling while \"=\" is interpreted as \"<=\".") logger.info(" > To generate a second process use the \"add process\" command") logger.info("Decay chain syntax:",'$MG:color:BLACK') logger.info(" o core process, decay1, (decay2, (decay2', ...)), ... etc") logger.info(" o Example: generate p p > t~ t QED=0, (t~ > W- b~, W- > l- vl~), t > j j b @2",'$MG:color:GREEN') logger.info(" > Note that identical particles will all be decayed.") logger.info("Loop processes syntax:",'$MG:color:BLACK') logger.info(" o core process [ LoopOrder1 LoopOrder2 ... ] SQUAREDCOUPi=ORDERi") logger.info(" o Example: generate p p > t~ t QED=0 QCD=2 [ all= QCD ] QCD=6",'$MG:color:GREEN') logger.info(" > Notice that in this format, decay chains are not allowed.") logger.info(" > The LoopOrder(s) defined specify the kind of loops to consider (only QCD for now).") logger.info(" > The coupling restrictions before '[' restrict the orders of born *amplitudes*.") logger.info(" So that in the example above QCD=2 restricts the born amplitude to have at") logger.info(" most QCD=2 and loop amplitudes at most QCD=2+2 (because QCD loops are considered)") logger.info(" > The coupling restrictions after ']' restrict the orders of the matrix element, ") logger.info(" namely the squared amplitudes. In the example above QCD=6 correspond to born") logger.info(" amplitudes with QCD=2 squared against loop amplitudes with QCD=4, adding up to 6.") logger.info(" > The optional can be any of the following ('all=' by default if absent):") logger.info(" all= : Generate all the real-emission and loop diagrams, ready for aMC@NLO runs.") logger.info(" virt= : Generate only the loop diagrams, read for MadLoop standalone checks/runs.") logger.info(" real= : Generate only the real-emission diagrams, for use with alternative OLP. ") logger.info(" > For processes without born amplitudes (i.e. loop-induced like g g > z), please use ") logger.info(" the 'virt=' NLO mode. aMC@NLO cannot integrate these processes, but standalone MadLoop5") logger.info(" can still handle these.") def help_add(self): logger.info("-- generate diagrams for a process and add to existing processes",'$MG:color:BLUE') logger.info(" OR merge two model",'$MG:color:BLUE') logger.info('') logger.info("-- generate diagrams for a process and add to existing processes",'$MG:color:BLUE') logger.info("General leading-order syntax:",'$MG:color:BLACK') logger.info(" o add process INITIAL STATE > REQ S-CHANNEL > FINAL STATE $ EXCL S-CHANNEL / FORBIDDEN PARTICLES COUP1=ORDER1 COUP2=ORDER2 @N") logger.info(" o Example: add process l+ vl > w+ > l+ vl a $ z / a h QED=3 QCD=0 @1",'$MG:color:GREEN') logger.info(" > Alternative required s-channels can be separated by \"|\":") logger.info(" b b~ > W+ W- | H+ H- > ta+ vt ta- vt~") logger.info(" > If no coupling orders are given, MG5 will try to determine") logger.info(" orders to ensure maximum number of QCD vertices.") logger.info(" > Note that if there are more than one non-QCD coupling type,") logger.info(" coupling orders need to be specified by hand.") logger.info("Decay chain syntax:",'$MG:color:BLACK') logger.info(" o core process, decay1, (decay2, (decay2', ...)), ... etc") logger.info(" o Example: add process p p > t~ t QED=0, (t~ > W- b~, W- > l- vl~), t > j j b @2",'$MG:color:GREEN') logger.info(" > Note that identical particles will all be decayed.") logger.info("Loop processes syntax:",'$MG:color:BLACK') logger.info(" o core process [ LoopOrder1 LoopOrder2 ... ] SQUAREDCOUPi=ORDERi") logger.info(" o Example: add process p p > t~ t QED=0 QCD=2 [ all= QCD ] QCD=6",'$MG:color:GREEN') logger.info(" > Notice that in this format, decay chains are not allowed.") logger.info(" > The LoopOrder(s) defined specify the kind of loops to consider (only QCD for now).") logger.info(" > The coupling restrictions before '[' restrict the orders of born *amplitudes*.") logger.info(" So that in the example above QCD=2 restricts the born amplitude to have at") logger.info(" most QCD=2 and loop amplitudes at most QCD=2+2 (because QCD loops are considered)") logger.info(" > The coupling restrictions after ']' restrict the orders of the matrix element, ") logger.info(" namely the squared amplitudes. In the example above QCD=6 correspond to born") logger.info(" amplitudes with QCD=2 squared against loop amplitudes with QCD=4, adding up to 6.") logger.info(" > The optional can be any of the following ('all=' by default if absent):") logger.info(" all= : Generate all the real-emission and loop diagrams, ready for aMC@NLO runs.") logger.info(" virt= : Generate only the loop diagrams, read for MadLoop standalone checks/runs.") logger.info(" real= : Generate only the real-emission diagrams, for use with alternative OLP. ") logger.info(" > For processes without born amplitudes (i.e. loop-induced like g g > z), please use ") logger.info(" the 'virt=' NLO mode. aMC@NLO cannot integrate these processes, but standalone MadLoop5") logger.info(" can still handle these.") logger.info("-- merge two model to create a new one", '$MG:color:BLUE') logger.info("syntax:",'$MG:color:BLACK') logger.info(" o add model MODELNAME [OPTIONS]") logger.info(" o Example: add model taudecay",'$MG:color:GREEN') logger.info(" > Merge the two model in a single one. If that same merge was done before.") logger.info(" > Just reload the previous merge. (WARNING: This doesn't check if those model are modified)") logger.info(" > Options:") logger.info(" --output= : Specify the name of the directory where the merge is done.") logger.info(" This allow to do \"import NAME\" to load that merge.") logger.info(" --recreate : Force to recreated the merge model even if the merge model directory already exists.") def help_compute_widths(self): logger.info("syntax: calculate_width PART [other particles] [OPTIONS]") logger.info(" Computes the width and partial width for a set of particles") logger.info(" Returns a valid param_card with this information.") logger.info(" ") logger.info(" PART: name of the particle you want to calculate width") logger.info(" you can enter either the name or pdg code.\n") logger.info(" Various options:\n") logger.info(" --body_decay=X: Parameter to control the precision of the computation") logger.info(" if X is an integer, we compute all channels up to X-body decay.") logger.info(" if X <1, then we stop when the estimated error is lower than X.") logger.info(" if X >1 BUT not an integer, then we X = N + M, with M <1 and N an integer") logger.info(" We then either stop at the N-body decay or when the estimated error is lower than M.") logger.info(" default: 4.0025") logger.info(" --min_br=X: All channel which are estimated below this value will not be integrated numerically.") logger.info(" default: precision (decimal part of the body_decay options) divided by four") logger.info(" --precision_channel=X: requested numerical precision for each channel") logger.info(" default: 0.01") logger.info(" --path=X: path for param_card") logger.info(" default: take value from the model") logger.info(" --output=X: path where to write the resulting card. ") logger.info(" default: overwrite input file. If no input file, write it in the model directory") logger.info(" --nlo: Compute NLO width [if the model support it]") logger.info("") logger.info(" example: calculate_width h --body_decay=2 --output=./param_card") def help_decay_diagram(self): logger.info("syntax: decay_diagram PART [other particles] [OPTIONS]") logger.info(" Returns the amplitude required for the computation of the widths") logger.info(" ") logger.info(" PART: name of the particle you want to calculate width") logger.info(" you can enter either the name or pdg code.\n") logger.info(" Various options:\n") logger.info(" --body_decay=X: Parameter to control the precision of the computation") logger.info(" if X is an integer, we compute all channels up to X-body decay.") logger.info(" if X <1, then we stop when the estimated error is lower than X.") logger.info(" if X >1 BUT not an integer, then we X = N + M, with M <1 and N an integer") logger.info(" We then either stop at the N-body decay or when the estimated error is lower than M.") logger.info(" default: 4.0025") logger.info(" --min_br=X: All channel which are estimated below this value will not be integrated numerically.") logger.info(" default: precision (decimal part of the body_decay options) divided by four") logger.info(" --precision_channel=X: requested numerical precision for each channel") logger.info(" default: 0.01") logger.info(" --path=X: path for param_card") logger.info(" default: take value from the model") logger.info(" --output=X: path where to write the resulting card. ") logger.info(" default: overwrite input file. If no input file, write it in the model directory") logger.info("") logger.info(" example: calculate_width h --body_decay=2 --output=./param_card") def help_define(self): logger.info("-- define a multiparticle",'$MG:color:BLUE') logger.info("Syntax: define multipart_name [=] part_name_list") logger.info("Example: define p = g u u~ c c~ d d~ s s~ b b~",'$MG:color:GREEN') logger.info("Special syntax: Use | for OR (used for required s-channels)") logger.info("Special syntax: Use / to remove particles. Example: define q = p / g") def help_set(self): logger.info("-- set options for generation or output.",'$MG:color:BLUE') logger.info("syntax: set ",'$MG:color:BLACK') logger.info("Possible options are: ") for opts in [self._set_options[i*3:(i+1)*3] for i in \ range((len(self._set_options)//4)+1)]: logger.info("%s"%(','.join(opts)),'$MG:color:GREEN') logger.info("Details of each option:") logger.info("group_subprocesses True/False/Auto: ",'$MG:color:GREEN') logger.info(" > (default Auto) Smart grouping of subprocesses into ") logger.info(" directories, mirroring of initial states, and ") logger.info(" combination of integration channels.") logger.info(" > Example: p p > j j j w+ gives 5 directories and 184 channels",'$MG:color:BLACK') logger.info(" (cf. 65 directories and 1048 channels for regular output)",'$MG:color:BLACK') logger.info(" > Auto means False for decay computation and True for collisions.") logger.info("ignore_six_quark_processes multi_part_label",'$MG:color:GREEN') logger.info(" > (default none) ignore processes with at least 6 of any") logger.info(" of the quarks given in multi_part_label.") logger.info(" > These processes give negligible contribution to the") logger.info(" cross section but have subprocesses/channels.") logger.info("stdout_level DEBUG|INFO|WARNING|ERROR|CRITICAL",'$MG:color:GREEN') logger.info(" > change the default level for printed information") logger.info("fortran_compiler NAME",'$MG:color:GREEN') logger.info(" > (default None) Force a specific fortran compiler.") logger.info(" If None, it tries first g77 and if not present gfortran") logger.info(" but loop output use gfortran.") logger.info("loop_optimized_output True|False",'$MG:color:GREEN') logger.info(" > Exploits the open loop thechnique for considerable") logger.info(" improvement.") logger.info(" > CP relations among helicites are detected and the helicity") logger.info(" filter has more potential.") logger.info("loop_color_flows True|False",'$MG:color:GREEN') logger.info(" > Only relevant for the loop optimized output.") logger.info(" > Reduces the loop diagrams at the amplitude level") logger.info(" rendering possible the computation of the loop amplitude") logger.info(" for a fixed color flow or color configuration.") logger.info(" > This option can considerably slow down the loop ME") logger.info(" computation time, especially when summing over all color") logger.info(" and helicity configuration, hence turned off by default.") logger.info("gauge unitary|Feynman",'$MG:color:GREEN') logger.info(" > (default unitary) choose the gauge of the non QCD part.") logger.info(" > For loop processes, only Feynman gauge is employable.") logger.info("complex_mass_scheme True|False",'$MG:color:GREEN') logger.info(" > (default False) Set complex mass scheme.") logger.info(" > Complex mass scheme is not yet supported for loop processes.") logger.info("timeout VALUE",'$MG:color:GREEN') logger.info(" > (default 20) Seconds allowed to answer questions.") logger.info(" > Note that pressing tab always stops the timer.") logger.info("cluster_temp_path PATH",'$MG:color:GREEN') logger.info(" > (default None) [Used in Madevent Output]") logger.info(" > Allow to perform the run in PATH directory") logger.info(" > This allow to not run on the central disk. ") logger.info(" > This is not used by condor cluster (since condor has") logger.info(" its own way to prevent it).") logger.info("mg5amc_py8_interface_path PATH",'$MG:color:GREEN') logger.info(" > Necessary when showering events with Pythia8 from Madevent.") logger.info("OLP ProgramName",'$MG:color:GREEN') logger.info(" > (default 'MadLoop') [Used for virtual generation]") logger.info(" > Chooses what One-Loop Program to use for the virtual") logger.info(" > matrix element generation via the BLAH accord.") logger.info("output_dependencies ",'$MG:color:GREEN') logger.info(" > (default 'external') [Use for NLO outputs]") logger.info(" > Choses how the external dependences (such as CutTools)") logger.info(" > of NLO outputs are handled. Possible values are:") logger.info(" o external: Some of the libraries the output depends") logger.info(" on are links to their installation in MG5 root dir.") logger.info(" o internal: All libraries the output depends on are") logger.info(" copied and compiled locally in the output directory.") logger.info(" o environment_paths: The location of all libraries the ") logger.info(" output depends on should be found in your env. paths.") # logger.info("max_npoint_for_channel ",'$MG:color:GREEN') # logger.info(" > (default '0') [Used for loop-induced outputs]") # logger.info(" > Sets the maximum 'n' of n-points loops to be used for") # logger.info(" > setting up the integration multichannels.") # logger.info(" > The default value of zero automatically picks the apparent") # logger.info(" > appropriate choice which is to sometimes pick box loops") # logger.info(" > but never higher n-points ones.") #=============================================================================== # CheckValidForCmd #=============================================================================== class CheckValidForCmd(cmd.CheckCmd): """ The Series of help routine for the MadGraphCmd""" class RWError(MadGraph5Error): """a class for read/write errors""" def check_add(self, args): """check the validity of line syntax: add process PROCESS | add model MODELNAME """ if len(args) < 2: self.help_add() raise self.InvalidCmd('\"add\" requires at least two arguments') if args[0] not in ['model', 'process']: raise self.InvalidCmd('\"add\" requires the argument \"process\" or \"model\"') if args[0] == 'process': return self.check_generate(args) if args[0] == 'model': pass def check_define(self, args): """check the validity of line syntax: define multipart_name [ part_name_list ] """ if len(args) < 2: self.help_define() raise self.InvalidCmd('\"define\" command requires at least two arguments') if args[1] == '=': del args[1] if len(args) < 2: self.help_define() raise self.InvalidCmd('\"define\" command requires at least one particles name after \"=\"') if '=' in args: self.help_define() raise self.InvalidCmd('\"define\" command requires symbols \"=\" at the second position') if not self._curr_model: logger.info('No model currently active. Try with the Standard Model') self.do_import('model sm') if self._curr_model['particles'].find_name(args[0]): raise self.InvalidCmd("label %s is a particle name in this model\n\ Please retry with another name." % args[0]) def check_display(self, args): """check the validity of line syntax: display XXXXX """ if len(args) < 1: self.help_display() raise self.InvalidCmd, 'display requires an argument specifying what to display' if args[0] not in self._display_opts: self.help_display() raise self.InvalidCmd, 'Invalid arguments for display command: %s' % args[0] if not self._curr_model: raise self.InvalidCmd("No model currently active, please import a model!") # check that either _curr_amps or _fks_multi_proc exists if (args[0] in ['processes', 'diagrams'] and not self._curr_amps and not self._fks_multi_proc): raise self.InvalidCmd("No process generated, please generate a process!") if args[0] == 'checks' and not self._comparisons and not self._cms_checks: raise self.InvalidCmd("No check results to display.") if args[0] == 'variable' and len(args) !=2: raise self.InvalidCmd('variable need a variable name') def check_draw(self, args): """check the validity of line syntax: draw DIRPATH [option=value] """ if len(args) < 1: args.append('/tmp') if not self._curr_amps: raise self.InvalidCmd("No process generated, please generate a process!") if not os.path.isdir(args[0]): raise self.InvalidCmd( "%s is not a valid directory for export file" % args[0]) def check_check(self, args): """check the validity of args""" if not self._curr_model: raise self.InvalidCmd("No model currently active, please import a model!") if self._model_v4_path: raise self.InvalidCmd(\ "\"check\" not possible for v4 models") if len(args) < 2 and not args[0].lower().endswith('options'): self.help_check() raise self.InvalidCmd("\"check\" requires a process.") if args[0] not in self._check_opts and \ not args[0].lower().endswith('options'): args.insert(0, 'full') param_card = None if args[0] not in ['stability','profile','timing'] and \ len(args)>1 and os.path.isfile(args[1]): param_card = args.pop(1) if len(args)>1: if args[1] != "-reuse": args.insert(1, '-no_reuse') else: args.append('-no_reuse') if args[0] in ['timing'] and len(args)>2 and os.path.isfile(args[2]): param_card = args.pop(2) if args[0] in ['stability', 'profile'] and len(args)>1: # If the first argument after 'stability' is not the integer # specifying the desired statistics (i.e. number of points), then # we insert the default value 100 try: int(args[2]) except ValueError: args.insert(2, '100') if args[0] in ['stability', 'profile'] and os.path.isfile(args[3]): param_card = args.pop(3) if any([',' in elem for elem in args if not elem.startswith('--')]): raise self.InvalidCmd('Decay chains not allowed in check') user_options = {'--energy':'1000','--split_orders':'-1', '--reduction':'1|2|3|4|5|6','--CTModeRun':'-1', '--helicity':'-1','--seed':'-1','--collier_cache':'-1', '--collier_req_acc':'auto', '--collier_internal_stability_test':'False', '--collier_mode':'1'} if args[0] in ['cms'] or args[0].lower()=='cmsoptions': # increase the default energy to 5000 user_options['--energy']='5000' # The first argument gives the name of the coupling order in which # the cms expansion is carried, and the expression following the # comma gives the relation of an external parameter with the # CMS expansions parameter called 'lambdaCMS'. parameters = ['aewm1->10.0/lambdaCMS','as->0.1*lambdaCMS'] user_options['--cms']='QED&QCD,'+'&'.join(parameters) # Widths are assumed to scale linearly with lambdaCMS unless # --force_recompute_width='always' or 'first_time' is used. user_options['--recompute_width']='auto' # It can be negative so as to be offshell below the resonant mass user_options['--offshellness']='10.0' # Pick the lambdaCMS values for the test. Instead of a python list # we specify here (low,N) which means that do_check will automatically # pick lambda values up to the value low and with N values uniformly # spread in each interval [1.0e-i,1.0e-(i+1)]. # Some points close to each other will be added at the end for the # stability test. user_options['--lambdaCMS']='(1.0e-6,5)' # Set the RNG seed, -1 is default (random). user_options['--seed']=666 # The option below can help the user re-analyze existing pickled check user_options['--analyze']='None' # Decides whether to show plot or not during the analysis user_options['--show_plot']='True' # Decides what kind of report user_options['--report']='concise' # 'secret' option to chose by which lambda power one should divide # the nwa-cms difference. Useful to set to 2 when doing the Born check # to see whether the NLO check will have sensitivity to the CMS # implementation user_options['--diff_lambda_power']='1' # Sets the range of lambda values to plot user_options['--lambda_plot_range']='[-1.0,-1.0]' # Sets a filter to apply at generation. See name of available # filters in loop_diagram_generations.py, function user_filter user_options['--loop_filter']='None' # Apply tweaks to the check like multiplying a certain width by a # certain parameters or changing the analytical continuation of the # logarithms of the UV counterterms user_options['--tweak']='default()' # Give a name to the run for the files to be saved user_options['--name']='auto' # Select what resonances must be run user_options['--resonances']='1' for arg in args[:]: if arg.startswith('--') and '=' in arg: parsed = arg.split('=') key, value = parsed[0],'='.join(parsed[1:]) if key not in user_options: raise self.InvalidCmd, "unknown option %s" % key user_options[key] = value args.remove(arg) # If we are just re-analyzing saved data or displaying options then we # shouldn't check the process format. if not (args[0]=='cms' and '--analyze' in user_options and \ user_options['--analyze']!='None') and not \ args[0].lower().endswith('options'): self.check_process_format(" ".join(args[1:])) for option, value in user_options.items(): args.append('%s=%s'%(option,value)) return param_card def check_generate(self, args): """check the validity of args""" if not self._curr_model: logger.info("No model currently active, so we import the Standard Model") self.do_import('model sm') if args[-1].startswith('--optimize'): if args[2] != '>': raise self.InvalidCmd('optimize mode valid only for 1->N processes. (See model restriction for 2->N)') if '=' in args[-1]: path = args[-1].split('=',1)[1] if not os.path.exists(path) or \ self.detect_file_type(path) != 'param_card': raise self.InvalidCmd('%s is not a valid param_card') else: path=None # Update the default value of the model here. if not isinstance(self._curr_model, model_reader.ModelReader): self._curr_model = model_reader.ModelReader(self._curr_model) self._curr_model.set_parameters_and_couplings(path) self.check_process_format(' '.join(args[1:-1])) else: self.check_process_format(' '.join(args[1:])) def check_process_format(self, process): """ check the validity of the string given to describe a format """ #check balance of paranthesis if process.count('(') != process.count(')'): raise self.InvalidCmd('Invalid Format, no balance between open and close parenthesis') #remove parenthesis for fututre introspection process = process.replace('(',' ').replace(')',' ') # split following , (for decay chains) subprocesses = process.split(',') if len(subprocesses) > 1: for subprocess in subprocesses: self.check_process_format(subprocess) return # request that we have one or two > in the process nbsep = len(re.findall('>\D', process)) # not use process.count because of QCD^2>2 if nbsep not in [1,2]: raise self.InvalidCmd( 'wrong format for \"%s\" this part requires one or two symbols \'>\', %s found' % (process, nbsep)) # we need at least one particles in each pieces particles_parts = re.split('>\D', process) for particles in particles_parts: if re.match(r'^\s*$', particles): raise self.InvalidCmd( '\"%s\" is a wrong process format. Please try again' % process) # '/' and '$' sould be used only after the process definition for particles in particles_parts[:-1]: if re.search('\D/', particles): raise self.InvalidCmd( 'wrong process format: restriction should be place after the final states') if re.search('\D\$', particles): raise self.InvalidCmd( 'wrong process format: restriction should be place after the final states') def check_tutorial(self, args): """check the validity of the line""" if len(args) == 1: if not args[0] in self._tutorial_opts: self.help_tutorial() raise self.InvalidCmd('Invalid argument for tutorial') elif len(args) == 0: #this means mg5 tutorial args.append('MadGraph5') else: self.help_tutorial() raise self.InvalidCmd('Too many arguments for tutorial') def check_import(self, args): """check the validity of line""" modelname = False prefix = True if '-modelname' in args: args.remove('-modelname') modelname = True elif '--modelname' in args: args.remove('--modelname') modelname = True if '--noprefix' in args: args.remove('--noprefix') prefix = False if not args: self.help_import() raise self.InvalidCmd('wrong \"import\" format') if len(args) >= 2 and args[0] not in self._import_formats: self.help_import() raise self.InvalidCmd('wrong \"import\" format') elif len(args) == 1: if args[0] in self._import_formats: if args[0] != "proc_v4": self.help_import() raise self.InvalidCmd('wrong \"import\" format') elif not self._export_dir: self.help_import() raise self.InvalidCmd('PATH is mandatory in the current context\n' + \ 'Did you forget to run the \"output\" command') # The type of the import is not given -> guess it format = self.find_import_type(args[0]) logger.info('The import format was not given, so we guess it as %s' % format) args.insert(0, format) if self.history[-1].startswith('import'): self.history[-1] = 'import %s %s' % \ (format, ' '.join(self.history[-1].split()[1:])) if not prefix: args.append('--noprefix') if modelname: args.append('-modelname') def check_install(self, args): """check that the install command is valid""" install_options = {'options_for_HEPToolsInstaller':[], 'update_options':[]} hidden_prog = ['Delphes2', 'pythia-pgs'] if len(args) < 1: self.help_install() raise self.InvalidCmd('install command require at least one argument') if len(args) > 1: for arg in args[1:]: try: option, value = arg.split('=') except ValueError: option = arg value = None # Options related to the MadGraph installer can be treated here, i.e if args[0]=='update': if value is None: install_options['update_options'].append(option) else: install_options['update_options'].append('='.join([option,value])) else: # Other options will be directly added to the call to HEPToolsInstallers # in the advanced_install function install_options['options_for_HEPToolsInstaller'].append(arg) # Now that the options have been treated keep only the target tool # to install as argument. args = args[:1] if args[0] not in self._install_opts + hidden_prog: if not args[0].startswith('td'): self.help_install() raise self.InvalidCmd('Not recognize program %s ' % args[0]) if args[0] in ["ExRootAnalysis", "Delphes", "Delphes2"]: if not misc.which('root'): raise self.InvalidCmd( '''In order to install ExRootAnalysis, you need to install Root on your computer first. please follow information on http://root.cern.ch/drupal/content/downloading-root''') if 'ROOTSYS' not in os.environ: raise self.InvalidCmd( '''The environment variable ROOTSYS is not configured. You can set it by adding the following lines in your .bashrc [.bash_profile for mac]: export ROOTSYS=%s export PATH=$PATH:$ROOTSYS/bin export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ROOTSYS/lib export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:$ROOTSYS/lib This will take effect only in a NEW terminal ''' % os.path.realpath(pjoin(misc.which('root'), \ os.path.pardir, os.path.pardir))) return install_options def check_launch(self, args, options): """check the validity of the line""" # modify args in order to be MODE DIR # mode being either standalone or madevent if not( 0 <= int(options.cluster) <= 2): return self.InvalidCmd, 'cluster mode should be between 0 and 2' if not args: if self._done_export: mode = self.find_output_type(self._done_export[0]) if (self._done_export[1] == 'plugin' and mode not in self._export_formats): args.append(mode) args.append(self._done_export[0]) elif self._done_export[1].startswith(mode): args.append(self._done_export[1]) args.append(self._done_export[0]) else: raise self.InvalidCmd, \ '%s not valid directory for launch' % self._done_export[0] return else: logger.warning('output command missing, run it automatically (with default argument)') self.do_output('') logger.warning('output done: running launch') return self.check_launch(args, options) if len(args) != 1: self.help_launch() return self.InvalidCmd, 'Invalid Syntax: Too many argument' # search for a valid path if os.path.isdir(args[0]): path = os.path.realpath(args[0]) elif os.path.isdir(pjoin(MG5DIR,args[0])): path = pjoin(MG5DIR,args[0]) elif MG4DIR and os.path.isdir(pjoin(MG4DIR,args[0])): path = pjoin(MG4DIR,args[0]) else: raise self.InvalidCmd, '%s is not a valid directory' % args[0] mode = self.find_output_type(path) args[0] = mode args.append(path) # inform where we are for future command self._done_export = [path, mode] def find_import_type(self, path): """ identify the import type of a given path valid output: model/model_v4/proc_v4/command""" possibility = [pjoin(MG5DIR,'models',path), \ pjoin(MG5DIR,'models',path+'_v4'), path] if '-' in path: name = path.rsplit('-',1)[0] possibility = [pjoin(MG5DIR,'models',name), name] + possibility # Check if they are a valid directory for name in possibility: if os.path.isdir(name): if os.path.exists(pjoin(name,'particles.py')): return 'model' elif os.path.exists(pjoin(name,'particles.dat')): return 'model_v4' # Not valid directory so maybe a file if os.path.isfile(path): text = open(path).read() pat = re.compile('(Begin process|)', re.I) matches = pat.findall(text) if not matches: return 'command' elif len(matches) > 1: return 'banner' elif matches[0].lower() == 'begin process': return 'proc_v4' else: return 'banner' else: return 'proc_v4' def find_output_type(self, path): """ identify the type of output of a given directory: valid output: madevent/standalone/standalone_cpp""" card_path = pjoin(path,'Cards') bin_path = pjoin(path,'bin') src_path = pjoin(path,'src') include_path = pjoin(path,'include') subproc_path = pjoin(path,'SubProcesses') mw_path = pjoin(path,'Source','MadWeight') if os.path.isfile(pjoin(include_path, 'Pythia.h')) or \ os.path.isfile(pjoin(include_path, 'Pythia8', 'Pythia.h')): return 'pythia8' elif not os.path.isdir(os.path.join(path, 'SubProcesses')): raise self.InvalidCmd, '%s : Not a valid directory' % path if os.path.isdir(src_path): return 'standalone_cpp' elif os.path.isdir(mw_path): return 'madweight' elif os.path.isfile(pjoin(bin_path,'madevent')): return 'madevent' elif os.path.isfile(pjoin(bin_path,'aMCatNLO')): return 'aMC@NLO' elif os.path.isdir(card_path): return 'standalone' raise self.InvalidCmd, '%s : Not a valid directory' % path def check_load(self, args): """ check the validity of the line""" if len(args) != 2 or args[0] not in self._save_opts: self.help_load() raise self.InvalidCmd('wrong \"load\" format') def check_customize_model(self, args): """check the validity of the line""" # Check argument validity if len(args) >1 : self.help_customize_model() raise self.InvalidCmd('No argument expected for this command') if len(args): if not args[0].startswith('--save='): self.help_customize_model() raise self.InvalidCmd('Wrong argument for this command') if '-' in args[0][6:]: raise self.InvalidCmd('The name given in save options can\'t contain \'-\' symbol.') if self._model_v4_path: raise self.InvalidCmd('Restriction of Model is not supported by v4 model.') def check_save(self, args): """ check the validity of the line""" if len(args) == 0: args.append('options') if args[0] not in self._save_opts and args[0] != 'global': self.help_save() raise self.InvalidCmd('wrong \"save\" format') elif args[0] == 'global': args.insert(0, 'options') if args[0] != 'options' and len(args) != 2: self.help_save() raise self.InvalidCmd('wrong \"save\" format') elif args[0] != 'options' and len(args) == 2: basename = os.path.dirname(args[1]) if not os.path.exists(basename): raise self.InvalidCmd('%s is not a valid path, please retry' % \ args[1]) if args[0] == 'options': has_path = None for arg in args[1:]: if arg in ['--auto', '--all'] or arg in self.options: continue elif arg.startswith('--'): raise self.InvalidCmd('unknow command for \'save options\'') elif arg == 'global': if os.environ.has_key('HOME'): args.remove('global') args.insert(1,pjoin(os.environ['HOME'],'.mg5','mg5_configuration.txt')) has_path = True else: basename = os.path.dirname(arg) if not os.path.exists(basename): raise self.InvalidCmd('%s is not a valid path, please retry' % \ arg) elif has_path: raise self.InvalidCmd('only one path is allowed') else: args.remove(arg) args.insert(1, arg) has_path = True if not has_path: args.insert(1, pjoin(MG5DIR,'input','mg5_configuration.txt')) def check_set(self, args, log=True): """ check the validity of the line""" if len(args) == 1 and args[0] in ['complex_mass_scheme',\ 'loop_optimized_output',\ 'loop_color_flows',\ 'low_mem_multicore_nlo_generation']: args.append('True') if len(args) > 2 and '=' == args[1]: args.pop(1) if len(args) < 2: self.help_set() raise self.InvalidCmd('set needs an option and an argument') if args[1] == 'default': if args[0] in self.options_configuration: default = self.options_configuration[args[0]] elif args[0] in self.options_madgraph: default = self.options_madgraph[args[0]] elif args[0] in self.options_madevent: default = self.options_madevent[args[0]] else: raise self.InvalidCmd('%s doesn\'t have a valid default value' % args[0]) if log: logger.info('Pass parameter %s to it\'s default value: %s' % (args[0], default)) args[1] = str(default) if args[0] not in self._set_options: if not args[0] in self.options and not args[0] in self.options: self.help_set() raise self.InvalidCmd('Possible options for set are %s' % \ self._set_options) if args[0] in ['group_subprocesses']: if args[1] not in ['False', 'True', 'Auto']: raise self.InvalidCmd('%s needs argument False, True or Auto' % \ args[0]) if args[0] in ['ignore_six_quark_processes']: if args[1] not in self._multiparticles.keys() and args[1] != 'False': raise self.InvalidCmd('ignore_six_quark_processes needs ' + \ 'a multiparticle name as argument') if args[0] in ['stdout_level']: if args[1] not in ['DEBUG','INFO','WARNING','ERROR','CRITICAL'] and \ not args[1].isdigit(): raise self.InvalidCmd('output_level needs ' + \ 'a valid level') if args[0] in ['timeout', 'max_npoint_for_channel']: if not args[1].isdigit(): raise self.InvalidCmd('%s values should be a integer' % args[0]) if args[0] in ['loop_optimized_output', 'loop_color_flows', 'low_mem_multicore_nlo_generation']: try: args[1] = banner_module.ConfigFile.format_variable(args[1], bool, args[0]) except Exception: raise self.InvalidCmd('%s needs argument True or False'%args[0]) if args[0] in ['gauge']: if args[1] not in ['unitary','Feynman']: raise self.InvalidCmd('gauge needs argument unitary or Feynman.') if args[0] in ['timeout']: if not args[1].isdigit(): raise self.InvalidCmd('timeout values should be a integer') if args[0] in ['OLP']: if args[1] not in MadGraphCmd._OLP_supported: raise self.InvalidCmd('OLP value should be one of %s'\ %str(MadGraphCmd._OLP_supported)) if args[0].lower() in ['ewscheme']: if not self._curr_model: raise self.InvalidCmd("ewscheme acts on the current model please load one first.") if args[1] not in ['external']: raise self.InvalidCmd('Only valid ewscheme is "external". To restore default, please re-import the model.') if args[0] in ['output_dependencies']: if args[1] not in MadGraphCmd._output_dependencies_supported: raise self.InvalidCmd('output_dependencies value should be one of %s'\ %str(MadGraphCmd._output_dependencies_supported)) def check_open(self, args): """ check the validity of the line """ if len(args) != 1: self.help_open() raise self.InvalidCmd('OPEN command requires exactly one argument') if args[0].startswith('./'): if not os.path.isfile(args[0]): raise self.InvalidCmd('%s: not such file' % args[0]) return True # if special : create the path. if not self._done_export: if not os.path.isfile(args[0]): self.help_open() raise self.InvalidCmd('No command \"output\" or \"launch\" used. Impossible to associate this name to a file') else: return True path = self._done_export[0] if os.path.isfile(pjoin(path,args[0])): args[0] = pjoin(path,args[0]) elif os.path.isfile(pjoin(path,'Cards',args[0])): args[0] = pjoin(path,'Cards',args[0]) elif os.path.isfile(pjoin(path,'HTML',args[0])): args[0] = pjoin(path,'HTML',args[0]) # special for card with _default define: copy the default and open it elif '_card.dat' in args[0]: name = args[0].replace('_card.dat','_card_default.dat') if os.path.isfile(pjoin(path,'Cards', name)): files.cp(path + '/Cards/' + name, path + '/Cards/'+ args[0]) args[0] = pjoin(path,'Cards', args[0]) else: raise self.InvalidCmd('No default path for this file') elif not os.path.isfile(args[0]): raise self.InvalidCmd('No default path for this file') def check_output(self, args, default='madevent'): """ check the validity of the line""" if args and args[0] in self._export_formats: self._export_format = args.pop(0) elif args: # check for PLUGIN format for plugpath in self.plugin_path: plugindirname = os.path.basename(plugpath) for plug in os.listdir(plugpath): if os.path.exists(pjoin(plugpath, plug, '__init__.py')): try: __import__('%s.%s' % (plugindirname,plug)) except Exception, error: logger.warning("error detected in plugin: %s.", plug) logger.warning("%s", error) continue plugin = sys.modules['%s.%s' % (plugindirname,plug)] if hasattr(plugin, 'new_output'): if not misc.is_plugin_supported(plugin): continue if args[0] in plugin.new_output: self._export_format = 'plugin' self._export_plugin = plugin.new_output[args[0]] logger.info('Output will be done with PLUGIN: %s' % plug ,'$MG:color:BLACK') args.pop(0) break else: continue break else: self._export_format = default else: self._export_format = default if not self._curr_model: text = 'No model found. Please import a model first and then retry.' raise self.InvalidCmd(text) if self._model_v4_path and \ (self._export_format not in self._v4_export_formats): text = " The Model imported (MG4 format) does not contain enough\n " text += " information for this type of output. In order to create\n" text += " output for " + args[0] + ", you have to use a UFO model.\n" text += " Those model can be imported with MG5> import model NAME." logger.warning(text) raise self.InvalidCmd('') if self._export_format == 'aloha': return if not self._curr_amps: text = 'No processes generated. Please generate a process first.' raise self.InvalidCmd(text) if args and args[0][0] != '-': # This is a path path = args.pop(0) forbiden_chars = ['>','<',';','&'] for char in forbiden_chars: if char in path: raise self.InvalidCmd('%s is not allowed in the output path' % char) # Check for special directory treatment if path == 'auto' and self._export_format in \ ['madevent', 'standalone', 'standalone_cpp', 'matchbox_cpp', 'madweight', 'matchbox', 'plugin']: self.get_default_path() if '-noclean' not in args and os.path.exists(self._export_dir): args.append('-noclean') elif path != 'auto': if path in ['HELAS', 'tests', 'MadSpin', 'madgraph', 'mg5decay', 'vendor']: if os.getcwd() == MG5DIR: raise self.InvalidCmd, "This name correspond to a buildin MG5 directory. Please choose another name" self._export_dir = path elif path == 'auto': if self.options['pythia8_path']: self._export_dir = self.options['pythia8_path'] else: self._export_dir = '.' else: if self._export_format != 'pythia8': # No valid path self.get_default_path() if '-noclean' not in args and os.path.exists(self._export_dir): args.append('-noclean') else: if self.options['pythia8_path']: self._export_dir = self.options['pythia8_path'] else: self._export_dir = '.' self._export_dir = os.path.realpath(self._export_dir) def check_compute_widths(self, args): """ check and format calculate decay width: Expected format: NAME [other names] [--options] # fill the options if not present. # NAME can be either (anti-)particle name, multiparticle, pid """ if len(args)<1: self.help_compute_widths() raise self.InvalidCmd('''compute_widths requires at least the name of one particle. If you want to compute the width of all particles, type \'compute_widths all\'''') particles = set() options = {'path':None, 'output':None, 'min_br':None, 'body_decay':4.0025, 'precision_channel':0.01, 'nlo':False} # check that the firsts argument is valid for i,arg in enumerate(args): if arg.startswith('--'): if arg.startswith('--nlo'): options['nlo'] =True continue elif not '=' in arg: raise self.InvalidCmd('Options required an equal (and then the value)') arg, value = arg.split('=') if arg[2:] not in options: raise self.InvalidCmd('%s not valid options' % arg) options[arg[2:]] = value continue # check for pid if arg.isdigit(): p = self._curr_model.get_particle(int(arg)) if not p: raise self.InvalidCmd('Model doesn\'t have pid %s for any particle' % arg) particles.add(abs(int(arg))) elif arg in self._multiparticles: particles.update([abs(id) for id in self._multiparticles[args[0]]]) else: if not self._curr_model['case_sensitive']: arg = arg.lower() for p in self._curr_model['particles']: if p['name'] == arg or p['antiname'] == arg: particles.add(abs(p.get_pdg_code())) break else: if arg == 'all': #sometimes the multiparticle all is not define particles.update([abs(p.get_pdg_code()) for p in self._curr_model['particles']]) else: raise self.InvalidCmd('%s invalid particle name' % arg) if options['path'] and not os.path.isfile(options['path']): if os.path.exists(pjoin(MG5DIR, options['path'])): options['path'] = pjoin(MG5DIR, options['path']) elif self._model_v4_path and os.path.exists(pjoin(self._model_v4_path, options['path'])): options['path'] = pjoin(self._curr_model_v4_path, options['path']) elif os.path.exists(pjoin(self._curr_model.path, options['path'])): options['path'] = pjoin(self._curr_model.path, options['path']) if os.path.isdir(options['path']) and os.path.isfile(pjoin(options['path'], 'param_card.dat')): options['path'] = pjoin(options['path'], 'param_card.dat') elif not os.path.isfile(options['path']): raise self.InvalidCmd('%s is not a valid path' % args[2]) # check that the path is indeed a param_card: if madevent_interface.MadEventCmd.detect_card_type(options['path']) != 'param_card.dat': raise self.InvalidCmd('%s should be a path to a param_card' % options['path']) if not options['path']: param_card_text = self._curr_model.write_param_card() if not options['output']: dirpath = self._curr_model.get('modelpath') options['path'] = pjoin(dirpath, 'param_card.dat') else: options['path'] = options['output'] ff = open(options['path'],'w') ff.write(param_card_text) ff.close() if not options['output']: options['output'] = options['path'] if not options['min_br']: options['min_br'] = (float(options['body_decay']) % 1) / 5 return particles, options check_decay_diagram = check_compute_widths def get_default_path(self): """Set self._export_dir to the default (\'auto\') path""" if self._export_format in ['madevent', 'standalone']: # Detect if this script is launched from a valid copy of the Template, # if so store this position as standard output directory if 'TemplateVersion.txt' in os.listdir('.'): #Check for ./ self._export_dir = os.path.realpath('.') return elif 'TemplateVersion.txt' in os.listdir('..'): #Check for ../ self._export_dir = os.path.realpath('..') return elif self.stdin != sys.stdin: #Check for position defined by the input files input_path = os.path.realpath(self.stdin.name).split(os.path.sep) print "Not standard stdin, use input path" if input_path[-2] == 'Cards': self._export_dir = os.path.sep.join(input_path[:-2]) if 'TemplateVersion.txt' in self._export_dir: return if self._export_format == 'NLO': name_dir = lambda i: 'PROCNLO_%s_%s' % \ (self._curr_model['name'], i) auto_path = lambda i: pjoin(self.writing_dir, name_dir(i)) elif self._export_format.startswith('madevent'): name_dir = lambda i: 'PROC_%s_%s' % \ (self._curr_model['name'], i) auto_path = lambda i: pjoin(self.writing_dir, name_dir(i)) elif self._export_format.startswith('standalone'): name_dir = lambda i: 'PROC_SA_%s_%s' % \ (self._curr_model['name'], i) auto_path = lambda i: pjoin(self.writing_dir, name_dir(i)) elif self._export_format == 'madweight': name_dir = lambda i: 'PROC_MW_%s_%s' % \ (self._curr_model['name'], i) auto_path = lambda i: pjoin(self.writing_dir, name_dir(i)) elif self._export_format == 'standalone_cpp': name_dir = lambda i: 'PROC_SA_CPP_%s_%s' % \ (self._curr_model['name'], i) auto_path = lambda i: pjoin(self.writing_dir, name_dir(i)) elif self._export_format in ['matchbox_cpp', 'matchbox']: name_dir = lambda i: 'PROC_MATCHBOX_%s_%s' % \ (self._curr_model['name'], i) auto_path = lambda i: pjoin(self.writing_dir, name_dir(i)) elif self._export_format in ['plugin']: name_dir = lambda i: 'PROC_PLUGIN_%s_%s' % \ (self._curr_model['name'], i) auto_path = lambda i: pjoin(self.writing_dir, name_dir(i)) elif self._export_format == 'pythia8': if self.options['pythia8_path']: self._export_dir = self.options['pythia8_path'] else: self._export_dir = '.' return else: self._export_dir = '.' return for i in range(500): if os.path.isdir(auto_path(i)): continue else: self._export_dir = auto_path(i) break if not self._export_dir: raise self.InvalidCmd('Can\'t use auto path,' + \ 'more than 500 dirs already') #=============================================================================== # CheckValidForCmdWeb #=============================================================================== class CheckValidForCmdWeb(CheckValidForCmd): """ Check the validity of input line for web entry (no explicit path authorized)""" class WebRestriction(MadGraph5Error): """class for WebRestriction""" def check_draw(self, args): """check the validity of line syntax: draw FILEPATH [option=value] """ raise self.WebRestriction('direct call to draw is forbidden on the web') def check_display(self, args): """ check the validity of line in web mode """ if args[0] == 'mg5_variable': raise self.WebRestriction('Display internal variable is forbidden on the web') CheckValidForCmd.check_history(self, args) def check_check(self, args): """ Not authorize for the Web""" raise self.WebRestriction('Check call is forbidden on the web') def check_history(self, args): """check the validity of line No Path authorize for the Web""" CheckValidForCmd.check_history(self, args) if len(args) == 2 and args[1] not in ['.', 'clean']: raise self.WebRestriction('Path can\'t be specify on the web.') def check_import(self, args): """check the validity of line No Path authorize for the Web""" if not args: raise self.WebRestriction, 'import requires at least one option' if args[0] not in self._import_formats: args[:] = ['command', './proc_card_mg5.dat'] elif args[0] == 'proc_v4': args[:] = [args[0], './proc_card.dat'] elif args[0] == 'command': args[:] = [args[0], './proc_card_mg5.dat'] CheckValidForCmd.check_import(self, args) def check_install(self, args): """ No possibility to install new software on the web """ if args == ['update','--mode=mg5_start']: return raise self.WebRestriction('Impossible to install program on the cluster') def check_load(self, args): """ check the validity of the line No Path authorize for the Web""" CheckValidForCmd.check_load(self, args) if len(args) == 2: if args[0] != 'model': raise self.WebRestriction('only model can be loaded online') if 'model.pkl' not in args[1]: raise self.WebRestriction('not valid pkl file: wrong name') if not os.path.realpath(args[1]).startswith(pjoin(MG4DIR, \ 'Models')): raise self.WebRestriction('Wrong path to load model') def check_save(self, args): """ not authorize on web""" raise self.WebRestriction('\"save\" command not authorize online') def check_open(self, args): """ not authorize on web""" raise self.WebRestriction('\"open\" command not authorize online') def check_output(self, args, default='madevent'): """ check the validity of the line""" # first pass to the default CheckValidForCmd.check_output(self, args, default=default) args[:] = ['.', '-f'] self._export_dir = os.path.realpath(os.getcwd()) # Check that we output madevent if 'madevent' != self._export_format: raise self.WebRestriction, 'only available output format is madevent (at current stage)' #=============================================================================== # CompleteForCmd #=============================================================================== class CompleteForCmd(cmd.CompleteCmd): """ The Series of help routine for the MadGraphCmd""" def nlo_completion(self,args,text,line,allowed_loop_mode=None): """ complete the nlo settings within square brackets. It uses the allowed_loop_mode for the proposed mode if specified, otherwise, it uses self._nlo_modes_for_completion""" # We are now editing the loop related options # Automatically allow for QCD perturbation if in the sm because the # loop_sm would then automatically be loaded nlo_modes = allowed_loop_mode if not allowed_loop_mode is None else \ self._nlo_modes_for_completion if isinstance(self._curr_model,loop_base_objects.LoopModel): pert_couplings_allowed = ['all']+self._curr_model['perturbation_couplings'] else: pert_couplings_allowed = [] if self._curr_model.get('name').startswith('sm'): pert_couplings_allowed = pert_couplings_allowed + ['QCD'] # Find wether the loop mode is already set or not loop_specs = line[line.index('[')+1:] try: loop_orders = loop_specs[loop_specs.index('=')+1:] except ValueError: loop_orders = loop_specs possibilities = [] possible_orders = [order for order in pert_couplings_allowed if \ order not in loop_orders] # Simplify obvious loop completion single_completion = '' if len(nlo_modes)==1: single_completion = '%s= '%nlo_modes[0] if len(possible_orders)==1: single_completion = single_completion + possible_orders[0] + ' ] ' # Automatically add a space if not present after [ or = if text.endswith('['): if single_completion != '': return self.list_completion(text, ['[ '+single_completion]) else: return self.list_completion(text,['[ ']) if text.endswith('='): return self.list_completion(text,[' ']) if args[-1]=='[': possibilities = possibilities + ['%s= '%mode for mode in nlo_modes] if single_completion != '': return self.list_completion(text, [single_completion]) else: if len(possible_orders)==1: return self.list_completion(text, [poss+' %s ] '%\ possible_orders[0] for poss in possibilities]) return self.list_completion(text, possibilities) if len(possible_orders)==1: possibilities.append(possible_orders[0]+' ] ') else: possibilities.extend(possible_orders) if any([(order in loop_orders) for order in pert_couplings_allowed]): possibilities.append(']') return self.list_completion(text, possibilities) def model_completion(self, text, process, line, categories = True, \ allowed_loop_mode = None, formatting=True): """ complete the line with model information. If categories is True, it will use completion with categories. If allowed_loop_mode is specified, it will only complete with these loop modes.""" # First check if we are within squared brackets so that specific # input for NLO settings must be completed args = self.split_arg(process) if len(args) > 2 and '>' in line and '[' in line and not ']' in line: return self.nlo_completion(args,text,line, allowed_loop_mode = \ allowed_loop_mode) while ',' in process: process = process[process.index(',')+1:] args = self.split_arg(process) couplings = [] # Do no complete the @ for the process number. if len(args) > 1 and args[-1]=='@': return # Automatically allow for QCD perturbation if in the sm because the # loop_sm would then automatically be loaded if isinstance(self._curr_model,loop_base_objects.LoopModel): pert_couplings_allowed = ['all'] + self._curr_model['perturbation_couplings'] else: pert_couplings_allowed = [] if self._curr_model.get('name').startswith('sm'): pert_couplings_allowed = pert_couplings_allowed + ['QCD'] # Remove possible identical names particles = list(set(self._particle_names + self._multiparticles.keys())) n_part_entered = len([1 for a in args if a in particles]) # Force '>' if two initial particles. if n_part_entered == 2 and args[-1] != '>': return self.list_completion(text, '>') # Add non-particle names syntax = [] couplings = [] if len(args) > 0 and args[-1] != '>' and n_part_entered > 0: syntax.append('>') if '>' in args and args.index('>') < len(args) - 1: couplings.extend(sum([[c+"<=", c+"==", c+">",c+'^2<=',c+'^2==',c+'^2>' ] for c in \ self._couplings+['WEIGHTED']],[])) syntax.extend(['@','$','/','>',',']) if '[' not in line and ',' not in line and len(pert_couplings_allowed)>0: syntax.append('[') # If information for the virtuals has been specified already, do not # propose syntax or particles input anymore if '[' in line: syntax = [] particles = [] # But still allow for defining the process id couplings.append('@') if not categories: # The direct completion (might be needed for some completion using # this function but adding some other completions (like in check)). # For those, it looks ok in the categorie mode on my mac, but if # someone sees wierd result on Linux systems, then use the # default completion for these features. return self.list_completion(text, particles+syntax+couplings) else: # A more elaborate one with categories poss_particles = self.list_completion(text, particles) poss_syntax = self.list_completion(text, syntax) poss_couplings = self.list_completion(text, couplings) possibilities = {} if poss_particles != []: possibilities['Particles']=poss_particles if poss_syntax != []: possibilities['Syntax']=poss_syntax if poss_couplings != []: possibilities['Coupling orders']=poss_couplings if len(possibilities.keys())==1: return self.list_completion(text, possibilities.values()[0]) else: return self.deal_multiple_categories(possibilities, formatting) def complete_generate(self, text, line, begidx, endidx, formatting=True): "Complete the generate command" # Return list of particle names and multiparticle names, as well as # coupling orders and allowed symbols args = self.split_arg(line[0:begidx]) valid_sqso_operators=['==','<=','>'] if any(line.endswith('^2 %s '%op) for op in valid_sqso_operators): return if args[-1].endswith('^2'): return self.list_completion(text,valid_sqso_operators) match_op = [o for o in valid_sqso_operators if o.startswith(args[-1])] if len(args)>2 and args[-2].endswith('^2') and len(match_op)>0: if args[-1] in valid_sqso_operators: return self.list_completion(text,' ') if len(match_op)==1: return self.list_completion(text,[match_op[0][len(args[-1]):]]) else: return self.list_completion(text,match_op) if len(args) > 2 and args[-1] == '@' or ( args[-1].endswith('=') and \ (not '[' in line or ('[' in line and ']' in line))): return try: return self.model_completion(text, ' '.join(args[1:]),line, formatting) except Exception as error: print error #if len(args) > 1 and args[-1] != '>': # couplings = ['>'] #if '>' in args and args.index('>') < len(args) - 1: # couplings = [c + "=" for c in self._couplings] + ['@','$','/','>'] #return self.list_completion(text, self._particle_names + \ # self._multiparticles.keys() + couplings) def complete_compute_widths(self, text, line, begidx, endidx,formatting=True): "Complete the compute_widths command" args = self.split_arg(line[0:begidx]) if args[-1] in ['--path=', '--output=']: completion = {'path': self.path_completion(text)} elif line[begidx-1] == os.path.sep: current_dir = pjoin(*[a for a in args if a.endswith(os.path.sep)]) if current_dir.startswith('--path='): current_dir = current_dir[7:] if current_dir.startswith('--output='): current_dir = current_dir[9:] completion = {'path': self.path_completion(text, current_dir)} else: completion = {} completion['options'] = self.list_completion(text, ['--path=', '--output=', '--min_br=0.\$', '--precision_channel=0.\$', '--body_decay=', '--nlo']) completion['particles'] = self.model_completion(text, '', line) return self.deal_multiple_categories(completion,formatting) complete_decay_diagram = complete_compute_widths def complete_add(self, text, line, begidx, endidx, formatting): "Complete the add command" args = self.split_arg(line[0:begidx]) # Format if len(args) == 1: return self.list_completion(text, self._add_opts) if args[1] == 'process': return self.complete_generate(text, " ".join(args[1:]), begidx, endidx) elif args[1] == 'model': completion_categories = self.complete_import(text, line, begidx, endidx, allow_restrict=False, formatting=False) completion_categories['options'] = self.list_completion(text,['--modelname=','--recreate']) return self.deal_multiple_categories(completion_categories, formatting) def complete_customize_model(self, text, line, begidx, endidx): "Complete the customize_model command" args = self.split_arg(line[0:begidx]) # Format if len(args) == 1: return self.list_completion(text, ['--save=']) def complete_check(self, text, line, begidx, endidx, formatting=True): "Complete the check command" out = {} args = self.split_arg(line[0:begidx]) # Format if len(args) == 1: return self.list_completion(text, self._check_opts) cms_check_mode = len(args) >= 2 and args[1]=='cms' cms_options = ['--name=','--tweak=','--seed=','--offshellness=', '--lambdaCMS=','--show_plot=','--report=','--lambda_plot_range=','--recompute_width=', '--CTModeRun=','--helicity=','--reduction=','--cms=','--diff_lambda_power=', '--loop_filter=','--resonances='] options = ['--energy='] if cms_options: options.extend(cms_options) # Directory continuation if args[-1].endswith(os.path.sep): return self.path_completion(text, pjoin(*[a for a in args \ if a.endswith(os.path.sep)])) # autocompletion for particles/couplings model_comp = self.model_completion(text, ' '.join(args[2:]),line, categories = True, allowed_loop_mode=['virt']) model_comp_and_path = self.deal_multiple_categories(\ {'Process completion': self.model_completion(text, ' '.join(args[2:]), line, categories = False, allowed_loop_mode=['virt']), 'Param_card.dat path completion:':self.path_completion(text), 'options': self.list_completion(text,options)}, formatting) #Special rules for check cms completion if cms_check_mode: # A couple of useful value completions if line[-1]!=' ' and line[-2]!='\\' and not '--' in line[begidx:endidx] \ and args[-1].startswith('--') and '=' in args[-1]: examples = { '--tweak=': ['default','alltweaks',"['default','allwidths->1.1*all_withds&seed333(Increased_widths_and_seed_333)','logp->logm&logm->logp(inverted_logs)']"], '--lambdaCMS=': ['(1.0e-2,5)',"[float('1.0e-%d'%exp)\\ for\\ exp\\ in\\ range(8)]","[1.0,0.5,0.001]"], '--lambda_plot_range=': [' [1e-05,1e-02]','[0.01,1.0]'], '--reduction=': ['1','1|2|3|4','1|2','3'], '--cms=': ['QED&QCD,aewm1->10.0/lambdaCMS&as->0.1*lambdaCMS', 'NP&QED&QCD,aewm1->10.0/lambdaCMS&as->0.1*lambdaCMS&newExpansionParameter->newExpansionParameter*lambdaCMS'], '--loop_filter=': ['None','n>3','n<4 and 6 in loop_pdgs and 3<=id<=7'], '--resonances=': ['1','all','(24,(3,4))','[(24,(3,4)),(24,(4,5))]'], '--analyze=': ['my_default_run.pkl', 'default_run.pkl,increased_widths.pkl(Increased_widths),logs_modified.pkl(Inverted_logs),seed_668.pkl(Different_seed)'] } for name, example in examples.items(): if args[-1].startswith(name): return self.deal_multiple_categories( {"Examples of completion for option '%s'"%args[-1].split('=')[0]: # ['%d: %s'%(i+1,ex) for i, ex in enumerate(example)]}, ['%s'%ex for i, ex in enumerate(example)]},formatting, forceCategory=True) if args[-1]=='--recompute_width=': return self.list_completion(text, ['never','first_time','always','auto']) elif args[-1]=='--show_plot=': return self.list_completion(text,['True','False']) elif args[-1]=='--report=': return self.list_completion(text,['concise','full']) elif args[-1]=='--CTModeRun=': return self.list_completion(text,['-1','1','2','3','4']) else: return text if len(args)==2 or len(args)==3 and args[-1]=='-reuse': return self.deal_multiple_categories( {'Process completion': self.model_completion(text, ' '.join(args[2:]), line, categories = False, allowed_loop_mode=['virt']), 'Param_card.dat path completion:': self.path_completion(text), 'reanalyze result on disk / save output:':self.list_completion( text,['-reuse','--analyze='])}, formatting) elif not any(arg.startswith('--') for arg in args): if '>' in args: return self.deal_multiple_categories({'Process completion': self.model_completion(text, ' '.join(args[2:]), line, categories = False, allowed_loop_mode=['virt']), 'options': self.list_completion(text,options)}, formatting) else: return self.deal_multiple_categories({'Process completion': self.model_completion(text, ' '.join(args[2:]), line, categories = False, allowed_loop_mode=['virt'])}, formatting) else: return self.list_completion(text,options) if len(args) == 2: return model_comp_and_path elif len(args) == 3: try: int(args[2]) except ValueError: return model_comp else: return model_comp_and_path elif len(args) > 3: return model_comp def complete_tutorial(self, text, line, begidx, endidx): "Complete the tutorial command" # Format if len(self.split_arg(line[0:begidx])) == 1: return self.list_completion(text, self._tutorial_opts) def complete_define(self, text, line, begidx, endidx): """Complete particle information""" return self.model_completion(text, line[6:],line) def complete_display(self, text, line, begidx, endidx): "Complete the display command" args = self.split_arg(line[0:begidx]) # Format if len(args) == 1: return self.list_completion(text, self._display_opts) if len(args) == 2 and args[1] == 'checks': return self.list_completion(text, ['failed']) if len(args) == 2 and args[1] == 'particles': return self.model_completion(text, line[begidx:],line) def complete_draw(self, text, line, begidx, endidx): "Complete the draw command" args = self.split_arg(line[0:begidx]) # Directory continuation if args[-1].endswith(os.path.sep): return self.path_completion(text, pjoin(*[a for a in args if a.endswith(os.path.sep)]), only_dirs = True) # Format if len(args) == 1: return self.path_completion(text, '.', only_dirs = True) #option if len(args) >= 2: opt = ['horizontal', 'external=', 'max_size=', 'add_gap=', 'non_propagating', '--'] return self.list_completion(text, opt) def complete_launch(self, text, line, begidx, endidx,formatting=True): """ complete the launch command""" args = self.split_arg(line[0:begidx]) # Directory continuation if args[-1].endswith(os.path.sep): return self.path_completion(text, pjoin(*[a for a in args if a.endswith(os.path.sep)]), only_dirs = True) # Format if len(args) == 1: out = {'Path from ./': self.path_completion(text, '.', only_dirs = True)} if MG5DIR != os.path.realpath('.'): out['Path from %s' % MG5DIR] = self.path_completion(text, MG5DIR, only_dirs = True, relative=False) if MG4DIR and MG4DIR != os.path.realpath('.') and MG4DIR != MG5DIR: out['Path from %s' % MG4DIR] = self.path_completion(text, MG4DIR, only_dirs = True, relative=False) #option if len(args) >= 2: out={} if line[0:begidx].endswith('--laststep='): opt = ['parton', 'pythia', 'pgs','delphes','auto'] out['Options'] = self.list_completion(text, opt, line) else: opt = ['--cluster', '--multicore', '-i', '--name=', '-f','-m', '-n', '-p','--parton','--interactive', '--laststep=parton', '--laststep=pythia', '--laststep=pgs', '--laststep=delphes','--laststep=auto'] out['Options'] = self.list_completion(text, opt, line) return self.deal_multiple_categories(out,formatting) def complete_load(self, text, line, begidx, endidx): "Complete the load command" args = self.split_arg(line[0:begidx]) # Format if len(args) == 1: return self.list_completion(text, self._save_opts) # Directory continuation if args[-1].endswith(os.path.sep): return self.path_completion(text, pjoin(*[a for a in args if \ a.endswith(os.path.sep)])) # Filename if directory is not given if len(args) == 2: return self.path_completion(text) def complete_save(self, text, line, begidx, endidx): "Complete the save command" args = self.split_arg(line[0:begidx]) # Format if len(args) == 1: return self.list_completion(text, self._save_opts) # Directory continuation if args[-1].endswith(os.path.sep): return self.path_completion(text, pjoin(*[a for a in args if a.endswith(os.path.sep)]), only_dirs = True) # Filename if directory is not given if len(args) == 2: return self.path_completion(text) + self.list_completion(text, ['global']) @cmd.debug() def complete_open(self, text, line, begidx, endidx): """ complete the open command """ args = self.split_arg(line[0:begidx]) # Directory continuation if os.path.sep in args[-1] + text: return self.path_completion(text, pjoin(*[a for a in args if \ a.endswith(os.path.sep)])) possibility = [] if self._done_export: path = self._done_export[0] possibility = ['index.html'] if os.path.isfile(pjoin(path,'README')): possibility.append('README') if os.path.isdir(pjoin(path,'Cards')): possibility += [f for f in os.listdir(pjoin(path,'Cards')) if f.endswith('.dat')] if os.path.isdir(pjoin(path,'HTML')): possibility += [f for f in os.listdir(pjoin(path,'HTML')) if f.endswith('.html') and 'default' not in f] else: possibility.extend(['./','../']) if os.path.exists('MG5_debug'): possibility.append('MG5_debug') if os.path.exists('ME5_debug'): possibility.append('ME5_debug') return self.list_completion(text, possibility) @cmd.debug() def complete_output(self, text, line, begidx, endidx, possible_options = ['f', 'noclean', 'nojpeg'], possible_options_full = ['-f', '-noclean', '-nojpeg']): "Complete the output command" possible_format = self._export_formats #don't propose directory use by MG_ME forbidden_names = ['MadGraphII', 'Template', 'pythia-pgs', 'CVS', 'Calculators', 'MadAnalysis', 'SimpleAnalysis', 'mg5', 'DECAY', 'EventConverter', 'Models', 'ExRootAnalysis', 'HELAS', 'Transfer_Fct', 'aloha', 'matchbox', 'matchbox_cpp', 'tests'] #name of the run =>proposes old run name args = self.split_arg(line[0:begidx]) if len(args) >= 1: if len(args) > 1 and args[1] == 'pythia8': possible_options_full = list(possible_options_full) + ['--version=8.1','--version=8.2'] if len(args) > 1 and args[1] == 'aloha': try: return self.aloha_complete_output(text, line, begidx, endidx) except Exception, error: print error # Directory continuation if args[-1].endswith(os.path.sep): return [name for name in self.path_completion(text, pjoin(*[a for a in args if a.endswith(os.path.sep)]), only_dirs = True) if name not in forbidden_names] # options if args[-1][0] == '-' or len(args) > 1 and args[-2] == '-': return self.list_completion(text, possible_options) if len(args) > 2: return self.list_completion(text, possible_options_full) # Formats if len(args) == 1: format = possible_format + ['.' + os.path.sep, '..' + os.path.sep, 'auto'] return self.list_completion(text, format) # directory names content = [name for name in self.path_completion(text, '.', only_dirs = True) \ if name not in forbidden_names] content += ['auto'] content += possible_options_full return self.list_completion(text, content) def aloha_complete_output(self, text, line, begidx, endidx,formatting=True): "Complete the output aloha command" args = self.split_arg(line[0:begidx]) completion_categories = {} forbidden_names = ['MadGraphII', 'Template', 'pythia-pgs', 'CVS', 'Calculators', 'MadAnalysis', 'SimpleAnalysis', 'mg5', 'DECAY', 'EventConverter', 'Models', 'ExRootAnalysis', 'Transfer_Fct', 'aloha', 'apidoc','vendor'] # options options = ['--format=Fortran', '--format=Python','--format=gpu','--format=CPP','--output='] options = self.list_completion(text, options) if options: completion_categories['options'] = options if args[-1] == '--output=' or args[-1].endswith(os.path.sep): # Directory continuation completion_categories['path'] = [name for name in self.path_completion(text, pjoin(*[a for a in args if a.endswith(os.path.sep)]), only_dirs = True) if name not in forbidden_names] else: ufomodel = ufomodels.load_model(self._curr_model.get('name')) wf_opt = [] amp_opt = [] opt_conjg = [] for lor in ufomodel.all_lorentz: amp_opt.append('%s_0' % lor.name) for i in range(len(lor.spins)): wf_opt.append('%s_%i' % (lor.name,i+1)) if i % 2 == 0 and lor.spins[i] == 2: opt_conjg.append('%sC%i_%i' % (lor.name,i //2 +1,i+1)) completion_categories['amplitude routines'] = self.list_completion(text, amp_opt) completion_categories['Wavefunctions routines'] = self.list_completion(text, wf_opt) completion_categories['conjugate_routines'] = self.list_completion(text, opt_conjg) return self.deal_multiple_categories(completion_categories,formatting) def complete_set(self, text, line, begidx, endidx): "Complete the set command" #misc.sprint([text,line,begidx, endidx]) args = self.split_arg(line[0:begidx]) # Format if len(args) == 1: opts = list(set(self.options.keys() + self._set_options)) return self.list_completion(text, opts) if len(args) == 2: if args[1] in ['group_subprocesses', 'complex_mass_scheme',\ 'loop_optimized_output', 'loop_color_flows',\ 'low_mem_multicore_nlo_generation']: return self.list_completion(text, ['False', 'True', 'default']) elif args[1] in ['ignore_six_quark_processes']: return self.list_completion(text, self._multiparticles.keys()) elif args[1].lower() == 'ewscheme': return self.list_completion(text, ["external"]) elif args[1] == 'gauge': return self.list_completion(text, ['unitary', 'Feynman','default']) elif args[1] == 'OLP': return self.list_completion(text, MadGraphCmd._OLP_supported) elif args[1] == 'output_dependencies': return self.list_completion(text, MadGraphCmd._output_dependencies_supported) elif args[1] == 'stdout_level': return self.list_completion(text, ['DEBUG','INFO','WARNING','ERROR', 'CRITICAL','default']) elif args[1] == 'fortran_compiler': return self.list_completion(text, ['f77','g77','gfortran','default']) elif args[1] == 'cpp_compiler': return self.list_completion(text, ['g++', 'c++', 'clang', 'default']) elif args[1] == 'nb_core': return self.list_completion(text, [str(i) for i in range(100)] + ['default'] ) elif args[1] == 'run_mode': return self.list_completion(text, [str(i) for i in range(3)] + ['default']) elif args[1] == 'cluster_type': return self.list_completion(text, cluster.from_name.keys() + ['default']) elif args[1] == 'cluster_queue': return [] elif args[1] == 'automatic_html_opening': return self.list_completion(text, ['False', 'True', 'default']) else: # directory names second_set = [name for name in self.path_completion(text, '.', only_dirs = True)] return self.list_completion(text, second_set + ['default']) elif len(args) >2 and args[-1].endswith(os.path.sep): return self.path_completion(text, pjoin(*[a for a in args if a.endswith(os.path.sep)]), only_dirs = True) def complete_import(self, text, line, begidx, endidx, allow_restrict=True, formatting=True): "Complete the import command" args=self.split_arg(line[0:begidx]) # Format if len(args) == 1: opt = self.list_completion(text, self._import_formats) if opt: return opt mode = 'all' elif args[1] in self._import_formats: mode = args[1] else: args.insert(1, 'all') mode = 'all' completion_categories = {} # restriction continuation (for UFO) if mode in ['model', 'all'] and '-' in text: # deal with - in readline splitting (different on some computer) path = '-'.join([part for part in text.split('-')[:-1]]) # remove the final - for the model name # find the different possibilities all_name = self.find_restrict_card(path, no_restrict=False) all_name += self.find_restrict_card(path, no_restrict=False, base_dir=pjoin(MG5DIR,'models')) if os.environ['PYTHONPATH']: for modeldir in os.environ['PYTHONPATH'].split(':'): if not modeldir: continue all_name += self.find_restrict_card(path, no_restrict=False, base_dir=modeldir) # select the possibility according to the current line all_name = [name+' ' for name in all_name if name.startswith(text) and name.strip() != text] if all_name: completion_categories['Restricted model'] = all_name # Path continuation if os.path.sep in args[-1]: if mode.startswith('model') or mode == 'all': # Directory continuation try: cur_path = pjoin(*[a for a in args \ if a.endswith(os.path.sep)]) except Exception: pass else: all_dir = self.path_completion(text, cur_path, only_dirs = True) if mode in ['model_v4','all']: completion_categories['Path Completion'] = all_dir # Only UFO model here new = [] data = [new.__iadd__(self.find_restrict_card(name, base_dir=cur_path)) for name in all_dir] if data: completion_categories['Path Completion'] = all_dir + new else: try: cur_path = pjoin(*[a for a in args \ if a.endswith(os.path.sep)]) except Exception: pass else: all_path = self.path_completion(text, cur_path) if mode == 'all': new = [] data = [new.__iadd__(self.find_restrict_card(name, base_dir=cur_path)) for name in all_path] if data: completion_categories['Path Completion'] = data[0] else: completion_categories['Path Completion'] = all_path # Model directory name if directory is not given if (len(args) == 2): is_model = True if mode == 'model': file_cond = lambda p : os.path.exists(pjoin(MG5DIR,'models',p,'particles.py')) mod_name = lambda name: name elif mode == 'model_v4': file_cond = lambda p : (os.path.exists(pjoin(MG5DIR,'models',p,'particles.dat')) or os.path.exists(pjoin(self._mgme_dir,'Models',p,'particles.dat'))) mod_name = lambda name :(name[-3:] != '_v4' and name or name[:-3]) elif mode == 'all': mod_name = lambda name: name file_cond = lambda p : os.path.exists(pjoin(MG5DIR,'models',p,'particles.py')) \ or os.path.exists(pjoin(MG5DIR,'models',p,'particles.dat')) \ or os.path.exists(pjoin(self._mgme_dir,'Models',p,'particles.dat')) else: cur_path = pjoin(*[a for a in args \ if a.endswith(os.path.sep)]) all_path = self.path_completion(text, cur_path) completion_categories['model name'] = all_path is_model = False if is_model: model_list = [mod_name(name) for name in \ self.path_completion(text, pjoin(MG5DIR,'models'), only_dirs = True) \ if file_cond(name)] if mode == 'model' and 'PYTHONPATH' in os.environ: for modeldir in os.environ['PYTHONPATH'].split(':'): if not modeldir: continue model_list += [name for name in self.path_completion(text, modeldir, only_dirs=True) if os.path.exists(pjoin(modeldir,name, 'particles.py'))] if mode == 'model_v4': completion_categories['model name'] = model_list elif allow_restrict: # need to update the list with the possible restriction all_name = [] for model_name in model_list: all_name += self.find_restrict_card(model_name, base_dir=pjoin(MG5DIR,'models')) else: all_name = model_list if mode == 'all': cur_path = pjoin(*[a for a in args \ if a.endswith(os.path.sep)]) all_path = self.path_completion(text, cur_path) completion_categories['model name'] = all_path + all_name elif mode == 'model': completion_categories['model name'] = all_name # Options if mode == 'all' and len(args)>1: mode = self.find_import_type(args[2]) if len(args) >= 3 and mode.startswith('model') and not '-modelname' in line: if not text and not completion_categories: return ['--modelname'] elif not (os.path.sep in args[-1] and line[-1] != ' '): completion_categories['options'] = self.list_completion(text, ['--modelname','-modelname','--noprefix']) if len(args) >= 3 and mode.startswith('banner') and not '--no_launch' in line: completion_categories['options'] = self.list_completion(text, ['--no_launch']) return self.deal_multiple_categories(completion_categories,formatting) def find_restrict_card(self, model_name, base_dir='./', no_restrict=True): """find the restriction file associate to a given model""" # check if the model_name should be keeped as a possibility if no_restrict: output = [model_name] else: output = [] # check that the model is a valid model if not os.path.exists(pjoin(base_dir, model_name, 'couplings.py')): # not valid UFO model return output if model_name.endswith(os.path.sep): model_name = model_name[:-1] # look for _default and treat this case if os.path.exists(pjoin(base_dir, model_name, 'restrict_default.dat')): output.append('%s-full' % model_name) # look for other restrict_file for name in os.listdir(pjoin(base_dir, model_name)): if name.startswith('restrict_') and not name.endswith('default.dat') \ and name.endswith('.dat'): tag = name[9:-4] #remove restrict and .dat while model_name.endswith(os.path.sep): model_name = model_name[:-1] output.append('%s-%s' % (model_name, tag)) # return return output def complete_install(self, text, line, begidx, endidx): "Complete the import command" args = self.split_arg(line[0:begidx]) # Format if len(args) == 1: return self.list_completion(text, self._install_opts) elif len(args) and args[0] == 'update': return self.list_completion(text, ['-f','--timeout=']) elif len(args)>=2 and args[1] in self._advanced_install_opts: options = ['--keep_source','--logging='] if args[1]=='pythia8': options.append('--pythia8_tarball=') elif args[1]=='mg5amc_py8_interface': options.append('--mg5amc_py8_interface_tarball=') elif args[1] in ['MadAnalysis5','MadAnalysis']: #options.append('--no_MA5_further_install') options.append('--no_root_in_MA5') options.append('--update') options.append('--madanalysis5_tarball=') for prefix in ['--with', '--veto']: for prog in ['fastjet', 'delphes', 'delphesMA5tune']: options.append('%s_%s' % (prefix, prog)) for opt in options[:]: if any(a.startswith(opt) for a in args): options.remove(opt) return self.list_completion(text, options) else: return self.list_completion(text, []) #=============================================================================== # MadGraphCmd #=============================================================================== class MadGraphCmd(HelpToCmd, CheckValidForCmd, CompleteForCmd, CmdExtended): """The command line processor of MadGraph""" writing_dir = '.' # Options and formats available _display_opts = ['particles', 'interactions', 'processes', 'diagrams', 'diagrams_text', 'multiparticles', 'couplings', 'lorentz', 'checks', 'parameters', 'options', 'coupling_order','variable'] _add_opts = ['process', 'model'] _save_opts = ['model', 'processes', 'options'] _tutorial_opts = ['aMCatNLO', 'stop', 'MadLoop', 'MadGraph5'] _switch_opts = ['mg5','aMC@NLO','ML5'] _check_opts = ['full', 'timing', 'stability', 'profile', 'permutation', 'gauge','lorentz', 'brs', 'cms'] _import_formats = ['model_v4', 'model', 'proc_v4', 'command', 'banner'] _install_opts = ['Delphes', 'MadAnalysis4', 'ExRootAnalysis', 'update', 'SysCalc', 'Golem95', 'PJFry', 'QCDLoop', 'maddm'] # The targets below are installed using the HEPToolsInstaller.py script _advanced_install_opts = ['pythia8','zlib','boost','lhapdf6','lhapdf5','collier', 'hepmc','mg5amc_py8_interface','ninja','oneloop','MadAnalysis5','MadAnalysis'] _install_opts.extend(_advanced_install_opts) _v4_export_formats = ['madevent', 'standalone', 'standalone_msP','standalone_msF', 'matrix', 'standalone_rw', 'madweight'] _export_formats = _v4_export_formats + ['standalone_cpp', 'pythia8', 'aloha', 'matchbox_cpp', 'matchbox'] _set_options = ['group_subprocesses', 'ignore_six_quark_processes', 'stdout_level', 'fortran_compiler', 'cpp_compiler', 'loop_optimized_output', 'complex_mass_scheme', 'gauge', 'EWscheme', 'max_npoint_for_channel'] _valid_nlo_modes = ['all','real','virt','sqrvirt','tree','noborn','LOonly'] _valid_sqso_types = ['==','<=','=','>'] _valid_amp_so_types = ['=','<=', '==', '>'] _OLP_supported = ['MadLoop', 'GoSam'] _output_dependencies_supported = ['external', 'internal','environment_paths'] # The three options categories are treated on a different footage when a # set/save configuration occur. current value are kept in self.options options_configuration = {'pythia8_path': './HEPTools/pythia8', 'hwpp_path': './herwigPP', 'thepeg_path': './thepeg', 'hepmc_path': './hepmc', 'madanalysis_path': './MadAnalysis', 'madanalysis5_path':'./HEPTools/madanalysis5/madanalysis5', 'pythia-pgs_path':'./pythia-pgs', 'td_path':'./td', 'delphes_path':'./Delphes', 'exrootanalysis_path':'./ExRootAnalysis', 'syscalc_path': './SysCalc', 'timeout': 60, 'web_browser':None, 'eps_viewer':None, 'text_editor':None, 'fortran_compiler':None, 'f2py_compiler':None, 'cpp_compiler':None, 'auto_update':7, 'cluster_type': 'condor', 'cluster_queue': None, 'cluster_status_update': (600, 30), 'fastjet':'fastjet-config', 'pjfry':'auto', 'golem':'auto', 'samurai':None, 'ninja':'./HEPTools/lib', 'collier':'./HEPTools/lib', 'lhapdf':'lhapdf-config', 'applgrid':'applgrid-config', 'amcfast':'amcfast-config', 'cluster_temp_path':None, 'mg5amc_py8_interface_path': './HEPTools/MG5aMC_PY8_interface', 'cluster_local_path': None, 'OLP': 'MadLoop', 'cluster_nb_retry':1, 'cluster_retry_wait':300, 'cluster_size':100, 'output_dependencies':'external', 'crash_on_error':False } options_madgraph= {'group_subprocesses': 'Auto', 'ignore_six_quark_processes': False, 'low_mem_multicore_nlo_generation': False, 'complex_mass_scheme': False, 'gauge':'unitary', 'stdout_level':None, 'loop_optimized_output':True, 'loop_color_flows':False, 'max_npoint_for_channel': 0 # 0 means automaticly adapted } options_madevent = {'automatic_html_opening':True, 'run_mode':2, 'nb_core': None, 'notification_center': True } # Variables to store object information _curr_model = None #base_objects.Model() _curr_amps = diagram_generation.AmplitudeList() _curr_proc_defs = base_objects.ProcessDefinitionList() _curr_matrix_elements = helas_objects.HelasMultiProcess() _curr_helas_model = None _curr_exporter = None _done_export = False _curr_decaymodel = None helporder = ['Main commands', 'Documented commands'] def preloop(self): """Initializing before starting the main loop""" self.prompt = 'MG5_aMC>' if madgraph.ReadWrite: # prevent on read-only disk self.do_install('update --mode=mg5_start') # By default, load the UFO Standard Model logger.info("Loading default model: sm") self.exec_cmd('import model sm', printcmd=False, precmd=True) # preloop mother CmdExtended.preloop(self) def __init__(self, mgme_dir = '', *completekey, **stdin): """ add a tracker of the history """ CmdExtended.__init__(self, *completekey, **stdin) # Set MG/ME directory path if mgme_dir: if os.path.isdir(pjoin(mgme_dir, 'Template')): self._mgme_dir = mgme_dir logger.info('Setting MG/ME directory to %s' % mgme_dir) else: logger.warning('Warning: Directory %s not valid MG/ME directory' % \ mgme_dir) self._mgme_dir = MG4DIR # check that make_opts exists make_opts = pjoin(MG5DIR, 'Template','LO','Source','make_opts') make_opts_source = pjoin(MG5DIR, 'Template','LO','Source','.make_opts') if not os.path.exists(make_opts): shutil.copy(make_opts_source, make_opts) elif os.path.getmtime(make_opts) < os.path.getmtime(make_opts_source): shutil.copy(make_opts_source, make_opts) # Variables to store state information self._multiparticles = {} self.options = {} self._generate_info = "" # store the first generated process self._model_v4_path = None self._export_dir = None self._export_format = 'madevent' self._mgme_dir = MG4DIR self._cuttools_dir=str(os.path.join(self._mgme_dir,'vendor','CutTools')) self._iregi_dir=str(os.path.join(self._mgme_dir,'vendor','IREGI','src')) self._comparisons = None self._cms_checks = [] self._nlo_modes_for_completion = ['all','virt','real','LOonly'] # Load the configuration file,i.e.mg5_configuration.txt self.set_configuration() def setup(self): """ Actions to carry when switching to this interface """ # Refresh all the interface stored value as things like generated # processes and amplitudes are not to be reused in between different # interfaces # Clear history, amplitudes and matrix elements when a model is imported # Remove previous imports, generations and outputs from history self.history.clean(remove_bef_last='import',keep_switch=True) # Reset amplitudes and matrix elements self._done_export=False self._curr_amps = diagram_generation.AmplitudeList() self._curr_proc_defs = base_objects.ProcessDefinitionList() self._curr_matrix_elements = helas_objects.HelasMultiProcess() self._v4_export_formats = ['madevent', 'standalone','standalone_msP','standalone_msF', 'matrix', 'standalone_rw'] self._export_formats = self._v4_export_formats + ['standalone_cpp', 'pythia8'] self._nlo_modes_for_completion = ['all','virt','real'] def do_quit(self, line): """Not in help: Do quit""" if self._done_export and \ os.path.exists(pjoin(self._done_export[0],'RunWeb')): os.remove(pjoin(self._done_export[0],'RunWeb')) value = super(MadGraphCmd, self).do_quit(line) if madgraph.ReadWrite: #prevent to run on Read Only disk self.do_install('update --mode=mg5_end') print misc.EasterEgg('quit') return value # Add a process to the existing multiprocess definition # Generate a new amplitude def do_add(self, line): """Generate an amplitude for a given process and add to existing amplitudes or merge two model """ args = self.split_arg(line) warning_duplicate = True if '--no_warning=duplicate' in args: warning_duplicate = False args.remove('--no_warning=duplicate') diagram_filter = False if '--diagram_filter' in args: diagram_filter = True args.remove('--diagram_filter') standalone_only = False if '--standalone' in args: standalone_only = True args.remove('--standalone') # Check the validity of the arguments self.check_add(args) if args[0] == 'model': return self.add_model(args[1:]) # special option for 1->N to avoid generation of kinematically forbidden #decay. if args[-1].startswith('--optimize'): optimize = True args.pop() else: optimize = False if args[0] == 'process': # Rejoin line line = ' '.join(args[1:]) # store the first process (for the perl script) if not self._generate_info: self._generate_info = line # Reset Helas matrix elements self._curr_matrix_elements = helas_objects.HelasMultiProcess() # Extract process from process definition if ',' in line: if ']' in line or '[' in line: error_msg=\ """The '[' and ']' syntax cannot be used in cunjunction with decay chains. This implies that with decay chains: > Squared coupling order limitations are not available. > Loop corrections cannot be considered.""" raise MadGraph5Error(error_msg) else: nb_proc = len([l for l in self.history if l.startswith(('generate','add process'))]) myprocdef, line = self.extract_decay_chain_process(line, proc_number=nb_proc) # Redundant with above, but not completely as in the future # one might think of allowing the core process to be # corrected by loops. if myprocdef.are_decays_perturbed(): raise MadGraph5Error("Decay processes cannot be perturbed.") # The two limitations below have some redundancy, but once # again, they might be relieved (one at a time or together) # int he future. if myprocdef.decays_have_squared_orders() or \ myprocdef['squared_orders']!={}: raise MadGraph5Error("Decay processes cannot specify "+\ "squared orders constraints.") if myprocdef.are_negative_orders_present(): raise MadGraph5Error("Decay processes cannot include negative"+\ " coupling orders constraints.") else: nb_proc = len([l for l in self.history if l.startswith(('generate','add process'))]) myprocdef = self.extract_process(line, proc_number=nb_proc) # Check that we have something if not myprocdef: raise self.InvalidCmd("Empty or wrong format process, please try again.") # Check that we have the same number of initial states as # existing processes if self._curr_amps and self._curr_amps[0].get_ninitial() != \ myprocdef.get_ninitial() and not standalone_only: raise self.InvalidCmd("Can not mix processes with different number of initial states.") self._curr_proc_defs.append(myprocdef) # Negative coupling order contraints can be given on at most one # coupling order (and either in squared orders or orders, not both) if len([1 for val in myprocdef.get('orders').values()+\ myprocdef.get('squared_orders').values() if val<0])>1: raise MadGraph5Error("Negative coupling order constraints"+\ " can only be given on one type of coupling and either on"+\ " squared orders or amplitude orders, not both.") cpu_time1 = time.time() # Generate processes if self.options['group_subprocesses'] == 'Auto': collect_mirror_procs = True else: collect_mirror_procs = self.options['group_subprocesses'] ignore_six_quark_processes = \ self.options['ignore_six_quark_processes'] if \ "ignore_six_quark_processes" in self.options \ else [] myproc = diagram_generation.MultiProcess(myprocdef, collect_mirror_procs = collect_mirror_procs, ignore_six_quark_processes = ignore_six_quark_processes, optimize=optimize, diagram_filter=diagram_filter) for amp in myproc.get('amplitudes'): if amp not in self._curr_amps: self._curr_amps.append(amp) elif warning_duplicate: raise self.InvalidCmd, "Duplicate process %s found. Please check your processes." % \ amp.nice_string_processes() # Reset _done_export, since we have new process self._done_export = False cpu_time2 = time.time() nprocs = len(myproc.get('amplitudes')) ndiags = sum([amp.get_number_of_diagrams() for \ amp in myproc.get('amplitudes')]) logger.info("%i processes with %i diagrams generated in %0.3f s" % \ (nprocs, ndiags, (cpu_time2 - cpu_time1))) ndiags = sum([amp.get_number_of_diagrams() for \ amp in self._curr_amps]) logger.info("Total: %i processes with %i diagrams" % \ (len(self._curr_amps), ndiags)) def add_model(self, args): """merge two model""" model_path = args[0] recreate = ('--recreate' in args) keep_decay = ('--keep_decay' in args) output_dir = [a.split('=',1)[1] for a in args if a.startswith('--output')] if output_dir: output_dir = output_dir[0] recreate = True restrict_name = '' else: name = os.path.basename(self._curr_model.get('modelpath')) restrict_name = self._curr_model.get('restrict_name') output_dir = pjoin(MG5DIR, 'models', '%s__%s' % (name, os.path.basename(model_path))) if os.path.exists(output_dir): if recreate: shutil.rmtree(output_dir) else: logger.info('Model already created! Loading it from %s' % output_dir) oldmodel = self._curr_model.get('modelpath') new_model_name = output_dir if restrict_name: new_model_name = '%s-%s' % (output_dir, restrict_name) try: self.exec_cmd('import model %s' % new_model_name, errorhandling=False, printcmd=False, precmd=True, postcmd=True) except Exception, error: logger.debug('fail to load model %s with error:\n %s' % (output_dir, error)) logger.warning('Fail to load the model. Restore previous model') self.exec_cmd('import model %s' % oldmodel, errorhandling=False, printcmd=False, precmd=True, postcmd=True) raise Exception('Invalid Model! Please retry with the option \'--recreate\'.') else: return #Need to do the work!!! import models.usermod as usermod base_model = copy.deepcopy(usermod.UFOModel(self._curr_model.get('modelpath'))) identify = dict(tuple(a.split('=')) for a in args if '=' in a) base_model.add_model(path=model_path, identify_particles=identify) base_model.write(output_dir) if keep_decay and os.path.exists(pjoin(self._curr_model.get('modelpath'), 'decays.py')): base_model.mod_file(pjoin(pjoin(self._curr_model.get('modelpath'), 'decays.py')), pjoin(pjoin(output_dir, 'decays.py'))) new_model_name = output_dir if restrict_name: new_model_name = '%s-%s' % (output_dir, restrict_name) if 'modelname' in self.history.get('full_model_line'): opts = '--modelname' else: opts='' self.exec_cmd('import model %s %s' % (new_model_name, opts), errorhandling=False, printcmd=False, precmd=True, postcmd=True) # Define a multiparticle label def do_define(self, line, log=True): """Define a multiparticle""" self.avoid_history_duplicate('define %s' % line, ['define']) if not self._curr_model: self.do_import('model sm') self.history.append('define %s' % line) if not self._curr_model['case_sensitive']: # Particle names lowercase line = line.lower() # Make sure there are spaces around =, | and / line = line.replace("=", " = ") line = line.replace("|", " | ") line = line.replace("/", " / ") args = self.split_arg(line) # check the validity of the arguments self.check_define(args) label = args[0] remove_ids = [] try: remove_index = args.index("/") except ValueError: pass else: remove_ids = args[remove_index + 1:] args = args[:remove_index] pdg_list = self.extract_particle_ids(args[1:]) remove_list = self.extract_particle_ids(remove_ids) pdg_list = [p for p in pdg_list if p not in remove_list] self.optimize_order(pdg_list) self._multiparticles[label] = pdg_list if log: logger.info("Defined multiparticle %s" % \ self.multiparticle_string(label)) # Display def do_display(self, line, output=sys.stdout): """Display current internal status""" args = self.split_arg(line) #check the validity of the arguments self.check_display(args) if args[0] == 'diagrams': self.draw(' '.join(args[1:])) if args[0] == 'particles' and len(args) == 1: propagating_particle = [] nb_unpropagating = 0 for particle in self._curr_model['particles']: if particle.get('propagating'): propagating_particle.append(particle) else: nb_unpropagating += 1 print "Current model contains %i particles:" % \ len(propagating_particle) part_antipart = [part for part in propagating_particle \ if not part['self_antipart']] part_self = [part for part in propagating_particle \ if part['self_antipart']] for part in part_antipart: print part['name'] + '/' + part['antiname'], print '' for part in part_self: print part['name'], print '' if nb_unpropagating: print 'In addition of %s un-physical particle mediating new interactions.' \ % nb_unpropagating elif args[0] == 'particles': for arg in args[1:]: if arg.isdigit() or (arg[0] == '-' and arg[1:].isdigit()): particle = self._curr_model.get_particle(abs(int(arg))) else: particle = self._curr_model['particles'].find_name(arg) if not particle: raise self.InvalidCmd, 'no particle %s in current model' % arg print "Particle %s has the following properties:" % particle.get_name() print str(particle) elif args[0] == 'interactions' and len(args) == 1: text = "Current model contains %i interactions\n" % \ len(self._curr_model['interactions']) for i, inter in enumerate(self._curr_model['interactions']): text += str(i+1) + ':' for part in inter['particles']: if part['is_part']: text += part['name'] else: text += part['antiname'] text += " " text += " ".join(order + '=' + str(inter['orders'][order]) \ for order in inter['orders']) text += '\n' pydoc.pager(text) elif args[0] == 'interactions' and len(args)==2 and args[1].isdigit(): for arg in args[1:]: if int(arg) > len(self._curr_model['interactions']): raise self.InvalidCmd, 'no interaction %s in current model' % arg if int(arg) == 0: print 'Special interactions which identify two particles' else: print "Interactions %s has the following property:" % arg print self._curr_model['interactions'][int(arg)-1] elif args[0] == 'interactions': request_part = args[1:] text = '' for i, inter in enumerate(self._curr_model['interactions']): present_part = [part['is_part'] and part['name'] or part['antiname'] for part in inter['particles'] if (part['is_part'] and part['name'] in request_part) or (not part['is_part'] and part['antiname'] in request_part)] if len(present_part) < len(request_part): continue # check that all particles are selected at least once if set(present_part) != set(request_part): continue # check if a particle is asked more than once if len(request_part) > len(set(request_part)): for p in request_part: if request_part.count(p) > present_part.count(p): continue name = str(i+1) + ' : ' for part in inter['particles']: if part['is_part']: name += part['name'] else: name += part['antiname'] name += " " text += "\nInteractions %s has the following property:\n" % name text += str(self._curr_model['interactions'][i]) text += '\n' print name if text =='': text += 'No matching for any interactions' pydoc.pager(text) elif args[0] == 'parameters' and len(args) == 1: text = "Current model contains %i parameters\n" % \ sum([len(part) for part in self._curr_model['parameters'].values()]) keys = self._curr_model['parameters'].keys() def key_sort(x, y): if ('external',) == x: return -1 elif ('external',) == y: return +1 elif len(x) < len(y): return -1 else: return 1 keys.sort(key_sort) for key in keys: item = self._curr_model['parameters'][key] text += '\nparameter type: %s\n' % str(key) for value in item: if hasattr(value, 'expr'): if value.value is not None: text+= ' %s = %s = %s\n' % (value.name, value.expr ,value.value) else: text+= ' %s = %s\n' % (value.name, value.expr) else: if value.value is not None: text+= ' %s = %s\n' % (value.name, value.value) else: text+= ' %s \n' % (value.name) pydoc.pager(text) elif args[0] == 'processes': for amp in self._curr_amps: print amp.nice_string_processes() elif args[0] == 'diagrams_text': text = "\n".join([amp.nice_string() for amp in self._curr_amps]) pydoc.pager(text) elif args[0] == 'multiparticles': print 'Multiparticle labels:' for key in self._multiparticles: print self.multiparticle_string(key) elif args[0] == 'coupling_order': hierarchy = self._curr_model['order_hierarchy'].items() #self._curr_model.get_order_hierarchy().items() def order(first, second): if first[1] < second[1]: return -1 else: return 1 hierarchy.sort(order) for order in hierarchy: print ' %s : weight = %s' % order elif args[0] == 'couplings' and len(args) == 1: if self._model_v4_path: print 'No couplings information available in V4 model' return text = '' text = "Current model contains %i couplings\n" % \ sum([len(part) for part in self._curr_model['couplings'].values()]) keys = self._curr_model['couplings'].keys() def key_sort(x, y): if ('external',) == x: return -1 elif ('external',) == y: return +1 elif len(x) < len(y): return -1 else: return 1 keys.sort(key_sort) for key in keys: item = self._curr_model['couplings'][key] text += '\ncouplings type: %s\n' % str(key) for value in item: if value.value is not None: text+= ' %s = %s = %s\n' % (value.name, value.expr ,value.value) else: text+= ' %s = %s\n' % (value.name, value.expr) pydoc.pager(text) elif args[0] == 'couplings': if self._model_v4_path: print 'No couplings information available in V4 model' return try: ufomodel = ufomodels.load_model(self._curr_model.get('name')) print 'Note that this is the UFO informations.' print ' "display couplings" present the actual definition' print 'prints the current states of mode' print eval('ufomodel.couplings.%s.nice_string()'%args[1]) except Exception: raise self.InvalidCmd, 'no couplings %s in current model' % args[1] elif args[0] == 'lorentz': if self._model_v4_path: print 'No lorentz information available in V4 model' return elif len(args) == 1: raise self.InvalidCmd,\ 'display lorentz require an argument: the name of the lorentz structure.' return try: ufomodel = ufomodels.load_model(self._curr_model.get('name')) print eval('ufomodel.lorentz.%s.nice_string()'%args[1]) except Exception: raise self.InvalidCmd, 'no lorentz %s in current model' % args[1] elif args[0] == 'checks': outstr = '' if self._comparisons: comparisons = self._comparisons[0] if len(args) > 1 and args[1] == 'failed': comparisons = [c for c in comparisons if not c['passed']] outstr += "Process check results:" for comp in comparisons: outstr += "\n%s:" % comp['process'].nice_string() outstr += "\n Phase space point: (px py pz E)" for i, p in enumerate(comp['momenta']): outstr += "\n%2s %+.9e %+.9e %+.9e %+.9e" % tuple([i] + p) outstr += "\n Permutation values:" outstr += "\n " + str(comp['values']) if comp['passed']: outstr += "\n Process passed (rel. difference %.9e)" % \ comp['difference'] else: outstr += "\n Process failed (rel. difference %.9e)" % \ comp['difference'] used_aloha = sorted(self._comparisons[1]) if used_aloha: outstr += "\nChecked ALOHA routines:" for aloha in used_aloha: aloha_str = aloha[0] if aloha[1]: aloha_str += 'C' + 'C'.join([str(ia) for ia in aloha[1]]) aloha_str += "_%d" % aloha[2] outstr += "\n" + aloha_str outstr += '\n' for cms_check in self._cms_checks: outstr += '*'*102+'\n' outstr += 'Complex Mass Scheme check:\n' outstr += ' -> check %s\n'%cms_check['line'] outstr += '*'*102+'\n' tmp_options = copy.copy(cms_check['options']) tmp_options['show_plot']=False outstr += process_checks.output_complex_mass_scheme( cms_check['cms_result'], cms_check['output_path'], tmp_options, self._curr_model) + '\n' outstr += '*'*102+'\n\n' pydoc.pager(outstr) elif args[0] == 'options': if len(args) == 1: to_print = lambda name: True else: to_print = lambda name: any(poss in name for poss in args[1:]) outstr = " MadGraph5_aMC@NLO Options \n" outstr += " ---------------- \n" keys = self.options_madgraph.keys() keys.sort() for key in keys: if not to_print(key): continue default = self.options_madgraph[key] value = self.options[key] if value == default: outstr += " %25s \t:\t%s\n" % (key,value) else: outstr += " %25s \t:\t%s (user set)\n" % (key,value) outstr += "\n" outstr += " MadEvent Options \n" outstr += " ---------------- \n" keys = self.options_madevent.keys() keys.sort() for key in keys: if not to_print(key): continue default = self.options_madevent[key] value = self.options[key] if value == default: outstr += " %25s \t:\t%s\n" % (key,value) else: outstr += " %25s \t:\t%s (user set)\n" % (key,value) outstr += "\n" outstr += " Configuration Options \n" outstr += " --------------------- \n" keys = self.options_configuration.keys() keys.sort() for key in keys: if not to_print(key): continue default = self.options_configuration[key] value = self.options[key] if value == default: outstr += " %25s \t:\t%s\n" % (key,value) else: outstr += " %25s \t:\t%s (user set)\n" % (key,value) output.write(outstr) elif args[0] in ["variable"]: super(MadGraphCmd, self).do_display(line, output) def multiparticle_string(self, key): """Returns a nicely formatted string for the multiparticle""" if self._multiparticles[key] and \ isinstance(self._multiparticles[key][0], list): return "%s = %s" % (key, "|".join([" ".join([self._curr_model.\ get('particle_dict')[part_id].get_name() \ for part_id in id_list]) \ for id_list in self._multiparticles[key]])) else: return "%s = %s" % (key, " ".join([self._curr_model.\ get('particle_dict')[part_id].get_name() \ for part_id in self._multiparticles[key]])) def do_tutorial(self, line): """Activate/deactivate the tutorial mode.""" args = self.split_arg(line) self.check_tutorial(args) tutorials = {'MadGraph5': logger_tuto, 'aMCatNLO': logger_tuto_nlo, 'MadLoop': logger_tuto_madloop} try: tutorials[args[0]].setLevel(logging.INFO) for mode in [m for m in tutorials.keys() if m != args[0]]: tutorials[mode].setLevel(logging.ERROR) except KeyError: logger_tuto.info("\n\tThanks for using the tutorial!") logger_tuto.setLevel(logging.ERROR) logger_tuto_nlo.info("\n\tThanks for using the aMC@NLO tutorial!") logger_tuto_nlo.setLevel(logging.ERROR) logger_tuto_madloop.info("\n\tThanks for using MadLoop tutorial!") logger_tuto_madloop.setLevel(logging.ERROR) if not self._mgme_dir: logger_tuto.info(\ "\n\tWarning: To use all features in this tutorial, " + \ "please run from a" + \ "\n\t valid MG_ME directory.") def draw(self, line,selection='all',type=''): """ draw the Feynman diagram for the given process. Type refers to born, real or loop""" args = self.split_arg(line) # Check the validity of the arguments self.check_draw(args) # Check if we plot a decay chain if any([isinstance(a, diagram_generation.DecayChainAmplitude) for \ a in self._curr_amps]) and not self._done_export: warn = 'WARNING: You try to draw decay chain diagrams without first running output.\n' warn += '\t The decay processes will be drawn separately' logger.warning(warn) (options, args) = _draw_parser.parse_args(args) options = draw_lib.DrawOption(options) start = time.time() # Collect amplitudes amplitudes = diagram_generation.AmplitudeList() for amp in self._curr_amps: amplitudes.extend(amp.get_amplitudes()) for amp in amplitudes: filename = pjoin(args[0], 'diagrams_' + \ amp.get('process').shell_string() + ".eps") if selection=='all' and type != 'loop': diags=amp.get('diagrams') elif selection=='born': diags=amp.get('born_diagrams') elif selection=='loop' or type == 'loop': diags=base_objects.DiagramList([d for d in amp.get('loop_diagrams') if d.get('type')>0]) if len(diags) > 5000: logger.warning('Displaying only the first 5000 diagrams') diags = base_objects.DiagramList(diags[:5000]) plot = draw.MultiEpsDiagramDrawer(diags, filename, model=self._curr_model, amplitude=amp, legend=amp.get('process').input_string(), diagram_type=type) logger.info("Drawing " + \ amp.get('process').nice_string()) plot.draw(opt=options) logger.info("Wrote file " + filename) self.exec_cmd('open %s' % filename) stop = time.time() logger.info('time to draw %s' % (stop - start)) # Perform checks def do_check(self, line): """Check a given process or set of processes""" def create_lambda_values_list(lower_bound, N): """ Returns a list of values spanning the range [1.0, lower_bound] with lower_bound < 1.0 and with each interval [1e-i, 1e-(i+1)] covered by N values uniformly distributed. For example, lower_bound=1e-2 and N=5 returns: [1, 0.8, 0.6, 0.4, 0.2, 0.1, 0.08, 0.06, 0.04, 0.02, 0.01]""" lCMS_values = [1] exp = 0 n = 0 while lCMS_values[-1]>=lower_bound: n = (n+1) lCMS_values.append(float('1.0e-%d'%exp)*((N-n%N)/float(N))) if lCMS_values[-1]==lCMS_values[-2]: lCMS_values.pop() exp = (n+1)//N lCMS_values = lCMS_values[:-1] if lCMS_values[-1]!=lower_bound: lCMS_values.append(lower_bound) return lCMS_values ###### BEGIN do_check args = self.split_arg(line) # Check args validity param_card = self.check_check(args) options= {'events':None} # If the momentum needs to be picked from a event file if param_card and 'banner' == madevent_interface.MadEventCmd.detect_card_type(param_card): logger_check.info("Will use the param_card contained in the banner and the events associated") import madgraph.various.banner as banner options['events'] = param_card mybanner = banner.Banner(param_card) param_card = mybanner.charge_card('param_card') aloha_lib.KERNEL.clean() # Back up the gauge for later gauge = str(self.options['gauge']) options['reuse'] = args[1]=="-reuse" args = args[:1]+args[2:] # For the stability check the user can specify the statistics (i.e # number of trial PS points) as a second argument if args[0] in ['stability', 'profile']: options['npoints'] = int(args[1]) args = args[:1]+args[2:] MLoptions={} i=-1 CMS_options = {} while args[i].startswith('--'): option = args[i].split('=') if option[0] =='--energy': options['energy']=float(option[1]) elif option[0]=='--split_orders': options['split_orders']=int(option[1]) elif option[0]=='--helicity': try: options['helicity']=int(option[1]) except ValueError: raise self.InvalidCmd("The value of the 'helicity' option"+\ " must be an integer, not %s."%option[1]) elif option[0]=='--reduction': MLoptions['MLReductionLib']=[int(ir) for ir in option[1].split('|')] elif option[0]=='--collier_mode': MLoptions['COLLIERMode']=int(option[1]) elif option[0]=='--collier_cache': MLoptions['COLLIERGlobalCache']=int(option[1]) elif option[0]=='--collier_req_acc': if option[1]!='auto': MLoptions['COLLIERRequiredAccuracy']=float(option[1]) elif option[0]=='--collier_internal_stability_test': MLoptions['COLLIERUseInternalStabilityTest']=eval(option[1]) elif option[0]=='--CTModeRun': try: MLoptions['CTModeRun']=int(option[1]) except ValueError: raise self.InvalidCmd("The value of the 'CTModeRun' option"+\ " must be an integer, not %s."%option[1]) elif option[0]=='--offshellness': CMS_options['offshellness'] = float(option[1]) if CMS_options['offshellness']<=-1.0: raise self.InvalidCmd('Offshellness must be number larger or'+ ' equal to -1.0, not %f'%CMS_options['offshellness']) elif option[0]=='--analyze': options['analyze'] = option[1] elif option[0]=='--show_plot': options['show_plot'] = 'true' in option[1].lower() elif option[0]=='--report': options['report'] = option[1].lower() elif option[0]=='--seed': options['seed'] = int(option[1]) elif option[0]=='--name': if '.' in option[1]: raise self.InvalidCmd("Do not specify the extension in the"+ " name of the run") CMS_options['name'] = option[1] elif option[0]=='--resonances': if option[1]=='all': CMS_options['resonances'] = 'all' else: try: resonances=eval(option[1]) except: raise self.InvalidCmd("Could not evaluate 'resonances'"+ " option '%s'"%option[1]) if isinstance(resonances,int) and resonances>0: CMS_options['resonances'] = resonances elif isinstance(resonances,list) and all(len(res)==2 and isinstance(res[0],int) and all(isinstance(i, int) for i in res[1]) for res in resonances): CMS_options['resonances'] = resonances else: raise self.InvalidCmd("The option 'resonances' can only be 'all'"+ " or and integer or a list of tuples of the form "+ "(resPDG,(res_mothers_ID)). You gave '%s'"%option[1]) elif option[0]=='--tweak': # Lists the sets of custom and widths modifications to apply value = option[1] # Set a shortcuts for applying all relevant tweaks if value=='alltweaks': value=str(['default','seed667(seed667)','seed668(seed668)', 'allwidths->0.9*allwidths(widths_x_0.9)', 'allwidths->0.99*allwidths(widths_x_0.99)', 'allwidths->1.01*allwidths(widths_x_1.01)', 'allwidths->1.1*allwidths(widths_x_1.1)', 'logp->logm(logp2logm)','logm->logp(logm2logp)']) try: tweaks = eval(value) if isinstance(tweaks, str): tweaks = [value] elif not isinstance(tweaks,list): tweaks = [value] except: tweaks = [value] if not all(isinstance(t,str) for t in tweaks): raise self.InvalidCmd("Invalid specificaiton of tweaks: %s"%value) CMS_options['tweak'] = [] for tweakID, tweakset in enumerate(tweaks): specs =re.match(r'^(?P.*)\((?P.*)\)$', tweakset) if specs: tweakset = specs.group('tweakset') name = specs.group('name') else: if tweakset!='default': name = 'tweak_%d'%(tweakID+1) else: name = '' new_tweak_set = {'custom':[],'params':{},'name':name} for tweak in tweakset.split('&'): if tweak=='default': continue if tweak.startswith('seed'): new_tweak_set['custom'].append(tweak) continue try: param, replacement = tweak.split('->') except ValueError: raise self.InvalidCmd("Tweak specification '%s'"%\ tweak+" is incorrect. It should be of"+\ " the form a->_any_function_of_(a,lambdaCMS).") if param in ['logp','logm','log'] and \ replacement in ['logp','logm','log']: new_tweak_set['custom'].append(tweak) continue try: # for safety prefix parameters, because 'as' for alphas # is a python reserved name for example orig_param, orig_replacement = param, replacement replacement = replacement.replace(param, '__tmpprefix__%s'%param) param = '__tmpprefix__%s'%param res = float(eval(replacement.lower(), {'lambdacms':1.0,param.lower():98.85})) except: raise self.InvalidCmd("The substitution expression "+ "'%s' for the tweaked parameter"%orig_replacement+ " '%s' could not be evaluated. It must be an "%orig_param+ "expression of the parameter and 'lambdaCMS'.") new_tweak_set['params'][param.lower()] = replacement.lower() CMS_options['tweak'].append(new_tweak_set) elif option[0]=='--recompute_width': if option[1].lower() not in ['never','always','first_time','auto']: raise self.InvalidCmd("The option 'recompute_width' can "+\ "only be 'never','always', 'first_time' or 'auto' (default).") CMS_options['recompute_width'] = option[1] elif option[0]=='--loop_filter': # Specify a loop, filter. See functions get_loop_filter and # user_filter in loop_diagram_generation.LoopAmplitude for # information on usage. CMS_options['loop_filter'] = '='.join(option[1:]) elif option[0]=='--diff_lambda_power': #'secret' option to chose by which lambda power one should divide # the nwa-cms difference. Useful to set to 2 when doing the Born check # to see whether the NLO check will have sensitivity to the CMS # implementation try: CMS_options['diff_lambda_power']=float(option[1]) except ValueError: raise self.InvalidCmd("the '--diff_lambda_power' option"+\ " must be an integer or float, not '%s'."%option[1]) elif option[0]=='--lambda_plot_range': try: plot_range=eval(option[1]) except Exception as e: raise self.InvalidCmd("The plot range specified %s"%option[1]+\ " is not a valid syntax. Error:\n%s"%str(e)) if not isinstance(plot_range,(list,tuple)) or \ len(plot_range)!=2 or any(not isinstance(p,(float,int)) for p in plot_range): raise self.InvalidCmd("The plot range specified %s"\ %option[1]+" is invalid") CMS_options['lambda_plot_range']=list([float(p) for p in plot_range]) elif option[0]=='--lambdaCMS': try: lambda_values = eval(option[1]) except SyntaxError: raise self.InvalidCmd("'%s' is not a correct"%option[1]+ " python expression for lambdaCMS values.") if isinstance(lambda_values,list): if lambda_values[0]!=1.0: raise self.InvalidCmd("The first value of the lambdaCMS values"+ " specified must be 1.0, not %s"%str(lambda_values)) for l in lambda_values: if not isinstance(l,float): raise self.InvalidCmd("All lambda CMS values must be"+ " float, not '%s'"%str(l)) elif isinstance(lambda_values,(tuple,float)): # Format here is then (lower_bound, N) were lower_bound is # the minimum lambdaCMS value that must be probed and the # integer N is the number of such values that must be # uniformly distributed in each intervale [1.0e-i,1.0e-(i+1)] if isinstance(lambda_values, float): # Use default of 10 for the number of lambda values lower_bound = lambda_values N = 10 else: if isinstance(lambda_values[0],float) and \ isinstance(lambda_values[1],int): lower_bound = lambda_values[0] N = lambda_values[1] else: raise self.InvalidCmd("'%s' must be a "%option[1]+ "tuple with types (float, int).") lambda_values = create_lambda_values_list(lower_bound,N) else: raise self.InvalidCmd("'%s' must be an expression"%option[1]+ " for either a float, tuple or list.") lower_bound = lambda_values[-1] # and finally add 5 points for stability test on the last values # Depending on how the stab test will behave at NLO, we can # consider automatically adding the values below # for stab in range(1,6): # lambda_values.append((1.0+(stab/100.0))*lower_bound) CMS_options['lambdaCMS'] = lambda_values elif option[0]=='--cms': try: CMS_expansion_orders, CMS_expansion_parameters = \ option[1].split(',') except ValueError: raise self.InvalidCmd("CMS expansion specification '%s'"%\ args[i]+" is incorrect.") CMS_options['expansion_orders'] = [expansion_order for expansion_order in CMS_expansion_orders.split('&')] CMS_options['expansion_parameters'] = {} for expansion_parameter in CMS_expansion_parameters.split('&'): try: param, replacement = expansion_parameter.split('->') except ValueError: raise self.InvalidCmd("CMS expansion specification '%s'"%\ expansion_parameter+" is incorrect. It should be of"+\ " the form a->_any_function_of_(a,lambdaCMS).") try: # for safety prefix parameters, because 'as' for alphas # is a python reserved name for example orig_param, orig_replacement = param, replacement replacement = replacement.replace(param, '__tmpprefix__%s'%param) param = '__tmpprefix__%s'%param res = float(eval(replacement.lower(), {'lambdacms':1.0,param.lower():98.85})) except: raise self.InvalidCmd("The substitution expression "+ "'%s' for CMS expansion parameter"%orig_replacement+ " '%s' could not be evaluated. It must be an "%orig_param+ "expression of the parameter and 'lambdaCMS'.") # Put everything lower case as it will be done when # accessing model variables CMS_options['expansion_parameters'][param.lower()]=\ replacement.lower() else: raise self.InvalidCmd("The option '%s' is not reckognized."%option[0]) i=i-1 args = args[:i+1] if args[0]=='options': # Simple printout of the check command options logger_check.info("Options for the command 'check' are:") logger_check.info("{:<20} {}".format(' name','default value')) logger_check.info("-"*40) for key, value in options.items(): logger_check.info("{:<20} = {}".format('--%s'%key,str(value))) return if args[0].lower()=='cmsoptions': # Simple printout of the special check cms options logger_check.info("Special options for the command 'check cms' are:") logger_check.info("{:<20} {}".format(' name','default value')) logger_check.info("-"*40) for key, value in CMS_options.items(): logger_check.info("{:<20} = {}".format('--%s'%key,str(value))) return # Set the seed here if not in cms check and if specified if args[0]!='cms' and options['seed']!=-1: # Not necessarily optimal as there could be additional call to # random() as the code develops, but at least it will encompass # everything in this way. logger_check.info('Setting random seed to %d.'%options['seed']) random.seed(options['seed']) proc_line = " ".join(args[1:]) # Don't try to extract the process if just re-analyzing a saved run if not (args[0]=='cms' and options['analyze']!='None'): myprocdef = self.extract_process(proc_line) # Check that we have something if not myprocdef: raise self.InvalidCmd("Empty or wrong format process, please try again.") # For the check command, only the mode 'virt' make sense. if myprocdef.get('NLO_mode')=='all': myprocdef.set('NLO_mode','virt') else: myprocdef = None # If the test has to write out on disk, it should do so at the location # specified below where the user must be sure to have writing access. output_path = os.getcwd() if args[0] in ['timing','stability', 'profile'] and not \ myprocdef.get('perturbation_couplings'): raise self.InvalidCmd("Only loop processes can have their "+ " timings or stability checked.") if args[0]=='gauge' and \ not myprocdef.get('perturbation_couplings') in [[],['QCD']]: raise self.InvalidCmd( """Feynman vs unitary gauge comparisons can only be done if there are no loop propagators affected by this gauge. Typically, either processes at tree level or including only QCD perturbations can be considered here.""") if args[0]=='gauge' and len(self._curr_model.get('gauge')) < 2: raise self.InvalidCmd("The current model does not allow for both "+\ "Feynman and unitary gauge.") # Disable some loggers loggers = [logging.getLogger('madgraph.diagram_generation'), logging.getLogger('madgraph.loop_diagram_generation'), logging.getLogger('ALOHA'), logging.getLogger('madgraph.helas_objects'), logging.getLogger('madgraph.loop_exporter'), logging.getLogger('madgraph.export_v4'), logging.getLogger('cmdprint'), logging.getLogger('madgraph.model'), logging.getLogger('madgraph.base_objects')] old_levels = [log.level for log in loggers] for log in loggers: log.setLevel(logging.WARNING) # run the check cpu_time1 = time.time() # Run matrix element generation check on processes # The aloha python output has trouble when doing (tree level of course) # python output and that loop_mode is True at the beginning. # So as a temporary fix for the problem that after doing a check at NLO # then a check at LO will fail, I make sure I set it to False if the # process is a tree-level one if myprocdef: if myprocdef.get('perturbation_couplings')==[]: aloha.loop_mode = False comparisons = [] gauge_result = [] gauge_result_no_brs = [] lorentz_result =[] nb_processes = 0 timings = [] stability = [] profile_time = [] profile_stab = [] cms_results = [] if "_cuttools_dir" in dir(self): CT_dir = self._cuttools_dir else: CT_dir ="" if "MLReductionLib" in MLoptions: if 1 in MLoptions["MLReductionLib"]: MLoptions["MLReductionLib"].remove(1) # directories for TIR TIR_dir={} if "_iregi_dir" in dir(self): TIR_dir['iregi_dir']=self._iregi_dir else: if "MLReductionLib" in MLoptions: if 3 in MLoptions["MLReductionLib"]: logger_check.warning('IREGI not available on your system; it will be skipped.') MLoptions["MLReductionLib"].remove(3) if 'pjfry' in self.options and isinstance(self.options['pjfry'],str): TIR_dir['pjfry_dir']=self.options['pjfry'] else: if "MLReductionLib" in MLoptions: if 2 in MLoptions["MLReductionLib"]: logger_check.warning('PJFRY not available on your system; it will be skipped.') MLoptions["MLReductionLib"].remove(2) if 'golem' in self.options and isinstance(self.options['golem'],str): TIR_dir['golem_dir']=self.options['golem'] else: if "MLReductionLib" in MLoptions: if 4 in MLoptions["MLReductionLib"]: logger_check.warning('GOLEM not available on your system; it will be skipped.') MLoptions["MLReductionLib"].remove(4) if 'samurai' in self.options and isinstance(self.options['samurai'],str): TIR_dir['samurai_dir']=self.options['samurai'] else: if "MLReductionLib" in MLoptions: if 5 in MLoptions["MLReductionLib"]: logger_check.warning('Samurai not available on your system; it will be skipped.') MLoptions["MLReductionLib"].remove(5) if 'collier' in self.options and isinstance(self.options['collier'],str): TIR_dir['collier_dir']=self.options['collier'] else: if "MLReductionLib" in MLoptions: if 7 in MLoptions["MLReductionLib"]: logger_check.warning('Collier not available on your system; it will be skipped.') MLoptions["MLReductionLib"].remove(7) if 'ninja' in self.options and isinstance(self.options['ninja'],str): TIR_dir['ninja_dir']=self.options['ninja'] else: if "MLReductionLib" in MLoptions: if 6 in MLoptions["MLReductionLib"]: logger_check.warning('Ninja not available on your system; it will be skipped.') MLoptions["MLReductionLib"].remove(6) if args[0] in ['timing']: timings = process_checks.check_timing(myprocdef, param_card = param_card, cuttools=CT_dir, tir=TIR_dir, options = options, cmd = self, output_path = output_path, MLOptions = MLoptions ) if args[0] in ['stability']: stability=process_checks.check_stability(myprocdef, param_card = param_card, cuttools=CT_dir, tir=TIR_dir, options = options, output_path = output_path, cmd = self, MLOptions = MLoptions) if args[0] in ['profile']: # In this case timing and stability will be checked one after the # other without re-generating the process. profile_time, profile_stab = process_checks.check_profile(myprocdef, param_card = param_card, cuttools=CT_dir, tir=TIR_dir, options = options, MLOptions = MLoptions, output_path = output_path, cmd = self) if args[0] in ['gauge', 'full'] and \ len(self._curr_model.get('gauge')) == 2 and\ myprocdef.get('perturbation_couplings') in [[],['QCD']]: line = " ".join(args[1:]) myprocdef = self.extract_process(line) if gauge == 'unitary': myprocdef_unit = myprocdef self.do_set('gauge Feynman', log=False) myprocdef_feyn = self.extract_process(line) else: myprocdef_feyn = myprocdef self.do_set('gauge unitary', log=False) myprocdef_unit = self.extract_process(line) nb_part_unit = len(myprocdef_unit.get('model').get('particles')) nb_part_feyn = len(myprocdef_feyn.get('model').get('particles')) if nb_part_feyn == nb_part_unit: logger_check.error('No Goldstone present for this check!!') gauge_result_no_brs = process_checks.check_unitary_feynman( myprocdef_unit, myprocdef_feyn, param_card = param_card, options=options, cuttools=CT_dir, tir=TIR_dir, reuse = options['reuse'], output_path = output_path, cmd = self) # restore previous settings self.do_set('gauge %s' % gauge, log=False) nb_processes += len(gauge_result_no_brs) if args[0] in ['permutation', 'full']: comparisons = process_checks.check_processes(myprocdef, param_card = param_card, quick = True, cuttools=CT_dir, tir=TIR_dir, reuse = options['reuse'], cmd = self, output_path = output_path, options=options) nb_processes += len(comparisons[0]) if args[0] in ['lorentz', 'full']: myprocdeff = copy.copy(myprocdef) lorentz_result = process_checks.check_lorentz(myprocdeff, param_card = param_card, cuttools=CT_dir, tir=TIR_dir, reuse = options['reuse'], cmd = self, output_path = output_path, options=options) nb_processes += len(lorentz_result) if args[0] in ['brs', 'full']: gauge_result = process_checks.check_gauge(myprocdef, param_card = param_card, cuttools=CT_dir, tir=TIR_dir, reuse = options['reuse'], cmd = self, output_path = output_path, options=options) nb_processes += len(gauge_result) # The CMS check is typically more complicated and slower than others # so we don't run it automatically with 'full'. if args[0] in ['cms']: cms_original_setup = self.options['complex_mass_scheme'] process_line = " ".join(args[1:]) # Merge in the CMS_options to the options for key, value in CMS_options.items(): if key=='tweak': continue if key not in options: options[key] = value else: raise MadGraph5Error,"Option '%s' is both in the option"%key+\ " and CMS_option dictionary." if options['analyze']=='None': cms_results = [] for tweak in CMS_options['tweak']: options['tweak']=tweak # Try to guess the save path and try to load it before running guessed_proc = myprocdef.get_process( [leg.get('ids')[0] for leg in myprocdef.get('legs') if not leg.get('state')], [leg.get('ids')[0] for leg in myprocdef.get('legs') if leg.get('state')]) save_path = process_checks.CMS_save_path('pkl', {'ordered_processes':[guessed_proc.base_string()], 'perturbation_orders':guessed_proc.get('perturbation_couplings')}, self._curr_model, options, output_path=output_path) if os.path.isfile(save_path) and options['reuse']: cms_result = save_load_object.load_from_file(save_path) logger_check.info("The cms check for tweak %s is recycled from file:\n %s"% (tweak['name'],save_path)) if cms_result is None: raise self.InvalidCmd('The complex mass scheme check result'+ " file below could not be read.\n %s"%save_path) else: cms_result = process_checks.check_complex_mass_scheme( process_line, param_card = param_card, cuttools=CT_dir, tir=TIR_dir, cmd = self, output_path = output_path, MLOptions = MLoptions, options=options) # Now set the correct save path save_path = process_checks.CMS_save_path('pkl', cms_result, self._curr_model, options, output_path=output_path) cms_results.append((cms_result,save_path,tweak['name'])) else: cms_result = save_load_object.load_from_file( options['analyze'].split(',')[0]) cms_results.append((cms_result,options['analyze'].split(',')[0], CMS_options['tweak'][0]['name'])) if cms_result is None: raise self.InvalidCmd('The complex mass scheme check result'+ " file below could not be read.\n %s" %options['analyze'].split(',')[0]) # restore previous settings self.do_set('complex_mass_scheme %s'%str(cms_original_setup), log=False) # Use here additional key 'ordered_processes' nb_processes += len(cms_result['ordered_processes']) cpu_time2 = time.time() logger_check.info("%i check performed in %s"% (nb_processes, misc.format_time(int(cpu_time2 - cpu_time1)))) if args[0] in ['cms']: text = "Note that the complex mass scheme test in principle only\n" text+= "works for stable particles in final states.\n\ns" if args[0] not in ['timing','stability', 'profile', 'cms']: if self.options['complex_mass_scheme']: text = "Note that Complex mass scheme gives gauge/lorentz invariant\n" text+= "results only for stable particles in final states.\n\ns" elif not myprocdef.get('perturbation_couplings'): text = "Note That all width have been set to zero for those checks\n\n" else: text = "\n" else: text ="\n" if timings: text += 'Timing result for the '+('optimized' if \ self.options['loop_optimized_output'] else 'default')+' output:\n' text += process_checks.output_timings(myprocdef, timings) if stability: text += 'Stability result for the '+('optimized' if \ self.options['loop_optimized_output'] else 'default')+' output:\n' text += process_checks.output_stability(stability,output_path) if profile_time and profile_stab: text += 'Timing result '+('optimized' if \ self.options['loop_optimized_output'] else 'default')+':\n' text += process_checks.output_profile(myprocdef, profile_stab, profile_time, output_path, options['reuse']) + '\n' if lorentz_result: text += 'Lorentz invariance results:\n' text += process_checks.output_lorentz_inv(lorentz_result) + '\n' if gauge_result: text += 'Gauge results:\n' text += process_checks.output_gauge(gauge_result) + '\n' if gauge_result_no_brs: text += 'Gauge results (switching between Unitary/Feynman):\n' text += process_checks.output_unitary_feynman(gauge_result_no_brs) + '\n' if cms_results: text += 'Complex mass scheme results (varying width in the off-shell regions):\n' cms_result = cms_results[0][0] if len(cms_results)>1: analyze = [] for i, (cms_res, save_path, tweakname) in enumerate(cms_results): save_load_object.save_to_file(save_path, cms_res) logger_check.info("Pickle file for tweak '%s' saved to disk at:\n ->%s"% (tweakname,save_path)) if i==0: analyze.append(save_path) else: analyze.append('%s(%s)'%(save_path,tweakname)) options['analyze']=','.join(analyze) options['tweak'] = CMS_options['tweak'][0] self._cms_checks.append({'line':line, 'cms_result':cms_result, 'options':options, 'output_path':output_path}) text += process_checks.output_complex_mass_scheme(cms_result, output_path, options, self._curr_model, output='concise_text' if options['report']=='concise' else 'text')+'\n' if comparisons and len(comparisons[0])>0: text += 'Process permutation results:\n' text += process_checks.output_comparisons(comparisons[0]) + '\n' self._comparisons = comparisons # We use the reuse tag for an alternative way of skipping the pager. if len(text.split('\n'))>20 and not '-reuse' in line and text!='': if 'test_manager' not in sys.argv[0]: pydoc.pager(text) # Restore diagram logger for i, log in enumerate(loggers): log.setLevel(old_levels[i]) # Output the result to the interface directly if short enough or if it # was anyway not output to the pager if len(text.split('\n'))<=20 or options['reuse']: # Useful to really specify what logger is used for ML acceptance tests logging.getLogger('madgraph.check_cmd').info(text) else: logging.getLogger('madgraph.check_cmd').debug(text) # clean the globals created. process_checks.clean_added_globals(process_checks.ADDED_GLOBAL) if not options['reuse']: process_checks.clean_up(self._mgme_dir) # Generate a new amplitude def do_generate(self, line): """Main commands: Generate an amplitude for a given process""" aloha_lib.KERNEL.clean() # Reset amplitudes self._curr_amps = diagram_generation.AmplitudeList() # Reset Process definition self._curr_proc_defs = base_objects.ProcessDefinitionList() # Reset Helas matrix elements self._curr_matrix_elements = helas_objects.HelasMultiProcess() self._generate_info = line # Reset _done_export, since we have new process self._done_export = False # Also reset _export_format and _export_dir self._export_format = None # Call add process args = self.split_arg(line) args.insert(0, 'process') self.do_add(" ".join(args)) def extract_process(self, line, proc_number = 0, overall_orders = {}): """Extract a process definition from a string. Returns a ProcessDefinition.""" orig_line = line # Check basic validity of the line if not len(re.findall('>\D', line)) in [1,2]: self.do_help('generate') raise self.InvalidCmd('Wrong use of \">\" special character.') # Perform sanity modifications on the lines: # Add a space before and after any > , $ / | [ ] space_before = re.compile(r"(?P\S)(?P[\\[\\]/\,\\$\\>|])(?P\S)") line = space_before.sub(r'\g \g \g', line) # Use regular expressions to extract s-channel propagators, # forbidden s-channel propagators/particles, coupling orders # and process number, starting from the back # Start with process number (identified by "@") proc_number_pattern = re.compile("^(.+)@\s*(\d+)\s*(.*)$") proc_number_re = proc_number_pattern.match(line) if proc_number_re: proc_number = int(proc_number_re.group(2)) line = proc_number_re.group(1)+ proc_number_re.group(3) #overall_order are already handle but it is better to pass the info to each group # Now check for perturbation orders, specified in between squared brackets perturbation_couplings_pattern = \ re.compile("^(?P.+>.+)\s*\[\s*((?P