示例#1
0
 def __init__(self, assy, list):
     Jig.__init__(self, assy, list)
     self.cancelled = False
     # set default color of new gamess jig to magenta
     self.color = magenta  # This is the "draw" color.  When selected, this will become highlighted red.
     self.normcolor = magenta  # This is the normal (unselected) color.
     #bruce 050913 thinks self.history is no longer needed:
     ## self.history = env.history
     #self.psets = [] # list of parms set objects [as of circa 050704, only the first of these is ever defined (thinks bruce)]
     self.pset = gamessParms('Parameter Set 1')
     self.gmsjob = GamessJob(Gamess.job_parms, jig=self)
     ## bruce 050701 removing this: self.gmsjob.edit()
     self.outputfile = ''  # Name of jig's most recent output file. [this attr is intentionally not copied -- bruce 050704]
示例#2
0
 def __init__(self, assy, list):
     Jig.__init__(self, assy, list)
     self.cancelled = False
     # set default color of new gamess jig to magenta
     self.color = magenta # This is the "draw" color.  When selected, this will become highlighted red.
     self.normcolor = magenta # This is the normal (unselected) color.
     #bruce 050913 thinks self.history is no longer needed:
     ## self.history = env.history
     #self.psets = [] # list of parms set objects [as of circa 050704, only the first of these is ever defined (thinks bruce)]
     self.pset = gamessParms('Parameter Set 1')
     self.gmsjob = GamessJob(Gamess.job_parms, jig=self)
     ## bruce 050701 removing this: self.gmsjob.edit()
     self.outputfile = '' # Name of jig's most recent output file. [this attr is intentionally not copied -- bruce 050704]
示例#3
0
 def __createJobs(self, jobInfoList):
     """
     Create SimJob objects, return the list of job objects
     """
     jobs = []
     for j in jobInfoList:
         if j[0]['Engine'] in ['GAMESS', 'PC GAMESS']:
             #Create GamessJob, call GamessJob.readProp()
             jobs += [GamessJob(j[0], job_from_file =j[1:])]
         elif j[0]['Engine'] == 'nanoSIM-1':
             #Create nanoEngineer-1 MD simulator job
             pass
             
     return jobs
