summaryrefslogtreecommitdiff
path: root/cad/src/analysis/GAMESS/jig_Gamess.py
blob: 03d2253ac182850b2e208fb213558d87cff1b47e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
# Copyright 2005-2008 Nanorex, Inc.  See LICENSE file for details.
"""
jig_Gamess.py

@author: Mark
@version: $Id$
@copyright: 2005-2008 Nanorex, Inc.  See LICENSE file for details.
"""

import sys

from model.jigs import Jig
from graphics.drawing.drawers import drawwirecube
from graphics.rendering.povray.povheader import povpoint # Fix for bug 692 Mark 050628
from analysis.GAMESS.files_gms import get_energy_from_gms_outfile, get_atompos_from_gms_outfile
from utilities.Log import redmsg, greenmsg
import foundation.env as env
from utilities.Log import redmsg
from operations.move_atoms_and_normalize_bondpoints import move_atoms_and_normalize_bondpoints
import foundation.state_utils as state_utils
from utilities.debug import print_compact_traceback

from analysis.GAMESS.GamessJob import GamessJob
from analysis.GAMESS.GamessProp import scftyp
from analysis.GAMESS.GamessProp import ecm
from analysis.GAMESS.GamessProp import pcgms_dfttyp_items
from analysis.GAMESS.GamessProp import gms_dfttyp_items
from analysis.GAMESS.GamessProp import runtyp
from analysis.GAMESS.GamessProp import mplevl
from analysis.GAMESS.GamessProp import inttyp
from analysis.GAMESS.GamessProp import pcgms_gridsize
from analysis.GAMESS.GamessProp import tf
from analysis.GAMESS.GamessProp import conv
from analysis.GAMESS.GamessProp import ncore
from analysis.GAMESS.GamessProp import gms_gridsize
from analysis.GAMESS.GamessProp import opttol
from analysis.GAMESS.GamessProp import gbasis
from analysis.GAMESS.GamessProp import contrl
from analysis.GAMESS.GamessProp import scf
from analysis.GAMESS.GamessProp import system
from analysis.GAMESS.GamessProp import mp2
from analysis.GAMESS.GamessProp import dft
from analysis.GAMESS.GamessProp import guess
from analysis.GAMESS.GamessProp import statpt
from analysis.GAMESS.GamessProp import basis
from analysis.GAMESS.GamessProp import ui

from utilities import debug_flags

from utilities.constants import magenta

from files.mmp.files_mmp_registration import MMP_RecordParser
from files.mmp.files_mmp_registration import register_MMP_RecordParser

from files.mmp.files_mmp import mmp_interp_just_for_decode_methods # for a kluge

# == GAMESS

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

# ==

class gamessParms(state_utils.DataMixin): #bruce 060306 added superclass
    def __init__(self, name):
        """
        A GAMESS parameter set contains all the parameters for a Gamess Jig.

        The ui ctlRec object is the "master".  From it, all the other ctlRec objects
        have their parms set/reset, in GamessProp._save_parms(), each time the user
        selects "Save and Run" or "Save".  The reason for this has to do with
        the fact that there is not a one-to-one relationship between UI settings (in the
        Gamess Jig Properties dialog) and the parameters written to the GAMESS
        input file.  There are all sorts of strange combinations and permutations
        between the UI settings and what the GAMESS input file parameters end up being.
        This is also why it is very difficult (but not impossible) to go from a raw GAMESS
        input file to the proper UI settings in the Gamess Jig Properties dialog.

        Many parameters have a value for the ui object and another value in one of
        the other ctlRec objects.  The ui object is used to setup the UI and
        read/write parms to/from the MMP file. The values for the other
        ctlRec objects are set (and only important) when writing the GAMESS input file.
        """
        self.name = name or "" # Parms set name, assumed to be a string by some code
        self.ui = ctlRec('UI', ui) # "Master" ui object.
        self.contrl = ctlRec('CONTRL',contrl) # $CONTRL group object
        self.scf = ctlRec('SCF',scf) # $SCF group object
        self.system = ctlRec('SYSTEM',system) # $SYSTEM group object
        self.mp2 = ctlRec('MP2',mp2) # $MP2 group object
        self.dft = ctlRec('DFT',dft) # $DFT group object
        self.guess = ctlRec('GUESS',guess) # $GUESS group object
        self.statpt = ctlRec('STATPT',statpt) # $STATPT group object
        self.basis = ctlRec('BASIS',basis) # $BASIS group object

    def prin1(self, f=None):
        """
        Write all parms to input file
        """
        self.contrl.prin1(f)
        self.scf.prin1(f)
        self.system.prin1(f)
        self.mp2.prin1(f)
        self.dft.prin1(f)
        self.guess.prin1(f)
        self.statpt.prin1(f)
