source: trunk/GSASIIrestrGUI.py @ 906

Last change on this file since 906 was 906, checked in by toby, 10 years ago

switch to aui.notebook for data display window; work on phase data display window - quicker initial draw, fix horizontal lines (needs more work); more sphinx documentation

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