source: trunk/GSASIIconstrGUI.py @ 3693

Last change on this file since 3693 was 3693, checked in by vondreele, 4 years ago

magAtoms --> magAtms spelling error n RefreshMagCellsGrid?
allow resizing of UseMagAtom? Dialog box
clean up commented out code in G2cstrGUI

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 98.8 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2018-10-22 20:08:06 +0000 (Mon, 22 Oct 2018) $
5# $Author: vondreele $
6# $Revision: 3693 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 3693 2018-10-22 20:08:06Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIIconstrGUI: Constraint GUI routines*
12------------------------------------------
13
14Used to define constraints and rigid bodies.
15
16'''
17from __future__ import division, print_function
18import sys
19import wx
20import wx.grid as wg
21import random as ran
22import numpy as np
23import numpy.ma as ma
24import numpy.linalg as nl
25import os.path
26import GSASIIpath
27GSASIIpath.SetVersionNumber("$Revision: 3693 $")
28import GSASIIElem as G2elem
29import GSASIIElemGUI as G2elemGUI
30import GSASIIstrIO as G2stIO
31import GSASIImapvars as G2mv
32import GSASIImath as G2mth
33import GSASIIlattice as G2lat
34import GSASIIdataGUI as G2gd
35import GSASIIctrlGUI as G2G
36import GSASIIplot as G2plt
37import GSASIIobj as G2obj
38import GSASIIspc as G2spc
39VERY_LIGHT_GREY = wx.Colour(235,235,235)
40
41class ConstraintDialog(wx.Dialog):
42    '''Window to edit Constraint values
43    '''
44    def __init__(self,parent,title,text,data,separator='*',varname="",varyflag=False):
45        wx.Dialog.__init__(self,parent,-1,'Edit '+title, 
46            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
47        self.data = data[:]
48        self.newvar = [varname,varyflag]
49        panel = wx.Panel(self)
50        mainSizer = wx.BoxSizer(wx.VERTICAL)
51        topLabl = wx.StaticText(panel,-1,text)
52        mainSizer.Add((10,10),1)
53        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
54        mainSizer.Add((10,10),1)
55        dataGridSizer = wx.FlexGridSizer(cols=3,hgap=2,vgap=2)
56        self.OkBtn = wx.Button(panel,wx.ID_OK)
57        for id in range(len(self.data)):
58            lbl1 = lbl = str(self.data[id][1])
59            if lbl[-1] != '=': lbl1 = lbl + ' ' + separator + ' '
60            name = wx.StaticText(panel,wx.ID_ANY,lbl1,
61                                 style=wx.ALIGN_RIGHT)
62            scale = G2G.ValidatedTxtCtrl(panel,self.data[id],0,
63                                          typeHint=float,
64                                          OKcontrol=self.DisableOK)
65            dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,5)
66            dataGridSizer.Add(scale,0,wx.RIGHT,3)
67            if ':' in lbl:
68                dataGridSizer.Add(
69                    wx.StaticText(panel,-1,G2obj.fmtVarDescr(lbl)),
70                    0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
71            else:
72                dataGridSizer.Add((-1,-1))
73        if title == 'New Variable':
74            name = wx.StaticText(panel,wx.ID_ANY,"New variable's\nname (optional)",
75                                 style=wx.ALIGN_CENTER)
76            scale = G2G.ValidatedTxtCtrl(panel,self.newvar,0,
77                                          typeHint=str,notBlank=False)
78            dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,5)
79            dataGridSizer.Add(scale,0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
80            self.refine = wx.CheckBox(panel,label='Refine?')
81            self.refine.SetValue(self.newvar[1]==True)
82            self.refine.Bind(wx.EVT_CHECKBOX, self.OnCheckBox)
83            dataGridSizer.Add(self.refine,0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
84           
85        mainSizer.Add(dataGridSizer,0,wx.EXPAND)
86        self.OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
87        self.OkBtn.SetDefault()
88        cancelBtn = wx.Button(panel,wx.ID_CANCEL)
89        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
90        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
91        btnSizer.Add((20,20),1)
92        btnSizer.Add(self.OkBtn)
93        btnSizer.Add((20,20),1)
94        btnSizer.Add(cancelBtn)
95        btnSizer.Add((20,20),1)
96
97        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
98        panel.SetSizer(mainSizer)
99        panel.Fit()
100        self.Fit()
101        self.CenterOnParent()
102       
103    def DisableOK(self,setting):
104        if setting:
105            self.OkBtn.Enable()
106        else:
107            self.OkBtn.Disable()
108
109    def OnCheckBox(self,event):
110        self.newvar[1] = self.refine.GetValue()
111
112    def OnOk(self,event):
113        parent = self.GetParent()
114        parent.Raise()
115        self.EndModal(wx.ID_OK)             
116
117    def OnCancel(self,event):
118        parent = self.GetParent()
119        parent.Raise()
120        self.EndModal(wx.ID_CANCEL)             
121
122    def GetData(self):
123        return self.data
124       
125################################################################################
126#####  Constraints
127################################################################################           
128       
129def UpdateConstraints(G2frame,data):
130    '''Called when Constraints tree item is selected.
131    Displays the constraints in the data window
132    '''
133    if not data:
134        data.update({'Hist':[],'HAP':[],'Phase':[],'Global':[]})       #empty dict - fill it
135    if 'Global' not in data:                                            #patch
136        data['Global'] = []
137    # DEBUG code ########################################
138    #import GSASIIconstrGUI
139    #reload(GSASIIconstrGUI)
140    #reload(G2obj)
141    #reload(G2stIO)
142    #import GSASIIstrMain
143    #reload(GSASIIstrMain)   
144    #reload(G2mv)
145    #reload(G2gd)
146    ###################################################
147    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
148    if not len(Phases) or not len(Histograms):
149        dlg = wx.MessageDialog(G2frame,'You need both phases and histograms to see Constraints',
150            'No phases or histograms')
151        dlg.CenterOnParent()
152        dlg.ShowModal()
153        dlg.Destroy()
154        return
155    G2obj.IndexAllIds(Histograms,Phases)
156    ##################################################################################
157    # patch: convert old-style (str) variables in constraints to G2VarObj objects
158    for key,value in data.items():
159        if key.startswith('_'): continue
160        j = 0
161        for cons in value:
162            #print cons             # DEBUG
163            for i in range(len(cons[:-3])):
164                if type(cons[i][1]) is str:
165                    cons[i][1] = G2obj.G2VarObj(cons[i][1])
166                    j += 1
167        if j:
168            print (str(key) + ': '+str(j)+' variable(s) as strings converted to objects')
169    ##################################################################################
170    rigidbodyDict = G2frame.GPXtree.GetItemPyData(
171        G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
172    rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
173    rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
174    badPhaseParms = ['Ax','Ay','Az','Amul','AI/A','Atype','SHorder','mV0','mV1','mV2','waveType','Vol','isMag',]
175    globalList = list(rbDict.keys())
176    globalList.sort()
177    try:
178        AtomDict = dict([Phases[phase]['pId'],Phases[phase]['Atoms']] for phase in Phases)
179    except KeyError:
180        G2frame.ErrorDialog('Constraint Error','Constraints cannot be set until a cycle of least squares'+
181                            ' has been run.\nWe suggest you refine a scale factor.')
182        return
183
184    # create a list of the phase variables
185    Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,rbIds=rbIds,Print=False)
186    phaseList = []
187    for item in phaseDict:
188        if item.split(':')[2] not in badPhaseParms:
189            phaseList.append(item)
190    phaseList.sort()
191    phaseAtNames = {}
192    phaseAtTypes = {}
193    TypeList = []
194    for item in phaseList:
195        Split = item.split(':')
196        if Split[2][:2] in ['AU','Af','dA','AM']:
197            Id = int(Split[0])
198            phaseAtNames[item] = AtomDict[Id][int(Split[3])][0]
199            phaseAtTypes[item] = AtomDict[Id][int(Split[3])][1]
200            if phaseAtTypes[item] not in TypeList:
201                TypeList.append(phaseAtTypes[item])
202        else:
203            phaseAtNames[item] = ''
204            phaseAtTypes[item] = ''
205             
206    # create a list of the hist*phase variables
207    seqList = G2frame.testSeqRefineMode()
208    if seqList: # for sequential refinement, only process 1st histgram in list
209        histDict = {seqList[0]:Histograms[seqList[0]]}
210    else:
211        histDict = Histograms
212    hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,histDict,Print=False,resetRefList=False)
213    hapList = sorted([i for i in hapDict.keys() if i.split(':')[2] not in ('Type',)])
214    if seqList: # convert histogram # to wildcard
215        wildList = [] # list of variables with "*" for histogram number
216        for i in hapList:
217            s = i.split(':')
218            if s[1] == "": continue
219            s[1] = '*'
220            sj = ':'.join(s)
221            if sj not in wildList: wildList.append(sj)
222        hapList = wildList
223    histVary,histDict,controlDict = G2stIO.GetHistogramData(histDict,Print=False)
224    histList = list(histDict.keys())
225    histList.sort()
226    if seqList: # convert histogram # to wildcard
227        wildList = [] # list of variables with "*" for histogram number
228        for i in histList:
229            s = i.split(':')
230            if s[1] == "": continue
231            s[1] = '*'
232            sj = ':'.join(s)
233            if sj not in wildList: wildList.append(sj)
234        histList = wildList       
235    Indx = {}
236    G2frame.Page = [0,'phs']
237       
238    def FindEquivVarb(name,nameList):
239        'Creates a list of variables appropriate to constrain with name'
240        outList = []
241        #phlist = []
242        items = name.split(':')
243        namelist = [items[2],]
244        if 'dA' in name:
245            namelist = ['dAx','dAy','dAz']
246        elif 'AU' in name:
247            namelist = ['AUiso','AU11','AU22','AU33','AU12','AU13','AU23']
248        elif 'AM' in name:
249            namelist = ['AMx','AMy','AMz']
250        elif items[-1] in ['A0','A1','A2','A3','A4','A5']:
251            namelist = ['A0','A1','A2','A3','A4','A5']
252        elif items[-1] in ['D11','D22','D33','D12','D13','D23']:
253            namelist = ['D11','D22','D33','D12','D13','D23']
254        elif 'Tm' in name:
255            namelist = ['Tmin','Tmax']
256        elif 'RB' in name:
257            rbfx = 'RB'+items[2][2]
258            if 'T' in name and 'Tr' not in name:
259                namelist = [rbfx+'T11',rbfx+'T22',rbfx+'T33',rbfx+'T12',rbfx+'T13',rbfx+'T23']
260            if 'L' in name:
261                namelist = [rbfx+'L11',rbfx+'L22',rbfx+'L33',rbfx+'L12',rbfx+'L13',rbfx+'L23']
262            if 'S' in name:
263                namelist = [rbfx+'S12',rbfx+'S13',rbfx+'S21',rbfx+'S23',rbfx+'S31',rbfx+'S32',rbfx+'SAA',rbfx+'SBB']
264            if 'U' in name:
265                namelist = [rbfx+'U',]
266
267        for item in nameList:
268            keys = item.split(':')
269            #if keys[0] not in phlist:
270            #    phlist.append(keys[0])
271            if items[1] == '*' and keys[2] in namelist: # wildcard -- select only sequential options
272                keys[1] = '*'
273                mitem = ':'.join(keys)
274                if mitem == name: continue
275                if mitem not in outList: outList.append(mitem)
276            elif keys[2] in namelist and item != name:
277                outList.append(item)
278        return outList
279       
280    def SelectVarbs(page,FrstVarb,varList,legend,constType):
281        '''Select variables used in constraints after one variable has
282        been selected. This routine determines the appropriate variables to be
283        used based on the one that has been selected and asks for more to be added.
284
285        It then creates the constraint and adds it to the constraints list.
286       
287        Called from OnAddEquivalence, OnAddFunction & OnAddConstraint (all but
288        OnAddHold)
289
290        :param list page: defines calling page -- type of variables to be used
291        :parm GSASIIobj.G2VarObj FrstVarb: reference to first selected variable
292        :param list varList: list of other appropriate variables to select from
293        :param str legend: header for selection dialog
294        :param str constType: type of constraint to be generated
295        :returns: a constraint, as defined in
296          :ref:`GSASIIobj <Constraint_definitions_table>`
297        '''
298        choices = [[i]+list(G2obj.VarDescr(i)) for i in varList]
299        meaning = G2obj.getDescr(FrstVarb.name)
300        if not meaning:
301            meaning = "(no definition found!)"
302        l = str(FrstVarb).split(':')
303        # make lists of phases & histograms to iterate over
304        phaselist = [l[0]]
305        if l[0]:
306            phaselbl = ['phase #'+l[0]]
307            if len(Phases) > 1:
308                phaselist += ['all'] 
309                phaselbl += ['all phases']
310        else:
311            phaselbl = ['']
312        histlist = [l[1]]
313        if l[1] == '*':
314            pass
315        elif l[1]:
316            histlbl = ['histogram #'+l[1]]
317            if len(Histograms) > 1:
318                histlist += ['all']
319                histlbl += ['all histograms']
320                typ = Histograms[G2obj.LookupHistName(l[1])[0]]['Instrument Parameters'][0]['Type'][1]
321                i = 0
322                for hist in Histograms:
323                    if Histograms[hist]['Instrument Parameters'][0]['Type'][1] == typ: i += 1
324                if i > 1:
325                    histlist += ['all='+typ]
326                    histlbl += ['all '+typ+' histograms']
327        else:
328            histlbl = ['']
329        # make a list of equivalent parameter names
330        nameList = [FrstVarb.name]
331        for var in varList:
332            nam = var.split(":")[2]
333            if nam not in nameList: nameList += [nam]
334        # add "wild-card" names to the list of variables
335        if l[1] == '*':
336            pass
337        elif page[1] == 'phs':
338            if 'RB' in FrstVarb.name:
339                pass
340            elif FrstVarb.atom is None:
341                for nam in nameList:
342                    for ph,plbl in zip(phaselist,phaselbl):
343                        if plbl: plbl = 'For ' + plbl
344                        var = ph+"::"+nam
345                        if var == str(FrstVarb) or var in varList: continue
346                        varList += [var]
347                        choices.append([var,plbl,meaning])
348            else:
349                for nam in nameList:
350                    for ph,plbl in zip(phaselist,phaselbl):
351                        if plbl: plbl = ' in ' + plbl
352                        for atype in ['']+TypeList:
353                            if atype:
354                                albl = "For "+atype+" atoms"
355                                akey = "all="+atype                       
356                            else:
357                                albl = "For all atoms"
358                                akey = "all"
359                            var = ph+"::"+nam+":"+akey
360                            if var == str(FrstVarb) or var in varList: continue
361                            varList += [var]
362                            choices.append([var,albl+plbl,meaning])
363        elif page[1] == 'hap':
364            if FrstVarb.name == "Scale":
365                meaning = "Phase fraction"
366            for nam in nameList:
367                for ph,plbl in zip(phaselist,phaselbl):
368                    if plbl: plbl = 'For ' + plbl
369                    for hst,hlbl in zip(histlist,histlbl):
370                        if hlbl:
371                            if plbl:
372                                hlbl = ' in ' + hlbl
373                            else:
374                                hlbl = 'For ' + hlbl                               
375                            var = ph+":"+hst+":"+nam
376                            if var == str(FrstVarb) or var in varList: continue
377                            varList += [var]
378                            choices.append([var,plbl+hlbl,meaning])
379        elif page[1] == 'hst':
380            if FrstVarb.name == "Scale":
381                meaning = "Scale factor"
382            for nam in nameList:
383                for hst,hlbl in zip(histlist,histlbl):
384                    if hlbl:
385                        hlbl = 'For ' + hlbl                               
386                        var = ":"+hst+":"+nam
387                        if var == str(FrstVarb) or var in varList: continue
388                        varList += [var]
389                        choices.append([var,hlbl,meaning])
390        elif page[1] == 'glb' or page[1] == 'sym':
391            pass
392        else:
393            raise Exception('Unknown constraint page '+ page[1])                   
394        if len(choices):
395            l1 = l2 = 1
396            for i1,i2,i3 in choices:
397                l1 = max(l1,len(i1))
398                l2 = max(l2,len(i2))
399            fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
400            atchoices = [fmt.format(*i1) for i1 in choices] # reformat list as str with columns
401            dlg = G2G.G2MultiChoiceDialog(
402                G2frame,legend,
403                'Constrain '+str(FrstVarb)+' with...',atchoices,
404                toggle=False,size=(625,400),monoFont=True)
405            dlg.CenterOnParent()
406            res = dlg.ShowModal()
407            Selections = dlg.GetSelections()[:]
408            dlg.Destroy()
409            if res != wx.ID_OK: return []
410            if len(Selections) == 0:
411                dlg = wx.MessageDialog(
412                    G2frame,
413                    'No variables were selected to include with '+str(FrstVarb),
414                    'No variables')
415                dlg.CenterOnParent()
416                dlg.ShowModal()
417                dlg.Destroy()
418                return []
419        else:
420            dlg = wx.MessageDialog(
421                G2frame,
422                'There are no appropriate variables to include with '+str(FrstVarb),
423                'No variables')
424            dlg.CenterOnParent()
425            dlg.ShowModal()
426            dlg.Destroy()
427            return []
428        # now process the variables provided by the user
429        varbs = [str(FrstVarb),] # list of selected variables
430        for sel in Selections:
431            var = varList[sel]
432            # phase(s) included
433            l = var.split(':')
434            if l[0] == "all":
435                phlist = [str(Phases[phase]['pId']) for phase in Phases]
436            else:
437                phlist = [l[0]]
438            # histogram(s) included
439            if l[1] == "all":
440                hstlist = [str(Histograms[hist]['hId']) for hist in Histograms]
441            elif '=' in l[1]:
442                htyp = l[1].split('=')[1]
443                hstlist = [str(Histograms[hist]['hId']) for hist in Histograms if 
444                           Histograms[hist]['Instrument Parameters'][0]['Type'][1] == htyp]
445            else:
446                hstlist = [l[1]]
447            if len(l) == 3:
448                for ph in phlist:
449                    for hst in hstlist:
450                        var = ph + ":" + hst + ":" + l[2]
451                        if var in varbs: continue
452                        varbs.append(var)
453            else: # constraints with atoms or rigid bodies
454                if len(l) == 5: # rigid body parameter
455                    var = ':'.join(l)
456                    if var in varbs: continue
457                    varbs.append(var)
458                elif l[3] == "all":
459                    for ph in phlist:
460                        key = G2obj.LookupPhaseName(ph)[0]
461                        for hst in hstlist: # should be blank
462                            for iatm,at in enumerate(Phases[key]['Atoms']):
463                                var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm)
464                                if var in varbs: continue
465                                varbs.append(var)
466                elif '=' in l[3]:
467                    for ph in phlist:
468                        key = G2obj.LookupPhaseName(ph)[0]
469                        cx,ct,cs,cia = Phases[key]['General']['AtomPtrs']
470                        for hst in hstlist: # should be blank
471                            atyp = l[3].split('=')[1]
472                            for iatm,at in enumerate(Phases[key]['Atoms']):
473                                if at[ct] != atyp: continue
474                                var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm)
475                                if var in varbs: continue
476                                varbs.append(var)
477                else:
478                    for ph in phlist:
479                        key = G2obj.LookupPhaseName(ph)[0]
480                        for hst in hstlist: # should be blank
481                            var = ph + ":" + hst + ":" + l[2] + ":" + l[3]
482                            if var in varbs: continue
483                            varbs.append(var)
484        if len(varbs) >= 1 or 'constraint' in constType:
485            constr = [[1.0,FrstVarb]]
486            for item in varbs[1:]:
487                constr += [[1.0,G2obj.G2VarObj(item)]]
488            if 'equivalence' in constType:
489                return [constr+[None,None,'e']]
490            elif 'function' in constType:
491                return [constr+[None,False,'f']]
492            elif 'constraint' in constType:
493                return [constr+[1.0,None,'c']]
494            else:
495                raise Exception('Unknown constraint type: '+str(constType))
496        else:
497            dlg = wx.MessageDialog(
498                G2frame,
499                'There are no selected variables to include with '+str(FrstVarb),
500                'No variables')
501            dlg.CenterOnParent()
502            dlg.ShowModal()
503            dlg.Destroy()
504        return []
505   
506    def FindAllCons(data):
507        ''' Find all constraints
508        '''
509        allcons = []
510        for key in data:
511            if key.startswith('_'): continue
512            allcons += data[key]
513        return allcons
514       
515    def CheckConstraints(constraintSet):
516        '''Check for errors in a set of constraints. Constraints based on symmetry (etc.)
517        are generated by running :func:`GSASIIstrIO.GetPhaseData`.
518        '''
519        G2mv.InitVars()   
520        constDictList,fixedList,ignored = G2stIO.ProcessConstraints(constraintSet)
521        # generate symmetry constraints to check for conflicts
522        rigidbodyDict = G2frame.GPXtree.GetItemPyData(   
523            G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
524        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
525        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
526        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
527            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
528        return G2mv.CheckConstraints('',constDictList,fixedList)
529
530    def CheckAddedConstraint(newcons):
531        '''Check a new constraint that has just been input.
532        If there is an error display a message and discard the last entry
533
534        Since the varylist is not available, no warning messages
535        should be generated here
536
537        :returns: True if constraint should be added
538        '''
539       
540        allcons1 = FindAllCons(data)
541        allcons = allcons1[:]
542        allcons += newcons
543        if not len(allcons): return True
544        errmsg,warnmsg = CheckConstraints(allcons)
545        if errmsg:
546            G2frame.ErrorDialog('Constraint Error',
547                'Error with newly added constraint:\n'+errmsg+
548                '\nIgnoring newly added constraint',parent=G2frame)
549            # reset error status
550            errmsg,warnmsg = CheckConstraints(allcons1)
551            if errmsg:
552                print (errmsg)
553                print (G2mv.VarRemapShow([],True))
554            return False
555        elif warnmsg:
556            print ('Unexpected contraint warning:\n'+warnmsg)
557        return True
558
559    def CheckChangedConstraint():
560        '''Check all constraints after an edit has been made.
561        If there is an error display a message and reject the change.
562
563        Since the varylist is not available, no warning messages
564        should be generated.
565       
566        :returns: True if the edit should be retained
567        '''
568        allcons = FindAllCons(data)
569        if not len(allcons): return True
570        errmsg,warnmsg = CheckConstraints(allcons)
571        if errmsg:
572            G2frame.ErrorDialog('Constraint Error',
573                'Error after editing constraint:\n'+errmsg+
574                '\nDiscarding last constraint edit',parent=G2frame)
575            # reset error status
576            errmsg,warnmsg = CheckConstraints(allcons)
577            if errmsg:
578                print (errmsg)
579                print (G2mv.VarRemapShow([],True))
580            return False
581        elif warnmsg:
582            print ('Unexpected contraint warning:\n'+warnmsg)
583        return True
584             
585    def PageSelection(page):
586        'Decode page reference'
587        if page[1] == "phs":
588            vartype = "phase"
589            varList = G2obj.removeNonRefined(phaseList)  # remove any non-refinable prms from list
590            constrDictEnt = 'Phase'
591        elif page[1] == "hap":
592            vartype = "Histogram*Phase"
593            varList = G2obj.removeNonRefined(hapList)  # remove any non-refinable prms from list
594            constrDictEnt = 'HAP'
595        elif page[1] == "hst":
596            vartype = "Histogram"
597            varList = G2obj.removeNonRefined(histList)  # remove any non-refinable prms from list
598            constrDictEnt = 'Hist'
599        elif page[1] == "glb":
600            vartype = "Global"
601            varList = G2obj.removeNonRefined(globalList)   # remove any non-refinable prms from list
602
603            constrDictEnt = 'Global'
604        elif page[1] == "sym":
605            return None,None,None
606        else:
607            raise Exception('Should not happen!')
608        return vartype,varList,constrDictEnt
609
610    def OnAddHold(event):
611        '''Create a new Hold constraint
612
613        Hold constraints allows the user to select one variable (the list of available
614        variables depends on which tab is currently active).
615        '''
616        page = G2frame.Page
617        vartype,varList,constrDictEnt = PageSelection(page)
618        if vartype is None: return
619        varList = G2obj.SortVariables(varList)
620        title1 = "Hold "+vartype+" variable"
621        if not varList:
622            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
623                parent=G2frame)
624            return
625        l2 = l1 = 1
626        for i in varList:
627            l1 = max(l1,len(i))
628            loc,desc = G2obj.VarDescr(i)
629            l2 = max(l2,len(loc))
630        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
631        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]
632        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
633        legend = "Select variables to hold (Will not be varied, even if vary flag is set)"
634        dlg = G2G.G2MultiChoiceDialog(G2frame,
635            legend,title1,varListlbl,toggle=False,size=(625,400),monoFont=True)
636        dlg.CenterOnParent()
637        if dlg.ShowModal() == wx.ID_OK:
638            for sel in dlg.GetSelections():
639                Varb = varList[sel]
640                VarObj = G2obj.G2VarObj(Varb)
641                newcons = [[[0.0,VarObj],None,None,'h']]
642                if CheckAddedConstraint(newcons):
643                    data[constrDictEnt] += newcons
644        dlg.Destroy()
645        wx.CallAfter(OnPageChanged,None)
646       
647    def OnAddEquivalence(event):
648        '''add an Equivalence constraint'''
649        page = G2frame.Page
650        vartype,varList,constrDictEnt = PageSelection(page)
651        if vartype is None: return
652        title1 = "Setup equivalent "+vartype+" variables"
653        title2 = "Select additional "+vartype+" variable(s) to be equivalent with "
654        if not varList:
655            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
656                parent=G2frame)
657            return
658#        legend = "Select variables to make equivalent (only one of the variables will be varied when all are set to be varied)"
659        GetAddVars(page,title1,title2,varList,constrDictEnt,'equivalence')
660       
661    def OnAddAtomEquiv(event):
662        ''' Add equivalences between all parameters on atoms '''
663        page = G2frame.Page
664        vartype,varList,constrDictEnt = PageSelection(page)
665        if vartype is None: return
666        title1 = "Setup equivalent atom variables"
667        title2 = "Select additional atoms(s) to be equivalent with "
668        if not varList:
669            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
670                parent=G2frame)
671            return
672#        legend = "Select atoms to make equivalent (only one of the atom variables will be varied when all are set to be varied)"
673        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'equivalence')
674       
675    def OnAddRiding(event):
676        ''' Add riding equivalences between all parameters on atoms '''
677        page = G2frame.Page
678        vartype,varList,constrDictEnt = PageSelection(page)
679        if vartype is None: return
680        title1 = "Setup riding atoms "
681        title2 = "Select additional atoms(s) to ride on "
682        if not varList:
683            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
684                parent=G2frame)
685            return
686#        legend = "Select atoms to ride (only one of the atom variables will be varied when all are set to be varied)"
687        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'riding')
688   
689    def OnAddFunction(event):
690        '''add a Function (new variable) constraint'''
691        page = G2frame.Page
692        vartype,varList,constrDictEnt = PageSelection(page)
693        if vartype is None: return
694        title1 = "Setup new variable based on "+vartype+" variables"
695        title2 = "Include additional "+vartype+" variable(s) to be included with "
696        if not varList:
697            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
698                parent=G2frame)
699            return
700#        legend = "Select variables to include in a new variable (the new variable will be varied when all included variables are varied)"
701        GetAddVars(page,title1,title2,varList,constrDictEnt,'function')
702                       
703    def OnAddConstraint(event):
704        '''add a constraint equation to the constraints list'''
705        page = G2frame.Page
706        vartype,varList,constrDictEnt = PageSelection(page)
707        if vartype is None: return
708        title1 = "Setup constraint on "+vartype+" variables"
709        title2 = "Select additional "+vartype+" variable(s) to include in constraint with "
710        if not varList:
711            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
712                parent=G2frame)
713            return
714#        legend = "Select variables to include in a constraint equation (the values will be constrainted to equal a specified constant)"
715        GetAddVars(page,title1,title2,varList,constrDictEnt,'constraint')
716
717    def GetAddVars(page,title1,title2,varList,constrDictEnt,constType):
718        '''Get the variables to be added for OnAddEquivalence, OnAddFunction,
719        and OnAddConstraint. Then create and check the constraint.
720        '''
721        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
722        if constType == 'equivalence':
723            omitVars = G2mv.GetDependentVars()
724        else:
725            omitVars = []
726        varList = G2obj.SortVariables([i for i in varList if i not in omitVars])
727        l2 = l1 = 1
728        for i in varList:
729            l1 = max(l1,len(i))
730            loc,desc = G2obj.VarDescr(i)
731            l2 = max(l2,len(loc))
732        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
733        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]
734        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st variable:',
735            title1,varListlbl,monoFont=True,size=(625,400))
736        dlg.CenterOnParent()
737        if dlg.ShowModal() == wx.ID_OK:
738            if constType == 'equivalence':
739                omitVars = G2mv.GetDependentVars() + G2mv.GetIndependentVars()
740            sel = dlg.GetSelection()
741            FrstVarb = varList[sel]
742            VarObj = G2obj.G2VarObj(FrstVarb)
743            moreVarb = G2obj.SortVariables(FindEquivVarb(FrstVarb,[i for i in varList if i not in omitVars]))
744            newcons = SelectVarbs(page,VarObj,moreVarb,title2+FrstVarb,constType)
745            if len(newcons) > 0:
746                if CheckAddedConstraint(newcons):
747                    data[constrDictEnt] += newcons
748        dlg.Destroy()
749        wx.CallAfter(OnPageChanged,None)
750                       
751    def FindNeighbors(phase,FrstName,AtNames):
752        General = phase['General']
753        cx,ct,cs,cia = General['AtomPtrs']
754        Atoms = phase['Atoms']
755        atNames = [atom[ct-1] for atom in Atoms]
756        Cell = General['Cell'][1:7]
757        Amat,Bmat = G2lat.cell2AB(Cell)
758        atTypes = General['AtomTypes']
759        Radii = np.array(General['BondRadii'])
760        AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
761        Orig = atNames.index(FrstName.split()[1])
762        OType = Atoms[Orig][ct]
763        XYZ = G2mth.getAtomXYZ(Atoms,cx)       
764        Neigh = []
765        Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
766        dist = np.sqrt(np.sum(Dx**2,axis=1))
767        sumR = AtInfo[OType]+0.5    #H-atoms only!
768        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
769        for j in IndB[0]:
770            if j != Orig:
771                Neigh.append(AtNames[j])
772        return Neigh
773       
774    def GetAddAtomVars(page,title1,title2,varList,constrDictEnt,constType):
775        '''Get the atom variables to be added for OnAddAtomEquiv. Then create and
776        check the constraints. Riding for H atoms only.
777        '''
778        Atoms = {G2obj.VarDescr(i)[0]:[] for i in varList if 'Atom' in G2obj.VarDescr(i)[0]}
779        for item in varList:
780            atName = G2obj.VarDescr(item)[0]
781            if atName in Atoms:
782                Atoms[atName].append(item)
783        AtNames = list(Atoms.keys())
784        AtNames.sort()
785        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st atom:',
786            title1,AtNames,monoFont=True,size=(625,400))
787        dlg.CenterOnParent()
788        FrstAtom = ''
789        if dlg.ShowModal() == wx.ID_OK:
790            sel = dlg.GetSelection()
791            FrstAtom = AtNames[sel]
792            if 'riding' in constType:
793                phaseName = (FrstAtom.split(' in ')[1]).strip()
794                phase = Phases[phaseName]
795                AtNames = FindNeighbors(phase,FrstAtom,AtNames)
796            else:
797                AtNames.remove(FrstAtom)
798        dlg.Destroy()
799        if FrstAtom == '':
800            print ('no atom selected')
801            return
802        dlg = G2G.G2MultiChoiceDialog(
803            G2frame,title2+FrstAtom,
804            'Constrain '+str(FrstAtom)+' with...',AtNames,
805            toggle=False,size=(625,400),monoFont=True)
806        if dlg.ShowModal() == wx.ID_OK:
807            Selections = dlg.GetSelections()[:]
808        else:
809            print ('no target atom selected')
810            dlg.Destroy()
811            return
812        dlg.Destroy()
813        for name in Atoms[FrstAtom]:
814            newcons = []
815            constr = []
816            if 'riding' in constType:
817                if 'AUiso' in name:
818                    constr = [[1.0,G2obj.G2VarObj(name)]]
819                elif 'AU11' in name:
820                    pass
821                elif 'AU' not in name:
822                    constr = [[1.0,G2obj.G2VarObj(name)]]
823            else:
824                constr = [[1.0,G2obj.G2VarObj(name)]]
825            pref = ':'+name.rsplit(':',1)[0].split(':',1)[1]    #get stuff between phase id & atom id
826            for sel in Selections:
827                name2 = Atoms[AtNames[sel]][0]
828                pid = name2.split(':',1)[0]                     #get phase id for 2nd atom
829                id = name2.rsplit(':',1)[-1]                    #get atom no. for 2nd atom
830                if 'riding' in constType:
831                    pref = pid+pref
832                    if 'AUiso' in pref:
833                        parts = pref.split('AUiso')
834                        constr += [[1.2,G2obj.G2VarObj('%s:%s'%(parts[0]+'AUiso',id))]]
835                    elif 'AU' not in pref:
836                        constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pref,id))]]
837                else:
838                    constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pid+pref,id))]]
839            if not constr:
840                continue
841            if 'frac' in pref and 'riding' not in constType:
842                newcons = [constr+[1.0,None,'c']]
843            else:
844                newcons = [constr+[None,None,'e']]
845            if len(newcons) > 0:
846                if CheckAddedConstraint(newcons):
847                    data[constrDictEnt] += newcons
848        wx.CallAfter(OnPageChanged,None)
849                       
850    def MakeConstraintsSizer(name,pageDisplay):
851        '''Creates a sizer displaying all of the constraints entered of
852        the specified type.
853
854        :param str name: the type of constraints to be displayed ('HAP',
855          'Hist', 'Phase', 'Global', 'Sym-Generated')
856        :param wx.Panel pageDisplay: parent panel for sizer
857        :returns: wx.Sizer created by method
858        '''
859        if name == 'Sym-Generated':         #show symmetry generated constraints
860            Sizer1 =  wx.BoxSizer(wx.VERTICAL)
861            Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
862                                    'Equivalences generated based on cell/space group input'))
863            Sizer1.Add((-1,5))
864            Sizer = wx.FlexGridSizer(0,2,0,0)
865            Sizer1.Add(Sizer)
866            for sym in G2mv.GetSymEquiv():
867                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,'EQUIV'),
868                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
869                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,sym))
870                Sizer.Add((-1,-1))
871                Sizer.Add((-1,2))
872            return Sizer1
873        constSizer = wx.FlexGridSizer(0,6,0,0)
874        maxlen = 50 # characters before wrapping a constraint
875        for Id,item in enumerate(data[name]):
876            refineflag = False
877            helptext = ""
878            eqString = ['',]
879            problemItem = False
880            for term in item[:-3]:
881                if str(term[1]) in G2mv.problemVars:
882                    problemItem = True
883            if item[-1] == 'h': # Hold on variable
884                constSizer.Add((-1,-1),0)              # blank space for edit button
885                typeString = 'FIXED'
886                var = str(item[0][1])
887                varMean = G2obj.fmtVarDescr(var)
888                eqString[-1] =  var +'   '
889                helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed"
890            elif isinstance(item[-1],str): # not true on original-style (2011?) constraints
891                constEdit = wx.Button(pageDisplay,wx.ID_ANY,'Edit',style=wx.BU_EXACTFIT)
892                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
893                constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)            # edit button
894                Indx[constEdit.GetId()] = [Id,name]
895                if item[-1] == 'f':
896                    helptext = "A new variable"
897                    if item[-3]:
898                        helptext += " named "+str(item[-3])
899                    helptext += " is created from a linear combination of the following variables:\n"
900                    for term in item[:-3]:
901                        var = str(term[1])
902                        if len(eqString[-1]) > maxlen:
903                            eqString.append(' ')
904                        m = term[0]
905                        if eqString[-1] != '':
906                            if m >= 0:
907                                eqString[-1] += ' + '
908                            else:
909                                eqString[-1] += ' - '
910                                m = abs(m)
911                        eqString[-1] += '%.3f*%s '%(m,var)
912                        varMean = G2obj.fmtVarDescr(var)
913                        helptext += "\n" + var + " ("+ varMean + ")"
914                    if '_Explain' in data:
915                        if data['_Explain'].get(item[-3]):
916                            helptext += '\n\n'
917                            helptext += data['_Explain'][item[-3]]
918                    # typeString = 'NEWVAR'
919                    # if item[-3]:
920                    #     eqString[-1] += ' = '+item[-3]
921                    # else:
922                    #     eqString[-1] += ' = New Variable'
923                    if item[-3]:
924                        typeString = item[-3] + ' = '
925                    else:
926                        typeString = 'New Variable = '
927                    #print 'refine',item[-2]
928                    refineflag = True
929                elif item[-1] == 'c':
930                    helptext = "The following variables constrained to equal a constant:"
931                    for term in item[:-3]:
932                        var = str(term[1])
933                        if len(eqString[-1]) > maxlen:
934                            eqString.append(' ')
935                        if eqString[-1] != '':
936                            if term[0] > 0:
937                                eqString[-1] += ' + '
938                            else:
939                                eqString[-1] += ' - '
940                        eqString[-1] += '%.3f*%s '%(abs(term[0]),var)
941                        varMean = G2obj.fmtVarDescr(var)
942                        helptext += "\n" + var + " ("+ varMean + ")"
943                    typeString = 'CONST'
944                    eqString[-1] += ' = '+str(item[-3])
945                elif item[-1] == 'e':
946                    helptext = "The following variables are set to be equivalent, noting multipliers:"
947                    normval = item[:-3][1][0]
948                    for i,term in enumerate(item[:-3]):
949                        var = str(term[1])
950                        if term[0] == 0: term[0] = 1.0
951                        if len(eqString[-1]) > maxlen:
952                            eqString.append(' ')
953                        if i == 0: # move independent variable to end
954                            indepterm = term
955                            continue
956                        elif eqString[-1] != '':
957                            eqString[-1] += ' = '
958                        if normval/term[0] == 1:
959                            eqString[-1] += '%s'% var
960                        else:
961                            eqString[-1] += '%.3f*%s'%(normval/term[0],var)
962                        varMean = G2obj.fmtVarDescr(var)
963                        helptext += "\n" + var + " ("+ varMean + ")"
964                    if normval/indepterm[0] == 1:
965                        eqString[-1] += ' = %s'% str(indepterm[1])
966                    else:
967                        eqString[-1] += ' = %.3f*%s'%(normval/indepterm[0],str(indepterm[1]))
968                    typeString = 'EQUIV'
969                else:
970                    print ('Unexpected constraint'+item)
971               
972            else:
973                print ('Removing old-style constraints')
974                data[name] = []
975                return constSizer
976            constDel = wx.Button(pageDisplay,wx.ID_ANY,'Delete',style=wx.BU_EXACTFIT)
977            constDel.Bind(wx.EVT_BUTTON,OnConstDel)
978            Indx[constDel.GetId()] = [Id,name]
979            constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)             # delete button
980            if helptext:
981                ch = G2G.HelpButton(pageDisplay,helptext)
982                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
983            else:
984                constSizer.Add((-1,-1))
985            if refineflag:
986                ch = G2G.G2CheckBox(pageDisplay,'',item,-2)
987                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
988            else:
989                constSizer.Add((-1,-1))               
990            constSizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,typeString),
991                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
992            if problemItem: eqString[-1] += ' -- Conflict: see console'
993            if len(eqString) > 1:
994                Eq = wx.BoxSizer(wx.VERTICAL)
995                for s in eqString:
996                    line = wx.StaticText(pageDisplay,wx.ID_ANY,s)
997                    if problemItem: line.SetBackgroundColour(wx.YELLOW)
998                    Eq.Add(line,0,wx.ALIGN_CENTER_VERTICAL)
999                Eq.Add((-1,4))
1000            else:
1001                Eq = wx.StaticText(pageDisplay,wx.ID_ANY,eqString[0])
1002                if problemItem: Eq.SetBackgroundColour(wx.YELLOW)
1003            constSizer.Add(Eq,1,wx.ALIGN_CENTER_VERTICAL)
1004        return constSizer
1005               
1006    def OnConstDel(event):
1007        'Delete a constraint'
1008        Obj = event.GetEventObject()
1009        Id,name = Indx[Obj.GetId()]
1010        del data[name][Id]
1011        allcons = FindAllCons(data)     #should I call CheckChangedConstraint() instead?
1012        if not len(allcons): return
1013        CheckConstraints(allcons)
1014        wx.CallAfter(OnPageChanged,None)
1015       
1016    def OnConstEdit(event):
1017        '''Called to edit an individual contraint in response to a
1018        click on its Edit button
1019        '''
1020        Obj = event.GetEventObject()
1021        Id,name = Indx[Obj.GetId()]
1022        if data[name][Id][-1] == 'f':
1023            items = data[name][Id][:-3]
1024            constType = 'New Variable'
1025            if data[name][Id][-3]:
1026                varname = data[name][Id][-3]
1027            else:
1028                varname = ""
1029            lbl = 'Enter value for each term in constraint; sum = new variable'
1030            dlg = ConstraintDialog(G2frame,constType,lbl,items,
1031                                   varname=varname,varyflag=data[name][Id][-2])
1032        elif data[name][Id][-1] == 'c':
1033            items = data[name][Id][:-3]+[
1034                [data[name][Id][-3],'fixed value =']]
1035            constType = 'Constraint'
1036            lbl = 'Edit value for each term in constant constraint sum'
1037            dlg = ConstraintDialog(G2frame,constType,lbl,items)
1038        elif data[name][Id][-1] == 'e':
1039            items = data[name][Id][:-3]
1040            constType = 'Equivalence'
1041            lbl = 'The following terms are set to be equal:'
1042            dlg = ConstraintDialog(G2frame,constType,lbl,items,'/')
1043        else:
1044            return
1045        try:
1046            prev = data[name][Id][:]
1047            if dlg.ShowModal() == wx.ID_OK:
1048                result = dlg.GetData()
1049                for i in range(len(data[name][Id][:-3])):
1050                    if type(data[name][Id][i]) is tuple: # fix non-mutable construct
1051                        data[name][Id][i] = list(data[name][Id][i])
1052                    data[name][Id][i][0] = result[i][0]
1053                if data[name][Id][-1] == 'c':
1054                    data[name][Id][-3] = str(result[-1][0])
1055                elif data[name][Id][-1] == 'f':
1056                    # process the variable name to put in global form (::var)
1057                    varname = str(dlg.newvar[0]).strip().replace(' ','_')
1058                    if varname.startswith('::'):
1059                        varname = varname[2:]
1060                    varname = varname.replace(':',';')
1061                    if varname:
1062                        data[name][Id][-3] = varname
1063                    else:
1064                        data[name][Id][-3] = ''
1065                    data[name][Id][-2] = dlg.newvar[1]
1066                if not CheckChangedConstraint():
1067                    data[name][Id] = prev
1068        except:
1069            import traceback
1070            print (traceback.format_exc())
1071        finally:
1072            dlg.Destroy()
1073        wx.CallAfter(OnPageChanged,None)
1074   
1075    def UpdateConstraintPanel(panel,typ):
1076        '''Update the contents of the selected Constraint
1077        notebook tab. Called in :func:`OnPageChanged`
1078        '''
1079        if panel.GetSizer(): panel.GetSizer().Clear(True)
1080        Siz = wx.BoxSizer(wx.VERTICAL)
1081        Siz.Add((5,5),0)
1082        Siz.Add(MakeConstraintsSizer(typ,panel),1,wx.EXPAND)
1083        panel.SetSizer(Siz,True)
1084        Size = Siz.GetMinSize()
1085        Size[0] += 40
1086        Size[1] = max(Size[1],450) + 20
1087        panel.SetSize(Size)
1088        panel.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1089        panel.Show()
1090
1091    def OnPageChanged(event):
1092        '''Called when a tab is pressed or when a "select tab" menu button is
1093        used (see RaisePage), or to refresh the current tab contents (event=None)
1094        '''
1095        if event:       #page change event!
1096            page = event.GetSelection()
1097        else: # called directly, get current page
1098            page = G2frame.constr.GetSelection()
1099        G2frame.constr.ChangeSelection(page)
1100        text = G2frame.constr.GetPageText(page)
1101        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,False)
1102#        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,False)
1103        if text == 'Histogram/Phase':
1104            enableEditCons = [False]+4*[True]
1105            G2frame.Page = [page,'hap']
1106            UpdateConstraintPanel(HAPConstr,'HAP')
1107        elif text == 'Histogram':
1108            enableEditCons = [False]+4*[True]
1109            G2frame.Page = [page,'hst']
1110            UpdateConstraintPanel(HistConstr,'Hist')
1111        elif text == 'Phase':
1112            enableEditCons = 5*[True]
1113            G2frame.Page = [page,'phs']
1114            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,True)
1115#            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,True)
1116            if 'DELETED' in str(PhaseConstr):   #seems to be no other way to do this (wx bug)
1117                if GSASIIpath.GetConfigValue('debug'):
1118                    print ('DBG_wx error: PhaseConstr not cleanly deleted after Refine')
1119                return
1120            UpdateConstraintPanel(PhaseConstr,'Phase')
1121        elif text == 'Global':
1122            enableEditCons = [False]+4*[True]
1123            G2frame.Page = [page,'glb']
1124            UpdateConstraintPanel(GlobalConstr,'Global')
1125        else:
1126            enableEditCons = 5*[False]
1127            G2frame.Page = [page,'sym']
1128            UpdateConstraintPanel(SymConstr,'Sym-Generated')
1129        # remove menu items when not allowed
1130        for obj,flag in zip(G2frame.dataWindow.ConstraintEdit.GetMenuItems(),enableEditCons): 
1131            obj.Enable(flag)
1132        G2frame.dataWindow.SetDataSize()
1133
1134    def RaisePage(event):
1135        'Respond to a "select tab" menu button'
1136        try:
1137            i = (G2G.wxID_CONSPHASE,
1138                 G2G.wxID_CONSHAP,
1139                 G2G.wxID_CONSHIST,
1140                 G2G.wxID_CONSGLOBAL,
1141                 G2G.wxID_CONSSYM,
1142                ).index(event.GetId())
1143            G2frame.constr.SetSelection(i)
1144            wx.CallAfter(OnPageChanged,None)
1145        except ValueError:
1146            print('Unexpected event in RaisePage')
1147
1148    def SetStatusLine(text):
1149        G2frame.GetStatusBar().SetStatusText(text,1)                                     
1150       
1151    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ConstraintMenu)
1152    SetStatusLine('')
1153   
1154    G2frame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2G.wxID_CONSTRAINTADD)
1155    G2frame.Bind(wx.EVT_MENU, OnAddFunction, id=G2G.wxID_FUNCTADD)
1156    G2frame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2G.wxID_EQUIVADD)
1157    G2frame.Bind(wx.EVT_MENU, OnAddHold, id=G2G.wxID_HOLDADD)
1158    G2frame.Bind(wx.EVT_MENU, OnAddAtomEquiv, id=G2G.wxID_EQUIVALANCEATOMS)
1159#    G2frame.Bind(wx.EVT_MENU, OnAddRiding, id=G2G.wxID_ADDRIDING)
1160    # tab commands
1161    for id in (G2G.wxID_CONSPHASE,
1162               G2G.wxID_CONSHAP,
1163               G2G.wxID_CONSHIST,
1164               G2G.wxID_CONSGLOBAL,
1165               G2G.wxID_CONSSYM,
1166               ):
1167        G2frame.Bind(wx.EVT_MENU, RaisePage,id=id)
1168
1169    #G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetClientSize())
1170    G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow)
1171    G2frame.dataWindow.GetSizer().Add(G2frame.constr,1,wx.ALL|wx.EXPAND)
1172    # note that order of pages is hard-coded in RaisePage
1173    PhaseConstr = wx.ScrolledWindow(G2frame.constr)
1174    G2frame.constr.AddPage(PhaseConstr,'Phase')
1175    HAPConstr = wx.ScrolledWindow(G2frame.constr)
1176    G2frame.constr.AddPage(HAPConstr,'Histogram/Phase')
1177    HistConstr = wx.ScrolledWindow(G2frame.constr)
1178    G2frame.constr.AddPage(HistConstr,'Histogram')
1179    GlobalConstr = wx.ScrolledWindow(G2frame.constr)
1180    G2frame.constr.AddPage(GlobalConstr,'Global')
1181    SymConstr = wx.ScrolledWindow(G2frame.constr)
1182    G2frame.constr.AddPage(SymConstr,'Sym-Generated')
1183    wx.CallAfter(OnPageChanged,None)
1184    G2frame.constr.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
1185    # validate all the constrants -- should not see any errors here normally
1186    allcons = FindAllCons(data)
1187    if not len(allcons): return
1188    errmsg,warnmsg = CheckConstraints(allcons)
1189    if errmsg:
1190        G2frame.ErrorDialog('Constraint Error',
1191                            'Error in constraints:\n'+errmsg+'\nCheck console output for more information',
1192                            parent=G2frame)
1193        print (errmsg)
1194        print (G2mv.VarRemapShow([],True))
1195    elif warnmsg:
1196        print ('Unexpected contraint warning:\n'+warnmsg)
1197
1198################################################################################
1199# check scale & phase fractions, create constraint if needed
1200################################################################################
1201def CheckAllScalePhaseFractions(G2frame):
1202    '''Check if scale factor and all phase fractions are refined without a constraint
1203    for all used histograms, if so, offer the user a chance to create a constraint
1204    on the sum of phase fractions
1205    '''
1206    histograms, phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1207    for i,hist in enumerate(histograms):
1208        CheckScalePhaseFractions(G2frame,hist,histograms,phases)
1209       
1210def CheckScalePhaseFractions(G2frame,hist,histograms,phases):
1211    '''Check if scale factor and all phase fractions are refined without a constraint
1212    for histogram hist, if so, offer the user a chance to create a constraint
1213    on the sum of phase fractions
1214    '''
1215    if G2frame.testSeqRefineMode():
1216        histStr = '*'
1217    else:
1218        histStr = str(histograms[hist]['hId'])
1219    # Is this powder?
1220    if not hist.startswith('PWDR '): return
1221    # do this only if the scale factor is varied
1222    if not histograms[hist]['Sample Parameters']['Scale'][1]: return
1223    # are all phase fractions varied in all used histograms?
1224    phaseCount = 0
1225    for p in phases:
1226        if hist not in phases[p]['Histograms']: continue
1227        if phases[p]['Histograms'][hist]['Use'] and not phases[p]['Histograms'][hist]['Scale'][1]:
1228            return
1229        else:
1230            phaseCount += 1
1231   
1232    # all phase fractions and scale factor varied, now scan through constraints
1233    sub = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints') 
1234    Constraints = G2frame.GPXtree.GetItemPyData(sub)
1235    for c in Constraints.get('HAP',[]):
1236        if c[-1] != 'c': continue
1237        if not c[-3]: continue
1238        if len(c[:-3]) != phaseCount: continue
1239        # got a constraint equation with right number of terms, is it on phase fractions for
1240        # the correct histogram?
1241        if all([(i[1].name == 'Scale' and i[1].varname().split(':')[1] == histStr) for i in c[:-3]]):
1242            # got a constraint, this is OK
1243            return
1244    dlg = wx.MessageDialog(G2frame,'You are refining the scale factor and all phase fractions for histogram #'+
1245        histStr+'. This will produce an unstable refinement. '+
1246        'Do you want to constrain the sum of phase fractions?','Create constraint?',wx.OK|wx.CANCEL)
1247    if dlg.ShowModal() != wx.ID_OK:
1248        dlg.Destroy()
1249        return
1250    dlg.Destroy()
1251
1252    constr = []
1253    for p in phases:
1254        if hist not in phases[p]['Histograms']: continue
1255        if not phases[p]['Histograms'][hist]['Use']: continue
1256        constr += [[1.0,G2obj.G2VarObj(':'.join((str(phases[p]['pId']),histStr,'Scale')))]]
1257    constr += [1.0,None,'c']
1258    Constraints['HAP'] += [constr]
1259       
1260################################################################################
1261#### Make nuclear/magnetic phase transition constraints - called by OnTransform in G2phsGUI
1262################################################################################       
1263       
1264def TransConstraints(G2frame,oldPhase,newPhase,Trans,Vec,atCodes):
1265    '''Add constraints for new magnetic phase created via transformation of old
1266    nuclear one
1267    NB: A = [G11,G22,G33,2*G12,2*G13,2*G23]
1268    '''
1269   
1270    def SetUniqAj(pId,Aname,SGLaue):
1271        if SGLaue in ['4/m','4/mmm'] and iA in [0,1]:
1272            parm = '%d::%s'%(pId,'A0')
1273        elif SGLaue in ['m3','m3m'] and iA in [0,1,2]:
1274            parm = '%d::%s'%(pId,'A0')
1275        elif SGLaue in ['6/m','6/mmm','3m1', '31m', '3'] and iA in [0,1,3]:
1276            parm = '%d::%s'%(pId,'A0')
1277        elif SGLaue in ['3R', '3mR']:
1278            if ia in [0,1,2]:
1279                parm = '%d::%s'%(pId,'A0')
1280            else:
1281                parm = '%d::%s'%(pId,'A3')
1282        else:
1283            parm = '%d::%s'%(pId,Aname)
1284        return parm
1285   
1286    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1287    UseList = newPhase['Histograms']
1288    detTrans = np.abs(nl.det(Trans))
1289    invTrans = nl.inv(Trans)
1290    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1291   
1292    opId = oldPhase['pId']
1293    npId = newPhase['pId']
1294    cx,ct,cs,cia = newPhase['General']['AtomPtrs']
1295    nAtoms = newPhase['Atoms']
1296    oSGData = oldPhase['General']['SGData']
1297    nSGData = newPhase['General']['SGData']
1298    oAcof = G2lat.cell2A(oldPhase['General']['Cell'][1:7])
1299    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1300    item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints')
1301    if not item:
1302        return
1303    constraints = G2frame.GPXtree.GetItemPyData(item)
1304    parmDict = {}
1305    varyList = []
1306    xnames = ['dAx','dAy','dAz']
1307#    Us = ['AU11','AU22','AU33','AU12','AU13','AU23']
1308#    Uids = [[0,0,'AU11'],[1,1,'AU22'],[2,2,'AU33'],[0,1,'AU12'],[0,2,'AU13'],[1,2,'AU23']]
1309    for ia,code in enumerate(atCodes):
1310        atom = nAtoms[ia]
1311        if not ia and atom[cia] == 'A':
1312            wx.MessageDialog(G2frame,
1313                'Anisotropic thermal motion constraints are not developed at the present time',
1314                'Anisotropic thermal constraint?',style=wx.ICON_INFORMATION).ShowModal()
1315        siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0]
1316        CSX = G2spc.GetCSxinel(siteSym)
1317#        CSU = G2spc.GetCSuinel(siteSym)
1318        item = code.split('+')[0]
1319        iat,opr = item.split(':')
1320        Nop = abs(int(opr))%100-1
1321        if '-' in opr:
1322            Nop *= -1
1323        Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
1324        if Nop < 0:         #inversion
1325            Opr *= -1
1326        XOpr = np.inner(Opr,Trans)
1327        for ix in list(set(CSX[0])):
1328            if not ix:
1329                continue
1330            name = xnames[ix-1]
1331            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1332            DepCons = []
1333            for iop,opval in enumerate(XOpr[ix-1]):
1334                if opval:
1335                    DepCons.append([opval,G2obj.G2VarObj('%d::%s:%s'%(opId,xnames[iop],iat))])
1336            if len(DepCons) == 1:
1337                constraints['Phase'].append([DepCons[0],IndpCon,None,None,'e'])
1338            elif len(DepCons) > 1:
1339                for Dep in DepCons:
1340                    Dep[0] *= -1
1341                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1342        for name in ['Afrac','AUiso']:
1343            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1344            DepCons = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1345            constraints['Phase'].append([DepCons,IndpCon,None,None,'e'])
1346       
1347#        DepConsDict = dict(zip(Us,[[],[],[],[],[],[]]))
1348#        for iu,Uid in enumerate(Uids):
1349#            UMT = np.zeros((3,3))
1350#            UMT[Uid[0],Uid[1]] = 1
1351#            nUMT = G2lat.prodMGMT(UMT,invTrans)
1352#            nUT = G2lat.UijtoU6(nUMT)
1353#            for iu,nU in enumerate(nUT):
1354#                if abs(nU) > 1.e-8:
1355#                    parm = '%d::%s;%s'%(opId,Us[iu],iat)
1356#                    DepConsDict[Uid[2]].append([abs(nU%1.),G2obj.G2VarObj(parm)])
1357#        nUcof = atom[iu:iu+6]
1358#        for iU,Usi in enumerate(Us):
1359#            parm = '%d::%s;%d'%(npId,Usi,ia)
1360#            parmDict[parm] = nUcof[iU]
1361#            varyList.append(parm)
1362#            IndpCon = [1.0,G2obj.G2VarObj(parm)]
1363#            if len(DepConsDict[Usi]) == 1:
1364#                if DepConsDict[Usi][0]:
1365#                    constraints['Phase'].append([IndpCon,DepConsDict[Usi][0],None,None,'e'])
1366#            elif len(DepConsDict[Usi]) > 1:       
1367#                for Dep in DepConsDict[Usi]:
1368#                    Dep[0] *= -1
1369#                constraints['Phase'].append([IndpCon]+DepConsDict[Usi]+[0.0,None,'c'])
1370           
1371        #how do I do Uij's for most Trans?
1372    As = ['A0','A1','A2','A3','A4','A5']
1373    Aids = [[0,0,'A0'],[1,1,'A1'],[2,2,'A2'],[0,1,'A3'],[0,2,'A4'],[1,2,'A5']]
1374    DepConsDict = dict(zip(As,[[],[],[],[],[],[]]))
1375    for iA,Aid in enumerate(Aids):
1376        GT = np.zeros((3,3))
1377        if abs(nAcof[iA]) > 1.e-8:
1378            GT[Aid[0],Aid[1]] = 1
1379            nGT = G2lat.prodMGMT(GT,Trans)
1380            nAT = G2lat.Gmat2A(nGT)
1381            for ia,nA in enumerate(nAT):
1382                if abs(nA) > 1.e-8 and abs(nAcof[ia]) > 1.e-8:
1383                    parm = SetUniqAj(npId,As[ia],nSGData['SGLaue'])
1384                    DepConsDict[Aid[2]].append([nA,G2obj.G2VarObj(parm)])
1385    for iA,Asi in enumerate(As):
1386        parm = SetUniqAj(opId,Asi,oSGData['SGLaue'])
1387        parmDict[parm] = oAcof[iA]
1388        varyList.append(parm)
1389        IndpCon = [1.0,G2obj.G2VarObj(parm)]
1390        if len(DepConsDict[Asi]) == 1:
1391            if DepConsDict[Asi][0]:
1392                constraints['Phase'].append([IndpCon,DepConsDict[Asi][0],None,None,'e'])
1393        elif len(DepConsDict[Asi]) > 1:       
1394            for Dep in DepConsDict[Asi]:
1395                Dep[0] *= -1
1396            constraints['Phase'].append([IndpCon]+DepConsDict[Asi]+[0.0,None,'c'])
1397    for hId,hist in enumerate(UseList):    #HAP - seems OK
1398        ohapkey = '%d:%d:'%(opId,hId)
1399        nhapkey = '%d:%d:'%(npId,hId)
1400        IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1401        DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1402        constraints['HAP'].append([DepCons,IndpCon,None,None,'e'])
1403        for name in ['Size;i','Mustrain;i']:
1404            IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1405            DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1406            constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1407       
1408################################################################################
1409#### Rigid bodies
1410################################################################################
1411
1412def UpdateRigidBodies(G2frame,data):
1413    '''Called when Rigid bodies tree item is selected.
1414    Displays the rigid bodies in the data window
1415    '''
1416    if not data.get('RBIds') or not data:
1417        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}},
1418            'RBIds':{'Vector':[],'Residue':[]}})       #empty/bad dict - fill it
1419           
1420    global resList,rbId
1421    Indx = {}
1422    resList = []
1423    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],}
1424
1425    G2frame.rbBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
1426    G2frame.dataWindow.GetSizer().Add(G2frame.rbBook,1,wx.ALL|wx.EXPAND)
1427    VectorRB = wx.ScrolledWindow(G2frame.rbBook)
1428    VectorRBDisplay = wx.Panel(VectorRB)
1429    G2frame.rbBook.AddPage(VectorRB,'Vector rigid bodies')
1430    ResidueRB = wx.ScrolledWindow(G2frame.rbBook)
1431    ResidueRBDisplay = wx.Panel(ResidueRB)
1432    G2frame.rbBook.AddPage(ResidueRB,'Residue rigid bodies')
1433   
1434    def OnPageChanged(event):
1435        global resList
1436        resList = []
1437        if event:       #page change event!
1438            page = event.GetSelection()
1439        else:
1440            page = G2frame.rbBook.GetSelection()
1441        G2frame.rbBook.ChangeSelection(page)
1442        text = G2frame.rbBook.GetPageText(page)
1443        if text == 'Vector rigid bodies':
1444            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.VectorBodyMenu)
1445            G2frame.Bind(wx.EVT_MENU, AddVectorRB, id=G2G.wxID_VECTORBODYADD)
1446            G2frame.Page = [page,'vrb']
1447            UpdateVectorRB()
1448        elif text == 'Residue rigid bodies':
1449            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
1450            G2frame.Bind(wx.EVT_MENU, AddResidueRB, id=G2G.wxID_RIGIDBODYADD)
1451            G2frame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2G.wxID_RIGIDBODYIMPORT)
1452            G2frame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2G.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist?
1453            G2frame.Page = [page,'rrb']
1454            UpdateResidueRB()
1455           
1456    def getMacroFile(macName):
1457        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
1458        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
1459            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
1460            style=wx.FD_OPEN | wx.FD_CHANGE_DIR)
1461        try:
1462            if dlg.ShowModal() == wx.ID_OK:
1463                macfile = dlg.GetPath()
1464                macro = open(macfile,'Ur')
1465                head = macro.readline()
1466                if macName not in head:
1467                    print (head)
1468                    print ('**** ERROR - wrong restraint macro file selected, try again ****')
1469                    macro = []
1470            else: # cancel was pressed
1471                macro = []
1472        finally:
1473            dlg.Destroy()
1474        return macro        #advanced past 1st line
1475       
1476    def getTextFile():
1477        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
1478            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
1479            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
1480            wx.FD_OPEN | wx.FD_CHANGE_DIR)
1481        try:
1482            if dlg.ShowModal() == wx.ID_OK:
1483                txtfile = dlg.GetPath()
1484                ext = os.path.splitext(txtfile)[1]
1485                text = open(txtfile,'Ur')
1486            else: # cancel was pressed
1487                ext = ''
1488                text = []
1489        finally:
1490            dlg.Destroy()
1491        if 'ent' in ext:
1492            ext = '.pdb'
1493        return text,ext.lower()
1494       
1495    def OnImportRigidBody(event):
1496        page = G2frame.rbBook.GetSelection()
1497        if 'Vector' in G2frame.rbBook.GetPageText(page):
1498            pass
1499        elif 'Residue' in G2frame.rbBook.GetPageText(page):
1500            ImportResidueRB()
1501           
1502    def AddVectorRB(event):
1503        AtInfo = data['Vector']['AtInfo']
1504        dlg = G2G.MultiIntegerDialog(G2frame,'New Rigid Body',['No. atoms','No. translations'],[1,1])
1505        if dlg.ShowModal() == wx.ID_OK:
1506            nAtoms,nTrans = dlg.GetValues()
1507            rbId = ran.randint(0,sys.maxsize)
1508            vecMag = [1.0 for i in range(nTrans)]
1509            vecRef = [False for i in range(nTrans)]
1510            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1511            rbTypes = ['C' for i in range(nAtoms)]
1512            Info = G2elem.GetAtomInfo('C')
1513            AtInfo['C'] = [Info['Drad'],Info['Color']]
1514            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1515                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1516            data['RBIds']['Vector'].append(rbId)
1517        dlg.Destroy()
1518        UpdateVectorRB()
1519       
1520    def AddResidueRB(event):
1521        AtInfo = data['Residue']['AtInfo']
1522        macro = getMacroFile('rigid body')
1523        if not macro:
1524            return
1525        macStr = macro.readline()
1526        while macStr:
1527            items = macStr.split()
1528            if 'I' == items[0]:
1529                rbId = ran.randint(0,sys.maxsize)
1530                rbName = items[1]
1531                rbTypes = []
1532                rbXYZ = []
1533                rbSeq = []
1534                atNames = []
1535                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1536                for iAtm in range(nAtms):
1537                    macStr = macro.readline().split()
1538                    atName = macStr[0]
1539                    atType = macStr[1]
1540                    atNames.append(atName)
1541                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1542                    rbTypes.append(atType)
1543                    if atType not in AtInfo:
1544                        Info = G2elem.GetAtomInfo(atType)
1545                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1546                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1547                for iSeq in range(nSeq):
1548                    macStr = macro.readline().split()
1549                    mSeq = int(macStr[0])
1550                    for jSeq in range(mSeq):
1551                        macStr = macro.readline().split()
1552                        iBeg = int(macStr[0])-1
1553                        iFin = int(macStr[1])-1
1554                        angle = 0.0
1555                        nMove = int(macStr[2])
1556                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1557                        rbSeq.append([iBeg,iFin,angle,iMove])
1558                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1559                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1560                    'SelSeq':[0,0],'useCount':0}
1561                data['RBIds']['Residue'].append(rbId)
1562                print ('Rigid body '+rbName+' added')
1563            macStr = macro.readline()
1564        macro.close()
1565        UpdateResidueRB()
1566       
1567    def ImportResidueRB():
1568        AtInfo = data['Residue']['AtInfo']
1569        text,ext = getTextFile()
1570        if not text:
1571            return
1572        rbId = ran.randint(0,sys.maxsize)
1573        rbTypes = []
1574        rbXYZ = []
1575        atNames = []
1576        txtStr = text.readline()
1577        if 'xyz' in ext:
1578            txtStr = text.readline()
1579            txtStr = text.readline()
1580        elif 'mol2' in ext:
1581            while 'ATOM' not in txtStr:
1582                txtStr = text.readline()
1583            txtStr = text.readline()
1584        elif 'pdb' in ext:
1585            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1586                txtStr = text.readline()
1587                #print txtStr
1588        items = txtStr.split()
1589        while len(items):
1590            if 'txt' in ext:
1591                atName = items[0]
1592                atType = items[1]
1593                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1594            elif 'xyz' in ext:
1595                atType = items[0]
1596                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1597                atName = atType+str(len(rbXYZ))
1598            elif 'mol2' in ext:
1599                atType = items[1]
1600                atName = items[1]+items[0]
1601                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1602            elif 'pdb' in ext:
1603                atType = items[-1]
1604                atName = items[2]
1605                xyz = txtStr[30:55].split()                   
1606                rbXYZ.append([float(x) for x in xyz])
1607            atNames.append(atName)
1608            rbTypes.append(atType)
1609            if atType not in AtInfo:
1610                Info = G2elem.GetAtomInfo(atType)
1611                AtInfo[atType] = [Info['Drad'],Info['Color']]
1612            txtStr = text.readline()
1613            if 'mol2' in ext and 'BOND' in txtStr:
1614                break
1615            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1616                break
1617            items = txtStr.split()
1618        if len(atNames) < 3:
1619            G2G.G2MessageBox(G2frame,'Not enough atoms in rigid body; must be 3 or more')
1620        else:
1621            rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1622            Xxyz = rbXYZ[1]
1623            X = Xxyz/np.sqrt(np.sum(Xxyz**2))
1624            Yxyz = rbXYZ[2]
1625            Y = Yxyz/np.sqrt(np.sum(Yxyz**2))
1626            Mat = G2mth.getRBTransMat(X,Y)
1627            rbXYZ = np.inner(Mat,rbXYZ).T
1628            data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1629                'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1630            data['RBIds']['Residue'].append(rbId)
1631            print ('Rigid body UNKRB added')
1632        text.close()
1633        UpdateResidueRB(rbId)
1634       
1635    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1636        Radii = []
1637        for Atype in atTypes:
1638            Radii.append(AtInfo[Atype][0])
1639        Radii = np.array(Radii)
1640        Neigh = []
1641        Dx = XYZ-XYZ[Orig]
1642        dist = np.sqrt(np.sum(Dx**2,axis=1))
1643        sumR = Radii[Orig]+Radii
1644        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1645        for j in IndB[0]:
1646            if j != Orig and atTypes[j] != 'H':
1647                Neigh.append(atNames[j])
1648        return Neigh
1649       
1650    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1651        NeighDict = {}
1652        for iat,xyz in enumerate(atNames):
1653            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1654        return NeighDict
1655       
1656    def FindRiding(Orig,Pivot,NeighDict):
1657        riding = [Orig,Pivot]
1658        iAdd = 1
1659        new = True
1660        while new:
1661            newAtms = NeighDict[riding[iAdd]]
1662            for At in newAtms:
1663                new = False
1664                if At not in riding:
1665                    riding.append(At)
1666                    new = True
1667            iAdd += 1
1668            if iAdd < len(riding):
1669                new = True
1670        return riding[2:]
1671                       
1672    def OnDefineTorsSeq(event):
1673        global rbId
1674        rbData = data['Residue'][rbId]
1675        if not len(rbData):
1676            return
1677        atNames = rbData['atNames']
1678        AtInfo = data['Residue']['AtInfo']
1679        atTypes = rbData['rbTypes']
1680        XYZ = rbData['rbXYZ']
1681        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1682        TargList = []           
1683        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1684        if dlg.ShowModal() == wx.ID_OK:
1685            Orig = dlg.GetSelection()
1686            TargList = neighDict[atNames[Orig]]
1687        dlg.Destroy()
1688        if not len(TargList):
1689            return
1690        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1691        if dlg.ShowModal() == wx.ID_OK:
1692            Piv = atNames.index(TargList[dlg.GetSelection()])
1693            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1694            Riding = []
1695            for atm in riding:
1696                Riding.append(atNames.index(atm))
1697            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1698        dlg.Destroy()
1699        UpdateResidueRB(rbId)
1700
1701    def UpdateVectorRB(Scroll=0):
1702        AtInfo = data['Vector']['AtInfo']
1703        refChoice = {}
1704        if 'DELETED' in str(G2frame.GetStatusBar()):   #seems to be no other way to do this (wx bug)
1705            if GSASIIpath.GetConfigValue('debug'):
1706                print ('DBG_wx error: Rigid Body/Status not cleanly deleted after Refine')
1707            return
1708        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1709        def rbNameSizer(rbId,rbData):
1710
1711            def OnRBName(event):
1712                event.Skip()
1713                Obj = event.GetEventObject()
1714                rbData['RBname'] = Obj.GetValue()
1715               
1716            def OnDelRB(event):
1717                Obj = event.GetEventObject()
1718                rbId = Indx[Obj.GetId()]
1719                if rbId in data['Vector']:
1720                    del data['Vector'][rbId]
1721                    data['RBIds']['Vector'].remove(rbId)
1722                    rbData['useCount'] -= 1
1723                wx.CallAfter(UpdateVectorRB)
1724               
1725            def OnPlotRB(event):
1726                Obj = event.GetEventObject()
1727                Obj.SetValue(False)
1728                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1729           
1730            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1731            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1732                0,wx.ALIGN_CENTER_VERTICAL)
1733            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1734            Indx[RBname.GetId()] = rbId
1735            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1736            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1737            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1738            nameSizer.Add((5,0),)
1739            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1740            Indx[plotRB.GetId()] = rbId
1741            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1742            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1743            nameSizer.Add((5,0),)
1744            if not rbData['useCount']:
1745                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1746                Indx[delRB.GetId()] = rbId
1747                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1748                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1749            return nameSizer
1750           
1751        def rbRefAtmSizer(rbId,rbData):
1752           
1753            def OnRefSel(event):
1754                Obj = event.GetEventObject()
1755                iref = Indx[Obj.GetId()]
1756                sel = Obj.GetValue()
1757                rbData['rbRef'][iref] = atNames.index(sel)
1758                FillRefChoice(rbId,rbData)
1759           
1760            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1761            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1762            rbRef = rbData.get('rbRef',[0,1,2,False])
1763            rbData['rbRef'] = rbRef
1764            if rbData['useCount']:
1765                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1766                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1767                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1768            else:
1769                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1770                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1771                for i in range(3):
1772                    choices = [atNames[j] for j in refChoice[rbId][i]]
1773                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1774                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1775                    refSel.SetValue(atNames[rbRef[i]])
1776                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1777                    Indx[refSel.GetId()] = i
1778                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1779            return refAtmSizer
1780                       
1781        def rbVectMag(rbId,imag,rbData):
1782           
1783            def OnRBVectorMag(event):
1784                event.Skip()
1785                Obj = event.GetEventObject()
1786                rbId,imag = Indx[Obj.GetId()]
1787                try:
1788                    val = float(Obj.GetValue())
1789                    if val <= 0.:
1790                        raise ValueError
1791                    rbData['VectMag'][imag] = val
1792                except ValueError:
1793                    pass
1794                Obj.SetValue('%8.4f'%(val))
1795                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1796                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1797               
1798            def OnRBVectorRef(event):
1799                Obj = event.GetEventObject()
1800                rbId,imag = Indx[Obj.GetId()]
1801                rbData['VectRef'][imag] = Obj.GetValue()
1802                       
1803            magSizer = wx.BoxSizer(wx.HORIZONTAL)
1804            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1805                0,wx.ALIGN_CENTER_VERTICAL)
1806            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1807            Indx[magValue.GetId()] = [rbId,imag]
1808            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1809            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1810            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1811            magSizer.Add((5,0),)
1812            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1813            magref.SetValue(rbData['VectRef'][imag])
1814            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1815            Indx[magref.GetId()] = [rbId,imag]
1816            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1817            return magSizer
1818           
1819        def rbVectors(rbId,imag,mag,XYZ,rbData):
1820
1821            def TypeSelect(event):
1822                AtInfo = data['Vector']['AtInfo']
1823                r,c = event.GetRow(),event.GetCol()
1824                if vecGrid.GetColLabelValue(c) == 'Type':
1825                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1826                    if PE.ShowModal() == wx.ID_OK:
1827                        if PE.Elem != 'None':
1828                            El = PE.Elem.strip().lower().capitalize()
1829                            if El not in AtInfo:
1830                                Info = G2elem.GetAtomInfo(El)
1831                                AtInfo[El] = [Info['Drad'],Info['Color']]
1832                            rbData['rbTypes'][r] = El
1833                            vecGrid.SetCellValue(r,c,El)
1834                    PE.Destroy()
1835                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1836
1837            def ChangeCell(event):
1838                r,c =  event.GetRow(),event.GetCol()
1839                if r >= 0 and (0 <= c < 3):
1840                    try:
1841                        val = float(vecGrid.GetCellValue(r,c))
1842                        rbData['rbVect'][imag][r][c] = val
1843                    except ValueError:
1844                        pass
1845                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1846                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1847
1848            vecSizer = wx.BoxSizer()
1849            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1850            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1851            table = []
1852            rowLabels = []
1853            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1854                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1855                rowLabels.append(str(ivec))
1856            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1857            vecGrid = G2G.GSGrid(VectorRBDisplay)
1858            vecGrid.SetTable(vecTable, True)
1859            if 'phoenix' in wx.version():
1860                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGED, ChangeCell)
1861            else:
1862                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1863            if not imag:
1864                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1865            attr = wx.grid.GridCellAttr()
1866            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1867            for c in range(3):
1868                vecGrid.SetColAttr(c, attr)
1869            for row in range(vecTable.GetNumberRows()):
1870                if imag:
1871                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1872                for col in [4,5,6]:
1873                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1874#            vecGrid.SetScrollRate(0,0)
1875            vecGrid.AutoSizeColumns(False)
1876            vecSizer.Add(vecGrid)
1877            return vecSizer
1878       
1879        def FillRefChoice(rbId,rbData):
1880            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1881           
1882            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1883            for i in range(3):
1884                choiceIds.remove(rbRef[i])
1885            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1886            for i in range(3):
1887                refChoice[rbId][i].append(rbRef[i])
1888                refChoice[rbId][i].sort()     
1889           
1890        if VectorRB.GetSizer(): VectorRB.GetSizer().Clear(True)
1891        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1892        for rbId in data['RBIds']['Vector']:
1893            if rbId != 'AtInfo':
1894                rbData = data['Vector'][rbId]
1895                FillRefChoice(rbId,rbData)
1896                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1897                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1898                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1899                for imag,mag in enumerate(rbData['VectMag']):
1900                    XYZ += mag*rbData['rbVect'][imag]
1901                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1902                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1903                VectorRBSizer.Add((5,5),0)
1904                data['Vector'][rbId]['rbXYZ'] = XYZ       
1905        VectorRBSizer.Layout()   
1906        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1907        Size = VectorRBSizer.GetMinSize()
1908        Size[0] += 40
1909        Size[1] = max(Size[1],450) + 20
1910        VectorRBDisplay.SetSize(Size)
1911        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1912        VectorRB.Scroll(0,Scroll)
1913       
1914    def UpdateResidueRB(rbId=0):
1915        AtInfo = data['Residue']['AtInfo']
1916        refChoice = {}
1917        RefObjs = []
1918
1919        def rbNameSizer(rbId,rbData):
1920
1921            def OnRBName(event):
1922                Obj = event.GetEventObject()
1923                rbData['RBname'] = Obj.GetValue()
1924                wx.CallAfter(UpdateResidueRB,rbId)
1925               
1926            def OnDelRB(event):
1927                Obj = event.GetEventObject()
1928                rbId = Indx[Obj.GetId()]
1929                if rbId in data['Residue']: 
1930                    del data['Residue'][rbId]
1931                    data['RBIds']['Residue'].remove(rbId)
1932                wx.CallAfter(UpdateResidueRB)
1933               
1934            def OnStripH(event):
1935                Obj = event.GetEventObject()
1936                rbId = Indx[Obj.GetId()]
1937                if rbId in data['Residue']:
1938                    newNames = []
1939                    newTypes = []
1940                    newXYZ = []
1941                    for i,atype in enumerate(rbData['rbTypes']):
1942                        if atype != 'H':
1943                            newNames.append(rbData['atNames'][i])
1944                            newTypes.append(rbData['rbTypes'][i])
1945                            newXYZ.append(rbData['rbXYZ'][i])
1946                    rbData['atNames'] = newNames
1947                    rbData['rbTypes'] = newTypes
1948                    rbData['rbXYZ'] = newXYZ
1949                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1950                wx.CallAfter(UpdateResidueRB,rbId)
1951                   
1952            def OnPlotRB(event):
1953                Obj = event.GetEventObject()
1954                Obj.SetValue(False)
1955                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1956           
1957            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1958            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1959                0,wx.ALIGN_CENTER_VERTICAL)
1960            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1961            Indx[RBname.GetId()] = rbId
1962            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1963            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1964            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1965            nameSizer.Add((5,0),)
1966            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1967            Indx[plotRB.GetId()] = rbId
1968            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1969            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1970            nameSizer.Add((5,0),)
1971            if not rbData['useCount']:
1972                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1973                Indx[delRB.GetId()] = rbId
1974                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1975                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1976                if 'H'  in rbData['rbTypes']:
1977                    stripH = wx.CheckBox(ResidueRBDisplay,-1,'Strip H-atoms?')
1978                    Indx[stripH.GetId()] = rbId
1979                    stripH.Bind(wx.EVT_CHECKBOX,OnStripH)
1980                    nameSizer.Add(stripH,0,wx.ALIGN_CENTER_VERTICAL)
1981            return nameSizer
1982           
1983        def rbResidues(rbId,rbData):
1984           
1985            def TypeSelect(event):
1986                AtInfo = data['Residue']['AtInfo']
1987                r,c = event.GetRow(),event.GetCol()
1988                if vecGrid.GetColLabelValue(c) == 'Type':
1989                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1990                    if PE.ShowModal() == wx.ID_OK:
1991                        if PE.Elem != 'None':
1992                            El = PE.Elem.strip().lower().capitalize()
1993                            if El not in AtInfo:
1994                                Info = G2elem.GetAtomInfo(El)
1995                                AtInfo[El] = [Info['Drad']['Color']]
1996                            rbData['rbTypes'][r] = El
1997                            vecGrid.SetCellValue(r,c,El)
1998                    PE.Destroy()
1999
2000            def ChangeCell(event):
2001                r,c =  event.GetRow(),event.GetCol()
2002                if r >= 0 and (0 <= c < 3):
2003                    try:
2004                        val = float(vecGrid.GetCellValue(r,c))
2005                        rbData['rbXYZ'][r][c] = val
2006                    except ValueError:
2007                        pass
2008                       
2009            def RowSelect(event):
2010                r,c =  event.GetRow(),event.GetCol()
2011                if c < 0:                   #only row clicks
2012                    for vecgrid in resList:
2013                        vecgrid.ClearSelection()
2014                    vecGrid.SelectRow(r,True)
2015
2016            def OnRefSel(event):
2017               
2018                Obj = event.GetEventObject()
2019                iref,res,jref = Indx[Obj.GetId()]
2020                sel = Obj.GetValue()
2021                ind = atNames.index(sel)
2022                if rbData['rbTypes'][ind] == 'H':
2023                    G2G.G2MessageBox(G2frame,'You should not select an H-atom for rigid body orientation')
2024                rbData['rbRef'][iref] = ind
2025                FillRefChoice(rbId,rbData)
2026                for i,ref in enumerate(RefObjs[jref]):
2027                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
2028                    ref.SetValue(atNames[rbData['rbRef'][i]])                   
2029                rbXYZ = rbData['rbXYZ']
2030                if not iref:     #origin change
2031                    rbXYZ -= rbXYZ[ind]
2032                #TODO - transform all atom XYZ by axis choices
2033                Xxyz = rbXYZ[rbData['rbRef'][1]]
2034                X = Xxyz/np.sqrt(np.sum(Xxyz**2))
2035                Yxyz = rbXYZ[rbData['rbRef'][2]]
2036                Y = Yxyz/np.sqrt(np.sum(Yxyz**2))
2037                Mat = G2mth.getRBTransMat(X,Y)
2038                rbXYZ = np.inner(Mat,rbXYZ).T
2039                rbData['rbXYZ'] = rbXYZ
2040                res.ClearSelection()
2041                resTable = res.GetTable()
2042                for r in range(res.GetNumberRows()):
2043                    row = resTable.GetRowValues(r)
2044                    row[2:4] = rbXYZ[r]
2045                    resTable.SetRowValues(r,row)
2046                res.ForceRefresh()
2047                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2048               
2049            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
2050            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
2051            table = []
2052            rowLabels = []
2053            for ivec,xyz in enumerate(rbData['rbXYZ']):
2054                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
2055                rowLabels.append(str(ivec))
2056            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
2057            vecGrid = G2G.GSGrid(ResidueRBDisplay)
2058            Indx[vecGrid.GetId()] = rbId
2059            resList.append(vecGrid)
2060            vecGrid.SetTable(vecTable, True)
2061            if 'phoenix' in wx.version():
2062                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGED, ChangeCell)
2063            else:
2064                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
2065            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
2066            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
2067            attr = wx.grid.GridCellAttr()
2068            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
2069            for c in range(3):
2070                vecGrid.SetColAttr(c, attr)
2071            for row in range(vecTable.GetNumberRows()):
2072                for col in range(5):
2073                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
2074            vecGrid.AutoSizeColumns(False)
2075            vecSizer = wx.BoxSizer()
2076            vecSizer.Add(vecGrid)
2077           
2078            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
2079            atNames = rbData['atNames']
2080            rbRef = rbData['rbRef']
2081            if rbData['rbRef'][3] or rbData['useCount']:
2082                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2083                    'Orientation reference non-H atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
2084                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
2085            else:
2086                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2087                    'Orientation reference non-H atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
2088                refObj = [0,0,0]
2089                for i in range(3):
2090                    choices = [atNames[j] for j in refChoice[rbId][i]]
2091                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
2092                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
2093                    refSel.SetValue(atNames[rbRef[i]])
2094                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
2095                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
2096                    refObj[i] = refSel
2097                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
2098                RefObjs.append(refObj)
2099           
2100            mainSizer = wx.BoxSizer(wx.VERTICAL)
2101            mainSizer.Add(refAtmSizer)
2102            mainSizer.Add(vecSizer)
2103            return mainSizer
2104           
2105        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
2106           
2107            def ChangeAngle(event):
2108                event.Skip()
2109                Obj = event.GetEventObject()
2110                rbId,Seq = Indx[Obj.GetId()][:2]
2111                val = Seq[2]
2112                try:
2113                    val = float(Obj.GetValue())
2114                    Seq[2] = val
2115                except ValueError:
2116                    pass
2117                Obj.SetValue('%8.2f'%(val))
2118                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
2119               
2120            def OnRadBtn(event):
2121                Obj = event.GetEventObject()
2122                Seq,iSeq,angId = Indx[Obj.GetId()]
2123                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
2124                angSlide.SetValue(int(100*Seq[2]))
2125               
2126            def OnDelBtn(event):
2127                Obj = event.GetEventObject()
2128                rbId,Seq = Indx[Obj.GetId()]
2129                data['Residue'][rbId]['rbSeq'].remove(Seq)       
2130                wx.CallAfter(UpdateResidueRB,rbId)
2131           
2132            seqSizer = wx.FlexGridSizer(0,5,2,2)
2133            seqSizer.AddGrowableCol(3,0)
2134            iBeg,iFin,angle,iMove = Seq
2135            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
2136            if not iSeq:
2137                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
2138                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
2139            else:
2140                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2141            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2142            seqSizer.Add(radBt)
2143            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2144            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2145            seqSizer.Add(delBt)
2146            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2147            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2148            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2149            Indx[delBt.GetId()] = [rbId,Seq]
2150            Indx[ang.GetId()] = [rbId,Seq,ang]
2151            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2152            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2153            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2154            atms = ''
2155            for i in iMove:   
2156                atms += ' %s,'%(atNames[i])
2157            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2158            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2159            return seqSizer
2160           
2161        def SlideSizer():
2162           
2163            def OnSlider(event):
2164                Obj = event.GetEventObject()
2165                rbData = Indx[Obj.GetId()]
2166                iSeq,angId = rbData['SelSeq']
2167                val = float(Obj.GetValue())/100.
2168                rbData['rbSeq'][iSeq][2] = val
2169                Indx[angId][2].SetValue('%8.2f'%(val))
2170                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2171           
2172            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2173            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2174            iSeq,angId = rbData['SelSeq']
2175            angSlide = wx.Slider(ResidueRBDisplay,-1,
2176                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2177                style=wx.SL_HORIZONTAL)
2178            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2179            Indx[angSlide.GetId()] = rbData
2180            slideSizer.Add(angSlide,0)           
2181            return slideSizer,angSlide
2182           
2183        def FillRefChoice(rbId,rbData):
2184            choiceIds = [i for i in range(len(rbData['atNames']))]
2185            for seq in rbData['rbSeq']:
2186                for i in seq[3]:
2187                    try:
2188                        choiceIds.remove(i)
2189                    except ValueError:
2190                        pass
2191            rbRef = rbData['rbRef']
2192            for i in range(3):
2193                try:
2194                    choiceIds.remove(rbRef[i])
2195                except ValueError:
2196                    pass
2197            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2198            for i in range(3):
2199                refChoice[rbId][i].append(rbRef[i])
2200                refChoice[rbId][i].sort()
2201               
2202        def OnSelect(event):
2203            rbname = rbchoice[select.GetSelection()]
2204            rbId = RBnames[rbname]
2205            wx.CallLater(100,UpdateResidueRB,rbId)
2206           
2207        GS = ResidueRBDisplay.GetSizer()
2208        if GS: 
2209            try:        #get around a c++ error in wx 4.0; doing is again seems to be OK
2210                GS.Clear(True)
2211            except:
2212                GS.Clear(True)
2213       
2214        RBnames = {}
2215        for rbid in data['RBIds']['Residue']:
2216            RBnames.update({data['Residue'][rbid]['RBname']:rbid,})
2217        if not RBnames:
2218            return
2219        rbchoice = list(RBnames.keys())
2220        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2221        if len(RBnames) > 1:
2222            selSizer = wx.BoxSizer(wx.HORIZONTAL)
2223            selSizer.Add(wx.StaticText(ResidueRBDisplay,label=' Select residue to view:'),0)
2224            rbchoice.sort()
2225            select = wx.ComboBox(ResidueRBDisplay,choices=rbchoice)
2226            select.Bind(wx.EVT_COMBOBOX,OnSelect)
2227            selSizer.Add(select,0)
2228            ResidueRBSizer.Add(selSizer,0)
2229        if not rbId:
2230            rbId = RBnames[rbchoice[0]]
2231        rbData = data['Residue'][rbId]
2232        FillRefChoice(rbId,rbData)
2233        ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2234        ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2235        ResidueRBSizer.Add((5,5),0)
2236        if rbData['rbSeq']:
2237            slideSizer,angSlide = SlideSizer()
2238        if len(rbData['rbSeq']):
2239            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2240                'Sel  Del  Bond             Angle      Riding atoms'),
2241                0,wx.ALIGN_CENTER_VERTICAL)                       
2242        for iSeq,Seq in enumerate(rbData['rbSeq']):
2243            ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2244        if rbData['rbSeq']:
2245            ResidueRBSizer.Add(slideSizer,)
2246
2247        ResidueRBSizer.Add((5,25),)
2248        ResidueRBSizer.Layout()   
2249        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2250        ResidueRBDisplay.SetAutoLayout(True)
2251        Size = ResidueRBSizer.GetMinSize()
2252        ResidueRBDisplay.SetSize(Size)
2253        ResidueRBDisplay.Show()
2254       
2255    def SetStatusLine(text):
2256        G2frame.GetStatusBar().SetStatusText(text,1)                                     
2257
2258    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
2259    SetStatusLine('')
2260    UpdateVectorRB()
2261    G2frame.rbBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2262    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.