示例#4
0
class Gamess(Jig):
    """
    A Gamess jig has a list of atoms with one or more parameter sets used to run a GAMESS calcuation.
    """
    sym = "GAMESS"
    icon_names = ["modeltree/gamess.png", "modeltree/gamess-hide.png"]
    featurename = "GAMESS Jig"  #bruce 051203

    #bruce 050704 added these attrs and related methods, to make copying of this jig work properly
    mutable_attrs = ('pset', )
    copyable_attrs = Jig.copyable_attrs + () + mutable_attrs

    # Default job parameters for a GAMESS job.
    job_parms = {
        'Engine': 'GAMESS',
        'Calculation': '',
        'Description': "No comments? How about today's weather?",
        'Status': '',
        'Server_id': '',
        'Job_id': '',
        'Time': '0.0'
    }

    # create a blank Gamess jig with the given list of atoms
    def __init__(self, assy, list):
        Jig.__init__(self, assy, list)
        self.cancelled = False
        # set default color of new gamess jig to magenta
        self.color = magenta  # This is the "draw" color.  When selected, this will become highlighted red.
        self.normcolor = magenta  # This is the normal (unselected) color.
        #bruce 050913 thinks self.history is no longer needed:
        ## self.history = env.history
        #self.psets = [] # list of parms set objects [as of circa 050704, only the first of these is ever defined (thinks bruce)]
        self.pset = gamessParms('Parameter Set 1')
        self.gmsjob = GamessJob(Gamess.job_parms, jig=self)
        ## bruce 050701 removing this: self.gmsjob.edit()
        self.outputfile = ''  # Name of jig's most recent output file. [this attr is intentionally not copied -- bruce 050704]

    def edit(self):
        self.gmsjob.edit()

    # it's drawn as a wire cube around each atom.
    def _draw(self, glpane, dispdef):
        for a in self.atoms:
            # Using dispdef of the atom's chunk instead of the glpane's dispdef fixes bug 373. mark 060122.
            chunk = a.molecule
            dispdef = chunk.get_dispdef(glpane)
            disp, rad = a.howdraw(dispdef)
            # wware 060203 selected bounding box bigger, bug 756
            if self.picked: rad *= 1.01
            drawwirecube(self.color, a.posn(), rad)

    # Write "gamess" record to POV-Ray file in the format:
    # gamess(<box-center>,box-radius,<r, g, b>)
    def writepov(self, file, dispdef):
        if self.hidden: return
        if self.is_disabled(): return
        if self.picked: c = self.normcolor
        else: c = self.color
        for a in self.atoms:
            disp, rad = a.howdraw(dispdef)
            grec = "gamess(" + povpoint(
                a.posn()) + "," + str(rad) + ",<" + str(c[0]) + "," + str(
                    c[1]) + "," + str(c[2]) + ">)\n"
            file.write(grec)

    def _getinfo(self):
        return "[Object: Gamess Jig] [Name: " + str(
            self.name) + "] [Total Atoms: " + str(len(
                self.atoms)) + "] [Parameters: " + self.gms_parms_info() + "]"

    def _getToolTipInfo(self):  #ninad060825
        """
        Return a string for display in Dynamic Tool tip
        """
        attachedAtomCount = "<font color=\"#0000FF\">Total  Atoms: </font>%d" % (
            len(self.atoms))
        return str(self.name) + "<br>" +  "<font color=\"#0000FF\"> Jig Type:</font>Gamess Jig"\
        + "<br>"  +  "<font color=\"#0000FF\"> Parameters:</font>" + self.gms_parms_info()\
        + "<br>" +  str(attachedAtomCount)

    def getstatistics(self, stats):
        stats.ngamess += 1

    def gms_parms_info(self, delimeter='/'):
        """
        Return a GAMESS parms shorthand string.
        """
        # This is something Damian and I discussed to quickly display the parms set for
        # a Gamess jig. It is used in the header of the GAMESS INP file and in the naming of
        # the new chunk made from a GAMESS optimization.  It is also used to display the
        # parameter info (along with the energy) when doing an energy calculation.
        # Mark 050625.

        d = delimeter

        pset = self.pset

        # SCFTYP (RHF, UHF, or ROHF)
        s1 = scftyp[pset.ui.scftyp]

        # Hartree-Fock (display nothing), DFT (display functional) or MP2
        if ecm[pset.ui.ecm] == 'DFT':
            if sys.platform == 'win32':  # Windows - PC GAMESS
                item = pcgms_dfttyp_items[pset.ui.dfttyp]
            else:  # Linux or MacOS - GAMESS
                item = gms_dfttyp_items[pset.ui.dfttyp]

            s2, junk = item.split(' ', 1)
            s2 = d + s2
        elif ecm[pset.ui.ecm] == 'MP2':
            s2 = d + 'MP2'
        else:
            s2 = ''

        # Basis Set
        s3 = d + pset.ui.gbasisname

        # Charge
        s4 = d + 'Ch' + str(pset.ui.icharg)

        # Multiplicity
        s5 = d + 'M' + str(pset.ui.mult + 1)

        return s1 + s2 + s3 + s4 + s5

    def __CM_Calculate_Energy(self):
        """
        Gamess Jig context menu "Calculate Energy"
        """
        self.calculate_energy()

    def calculate_energy(self):
        """
        Calculate energy.
        """

        cmd = greenmsg("Calculate Energy: ")

        errmsgs = ["GAMESS job aborted.", "Error: GAMESS job failed."]

        pset = self.pset
        runtyp = pset.ui.runtyp  # Save runtyp (Calculate) setting to restore it later.
        pset.ui.runtyp = 0  # Energy calculation
        origCalType = self.gmsjob.Calculation
        self.gmsjob.Calculation = 'Energy'

        self.update_gamess_parms()

        # Run GAMESS job.  Return value r:
        # 0 = success
        # 1 = job aborted
        # 2 = job failed.
        r = self.gmsjob.launch()

        pset.ui.runtyp = runtyp  # Restore to original value
        self.gmsjob.Calculation = origCalType

        if r:  # Job was aborted or an error occurred.
            msg = redmsg(errmsgs[r - 1])
            env.history.message(cmd + msg)
            return

        self.print_energy()

    def __CM_Optimize_Geometry(self):
        """
        Gamess Jig context menu "Optimize Geometry"
        """
        self.optimize_geometry()

    def optimize_geometry(self):
        """
        Optimize geometry
        """

        cmd = greenmsg("Optimize Geometry: ")

        errmsgs = ["GAMESS job aborted.", "Error: GAMESS job failed."]

        pset = self.pset
        runtyp = pset.ui.runtyp  # Save runtyp (Calculate) setting to restore it later.
        pset.ui.runtyp = 1  # Optimize
        origCalType = self.gmsjob.Calculation
        self.gmsjob.Calculation = 'Optimize'

        self.update_gamess_parms()

        # Run GAMESS job.  Return value r:
        # 0 = success
        # 1 = job aborted.
        # 2 = job failed.
        r = self.gmsjob.launch()

        pset.ui.runtyp = runtyp  # Restore to original value
        self.gmsjob.Calculation = origCalType

        if r:  # Job was aborted or an error occurred.
            msg = redmsg(errmsgs[r - 1])
            env.history.message(cmd + msg)
            return

        try:
            r2 = self.move_optimized_atoms()
        except:
            print_compact_traceback( "GamessProp.run_job(): error reading GAMESS OUT file [%s]: " % \
                    self.outputfile )
            env.history.message(
                redmsg(cmd +
                       "Internal error while inserting GAMESS geometry: " +
                       self.outputfile))
        else:
            if r2:
                env.history.message(cmd + redmsg("Atoms not adjusted."))
            else:
                self.assy.changed()  # The file and the part are not the same.
                self.print_energy(
                )  # Print the final energy from the optimize OUT file, too.
                env.history.message(cmd + "Atoms adjusted.")

    def __CM_Optimize_Geometry__options(self):
        if Jig.is_disabled(self):
            return ['disabled']
        else:
            return []

    def __CM_Calculate_Energy__options(self):
        if Jig.is_disabled(self):
            return ['disabled']
        else:
            return []
        pass

    def print_energy(self):

        r, final_energy_str = get_energy_from_gms_outfile(self.outputfile)

        if r == 1:  # GAMESS terminated abnormally.
            if final_energy_str:
                env.history.message(
                    redmsg(
                        final_energy_str +
                        " Check if you have set the right Gamess executable file. Usually it's called gamess.??.x or ??gamess.exe."
                    ))
                return

            msg = "Final energy value not found. The output file is located at: " + self.outputfile
            env.history.message(redmsg(msg))

        elif r == 2:  # The output file not exist
            msg = "The output file %s doesn't exist. The reason is either that Gamess didn't run or the output file has been deleted. " % self.outputfile
            env.history.message(redmsg(msg))

        else:  # Final energy was found.
            gmstr = self.gms_parms_info()
            msg = "GAMESS finished. The output file is located at: " + self.outputfile
            env.history.message(msg)
            msg = "Parameters: " + gmstr + ".  The final energy is: " + final_energy_str + " Hartree."
            env.history.message(msg)

    mmp_record_name = "gamess"  #bruce 050701

    def move_optimized_atoms(self):

        newPositions = get_atompos_from_gms_outfile(self.assy, self.outputfile,
                                                    self.atoms)
        # retval is either a list of atom posns or an error message string.
        assert type(newPositions) in [type([]), type("")]
        if type(newPositions) == type([]):
            self.move_atoms(newPositions)
            self.assy.changed()
            self.assy.o.gl_update()
            #self.assy.w.win_update()
            return 0
        else:
            env.history.message(redmsg(newPositions))
            return 1

    def move_atoms(self, newPositions):  # used when reading xyz files
        """
        [Borrowed from movie.moveAtoms.]
        
        Move a list of atoms to newPosition. After 
        all atoms moving, bond updated, update display once.
        <parameter>newPosition is a list of atom absolute position,
        the list order is the same as self.alist
        """

        atomList = self.atoms

        if len(newPositions) != len(atomList):
            #bruce 050225 added some parameters to this error message
            #bruce 050406 comment: but it probably never comes out, since readxyz checks this,
            # so I won't bother to print it to history here. But leaving it in is good for safety.
            print "move_atoms: The number of atoms from GAMESS file (%d) is not matching with that of the current model (%d)" % \
                  (len(newPositions), len(atomList))
            return
        move_alist_and_snuggle(atomList, newPositions)
        #bruce 051221 fix a bug analogous to bug 1239 by letting this new function (containing a loop)
        # replace a copy (which was right here) of the older buggy version of that loop
        self.assy.o.gl_update()
        return

    def writemmp(self, mapping):  #bruce 050701
        "[extends Jig method]"
        super = Jig
        super.writemmp(
            self, mapping
        )  # this writes the main gamess record, and some general info leaf records valid for all nodes
        pset = self.pset
        pset.writemmp(mapping, 0)
        # This writes the pset's info, as a series of "info gamess" records which modify the last gamess jig;
        # in case we ever want to extend this to permit more than one pset per jig in the mmp file,
        # each of those records has a "pset index" which we pass here as 0 (and which is written using "%s").
        # So if we wanted to write more psets we could say self.psets[i].writemmp(mapping, i) for each one.
        return

    def readmmp_info_gamess_setitem(self, key, val, interp):  #bruce 050701
        """
        This is called when reading an mmp file, for each "info gamess" record
        which occurs right after this node is read and no other (gamess jig) node has been read.
           Key is a list of words, val a string; the entire record format
        is presently [050701] "info gamess <key> = <val>", and there are exactly
        two words in <key>, the "parameter set index" (presently always 0) and the "param name".
           Interp is an object to help us translate references in <val>
        into other objects read from the same mmp file or referred to by it.
        See the calls of this or similar methods from files_mmp for the doc of interp methods.
           If key is recognized, this method should set the attribute or property
        it refers to to val; otherwise it must do nothing.
           (An unrecognized key, even if longer than any recognized key,
        is not an error. Someday it would be ok to warn about an mmp file
        containing unrecognized info records or keys, but not too verbosely
        (at most once per file per type of info).)
        """
        if len(key) != 2 or not key[0].isdigit():
            if debug_flags.atom_debug:
                print "atom_debug: fyi: info gamess with unrecognized key %r (not an error)" % (
                    key, )
            return
        pset_index, name = key
        pset_index = int(pset_index)
        # pset_index is presently always 0, but this code should work provided self.psets has an element with this index;
        try:
            pset = self.pset
        except:
            # not an error -- future mmp formats might use non-existent indices and expect readers to create new psets.
            if debug_flags.atom_debug:
                print "atom_debug: fyi: info gamess with non-existent pset index in key %r (not an error)" % (
                    key, )
            return
        # the rest of the work should be done by the pset.
        try:
            self.pset.info_gamess_setitem(name, val, interp)

        except:
            print_compact_traceback(
                "bug: exception (ignored) in pset.info_gamess_setitem( %r, %r, interp ): "
                % (name, val))
            return
        pass

    def own_mutable_copyable_attrs(self):  #bruce 050704
        """
        [overrides Node method]
        """
        super = Jig
        super.own_mutable_copyable_attrs(self)
        for attr in self.mutable_attrs:
            if attr == 'pset':
                # special-case code for this attr, a list of gamessParms objects
                # (those objects, and the list itself, are mutable and need to be de-shared)
                val = getattr(self, attr)
                #assert type(val) == type([])
                newval = val.deepcopy()  #for item in val]
                setattr(self, attr, newval)
            else:
                print "bug: don't know how to copy attr %r in %r", attr, self
            pass
        return

    def cm_duplicate(self):  #bruce 050704.
        """
        Make a sibling node in the MT which has the same atoms, and a copy of the params, of this jig.
        """
        #e Warning: The API (used by modelTree to decide whether to offer this command) is wrong,
        # and the implem should be generalized (to work on any Node or Group). Specifically,
        # this should become a Node method which always works (whether or not it's advisable to use it);
        # then the MT cmenu should dim it if some other method (which might depend on more than just the class)
        # says it's not advisable to use it.
        #    I think it's advisable only on a Gamess jig, and on a chunk,
        # and maybe on a Group -- but what to do about contained jigs in a Group for which
        # some but not all atoms are being duplicated, or even other jigs in the Group, is a
        # design question, and it might turn out to be too ambiguous to safely offer it at all
        # for a Group with jigs in it.
        # Some code taken from Jig.copy_full_in_mapping and Jig._copy_fixup_at_end.
        copy = self.__class__(self.assy, self.atoms[:])
        orig = self
        orig.copy_copyable_attrs_to(copy)  # replaces .name set by __init__
        copy.name = copy.name + "-copy"  #e could improve
        copy.own_mutable_copyable_attrs(
        )  # eliminate unwanted sharing of mutable copyable_attrs
        if orig.picked:
            self.color = self.normcolor
        orig.addsibling(copy)
        if copy.part is None:  #bruce 050707 see if this is enough to fix bug 755
            self.assy.update_parts()
        env.history.message("Made duplicate Gamess jig on same atoms: [%s]" %
                            copy.name)
        # note: the wire cubes from multiple jigs on the sme atoms get overdrawn,
        # which will mess up the selection coloring of those wirecubes
        # since the order of drawing them is unrelated to which one is selected
        # (and since the OpenGL behavior seems a bit unpredictable anyway).
        ##e Should fix this to only draw one wirecube, of the "maximal color", I guess...
        self.assy.w.win_update()  # MT and glpane both might need update
        return

    #def set_disabled_by_user_choice(self, val):
    #    """Called when users disable/enable the jig"""
    #    self.gmsjob.edit_cntl.run_job_btn.setEnabled(not val)
    #    Jig.set_disabled_by_user_choice(self, val)
    def is_disabled(self):
        """
        Which is called when model tree is updated?
        """
        val = Jig.is_disabled(self)
        self.gmsjob.edit_cntl.run_job_btn.setEnabled(not val)
        return val

    def update_gamess_parms(self):
        """
        Update the GAMESS parameter set values using the settings in the UI object.
        """
        # $CONTRL group ###########################################

        # Parms Values
        self.pset.contrl.runtyp = runtyp[self.pset.ui.runtyp]  # RUNTYP
        self.pset.contrl.scftyp = scftyp[self.pset.ui.scftyp]  # SCFTYP
        self.pset.contrl.icharg = str(self.pset.ui.icharg)  # ICHARG
        self.pset.contrl.mult = str(self.pset.ui.mult + 1)  # MULT
        self.pset.contrl.mplevl = mplevl[self.pset.ui.ecm]  # MPLEVL
        self.pset.contrl.inttyp = inttyp[self.pset.ui.ecm]  # INTTYP
        self.pset.contrl.maxit = self.pset.ui.iterations  # Iterations

        # ICUT and QMTTOL
        #s = str(self.gbasis_combox.currentText())
        m = self.pset.ui.gbasisname.count(
            '+'
        )  # If there is a plus sign in the basis set name, we have "diffuse orbitals"
        if m:  # We have diffuse orbitals
            self.pset.contrl.icut = 11
            if self.gmsjob.server.engine != 'PC GAMESS':  # PC GAMESS does not support QMTTOL. Mark 052105
                self.pset.contrl.qmttol = '3.0E-6'
            else:
                self.pset.contrl.qmttol = None
        else:  # No diffuse orbitals
            self.pset.contrl.icut = 9
            if self.gmsjob.server.engine == 'GAMESS':
                self.pset.contrl.qmttol = '1.0E-6'
            else:
                self.pset.contrl.qmttol = None  # PC GAMESS does not support QMTTOL. Mark 052105

        # DFTTYP (PC GAMESS only)
        # For PC GAMESS, the DFTTYP keyword is included in the CONTRL section, not the $DFT group.
        if self.gmsjob.server.engine == 'PC GAMESS':
            if ecm[self.pset.ui.ecm] == 'DFT':
                item = pcgms_dfttyp_items[
                    self.pset.ui.
                    dfttyp]  # Item's full text, including the '(xxx)'
                self.pset.contrl.dfttyp, junk = item.split(
                    ' ', 1)  # DFTTYPE, removing the '(xxx)'.
                self.pset.dft.nrad = pcgms_gridsize[
                    self.pset.ui.gridsize]  # Grid Size parameters
            else:  # None or MP2
                self.pset.contrl.dfttyp = 0
                self.pset.dft.nrad = 0

        # $SCF group ###########################################

        self.pset.scf.extrap = tf[self.pset.ui.extrap]  # EXTRAP
        self.pset.scf.dirscf = tf[self.pset.ui.dirscf]  # DIRSCF
        self.pset.scf.damp = tf[self.pset.ui.damp]  # DAMP
        self.pset.scf.diis = tf[self.pset.ui.diis]  # DIIS
        self.pset.scf.shift = tf[self.pset.ui.shift]  # SHIFT
        self.pset.scf.soscf = tf[self.pset.ui.soscf]  # SOSCF
        self.pset.scf.rstrct = tf[self.pset.ui.rstrct]  # RSTRCT

        # CONV (GAMESS) or
        # NCONV (PC GAMESS)
        if self.gmsjob.server.engine == 'GAMESS':
            self.pset.scf.conv = conv[self.pset.ui.conv]  # CONV (GAMESS)
            self.pset.scf.nconv = 0  # Turn off NCONV
        else:  # PC GAMESS
            self.pset.scf.nconv = conv[self.pset.ui.conv]  # NCONV (PC GAMESS)
            self.pset.scf.conv = 0  # Turn off CONV

        # $SYSTEM group ###########################################

        self.pset.system.timlin = 1000  # Time limit in minutes
        self.pset.system.memory = self.pset.ui.memory * 1000000

        # $MP2 group ###########################################

        self.pset.mp2.ncore = ncore[self.pset.ui.ncore]

        # $DFT group ###########################################

        # The DFT section record is supported in GAMESS only.
        if self.gmsjob.server.engine == 'GAMESS':
            if ecm[self.pset.ui.ecm] == 'DFT':
                item = gms_dfttyp_items[self.pset.ui.dfttyp]
                self.pset.dft.dfttyp, junk = item.split(' ',
                                                        1)  # DFTTYP in $CONTRL
                self.pset.dft.nrad = gms_gridsize[
                    self.pset.ui.gridsize]  # Grid Size parameters
            else:  # None or MP2
                self.pset.dft.dfttyp = 'NONE'
                self.pset.dft.nrad = 0

        # $GUESS group ###########################################

        # $STATPT group ###########################################

        if runtyp[self.pset.ui.runtyp] == 'optimize':
            self.pset.statpt.opttol = float(opttol[self.pset.ui.rmsdconv])
        else:
            self.pset.statpt.opttol = None

        # $BASIS group ###########################################

        if ecm[self.pset.ui.ecm] == 'None':
            self.pset.basis.gbasis = gbasis[self.pset.ui.gbasis]  # GBASIS
        else:
            self.pset.basis.gbasis = gbasis[self.pset.ui.gbasis + 2]  # GBASIS
        return

    pass  # end of class Gamess
