class LoopUFOImportTest(unittest.TestCase):
    """Test class to check that the import of the loop UFO model is correct."""
    
    hardcoded_loopmodel = loop_base_objects.LoopModel()
    imported_loopmodel = loop_base_objects.LoopModel()
    
    def setUp(self):
        """load the hardcoded NLO toy model to compare against the imported 
            one"""
        
        self.hardcoded_loopmodel = loadLoopToyModel() 
        # Make sure to move the pickle first in order not to load it
        if os.path.exists(os.path.join(\
            _input_file_path,'loop_ToyModel','model.pkl')):
            os.system("mv -f "+str(os.path.join(\
                _input_file_path,'loop_ToyModel','model.pkl'))+" "+\
                str(os.path.join(_input_file_path,'loop_ToyModel',\
                'model_old.pkl')))
#        self.imported_loopmodel = models.import_full_model(os.path.join(\
#            _input_file_path,'loop_ToyModel'))
        self.imported_loopmodel = models.import_full_model(os.path.join(\
            _input_file_path,'LoopSMTest'))
        self.imported_loopmodel.actualize_dictionaries()
        self.hardcoded_loopmodel.actualize_dictionaries()
    
    def test_loadingLoopToyModel(self):
        """ Several test on the correctness of the model imported.
        Initially the idea was to compare against the hardcoded model but it
        is too tidious to copy it down. So only a few characteristics are tested
        """
        
        self.assertEqual(self.imported_loopmodel['perturbation_couplings'],\
                         ['QCD',])
        self.assertEqual(len(self.imported_loopmodel.get('interactions')),205)
        self.assertEqual(len(self.imported_loopmodel.get('interactions').\
                             get_type('base')),71)
        self.assertEqual(len(self.imported_loopmodel.get('interactions').\
                             get_R2()),78)
        self.assertEqual(len(self.imported_loopmodel.get('interactions').\
                             get_UV()),56)
        self.assertEqual(len(self.imported_loopmodel.get('interactions').\
                             get_UVmass()),4)
        self.assertEqual(self.imported_loopmodel.get('name'),'LoopSMTest')
        self.assertEqual(self.imported_loopmodel.get('order_hierarchy'),\
                         {'QCD':1,'QED':2})
        self.assertEqual(self.imported_loopmodel.get('coupling_orders'),\
                         set(['QCD','QED']))
        # The up quark
        for key in self.hardcoded_loopmodel['particles'][2].keys():
            self.assertEqual(self.imported_loopmodel['particles'][0][key],\
                         self.hardcoded_loopmodel['particles'][2][key])
        # The down quark
        for key in self.hardcoded_loopmodel['particles'][1].keys():
            self.assertEqual(self.imported_loopmodel['particles'][1][key],\
                         self.hardcoded_loopmodel['particles'][1][key])
        # The gluon
        for key in self.hardcoded_loopmodel['particles'][0].keys():
            self.assertEqual(self.imported_loopmodel['particles'][6][key],\
                         self.hardcoded_loopmodel['particles'][0][key])
