source: trunk/GSASIIrestrGUI.py @ 3802

Last change on this file since 3802 was 3802, checked in by toby, 3 years ago

generalized restraints completed

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