source: trunk/GSASIIrestrGUI.py @ 4905

Last change on this file since 4905 was 4905, checked in by toby, 5 months ago

fix delete for general; flag constraints where vars are removed

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