source: trunk/GSASIIrestrGUI.py @ 818

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

complete restraint GUI stuff
provide restraint plots for torsions & Ramachandran

File size: 64.8 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIrestr - restraint GUI routines
3########### SVN repository information ###################
4# $Date: 2012-12-05 15:38:26 -0600 (Wed, 05 Dec 2012) $
5# $Author: vondreele $
6# $Revision: 810 $
7# $URL: https://subversion.xor.aps.anl.gov/pyGSAS/trunk/GSASIIrestrGUI.py $
8# $Id: GSASIIrestrGUI.py 810 2012-12-05 21:38:26Z vondreele $
9########### SVN repository information ###################
10import wx
11import wx.grid as wg
12import time
13import numpy as np
14import numpy.ma as ma
15import os.path
16import GSASIIpath
17GSASIIpath.SetVersionNumber("$Revision: 810 $")
18import GSASIImath as G2mth
19import GSASIIlattice as G2lat
20import GSASIIphsGUI as G2phG
21import GSASIIspc as G2spc
22import GSASIIgrid as G2gd
23import GSASIIplot as G2plt
24import GSASIIdata as G2data
25
26VERY_LIGHT_GREY = wx.Colour(235,235,235)
27
28################################################################################
29#####  Restraints
30################################################################################           
31       
32def UpdateRestraints(G2frame,data,Phases,phaseName):
33    if not len(Phases):
34        print 'There are no phases to form restraints'
35        return
36    phasedata = Phases[phaseName]
37    if phaseName not in data:
38        data[phaseName] = {}
39    restrData = data[phaseName]
40    if 'Bond' not in restrData:
41        restrData['Bond'] = {'wtFactor':1.0,'Range':0.9,'Bonds':[],'Use':True}
42    if 'Angle' not in restrData:
43        restrData['Angle'] = {'wtFactor':1.0,'Range':0.85,'Angles':[],'Use':True}
44    if 'Plane' not in restrData:
45        restrData['Plane'] = {'wtFactor':1.0,'Range':0.9,'Planes':[],'Use':True}
46    if 'Chiral' not in restrData:
47        restrData['Chiral'] = {'wtFactor':1.0,'Range':0.9,'Volumes':[],'Use':True}
48    if 'Torsion' not in restrData:
49        restrData['Torsion'] = {'wtFactor':1.0,'Range':0.9,'Coeff':{},'Torsions':[],'Use':True}
50    if 'Rama' not in restrData:
51        restrData['Rama'] = {'wtFactor':1.0,'Range':0.9,'Coeff':{},'Ramas':[],'Use':True}
52    General = phasedata['General']
53    Cell = General['Cell'][1:7]          #skip flag & volume   
54    Amat,Bmat = G2lat.cell2AB(Cell)
55    SGData = General['SGData']
56    cx,ct = General['AtomPtrs'][:2]
57    Atoms = phasedata['Atoms']
58    AtLookUp = G2mth.FillAtomLookUp(Atoms)
59    if 'macro' in General['Type']:
60        Names = [atom[0]+':'+atom[1]+atom[2]+' '+atom[3] for atom in Atoms]
61        Ids = []
62        Coords = []
63        Types = []
64    else:   
65        Names = ['all '+ name for name in General['AtomTypes']]
66        iBeg = len(Names)
67        Types = [name for name in General['AtomTypes']]
68        Coords = [ [] for type in Types]
69        Ids = [ 0 for type in Types]
70        Names += [atom[ct-1] for atom in Atoms]
71    Types += [atom[ct] for atom in Atoms]
72    Coords += [atom[cx:cx+3] for atom in Atoms]
73    Ids += [atom[-1] for atom in Atoms]
74    rama = G2data.ramachandranDist['All']
75    ramaName = 'All'
76   
77    def OnSelectPhase(event):
78        dlg = wx.SingleChoiceDialog(G2frame,'Select','Phase',Phases.keys())
79        try:
80            if dlg.ShowModal() == wx.ID_OK:
81                phaseName = Phases.keys()[dlg.GetSelection()]
82                UpdateRestraints(G2frame,data,Phases,phaseName)
83        finally:
84            dlg.Destroy()
85   
86    def getMacroFile(macName):
87        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
88        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' restraint macro file',
89            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
90            style=wx.OPEN | wx.CHANGE_DIR)
91        try:
92            if dlg.ShowModal() == wx.ID_OK:
93                macfile = dlg.GetPath()
94                macro = open(macfile,'Ur')
95                head = macro.readline()
96                if macName not in head:
97                    print head
98                    print '**** ERROR - wrong restraint macro file selected, try again ****'
99                    macro = []
100            else: # cancel was pressed
101                macxro = []
102        finally:
103            dlg.Destroy()
104        return macro        #advanced past 1st line
105       
106    def OnPlotAARestraint(event):
107        page = G2frame.dataDisplay.GetSelection()
108        if 'Torsion' in G2frame.dataDisplay.GetPageText(page):
109            torNames = []
110            torNames += restrData['Torsion']['Coeff'].keys()
111            dlg = wx.SingleChoiceDialog(G2frame,'Select','Torsion data',torNames)
112            try:
113                if dlg.ShowModal() == wx.ID_OK:
114                    torName = torNames[dlg.GetSelection()]
115                    torsion = G2data.torsionDist[torName]
116                    torCoeff = restrData['Torsion']['Coeff'][torName]
117                    torList = restrData['Torsion']['Torsions']
118                    Names = []
119                    Angles = []
120                    for i,[indx,ops,cofName,esd] in enumerate(torList):
121                        if cofName == torName:
122                            atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
123                            name = '('+atoms[2][1]+atoms[2][0].strip()+atoms[2][2]+')'
124                            for atom in atoms:
125                                name += '  '+atom[3]
126                            XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
127                            angle = G2mth.getRestTorsion(XYZ,Amat)
128                            Angles.append(angle)
129                            Names.append(name) 
130                    G2plt.PlotTorsion(G2frame,phaseName,torsion,torName,Names,np.array(Angles),torCoeff)
131            finally:
132                dlg.Destroy()
133           
134        elif 'Rama' in G2frame.dataDisplay.GetPageText(page):
135            ramaNames = ['All',]
136            ramaNames += restrData['Rama']['Coeff'].keys()
137            dlg = wx.SingleChoiceDialog(G2frame,'Select','Ramachandran data',ramaNames)
138            try:
139                if dlg.ShowModal() == wx.ID_OK:
140                    ramaName = ramaNames[dlg.GetSelection()]
141                    rama = G2data.ramachandranDist[ramaName]
142                    ramaCoeff = []
143                    if ramaName != 'All':
144                        ramaCoeff = restrData['Rama']['Coeff'][ramaName]
145                    ramaList = restrData['Rama']['Ramas']
146                    Names = []
147                    PhiPsi = []
148                    for i,[indx,ops,cofName,esd] in enumerate(ramaList):
149                        if cofName == ramaName or (ramaName == 'All' and '-1' in cofName):
150                            atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
151                            name = '('+atoms[3][1]+atoms[3][0].strip()+atoms[3][2]+')'
152                            for atom in atoms:
153                                name += '  '+atom[3]
154                            XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
155                            phi,psi = G2mth.getRestRama(XYZ,Amat)
156                            PhiPsi.append([phi,psi])
157                            Names.append(name) 
158                    G2plt.PlotRama(G2frame,phaseName,rama,ramaName,Names,np.array(PhiPsi),ramaCoeff)
159            finally:
160                dlg.Destroy()
161           
162    def OnAddRestraint(event):
163        page = G2frame.dataDisplay.GetSelection()
164        if 'Bond' in G2frame.dataDisplay.GetPageText(page):
165            AddBondRestraint(restrData['Bond'])
166        elif 'Angle' in G2frame.dataDisplay.GetPageText(page):
167            AddAngleRestraint(restrData['Angle'])
168        elif 'Plane' in G2frame.dataDisplay.GetPageText(page):
169            AddPlaneRestraint(restrData['Plane'])
170        elif 'Chiral' in G2frame.dataDisplay.GetPageText(page):
171            AddChiralRestraint(restrData['Chiral'])
172#        elif 'Torsion' in G2frame.dataDisplay.GetPageText(page):
173#            AddTorsionRestraint(restrData['Torsion'])
174#        elif 'Rama' in G2frame.dataDisplay.GetPageText(page):
175#            AddRamaRestraint(restrData['Rama'])
176           
177    def OnAddAARestraint(event):
178        page = G2frame.dataDisplay.GetSelection()
179        if 'Bond' in G2frame.dataDisplay.GetPageText(page):
180            AddAABondRestraint(restrData['Bond'])
181        elif 'Angle' in G2frame.dataDisplay.GetPageText(page):
182            AddAAAngleRestraint(restrData['Angle'])
183        elif 'Plane' in G2frame.dataDisplay.GetPageText(page):
184            AddAAPlaneRestraint(restrData['Plane'])
185        elif 'Chiral' in G2frame.dataDisplay.GetPageText(page):
186            AddAAChiralRestraint(restrData['Chiral'])
187        elif 'Torsion' in G2frame.dataDisplay.GetPageText(page):
188            AddAATorsionRestraint(restrData['Torsion'])
189        elif 'Rama' in G2frame.dataDisplay.GetPageText(page):
190            AddAARamaRestraint(restrData['Rama'])
191           
192    def AddBondRestraint(bondRestData):
193        Radii = dict(zip(General['AtomTypes'],General['BondRadii']))
194        Lists = {'origin':[],'target':[]}
195        for listName in ['origin','target']:
196            dlg = wx.MultiChoiceDialog(G2frame,'Bond restraint '+listName+' for '+General['Name'],
197                    'Select bond restraint '+listName+' atoms',Names)
198            if dlg.ShowModal() == wx.ID_OK:
199                sel = dlg.GetSelections()
200                for x in sel:
201                    if 'all' in Names[x]:
202                        allType = Types[x]
203                        for name,Type,coords,id in zip(Names,Types,Coords,Ids):
204                            if Type == allType and 'all' not in name:
205                                Lists[listName].append([id,Type,coords])
206                    else:
207                        Lists[listName].append([Ids[x],Types[x],Coords[x],])
208        Factor = bondRestData['Range']
209        indices = (-1,0,1)
210        Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
211        origAtoms = Lists['origin']
212        targAtoms = Lists['target']
213        dlg = wx.ProgressDialog("Generating bond restraints","Processed origin atoms",len(origAtoms), 
214            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME)
215        try:
216            Norig = 0
217            for Oid,Otype,Ocoord in origAtoms:
218                Norig += 1
219                dlg.Update(Norig)
220                for Tid,Ttype,Tcoord in targAtoms:
221                    if 'macro' in General['Type']:
222                        result = [[Tcoord,1,[0,0,0]],]
223                    else:
224                        result = G2spc.GenAtom(Tcoord,SGData,False,Move=False)
225                    BsumR = (Radii[Otype]+Radii[Ttype])*Factor
226                    for Txyz,Top,Tunit in result:
227                        Dx = (Txyz-np.array(Ocoord))+Units
228                        dx = np.inner(Amat,Dx)
229                        dist = ma.masked_less(np.sqrt(np.sum(dx**2,axis=0)),0.5)
230                        IndB = ma.nonzero(ma.masked_greater(dist-BsumR,0.))
231                        if np.any(IndB):
232                            for indb in IndB:
233                                for i in range(len(indb)):
234                                    unit = Units[indb][i]+Tunit
235                                    if np.any(unit):
236                                        Topstr = '%d+%d,%d,%d'%(Top,unit[0],unit[1],unit[2])
237                                    else:
238                                        Topstr = str(Top)
239                                    newBond = [[Oid,Tid],['1',Topstr],1.54,0.01]
240                                    if newBond not in bondRestData['Bonds']:
241                                        bondRestData['Bonds'].append(newBond)
242        finally:
243            dlg.Destroy()
244        UpdateBondRestr(bondRestData)               
245
246    def AddAABondRestraint(bondRestData):
247        Radii = dict(zip(General['AtomTypes'],General['BondRadii']))
248        macro = getMacroFile('bond')
249        if not macro:
250            return
251        macStr = macro.readline()
252        atoms = zip(Names,Coords,Ids)
253       
254        Factor = bondRestData['Range']
255        while macStr:
256            items = macStr.split()
257            if 'F' in items[0]:
258                restrData['Bond']['wtFactor'] = float(items[1])
259            elif 'S' in items[0]:
260                oIds = []
261                oCoords = []
262                tIds = []
263                tCoords = []
264                res = items[1]
265                dist = float(items[2])
266                esd = float(items[3])
267                oAtm,tAtm = items[4:6]
268                for Name,coords,Id in atoms:
269                    names = Name.split()
270                    if res == '*' or res in names[0]:
271                        if oAtm == names[2]:
272                            oIds.append(Id)
273                            oCoords.append(np.array(coords))
274                        if tAtm == names[2]:
275                            tIds.append(Id)
276                            tCoords.append(np.array(coords))
277                for i,[oId,oCoord] in enumerate(zip(oIds,oCoords)):
278                    for tId,tCoord in zip(tIds,tCoords)[i:]:
279                        obsd = np.sqrt(np.sum(np.inner(Amat,tCoord-oCoord)**2))
280                        if dist*Factor < obsd < dist/Factor:
281                            newBond = [[oId,tId],['1','1'],dist,esd]
282                            if newBond not in bondRestData['Bonds']:
283                                bondRestData['Bonds'].append(newBond)                         
284            macStr = macro.readline()
285        macro.close()
286        UpdateBondRestr(bondRestData)               
287           
288    def AddAngleRestraint(angleRestData):
289        Radii = dict(zip(General['AtomTypes'],zip(General['BondRadii'],General['AngleRadii'])))
290        origAtoms = []
291        dlg = wx.MultiChoiceDialog(G2frame,'Select atom B for angle A-B-C for '+General['Name'],
292                'Select angle restraint origin atoms',Names)
293        if dlg.ShowModal() == wx.ID_OK:
294            sel = dlg.GetSelections()
295            for x in sel:
296                if 'all' in Names[x]:
297                    allType = Types[x]
298                    for name,Type,coords,id in zip(Names,Types,Coords,Ids):
299                        if Type == allType and 'all' not in name:
300                            origAtoms.append([id,Type,coords])
301                else:
302                    origAtoms.append([Ids[x],Types[x],Coords[x]])
303        targAtoms = [[Ids[x+iBeg],Types[x+iBeg],Coords[x+iBeg]] for x in range(len(Names[iBeg:]))]
304
305        Factor = angleRestData['Range']
306        indices = (-1,0,1)
307        Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
308        VectA = []
309        for Oid,Otype,Ocoord in origAtoms:
310            IndBlist = []
311            VectB = []
312            for Tid,Ttype,Tcoord in targAtoms:
313                result = G2spc.GenAtom(Tcoord,SGData,False,Move=False)
314                BsumR = (Radii[Otype][0]+Radii[Ttype][0])*Factor
315                AsumR = (Radii[Otype][1]+Radii[Ttype][1])*Factor
316                for Txyz,Top,Tunit in result:
317                    Dx = (Txyz-Ocoord)+Units
318                    dx = np.inner(Amat,Dx)
319                    dist = ma.masked_less(np.sqrt(np.sum(dx**2,axis=0)),0.5)
320                    IndB = ma.nonzero(ma.masked_greater(dist-BsumR,0.))
321                    if np.any(IndB):
322                        for indb in IndB:
323                            for i in range(len(indb)):
324                                if str(dx.T[indb][i]) not in IndBlist:
325                                    IndBlist.append(str(dx.T[indb][i]))
326                                    unit = Units[indb][i]+Tunit
327                                if np.any(unit):
328                                    Topstr = '%d+%d,%d,%d'%(Top,unit[0],unit[1],unit[2])
329                                else:
330                                    Topstr = str(Top)
331                                    tunit = '[%2d%2d%2d]'%(unit[0]+Tunit[0],unit[1]+Tunit[1],unit[2]+Tunit[2])
332                                    Dist = ma.getdata(dist[indb])[i]
333                                    if (Dist-AsumR) <= 0.:
334                                        VectB.append([Oid,'1',Ocoord,Tid,Topstr,Tcoord,Dist])
335            VectA.append(VectB)
336            for Vects in VectA:
337                for i,vecta in enumerate(Vects):                   
338                    for vectb in Vects[:i]:
339                        ids = [vecta[3],vecta[0],vectb[3]]
340                        ops = [vecta[4],vecta[1],vectb[4]]
341                        XYZ = np.array([vecta[5],vecta[2],vectb[5]])
342                        angle = G2mth.getRestAngle(XYZ,Amat)
343                        if angle not in angleRestData['Angles']:
344                            angleRestData['Angles'].append([ids,ops,109.5,1.0])
345        UpdateAngleRestr(angleRestData)               
346
347    def AddAAAngleRestraint(angleRestData):
348        macro = getMacroFile('angle')
349        if not macro:
350            return
351        atoms = zip(Names,Ids)
352        macStr = macro.readline()
353        while macStr:
354            items = macStr.split()
355            if 'F' in items[0]:
356                restrData['Angle']['wtFactor'] = float(items[1])
357            elif 'S' in items[0]:
358                res = items[1]
359                value = float(items[2])
360                esd = float(items[3])
361                Atms = items[4:7]
362                pAtms = ['','','']
363                for i,atm in enumerate(Atms):
364                    if '+' in atm:
365                        pAtms[i] = atm.strip('+')
366                ids = np.array([0,0,0])
367                rNum = -1
368                for name,id in atoms:
369                    names = name.split()
370                    tNum = int(names[0].split(':')[0])
371                    if res in names[0]:
372                        try:
373                            ipos = Atms.index(names[2])
374                            ids[ipos] = id
375                        except ValueError:
376                            continue
377                    elif res == '*':
378                        try:
379                            ipos = Atms.index(names[2])
380                            if not np.all(ids):
381                                rNum = int(names[0].split(':')[0])
382                            ids[ipos] = id
383                        except ValueError:
384                            try:
385                                if tNum == rNum+1:
386                                    ipos = pAtms.index(names[2])
387                                    ids[ipos] = id
388                            except ValueError:
389                                continue
390                    if np.all(ids):
391                        angle = [list(ids),['1','1','1'],value,esd]
392                        if angle not in angleRestData['Angles']:
393                            angleRestData['Angles'].append(angle)
394                        ids = np.array([0,0,0])
395            macStr = macro.readline()
396        macro.close()
397        UpdateAngleRestr(angleRestData)               
398       
399    def AddPlaneRestraint(restrData):
400        ids = []
401        dlg = wx.MultiChoiceDialog(G2frame,'Select 4 or more atoms for plane in '+General['Name'],
402                'Select 4+ atoms',Names[iBeg:])
403        if dlg.ShowModal() == wx.ID_OK:
404            sel = dlg.GetSelections()
405            if len(sel) > 3:
406                for x in sel:
407                    ids.append(Ids[x+iBeg])
408                ops = ['1' for i in range(len(sel))]
409                plane = [ids,ops,0.0,0.01]
410                if plane not in restrData['Planes']:
411                    restrData['Planes'].append(plane)
412            else:
413                print '**** ERROR - not enough atoms fro a plane restraint - try again ****'
414        UpdatePlaneRestr(restrData)               
415
416    def AddAAPlaneRestraint(planeRestData):
417        macro = getMacroFile('plane')
418        if not macro:
419            return
420        atoms = zip(Names,Ids)
421        macStr = macro.readline()
422        while macStr:
423            items = macStr.split()
424            if 'F' in items[0]:
425                restrData['Plane']['wtFactor'] = float(items[1])
426            elif 'S' in items[0]:
427                res = items[1]
428                esd = float(items[2])
429                Atms = items[3:]
430                pAtms = ['' for i in Atms]
431                for i,atm in enumerate(Atms):
432                    if '+' in atm:
433                        pAtms[i] = atm.strip('+')
434                rNum = -1
435                ids = np.zeros(len(Atms))
436                ops = ['1' for i in range(len(Atms))]
437                for name,id in atoms:
438                    names = name.split()
439                    tNum = int(names[0].split(':')[0])
440                    if res in names[0]:
441                        try:
442                            ipos = Atms.index(names[2])
443                            ids[ipos] = id
444                        except ValueError:
445                            continue
446                    elif res == '*':
447                        try:
448                            ipos = Atms.index(names[2])
449                            if not np.all(ids):
450                                rNum = int(names[0].split(':')[0])
451                            ids[ipos] = id
452                        except ValueError:
453                            try:
454                                if tNum == rNum+1:
455                                    ipos = pAtms.index(names[2])
456                                    ids[ipos] = id
457                            except ValueError:
458                                continue
459                    if np.all(ids):
460                        plane = [list(ids),ops,0.0,esd]
461                        if plane not in planeRestData['Planes']:
462                            planeRestData['Planes'].append(plane)
463                        ids = np.zeros(len(Atms))
464            macStr = macro.readline()
465        macro.close()
466        UpdatePlaneRestr(planeRestData)               
467
468    def AddChiralRestraint():
469        print 'Chiral restraint'
470       
471    def AddAAChiralRestraint(chiralRestData):
472        macro = getMacroFile('chiral')
473        if not macro:
474            return
475        atoms = zip(Names,Ids)
476        macStr = macro.readline()
477        while macStr:
478            items = macStr.split()
479            if 'F' in items[0]:
480                restrData['Chiral']['wtFactor'] = float(items[1])
481            elif 'S' in items[0]:
482                res = items[1]
483                value = float(items[2])
484                esd = float(items[3])
485                Atms = items[4:8]
486                ids = np.array([0,0,0,0])
487                for name,id in atoms:
488                    names = name.split()
489                    if res in names[0]:
490                        try:
491                            ipos = Atms.index(names[2])
492                            ids[ipos] = id
493                        except ValueError:
494                            pass
495                        if np.all(ids):
496                            chiral = [list(ids),['1','1','1','1'],value,esd]
497                            if chiral not in chiralRestData['Volumes']:
498                                chiralRestData['Volumes'].append(chiral)
499                            ids = np.array([0,0,0,0])
500            macStr = macro.readline()
501        macro.close()
502        UpdateChiralRestr(chiralRestData)               
503       
504    def makeChains(Names,Ids):
505        Chains = {}
506        chain = ''
507        atoms = zip(Names,Ids)
508        for name,id in atoms:
509            items = name.split()
510            rnum,res = items[0].split(':')
511            if items[1] not in Chains:
512                Residues = {}
513                Chains[items[1]] = Residues
514            if int(rnum) not in Residues:
515                Residues[int(rnum)] = []
516            Residues[int(rnum)].append([res,items[2],id])
517        return Chains
518       
519#       
520    def AddAATorsionRestraint(torsionRestData):
521        macro = getMacroFile('torsion')
522        if not macro:
523            return
524        Chains = makeChains(Names,Ids)           
525        macStr = macro.readline()[:-1]
526        while macStr:
527            items = macStr.split()
528            if 'F' in items[0]:
529                restrData['Torsion']['wtFactor'] = float(items[1])
530            elif 'A' in items[0]:
531                name = items[10]
532                coeff = np.zeros(9)
533                for i,item in enumerate(items[1:10]):
534                    coeff[i] = float(item)
535                torsionRestData['Coeff'][name] = coeff
536            elif 'S' in items[0]:
537                Name = items[1]
538                Res = items[2]
539                Esd = float(items[3])
540                Atms = items[4:8]
541                pAtms = ['','','','']
542                for i,atm in enumerate(Atms):
543                    if '+' in atm:
544                        pAtms[i] = atm.strip('+')
545                ids = np.array([0,0,0,0])
546                chains = Chains.keys()
547                chains.sort()
548                for chain in chains:
549                    residues = Chains[chain].keys()
550                    residues.sort()
551                    for residue in residues:
552                        if residue == residues[-1] and Res == '*':
553                            continue
554                        if Res != '*':
555                            for res,name,id in Chains[chain][residue]:
556                                if Res == res:
557                                    try:
558                                        ipos = Atms.index(name)
559                                        ids[ipos] = id
560                                    except ValueError:
561                                        continue
562                        else:
563                            for res,name,id in Chains[chain][residue]:
564                                try:
565                                    ipos = Atms.index(name)
566                                    ids[ipos] = id
567                                except ValueError:
568                                    continue
569                            for res,name,id in Chains[chain][residue+1]:
570                                try:
571                                    ipos = pAtms.index(name)
572                                    ids[ipos] = id
573                                except ValueError:
574                                    continue
575                        if np.all(ids):
576                            torsion = [list(ids),['1','1','1','1'],Name,Esd]
577                            if torsion not in torsionRestData['Torsions']:
578                                torsionRestData['Torsions'].append(torsion)
579                            ids = np.array([0,0,0,0])                           
580            macStr = macro.readline()
581        macro.close()
582        UpdateTorsionRestr(torsionRestData)                       
583       
584    def AddAARamaRestraint(ramaRestData):
585        macro = getMacroFile('Ramachandran')
586        if not macro:
587            return
588        Chains = makeChains(Names,Ids)           
589        macStr = macro.readline()
590        while macStr:
591            items = macStr.split()
592            if 'F' in items[0]:
593                restrData['Rama']['wtFactor'] = float(items[1])
594            elif 'A' in items[0]:
595                nTerms = int(items[1])
596                name = items[2]
597                coeff = np.zeros((nTerms,6))
598                for i in range(nTerms):
599                    macStr = macro.readline()
600                    items = macStr.split()
601                    for j,val in enumerate(items):
602                        coeff[i][j] = float(val)
603                ramaRestData['Coeff'][name] = coeff
604            elif 'S' in items[0]:
605                Name = items[1]
606                Res = items[2]
607                Esd = float(items[3])
608                Atms = items[4:9]
609                mAtms = ['','','','','']
610                pAtms = ['','','','','']
611                for i,atm in enumerate(Atms):
612                    if '+' in atm:
613                        pAtms[i] = atm.strip('+')
614                    elif '-' in atm:
615                        mAtms[i] = atm.strip('-')
616                ids = np.array([0,0,0,0,0])
617                chains = Chains.keys()
618                chains.sort()
619                for chain in chains:
620                    residues = Chains[chain].keys()
621                    residues.sort()
622                    if not (any(mAtms) or any(pAtms)):
623                        for residue in residues:
624                            for res,name,id in Chains[chain][residue]:
625                                if Res == res:
626                                    try:
627                                        ipos = Atms.index(name)
628                                        ids[ipos] = id
629                                    except ValueError:
630                                        continue
631                            if np.all(ids):
632                                rama = [list(ids),['1','1','1','1','1'],Name,Esd]
633                                if rama not in ramaRestData['Ramas']:
634                                    ramaRestData['Ramas'].append(rama)
635                                ids = np.array([0,0,0,0,0])
636                    else:
637                        for residue in residues[1:-1]:
638                            for res,name,id in Chains[chain][residue-1]:
639                                try:
640                                    ipos = mAtms.index(name)
641                                    ids[ipos] = id
642                                except ValueError:
643                                    continue
644                            for res,name,id in Chains[chain][residue+1]:
645                                try:
646                                    ipos = pAtms.index(name)
647                                    ids[ipos] = id
648                                except ValueError:
649                                    continue
650                            for res,name,id in Chains[chain][residue]:
651                                if Res == res:
652                                    try:
653                                        ipos = Atms.index(name)
654                                        ids[ipos] = id
655                                    except ValueError:
656                                        continue
657                            if np.all(ids):
658                                rama = [list(ids),['1','1','1','1','1'],Name,Esd]
659                                if rama not in ramaRestData['Ramas']:
660                                    ramaRestData['Ramas'].append(rama)
661                                ids = np.array([0,0,0,0,0])
662            macStr = macro.readline()
663        macro.close()
664        UpdateRamaRestr(ramaRestData)               
665               
666    def WtBox(wind,restData):
667        if 'Range' not in restData: restData['Range'] = 0.9     #patch
668       
669        def OnWtFactor(event):
670            try:
671                value = float(wtfactor.GetValue())
672            except ValueError:
673                value = 1.0
674            restData['wtFactor'] = value
675            wtfactor.SetValue('%.2f'%(value))
676           
677        def OnRange(event):
678            try:
679                value = float(sRange.GetValue())
680            except ValueError:
681                value = 1.0
682            restData['Range'] = value
683            sRange.SetValue('%.2f'%(value))
684           
685        def OnUseData(event):
686            restData['Use'] = Obj.GetValue()
687
688        wtBox = wx.BoxSizer(wx.HORIZONTAL)
689        wtBox.Add(wx.StaticText(wind,-1,'Restraint weight factor:'),0,wx.ALIGN_CENTER_VERTICAL)
690        wtfactor = wx.TextCtrl(wind,-1,value='%.2f'%(restData['wtFactor']),style=wx.TE_PROCESS_ENTER)
691        wtfactor.Bind(wx.EVT_TEXT_ENTER,OnWtFactor)
692        wtfactor.Bind(wx.EVT_KILL_FOCUS,OnWtFactor)
693        wtBox.Add(wtfactor,0,wx.ALIGN_CENTER_VERTICAL)
694        useData = wx.CheckBox(wind,-1,label=' Use?')
695        useData.Bind(wx.EVT_CHECKBOX, OnUseData)
696        useData.SetValue(restData['Use'])       
697        wtBox.Add(useData,0,wx.ALIGN_CENTER_VERTICAL)
698        if 'Bond' in restData or 'Angle' in restData:
699            wtBox.Add(wx.StaticText(wind,-1,'Search range:'),0,wx.ALIGN_CENTER_VERTICAL)
700            sRange = wx.TextCtrl(wind,-1,value='%.2f'%(restData['Range']),style=wx.TE_PROCESS_ENTER)
701            sRange.Bind(wx.EVT_TEXT_ENTER,OnRange)
702            sRange.Bind(wx.EVT_KILL_FOCUS,OnRange)
703            wtBox.Add(sRange,0,wx.ALIGN_CENTER_VERTICAL)
704        return wtBox
705       
706    def OnRowSelect(event):
707        r,c =  event.GetRow(),event.GetCol()
708        Obj = event.GetEventObject()
709        if r < 0 and c < 0:
710            if Obj.IsSelection():
711                Obj.ClearSelection()
712            else:
713                for row in range(Obj.GetNumberRows()):
714                    Obj.SelectRow(row,True)
715        elif c < 0:                   #only row clicks
716            if event.ControlDown():                   
717                if r in Obj.GetSelectedRows():
718                    Obj.DeselectRow(r)
719                else:
720                    Obj.SelectRow(r,True)
721            elif event.ShiftDown():
722                indxs = Obj.GetSelectedRows()
723                Obj.ClearSelection()
724                ibeg = 0
725                if indxs:
726                    ibeg = indxs[-1]
727                for row in range(ibeg,r+1):
728                    Obj.SelectRow(row,True)
729            else:
730                Obj.ClearSelection()
731                Obj.SelectRow(r,True)
732                   
733    def UpdateBondRestr(bondRestData):
734       
735        def OnChangeValue(event):
736            rows = Bonds.GetSelectedRows()
737            if not rows:
738                return
739            Bonds.ClearSelection()
740            val = bondList[rows[0]][3]
741            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new value for bond',val,[0.,5.],'%.4f')
742            if dlg.ShowModal() == wx.ID_OK:
743                parm = dlg.GetValue()
744                for r in rows:
745                    bondList[r][3] = parm
746            dlg.Destroy()
747            UpdateBondRestr(bondRestData)               
748
749        def OnChangeEsd(event):
750            rows = Bonds.GetSelectedRows()
751            if not rows:
752                return
753            Bonds.ClearSelection()
754            val = bondList[rows[0]][4]
755            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new esd for bond',val,[0.,1.],'%.4f')
756            if dlg.ShowModal() == wx.ID_OK:
757                parm = dlg.GetValue()
758                for r in rows:
759                    bondList[r][4] = parm
760            dlg.Destroy()
761            UpdateBondRestr(bondRestData)               
762                               
763        def OnDeleteRestraint(event):
764            rows = Bonds.GetSelectedRows()
765            if not rows:
766                return
767            Bonds.ClearSelection()
768            rows.sort()
769            rows.reverse()
770            for row in rows:
771                bondList.remove(bondList[row])
772            UpdateBondRestr(bondRestData)               
773           
774        BondRestr.DestroyChildren()
775        dataDisplay = wx.Panel(BondRestr)
776        mainSizer = wx.BoxSizer(wx.VERTICAL)
777        mainSizer.Add((5,5),0)
778        mainSizer.Add(WtBox(BondRestr,bondRestData),0,wx.ALIGN_CENTER_VERTICAL)
779
780        bondList = bondRestData['Bonds']
781        if len(bondList) and len(bondList[0]) == 6:   #patch
782            bondList = bondRestData['Bonds'] = []
783        if len(bondList):
784            table = []
785            rowLabels = []
786            Types = [wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,3',]
787            if 'macro' in General['Type']:
788                colLabels = ['(res) A - (res) B','calc','obs','esd']
789                for i,[indx,ops,obs,esd] in enumerate(bondList):
790                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
791                    name = ''
792                    for atom in atoms:
793                        name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
794                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
795                    calc = G2mth.getRestDist(XYZ,Amat)
796                    table.append([name[:-3],calc,obs,esd])
797                    rowLabels.append(str(i))               
798            else:
799                colLabels = ['A+SymOp - B+SymOp','calc','obs','esd']
800                for i,[indx,ops,obs,esd] in enumerate(bondList):
801                    names = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
802                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
803                    XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
804                    calc = G2mth.getRestDist(XYZ,Amat)
805                    table.append([names[0]+'+('+ops[0]+') - '+names[1]+'+('+ops[1]+')',calc,obs,esd])
806                    rowLabels.append(str(i))
807            bondTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
808            Bonds = G2gd.GSGrid(BondRestr)
809            Bonds.SetTable(bondTable, True)
810            Bonds.AutoSizeColumns(False)
811            for r in range(len(bondList)):
812                for c in range(2):
813                    Bonds.SetReadOnly(r,c,True)
814                    Bonds.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
815            Bonds.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
816            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
817            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeValue, id=G2gd.wxID_RESRCHANGEVAL)
818            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
819            mainSizer.Add(Bonds,0,)
820        else:
821            mainSizer.Add(wx.StaticText(BondRestr,-1,'No bond distance restraints for this phase'),0,)
822
823        BondRestr.SetSizer(mainSizer)
824        Size = mainSizer.Fit(G2frame.dataFrame)
825        Size[0] = 600
826        Size[1] += 50       #make room for tab
827        BondRestr.SetSize(Size)
828        BondRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
829        G2frame.dataFrame.SetSize(Size)
830       
831    def UpdateAngleRestr(angleRestData):
832       
833        def OnChangeValue(event):
834            rows = Angles.GetSelectedRows()
835            if not rows:
836                return
837            Angles.ClearSelection()
838            val = angleList[rows[0]][3]
839            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new value for angle',val,[0.,360.],'%.2f')
840            if dlg.ShowModal() == wx.ID_OK:
841                parm = dlg.GetValue()
842                for r in rows:
843                    angleList[r][3] = parm
844            dlg.Destroy()
845            UpdateAngleRestr(angleRestData)               
846
847        def OnChangeEsd(event):
848            rows = Angles.GetSelectedRows()
849            if not rows:
850                return
851            Angles.ClearSelection()
852            val = angleList[rows[0]][4]
853            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new esd for angle',val,[0.,5.],'%.2f')
854            if dlg.ShowModal() == wx.ID_OK:
855                parm = dlg.GetValue()
856                for r in rows:
857                    angleList[r][4] = parm
858            dlg.Destroy()
859            UpdateAngleRestr(angleRestData)               
860                                           
861        def OnDeleteRestraint(event):
862            rows = Angles.GetSelectedRows()
863            if not rows:
864                return
865            rows.sort()
866            rows.reverse()
867            for row in rows:
868                angleList.remove(angleList[row])
869            UpdateAngleRestr(angleRestData)               
870           
871        AngleRestr.DestroyChildren()
872        dataDisplay = wx.Panel(AngleRestr)
873        mainSizer = wx.BoxSizer(wx.VERTICAL)
874        mainSizer.Add((5,5),0)
875        mainSizer.Add(WtBox(AngleRestr,angleRestData),0,wx.ALIGN_CENTER_VERTICAL)
876
877        angleList = angleRestData['Angles']
878        if len(angleList):
879            table = []
880            rowLabels = []
881            Types = [wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,2',]
882            if 'macro' in General['Type']:
883                colLabels = ['(res) A - (res) B - (res) C','calc','obs','esd']
884                for i,[indx,ops,obs,esd] in enumerate(angleList):
885                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
886                    name = ''
887                    for atom in atoms:
888                        name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
889                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
890                    calc = G2mth.getRestAngle(XYZ,Amat)
891                    table.append([name[:-3],calc,obs,esd])
892                    rowLabels.append(str(i))                               
893            else:
894                colLabels = ['A+SymOp - B+SymOp - C+SymOp','calc','obs','esd']
895                for i,[indx,ops,obs,esd] in enumerate(angleList):
896                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
897                    name = atoms[0]+'+('+ops[0]+') - '+atoms[1]+'+('+ops[1]+') - '+atoms[2]+ \
898                    '+('+ops[2]+')'
899                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
900                    XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
901                    calc = G2mth.getRestAngle(XYZ,Amat)
902                    table.append([name,calc,obs,esd])
903                    rowLabels.append(str(i))
904            angleTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
905            Angles = G2gd.GSGrid(AngleRestr)
906            Angles.SetTable(angleTable, True)
907            Angles.AutoSizeColumns(False)
908            for r in range(len(angleList)):
909                for c in range(2):
910                    Angles.SetReadOnly(r,c,True)
911                    Angles.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
912            Angles.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
913            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
914            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeValue, id=G2gd.wxID_RESRCHANGEVAL)
915            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
916            mainSizer.Add(Angles,0,)
917        else:
918            mainSizer.Add(wx.StaticText(AngleRestr,-1,'No bond angle restraints for this phase'),0,)
919
920        AngleRestr.SetSizer(mainSizer)
921        Size = mainSizer.Fit(G2frame.dataFrame)
922        Size[0] = 600
923        Size[1] += 50      #make room for tab
924        AngleRestr.SetSize(Size)
925        AngleRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
926        G2frame.dataFrame.SetSize(Size)
927#        G2frame.dataFrame.setSizePosLeft(Size)
928   
929    def UpdatePlaneRestr(planeRestData):
930       
931        items = G2frame.dataFrame.RestraintEdit.GetMenuItems()
932        for item in items:
933            if item.GetLabel() in ['Change value']:
934                item.Enable(False)
935
936        def OnChangeEsd(event):
937            rows = Planes.GetSelectedRows()
938            if not rows:
939                return
940            Planes.ClearSelection()
941            val = planeList[rows[0]][4]
942            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new esd for plane',val,[0.,5.],'%.2f')
943            if dlg.ShowModal() == wx.ID_OK:
944                parm = dlg.GetValue()
945                for r in rows:
946                    planeList[r][4] = parm
947            dlg.Destroy()
948            UpdatePlaneRestr(planeRestData)               
949                                           
950        def OnDeleteRestraint(event):
951            rows = Planes.GetSelectedRows()
952            if not rows:
953                return
954            rows.sort()
955            rows.reverse()
956            for row in rows:
957                planeList.remove(planeList[row])
958            UpdatePlaneRestr(planeRestData)               
959           
960        PlaneRestr.DestroyChildren()
961        dataDisplay = wx.Panel(PlaneRestr)
962        mainSizer = wx.BoxSizer(wx.VERTICAL)
963        mainSizer.Add((5,5),0)
964        mainSizer.Add(WtBox(PlaneRestr,planeRestData),0,wx.ALIGN_CENTER_VERTICAL)
965
966        planeList = planeRestData['Planes']
967        if len(planeList):
968            table = []
969            rowLabels = []
970            Types = [wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,2',]
971            if 'macro' in General['Type']:
972                colLabels = ['(res) atom','calc','obs','esd']
973                for i,[indx,ops,obs,esd] in enumerate(planeList):
974                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
975                    name = ''
976                    for a,atom in enumerate(atoms):
977                        name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' - '
978                        if (a+1)%3 == 0:
979                            name += '\n'
980                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
981                    calc = G2mth.getRestPlane(XYZ,Amat)
982                    table.append([name[:-3],calc,obs,esd])
983                    rowLabels.append(str(i))
984            else:                               
985                colLabels = ['atom+SymOp','calc','obs','esd']
986                for i,[indx,ops,obs,esd] in enumerate(planeList):
987                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
988                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
989                    XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
990                    calc = G2mth.getRestPlane(XYZ,Amat)
991                    name = ''
992                    for a,atom in enumerate(atoms):
993                        name += atom+'+ ('+ops[a]+'),'
994                        if (a+1)%3 == 0:
995                            name += '\n'
996                    table.append([name[:-1],calc,obs,esd])
997                    rowLabels.append(str(i))
998            planeTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
999            Planes = G2gd.GSGrid(PlaneRestr)
1000            Planes.SetTable(planeTable, True)
1001            Planes.AutoSizeColumns(False)
1002            Planes.AutoSizeRows(False)
1003            for r in range(len(planeList)):
1004                for c in range(3):
1005                    Planes.SetReadOnly(r,c,True)
1006                    Planes.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1007            Planes.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1008            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1009            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1010            mainSizer.Add(Planes,0,)
1011        else:
1012            mainSizer.Add(wx.StaticText(PlaneRestr,-1,'No plane restraints for this phase'),0,)
1013
1014        PlaneRestr.SetSizer(mainSizer)
1015        Size = mainSizer.Fit(G2frame.dataFrame)
1016        Size[0] = 600
1017        Size[1] += 50       #make room for tab
1018        PlaneRestr.SetSize(Size)
1019        PlaneRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1020        G2frame.dataFrame.SetSize(Size)
1021#        G2frame.dataFrame.setSizePosLeft(Size)
1022   
1023    def UpdateChiralRestr(chiralRestData):
1024
1025        def OnDeleteRestraint(event):
1026            rows = Volumes.GetSelectedRows()
1027            if not rows:
1028                return
1029            rows.sort()
1030            rows.reverse()
1031            for row in rows:
1032                volumeList.remove(volumeList[row])
1033            UpdateChiralRestr(chiralRestData)               
1034           
1035        def OnChangeValue(event):
1036            rows = Volumes.GetSelectedRows()
1037            if not rows:
1038                return
1039            Volumes.ClearSelection()
1040            val = volumeList[rows[0]][3]
1041            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new value for chiral volume',val,[0.,360.],'%.2f')
1042            if dlg.ShowModal() == wx.ID_OK:
1043                parm = dlg.GetValue()
1044                for r in rows:
1045                    volumeList[r][3] = parm
1046            dlg.Destroy()
1047            UpdateChiralRestr(chiralRestData)               
1048
1049        def OnChangeEsd(event):
1050            rows = Volumes.GetSelectedRows()
1051            if not rows:
1052                return
1053            Volumes.ClearSelection()
1054            val = volumeList[rows[0]][4]
1055            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new esd for chiral volume',val,[0.,5.],'%.2f')
1056            if dlg.ShowModal() == wx.ID_OK:
1057                parm = dlg.GetValue()
1058                for r in rows:
1059                    volumeList[r][4] = parm
1060            dlg.Destroy()
1061            UpdateChiralRestr(chiralRestData)               
1062                                           
1063        ChiralRestr.DestroyChildren()
1064        dataDisplay = wx.Panel(ChiralRestr)
1065        mainSizer = wx.BoxSizer(wx.VERTICAL)
1066        mainSizer.Add((5,5),0)
1067        mainSizer.Add(WtBox(ChiralRestr,chiralRestData),0,wx.ALIGN_CENTER_VERTICAL)
1068
1069        volumeList = chiralRestData['Volumes']
1070        if len(volumeList):
1071            table = []
1072            rowLabels = []
1073            Types = [wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,2',]
1074            if 'macro' in General['Type']:
1075                colLabels = ['(res) O (res) A (res) B (res) C','calc','obs','esd']
1076                for i,[indx,ops,obs,esd] in enumerate(volumeList):
1077                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1078                    name = ''
1079                    for atom in atoms:
1080                        name += '('+atom[1]+atom[0].strip()+atom[2]+') '+atom[3]+' '
1081                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1082                    calc = G2mth.getRestChiral(XYZ,Amat)
1083                    table.append([name,calc,obs,esd])
1084                    rowLabels.append(str(i))
1085            else:
1086                colLabels = ['O+SymOp  A+SymOp  B+SymOp  C+SymOp)','calc','obs','esd']
1087                for i,[indx,ops,obs,esd] in enumerate(volumeList):
1088                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,ct-1)
1089                    name = atoms[0]+'+('+ops[0]+') '+atoms[1]+'+('+ops[1]+') '+atoms[2]+ \
1090                        '+('+ops[2]+') '+atoms[3]+'+('+ops[3]+')'
1091                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1092                    XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1093                    calc = G2mth.getRestChiral(XYZ,Amat)
1094                    table.append([name,calc,obs,esd])
1095                    rowLabels.append(str(i))
1096            volumeTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1097            Volumes = G2gd.GSGrid(ChiralRestr)
1098            Volumes.SetTable(volumeTable, True)
1099            Volumes.AutoSizeColumns(False)
1100            for r in range(len(volumeList)):
1101                for c in range(2):
1102                    Volumes.SetReadOnly(r,c,True)
1103                    Volumes.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1104            Volumes.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1105            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1106            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeValue, id=G2gd.wxID_RESRCHANGEVAL)
1107            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1108            mainSizer.Add(Volumes,0,)
1109        else:
1110            mainSizer.Add(wx.StaticText(ChiralRestr,-1,'No chiral volume restraints for this phase'),0,)
1111
1112        ChiralRestr.SetSizer(mainSizer)
1113        Size = mainSizer.Fit(G2frame.dataFrame)
1114        Size[0] = 600
1115        Size[1] += 50       #make room for tab
1116        ChiralRestr.SetSize(Size)
1117        ChiralRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1118        G2frame.dataFrame.SetSize(Size)
1119#        G2frame.dataFrame.setSizePosLeft(Size)
1120   
1121    def UpdateTorsionRestr(torsionRestData):
1122
1123        def OnDeleteRestraint(event):
1124            rows = Torsions.GetSelectedRows()
1125            if not rows:
1126                return
1127            rows.sort()
1128            rows.reverse()
1129            for row in rows:
1130                torsionList.remove(torsionList[row])
1131            UpdateTorsionRestr(torsionRestData)               
1132           
1133        def OnChangeEsd(event):
1134            rows = Torsions.GetSelectedRows()
1135            if not rows:
1136                return
1137            Torsions.ClearSelection()
1138            val = torsionList[rows[0]][4]
1139            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new esd for torsion restraints',val,[0.,5.],'%.2f')
1140            if dlg.ShowModal() == wx.ID_OK:
1141                parm = dlg.GetValue()
1142                for r in rows:
1143                    volumeList[r][4] = parm
1144            dlg.Destroy()
1145            UpdateTorsionRestr(torsionRestData)               
1146                                           
1147        TorsionRestr.DestroyChildren()
1148        dataDisplay = wx.Panel(TorsionRestr)
1149        mainSizer = wx.BoxSizer(wx.VERTICAL)
1150        mainSizer.Add((5,5),0)
1151        mainSizer.Add(WtBox(TorsionRestr,torsionRestData),0,wx.ALIGN_CENTER_VERTICAL)
1152       
1153        coeffDict = torsionRestData['Coeff']
1154        torsionList = torsionRestData['Torsions']
1155        mainSizer.Add(wx.StaticText(TorsionRestr,-1,'Torsion restraints:'),0,wx.ALIGN_CENTER_VERTICAL)
1156        if len(torsionList):
1157            table = []
1158            rowLabels = []
1159            Types = 2*[wg.GRID_VALUE_STRING,]+4*[wg.GRID_VALUE_FLOAT+':10,2',]
1160            if 'macro' in General['Type']:
1161                colLabels = ['(res) A  B  C  D','coef name','torsion','obs E','restr','esd']
1162                for i,[indx,ops,cofName,esd] in enumerate(torsionList):
1163                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1164                    name = '('+atoms[2][1]+atoms[2][0].strip()+atoms[2][2]+')'
1165                    for atom in atoms:
1166                        name += '  '+atom[3]
1167                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1168                    tor = G2mth.getRestTorsion(XYZ,Amat)
1169                    restr,calc = G2mth.calcTorsionEnergy(tor,coeffDict[cofName])
1170                    table.append([name,cofName,tor,calc,restr,esd])
1171                    rowLabels.append(str(i))
1172            torsionTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1173            Torsions = G2gd.GSGrid(TorsionRestr)
1174            Torsions.SetTable(torsionTable, True)
1175            Torsions.AutoSizeColumns(False)
1176            for r in range(len(torsionList)):
1177                for c in range(2):
1178                    Torsions.SetReadOnly(r,c,True)
1179                    Torsions.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1180            Torsions.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1181            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1182            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1183            mainSizer.Add(Torsions,0,)
1184           
1185            mainSizer.Add((5,5))
1186            mainSizer.Add(wx.StaticText(TorsionRestr,-1,'Torsion function coefficients:'),0,wx.ALIGN_CENTER_VERTICAL)
1187            table = []
1188            rowLabels = []
1189            Types = 9*[wg.GRID_VALUE_FLOAT+':10,4',]
1190            colLabels = ['Mag A','Pos A','Width A','Mag B','Pos B','Width B','Mag C','Pos C','Width C']
1191            for item in coeffDict:
1192                rowLabels.append(item)
1193                table.append(coeffDict[item])
1194            coeffTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1195            Coeff = G2gd.GSGrid(TorsionRestr)
1196            Coeff.SetTable(coeffTable, True)
1197            Coeff.AutoSizeColumns(False)
1198            for r in range(len(coeffDict)):
1199                for c in range(9):
1200                    Coeff.SetReadOnly(r,c,True)
1201                    Coeff.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1202            mainSizer.Add(Coeff,0,)
1203        else:
1204            mainSizer.Add(wx.StaticText(TorsionRestr,-1,'No torsion restraints for this phase'),0,)
1205
1206        TorsionRestr.SetSizer(mainSizer)
1207        Size = mainSizer.Fit(G2frame.dataFrame)
1208        Size[0] = 600
1209        Size[1] += 50       #make room for tab
1210        TorsionRestr.SetSize(Size)
1211        TorsionRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1212        G2frame.dataFrame.SetSize(Size)
1213#        G2frame.dataFrame.setSizePosLeft(Size)
1214
1215    def UpdateRamaRestr(ramaRestData):
1216
1217        def OnDeleteRestraint(event):
1218            rows = Ramas.GetSelectedRows()
1219            if not rows:
1220                return
1221            rows.sort()
1222            rows.reverse()
1223            for row in rows:
1224                ramaList.remove(ramaList[row])
1225            UpdateRamaRestr(ramaRestData)               
1226           
1227        def OnChangeEsd(event):
1228            rows = Ramas.GetSelectedRows()
1229            if not rows:
1230                return
1231            Ramas.ClearSelection()
1232            val = ramaList[rows[0]][5]
1233            dlg = G2phG.SingleFloatDialog(G2frame,'New value','Enter new esd for energy',val,[0.,5.],'%.2f')
1234            if dlg.ShowModal() == wx.ID_OK:
1235                parm = dlg.GetValue()
1236                for r in rows:
1237                    ramaList[r][4] = parm
1238            dlg.Destroy()
1239            UpdateRamaRestr(ramaRestData)               
1240                                           
1241        RamaRestr.DestroyChildren()
1242        dataDisplay = wx.Panel(RamaRestr)
1243        mainSizer = wx.BoxSizer(wx.VERTICAL)
1244        mainSizer.Add((5,5),0)
1245        mainSizer.Add(WtBox(RamaRestr,ramaRestData),0,wx.ALIGN_CENTER_VERTICAL)
1246
1247        ramaList = ramaRestData['Ramas']
1248        coeffDict = ramaRestData['Coeff']
1249        if len(ramaList):
1250            mainSizer.Add(wx.StaticText(RamaRestr,-1,'Ramachandran restraints:'),0,wx.ALIGN_CENTER_VERTICAL)
1251            table = []
1252            rowLabels = []
1253            Types = 2*[wg.GRID_VALUE_STRING,]+5*[wg.GRID_VALUE_FLOAT+':10,2',]
1254            if 'macro' in General['Type']:
1255                colLabels = ['(res) A  B  C  D  E','coef name','phi','psi','obs E','restr','esd']
1256                for i,[indx,ops,cofName,esd] in enumerate(ramaList):
1257                    atoms = G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,0,4)
1258                    name = '('+atoms[3][1]+atoms[3][0].strip()+atoms[3][2]+')'
1259                    for atom in atoms:
1260                        name += '  '+atom[3]
1261                    XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookUp,indx,cx,3))
1262                    phi,psi = G2mth.getRestRama(XYZ,Amat)
1263                    restr,calc = G2mth.calcRamaEnergy(phi,psi,coeffDict[cofName])
1264                    table.append([name,cofName,phi,psi,calc,restr,esd])
1265                    rowLabels.append(str(i))
1266            ramaTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1267            Ramas = G2gd.GSGrid(RamaRestr)
1268            Ramas.SetTable(ramaTable, True)
1269            Ramas.AutoSizeColumns(False)
1270            for r in range(len(ramaList)):
1271                for c in range(2):
1272                    Ramas.SetReadOnly(r,c,True)
1273                    Ramas.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1274            Ramas.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK,OnRowSelect)
1275            G2frame.dataFrame.Bind(wx.EVT_MENU, OnDeleteRestraint, id=G2gd.wxID_RESTDELETE)
1276            G2frame.dataFrame.Bind(wx.EVT_MENU, OnChangeEsd, id=G2gd.wxID_RESTCHANGEESD)
1277            mainSizer.Add(Ramas,0,)
1278        else:
1279            mainSizer.Add(wx.StaticText(RamaRestr,-1,'No Ramachandran restraints for this phase'),0,)
1280        mainSizer.Add((5,5))
1281        mainSizer.Add(wx.StaticText(RamaRestr,-1,'Ramachandran function coefficients:'),0,wx.ALIGN_CENTER_VERTICAL)
1282        if len(coeffDict):
1283            table = []
1284            rowLabels = []
1285            Types = 6*[wg.GRID_VALUE_FLOAT+':10,4',]
1286            colLabels = ['Mag','Pos phi','Pos psi','sig(phi)','sig(psi)','sig(cov)']
1287            for item in coeffDict:
1288                for i,term in enumerate(coeffDict[item]):
1289                    rowLabels.append(item+' term:'+str(i))
1290                    table.append(term)
1291            coeffTable = G2gd.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1292            Coeff = G2gd.GSGrid(RamaRestr)
1293            Coeff.SetTable(coeffTable, True)
1294            Coeff.AutoSizeColumns(False)
1295            for r in range(Coeff.GetNumberRows()):
1296                for c in range(6):
1297                    Coeff.SetReadOnly(r,c,True)
1298                    Coeff.SetCellStyle(r,c,VERY_LIGHT_GREY,True)
1299            mainSizer.Add(Coeff,0,)
1300
1301        RamaRestr.SetSizer(mainSizer)
1302        Size = mainSizer.Fit(G2frame.dataFrame)
1303        Size[0] = 600
1304        Size[1] += 50       #make room for tab
1305        RamaRestr.SetSize(Size)
1306        RamaRestr.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1307        G2frame.dataFrame.SetSize(Size)
1308#        G2frame.dataFrame.setSizePosLeft(Size)
1309
1310    def OnPageChanged(event):
1311        page = event.GetSelection()
1312        text = G2frame.dataDisplay.GetPageText(page)
1313        if text == 'Bond restraints':
1314            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1315            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1316            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,True)
1317            bondRestData = restrData['Bond']
1318            UpdateBondRestr(bondRestData)
1319        elif text == 'Angle restraints':
1320            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1321            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1322            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,True)
1323            angleRestData = restrData['Angle']
1324            UpdateAngleRestr(angleRestData)
1325        elif text == 'Plane restraints':
1326            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1327            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1328            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,False)
1329            planeRestData = restrData['Plane']
1330            UpdatePlaneRestr(planeRestData)
1331        elif text == 'Chiral restraints':
1332            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1333            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,True)
1334            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,True)
1335            chiralRestData = restrData['Chiral']
1336            UpdateChiralRestr(chiralRestData)
1337        elif text == 'Torsion restraints':
1338            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1339            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,False)
1340            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,False)
1341            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_AARESTRAINTPLOT,True)
1342            torsionRestData = restrData['Torsion']
1343            UpdateTorsionRestr(torsionRestData)
1344        elif text == 'Ramachandran restraints':
1345            G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1346            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTRAINTADD,False)
1347            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESRCHANGEVAL,False)
1348            G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_AARESTRAINTPLOT,True)
1349            ramaRestData = restrData['Rama']
1350            UpdateRamaRestr(ramaRestData)
1351            G2plt.PlotRama(G2frame,phaseName,rama,ramaName)
1352        event.Skip()
1353
1354    def SetStatusLine(text):
1355        Status.SetStatusText(text)                                     
1356       
1357    if G2frame.dataDisplay:
1358        G2frame.dataDisplay.Destroy()
1359       
1360    G2gd.SetDataMenuBar(G2frame,G2frame.dataFrame.RestraintMenu)
1361    G2frame.dataFrame.SetLabel('restraints for '+phaseName)
1362    if not G2frame.dataFrame.GetStatusBar():
1363        Status = G2frame.dataFrame.CreateStatusBar()
1364    SetStatusLine('')
1365   
1366    G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTSELPHASE,False)
1367    if len(Phases) > 1:
1368        G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_RESTSELPHASE,True)
1369        G2frame.dataFrame.Bind(wx.EVT_MENU, OnSelectPhase, id=G2gd.wxID_RESTSELPHASE)
1370    G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddRestraint, id=G2gd.wxID_RESTRAINTADD)
1371    if 'macro' in phasedata['General']['Type']:
1372        G2frame.dataFrame.RestraintEdit.Enable(G2gd.wxID_AARESTRAINTADD,True)
1373        G2frame.dataFrame.Bind(wx.EVT_MENU, OnAddAARestraint, id=G2gd.wxID_AARESTRAINTADD)
1374        G2frame.dataFrame.Bind(wx.EVT_MENU, OnPlotAARestraint, id=G2gd.wxID_AARESTRAINTPLOT)
1375    G2frame.dataDisplay = G2gd.GSNoteBook(parent=G2frame.dataFrame,size=G2frame.dataFrame.GetClientSize())
1376   
1377    BondRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1378    G2frame.dataDisplay.AddPage(BondRestr,'Bond restraints')
1379    AngleRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1380    G2frame.dataDisplay.AddPage(AngleRestr,'Angle restraints')
1381    PlaneRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1382    G2frame.dataDisplay.AddPage(PlaneRestr,'Plane restraints')
1383    ChiralRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1384    G2frame.dataDisplay.AddPage(ChiralRestr,'Chiral restraints')
1385    if 'macro' in General['Type']:
1386        TorsionRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1387        G2frame.dataDisplay.AddPage(TorsionRestr,'Torsion restraints')
1388        RamaRestr = wx.ScrolledWindow(G2frame.dataDisplay)
1389        G2frame.dataDisplay.AddPage(RamaRestr,'Ramachandran restraints')
1390   
1391    UpdateBondRestr(restrData['Bond'])
1392
1393    G2frame.dataDisplay.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, OnPageChanged)
Note: See TracBrowser for help on using the repository browser.