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

Last change on this file since 2908 was 2908, checked in by toby, 4 years ago

Scroll bars work for Phase & Histogram windows; Constraints, Restraints & Rigid Bodies -- not yet

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