#        self.force.prin1()
        self.basis.prin1(f)

    def param_names_and_valstrings(self, canonical = False): #bruce 050701; extended by Mark 050704 to return the proper set of params
        """
        Return a list of pairs of (<param name>, <param value printable by %s>) for all
        gamess params we want to write to an mmp file from this set, sorted by <param name>.

        These names and value-strings need to be recognized and decoded by the
        info_gamess_setitem method of this class, and they need to strictly follow certain rules
        documented in comments in the self.writemmp() method.
        """
        # Note: If we implement a "duplicate" context menu command for gamess jigs,
        # it should work by generating this same set of items, and feeding them to that
        # same info_gamess_setitem method (or an appropriate subroutine it calls)
        # of the new jig being made as a copy.
        # [Since that was written, we did it differently (see cm_duplicate); we should
        # review someday whether this advice is obsolete or should have been taken. -- bruce 060307]
        return self.ui.get_mmp_parms(canonical = canonical)

    def deepcopy(self, alter_name = True): #bruce 051003 added alter_name, but I don't know if passing False is ever legal. ###@@@
        #bruce 050704; don't know whether this is complete [needs review by Mark; is it ok it only sets .ui?]
        """
        Make a copy of self (a gamessParms object), which shares no mutable state with self.
        (Used to copy a Gamess Jig containing self.)
        """
        if alter_name:
            newname = self.name + " copy"
            # copy needs a different name #e could improve this -- see the code used to rename chunk copies
        else:
            newname = self.name
        new = self.__class__(newname)
        interp = mmp_interp_just_for_decode_methods() #kluge
        for name, valstring in self.param_names_and_valstrings():
            valstring = "%s" % (valstring,)
            valstring = valstring.strip()
            # we're too lazy to also check whether valstring is multiline or too long, like writemmp does;
            # result of this is only that some bugs will show up in writemmp but not in deepcopy (used to copy this kind of jig).
            new.info_gamess_setitem( name, valstring, interp, error_if_name_not_known = True )
        return new

    # override abstract method of DataMixin
    def _copyOfObject(self): #bruce 051003, for use by state_utils.copy_val
        return self.deepcopy(alter_name = False) ###k I'm not sure alter_name = False can ever be legal,
            # or (if it can be) whether it's good here. I think Mark or I should review this,
            # and we should not change the code to rely on copy_val alone on this object
            # (i.e. we should not remove the mutable_attr decl for pset and the related code that calls deepcopy directly)
            # until that's reviewed. [bruce 051003]

    # override abstract method of DataMixin
    def __eq__(self, other): #bruce 060306-08 for Undo bug 1616
        # note: defining __eq__ is sufficient, but only because we inherit
        # from DataMixin, which defines __ne__ based on __eq__
        if other.__class__ is not self.__class__:
            return False
        return self.param_names_and_valstrings(canonical = True) == other.param_names_and_valstrings(canonical = True)
            # without canonical = True, self has 0/1 where other has False/True, which caused first try of __eq__ to not fix bug 1616.

    def writemmp(self, mapping, pset_index): #bruce 050701
        mapping.write("# gamess parameter set %s for preceding jig\n" % pset_index)
            # you can write any comment starting "# " into an mmp file (if length < 512).
            # You always have to explicitly write the newline at the end (when using mapping.write).
            # But this is not for comments which need to be read back in and shown in the params dialog!
            # Those need to be string-valued params (and not contain newlines, or encode those if they do).
        items = self.param_names_and_valstrings()
            # Rules for these name/valstring pairs [bruce 050701]:
            # param names must not contain whitespace.
            # valstrings must not start or end with whitespace, or contain newlines, but they can contain blanks or tabs.
            # (if you need to write comments that might contain newlines, these must be encoded somehow as non-newlines.)
            # it's ok to append comments "# ..." to valstrings, but only if these are noticed and stripped by the parsing methods
            # you also write.
            # entire line must be <512 chars in length (this limit applies to any line in any mmp file).
            # if valstring might be too long, you have to truncate it or split it into more than one valstring.
        for name, valstring in items:
            assert type(name) == type("")
            assert name and (' ' not in name) and len(name.split()) == 1 and name.strip() == name, "illegal param name %r" % name
                # some of these checks are redundant
            valstring = "%s" % (valstring,)
            # the next bit of code is just to work around bugs in valstrings without completely failing to write them.
            valstring = valstring.strip() # the reader does this, so we might as well not fool ourselves and do it now
            if '\n' in valstring:
                print "error: multiline valstring in gamess writemmp. workaround: writing only the first line."
                valstring = valstring.split('\n',1)[0]
                valstring = valstring.strip()
            line = "info gamess %s %s = %s\n" % (pset_index, name, valstring)
            if len(line) > 511:
                msg = "can't write this mmp line (too long for mmp format): " + line
                    #bruce 050913 comment: this restriction might no longer be valid for sim executables as of a few days ago
                print msg
                env.history.message( redmsg( "Error: " + msg) )
                mapping.write("# didn't write too-long valstring for info gamess %s %s = ...\n" % (pset_index, name))
            else:
                mapping.write(line)
        mapping.write("# end of gamess parameter set %s\n" % pset_index)
        return

    boolparms = ('damp', 'diis', 'dirscf', 'extrap', 'ncore', 'rstrct', 'shift', 'soscf' )
        # these MUST MATCH info_gamess_setitem uses of decode_bool [bruce 060307, to help with bug 1616]

    def info_gamess_setitem(self, name, val, interp, error_if_name_not_known = False):
        #bruce 050701; extended by Mark 050704 to read and set the actual params; bruce 050704 added error_if_name_not_known
        """
        This must set the parameter in self with the given name
        to the value encoded by the string val
        (read from an mmp file from which this parameter set and its gamess jig is being read).
           If it doesn't recognize name or can't parse val,
        it should do nothing (except possibly print a message if atom_debug is set),
        unless error_if_name_not_known is true, in which case it should print an error message reporting a bug.
           (If it's too tedious to avoid exceptions in parsing val,
        change the caller (which already ignores those exceptions, but always prints a message calling them bugs)
        to classify those exceptions as not being bugs (and to only print a message when atom_debug is set).

        [See also the docstring of Gamess.readmmp_info_gamess_setitem, which calls this.]
        """
        if name == 'comment':       # Description/Comment
            self.ui.comment = val
        elif name == 'conv':            # Density and Energy Convergence (1-4)
            p = interp.decode_int(val)
            if p is not None:
                self.ui.conv = p
        elif name == 'damp':            # DAMP
            p = interp.decode_bool(val) # if you add, remove, or rename decode_bool params here, you must also fix boolparms above
            if p is not None:
                self.ui.damp = p
        elif name == 'dfttyp':          # DFT Functional Type
            p = interp.decode_int(val)
            if p is not None:
                self.ui.dfttyp = p
        elif name == 'diis':            # DIIS
            p = interp.decode_bool(val)
            if p is not None:
                self.ui.diis = p
        elif name == 'dirscf':          # DIRSCF
            p = interp.decode_bool(val)
            if p is not None:
                self.ui.dirscf = p
        elif name == 'ecm':            # emc = None (0), DFT (1) or MP2 (2)
            p = interp.decode_int(val)
            if p is not None:
                self.ui.ecm = p
        elif name == 'extrap':          # EXTRAP
            p = interp.decode_bool(val)
            if p is not None:
                self.ui.extrap = p
        elif name == 'gbasis':            # Basis Set Id
            p = interp.decode_int(val)
            if p is not None:
                self.ui.gbasis = p
        elif name == 'gbasisname':      # Basis Set Name
            self.ui.gbasisname = val
        elif name == 'gridsize':            # Grid Size
            p = interp.decode_int(val)
            if p is not None:
                self.ui.gridsize = p
        elif name == 'icharg':            # Charge
            p = interp.decode_int(val)
            if p is not None:
                self.ui.icharg = p
        elif name == 'iterations':            # Iterations
            p = interp.decode_int(val)
            if p is not None:
                self.ui.iterations = p
        elif name == 'memory':            # System Memory
            p = interp.decode_int(val)
            if p is not None:
                self.ui.memory = p
        elif name == 'mult':            # Multiplicity
            p = interp.decode_int(val)
            if p is not None:
                self.ui.mult = p
        elif name == 'ncore':            # Include core electrons
            p = interp.decode_bool(val)
            if p is not None:
                self.ui.ncore = p
        elif name == 'rmsdconv':            # RMSD convergence (1-4)
            p = interp.decode_int(val)
            if p is not None:
                self.ui.rmsdconv = p
        elif name == 'rstrct':          # RSTRCT
            p = interp.decode_bool(val)
            if p is not None:
                self.ui.rstrct = p
        elif name == 'runtyp':            # RUNTYP = Energy (0), or Optimize (1)
            p = interp.decode_int(val)
            if p is not None:
                self.ui.runtyp = p
        elif name == 'scftyp':            # SCFTYP = RHF (0), UHF (1), or ROHF (2)
            p = interp.decode_int(val)
            if p is not None:
                self.ui.scftyp = p
        elif name == 'shift':          # SHIFT
            p = interp.decode_bool(val)
            if p is not None:
                self.ui.shift = p
        elif name == 'soscf':          # SOSCF
            p = interp.decode_bool(val)
            if p is not None:
                self.ui.soscf = p

        # Unused - keeping them for examples.
        # Mark 050603
        elif name == 'param2':
            self.param2 = val.split() # always legal for strings
        elif name == 'param3':
            p3 = interp.decode_int(val) # use this method for int-valued params
            if p3 is not None:
                self.param3 = p3
            # otherwise it was a val we don't recognize as an int; not an error
            # (since the mmp file format might be extended to permit it),
            # but a debug message was printed if atom_debug is set.
        elif name == 'param4':
            p4 = interp.decode_bool(val) # use this method for boolean-valued params
                # (they can be written as 0, 1, False, True, or in a few other forms)
            if p4 is not None:
                self.param4 = p4

        else:
            if error_if_name_not_known:
                #bruce 050704, only correct when this method is used internally to copy an object of this class
                print "error: unrecognized parameter name %r in info_gamess_setitem" % (name,)
            elif debug_flags.atom_debug:
                print "atom_debug: fyi: info gamess with unrecognized parameter name %r (not an error)" % (name,)
            # this is not an error, since old code might read newer mmp files which know about more gamess params;
            # it's better (in general) to ignore those than for this to make it impossible to read the mmp file.
            # If non-debug warnings were added, that might be ok in this case since not many lines per file will trigger them.

        return # from info_gamess_setitem

    pass # end of class gamessParms

