Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion madgraph/core/helas_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -5479,8 +5479,11 @@ def _flavor_enumeration_context(self, model):
"""
pdgs = []
pdg_signs = []
# L-cut wavefunctions of a loop ME are also motherless, but they are not
# external legs of the process (same filter as get_nexternal_ninitial).
external_wfs = sorted([wf for wf in self.get_all_wavefunctions()
if len(wf.get('mothers')) == 0],
if len(wf.get('mothers')) == 0
and not wf.get('is_loop')],
key=lambda w: w['number_external'])
external_number = 1
id_to_wf = collections.defaultdict(list)
Expand Down
9 changes: 8 additions & 1 deletion madgraph/interface/loop_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,11 +298,18 @@ def proc_validity(self, proc, mode):
def validate_model(self, loop_type='virtual',coupling_type=['QCD'], stop=True):
""" Upgrade the model sm to loop_sm if needed """

# Allow to call this function with a string instead of a list of
# Allow to call this function with a string instead of a list of
# perturbation orders.
if isinstance(coupling_type,str):
coupling_type = [coupling_type,]

# Everything below assumes a model. [virt=]/[real=] get one from
# check_generate before we are called, but master_interface calls us
# directly for [noborn=], ahead of create_loop_induced's check_add.
if not self._curr_model:
logger.info("No model currently active, so we import the Standard Model")
self.do_import('model sm')

active_interface = getattr(self, 'current_interface', None)

## if coupling_type!= ['QCD'] and loop_type not in ['virtual','noborn']:
Expand Down
17 changes: 17 additions & 0 deletions madgraph/interface/madgraph_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -9806,6 +9806,23 @@ def do_output(self, line):
# merge_quartic_vertices can be resolved -- before anything is built
self.apply_quartic_diagram_order(options)

# A loop-induced process is exported by this tree-level do_output (see
# create_loop_induced), but only the formats in LOOP_INDUCED_FORMATS
# have a loop backend to route it to. Refuse the others here, ahead of
# the directory cleaning just below, so that a guaranteed refusal never
# deletes an existing output directory first. The factories carry the
# same check, but they run after that cleaning.
if self._curr_amps and isinstance(self._curr_amps[0],
loop_diagram_generation.LoopAmplitude):
# --me_exporter= writes into the same directory, so it has to be
# checked here too
for format in [self._export_format,
options['me_exporter'].get('name')]:
if format and format not in export_v4.LOOP_INDUCED_FORMATS:
raise self.InvalidCmd(
export_v4.loop_induced_not_supported_msg(
format, self._curr_amps[0].get('process')))

# check
if os.path.realpath(self._export_dir) == os.getcwd():
if len(args) == 0:
Expand Down
11 changes: 10 additions & 1 deletion madgraph/iolibs/export_cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@
import madgraph.iolibs.file_writers as writers
import madgraph.iolibs.template_files as template_files
import madgraph.iolibs.ufo_expression_parsers as parsers
import madgraph.loop.loop_diagram_generation as loop_diagram_generation
import madgraph.various.banner as banner_mod
from madgraph import MadGraph5Error, InvalidCmd, MG5DIR
from madgraph.iolibs.files import cp, ln, mv

import madgraph.iolibs.export_v4 as export_v4
from madgraph.iolibs.export_v4 import VirtualExporter, ProcessExporterFortran
import madgraph.various.misc as misc

Expand Down Expand Up @@ -3559,7 +3561,14 @@ def ExportCPPFactory(cmd, group_subprocesses=False, cmd_options={}):
opt = dict(cmd.options)
opt['output_options'] = cmd_options
cformat = cmd._export_format


# No C++ exporter has a MadLoop backend (the mg7 one cannot even index the
# loop legs: it builds its edge names from the external legs alone).
if cformat not in export_v4.LOOP_INDUCED_FORMATS and cmd._curr_amps and \
isinstance(cmd._curr_amps[0], loop_diagram_generation.LoopAmplitude):
raise InvalidCmd(export_v4.loop_induced_not_supported_msg(
cformat, cmd._curr_amps[0].get('process')))

if cformat == 'pythia8':
return ProcessExporterPythia8(cmd._export_dir, opt)
elif cformat == 'standalone_cpp':
Expand Down
42 changes: 40 additions & 2 deletions madgraph/iolibs/export_v4.py
Original file line number Diff line number Diff line change
Expand Up @@ -11502,7 +11502,23 @@ def create_param_card(self, write_special=True):
rule_card_path=rule_card,
mssm_convert=True,
write_special=write_special)


# Output formats with a loop backend for a loop-induced ([noborn=]) process
# coming through the tree-level do_output. Test membership EXACTLY: 'standalone'
# is a prefix of standalone_cpp / _mg7 / _msP / _msF / _rw, which have none.
LOOP_INDUCED_FORMATS = ['madevent', 'plugin', 'standalone']

def loop_induced_not_supported_msg(format, process=None):
"""Refusal text for a format that cannot write a LoopHelasMatrixElement."""

orders = (' '.join(process.get('perturbation_couplings')) if process
else '') or 'QCD'

return """The '%(format)s' output format does not support loop-induced processes.
Generate the process with [sqrvirt=%(orders)s] rather than [noborn=%(orders)s] to obtain the
same matrix element as a standalone MadLoop output, or use 'output madevent'
to integrate it.""" % {'format': format, 'orders': orders}

def ExportV4Factory(cmd, noclean, output_type='default', group_subprocesses=True, cmd_options={}):
""" Determine which Export_v4 class is required. cmd is the command
interface containing all potential usefull information.
Expand Down Expand Up @@ -11652,8 +11668,28 @@ def ExportV4Factory(cmd, noclean, output_type='default', group_subprocesses=True
opt['madanalysis5'] = cmd.options['madanalysis5_path']

if format == 'matrix' or format.startswith('standalone'):
if cmd._curr_amps and isinstance(
cmd._curr_amps[0], loop_diagram_generation.LoopAmplitude):
# of the formats sharing this branch only 'standalone' has a
# MadLoop backend; ProcessExporterFortranSA has none
if format not in LOOP_INDUCED_FORMATS:
raise InvalidCmd(
loop_induced_not_supported_msg(format, curr_proc))
import madgraph.loop.loop_exporters as loop_exporters
if not os.path.isdir(os.path.join(cmd._mgme_dir,
'Template/loop_material')):
raise MadGraph5Error(
'MG5_aMC cannot find the \'loop_material\' directory'
' in %s' % str(cmd._mgme_dir))
if cmd.options['loop_optimized_output']:
MadLoop_SA_options['export_format'] = 'madloop_optimized'
ExporterClass = \
loop_exporters.LoopProcessOptimizedExporterFortranSA
else:
ExporterClass = loop_exporters.LoopProcessExporterFortranSA
return ExporterClass(cmd._export_dir, MadLoop_SA_options)
return ProcessExporterFortranSA(cmd._export_dir, opt, format=format)

elif format in ['madevent'] and group_subprocesses:
if isinstance(cmd._curr_amps[0],
loop_diagram_generation.LoopAmplitude):
Expand All @@ -11673,6 +11709,8 @@ def ExportV4Factory(cmd, noclean, output_type='default', group_subprocesses=True
else:
return ProcessExporterFortranME(cmd._export_dir,opt)
elif format in ['matchbox']:
# no loop-induced backstop needed: do_output refuses 'matchbox'
# before any factory runs, and loop_interface never comes here
return ProcessExporterFortranMatchBox(cmd._export_dir,opt)
elif cmd._export_format in ['madweight'] and group_subprocesses:

Expand Down
6 changes: 4 additions & 2 deletions madgraph/loop/loop_exporters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1200,10 +1200,12 @@ def write_process_info_file(self, writer, matrix_element):

writer.writelines(proc_include)

def generate_subprocess_directory(self, matrix_element, fortran_model, second_exporter=None):
def generate_subprocess_directory(self, matrix_element, fortran_model,
me_number=None, second_exporter=None):
""" To overload the default name for this function such that the correct
function is used when called from the command interface """

# 3rd positional slot is the subprocess number (base-class convention);
# loop_interface.ML5export omits it, madgraph_interface.export passes it.
assert second_exporter is None
self.unique_id +=1
return self.generate_loop_subprocess(matrix_element,fortran_model,
Expand Down
22 changes: 22 additions & 0 deletions tests/acceptance_tests/test_cmd_madloop.py
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,28 @@ def run_cmd(cmd):
IOTests.IOTest.remove_f77_function_from_file(
pjoin(self.IOpath,'ggttx_IOTest', 'SubProcesses','MadLoopCommons.f'),
'PRINT_MADLOOP_BANNER')

@IOTests.createIOTest(groupName='MadLoop_output_from_the_interface')
def testIO_loop_induced_standalone_output(self):
r""" target: gghLI_IOTest/SubProcesses/P0_gg_h/[(check_sa|loop_matrix)\.f]
"""
# A loop-induced ([noborn=]) process is exported from the MadGraph
# interface, which used to hand the loop matrix element to the
# tree-level standalone exporter and crash.
interface = MGCmd.MasterCmd()
interface.no_notification()

# Select the Tensor Integral to include in the test
misc.deactivate_dependence('pjfry', cmd = interface, log='stdout')
misc.deactivate_dependence('samurai', cmd = interface, log='stdout')
misc.deactivate_dependence('golem', cmd = interface, log='stdout')
misc.activate_dependence('ninja', cmd = interface, log='stdout',MG5dir=MG5DIR)

# no 'import model': validate_model must bootstrap sm -> loop_sm itself
interface.exec_cmd('generate g g > h [noborn=QCD]', errorhandling=False,
printcmd=False, precmd=True, postcmd=True)
interface.onecmd('output standalone %s -f' %
str(pjoin(self.IOpath,'gghLI_IOTest')))



Expand Down
Loading
Loading