source: branch/2frame/GSASIIrestrGUI.py @ 2913

Last change on this file since 2913 was 2913, checked in by toby, 6 years ago

implement restraint tab selection menu; fix restraint scroll bar problem (remove/comment all DestroyChildren?); consolidate menu generation

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