Esempio n. 2
0
class LoopDiagramDrawerTest(unittest.TestCase):
    """Test class for all functions related to the LoopDiagramDrawer
        diagram made by hand
    """

    myloopmodel = loop_base_objects.LoopModel()
    mypartlist = base_objects.ParticleList()
    myinterlist = base_objects.InteractionList()
    mymodel = base_objects.Model()
    myproc = base_objects.Process()

    def setUp(self):
        """ Setup a toy-model with gluon and down-quark only """

        # A gluon
        self.mypartlist.append(
            base_objects.Particle({
                'name': 'g',
                'antiname': 'g',
                'spin': 3,
                'color': 8,
                'mass': 'zero',
                'width': 'zero',
                'texname': 'g',
                'antitexname': 'g',
                'line': 'curly',
                'charge': 0.,
                'pdg_code': 21,
                'propagating': True,
                'is_part': True,
                'self_antipart': True
            }))

        # A quark D and its antiparticle
        self.mypartlist.append(
            base_objects.Particle({
                'name': 'd',
                'antiname': 'd~',
                'spin': 2,
                'color': 3,
                'mass': 'dmass',
                'width': 'zero',
                'texname': 'd',
                'antitexname': '\bar d',
                'line': 'straight',
                'charge': -1. / 3.,
                'pdg_code': 1,
                'propagating': True,
                'is_part': True,
                'self_antipart': False
            }))
        antid = copy.copy(self.mypartlist[1])
        antid.set('is_part', False)

        # 3 gluon vertex
        self.myinterlist.append(base_objects.Interaction({
                      'id': 1,
                      'particles': base_objects.ParticleList(\
                                            [self.mypartlist[0]] * 3),
                      'color': [],
                      'lorentz':['L1'],
                      'couplings':{(0, 0):'G'},
                      'orders':{'QCD':1}}))

        # 4 gluon vertex
        self.myinterlist.append(base_objects.Interaction({
                      'id': 2,
                      'particles': base_objects.ParticleList(\
                                            [self.mypartlist[0]] * 4),
                      'color': [],
                      'lorentz':['L1'],
                      'couplings':{(0, 0):'G^2'},
                      'orders':{'QCD':2}}))

        # Gluon coupling to the down-quark
        self.myinterlist.append(base_objects.Interaction({
                      'id': 3,
                      'particles': base_objects.ParticleList(\
                                            [self.mypartlist[1], \
                                             antid, \
                                             self.mypartlist[0]]),
                      'color': [],
                      'lorentz':['L1'],
                      'couplings':{(0, 0):'GQQ'},
                      'orders':{'QCD':1}}))

        self.mymodel.set('particles', self.mypartlist)
        self.mymodel.set('interactions', self.myinterlist)
        self.myproc.set('model', self.mymodel)

        self.myloopmodel = save_load_object.load_from_file(os.path.join(_input_file_path,\
                                                            'test_toyLoopModel.pkl'))

        box_diagram, box_struct = self.def_box()
        pent_diagram, pent_struct = self.def_pent()

        self.box_drawing = draw_lib.LoopFeynmanDiagram(box_diagram, box_struct,
                                                       self.myloopmodel)

    def test_fuse_line(self):
        """ check that we fuse line correctly """

        self.box_drawing.load_diagram()
        #avoid that element are erase from memory
        line1 = self.box_drawing.lineList[0]
        line2 = self.box_drawing.lineList[1]
        vertex1 = line1.begin
        vertex2 = line1.end
        vertex3 = line2.begin
        vertex4 = line2.end

        # fuse line1 and line2
        self.box_drawing.fuse_line(line1, line2)

        # check that all link to line1 are ok
        self.assertEqual(line1.begin, vertex1)
        self.assertEqual(line1.end, vertex3)
        self.assertTrue(line1 in vertex1.lines)
        self.assertTrue(line1 in vertex3.lines)
        #self.assertTrue(vertex1 in self.box_drawing.vertexList)
        #self.assertTrue(vertex4 in self.box_drawing.vertexList)

        #check that all info to line2 are deleted
        self.assertFalse(line2 in self.box_drawing.lineList)
        self.assertFalse(line2 in vertex1.lines)
        self.assertFalse(line2 in vertex3.lines)
        self.assertFalse(vertex2 in self.box_drawing.vertexList)
        self.assertFalse(vertex3 in self.box_drawing.vertexList)

    def def_box(self):
        """ Test the drawing of a simple loop box """

        myleglist = base_objects.LegList([base_objects.Leg({'id':21,
                                              'number':num, 'state':True,
                                              'loop_line':False}) \
                                              for num in range(1, 5)])
        myleglist.append(
            base_objects.Leg({
                'id': 1,
                'number': 5,
                'loop_line': True
            }))
        myleglist.append(
            base_objects.Leg({
                'id': -1,
                'number': 6,
                'loop_line': True
            }))
        l1 = myleglist[0]
        l1.set('state', False)
        l2 = myleglist[1]
        l2.set('state', False)
        l3 = myleglist[2]
        l4 = myleglist[3]
        l5 = myleglist[4]
        l6 = myleglist[5]

        # One way of constructing this diagram, with a three-point amplitude
        l15 = base_objects.Leg({
            'id': 1,
            'number': 1,
            'loop_line': True,
            'state': False
        })
        l12 = base_objects.Leg({'id': 1, 'number': 1, 'loop_line': True})
        l13 = base_objects.Leg({'id': 1, 'number': 1, 'loop_line': True})
        lfake = base_objects.Leg({'id': 1, 'number': 1, 'loop_line': True})

        vx15 = base_objects.Vertex({
            'legs': base_objects.LegList([l1, l5, l15]),
            'id': 3
        })
        vx12 = base_objects.Vertex({
            'legs': base_objects.LegList([l15, l2, l12]),
            'id': 3
        })
        vx13 = base_objects.Vertex({
            'legs': base_objects.LegList([l12, l3, l13]),
            'id': 3
        })
        vx164 = base_objects.Vertex({
            'legs': base_objects.LegList([l13, l6, l4]),
            'id': 3
        })
        fakevx = base_objects.Vertex({
            'legs': base_objects.LegList([l13, lfake]),
            'id': 0
        })
        ctvx = base_objects.Vertex({
            'legs':
            base_objects.LegList([l1, l2, l3, l4]),
            'id':
            666
        })

        myVertexList1 = base_objects.VertexList([vx15, vx12, vx13, vx164])
        myCTVertexList = base_objects.VertexList([
            ctvx,
        ])
        myPentaDiag1=loop_base_objects.LoopDiagram({'vertices':myVertexList1,'type':1,\
                                                    'CT_vertices':myCTVertexList})

        return myPentaDiag1, []

    def def_pent(self):
        """ Test the gg>gggg d*dx* tagging of a quark pentagon which is tagged"""

        # Five gluon legs with two initial states
        myleglist = base_objects.LegList([base_objects.Leg({'id':21,
                                              'number':num,
                                              'loop_line':False}) \
                                              for num in range(1, 7)])
        myleglist.append(
            base_objects.Leg({
                'id': 1,
                'number': 7,
                'loop_line': True
            }))
        myleglist.append(
            base_objects.Leg({
                'id': -1,
                'number': 8,
                'loop_line': True
            }))
        l1 = myleglist[0]
        l2 = myleglist[1]
        l3 = myleglist[2]
        l4 = myleglist[3]
        l5 = myleglist[4]
        l6 = myleglist[5]
        l7 = myleglist[6]
        l8 = myleglist[7]

        # One way of constructing this diagram, with a three-point amplitude
        l17 = base_objects.Leg({'id': 1, 'number': 1, 'loop_line': True})
        l12 = base_objects.Leg({'id': 1, 'number': 1, 'loop_line': True})
        l68 = base_objects.Leg({'id': -1, 'number': 6, 'loop_line': True})
        l56 = base_objects.Leg({'id': -1, 'number': 5, 'loop_line': True})
        l34 = base_objects.Leg({'id': 21, 'number': 3, 'loop_line': False})

        self.myproc.set('legs', myleglist)

        vx17 = base_objects.Vertex({
            'legs': base_objects.LegList([l1, l7, l17]),
            'id': 3
        })
        vx12 = base_objects.Vertex({
            'legs': base_objects.LegList([l17, l2, l12]),
            'id': 3
        })
        vx68 = base_objects.Vertex({
            'legs': base_objects.LegList([l6, l8, l68]),
            'id': 3
        })
        vx56 = base_objects.Vertex({
            'legs': base_objects.LegList([l5, l68, l56]),
            'id': 3
        })
        vx34 = base_objects.Vertex({
            'legs': base_objects.LegList([l3, l4, l34]),
            'id': 1
        })
        vx135 = base_objects.Vertex({
            'legs':
            base_objects.LegList([l12, l56, l34]),
            'id':
            3
        })

        myVertexList1 = base_objects.VertexList(
            [vx17, vx12, vx68, vx56, vx34, vx135])

        myPentaDiag1 = loop_base_objects.LoopDiagram({
            'vertices': myVertexList1,
            'type': 1
        })

        myStructRep = loop_base_objects.FDStructureList()

        myPentaDiag1.tag(myStructRep, self.myproc['model'], 7, 8)

        return myPentaDiag1, myStructRep
        # test the drawing of myPentaDiag with its loop vertices and those in the
        # structures of myStructRep

    def def_diagrams_epemddx(self):
        """ Test the drawing of diagrams from the loop process e+e- > dd~ """

        myleglist = base_objects.LegList()
        myleglist.append(base_objects.Leg({'id': -11, 'state': False}))
        myleglist.append(base_objects.Leg({'id': 11, 'state': False}))
        myleglist.append(base_objects.Leg({'id': 1, 'state': True}))
        myleglist.append(base_objects.Leg({'id': -1, 'state': True}))

        myproc = base_objects.Process({
            'legs': myleglist,
            'model': self.myloopmodel,
            'orders': {},
            'perturbation_couplings': [
                'QCD',
            ],
            'squared_orders': {}
        })

        myloopamplitude = loop_diagram_generation.LoopAmplitude()
        myloopamplitude.set('process', myproc)
        myloopamplitude.generate_diagrams()

        # Now the drawing test on myloopamplitude['loop_diagrams']
        return myloopamplitude['loop_diagrams']
