source: trunk/GSASIIrestrGUI.py @ 3780

Last change on this file since 3780 was 3780, checked in by toby, 4 years ago

fix typos; error in CIF Dij reporting; improve generalized restraint GUI

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 100.7 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIrestr - restraint GUI routines
3########### SVN repository information ###################
4# $Date: 2019-01-11 19:51:15 +0000 (Fri, 11 Jan 2019) $
5# $Author: toby $
6# $Revision: 3780 $
7# $URL: trunk/GSASIIrestrGUI.py $
8# $Id: GSASIIrestrGUI.py 3780 2019-01-11 19:51:15Z 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: 3780 $")
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,'(not implemented yet)'))
1882        hSizer.Add((5,5),0)
1883        hSizer.Add(wx.StaticText(GeneralRestr,wx.ID_ANY,'Weight factor: '))
1884        hSizer.Add(
1885                    G2G.ValidatedTxtCtrl(GeneralRestr,generalRestData,
1886                                'wtFactor',nDig=(10,1),typeHint=float)
1887                    )
1888        btn = G2G.G2CheckBox(GeneralRestr,'Use?',generalRestData,'Use')
1889        hSizer.Add(btn)
1890        hSizer.Add((5,5),0)
1891        btn = wx.Button(GeneralRestr, wx.ID_ANY,"Add restraint")
1892        btn.Bind(wx.EVT_BUTTON,OnAddRestraint)
1893        hSizer.Add(btn,0,wx.ALIGN_CENTER|wx.EXPAND|wx.ALL)
1894        mainSizer.Add(hSizer,0)
1895        mainSizer.Add((5,5),0)
1896        if generalRestData['General']:
1897            parmDict = SetupParmDict(G2frame)
1898            GridSiz = wx.FlexGridSizer(0,7,10,2)
1899            for lbl in ('expression','target\nvalue','current\nvalue','esd'):
1900                GridSiz.Add(
1901                    wx.StaticText(GeneralRestr,wx.ID_ANY,lbl,style=wx.CENTER),
1902                    0,wx.ALIGN_CENTER)
1903            GridSiz.Add((-1,-1))
1904            GridSiz.Add((-1,-1))
1905            GridSiz.Add(
1906                    wx.StaticText(GeneralRestr,wx.ID_ANY,'Variables',style=wx.CENTER),
1907                    0,wx.ALIGN_CENTER)
1908            for i,rest in enumerate(generalRestData['General']):
1909                txt = rest[0].expression
1910                if len(txt) > 50:
1911                    txt = txt[:47]+'... '
1912                GridSiz.Add(wx.StaticText(GeneralRestr,wx.ID_ANY,txt))
1913                GridSiz.Add(
1914                    G2G.ValidatedTxtCtrl(GeneralRestr,rest,1,nDig=(10,2),typeHint=float)
1915                    )
1916                # evaluate the expression
1917                try:
1918                    calcobj = G2obj.ExpressionCalcObj(rest[0])
1919                    calcobj.SetupCalc(parmDict)
1920                    txt = '{:f}'.format(calcobj.EvalExpression())
1921                except:
1922                    txt = '(error)'
1923                GridSiz.Add(wx.StaticText(GeneralRestr,wx.ID_ANY,txt))
1924                GridSiz.Add(
1925                    G2G.ValidatedTxtCtrl(GeneralRestr,rest,2,nDig=(10,1),typeHint=float)
1926                    )
1927                btn = wx.Button(GeneralRestr, wx.ID_ANY,"Edit",size=(40,-1))
1928                btn.index = i
1929                btn.Bind(wx.EVT_BUTTON,OnEditGenRestraint)
1930                GridSiz.Add(btn)
1931                btn = wx.Button(GeneralRestr, wx.ID_ANY,"Delete",size=(60,-1))
1932                btn.index = i
1933                btn.Bind(wx.EVT_BUTTON,OnDelGenRestraint)
1934                GridSiz.Add(btn)
1935                txt = str(rest[0].assgnVars)[1:-1].replace("'","")
1936                if len(txt) > 50:
1937                    txt = txt[:47]+'...'
1938                GridSiz.Add(wx.StaticText(GeneralRestr,wx.ID_ANY,txt))
1939            mainSizer.Add(GridSiz)
1940        G2phsGUI.SetPhaseWindow(GeneralRestr,mainSizer,Scroll=0)
1941
1942   
1943    def OnPageChanged(event):
1944        page = event.GetSelection()
1945        #G2frame.restrBook.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1946        text = G2frame.restrBook.GetPageText(page)
1947        G2frame.dataWindow.RestraintEdit.SetLabel(G2G.wxID_RESRCHANGEVAL,'Change value')
1948        G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_USEMOGUL,False)
1949        if text == 'Bond':
1950            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1951            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1952            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1953            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_USEMOGUL,True)
1954            bondRestData = restrData['Bond']
1955            UpdateBondRestr(bondRestData)
1956        elif text == 'Angle':
1957            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1958            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1959            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1960            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_USEMOGUL,True)
1961            angleRestData = restrData['Angle']
1962            UpdateAngleRestr(angleRestData)
1963        elif text == 'Plane':
1964            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1965            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1966            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,False)
1967            planeRestData = restrData['Plane']
1968            UpdatePlaneRestr(planeRestData)
1969        elif text == 'Chiral':
1970            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1971            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,False)
1972            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1973            chiralRestData = restrData['Chiral']
1974            UpdateChiralRestr(chiralRestData)
1975        elif text == 'Torsion':
1976            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1977            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,False)
1978            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,False)
1979            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_AARESTRAINTPLOT,True)
1980            torsionRestData = restrData['Torsion']
1981            UpdateTorsionRestr(torsionRestData)
1982        elif text == 'Ramachandran':
1983            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1984            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,False)
1985            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,False)
1986            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_AARESTRAINTPLOT,True)
1987            ramaRestData = restrData['Rama']
1988            UpdateRamaRestr(ramaRestData)
1989            wx.CallAfter(G2plt.PlotRama,G2frame,phaseName,rama,ramaName)
1990        elif text == 'Chem. comp.':
1991            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
1992            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
1993            G2frame.dataWindow.RestraintEdit.SetLabel(G2G.wxID_RESRCHANGEVAL,'Change factor')
1994            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
1995            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTCHANGEESD,False)
1996            chemcompRestData = restrData['ChemComp']
1997            UpdateChemcompRestr(chemcompRestData)
1998        elif text == 'Texture':
1999            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
2000            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
2001            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,True)
2002            textureRestData = restrData['Texture']
2003            UpdateTextureRestr(textureRestData)
2004        elif text == 'General':
2005            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)
2006            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTRAINTADD,True)
2007            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESRCHANGEVAL,False)
2008            G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_RESTCHANGEESD,False)
2009            UpdateGeneralRestr(restrData['General'])
2010        event.Skip()
2011
2012#    def RaisePage(event):
2013#        'Respond to a "select tab" menu button'
2014#        # class PseudoEvent(object):
2015#        #     def __init__(self,page): self.page = page
2016#        #     def Skip(self): pass
2017#        #     def GetSelection(self): return self.page
2018#        try:
2019#            i = tabIndex.get(event.GetId())
2020#            G2frame.restrBook.SetSelection(i)
2021#            #OnPageChanged(PseudoEvent(i))
2022#        except ValueError:
2023#            print('Unexpected event in RaisePage')
2024#
2025    def OnSelectPage(event):
2026        'Called when an item is selected from the Select page menu'
2027        # lookup the menu item that called us and get its text
2028        tabname = TabSelectionIdDict.get(event.GetId())
2029        if not tabname:
2030            print ('Warning: menu item not in dict! id= %d'%event.GetId())
2031            return
2032        # find the matching tab
2033        for PageNum in range(G2frame.restrBook.GetPageCount()):
2034            if tabname == G2frame.restrBook.GetPageText(PageNum):
2035                G2frame.restrBook.SetSelection(PageNum)
2036                return
2037        else:
2038            print ("Warning: tab "+tabname+" was not found")
2039
2040    # UpdateRestraints execution starts here
2041    try:
2042        phasedata = G2frame.GetPhaseData()[phaseName]
2043    except KeyError:        #delete unknown or previously deleted phases from Restraints
2044        rId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Restraints')
2045        pId = G2gd.GetGPXtreeItemId(G2frame,rId,phaseName)
2046        G2frame.GPXtree.Delete(pId)
2047        print('Unknown phase '+phaseName+' is deleted from Restraints')
2048        return
2049    restrData = data[phaseName]
2050    if 'Bond' not in restrData:
2051        restrData['Bond'] = {'wtFactor':1.0,'Range':1.1,'Bonds':[],'Use':True}
2052    if 'Angle' not in restrData:
2053        restrData['Angle'] = {'wtFactor':1.0,'Range':0.85,'Angles':[],'Use':True}
2054    if 'Plane' not in restrData:
2055        restrData['Plane'] = {'wtFactor':1.0,'Planes':[],'Use':True}
2056    if 'Chiral' not in restrData:
2057        restrData['Chiral'] = {'wtFactor':1.0,'Volumes':[],'Use':True}
2058    if 'Torsion' not in restrData:
2059        restrData['Torsion'] = {'wtFactor':1.0,'Coeff':{},'Torsions':[],'Use':True}
2060    if 'Rama' not in restrData:
2061        restrData['Rama'] = {'wtFactor':1.0,'Coeff':{},'Ramas':[],'Use':True}
2062    if 'Texture' not in restrData:
2063        restrData['Texture'] = {'wtFactor':1.0,'HKLs':[],'Use':True}
2064    if 'ChemComp' not in restrData:
2065        restrData['ChemComp'] = {'wtFactor':1.0,'Sites':[],'Use':True}
2066    if 'General' not in restrData:
2067        restrData['General'] = {'wtFactor':1.0,'General':[], 'Use':True}
2068    General = phasedata['General']
2069    Cell = General['Cell'][1:7]          #skip flag & volume   
2070    Amat,Bmat = G2lat.cell2AB(Cell)
2071    SGData = General['SGData']
2072    cx,ct,cs,cia = General['AtomPtrs']
2073    Atoms = phasedata['Atoms']
2074    AtLookUp = G2mth.FillAtomLookUp(Atoms,cia+8)
2075    if 'macro' in General['Type']:
2076        Names = [atom[0]+':'+atom[1]+atom[2]+' '+atom[3].ljust(4) for atom in Atoms]
2077        Ids = []
2078        Coords = []
2079        Types = []
2080    else:   
2081        Names = ['all '+ name for name in General['AtomTypes']]
2082        iBeg = len(Names)
2083        Types = [name for name in General['AtomTypes']]
2084        Coords = [ [] for type in Types]
2085        Ids = [ 0 for type in Types]
2086        Names += [atom[ct-1] for atom in Atoms]
2087    Types += [atom[ct] for atom in Atoms]
2088    Coords += [atom[cx:cx+3] for atom in Atoms]
2089    Ids += [atom[cia+8] for atom in Atoms]
2090    rama = G2data.ramachandranDist['All']
2091    ramaName = 'All'
2092    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RestraintMenu)   
2093    G2frame.Bind(wx.EVT_MENU, OnAddRestraint, id=G2G.wxID_RESTRAINTADD)
2094    G2frame.Bind(wx.EVT_MENU, OnUseMogul, id=G2G.wxID_USEMOGUL)
2095    G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_USEMOGUL,True)
2096    if 'macro' in phasedata['General']['Type']:
2097        G2frame.dataWindow.RestraintEdit.Enable(G2G.wxID_AARESTRAINTADD,True)
2098        G2frame.Bind(wx.EVT_MENU, OnAddAARestraint, id=G2G.wxID_AARESTRAINTADD)
2099        G2frame.Bind(wx.EVT_MENU, OnPlotAARestraint, id=G2G.wxID_AARESTRAINTPLOT)
2100   
2101    # GUI defined here
2102    G2frame.restrBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
2103    G2frame.dataWindow.GetSizer().Add(G2frame.restrBook,1,wx.ALL|wx.EXPAND,1)
2104    # clear menu and menu pointers
2105    Pages = []   
2106
2107    txt = 'Bond'
2108    BondRestr = wx.ScrolledWindow(G2frame.restrBook)
2109    G2frame.restrBook.AddPage(BondRestr,txt)
2110    Pages.append(txt)
2111
2112    txt = 'Angle'
2113    AngleRestr = wx.ScrolledWindow(G2frame.restrBook)
2114    G2frame.restrBook.AddPage(AngleRestr,txt) 
2115    Pages.append(txt)
2116   
2117    txt = 'Plane'
2118    PlaneRestr = wx.ScrolledWindow(G2frame.restrBook)
2119    G2frame.restrBook.AddPage(PlaneRestr,txt)
2120    Pages.append(txt)
2121
2122    txt = 'Chiral'
2123    ChiralRestr = wx.ScrolledWindow(G2frame.restrBook)
2124    G2frame.restrBook.AddPage(ChiralRestr,txt)
2125    Pages.append(txt)
2126
2127    if 'macro' in General['Type']:
2128        txt = 'Torsion'
2129        TorsionRestr = wx.ScrolledWindow(G2frame.restrBook)
2130        G2frame.restrBook.AddPage(TorsionRestr,txt)
2131        Pages.append(txt)
2132
2133        txt = 'Ramachandran'
2134        RamaRestr = wx.ScrolledWindow(G2frame.restrBook)
2135        G2frame.restrBook.AddPage(RamaRestr,txt)
2136        Pages.append(txt)
2137
2138    txt = 'Chem. comp.'
2139    ChemCompRestr = wx.ScrolledWindow(G2frame.restrBook)
2140    G2frame.restrBook.AddPage(ChemCompRestr,txt)
2141    Pages.append(txt)
2142   
2143    if GSASIIpath.GetConfigValue('debug'):
2144        txt = 'General'
2145        GeneralRestr = wx.ScrolledWindow(G2frame.restrBook)
2146        G2frame.restrBook.AddPage(GeneralRestr,txt)
2147        Pages.append(txt)
2148   
2149    if General['SH Texture']['Order']:
2150        txt = 'Texture'
2151        TextureRestr = wx.ScrolledWindow(G2frame.restrBook)
2152        G2frame.restrBook.AddPage(TextureRestr,txt)
2153        Pages.append(txt)
2154
2155    UpdateBondRestr(restrData['Bond'])
2156
2157    G2frame.restrBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2158
2159    # fill page selection menu
2160    menu = G2frame.dataWindow.RestraintTab
2161    for page in Pages:
2162        if menu.FindItem(page) >= 0: continue # is tab already in menu?
2163        Id = wx.NewId()
2164        TabSelectionIdDict[Id] = page
2165        menu.Append(Id,page,'')
2166        G2frame.Bind(wx.EVT_MENU, OnSelectPage, id=Id)
Note: See TracBrowser for help on using the repository browser.