source: trunk/GSASIIrestrGUI.py

Last change on this file was 5243, checked in by vondreele, 6 months ago

fix crash in GetTruePosition? if getSelection() = 0
add missing delete option for chiral center restraint
updates to help/gsasII-phase.html

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