Esempio n. 3
0
class ME7ContributionTest(IOTests.IOTestManager):
    """Test class for functionalities related to contributions."""
    
    mymodel = loop_base_objects.LoopModel()
    mylegs = base_objects.LegList()
    myprocess = base_objects.Process()
    # To be set during setUp
    LO_contributions  = None
    NLO_contributions = None
 
    @misc.mute_logger()
    def setUp(self):

        self.mymodel = import_ufo.import_model(
            pjoin(MG5DIR,'tests','input_files','LoopSMTest'),
            prefix=True, complex_mass_scheme = False )
        self.mymodel.pass_particles_name_in_mg_default()

        # Setting up the process p p > h j j and its subtraction
        self.mylegs = base_objects.MultiLegList([
            base_objects.MultiLeg(
                    {'ids': [1,2,-1,-2,21], 'state': base_objects.Leg.INITIAL}),
            base_objects.MultiLeg(
                    {'ids': [1,2,-1,-2,21], 'state': base_objects.Leg.INITIAL}),
            base_objects.MultiLeg(
                    {'ids': [22], 'state': base_objects.Leg.FINAL}),
            base_objects.MultiLeg(
                    {'ids': [21,1,-1,2,-2], 'state': base_objects.Leg.FINAL})
        ])

        self.myprocdef = base_objects.ProcessDefinition({
            'legs': self.mylegs,
            'model': self.mymodel,
            'split_orders': ['QCD','QED']
        })

        # The general accessor with the Born ME registered
        self.all_born_MEs_accessor = accessors.MEAccessorDict()
        # Generate only Born LO contributions
        with misc.TMP_directory(debug=False) as tmp_path:
            
            # Generate the output for this.
            self.madgraph_cmd = cmd.MasterCmd(main='MadGraph')
            self.madgraph_cmd._curr_model = self.mymodel
            self.madgraph_cmd.reset_interface_before_new_generation()            
            self.madgraph_cmd._export_dir = pjoin(tmp_path,'ME7ContributionTest_LO')

            # Generate contributions
            generation_options = {'ME7_definition': True, 
                                  'diagram_filter': False, 
                                  'LO': True, 
                                  'NNLO': [], 
                                  'NNNLO': [],
                                  'optimize': False, 
                                  'NLO': [], 
                                  'loop_induced': [],
                                  'ignore_contributions' : [],
                                  'beam_types': ['auto', 'auto'],
                                  'loop_filter'          : None,
                                  'process_definitions'  : {}}

            self.madgraph_cmd.add_contributions(self.myprocdef, generation_options)
            LO_contributions = self.madgraph_cmd._curr_contribs
            LO_contributions.apply_method_to_all_contribs(
                    'generate_amplitudes', log='Generate diagrams for')

            self.exporter = export_ME7.ME7Exporter(
                self.madgraph_cmd, False, group_subprocesses=True )
            self.exporter.pass_information_from_cmd(self.madgraph_cmd)
            self.exporter.copy_template(self.madgraph_cmd._curr_model)
            self.exporter.export(True, args=[])
            # We want to finalize and output the model for the Born, because need to
            # register its MEs in the accessor.
            self.exporter.finalize(['nojpeg'], self.madgraph_cmd.history)
            self.LO_contributions = self.madgraph_cmd._curr_contribs         
            # Add the Born ME accessors to the dictionary
            self.LO_contributions[0].add_ME_accessors(
                self.all_born_MEs_accessor, pjoin(tmp_path,'ME7ContributionTest_LO') )

        # Generate all NLO contributions 
        with misc.TMP_directory(debug=False) as tmp_path:

            # Generate the output for this.
            self.madgraph_cmd = cmd.MasterCmd(main='MadGraph')
            self.madgraph_cmd._curr_model = self.mymodel
            self.madgraph_cmd.reset_interface_before_new_generation()
            self.madgraph_cmd._export_dir = pjoin(tmp_path,'ME7ContributionTest_LO')

            # Generate contributions
            generation_options = {'ME7_definition': True, 
                                  'diagram_filter': False, 
                                  'LO': True, 
                                  'NNLO': [], 
                                  'NNNLO': [],
                                  'optimize': False, 
                                  'NLO': ['QCD'], 
                                  'loop_induced': [],
                                  'ignore_contributions' : [],
                                  'beam_types': ['auto', 'auto'],
                                  'loop_filter'          : None,
                                  'process_definitions'  : {},
                                  }

            self.madgraph_cmd.add_contributions(self.myprocdef, generation_options)  

            self.madgraph_cmd._curr_contribs.apply_method_to_all_contribs(
                    'generate_amplitudes', log='Generate diagrams for')

            self.exporter = export_ME7.ME7Exporter(
                self.madgraph_cmd, False, group_subprocesses=True )
            self.exporter.pass_information_from_cmd(self.madgraph_cmd)
            self.exporter.copy_template(self.madgraph_cmd._curr_model)
            self.exporter.export(True, args=[])
            # The export above was enough to have fully functional contributions to test            
            # self.exporter.finalize(['nojpeg'], self.madgraph_cmd.history)
            self.NLO_contributions = self.madgraph_cmd._curr_contribs 

    @IOTests.createIOTest()
    def testIO_current_generation_and_access(self):
        """ target: Counterterms_R.txt
            target: Counterterms_V.txt
            target: Currents_Local.txt
            target: Currents_Integ.txt
        """

        # Test the generation of counterterms in single real-emission and virtual
        # type of contributions. Also make sure that they can be exported.

        verbose = False 
        
        # Real counterterms

        # Fetch the real contribution
        real_emission_contrib = self.NLO_contributions.get_contributions_of_type(
            contributions.Contribution_R )[0]
        # Use the ME accessor dictionary with all Born MEs to filter
        # the unphysical counterterms,
        # for example those with the reduced process g g > a g
        n_CTs_before_filtering = len(
            sum(real_emission_contrib.counterterms.values(), []) )
        for CT_list in real_emission_contrib.counterterms.values():
            contributions.Contribution_R.remove_counterterms_with_no_reduced_process(
                self.all_born_MEs_accessor, CT_list )
        n_CTs_after_filtering = len(
            sum(real_emission_contrib.counterterms.values(), []) )
        n_CTs_difference = n_CTs_before_filtering - n_CTs_after_filtering
        if verbose:
            print_string = 'A total of %d local counterterms were filtered'
            print_string += 'because a reduced process did not exist.'
            misc.sprint(print_string % (n_CTs_difference))
        # Check the number of filtered local counterterms
        self.assertEqual(n_CTs_difference, 6)
        # Output all local counterterms
        counterterm_strings = [
            CT.__str__(print_n=True, print_pdg=True, print_state=True)
            for CT in sum(real_emission_contrib.counterterms.values(), []) ]
        open(pjoin(self.IOpath,'Counterterms_R.txt'),'w').write(
            "\n".join(sorted(counterterm_strings)) )

        # Virtual counterterms

        # Fetch the virtual contribution
        virtual_contrib = self.NLO_contributions.get_contributions_of_type(
            contributions.Contribution_V)[0]
        # Apply the filter to integrated counterterms
        n_CTs_before_filtering = len(
            sum(virtual_contrib.integrated_counterterms.values(),[]) )
        for CT_list in virtual_contrib.integrated_counterterms.values():
            contributions.Contribution_V.remove_counterterms_with_no_reduced_process(
                self.all_born_MEs_accessor, CT_list )
        n_CTs_after_filtering = len(
            sum(virtual_contrib.integrated_counterterms.values(),[]) )
        n_CTs_difference = n_CTs_before_filtering - n_CTs_after_filtering
        if verbose:
            print_string = 'A total of %d integrated counterterms were filtered'
            print_string += 'because a reduced process did not exist.'
            misc.sprint(print_string % (n_CTs_difference))

        # Check the number of filtered integrated counterterms
        self.assertEqual(n_CTs_difference, 0)
        # Output all integrated counterterms
        counterterm_strings = [
            CT['integrated_counterterm'].__str__(
                print_n=True, print_pdg=True, print_state=True )
            for CT in sum(virtual_contrib.integrated_counterterms.values(), []) ]
        open(pjoin(self.IOpath,'Counterterms_V.txt'),'w').write(
            "\n".join(sorted(counterterm_strings)) )

        # Check the number of counterterms that did not find a host contribution
        refused_cts = len(self.exporter.integrated_counterterms_refused_from_all_contribs)
        if verbose:
            print_string = 'A total of %d integrated subtraction counterterms'
            print_string += 'did not find a host contribution.'
            misc.sprint(print_string % refused_cts)
        self.assertEqual(refused_cts, 14)

        # Local currents

        # Initialize an empty accessor dictionary for the currents.
        # No currents are ignored because of potential pre-existing ones.
        accessors_dict = accessors.MEAccessorDict()
        all_local_currents = \
            real_emission_contrib.get_all_necessary_local_currents(accessors_dict)
        current_strings = [str(current) for current in all_local_currents]
        # Print all local currents
        if verbose: misc.sprint('Local currents:\n' + '\n'.join(current_strings))
        # Output all local currents
        open(pjoin(self.IOpath, 'Currents_Local.txt'), 'w').write(
            "\n".join(sorted(current_strings)) )

        # Integrated currents

        # Use the ME accessor dictionary with all Born MEs to filter what are the
        # unphysical counterterms, for example those with the reduced process g g > a g
        all_integrated_currents = virtual_contrib.get_all_necessary_integrated_currents(
            self.all_born_MEs_accessor )
        current_strings = [str(current) for current in all_integrated_currents]
        # Print all currents
        if verbose: misc.sprint('Integrated currents:\n' + '\n'.join(current_strings))
        # Output all local currents
        open(pjoin(self.IOpath, 'Currents_Integ.txt'), 'w').write(
            "\n".join(sorted(current_strings)) )

        with misc.TMP_directory(debug=False) as tmp_path:

            print_string = "A total of %d accessor keys have been generated"
            print_string += "for %s subtraction currents."

            # Reset the accessor dictionary so as to monitor only the newly added keys
            accessors_dict = accessors.MEAccessorDict()
            real_emission_contrib.add_current_accessors(
                self.mymodel, accessors_dict, tmp_path, 'colorful', all_local_currents )
            # Print all accessor keys
            if verbose: misc.sprint(print_string % (len(accessors_dict), "local"))
            self.assertEqual(len(accessors_dict), 43)

            # Reset the accessor dictionary so as to monitor only the newly added keys
            accessors_dict = accessors.MEAccessorDict()
            virtual_contrib.add_current_accessors(
                self.mymodel, accessors_dict, tmp_path, 'colorful', all_integrated_currents )
            # Print all accessor keys
            if verbose: misc.sprint(print_string % (len(accessors_dict), "integrated"))
            self.assertEqual(len(accessors_dict), 50)
