source: trunk/GSASIIrestrGUI.py @ 1097

Last change on this file since 1097 was 1097, checked in by vondreele, 10 years ago

mods to restraint GUI & LS output for restraints
correct torsion & Ramachandran penalties

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 86.1 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIrestr - restraint GUI routines
3########### SVN repository information ###################
4# $Date: 2013-10-10 15:35:50 +0000 (Thu, 10 Oct 2013) $
5# $Author: vondreele $
6# $Revision: 1097 $
7# $URL: trunk/GSASIIrestrGUI.py $
8# $Id: GSASIIrestrGUI.py 1097 2013-10-10 15:35:50Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIrestrGUI: Restraint GUI routines*
12----------------------------------------
13
14Used to define restraints.
15
16'''
17import wx
18import wx.grid as wg
19import time
20import numpy as np
21import numpy.ma as ma
22import os.path
23import GSASIIpath
24GSASIIpath.SetVersionNumber("$Revision: 1097 $")
25import GSASIImath as G2mth
26import GSASIIlattice as G2lat
27import GSASIIspc as G2spc
28import GSASIIgrid as G2gd
29import GSASIIplot as G2plt
30import GSASIIdata as G2data
31
32VERY_LIGHT_GREY = wx.Colour(235,235,235)
33
34################################################################################
35#####  Restraints
36################################################################################           
37       
38def UpdateRestraints(G2frame,data,Phases,phaseName):
39    '''Respond to selection of the Restraints item on the
40    data tree
41    '''
42    if not Phases:
43        print 'There are no phases to form restraints'
44        return
45    if not len(Phases):
46        print 'There are no phases to form restraints'
47        return
48    phasedata = Phases[phaseName]
49    if phaseName not in data:
50        data[phaseName] = {}
51    restrData = data[phaseName]
52    if 'Bond' not in restrData:
53        restrData['Bond'] = {'wtFactor':1.0,'Range':1.1,'Bonds':[],'Use':True}
54    if 'Angle' not in restrData:
55        restrData['Angle'] = {'wtFactor':1.0,'Range':0.85,'Angles':[],'Use':True}
56    if 'Plane' not in restrData:
57        restrData['Plane'] = {'wtFactor':1.0,'Planes':[],'Use':True}
58    if 'Chiral' not in restrData:
59        restrData['Chiral'] = {'wtFactor':1.0,'Volumes':[],'Use':True}
60    if 'Torsion' not in restrData:
61        restrData['Torsion'] = {'wtFactor':1.0,'Coeff':{},'Torsions':[],'Use':True}
62    if 'Rama' not in restrData:
63        restrData['Rama'] = {'wtFactor':1.0,'Coeff':{},'Ramas':[],'Use':True}
64    if 'Texture' not in restrData:
65        restrData['Texture'] = {'wtFactor':1.0,'HKLs':[],'Use':True}
66    if 'ChemComp' not in restrData:
67        restrData['ChemComp'] = {'wtFactor':1.0,'Sites':[],'Use':True}
68    General = phasedata['General']
69    Cell = General['Cell'][1:7]          #skip flag & volume   
70    Amat,Bmat = G2lat.cell2AB(Cell)
71    SGData = General['SGData']
72    cx,ct,cs = General['AtomPtrs'][:3]
73    Atoms = phasedata['Atoms']
74    AtLookUp = G2mth.FillAtomLookUp(Atoms)
75    if 'macro' in General['Type']:
76        Names = [atom[0]+':'+atom[1]+atom[2]+' '+atom[3] for atom in Atoms]
77        Ids = []
78        Coords = []
79        Types = []
80    else:   
81        Names = ['all '+ name for name in General['AtomTypes']]
82        iBeg = len(Names)
83        Types = [name for name in General['AtomTypes']]
84        Coords = [ [] for type in Types]
85        Ids = [ 0 for type in Types]
86        Names += [atom[ct-1] for atom in Atoms]
87    Types += [atom[ct] for atom in Atoms]
88    Coords += [atom[cx:cx+3] for atom in Atoms]
89    Ids += [atom[-1] for atom in Atoms]
90    rama = G2data.ramachandranDist['All']
91    ramaName = 'All'
92   
93    def OnSelectPhase(event):
94        dlg = wx.SingleChoiceDialog(G2frame,'Select','Phase',Phases.keys())
95        try:
96            if dlg.ShowModal() == wx.ID_OK:
97                phaseName = Phases.keys()[dlg.GetSelection()]
98                UpdateRestraints(G2frame,data,Phases,phaseName)
99        finally:
100            dlg.Destroy()
101   
102    def getMacroFile(macName):
103        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
104        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' restraint macro file',
105            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
106            style=wx.OPEN | wx.CHANGE_DIR)
107        try:
108            if dlg.ShowModal() == wx.ID_OK:
109                macfile = dlg.GetPath()
110                macro = open(macfile,'Ur')
111                head = macro.readline()
112                if macName not in head:
113                    print head
114                    print '**** ERROR - wrong restraint macro file selected, try again ****'
115                    macro = []
116            else: # cancel was pressed
117                macxro = []
118        finally:
119            dlg.Destroy()
120        return macro        #advanced past 1st line
121       
122    def OnPlotAARestraint(event):
123        page = G2frame.dataDisplay.GetSelection()
124        if 'Torsion' in G2frame.dataDisplay.GetPageText(page):
125            torNames = []
126            torNames += restrData['Torsion']['Coeff'].keys()
127            dlg = wx.SingleChoiceDialog(G2frame,'Select','Torsion data',torNames)
128            try:
129                if dlg.ShowModal() == wx.ID_OK:
130                    torName = torNames[dlg.GetSelection()]
131                    torsion = G2data.torsionDist[torName]
132                    torCoeff = restrData['Torsion']['Coeff'][torName]
133                    torList = restrData['Torsion']['Torsions']
134                    Names = []
135                    Angles = []
136                    for i,[indx,ops,cofName,esd] in enumerate(torList):
137                        if cofName == torName:
138                            atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
139                            name = '('+atoms[2][1]+atoms[2][0].strip()+atoms[2][2]+')'
140                            for atom in atoms:
141                                name += '  '+atom[3]
142                            XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
143                            angle = G2mth.getRestTorsion(XYZ,Amat)
144                            Angles.append(angle)
145                            Names.append(name) 
146                    G2plt.PlotTorsion(G2frame,phaseName,torsion,torName,Names,np.array(Angles),torCoeff)
147            finally:
148                dlg.Destroy()
149           
150        elif 'Rama' in G2frame.dataDisplay.GetPageText(page):
151            ramaNames = ['All',]
152            ramaNames += restrData['Rama']['Coeff'].keys()
153            dlg = wx.SingleChoiceDialog(G2frame,'Select','Ramachandran data',ramaNames)
154            try:
155                if dlg.ShowModal() == wx.ID_OK:
156                    ramaName = ramaNames[dlg.GetSelection()]
157                    rama = G2data.ramachandranDist[ramaName]
158                    ramaCoeff = []
159                    if ramaName != 'All':
160                        ramaCoeff = restrData['Rama']['Coeff'][ramaName]
161                    ramaList = restrData['Rama']['Ramas']
162                    Names = []
163                    PhiPsi = []
164                    for i,[indx,ops,cofName,esd] in enumerate(ramaList):
165                        if cofName == ramaName or (ramaName == 'All' and '-1' in cofName):
166                            atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
167                            name = '('+atoms[3][1]+atoms[3][0].strip()+atoms[3][2]+')'
168                            for atom in atoms:
169                                name += '  '+atom[3]
170                            XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
171                            phi,psi = G2mth.getRestRama(XYZ,Amat)
172                            PhiPsi.append([phi,psi])
173                            Names.append(name) 
174                    G2plt.PlotRama(G2frame,phaseName,rama,ramaName,Names,np.array(PhiPsi),ramaCoeff)
175            finally:
176                dlg.Destroy()
177           
178    def OnAddRestraint(event):
179        page = G2frame.dataDisplay.GetSelection()
180        if 'Bond' in G2frame.dataDisplay.GetPageText(page):
181            AddBondRestraint(restrData['Bond'])
182        elif 'Angle' in G2frame.dataDisplay.GetPageText(page):
183            AddAngleRestraint(restrData['Angle'])
184        elif 'Plane' in G2frame.dataDisplay.GetPageText(page):
185            AddPlaneRestraint(restrData['Plane'])
186        elif 'Chem' in G2frame.dataDisplay.GetPageText(page):
187            AddChemCompRestraint(restrData['ChemComp'])
188        elif 'Texture' in G2frame.dataDisplay.GetPageText(page):
189            AddTextureRestraint(restrData['Texture'])
190           
191    def OnAddAARestraint(event):
192        page = G2frame.dataDisplay.GetSelection()
193        if 'Bond' in G2frame.dataDisplay.GetPageText(page):
194            AddAABondRestraint(restrData['Bond'])
195        elif 'Angle' in G2frame.dataDisplay.GetPageText(page):
196            AddAAAngleRestraint(restrData['Angle'])
197        elif 'Plane' in G2frame.dataDisplay.GetPageText(page):
198            AddAAPlaneRestraint(restrData['Plane'])
199        elif 'Chiral' in G2frame.dataDisplay.GetPageText(page):
200            AddAAChiralRestraint(restrData['Chiral'])
201        elif 'Torsion' in G2frame.dataDisplay.GetPageText(page):
202            AddAATorsionRestraint(restrData['Torsion'])
203        elif 'Rama' in G2frame.dataDisplay.GetPageText(page):
204            AddAARamaRestraint(restrData['Rama'])
205           
206    def AddBondRestraint(bondRestData):
207        Lists = {'origin':[],'target':[]}
208        for listName in ['origin','target']:
209            dlg = wx.MultiChoiceDialog(G2frame,'Bond restraint '+listName+' for '+General['Name'],
210                    'Select bond restraint '+listName+' atoms',Names)
211            if dlg.ShowModal() == wx.ID_OK:
212                sel = dlg.GetSelections()
213                for x in sel:
214                    if 'all' in Names[x]:
215                        allType = Types[x]
216                        for name,Type,coords,id in zip(Names,Types,Coords,Ids):
217                            if Type == allType and 'all' not in name:
218                                Lists[listName].append([id,Type,coords])
219                    else:
220                        Lists[listName].append([Ids[x],Types[x],Coords[x],])
221            else:
222                break
223        if len(Lists['origin']) and len(Lists['target']):
224            bond = 1.54
225            dlg = G2gd.SingleFloatDialog(G2frame,'Distance','Enter restraint distance for bond',bond,[0.01,4.],'%.4f')
226            if dlg.ShowModal() == wx.ID_OK:
227                bond = dlg.GetValue()
228            dlg.Destroy()
229        Factor = bondRestData['Range']
230        indices = (-1,0,1)
231        Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
232        origAtoms = Lists['origin']
233        targAtoms = Lists['target']
234        dlg = wx.ProgressDialog("Generating bond restraints","Processed origin atoms",len(origAtoms), 
235            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME)
236        try:
237            Norig = 0
238            for Oid,Otype,Ocoord in origAtoms:
239                Norig += 1
240                dlg.Update(Norig)
241                for Tid,Ttype,Tcoord in targAtoms:
242                    if 'macro' in General['Type']:
243                        result = [[Tcoord,1,[0,0,0]],]
244                    else:
245                        result = G2spc.GenAtom(Tcoord,SGData,False,Move=False)
246                    for Txyz,Top,Tunit in result:
247                        Dx = (Txyz-np.array(Ocoord))+Units
248                        dx = np.inner(Amat,Dx)
249                        dist = ma.masked_less(np.sqrt(np.sum(dx**2,axis=0)),bond/Factor)
250                        IndB = ma.nonzero(ma.masked_greater(dist,bond*Factor))
251                        if np.any(IndB):
252                            for indb in IndB:
253                                for i in range(len(indb)):
254                                    unit = Units[indb][i]+Tunit
255                                    if np.any(unit):
256                                        Topstr = '%d+%d,%d,%d'%(Top,unit[0],unit[1],unit[2])
257                                    else:
258                                        Topstr = str(Top)
259                                    newBond = [[Oid,Tid],['1',Topstr],bond,0.01]
260                                    if newBond not in bondRestData['Bonds']:
261                                        bondRestData['Bonds'].append(newBond)
262        finally:
263            dlg.Destroy()
264        UpdateBondRestr(bondRestData)               
265
266    def AddAABondRestraint(bondRestData):
267        Radii = dict(zip(General['AtomTypes'],General['BondRadii']))
268        macro = getMacroFile('bond')
269        if not macro:
270            return
271        macStr = macro.readline()
272        atoms = zip(Names,Coords,Ids)
273       
274        Factor = bondRestData['Range']
275        while macStr:
276            items = macStr.split()
277            if 'F' in items[0]:
278                restrData['Bond']['wtFactor'] = float(items[1])
279            elif 'S' in items[0]:
280                oIds = []
281                oCoords = []
282                tIds = []
283                tCoords = []
284                res = items[1]
285                dist = float(items[2])
286                esd = float(items[3])
287                oAtm,tAtm = items[4:6]
288                for Name,coords,Id in atoms:
289                    names = Name.split()
290                    if res == '*' or res in names[0]:
291                        if oAtm == names[2]:
292                            oIds.append(Id)
293                            oCoords.append(np.array(coords))
294                        if tAtm == names[2]:
295                            tIds.append(Id)
296                            tCoords.append(np.array(coords))
297                for i,[oId,oCoord] in enumerate(zip(oIds,oCoords)):
298                    for tId,tCoord in zip(tIds,tCoords)[i:]:
299                        obsd = np.sqrt(np.sum(np.inner(Amat,tCoord-oCoord)**2))
300                        if dist/Factor < obsd < dist*Factor:
301                            newBond = [[oId,tId],['1','1'],dist,esd]
302                            if newBond not in bondRestData['Bonds']:
303                                bondRestData['Bonds'].append(newBond)                         
304            macStr = macro.readline()
305        macro.close()
306        UpdateBondRestr(bondRestData)               
307           
308    def AddAngleRestraint(angleRestData):
309        Radii = dict(zip(General['AtomTypes'],zip(General['BondRadii'],General['AngleRadii'])))
310        Lists = {'A-atom':[],'B-atom':[],'C-atom':[]}
311        for listName in ['A-atom','B-atom']:
312            dlg = wx.MultiChoiceDialog(G2frame,'Select '+listName+' for angle A-B-C for '+General['Name']                                                                           ,
313                    'Select angle restraint '+listName,Names)
314            if dlg.ShowModal() == wx.ID_OK:
315                sel = dlg.GetSelections()
316                for x in sel:
317                    if 'all' in Names[x]:
318                        allType = Types[x]
319                        for name,Type,coords,id in zip(Names,Types,Coords,Ids):
320                            if Type == allType and 'all' not in name:
321                                if 'A' in listName:
322                                    Lists[listName].append(Type)
323                                else:
324                                    Lists[listName].append([id,Type,coords])
325                    else:
326                        if 'A' in listName:
327                            Lists[listName].append(Types[x])
328                        else:
329                            Lists[listName].append([Ids[x],Types[x],Coords[x],])
330            else:
331                break
332            targAtoms = [[Ids[x+iBeg],Types[x+iBeg],Coords[x+iBeg]] for x in range(len(Names[iBeg:]))]
333        if len(Lists['B-atom']):
334            value = 109.54
335            dlg = G2gd.SingleFloatDialog(G2frame,'Angle','Enter restraint angle ',value,[30.,180.],'%.2f')
336            if dlg.ShowModal() == wx.ID_OK:
337                value = dlg.GetValue()
338            dlg.Destroy()
339
340        Factor = angleRestData['Range']
341        indices = (-1,0,1)
342        Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
343        VectA = []
344        for Oid,Otype,Ocoord in Lists['B-atom']:
345            IndBlist = []
346            VectB = []
347            for Tid,Ttype,Tcoord in targAtoms:
348                result = G2spc.GenAtom(Tcoord,SGData,False,Move=False)
349                BsumR = (Radii[Otype][0]+Radii[Ttype][0])*Factor
350                AsumR = (Radii[Otype][1]+Radii[Ttype][1])*Factor
351                for Txyz,Top,Tunit in result:
352                    Dx = (Txyz-Ocoord)+Units
353                    dx = np.inner(Amat,Dx)
354                    dist = ma.masked_less(np.sqrt(np.sum(dx**2,axis=0)),0.5)
355                    IndB = ma.nonzero(ma.masked_greater(dist,BsumR))
356                    if np.any(IndB):
357                        for indb in IndB:
358                            for i in range(len(indb)):
359                                if str(dx.T[indb][i]) not in IndBlist:
360                                    IndBlist.append(str(dx.T[indb][i]))
361                                    unit = Units[indb][i]+Tunit
362                                if np.any(unit):
363                                    Topstr = '%d+%d,%d,%d'%(Top,unit[0],unit[1],unit[2])
364                                else:
365                                    Topstr = str(Top)
366                                Dist = ma.getdata(dist[indb])[i]
367                                if (Dist-AsumR) <= 0.:
368                                    VectB.append([Oid,'1',Ocoord,Ttype,Tid,Topstr,Tcoord,Dist])
369            VectA.append(VectB)
370        for Vects in VectA:
371            for i,vecta in enumerate(Vects):                   
372                for vectb in Vects[:i]:
373                    if vecta[3] in Lists['A-atom']:
374                        ids = [vecta[4],vecta[0],vectb[4]]
375                        ops = [vecta[5],vecta[1],vectb[5]]
376                        XYZ = np.array([vecta[6],vecta[2],vectb[6]])
377                        angle = [ids,ops,value,1.0]
378                        if angle not in angleRestData['Angles']:
379                            angleRestData['Angles'].append(angle)
380        UpdateAngleRestr(angleRestData)               
381
382    def AddAAAngleRestraint(angleRestData):
383        macro = getMacroFile('angle')
384        if not macro:
385            return
386        atoms = zip(Names,Ids)
387        macStr = macro.readline()
388        while macStr:
389            items = macStr.split()
390            if 'F' in items[0]:
391                restrData['Angle']['wtFactor'] = float(items[1])
392            elif 'S' in items[0]:
393                res = items[1]
394                value = float(items[2])
395                esd = float(items[3])
396                Atms = items[4:7]
397                pAtms = ['','','']
398                for i,atm in enumerate(Atms):
399                    if '+' in atm:
400                        pAtms[i] = atm.strip('+')
401                ids = np.array([0,0,0])
402                rNum = -1
403                for name,id in atoms:
404                    names = name.split()
405                    tNum = int(names[0].split(':')[0])
406                    if res in names[0]:
407                        try:
408                            ipos = Atms.index(names[2])
409                            ids[ipos] = id
410                        except ValueError:
411                            continue
412                    elif res == '*':
413                        try:
414                            ipos = Atms.index(names[2])
415                            if not np.all(ids):
416                                rNum = int(names[0].split(':')[0])
417                            ids[ipos] = id
418                        except ValueError:
419                            try:
420                                if tNum == rNum+1:
421                                    ipos = pAtms.index(names[2])
422                                    ids[ipos] = id
423                            except ValueError:
424                                continue
425                    if np.all(ids):
426                        angle = [list(ids),['1','1','1'],value,esd]
427                        if angle not in angleRestData['Angles']:
428                            angleRestData['Angles'].append(angle)
429                        ids = np.array([0,0,0])
430            macStr = macro.readline()
431        macro.close()
432        UpdateAngleRestr(angleRestData)               
433       
434    def AddPlaneRestraint(restrData):
435        ids = []
436        dlg = wx.MultiChoiceDialog(G2frame,'Select 4 or more atoms for plane in '+General['Name'],
437                'Select 4+ atoms',Names[iBeg:])
438        if dlg.ShowModal() == wx.ID_OK:
439            sel = dlg.GetSelections()
440            if len(sel) > 3:
441                for x in sel:
442                    ids.append(Ids[x+iBeg])
443                ops = ['1' for i in range(len(sel))]
444                plane = [ids,ops,0.0,0.01]
445                if plane not in restrData['Planes']:
446                    restrData['Planes'].append(plane)
447            else:
448                print '**** ERROR - not enough atoms for a plane restraint - try again ****'
449        UpdatePlaneRestr(restrData)               
450
451    def AddAAPlaneRestraint(planeRestData):
452        macro = getMacroFile('plane')
453        if not macro:
454            return
455        atoms = zip(Names,Ids)
456        macStr = macro.readline()
457        while macStr:
458            items = macStr.split()
459            if 'F' in items[0]:
460                restrData['Plane']['wtFactor'] = float(items[1])
461            elif 'S' in items[0]:
462                res = items[1]
463                esd = float(items[2])
464                Atms = items[3:]
465                pAtms = ['' for i in Atms]
466                for i,atm in enumerate(Atms):
467                    if '+' in atm:
468                        pAtms[i] = atm.strip('+')
469                rNum = -1
470                ids = np.zeros(len(Atms))
471                ops = ['1' for i in range(len(Atms))]
472                for name,id in atoms:
473                    names = name.split()
474                    tNum = int(names[0].split(':')[0])
475                    if res in names[0]:
476                        try:
477                            ipos = Atms.index(names[2])
478                            ids[ipos] = id
479                        except ValueError:
480                            continue
481                    elif res == '*':
482                        try:
483                            ipos = Atms.index(names[2])
484                            if not np.all(ids):
485                                rNum = int(names[0].split(':')[0])
486                            ids[ipos] = id
487                        except ValueError:
488                            try:
489                                if tNum == rNum+1:
490                                    ipos = pAtms.index(names[2])
491                                    ids[ipos] = id
492                            except ValueError:
493                                continue
494                    if np.all(ids):
495                        plane = [list(ids),ops,0.0,esd]
496                        if plane not in planeRestData['Planes']:
497                            planeRestData['Planes'].append(plane)
498                        ids = np.zeros(len(Atms))
499            macStr = macro.readline()
500        macro.close()
501        UpdatePlaneRestr(planeRestData)               
502
503    def AddAAChiralRestraint(chiralRestData):
504        macro = getMacroFile('chiral')
505        if not macro:
506            return
507        atoms = zip(Names,Ids)
508        macStr = macro.readline()
509        while macStr:
510            items = macStr.split()
511            if 'F' in items[0]:
512                restrData['Chiral']['wtFactor'] = float(items[1])
513            elif 'S' in items[0]:
514                res = items[1]
515                value = float(items[2])
516                esd = float(items[3])
517                Atms = items[4:8]
518                ids = np.array([0,0,0,0])
519                for name,id in atoms:
520                    names = name.split()
521                    if res in names[0]:
522                        try:
523                            ipos = Atms.index(names[2])
524                            ids[ipos] = id
525                        except ValueError:
526                            pass
527                        if np.all(ids):
528                            chiral = [list(ids),['1','1','1','1'],value,esd]
529                            if chiral not in chiralRestData['Volumes']:
530                                chiralRestData['Volumes'].append(chiral)
531                            ids = np.array([0,0,0,0])
532            macStr = macro.readline()
533        macro.close()
534        UpdateChiralRestr(chiralRestData)               
535       
536    def makeChains(Names,Ids):
537        Chains = {}
538        chain = ''
539        atoms = zip(Names,Ids)
540        for name,id in atoms:
541            items = name.split()
542            rnum,res = items[0].split(':')
543            if items[1] not in Chains:
544                Residues = {}
545                Chains[items[1]] = Residues
546            if int(rnum) not in Residues:
547                Residues[int(rnum)] = []
548            Residues[int(rnum)].append([res,items[2],id])
549        return Chains
550       
551    def AddAATorsionRestraint(torsionRestData):
552        macro = getMacroFile('torsion')
553        if not macro:
554            return
555        Chains = makeChains(Names,Ids)           
556        macStr = macro.readline()[:-1]
557        while macStr:
558            items = macStr.split()
559            if 'F' in items[0]:
560                restrData['Torsion']['wtFactor'] = float(items[1])
561            elif 'A' in items[0]:
562                name = items[10]
563                coeff = np.zeros(9)
564                for i,item in enumerate(items[1:10]):
565                    coeff[i] = float(item)
566                torsionRestData['Coeff'][name] = coeff
567            elif 'S' in items[0]:
568                Name = items[1]
569                Res = items[2]
570                Esd = float(items[3])
571                Atms = items[4:8]
572                pAtms = ['','','','']
573                for i,atm in enumerate(Atms):
574                    if '+' in atm:
575                        pAtms[i] = atm.strip('+')
576                ids = np.array([0,0,0,0])
577                chains = Chains.keys()
578                chains.sort()
579                for chain in chains:
580                    residues = Chains[chain].keys()
581                    residues.sort()
582                    for residue in residues:
583                        if residue == residues[-1] and Res == '*':
584                            continue
585                        if Res != '*':
586                            for res,name,id in Chains[chain][residue]:
587                                if Res == res:
588                                    try:
589                                        ipos = Atms.index(name)
590                                        ids[ipos] = id
591                                    except ValueError:
592                                        continue
593                        else:
594                            for res,name,id in Chains[chain][residue]:
595                                try:
596                                    ipos = Atms.index(name)
597                                    ids[ipos] = id
598                                except ValueError:
599                                    continue
600                            for res,name,id in Chains[chain][residue+1]:
601                                try:
602                                    ipos = pAtms.index(name)
603                                    ids[ipos] = id
604                                except ValueError:
605                                    continue
606                        if np.all(ids):
607                            torsion = [list(ids),['1','1','1','1'],Name,Esd]
608                            if torsion not in torsionRestData['Torsions']:
609                                torsionRestData['Torsions'].append(torsion)
610                            ids = np.array([0,0,0,0])                           
611            macStr = macro.readline()
612        macro.close()
613        UpdateTorsionRestr(torsionRestData)                       
614       
615    def AddAARamaRestraint(ramaRestData):
616        macro = getMacroFile('Ramachandran')
617        if not macro:
618            return
619        Chains = makeChains(Names,Ids)           
620        macStr = macro.readline()
621        while macStr:
622            items = macStr.split()
623            if 'F' in items[0]:
624                restrData['Rama']['wtFactor'] = float(items[1])
625            elif 'A' in items[0]:
626                nTerms = int(items[1])
627                name = items[2]
628                coeff = np.zeros((nTerms,6))
629                for i in range(nTerms):
630                    macStr = macro.readline()
631                    items = macStr.split()
632                    for j,val in enumerate(items):
633                        coeff[i][j] = float(val)
634                ramaRestData['Coeff'][name] = coeff
635            elif 'S' in items[0]:
636                Name = items[1]
637                Res = items[2]
638                Esd = float(items[3])
639                Atms = items[4:9]
640                mAtms = ['','','','','']
641                pAtms = ['','','','','']
642                for i,atm in enumerate(Atms):
643                    if '+' in atm:
644                        pAtms[i] = atm.strip('+')
645                    elif '-' in atm:
646                        mAtms[i] = atm.strip('-')
647                ids = np.array([0,0,0,0,0])
648                chains = Chains.keys()
649                chains.sort()
650                for chain in chains:
651                    residues = Chains[chain].keys()
652                    residues.sort()
653                    if not (any(mAtms) or any(pAtms)):
654                        for residue in residues:
655                            for res,name,id in Chains[chain][residue]:
656                                if Res == res:
657                                    try:
658                                        ipos = Atms.index(name)
659                                        ids[ipos] = id
660                                    except ValueError:
661                                        continue
662                            if np.all(ids):
663                                rama = [list(ids),['1','1','1','1','1'],Name,Esd]
664                                if rama not in ramaRestData['Ramas']:
665                                    ramaRestData['Ramas'].append(rama)
666                                ids = np.array([0,0,0,0,0])
667                    else:
668                        for residue in residues[1:-1]:
669                            for res,name,id in Chains[chain][residue-1]:
670                                try:
671                                    ipos = mAtms.index(name)
672                                    ids[ipos] = id
673                                except ValueError:
674                                    continue
675                            for res,name,id in Chains[chain][residue+1]:
676                                try:
677                                    ipos = pAtms.index(name)
678                                    ids[ipos] = id
679                                except ValueError:
680                                    continue
681                            for res,name,id in Chains[chain][residue]:
682                                if Res == res:
683                                    try:
684                                        ipos = Atms.index(name)
685                                        ids[ipos] = id
686                                    except ValueError:
687                                        continue
688                            if np.all(ids):
689                                rama = [list(ids),['1','1','1','1','1'],Name,Esd]
690                                if rama not in ramaRestData['Ramas']:
691                                    ramaRestData['Ramas'].append(rama)
692                                ids = np.array([0,0,0,0,0])
693            macStr = macro.readline()
694        macro.close()
695        UpdateRamaRestr(ramaRestData)
696       
697    def AddChemCompRestraint(chemcompRestData):
698        ids = []
699        factors = []
700        dlg = wx.MultiChoiceDialog(G2frame,'Select atoms for chemical restraint in '+General['Name'],
701                'Select atoms',Names)
702        if dlg.ShowModal() == wx.ID_OK:
703            sel = dlg.GetSelections()
704            for x in sel:
705                if 'all' in Names[x]:
706                    allType = Types[x]
707                    for name,Type,id in zip(Names,Types,Ids):
708                        if Type == allType and 'all' not in name:
709                            ids.append(id)
710                            factors.append(1.0)
711                else:
712                    ids.append(Ids[x])
713                    factors.append(1.0)
714            dlg.Destroy()
715            if len(ids) > 2:
716                value = 1.0
717                dlg = G2gd.SingleFloatDialog(G2frame,'Angle','Enter unit cell sum ',value,[-1.e6,1.e6],'%.2f')
718                if dlg.ShowModal() == wx.ID_OK:
719                    value = dlg.GetValue()               
720                    comp = [ids,factors,value,0.01]
721                    if comp not in chemcompRestData['Sites']:
722                        chemcompRestData['Sites'].append(comp)
723                UpdateChemcompRestr(chemcompRestData)
724            else:
725                print '**** ERROR - not enough atoms for a composition restraint - try again ****'
726       
727    def AddTextureRestraint(textureRestData):
728        dlg = wx.TextEntryDialog(G2frame,'Enter h k l for pole figure restraint','Enter HKL','')
729        if dlg.ShowModal() == wx.ID_OK:
730            text = dlg.GetValue()
731            vals = text.split()
732            try:
733                hkl = [int(vals[i]) for i in range(3)]
734                texture = [hkl,24,0.01,False,1.0]
735                if texture not in textureRestData['HKLs']:
736                        textureRestData['HKLs'].append(texture)
737                UpdateTextureRestr(textureRestData)
738            except (ValueError,IndexError):
739                print '**** ERROR - bad input of h k l - try again ****'
740        dlg.Destroy()
741               
742    def WtBox(wind,restData):
743        if 'Range' not in restData: restData['Range'] = 1.1     #patch
744       
745        def OnWtFactor(event):
746            try:
747                value = float(wtfactor.GetValue())
748            except ValueError:
749                value = 1.0
750            restData['wtFactor'] = value
751            wtfactor.SetValue('%.2f'%(value))
752           
753        def OnRange(event):
754            try:
755                value = float(sRange.GetValue())
756            except ValueError:
757                value = 1.0
758            restData['Range'] = value
759            sRange.SetValue('%.2f'%(value))
760           
761        def OnUseData(event):
762            Obj = event.GetEventObject()
763            restData['Use'] = Obj.GetValue()
764
765        wtBox = wx.BoxSizer(wx.HORIZONTAL)
766        wtBox.Add(wx.StaticText(wind,-1,'Restraint weight factor:'),0,wx.ALIGN_CENTER_VERTICAL)
767        wtfactor = wx.TextCtrl(wind,-1,value='%.2f'%(restData['wtFactor']),style=wx.TE_PROCESS_ENTER,size=(50,20))
768        wtfactor.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
769        wtfactor.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
770        wtBox.Add(wtfactor,0,wx.ALIGN_CENTER_VERTICAL)
771        useData = wx.CheckBox(wind,-1,label=' Use?')
772        useData.Bind(wx.EVT_CHECKBOX, OnUseData)
773        useData.SetValue(restData['Use'])       
774        wtBox.Add(useData,0,wx.ALIGN_CENTER_VERTICAL)
775        if 'Bonds' in restData or 'Angles' in restData:
776            wtBox.Add(wx.StaticText(wind,-1,'Search range:'),0,wx.ALIGN_CENTER_VERTICAL)
777            sRange = wx.TextCtrl(wind,-1,value='%.2f'%(restData['Range']),style=wx.TE_PROCESS_ENTER,size=(50,20))
778            sRange.Bind(wx.EVT_TEXT_ENTER,OnRange)
779            sRange.Bind(wx.EVT_KILL_FOCUS,OnRange)
780            wtBox.Add(sRange,0,wx.ALIGN_CENTER_VERTICAL)
781        return wtBox
782       
783    def OnRowSelect(event):
784        r,c =  event.GetRow(),event.GetCol()
785        Obj = event.GetEventObject()
786        if r < 0 and c < 0:
787            if Obj.IsSelection():
788                Obj.ClearSelection()
789            else:
790                for row in range(Obj.GetNumberRows()):
791                    Obj.SelectRow(row,True)
792        elif c < 0:                   #only row clicks
793            if event.ControlDown():                   
794                if r in Obj.GetSelectedRows():
795                    Obj.DeselectRow(r)
796                else:
797                    Obj.SelectRow(r,True)
798            elif event.ShiftDown():
799                indxs = Obj.GetSelectedRows()
800                Obj.ClearSelection()
801                ibeg = 0
802                if indxs:
803                    ibeg = indxs[-1]
804                for row in range(ibeg,r+1):
805                    Obj.SelectRow(row,True)
806            else:
807                Obj.ClearSelection()
808                Obj.SelectRow(r,True)
809               
810                   
811    def UpdateBondRestr(bondRestData):
812       
813        def OnCellChange(event):
814            r,c =  event.GetRow(),event.GetCol()
815            val = bondRestData['Bonds'][r][c]
816            try:
817                new = float(bondTable.GetValue(r,c))
818                if new <= 0.:
819                    raise ValueError
820                bondRestData['Bonds'][r][c] = new
821            except ValueError:
822                pass           
823            wx.CallAfter(UpdateBondRestr,bondRestData)               
824           
825        def OnChangeValue(event):
826            rows = Bonds.GetSelectedRows()
827            if not rows:
828                return
829            Bonds.ClearSelection()
830            val = bondList[rows[0]][2]
831            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new value for bond',val,[0.,5.],'%.4f')
832            if dlg.ShowModal() == wx.ID_OK:
833                parm = dlg.GetValue()
834                for r in rows:
835                    bondRestData['Bonds'][r][2] = parm
836            dlg.Destroy()
837            UpdateBondRestr(bondRestData)               
838
839        def OnChangeEsd(event):
840            rows = Bonds.GetSelectedRows()
841            if not rows:
842                return
843            Bonds.ClearSelection()
844            val = bondList[rows[0]][3]
845            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new esd for bond',val,[0.,1.],'%.4f')
846            if dlg.ShowModal() == wx.ID_OK:
847                parm = dlg.GetValue()
848                for r in rows:
849                    bondRestData['Bonds'][r][3] = parm
850            dlg.Destroy()
851            UpdateBondRestr(bondRestData)               
852                               
853        def OnDeleteRestraint(event):
854            rows = Bonds.GetSelectedRows()
855            if not rows:
856                return
857            Bonds.ClearSelection()
858            rows.sort()
859            rows.reverse()
860            for row in rows:
861                bondList.remove(bondList[row])
862            UpdateBondRestr(bondRestData)               
863           
864        BondRestr.DestroyChildren()
865        dataDisplay = wx.Panel(BondRestr)
866        mainSizer = wx.BoxSizer(wx.VERTICAL)
867        mainSizer.Add((5,5),0)
868        mainSizer.Add(WtBox(BondRestr,bondRestData),0,wx.ALIGN_CENTER_VERTICAL)
869
870        bondList = bondRestData['Bonds']
871        if len(bondList) and len(bondList[0]) == 6:   #patch
872            bondList = bondRestData['Bonds'] = []
873        if len(bondList):
874            table = []
875            rowLabels = []
876            bad = []
877            chisq = 0.
878            Types = [wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,3',]
879            if 'macro' in General['Type']:
880                colLabels = ['(res) A - (res) B','calc','obs','esd','delt/sig']
881                for i,[indx,ops,obs,esd] in enumerate(bondList):
882                    try:
883                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
884                        name = ''
885                        for atom in atoms:
886                            name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
887                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
888                        calc = G2mth.getRestDist(XYZ,Amat)
889                        chisq += bondRestData['wtFactor']*((obs-calc)/esd)**2
890                        table.append([name[:-3],calc,obs,esd,(obs-calc)/esd])
891                        rowLabels.append(str(i))               
892                    except KeyError:
893                        print '**** WARNING - missing atom - restraint deleted ****'
894                        bad.append(i)
895            else:
896                colLabels = ['A+SymOp - B+SymOp','calc','obs','esd','delt/sig']
897                for i,[indx,ops,obs,esd] in enumerate(bondList):
898                    try:
899                        names = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
900                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
901                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
902                        calc = G2mth.getRestDist(XYZ,Amat)
903                        chisq += bondRestData['wtFactor']*((obs-calc)/esd)**2
904                        table.append([names[0]+'+('+ops[0]+') - '+names[1]+'+('+ops[1]+')',calc,obs,esd,(obs-calc)/esd])
905                        rowLabels.append(str(i))
906                    except KeyError:
907                        print '**** WARNING - missing atom - restraint deleted ****'
908                        bad.append(i)
909            if len(bad):
910                bad.reverse()
911                for ibad in bad:
912                    del bondList[ibad]
913            bondTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
914            Bonds = G2gd.GSGrid(BondRestr)
915            Bonds.SetTable(bondTable, True)
916            Bonds.AutoSizeColumns(False)
917            for r in range(len(bondList)):
918                for c in [0,1,4]:
919                    Bonds.SetReadOnly(r,c,True)
920                    Bonds.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
921            Bonds.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
922            Bonds.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
923            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
924            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeValue, id=G2gd.wxID_RESRCHANGEVAL)
925            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
926            mainSizer.Add(wx.StaticText(BondRestr,-1,
927                'Bond restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
928                %(chisq,chisq/len(bondList))),0,wx.ALIGN_CENTER_VERTICAL)
929            mainSizer.Add(Bonds,0,)
930        else:
931            mainSizer.Add(wx.StaticText(BondRestr,-1,'No bond distance restraints for this phase'),0,)
932
933        BondRestr.SetSizer(mainSizer)
934        Size = mainSizer.Fit(G2frame.dataFrame)
935        Size[0] = 600
936        Size[1] += 50       #make room for tab
937        BondRestr.SetSize(Size)
938        BondRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
939        G2frame.dataFrame.SetSize(Size)
940       
941    def UpdateAngleRestr(angleRestData):
942       
943        def OnCellChange(event):
944            r,c =  event.GetRow(),event.GetCol()
945            val = angleRestData['Angles'][r][c]
946            try:
947                new = float(angleTable.GetValue(r,c))
948                if new <= 0. or new > 180.:
949                    raise ValueError
950                angleRestData['Angles'][r][c] = new
951            except ValueError:
952                pass           
953            wx.CallAfter(UpdateAngleRestr,angleRestData)               
954           
955        def OnChangeValue(event):
956            rows = Angles.GetSelectedRows()
957            if not rows:
958                return
959            Angles.ClearSelection()
960            val = angleList[rows[0]][2]
961            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new value for angle',val,[0.,360.],'%.2f')
962            if dlg.ShowModal() == wx.ID_OK:
963                parm = dlg.GetValue()
964                for r in rows:
965                    angleRestData['Angles'][r][2] = parm
966            dlg.Destroy()
967            UpdateAngleRestr(angleRestData)               
968
969        def OnChangeEsd(event):
970            rows = Angles.GetSelectedRows()
971            if not rows:
972                return
973            Angles.ClearSelection()
974            val = angleList[rows[0]][3]
975            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new esd for angle',val,[0.,5.],'%.2f')
976            if dlg.ShowModal() == wx.ID_OK:
977                parm = dlg.GetValue()
978                for r in rows:
979                    angleRestData['Angles'][r][3] = parm
980            dlg.Destroy()
981            UpdateAngleRestr(angleRestData)               
982                                           
983        def OnDeleteRestraint(event):
984            rows = Angles.GetSelectedRows()
985            if not rows:
986                return
987            rows.sort()
988            rows.reverse()
989            for row in rows:
990                angleList.remove(angleList[row])
991            UpdateAngleRestr(angleRestData)               
992           
993        AngleRestr.DestroyChildren()
994        dataDisplay = wx.Panel(AngleRestr)
995        mainSizer = wx.BoxSizer(wx.VERTICAL)
996        mainSizer.Add((5,5),0)
997        mainSizer.Add(WtBox(AngleRestr,angleRestData),0,wx.ALIGN_CENTER_VERTICAL)
998
999        angleList = angleRestData['Angles']
1000        if len(angleList):
1001            table = []
1002            rowLabels = []
1003            bad = []
1004            chisq = 0.
1005            Types = [wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,2',]
1006            if 'macro' in General['Type']:
1007                colLabels = ['(res) A - (res) B - (res) C','calc','obs','esd','delt/sig']
1008                for i,[indx,ops,obs,esd] in enumerate(angleList):
1009                    try:
1010                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1011                        name = ''
1012                        for atom in atoms:
1013                            name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
1014                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1015                        calc = G2mth.getRestAngle(XYZ,Amat)
1016                        chisq += angleRestData['wtFactor']*((obs-calc)/esd)**2
1017                        table.append([name[:-3],calc,obs,esd,(obs-calc)/esd])
1018                        rowLabels.append(str(i))                               
1019                    except KeyError:
1020                        print '**** WARNING - missing atom - restraint deleted ****'
1021                        bad.append(i)
1022            else:
1023                colLabels = ['A+SymOp - B+SymOp - C+SymOp','calc','obs','esd','delt/sig']
1024                for i,[indx,ops,obs,esd] in enumerate(angleList):
1025                    try:
1026                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1027                        name = atoms[0]+'+('+ops[0]+') - '+atoms[1]+'+('+ops[1]+') - '+atoms[2]+ \
1028                        '+('+ops[2]+')'
1029                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1030                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1031                        calc = G2mth.getRestAngle(XYZ,Amat)
1032                        chisq += angleRestData['wtFactor']*((obs-calc)/esd)**2
1033                        table.append([name,calc,obs,esd,(obs-calc)/esd])
1034                        rowLabels.append(str(i))
1035                    except KeyError:
1036                        print '**** WARNING - missing atom - restraint deleted ****'
1037                        bad.append(i)
1038            if len(bad):
1039                bad.reverse()
1040                for ibad in bad:
1041                    del angleList[ibad]
1042            angleTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1043            Angles = G2gd.GSGrid(AngleRestr)
1044            Angles.SetTable(angleTable, True)
1045            Angles.AutoSizeColumns(False)
1046            for r in range(len(angleList)):
1047                for c in [0,1,4]:
1048                    Angles.SetReadOnly(r,c,True)
1049                    Angles.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1050            Angles.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1051            Angles.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1052            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1053            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeValue, id=G2gd.wxID_RESRCHANGEVAL)
1054            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1055            mainSizer.Add(wx.StaticText(AngleRestr,-1,
1056                'Angle restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1057                %(chisq,chisq/len(angleList))),0,wx.ALIGN_CENTER_VERTICAL)
1058            mainSizer.Add(Angles,0,)
1059        else:
1060            mainSizer.Add(wx.StaticText(AngleRestr,-1,'No bond angle restraints for this phase'),0,)
1061
1062        AngleRestr.SetSizer(mainSizer)
1063        Size = mainSizer.Fit(G2frame.dataFrame)
1064        Size[0] = 600
1065        Size[1] += 50      #make room for tab
1066        AngleRestr.SetSize(Size)
1067        AngleRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1068        G2frame.dataFrame.SetSize(Size)
1069   
1070    def UpdatePlaneRestr(planeRestData):
1071       
1072        items = G2frame.dataFrame.RestraintEdit.GetMenuItems()
1073        for item in items:
1074            if item.GetLabel() in ['Change value']:
1075                item.Enable(False)
1076
1077        def OnCellChange(event):
1078            r,c =  event.GetRow(),event.GetCol()
1079            val = planeRestData['Planes'][r][c]
1080            try:
1081                new = float(planeTable.GetValue(r,c))
1082                if new <= 0.:
1083                    raise ValueError
1084                planeRestData['Planes'][r][c] = new
1085            except ValueError:
1086                pass           
1087            wx.CallAfter(UpdatePlaneRestr,planeRestData)               
1088           
1089        def OnChangeEsd(event):
1090            rows = Planes.GetSelectedRows()
1091            if not rows:
1092                return
1093            Planes.ClearSelection()
1094            val = planeList[rows[0]][3]
1095            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new esd for plane',val,[0.,5.],'%.2f')
1096            if dlg.ShowModal() == wx.ID_OK:
1097                parm = dlg.GetValue()
1098                for r in rows:
1099                    planeRestData['Planes'][r][3] = parm
1100            dlg.Destroy()
1101            UpdatePlaneRestr(planeRestData)               
1102                                           
1103        def OnDeleteRestraint(event):
1104            rows = Planes.GetSelectedRows()
1105            if not rows:
1106                return
1107            rows.sort()
1108            rows.reverse()
1109            for row in rows:
1110                planeList.remove(planeList[row])
1111            UpdatePlaneRestr(planeRestData)               
1112           
1113        PlaneRestr.DestroyChildren()
1114        dataDisplay = wx.Panel(PlaneRestr)
1115        mainSizer = wx.BoxSizer(wx.VERTICAL)
1116        mainSizer.Add((5,5),0)
1117        mainSizer.Add(WtBox(PlaneRestr,planeRestData),0,wx.ALIGN_CENTER_VERTICAL)
1118
1119        planeList = planeRestData['Planes']
1120        if len(planeList):
1121            table = []
1122            rowLabels = []
1123            bad = []
1124            chisq = 0.
1125            Types = [wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,2',]
1126            if 'macro' in General['Type']:
1127                colLabels = ['(res) atom','calc','obs','esd']
1128                for i,[indx,ops,obs,esd] in enumerate(planeList):
1129                    try:
1130                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1131                        name = ''
1132                        for a,atom in enumerate(atoms):
1133                            name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
1134                            if (a+1)%3 == 0:
1135                                name += '\n'
1136                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1137                        calc = G2mth.getRestPlane(XYZ,Amat)
1138                        chisq += planeRestData['wtFactor']*((calc)/esd)**2
1139                        table.append([name[:-3],calc,obs,esd])
1140                        rowLabels.append(str(i))
1141                    except KeyError:
1142                        print '**** WARNING - missing atom - restraint deleted ****'
1143                        bad.append(i)
1144            else:                               
1145                colLabels = ['atom+SymOp','calc','obs','esd']
1146                for i,[indx,ops,obs,esd] in enumerate(planeList):
1147                    try:
1148                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1149                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1150                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1151                        calc = G2mth.getRestPlane(XYZ,Amat)
1152                        chisq += planeRestData['wtFactor']*((calc)/esd)**2
1153                        name = ''
1154                        for a,atom in enumerate(atoms):
1155                            name += atom+'+ ('+ops[a]+'),'
1156                            if (a+1)%3 == 0:
1157                                name += '\n'
1158                        table.append([name[:-1],calc,obs,esd])
1159                        rowLabels.append(str(i))
1160                    except KeyError:
1161                        print '**** WARNING - missing atom - restraint deleted ****'
1162                        bad.append(i)
1163            if len(bad):
1164                bad.reverse()
1165                for ibad in bad:
1166                    del planeList[ibad]
1167            planeTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1168            Planes = G2gd.GSGrid(PlaneRestr)
1169            Planes.SetTable(planeTable, True)
1170            Planes.AutoSizeColumns(False)
1171            Planes.AutoSizeRows(False)
1172            for r in range(len(planeList)):
1173                for c in range(3):
1174                    Planes.SetReadOnly(r,c,True)
1175                    Planes.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1176            Planes.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1177            Planes.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1178            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1179            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1180            mainSizer.Add(wx.StaticText(PlaneRestr,-1,
1181                'Plane restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1182                %(chisq,chisq/len(planeList))),0,wx.ALIGN_CENTER_VERTICAL)
1183            mainSizer.Add(Planes,0,)
1184        else:
1185            mainSizer.Add(wx.StaticText(PlaneRestr,-1,'No plane restraints for this phase'),0,)
1186
1187        PlaneRestr.SetSizer(mainSizer)
1188        Size = mainSizer.Fit(G2frame.dataFrame)
1189        Size[0] = 600
1190        Size[1] += 50       #make room for tab
1191        PlaneRestr.SetSize(Size)
1192        PlaneRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1193        G2frame.dataFrame.SetSize(Size)
1194#        G2frame.dataFrame.setSizePosLeft(Size)
1195   
1196    def UpdateChiralRestr(chiralRestData):
1197
1198        def OnCellChange(event):
1199            r,c =  event.GetRow(),event.GetCol()
1200            val = chiralRestData['Volumes'][r][c]
1201            try:
1202                new = float(volumeTable.GetValue(r,c))
1203                if new <= 0.:
1204                    raise ValueError
1205                chiralRestData['Volumes'][r][c] = new
1206            except ValueError:
1207                pass           
1208            wx.CallAfter(UpdateChiralRestr,chiralRestData)               
1209           
1210        def OnDeleteRestraint(event):
1211            rows = Volumes.GetSelectedRows()
1212            if not rows:
1213                return
1214            rows.sort()
1215            rows.reverse()
1216            for row in rows:
1217                volumeList.remove(volumeList[row])
1218            UpdateChiralRestr(chiralRestData)               
1219           
1220        def OnChangeValue(event):
1221            rows = Volumes.GetSelectedRows()
1222            if not rows:
1223                return
1224            Volumes.ClearSelection()
1225            val = volumeList[rows[0]][2]
1226            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new value for chiral volume',val,[0.,360.],'%.2f')
1227            if dlg.ShowModal() == wx.ID_OK:
1228                parm = dlg.GetValue()
1229                for r in rows:
1230                    chiralRestData['Volumes'][r][2] = parm
1231            dlg.Destroy()
1232            UpdateChiralRestr(chiralRestData)               
1233
1234        def OnChangeEsd(event):
1235            rows = Volumes.GetSelectedRows()
1236            if not rows:
1237                return
1238            Volumes.ClearSelection()
1239            val = volumeList[rows[0]][3]
1240            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new esd for chiral volume',val,[0.,5.],'%.2f')
1241            if dlg.ShowModal() == wx.ID_OK:
1242                parm = dlg.GetValue()
1243                for r in rows:
1244                    chiralRestData['Volumes'][r][3] = parm
1245            dlg.Destroy()
1246            UpdateChiralRestr(chiralRestData)               
1247                                           
1248        ChiralRestr.DestroyChildren()
1249        dataDisplay = wx.Panel(ChiralRestr)
1250        mainSizer = wx.BoxSizer(wx.VERTICAL)
1251        mainSizer.Add((5,5),0)
1252        mainSizer.Add(WtBox(ChiralRestr,chiralRestData),0,wx.ALIGN_CENTER_VERTICAL)
1253
1254        volumeList = chiralRestData['Volumes']
1255        if len(volumeList):
1256            table = []
1257            rowLabels = []
1258            bad = []
1259            chisq = 0.
1260            Types = [wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,2',]
1261            if 'macro' in General['Type']:
1262                colLabels = ['(res) O (res) A (res) B (res) C','calc','obs','esd','delt/sig']
1263                for i,[indx,ops,obs,esd] in enumerate(volumeList):
1264                    try:
1265                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1266                        name = ''
1267                        for atom in atoms:
1268                            name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' '
1269                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1270                        calc = G2mth.getRestChiral(XYZ,Amat)
1271                        chisq += chiralRestData['wtFactor']*((obs-calc)/esd)**2
1272                        table.append([name,calc,obs,esd,(obs-calc)/esd])
1273                        rowLabels.append(str(i))
1274                    except KeyError:
1275                        print '**** WARNING - missing atom - restraint deleted ****'
1276                        bad.append(i)
1277            else:
1278                colLabels = ['O+SymOp  A+SymOp  B+SymOp  C+SymOp)','calc','obs','esd','delt/sig']
1279                for i,[indx,ops,obs,esd] in enumerate(volumeList):
1280                    try:
1281                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1282                        name = atoms[0]+'+('+ops[0]+') '+atoms[1]+'+('+ops[1]+') '+atoms[2]+ \
1283                            '+('+ops[2]+') '+atoms[3]+'+('+ops[3]+')'
1284                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1285                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1286                        calc = G2mth.getRestChiral(XYZ,Amat)
1287                        chisq += chiralRestData['wtFactor']*((obs-calc)/esd)**2
1288                        table.append([name,calc,obs,esd,(obs-calc)/esd])
1289                        rowLabels.append(str(i))
1290                    except KeyError:
1291                        print '**** WARNING - missing atom - restraint deleted ****'
1292                        bad.append(i)
1293            if len(bad):
1294                bad.reverse()
1295                for ibad in bad:
1296                    del volumeList[ibad]
1297            volumeTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1298            Volumes = G2gd.GSGrid(ChiralRestr)
1299            Volumes.SetTable(volumeTable, True)
1300            Volumes.AutoSizeColumns(False)
1301            for r in range(len(volumeList)):
1302                for c in [0,1,4]:
1303                    Volumes.SetReadOnly(r,c,True)
1304                    Volumes.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1305            Volumes.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1306            Volumes.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1307            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1308            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeValue, id=G2gd.wxID_RESRCHANGEVAL)
1309            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1310            mainSizer.Add(wx.StaticText(ChiralRestr,-1,
1311                'Chiral volume restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1312                %(chisq,chisq/len(volumeList))),0,wx.ALIGN_CENTER_VERTICAL)
1313            mainSizer.Add(Volumes,0,)
1314        else:
1315            mainSizer.Add(wx.StaticText(ChiralRestr,-1,'No chiral volume restraints for this phase'),0,)
1316
1317        ChiralRestr.SetSizer(mainSizer)
1318        Size = mainSizer.Fit(G2frame.dataFrame)
1319        Size[0] = 600
1320        Size[1] += 50       #make room for tab
1321        ChiralRestr.SetSize(Size)
1322        ChiralRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1323        G2frame.dataFrame.SetSize(Size)
1324   
1325    def UpdateTorsionRestr(torsionRestData):
1326
1327        def OnCellChange(event):
1328            r,c =  event.GetRow(),event.GetCol()
1329            val = torsionRestData['Torsions'][r][c]
1330            try:
1331                new = float(torsionTable.GetValue(r,c))
1332                if new <= 0. or new > 5.:
1333                    raise ValueError
1334                torsionRestData['Torsions'][r][3] = new     #only esd is editable
1335            except ValueError:
1336                pass           
1337            wx.CallAfter(UpdateTorsionRestr,torsionRestData)               
1338           
1339        def OnDeleteRestraint(event):
1340            rows = Torsions.GetSelectedRows()
1341            if not rows:
1342                return
1343            rows.sort()
1344            rows.reverse()
1345            for row in rows:
1346                torsionList.remove(torsionList[row])
1347            wx.CallAfter(UpdateTorsionRestr,torsionRestData)               
1348           
1349        def OnChangeEsd(event):
1350            rows = Torsions.GetSelectedRows()
1351            if not rows:
1352                return
1353            Torsions.ClearSelection()
1354            val = torsionList[rows[0]][4]
1355            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new esd for torsion restraints',val,[0.,5.],'%.2f')
1356            if dlg.ShowModal() == wx.ID_OK:
1357                parm = dlg.GetValue()
1358                for r in rows:
1359                    torsionRestData['Torsions'][r][4] = parm
1360            dlg.Destroy()
1361            wx.CallAfter(UpdateTorsionRestr,torsionRestData)               
1362                                           
1363        TorsionRestr.DestroyChildren()
1364        dataDisplay = wx.Panel(TorsionRestr)
1365        mainSizer = wx.BoxSizer(wx.VERTICAL)
1366        mainSizer.Add((5,5),0)
1367        mainSizer.Add(WtBox(TorsionRestr,torsionRestData),0,wx.ALIGN_CENTER_VERTICAL)
1368       
1369        coeffDict = torsionRestData['Coeff']
1370        torsionList = torsionRestData['Torsions']
1371        if len(torsionList):
1372            table = []
1373            rowLabels = []
1374            bad = []
1375            chisq = 0.
1376            Types = 2*[wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,2',]
1377            if 'macro' in General['Type']:
1378                colLabels = ['(res) A  B  C  D','coef name','torsion','obs E','restr','esd']
1379                for i,[indx,ops,cofName,esd] in enumerate(torsionList):
1380                    try:
1381                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1382                        name = '('+atoms[2][1]+atoms[2][0].strip()+atoms[2][2]+')'
1383                        for atom in atoms:
1384                            name += '  '+atom[3]
1385                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1386                        tor = G2mth.getRestTorsion(XYZ,Amat)
1387                        restr,calc = G2mth.calcTorsionEnergy(tor,coeffDict[cofName])
1388                        chisq += torsionRestData['wtFactor']*(restr/esd)**2
1389                        table.append([name,cofName,tor,calc,restr,esd])
1390                        rowLabels.append(str(i))
1391                    except KeyError:
1392                        print '**** WARNING - missing atom - restraint deleted ****'
1393                        bad.append(i)
1394            if len(bad):
1395                bad.reverse()
1396                for ibad in bad:
1397                    del torsionList[ibad]
1398            torsionTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1399            Torsions = G2gd.GSGrid(TorsionRestr)
1400            Torsions.SetTable(torsionTable, True)
1401            Torsions.AutoSizeColumns(False)
1402            for r in range(len(torsionList)):
1403                for c in range(5):
1404                    Torsions.SetReadOnly(r,c,True)
1405                    Torsions.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1406            Torsions.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1407            Torsions.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1408            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1409            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1410            mainSizer.Add(wx.StaticText(TorsionRestr,-1,
1411                'Torsion restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1412                %(chisq,chisq/len(torsionList))),0,wx.ALIGN_CENTER_VERTICAL)
1413            mainSizer.Add(Torsions,0,)
1414           
1415            mainSizer.Add((5,5))
1416            mainSizer.Add(wx.StaticText(TorsionRestr,-1,'Torsion function coefficients:'),0,wx.ALIGN_CENTER_VERTICAL)
1417            table = []
1418            rowLabels = []
1419            Types = 9*[wg.GRID_VALUE_FLOAT+':10,4',]
1420            colLabels = ['Mag A','Pos A','Width A','Mag B','Pos B','Width B','Mag C','Pos C','Width C']
1421            for item in coeffDict:
1422                rowLabels.append(item)
1423                table.append(coeffDict[item])
1424            coeffTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1425            Coeff = G2gd.GSGrid(TorsionRestr)
1426            Coeff.SetTable(coeffTable, True)
1427            Coeff.AutoSizeColumns(False)
1428            for r in range(len(coeffDict)):
1429                for c in range(9):
1430                    Coeff.SetReadOnly(r,c,True)
1431                    Coeff.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1432            mainSizer.Add(Coeff,0,)
1433        else:
1434            mainSizer.Add(wx.StaticText(TorsionRestr,-1,'No torsion restraints for this phase'),0,)
1435
1436        TorsionRestr.SetSizer(mainSizer)
1437        Size = mainSizer.Fit(G2frame.dataFrame)
1438        Size[0] = 600
1439        Size[1] += 50       #make room for tab
1440        TorsionRestr.SetSize(Size)
1441        TorsionRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1442        G2frame.dataFrame.SetSize(Size)
1443
1444    def UpdateRamaRestr(ramaRestData):
1445
1446        def OnCellChange(event):
1447            r,c =  event.GetRow(),event.GetCol()
1448            val = ramaRestData['Ramas'][r][c]
1449            try:
1450                new = float(ramaTable.GetValue(r,c))
1451                if new <= 0. or new > 5.:
1452                    raise ValueError
1453                ramaRestData['Ramas'][r][4] = new     #only esd is editable
1454            except ValueError:
1455                pass           
1456            wx.CallAfter(UpdateRamaRestr,ramaRestData)               
1457           
1458        def OnDeleteRestraint(event):
1459            rows = Ramas.GetSelectedRows()
1460            if not rows:
1461                return
1462            rows.sort()
1463            rows.reverse()
1464            for row in rows:
1465                ramaList.remove(ramaList[row])
1466            UpdateRamaRestr(ramaRestData)               
1467           
1468        def OnChangeEsd(event):
1469            rows = Ramas.GetSelectedRows()
1470            if not rows:
1471                return
1472            Ramas.ClearSelection()
1473            val = ramaList[rows[0]][4]
1474            dlg = G2gd.SingleFloatDialog(G2frame,'New value','Enter new esd for energy',val,[0.,5.],'%.2f')
1475            if dlg.ShowModal() == wx.ID_OK:
1476                parm = dlg.GetValue()
1477                for r in rows:
1478                    ramaRestData['Ramas'][r][4] = parm
1479            dlg.Destroy()
1480            UpdateRamaRestr(ramaRestData)               
1481                                           
1482        RamaRestr.DestroyChildren()
1483        dataDisplay = wx.Panel(RamaRestr)
1484        mainSizer = wx.BoxSizer(wx.VERTICAL)
1485        mainSizer.Add((5,5),0)
1486        mainSizer.Add(WtBox(RamaRestr,ramaRestData),0,wx.ALIGN_CENTER_VERTICAL)
1487
1488        ramaList = ramaRestData['Ramas']
1489        coeffDict = ramaRestData['Coeff']
1490        if len(ramaList):
1491            mainSizer.Add(wx.StaticText(RamaRestr,-1,'Ramachandran restraints:'),0,wx.ALIGN_CENTER_VERTICAL)
1492            table = []
1493            rowLabels = []
1494            bad = []
1495            chisq = 0.
1496            Types = 2*[wg.GRID_VALUE_STRING,]+5*[wg.GRID_VALUE_FLOAT+':10,2',]
1497            if 'macro' in General['Type']:
1498                colLabels = ['(res) A  B  C  D  E','coef name','phi','psi','obs E','restr','esd']
1499                for i,[indx,ops,cofName,esd] in enumerate(ramaList):
1500                    try:
1501                        atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1502                        name = '('+atoms[3][1]+atoms[3][0].strip()+atoms[3][2]+')'
1503                        for atom in atoms:
1504                            name += '  '+atom[3]
1505                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1506                        phi,psi = G2mth.getRestRama(XYZ,Amat)
1507                        restr,calc = G2mth.calcRamaEnergy(phi,psi,coeffDict[cofName])
1508                        chisq += ramaRestData['wtFactor']*(restr/esd)**2
1509                        table.append([name,cofName,phi,psi,calc,restr,esd])
1510                        rowLabels.append(str(i))
1511                    except KeyError:
1512                        print '**** WARNING - missing atom - restraint deleted ****'
1513                        bad.append(i)
1514            if len(bad):
1515                bad.reverse()
1516                for ibad in bad:
1517                    del ramaList[ibad]
1518            ramaTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1519            Ramas = G2gd.GSGrid(RamaRestr)
1520            Ramas.SetTable(ramaTable, True)
1521            Ramas.AutoSizeColumns(False)
1522            for r in range(len(ramaList)):
1523                for c in range(6):
1524                    Ramas.SetReadOnly(r,c,True)
1525                    Ramas.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1526            Ramas.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1527            Ramas.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1528            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1529            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1530            mainSizer.Add(wx.StaticText(RamaRestr,-1,
1531                'Ramachandran restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1532                %(chisq,chisq/len(ramaList))),0,wx.ALIGN_CENTER_VERTICAL)
1533            mainSizer.Add(Ramas,0,)
1534        else:
1535            mainSizer.Add(wx.StaticText(RamaRestr,-1,'No Ramachandran restraints for this phase'),0,)
1536        mainSizer.Add((5,5))
1537        mainSizer.Add(wx.StaticText(RamaRestr,-1,'Ramachandran function coefficients:'),0,wx.ALIGN_CENTER_VERTICAL)
1538        if len(coeffDict):
1539            table = []
1540            rowLabels = []
1541            Types = 6*[wg.GRID_VALUE_FLOAT+':10,4',]
1542            colLabels = ['Mag','Pos phi','Pos psi','sig(phi)','sig(psi)','sig(cov)']
1543            for item in coeffDict:
1544                for i,term in enumerate(coeffDict[item]):
1545                    rowLabels.append(item+' term:'+str(i))
1546                    table.append(term)
1547            coeffTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1548            Coeff = G2gd.GSGrid(RamaRestr)
1549            Coeff.SetTable(coeffTable, True)
1550            Coeff.AutoSizeColumns(False)
1551            for r in range(Coeff.GetNumberRows()):
1552                for c in range(6):
1553                    Coeff.SetReadOnly(r,c,True)
1554                    Coeff.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1555            mainSizer.Add(Coeff,0,)
1556
1557        RamaRestr.SetSizer(mainSizer)
1558        Size = mainSizer.Fit(G2frame.dataFrame)
1559        Size[0] = 600
1560        Size[1] += 50       #make room for tab
1561        RamaRestr.SetSize(Size)
1562        RamaRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1563        G2frame.dataFrame.SetSize(Size)
1564
1565    def UpdateChemcompRestr(chemcompRestData):
1566       
1567        def OnCellChange(event):
1568            r,c =  event.GetRow(),event.GetCol()
1569            rowLabl = ChemComps.GetRowLabelValue(r)
1570            row = int(rowLabl.split(':')[1])
1571            if 'Restr' in rowLabl:
1572                try:
1573                    new = float(chemcompTable.GetValue(r,c))
1574                    chemcompRestData['Sites'][row][c-2] = new         #obsd or esd
1575                except ValueError:
1576                    pass
1577            else:
1578                try:
1579                    new = float(chemcompTable.GetValue(r,c))
1580                    id = int(rowLabl.split(':')[2])
1581                    chemcompRestData['Sites'][row][1][id] = new     #only factor
1582                except ValueError:
1583                    pass
1584            wx.CallAfter(UpdateChemcompRestr,chemcompRestData)               
1585           
1586        def OnDeleteRestraint(event):
1587            rows = ChemComps.GetSelectedRows()[0]
1588            if not rows:
1589                return
1590            rowLabl = ChemComps.GetRowLabelValue(r)
1591            row = int(rowLabl.split(':')[1])
1592            if 'Restr' in rowLabl:
1593                del chemcompList[row]
1594            else:
1595                term = int(rowLabl.split(':')[2])
1596                del chemcompList[row][0][term]
1597                del chemcompList[row][1][term]
1598            UpdateChemcompRestr(chemcompRestData)               
1599           
1600        def OnChangeValue(event):
1601            rows = ChemComps.GetSelectedRows()
1602            if not rows:
1603                return
1604            ChemComps.ClearSelection()
1605            dlg = G2gd.SingleFloatDialog(G2frame,'New value',
1606                'Enter new value for restraint multiplier',1.0,[-1.e6,1.e6],'%.2f')
1607            if dlg.ShowModal() == wx.ID_OK:
1608                parm = dlg.GetValue()
1609                for r in rows:
1610                    rowLabl = ChemComps.GetRowLabelValue(r)
1611                    if 'term' in rowLabl:
1612                        items = rowLabl.split(':')
1613                        chemcompRestData['Sites'][int(items[1])][1][int(items[2])] = parm
1614            dlg.Destroy()
1615            UpdateChemcompRestr(chemcompRestData)               
1616
1617        ChemCompRestr.DestroyChildren()
1618        dataDisplay = wx.Panel(ChemCompRestr)
1619        mainSizer = wx.BoxSizer(wx.VERTICAL)
1620        mainSizer.Add((5,5),0)
1621        mainSizer.Add(WtBox(ChemCompRestr,chemcompRestData),0,wx.ALIGN_CENTER_VERTICAL)
1622        mainSizer.Add(wx.StaticText(ChemCompRestr,-1, 
1623            'NB: The chemical restraint sum is over the unit cell contents'),0,wx.ALIGN_CENTER_VERTICAL)
1624        mainSizer.Add((5,5),0)
1625
1626        chemcompList = chemcompRestData['Sites']
1627        if len(chemcompList):
1628            table = []
1629            rowLabels = []
1630            bad = []
1631            chisq = 0.
1632            Types = [wg.GRID_VALUE_STRING,]+5*[wg.GRID_VALUE_FLOAT+':10,2',]
1633            colLabels = ['Atoms','mul*frac','factor','calc','obs','esd']
1634            for i,[indx,factors,obs,esd] in enumerate(chemcompList):
1635                try:
1636                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1637                    mul = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cs+1))
1638                    frac = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cs-1))
1639                    mulfrac = mul*frac
1640                    calcs = mul*frac*factors
1641                    chisq += chiralRestData['wtFactor']*((obs-np.sum(calcs))/esd)**2
1642                    for iatm,[atom,mf,fr,clc] in enumerate(zip(atoms,mulfrac,factors,calcs)):
1643                        table.append([atom,mf,fr,clc,'',''])
1644                        rowLabels.append('term:'+str(i)+':'+str(iatm))
1645                    table.append(['Sum','','',np.sum(calcs),obs,esd])
1646                    rowLabels.append('Restr:'+str(i))
1647                except KeyError:
1648                    print '**** WARNING - missing atom - restraint deleted ****'
1649                    bad.append(i)
1650            if len(bad):
1651                bad.reverse()
1652                for ibad in bad:
1653                    del chemcompList[ibad]
1654            chemcompTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1655            ChemComps = G2gd.GSGrid(ChemCompRestr)
1656            ChemComps.SetTable(chemcompTable, True)
1657            ChemComps.AutoSizeColumns(False)
1658            for r in range(chemcompTable.GetNumberRows()):
1659                for c in range(2):
1660                    ChemComps.SetReadOnly(r,c,True)
1661                    ChemComps.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1662                if 'Restr' in ChemComps.GetRowLabelValue(r):
1663                    for c in range(4):
1664                        ChemComps.SetReadOnly(r,c,True)
1665                        ChemComps.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1666                    for c in [1,2]:
1667                        ChemComps.SetCellTextColour(r,c,VERY_LIGHT_GREY)
1668                else:
1669                    for c in [3,4,5]:
1670                        ChemComps.SetReadOnly(r,c,True)
1671                        ChemComps.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1672                    for c in [4,5]:
1673                        ChemComps.SetCellTextColour(r,c,VERY_LIGHT_GREY)
1674                       
1675            ChemComps.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1676            ChemComps.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1677            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1678            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeValue, id=G2gd.wxID_RESRCHANGEVAL)
1679            mainSizer.Add(wx.StaticText(ChemCompRestr,-1,
1680                'Chemical composition restraints: sum(wt*(delt/sig)^2) =    %.2f, mean(wt*(delt/sig)^2) =    %.2f'    \
1681                %(chisq,chisq/len(chemcompList))),0,wx.ALIGN_CENTER_VERTICAL)
1682            mainSizer.Add(ChemComps,0,)
1683        else:
1684            mainSizer.Add(wx.StaticText(ChemCompRestr,-1,'No chemical composition restraints for this phase'),0,)
1685
1686        ChemCompRestr.SetSizer(mainSizer)
1687        Size = mainSizer.Fit(G2frame.dataFrame)
1688        Size[0] = 600
1689        Size[1] += 50       #make room for tab
1690        ChemCompRestr.SetSize(Size)
1691        ChemCompRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1692        G2frame.dataFrame.SetSize(Size)
1693   
1694       
1695    def UpdateTextureRestr(textureRestData):
1696           
1697        def OnDeleteRestraint(event):
1698            rows = Textures.GetSelectedRows()
1699            if not rows:
1700                return
1701            rows.sort()
1702            rows.reverse()
1703            for row in rows:
1704                textureList.remove(textureList[row])
1705            wx.CallAfter(UpdateTextureRestr,textureRestData)               
1706           
1707        def OnCellChange(event):
1708            r,c = event.GetRow(),event.GetCol()
1709            val = textureRestData['HKLs'][r][c]
1710            try:
1711                if c == 1:  #grid size
1712                    new = int(textureTable.GetValue(r,c))
1713                    if new < 6 or new > 24:
1714                        raise valueError
1715                elif c in [2,4]:   #esds
1716                    new = float(textureTable.GetValue(r,c))
1717                    if new < -1. or new > 2.:
1718                        raise ValueError
1719                else:
1720                    new = textureTable.GetValue(r,c)
1721                textureRestData['HKLs'][r][c] = new
1722            except ValueError:
1723                pass           
1724            wx.CallAfter(UpdateTextureRestr,textureRestData)               
1725
1726        TextureRestr.DestroyChildren()
1727        dataDisplay = wx.Panel(TextureRestr)
1728        mainSizer = wx.BoxSizer(wx.VERTICAL)
1729        mainSizer.Add((5,5),0)
1730        mainSizer.Add(WtBox(TextureRestr,textureRestData),0,wx.ALIGN_CENTER_VERTICAL)
1731        mainSizer.Add(wx.StaticText(TextureRestr,-1, 
1732            'NB: The texture restraints suppress negative pole figure values for the selected HKLs\n'
1733            '    "unit esd" gives a bias toward a flatter polefigure'),0,wx.ALIGN_CENTER_VERTICAL)
1734        mainSizer.Add((5,5),0)
1735
1736        textureList = textureRestData['HKLs']
1737        textureData = General['SH Texture']
1738        if len(textureList):
1739            table = []
1740            rowLabels = []
1741            Types = [wg.GRID_VALUE_STRING,wg.GRID_VALUE_LONG,wg.GRID_VALUE_FLOAT+':10,2',
1742                wg.GRID_VALUE_BOOL,wg.GRID_VALUE_FLOAT+':10,2']
1743            colLabels = ['HKL','grid','neg esd','use unit?','unit esd']
1744            for i,[hkl,grid,esd1,ifesd2,esd2] in enumerate(textureList):
1745                table.append(['%d %d %d'%(hkl[0],hkl[1],hkl[2]),grid,esd1,ifesd2,esd2])
1746                rowLabels.append(str(i))
1747            textureTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1748            Textures = G2gd.GSGrid(TextureRestr)
1749            Textures.SetTable(textureTable, True)
1750            Textures.AutoSizeColumns(False)
1751            for r in range(len(textureList)):
1752                Textures.SetReadOnly(r,0,True)
1753                Textures.SetCellStyle(r,0,VERY_LIGHT_GREY,True)
1754                if not textureTable.GetValue(r,3):
1755                    Textures.SetReadOnly(r,4,True)
1756                    Textures.SetCellStyle(r,4,VERY_LIGHT_GREY,True)
1757                    Textures.SetCellTextColour(r,4,VERY_LIGHT_GREY)
1758            Textures.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1759            Textures.Bind(wg.EVT_GRID_CELL_CHANGE, OnCellChange)
1760            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1761            mainSizer.Add(Textures,0,)
1762        else:
1763            mainSizer.Add(wx.StaticText(TextureRestr,-1,'No texture restraints for this phase'),0,)
1764        TextureRestr.SetSizer(mainSizer)
1765        Size = mainSizer.Fit(G2frame.dataFrame)
1766        Size[0] = 600
1767        Size[1] += 50       #make room for tab
1768        TextureRestr.SetSize(Size)
1769        TextureRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1770        G2frame.dataFrame.SetSize(Size)
1771           
1772    def OnPageChanged(event):
1773        page = event.GetSelection()
1774        text = G2frame.dataDisplay.GetPageText(page)
1775        G2frame.dataFrame.RestraintEdit.SetLabel(G2gd.wxID_RESRCHANGEVAL,'Change value')
1776        SetStatusLine('')
1777        if text == 'Bond restraints':
1778            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1779            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1780            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,True)
1781            bondRestData = restrData['Bond']
1782            UpdateBondRestr(bondRestData)
1783        elif text == 'Angle restraints':
1784            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1785            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1786            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,True)
1787            angleRestData = restrData['Angle']
1788            UpdateAngleRestr(angleRestData)
1789        elif text == 'Plane restraints':
1790            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1791            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1792            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,False)
1793            planeRestData = restrData['Plane']
1794            UpdatePlaneRestr(planeRestData)
1795        elif text == 'Chiral restraints':
1796            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1797            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,False)
1798            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,True)
1799            chiralRestData = restrData['Chiral']
1800            UpdateChiralRestr(chiralRestData)
1801        elif text == 'Torsion restraints':
1802            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1803            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,False)
1804            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,False)
1805            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_AARESTRAINTPLOT,True)
1806            torsionRestData = restrData['Torsion']
1807            UpdateTorsionRestr(torsionRestData)
1808        elif text == 'Ramachandran restraints':
1809            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1810            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,False)
1811            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,False)
1812            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_AARESTRAINTPLOT,True)
1813            ramaRestData = restrData['Rama']
1814            UpdateRamaRestr(ramaRestData)
1815            G2plt.PlotRama(G2frame,phaseName,rama,ramaName)
1816        elif text == 'Chem. comp. restraints':
1817            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1818            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1819            G2frame.dataFrame.RestraintEdit.SetLabel(G2gd.wxID_RESRCHANGEVAL,'Change factor')
1820            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,True)
1821            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTCHANGEESD,False)
1822            chemcompRestData = restrData['ChemComp']
1823            UpdateChemcompRestr(chemcompRestData)
1824        elif text == 'Texture restraints':
1825            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1826            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1827            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,True)
1828            textureRestData = restrData['Texture']
1829            UpdateTextureRestr(textureRestData)
1830           
1831        event.Skip()
1832
1833    def SetStatusLine(text):
1834        Status.SetStatusText(text)                                     
1835       
1836    if G2frame.dataDisplay:
1837        G2frame.dataDisplay.Destroy()
1838       
1839    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1840    G2frame.dataFrame.SetLabel('restraints for '+phaseName)
1841    if not G2frame.dataFrame.GetStatusBar():
1842        Status = G2frame.dataFrame.CreateStatusBar()
1843    SetStatusLine('')
1844   
1845    G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTSELPHASE,False)
1846    if len(Phases) > 1:
1847        G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTSELPHASE,True)
1848        G2frame.dataFrame.Bind(wx.EVT_MENU, OnSelectPhase, id=G2gd.wxID_RESTSELPHASE)
1849    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddRestraint, id=G2gd.wxID_RESTRAINTADD)
1850    if 'macro' in phasedata['General']['Type']:
1851        G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_AARESTRAINTADD,True)
1852        G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddAARestraint, id=G2gd.wxID_AARESTRAINTADD)
1853        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotAARestraint, id=G2gd.wxID_AARESTRAINTPLOT)
1854    G2frame.dataDisplay = G2gd.GSNoteBook(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize())
1855   
1856    BondRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1857    G2frame.dataDisplay.AddPage(BondRestr,'Bond restraints')
1858    AngleRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1859    G2frame.dataDisplay.AddPage(AngleRestr,'Angle restraints')
1860    PlaneRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1861    G2frame.dataDisplay.AddPage(PlaneRestr,'Plane restraints')
1862    ChiralRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1863    G2frame.dataDisplay.AddPage(ChiralRestr,'Chiral restraints')
1864    if 'macro' in General['Type']:
1865        TorsionRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1866        G2frame.dataDisplay.AddPage(TorsionRestr,'Torsion restraints')
1867        RamaRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1868        G2frame.dataDisplay.AddPage(RamaRestr,'Ramachandran restraints')
1869    ChemCompRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1870    G2frame.dataDisplay.AddPage(ChemCompRestr,'Chem. comp. restraints')
1871    if General['SH Texture']['Order']:
1872        TextureRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1873        G2frame.dataDisplay.AddPage(TextureRestr,'Texture restraints')
1874   
1875    UpdateBondRestr(restrData['Bond'])
1876
1877    G2frame.dataDisplay.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
Note: See TracBrowser for help on using the repository browser.