示例#5
0
class Gamess(Jig):
    """
    A Gamess jig has a list of atoms with one or more parameter sets used to run a GAMESS calcuation.
    """
    sym = "GAMESS"
    icon_names = ["modeltree/gamess.png", "modeltree/gamess-hide.png"]
    featurename = "GAMESS Jig" #bruce 051203

    #bruce 050704 added these attrs and related methods, to make copying of this jig work properly
    mutable_attrs = ('pset',)
    copyable_attrs = Jig.copyable_attrs + () + mutable_attrs
    
    # Default job parameters for a GAMESS job.
    job_parms = {
        'Engine':'GAMESS',
        'Calculation':'',
        'Description':"No comments? How about today's weather?",
        'Status':'',
        'Server_id':'',
        'Job_id':'',
        'Time':'0.0'}

    # create a blank Gamess jig with the given list of atoms
    def __init__(self, assy, list):
        Jig.__init__(self, assy, list)
        self.cancelled = False
        # set default color of new gamess jig to magenta
        self.color = magenta # This is the "draw" color.  When selected, this will become highlighted red.
        self.normcolor = magenta # This is the normal (unselected) color.
        #bruce 050913 thinks self.history is no longer needed:
        ## self.history = env.history
        #self.psets = [] # list of parms set objects [as of circa 050704, only the first of these is ever defined (thinks bruce)]
        self.pset = gamessParms('Parameter Set 1')
        self.gmsjob = GamessJob(Gamess.job_parms, jig=self)
        ## bruce 050701 removing this: self.gmsjob.edit()
        self.outputfile = '' # Name of jig's most recent output file. [this attr is intentionally not copied -- bruce 050704]

    def edit(self):
        self.gmsjob.edit()
        
    # it's drawn as a wire cube around each atom.
    def _draw(self, glpane, dispdef):
        for a in self.atoms:
            # Using dispdef of the atom's chunk instead of the glpane's dispdef fixes bug 373. mark 060122.
            chunk = a.molecule
            dispdef = chunk.get_dispdef(glpane)
            disp, rad = a.howdraw(dispdef)
            # wware 060203 selected bounding box bigger, bug 756
            if self.picked: rad *= 1.01
            drawwirecube(self.color, a.posn(), rad)
            
    # Write "gamess" record to POV-Ray file in the format:
    # gamess(<box-center>,box-radius,<r, g, b>)
    def writepov(self, file, dispdef):
        if self.hidden: return
        if self.is_disabled(): return
        if self.picked: c = self.normcolor
        else: c = self.color
        for a in self.atoms:
            disp, rad = a.howdraw(dispdef)
            grec = "gamess(" + povpoint(a.posn()) + "," + str(rad) + ",<" + str(c[0]) + "," + str(c[1]) + "," + str(c[2]) + ">)\n"
            file.write(grec)

    def _getinfo(self):
        return "[Object: Gamess Jig] [Name: " + str(self.name) + "] [Total Atoms: " + str(len(self.atoms)) + "] [Parameters: " + self.gms_parms_info() + "]"
     
    def _getToolTipInfo(self): #ninad060825
        """
        Return a string for display in Dynamic Tool tip
        """
        attachedAtomCount = "<font color=\"#0000FF\">Total  Atoms: </font>%d"%(len(self.atoms))
        return str(self.name) + "<br>" +  "<font color=\"#0000FF\"> Jig Type:</font>Gamess Jig"\
        + "<br>"  +  "<font color=\"#0000FF\"> Parameters:</font>" + self.gms_parms_info()\
        + "<br>" +  str(attachedAtomCount)

    def getstatistics(self, stats):
        stats.ngamess += 1

    def gms_parms_info(self, delimeter='/'):
        """
        Return a GAMESS parms shorthand string.
        """
        # This is something Damian and I discussed to quickly display the parms set for
        # a Gamess jig. It is used in the header of the GAMESS INP file and in the naming of
        # the new chunk made from a GAMESS optimization.  It is also used to display the
        # parameter info (along with the energy) when doing an energy calculation.
        # Mark 050625.
        
        d = delimeter
        
        pset = self.pset
        
        # SCFTYP (RHF, UHF, or ROHF)
        s1 = scftyp[pset.ui.scftyp]
        
        # Hartree-Fock (display nothing), DFT (display functional) or MP2
        if ecm[pset.ui.ecm] == 'DFT':
            if sys.platform == 'win32': # Windows - PC GAMESS
                item = pcgms_dfttyp_items[pset.ui.dfttyp]
            else: # Linux or MacOS - GAMESS
                item = gms_dfttyp_items[pset.ui.dfttyp]
            
            s2, junk = item.split(' ',1)
            s2 = d + s2
        elif ecm[pset.ui.ecm] == 'MP2':
            s2 = d + 'MP2'
        else:
            s2 = ''
        
        # Basis Set    
        s3 = d + pset.ui.gbasisname
        
        # Charge
        s4 = d + 'Ch' + str(pset.ui.icharg)
        
        # Multiplicity
        s5 = d + 'M' + str(pset.ui.mult + 1)

        return s1 + s2 + s3 + s4 + s5
                        
    def __CM_Calculate_Energy(self):
        """
        Gamess Jig context menu "Calculate Energy"
        """
        self.calculate_energy()
        
    def calculate_energy(self):
        """
        Calculate energy.
        """
        
        cmd = greenmsg("Calculate Energy: ")
        
        errmsgs = ["GAMESS job aborted.",
                            "Error: GAMESS job failed."]
                            
        pset = self.pset
        runtyp = pset.ui.runtyp # Save runtyp (Calculate) setting to restore it later.
        pset.ui.runtyp = 0 # Energy calculation
        origCalType = self.gmsjob.Calculation
        self.gmsjob.Calculation = 'Energy'
        
        self.update_gamess_parms()
        
        # Run GAMESS job.  Return value r:
        # 0 = success
        # 1 = job aborted
        # 2 = job failed.
        r = self.gmsjob.launch()
        
        pset.ui.runtyp = runtyp # Restore to original value
        self.gmsjob.Calculation = origCalType
        
        if r: # Job was aborted or an error occurred.
            msg = redmsg(errmsgs[r-1])
            env.history.message( cmd + msg )
            return
            
        self.print_energy()
            
    def __CM_Optimize_Geometry(self):
        """
        Gamess Jig context menu "Optimize Geometry"
        """
        self.optimize_geometry()
        
    def optimize_geometry(self):
        """
        Optimize geometry
        """
        
        cmd = greenmsg("Optimize Geometry: ")
        
        errmsgs = ["GAMESS job aborted.",
                            "Error: GAMESS job failed."]
                            
        pset = self.pset
        runtyp = pset.ui.runtyp # Save runtyp (Calculate) setting to restore it later.
        pset.ui.runtyp = 1 # Optimize
        origCalType = self.gmsjob.Calculation
        self.gmsjob.Calculation = 'Optimize'
        
        self.update_gamess_parms()
        
        # Run GAMESS job.  Return value r:
        # 0 = success
        # 1 = job aborted.
        # 2 = job failed.
        r = self.gmsjob.launch()
        
        pset.ui.runtyp = runtyp # Restore to original value
        self.gmsjob.Calculation = origCalType
        
        if r: # Job was aborted or an error occurred.
            msg = redmsg(errmsgs[r-1])
            env.history.message( cmd + msg )
            return
        
        try:
            r2 = self.move_optimized_atoms()
        except:
            print_compact_traceback( "GamessProp.run_job(): error reading GAMESS OUT file [%s]: " % \
                    self.outputfile )
            env.history.message( redmsg( cmd + "Internal error while inserting GAMESS geometry: " + self.outputfile) )
        else:
            if r2:
                env.history.message(cmd + redmsg( "Atoms not adjusted."))
            else:
                self.assy.changed() # The file and the part are not the same.
                self.print_energy() # Print the final energy from the optimize OUT file, too.
                env.history.message( cmd + "Atoms adjusted.")
    
    def __CM_Optimize_Geometry__options(self):
        if Jig.is_disabled(self):
            return ['disabled']
        else:
            return []
    
    def __CM_Calculate_Energy__options(self):
        if Jig.is_disabled(self):
            return ['disabled']
        else:
            return []
        pass

    def print_energy(self):
        
        r, final_energy_str = get_energy_from_gms_outfile(self.outputfile)

        if r == 1: # GAMESS terminated abnormally.
            if final_energy_str:
                env.history.message(redmsg(final_energy_str + " Check if you have set the right Gamess executable file. Usually it's called gamess.??.x or ??gamess.exe."))
                return
                
            msg = "Final energy value not found. The output file is located at: " + self.outputfile
            env.history.message(redmsg(msg))
        
        elif r == 2: # The output file not exist
            msg = "The output file %s doesn't exist. The reason is either that Gamess didn't run or the output file has been deleted. " % self.outputfile
            env.history.message(redmsg(msg))
            
        else: # Final energy was found.
            gmstr = self.gms_parms_info()
            msg = "GAMESS finished. The output file is located at: " + self.outputfile
            env.history.message(msg)
            msg = "Parameters: " + gmstr + ".  The final energy is: " + final_energy_str + " Hartree."
            env.history.message(msg)

    mmp_record_name = "gamess" #bruce 050701


    def move_optimized_atoms(self):
        
        newPositions = get_atompos_from_gms_outfile( self.assy, self.outputfile, self.atoms )
        # retval is either a list of atom posns or an error message string.
        assert type(newPositions) in [type([]),type("")]
        if type(newPositions) == type([]):
            self.move_atoms(newPositions)
            self.assy.changed()
            self.assy.o.gl_update()
            #self.assy.w.win_update()
            return 0
        else:
            env.history.message(redmsg( newPositions))
            return 1
                
    def move_atoms(self, newPositions): # used when reading xyz files
        """
        [Borrowed from movie.moveAtoms.]
        
        Move a list of atoms to newPosition. After 
        all atoms moving, bond updated, update display once.
        <parameter>newPosition is a list of atom absolute position,
        the list order is the same as self.alist
        """   
        
        atomList = self.atoms
        
        if len(newPositions) != len(atomList):
            #bruce 050225 added some parameters to this error message
            #bruce 050406 comment: but it probably never comes out, since readxyz checks this,
            # so I won't bother to print it to history here. But leaving it in is good for safety.
            print "move_atoms: The number of atoms from GAMESS file (%d) is not matching with that of the current model (%d)" % \
                  (len(newPositions), len(atomList))
            return
        move_atoms_and_normalize_bondpoints(atomList, newPositions)
            #bruce 051221 fix a bug analogous to bug 1239 by letting this new function (containing a loop)
            # replace a copy (which was right here) of the older buggy version of that loop
        self.assy.o.gl_update()
        return
                        
    def writemmp(self, mapping): #bruce 050701
        "[extends Jig method]"
        super = Jig
        super.writemmp(self, mapping) # this writes the main gamess record, and some general info leaf records valid for all nodes
        pset = self.pset
        pset.writemmp(mapping, 0)
            # This writes the pset's info, as a series of "info gamess" records which modify the last gamess jig;
            # in case we ever want to extend this to permit more than one pset per jig in the mmp file,
            # each of those records has a "pset index" which we pass here as 0 (and which is written using "%s").
            # So if we wanted to write more psets we could say self.psets[i].writemmp(mapping, i) for each one.
        return

    def readmmp_info_gamess_setitem( self, key, val, interp ): #bruce 050701
        """
        This is called when reading an mmp file, for each "info gamess" record
        which occurs right after this node is read and no other (gamess jig) node has been read.
           Key is a list of words, val a string; the entire record format
        is presently [050701] "info gamess <key> = <val>", and there are exactly
        two words in <key>, the "parameter set index" (presently always 0) and the "param name".
           Interp is an object to help us translate references in <val>
        into other objects read from the same mmp file or referred to by it.
        See the calls of this or similar methods from files_mmp for the doc of interp methods.
           If key is recognized, this method should set the attribute or property
        it refers to to val; otherwise it must do nothing.
           (An unrecognized key, even if longer than any recognized key,
        is not an error. Someday it would be ok to warn about an mmp file
        containing unrecognized info records or keys, but not too verbosely
        (at most once per file per type of info).)
        """
        if len(key) != 2 or not key[0].isdigit():
            if debug_flags.atom_debug:
                print "atom_debug: fyi: info gamess with unrecognized key %r (not an error)" % (key,)
            return
        pset_index, name = key
        pset_index = int(pset_index)
            # pset_index is presently always 0, but this code should work provided self.psets has an element with this index;
        try:
            pset = self.pset
        except:
            # not an error -- future mmp formats might use non-existent indices and expect readers to create new psets.
            if debug_flags.atom_debug:
                print "atom_debug: fyi: info gamess with non-existent pset index in key %r (not an error)" % (key,)
            return
        # the rest of the work should be done by the pset.
        try:
            self.pset.info_gamess_setitem( name, val, interp )
            
        except:
            print_compact_traceback("bug: exception (ignored) in pset.info_gamess_setitem( %r, %r, interp ): " % (name,val) )
            return
        pass
    
    def own_mutable_copyable_attrs(self): #bruce 050704
        """
        [overrides Node method]
        """
        super = Jig
        super.own_mutable_copyable_attrs( self)
        for attr in self.mutable_attrs:
            if attr == 'pset':
                # special-case code for this attr, a list of gamessParms objects
                # (those objects, and the list itself, are mutable and need to be de-shared)
                val = getattr(self, attr)
                #assert type(val) == type([])
                newval = val.deepcopy()#for item in val]
                setattr(self, attr, newval)
            else:
                print "bug: don't know how to copy attr %r in %r", attr, self
            pass
        return

    def cm_duplicate(self): #bruce 050704.
        """
        Make a sibling node in the MT which has the same atoms, and a copy of the params, of this jig.
        """
            #e Warning: The API (used by modelTree to decide whether to offer this command) is wrong,
            # and the implem should be generalized (to work on any Node or Group). Specifically,
            # this should become a Node method which always works (whether or not it's advisable to use it);
            # then the MT cmenu should dim it if some other method (which might depend on more than just the class)
            # says it's not advisable to use it.
            #    I think it's advisable only on a Gamess jig, and on a chunk,
            # and maybe on a Group -- but what to do about contained jigs in a Group for which
            # some but not all atoms are being duplicated, or even other jigs in the Group, is a
            # design question, and it might turn out to be too ambiguous to safely offer it at all
            # for a Group with jigs in it.
        # Some code taken from Jig.copy_full_in_mapping and Jig._copy_fixup_at_end.
        copy = self.__class__( self.assy, self.atoms[:] )
        orig = self
        orig.copy_copyable_attrs_to(copy) # replaces .name set by __init__
        copy.name = copy.name + "-copy" #e could improve
        copy.own_mutable_copyable_attrs() # eliminate unwanted sharing of mutable copyable_attrs
        if orig.picked:
            self.color = self.normcolor
        orig.addsibling(copy)
        if copy.part is None: #bruce 050707 see if this is enough to fix bug 755
            self.assy.update_parts()
        env.history.message( "Made duplicate Gamess jig on same atoms: [%s]" % copy.name )
            # note: the wire cubes from multiple jigs on the sme atoms get overdrawn,
            # which will mess up the selection coloring of those wirecubes
            # since the order of drawing them is unrelated to which one is selected
            # (and since the OpenGL behavior seems a bit unpredictable anyway).
            ##e Should fix this to only draw one wirecube, of the "maximal color", I guess...
        self.assy.w.win_update() # MT and glpane both might need update
        return
    
    
    #def set_disabled_by_user_choice(self, val):
    #    """Called when users disable/enable the jig"""
    #    self.gmsjob.edit_cntl.run_job_btn.setEnabled(not val)
    #    Jig.set_disabled_by_user_choice(self, val)
    def is_disabled(self):
        """
        Which is called when model tree is updated?
        """
        val = Jig.is_disabled(self)
        self.gmsjob.edit_cntl.run_job_btn.setEnabled(not val)
        return val
   
    def update_gamess_parms(self):
        """
        Update the GAMESS parameter set values using the settings in the UI object.
        """
        # $CONTRL group ###########################################
        
        # Parms Values
        self.pset.contrl.runtyp = runtyp[self.pset.ui.runtyp] # RUNTYP
        self.pset.contrl.scftyp = scftyp[self.pset.ui.scftyp] # SCFTYP
        self.pset.contrl.icharg = str(self.pset.ui.icharg) # ICHARG
        self.pset.contrl.mult = str(self.pset.ui.mult + 1) # MULT
        self.pset.contrl.mplevl = mplevl[self.pset.ui.ecm] # MPLEVL
        self.pset.contrl.inttyp = inttyp[self.pset.ui.ecm] # INTTYP
        self.pset.contrl.maxit = self.pset.ui.iterations # Iterations
        
        # ICUT and QMTTOL
        #s = str(self.gbasis_combox.currentText())
        m = self.pset.ui.gbasisname.count('+') # If there is a plus sign in the basis set name, we have "diffuse orbitals"
        if m: # We have diffuse orbitals
            self.pset.contrl.icut = 11
            if self.gmsjob.server.engine != 'PC GAMESS': # PC GAMESS does not support QMTTOL. Mark 052105
                self.pset.contrl.qmttol = '3.0E-6'
            else:
                self.pset.contrl.qmttol = None
        else:  # No diffuse orbitals
            self.pset.contrl.icut = 9
            if self.gmsjob.server.engine == 'GAMESS': 
                self.pset.contrl.qmttol = '1.0E-6'
            else:
                self.pset.contrl.qmttol = None # PC GAMESS does not support QMTTOL. Mark 052105
        
        # DFTTYP (PC GAMESS only)
        # For PC GAMESS, the DFTTYP keyword is included in the CONTRL section, not the $DFT group.
        if self.gmsjob.server.engine == 'PC GAMESS':
            if ecm[self.pset.ui.ecm] == 'DFT':
                item = pcgms_dfttyp_items[self.pset.ui.dfttyp] # Item's full text, including the '(xxx)'
                self.pset.contrl.dfttyp, junk = item.split(' ',1) # DFTTYPE, removing the '(xxx)'.
                self.pset.dft.nrad = pcgms_gridsize[self.pset.ui.gridsize] # Grid Size parameters
            else: # None or MP2
                self.pset.contrl.dfttyp = 0
                self.pset.dft.nrad = 0
        
        # $SCF group ###########################################
        
        self.pset.scf.extrap = tf[self.pset.ui.extrap] # EXTRAP
        self.pset.scf.dirscf = tf[self.pset.ui.dirscf] # DIRSCF
        self.pset.scf.damp = tf[self.pset.ui.damp] # DAMP
        self.pset.scf.diis = tf[self.pset.ui.diis] # DIIS
        self.pset.scf.shift = tf[self.pset.ui.shift] # SHIFT
        self.pset.scf.soscf = tf[self.pset.ui.soscf] # SOSCF
        self.pset.scf.rstrct = tf[self.pset.ui.rstrct] # RSTRCT
        
        # CONV (GAMESS) or 
        # NCONV (PC GAMESS)
        if self.gmsjob.server.engine == 'GAMESS':
            self.pset.scf.conv = conv[self.pset.ui.conv] # CONV (GAMESS)
            self.pset.scf.nconv = 0 # Turn off NCONV
        else: # PC GAMESS
            self.pset.scf.nconv = conv[self.pset.ui.conv] # NCONV (PC GAMESS)
            self.pset.scf.conv = 0 # Turn off CONV
        
        # $SYSTEM group ###########################################
        
        self.pset.system.timlin = 1000 # Time limit in minutes
        self.pset.system.memory = self.pset.ui.memory * 1000000
        
        # $MP2 group ###########################################
        
        self.pset.mp2.ncore = ncore[self.pset.ui.ncore]
        
        # $DFT group ###########################################

        # The DFT section record is supported in GAMESS only.
        if self.gmsjob.server.engine == 'GAMESS':
            if ecm[self.pset.ui.ecm] == 'DFT':
                item = gms_dfttyp_items[self.pset.ui.dfttyp]
                self.pset.dft.dfttyp, junk = item.split(' ',1) # DFTTYP in $CONTRL
                self.pset.dft.nrad = gms_gridsize[self.pset.ui.gridsize] # Grid Size parameters
            else: # None or MP2
                self.pset.dft.dfttyp = 'NONE'
                self.pset.dft.nrad = 0
        
        # $GUESS group ###########################################
        
        # $STATPT group ###########################################
        
        if runtyp[self.pset.ui.runtyp] == 'optimize':
            self.pset.statpt.opttol = float(opttol[self.pset.ui.rmsdconv])
        else:
            self.pset.statpt.opttol = None
        
        # $BASIS group ###########################################
        
        if ecm[self.pset.ui.ecm] == 'None':
            self.pset.basis.gbasis = gbasis[self.pset.ui.gbasis] # GBASIS
        else:
            self.pset.basis.gbasis = gbasis[self.pset.ui.gbasis + 2] # GBASIS
        return
    
    pass # end of class Gamess