source: trunk/GSASIIrestrGUI.py @ 5164

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

more Mogul fixes

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