source: trunk/GSASIIrestrGUI.py @ 3511

Last change on this file since 3511 was 3511, checked in by vondreele, 3 years ago

more fixes to cell transform stuff
remove commented out code from G2RESTRgui

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