source: trunk/GSASIIrestrGUI.py @ 1077

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

cleanup plot & svn bugs; set missing keywords; CIF export done; update docs

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