source: trunk/GSASIIconstrGUI.py @ 3599

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

remove vars from constraints (see G2obj.removeNonRefined); new tutorial

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