# ==

_boolparms = {} # used in ctlRec [bruce 060308]
for p in gamessParms.boolparms:
    _boolparms[p] = None

class ctlRec:
    def __init__(self, name, parms):
        self.name = name
        self.parms = parms.keys()
        self.parms.sort() # Sort parms.

        # WARNING: Bugs will be caused if any of ctlRec's own methods or
        # instance variables had the same name as any of the parameter ('k') values.

        for k in self.parms:
            self.__dict__[k] = parms[k]

    def prin1(self, f):
        """
        Write parms group to input file
        """
        f.write (" $"  + self.name + ' ')
        col = len(self.name) + 3
        for k in self.parms:
            if not self.__dict__[k]: continue # Do not print null parms.
            phrase = k + '=' + str(self.__dict__[k])
            col += 1 + len(phrase)
            if col > 70:
                col = len(phrase)
                f.write ('\n')
            f.write (phrase + ' ')
        f.write('$END\n')

    def get_mmp_parms(self, canonical = False):
        """
        Return a list of pairs (parmname, str(parmvalue)) of all the Gamess jig parms
        (and str() of their values) to be stored in the MMP file, sorted by parm name.

        If option canonical = True, canonicalize the values for comparison
        (i.e. use "False" and "True" rather than "0" and "1", for all booleans).
        WARNING: This option is only reviewed for correctness in the instance of this class
        stored in the .ui attribute of its gamesParms object.
        """
        #bruce 060308 added canonical option, to help fix bug 1616
        #bruce 060307 revised docstring to fit code (added str())
        items = []

        for p in self.parms: # note: self.parms is already sorted [bruce 060307 comment]
            val = self.__dict__[p]
            if canonical and p in _boolparms:
                val = not not val
            items.append((p, str(val)))

        return items

    pass # end of class ctlRec

# ==

class _MMP_RecordParser_for_Gamess( MMP_RecordParser): #bruce 071023
    """
    Read the first line of the MMP record for a Gamess Jig.
    (Subsequent lines are "info records" which are
    read separately and interpreted by methods in the new
    Gamess object created by our read_record method.)
    """
    def read_record(self, card): #bruce 050701
        constructor = Gamess
        jig = self.read_new_jig(card, constructor)
            # note: this includes a call of self.addmember
            # to add the new jig into the model
        # for interpreting "info gamess" records:
        self.set_info_object('gamess', jig)
        return
    pass

def register_MMP_RecordParser_for_Gamess():
    """
    [call this during init, before reading any mmp files]
    """
    register_MMP_RecordParser( 'gamess', _MMP_RecordParser_for_Gamess )
    return

# end