def loadLoopToyModel():
    """Setup the Loop Toy QCD model with d,dx,u,ux and the gluon"""
    
    mypartlist = base_objects.ParticleList()
    myinterlist = base_objects.InteractionList()
    myloopmodel = loop_base_objects.LoopModel()

    # A gluon
    mypartlist.append(base_objects.Particle({'name':'g',
                  'antiname':'g',
                  'spin':3,
                  'color':8,
                  'mass':'ZERO',
                  'width':'ZERO',
                  'texname':'G',
                  'antitexname':'G',
                  'line':'curly',
                  'charge':0.,
                  'pdg_code':21,
                  'propagating':True,
                  'propagator':0,                                             
                  'is_part':True,
                  'counterterm':{('QCD', ((6,),)): {0: 'UVWfct_G_1', -1: 'UVWfct_G_1_1eps'}, ('QCD', ((5,),)): {0: 'UVWfct_G_0', -1: 'UVWfct_G_0_1eps'}},
                  'self_antipart':True}))
    
    # A quark U and its antiparticle
    mypartlist.append(base_objects.Particle({'name':'u',
                  'antiname':'u~',
                  'spin':2,
                  'color':3,
                  'mass':'ZERO',
                  'width':'ZERO',
                  'texname':'u',
                  'antitexname':'u',
                  'line':'straight',
                  'charge':2. / 3.,
                  'pdg_code':2,
                  'propagating':True,
                  'propagator':'',                                             
                  'is_part':True,
                  'self_antipart':False}))
    antiu = copy.copy(mypartlist[1])
    antiu.set('is_part', False)

    # A quark D and its antiparticle
    mypartlist.append(base_objects.Particle({'name':'d',
                  'antiname':'d~',
                  'spin':2,
                  'color':3,
                  'mass':'ZERO',
                  'width':'ZERO',
                  'texname':'d',
                  'antitexname':'d',
                  'line':'straight',
                  'charge':-1. / 3.,
                  'pdg_code':1,
                  'propagating':True,
                  'propagator':'',
                  'is_part':True,
                  'self_antipart':False}))
    antid = copy.copy(mypartlist[2])
    antid.set('is_part', False)

    myloopmodel.set('particles', mypartlist)
    myloopmodel.set('couplings', ['QCD'])        
    myloopmodel.set('interactions', myinterlist)
    myloopmodel.set('perturbation_couplings', ['QCD'])
    myloopmodel.set('order_hierarchy', {'QCD':1})

    return myloopmodel