Changeset 5038


Ignore:
Timestamp:
Sep 27, 2021 6:02:41 PM (8 months ago)
Author:
toby
Message:

Major revision to constraints including an update to the Sequential Refinement tutorial

Location:
trunk
Files:
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/GSASIIIO.py

    r4968 r5038  
    14531453        the active histogram being exported.
    14541454        '''
     1455        # TODO: this does not expand wild-card constraints properly for sequential fits.
     1456        # this needs revisiting for sequential exports if constraints need to be generated correctly.
     1457       
    14551458        self.G2frame.CheckNotebook()
    14561459        self.parmDict = {}
     
    14951498        # now process the constraints
    14961499        G2mv.InitVars()
    1497         constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
     1500        constrDict,fixedList,ignored = G2mv.ProcessConstraints(constList)
    14981501        varyList = covDict.get('varyListStart')
    1499         if varyList is None and len(constDict) == 0:
     1502        if varyList is None and len(constrDict) == 0:
    15001503            # no constraints can use varyList
    15011504            varyList = covDict.get('varyList')
     
    15101513            G2gd.GetGPXtreeItemId(self.G2frame,self.G2frame.root,'Rigid bodies'))
    15111514        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
    1512         rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
     1515        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)  # done twice, needed?
    15131516        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
    15141517            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
    1515         msg = G2mv.EvaluateMultipliers(constDict,phaseDict)
     1518        msg = G2mv.EvaluateMultipliers(constrDict,phaseDict)
    15161519        if msg:
    15171520            print('Unable to interpret multiplier(s): '+msg)
    15181521            raise Exception(' *** CIF creation aborted ***')
    1519         try:
    1520             G2mv.GenerateConstraints(varyList,constDict,fixedList,self.parmDict)
    1521             #print(G2mv.VarRemapShow(varyList))
    1522         except:
     1522        errmsg,warnmsg,groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,self.parmDict)
     1523        if errmsg:
    15231524            # this really should not happen
    15241525            print (' *** ERROR - constraints are internally inconsistent ***')
    1525             errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
    1526             print ('Errors'+errmsg)
     1526            print ('Errors: ',errmsg)
    15271527            if warnmsg: print ('Warnings'+warnmsg)
    15281528            raise Exception(' *** CIF creation aborted ***')
    1529         # add the constrained values to the parameter dictionary
    1530         G2mv.Dict2Map(self.parmDict,varyList)
     1529        G2mv.Map2Dict(self.parmDict,varyList)   # changes varyList
     1530        G2mv.Dict2Map(self.parmDict)   # add the constrained values to the parameter dictionary
    15311531        # and add their uncertainties into the esd dictionary (sigDict)
    15321532        if covDict.get('covMatrix') is not None:
  • trunk/GSASIIconstrGUI.py

    r5002 r5038  
    363363       
    364364#####  Constraints ################################################################################           
    365 def UpdateConstraints(G2frame,data):
     365def CheckConstraints(G2frame,Phases,Histograms,data,newcons=[],reqVaryList=None,seqhst=None,seqmode='use-all'):
     366    '''Load constraints & check them for errors.
     367
     368    N.B. Equivalences based on symmetry (etc.)
     369    are generated by running :func:`GSASIIstrIO.GetPhaseData`.
     370
     371    When reqVaryList is included (see WarnConstraintLimit) then
     372    parameters with limits are checked against constraints and a
     373    warning is shown.
     374    '''
     375    G2mv.InitVars()
     376    #Find all constraints
     377    constrDict = []
     378    for key in data:
     379        if key.startswith('_'): continue
     380        constrDict += data[key]
     381    if newcons:
     382        constrDict = constrDict + newcons
     383    constrDict, fixedList, ignored = G2mv.ProcessConstraints(constrDict, seqhst=seqhst, seqmode=seqmode)
     384    parmDict = {}
     385    # generate symmetry constraints to check for conflicts
     386    rigidbodyDict = G2frame.GPXtree.GetItemPyData(
     387            G2gd.GetGPXtreeItemId(G2frame, G2frame.root, 'Rigid bodies'))
     388    rbIds = rigidbodyDict.get('RBIds', {'Vector': [], 'Residue': []})
     389    rbVary, rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict, Print=False)
     390    parmDict.update(rbDict)
     391    (Natoms, atomIndx, phaseVary, phaseDict, pawleyLookup, FFtables,
     392         BLtables, MFtables, maxSSwave) = G2stIO.GetPhaseData(
     393            Phases, RestraintDict=None, rbIds=rbIds, Print=False) # generates atom symmetry constraints
     394    parmDict.update(phaseDict)
     395    # get Hist and HAP info
     396    hapVary, hapDict, controlDict = G2stIO.GetHistogramPhaseData(
     397            Phases, Histograms, Print=False, resetRefList=False)
     398    parmDict.update(hapDict)
     399    histVary, histDict, controlDict = G2stIO.GetHistogramData(Histograms, Print=False)
     400    parmDict.update(histDict)
     401   
     402    # TODO: twining info needed?
     403    #TwConstr,TwFixed = G2stIO.makeTwinFrConstr(Phases,Histograms,hapVary)
     404    #constrDict += TwConstr
     405    #fixedList += TwFixed
     406    varyList = rbVary+phaseVary+hapVary+histVary
     407
     408    msg = G2mv.EvaluateMultipliers(constrDict,parmDict)
     409    if msg:
     410        return 'Unable to interpret multiplier(s): '+msg,''
     411    if reqVaryList:
     412        varyList = reqVaryList[:]
     413    errmsg,warnmsg,groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict) # changes varyList
     414   
     415    impossible = []
     416    if reqVaryList:
     417        Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
     418        for key in ('parmMinDict','parmMaxDict','parmFrozen'):
     419            if key not in Controls: Controls[key] = {}
     420        G2mv.Map2Dict(parmDict,varyList)   # changes varyList
     421        # check for limits on dependent vars
     422        consVars = [i for i in reqVaryList if i not in varyList]
     423        impossible = set(
     424                [str(i) for i in Controls['parmMinDict'] if i in consVars] +
     425                [str(i) for i in Controls['parmMaxDict'] if i in consVars])
     426        if impossible:
     427            msg = ''
     428            for i in sorted(impossible):
     429                if msg: msg += ', '
     430                msg += i
     431            msg =  ' &'.join(msg.rsplit(',',1))
     432            msg = ('Note: limits on variable(s) '+msg+
     433            ' will be ignored because they are constrained.')
     434            G2G.G2MessageBox(G2frame,msg,'Limits ignored for constrained vars')
     435    else:
     436        G2mv.Map2Dict(parmDict,varyList)   # changes varyList
     437    return errmsg,warnmsg
     438
     439def UpdateConstraints(G2frame, data, selectTab=None, Clear=False):
    366440    '''Called when Constraints tree item is selected.
    367441    Displays the constraints in the data window
     
    642716            dlg.Destroy()
    643717        return []
    644    
    645     def ConstraintsLoad(data,newcons=[]):
    646         '''Load all constraints. Constraints based on symmetry (etc.)
    647         are generated by running :func:`GSASIIstrIO.GetPhaseData`.
    648         '''
    649         G2mv.InitVars()
    650         #Find all constraints
    651         constraintSet = []
    652         for key in data:
    653             if key.startswith('_'): continue
    654             constraintSet += data[key]
    655         if newcons:
    656             constraintSet = constraintSet + newcons
    657         constDictList,fixedList,ignored = G2stIO.ProcessConstraints(constraintSet)
    658         # generate symmetry constraints to check for conflicts
    659         rigidbodyDict = G2frame.GPXtree.GetItemPyData(   
    660             G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
    661         rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
    662         rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
    663         (Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,
    664              BLtables,MFtables,maxSSwave) = G2stIO.GetPhaseData(
    665                  Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
    666         return constDictList,phaseDict,fixedList
    667            
    668     def ConstraintsCheck(data,newcons=[],reqVaryList=None):
    669         '''Load constraints & check them for errors. Since error checking
    670         can cause changes in constraints in case of repairable conflicts
    671         between equivalences, reload the constraints again after the check.
    672         This could probably be done more effectively, but only reloading when
    673         needed, but the reload should be quick.
    674        
    675         When reqVaryList is included (see WarnConstraintLimit) then
    676         parameters with limits are checked against constraints and a
    677         warning is shown.
    678         '''
    679         constDictList,phaseDict,fixedList = ConstraintsLoad(data,newcons)
    680         msg = G2mv.EvaluateMultipliers(constDictList,phaseDict)
    681         if msg:
    682             return 'Unable to interpret multiplier(s): '+msg,''
    683         res = G2mv.CheckConstraints('',constDictList,fixedList)
    684         # reload constraints in case any were merged in MoveConfEquiv
    685         constDictList,phaseDict,fixedList = ConstraintsLoad(data,newcons)
    686         impossible = []
    687         if reqVaryList:
    688             Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls'))
    689             for key in ('parmMinDict','parmMaxDict','parmFrozen'):
    690                 if key not in Controls: Controls[key] = {}
    691             varyList = reqVaryList[:]
    692             try:
    693                 G2mv.GenerateConstraints(varyList,constDictList,fixedList,phaseDict)
    694                 G2mv.Map2Dict(phaseDict,varyList)
    695                 # check for limits on dependent vars
    696                 consVars = [i for i in reqVaryList if i not in varyList]
    697                 impossible = set(
    698                     [str(i) for i in Controls['parmMinDict'] if i in consVars] +
    699                     [str(i) for i in Controls['parmMaxDict'] if i in consVars])
    700             except G2mv.ConstraintException:
    701                 pass
    702         if impossible:
    703             msg = ''
    704             for i in sorted(impossible):
    705                 if msg: msg += ', '
    706                 msg += i
    707             msg =  ' &'.join(msg.rsplit(',',1))
    708             msg = ('Note: limits on variable(s) '+msg+
    709             ' will be ignored because they are constrained.')
    710             G2G.G2MessageBox(G2frame,msg,'Limits ignored for constrained vars')
    711         return res
    712 
     718               
    713719    def CheckAddedConstraint(newcons):
    714720        '''Check a new constraint that has just been input.
     
    721727        '''
    722728       
    723         errmsg,warnmsg = ConstraintsCheck(data,newcons)
     729        errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,newcons,seqhst=seqhistnum,seqmode=seqmode)
    724730        if errmsg:
    725731            G2frame.ErrorDialog('Constraint Error',
     
    727733                '\nIgnoring newly added constraint',parent=G2frame)
    728734            # reset error status
    729             errmsg,warnmsg = ConstraintsCheck(data)
     735            errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,seqhst=seqhistnum,seqmode=seqmode)
    730736            if errmsg:
    731737                print (errmsg)
     
    740746        Displays a warning message, but does nothing
    741747        '''
    742        
     748        parmDict,reqVaryList = G2frame.MakeLSParmDict()
    743749        try:
    744             parmDict,reqVaryList = G2frame.MakeLSParmDict()
    745             errmsg,warnmsg = ConstraintsCheck(data,[],reqVaryList)
    746         except:
    747             print('Error retrieving parameters')           
    748 
     750            errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,[],reqVaryList,seqhst=seqhistnum,seqmode=seqmode)
     751        except Exception as msg:
     752            return 'CheckConstraints error retrieving parameter\nError='+str(msg),''
     753        return errmsg,warnmsg
    749754
    750755    def CheckChangedConstraint():
     
    757762        :returns: True if the edit should be retained
    758763        '''
    759         errmsg,warnmsg = ConstraintsCheck(data)
     764        errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,seqhst=seqhistnum,seqmode=seqmode)
    760765        if errmsg:
    761766            G2frame.ErrorDialog('Constraint Error',
     
    763768                '\nDiscarding last constraint edit',parent=G2frame)
    764769            # reset error status
    765             errmsg,warnmsg = ConstraintsCheck(data)
     770            errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,seqhst=seqhistnum,seqmode=seqmode)
    766771            if errmsg:
    767772                print (errmsg)
     
    10391044        wx.CallAfter(OnPageChanged,None)
    10401045                       
    1041     def MakeConstraintsSizer(name,pageDisplay):
     1046    def MakeConstraintsSizer(name,panel):
    10421047        '''Creates a sizer displaying all of the constraints entered of
    10431048        the specified type.
     
    10451050        :param str name: the type of constraints to be displayed ('HAP',
    10461051          'Hist', 'Phase', 'Global', 'Sym-Generated')
    1047         :param wx.Panel pageDisplay: parent panel for sizer
     1052        :param wx.Panel panel: parent panel for sizer
    10481053        :returns: wx.Sizer created by method
    10491054        '''
     
    10511056            Sizer1 =  wx.BoxSizer(wx.VERTICAL)
    10521057            if symHolds:
    1053                 Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
     1058                Sizer1.Add(wx.StaticText(panel,wx.ID_ANY,
    10541059                    'Position variables fixed by space group symmetry'))
    10551060                Sizer1.Add((-1,5))
    1056                 Sizer = wx.FlexGridSizer(0,2,0,0)
     1061                Sizer = wx.FlexGridSizer(0,3,0,0)
    10571062                Sizer1.Add(Sizer)
    10581063                for var in symHolds:
    1059                     Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,'  FIXED'),
    1060                               0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
    1061                     Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,var))
    1062                     Sizer.Add((-1,-1))
    1063                     Sizer.Add((-1,2))
     1064                    varMean = G2obj.fmtVarDescr(var)
     1065                    helptext = "Prevents variable: "+ var + " ("+ varMean + ")\nfrom being changed"
     1066                    ch = G2G.HelpButton(panel,helptext)
     1067                    Sizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
     1068                    Sizer.Add(wx.StaticText(panel,wx.ID_ANY,'FIXED'),
     1069                              0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,2)
     1070                    Sizer.Add(wx.StaticText(panel,wx.ID_ANY,var),
     1071                              0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT)
    10641072            else:
    1065                 Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
     1073                Sizer1.Add(wx.StaticText(panel,wx.ID_ANY,
    10661074                    'No holds generated'))
    10671075            Sizer1.Add((-1,10))
    1068             symGen = G2mv.GetSymEquiv()
     1076            symGen,SymErr,SymHelp = G2mv.GetSymEquiv()
    10691077            if len(symGen) == 0:
    1070                 Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
    1071                     'No equvalences generated'))
     1078                Sizer1.Add(wx.StaticText(panel,wx.ID_ANY,
     1079                    'No equivalences generated'))
    10721080                return Sizer1
    1073             Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
     1081            Sizer1.Add(wx.StaticText(panel,wx.ID_ANY,
    10741082                'Equivalences generated based on cell/space group input'))
    10751083            Sizer1.Add((-1,5))
    1076             Sizer = wx.FlexGridSizer(0,2,0,0)
     1084            Sizer = wx.FlexGridSizer(0,5,0,0)
    10771085            Sizer1.Add(Sizer)
    1078             for sym in symGen:
    1079                 Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,'  EQUIV'),
    1080                     0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
    1081                 Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,sym))
     1086            helptext = ''
     1087            for sym,(warnmsg,note),helptext in zip(symGen,SymErr,SymHelp):
     1088                if warnmsg:
     1089                    if helptext: helptext += '\n\n'
     1090                    helptext += warnmsg
     1091                if helptext:
     1092                    ch = G2G.HelpButton(panel,helptext)
     1093                    Sizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
     1094                else:
     1095                    Sizer.Add((-1,-1))
     1096                Sizer.Add(wx.StaticText(panel,wx.ID_ANY,'EQUIV'),
     1097                    0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,2)
     1098                Sizer.Add(wx.StaticText(panel,wx.ID_ANY,sym),
     1099                                  0,WACV|wx.ALIGN_LEFT|wx.RIGHT|wx.LEFT,2)
     1100                if note:
     1101                    Sizer.Add(wx.StaticText(panel,wx.ID_ANY,note),
     1102                                  0,WACV|wx.ALIGN_LEFT|wx.RIGHT|wx.LEFT,2)
     1103                else:
     1104                    Sizer.Add((-1,-1))
    10821105                Sizer.Add((-1,-1))
    1083                 Sizer.Add((-1,2))
    10841106            return Sizer1
    1085         constSizer = wx.FlexGridSizer(0,6,0,0)
     1107        constSizer = wx.FlexGridSizer(0,8,0,0)
    10861108        maxlen = 50 # characters before wrapping a constraint
    10871109        for Id,item in enumerate(data[name]):
     
    10891111            helptext = ""
    10901112            eqString = ['',]
    1091             problemItem = False
    1092             badVar = False
    1093             for term in item[:-3]:
    1094                 if str(term[1]) in G2mv.problemVars:
    1095                     problemItem = True
     1113            problemItem, warnmsg, note = G2mv.getConstrError(item,seqmode,seqhistnum)
     1114            #badVar = False
     1115            #for term in item[:-3]:
     1116            #    if str(term[1]) in G2mv.problemVars:
     1117            #        problemItem = True
    10961118            if item[-1] == 'h': # Hold on variable
    10971119                constSizer.Add((-1,-1),0)              # blank space for edit button
    10981120                typeString = 'FIXED'
    1099                 var = str(item[0][1])
    1100                 if '?' in var: badVar = True
     1121                #var = str(item[0][1])
     1122                var,explain,note,warnmsg = item[0][1].fmtVarByMode(seqmode,note,warnmsg)
     1123                #if '?' in var: badVar = True
    11011124                varMean = G2obj.fmtVarDescr(var)
    11021125                eqString[-1] =  var +'   '
    11031126                helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed"
    11041127            elif item[-1] == 'f' or item[-1] == 'e' or item[-1] == 'c': # not true on original-style (2011?) constraints
    1105                 constEdit = wx.Button(pageDisplay,wx.ID_ANY,'Edit',style=wx.BU_EXACTFIT)
     1128                constEdit = wx.Button(panel,wx.ID_ANY,'Edit',style=wx.BU_EXACTFIT)
    11061129                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
    11071130                constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)            # edit button
     
    11131136                    helptext += " is created from a linear combination of the following variables:\n"
    11141137                    for term in item[:-3]:
    1115                         var = str(term[1])
    1116                         if '?' in var: badVar = True
     1138                        #var = str(term[1])
     1139                        var,explain,note,warnmsg = term[1].fmtVarByMode(seqmode,note,warnmsg)
     1140                        #if '?' in var: badVar = True
    11171141                        if len(eqString[-1]) > maxlen:
    11181142                            eqString.append(' ')
     
    11291153                            eqString[-1] += '{:g}*{:} '.format(m,var)
    11301154                        varMean = G2obj.fmtVarDescr(var)
    1131                         helptext += '\n{:3g} * {:} '.format(m,var) + " ("+ varMean + ")"
     1155                        helptext += '\n  {:3g} * {:} '.format(m,var) + " ("+ varMean + ")"
    11321156                    # Add ISODISTORT help items
    11331157                    if '_Explain' in data:
    1134                         # this ignores the phase number. TODO: refactor that in
     1158                        # this ignores the phase number. TODO: refactor that in?
    11351159                        hlptxt = None
    11361160                        try:
     
    11401164                            hlptxt = data['_Explain'].get(str(item[-3].phase)+item[-3].name)
    11411165                        if hlptxt:
    1142                             helptext += '\n\n'
     1166                            if helptext: helptext += '\n\nNote warning:\n'
    11431167                            helptext += hlptxt
    11441168                    if item[-3]:
     
    11491173                    refineflag = True
    11501174                elif item[-1] == 'c':
    1151                     helptext = "The following variables constrained to equal a constant:"
     1175                    helptext = "The following variables are constrained to equal a constant:"
    11521176                    for term in item[:-3]:
    1153                         var = str(term[1])
    1154                         if '?' in var: badVar = True
     1177                        #var = str(term[1])
     1178                        var,explain,note,warnmsg = term[1].fmtVarByMode(seqmode,note,warnmsg)
     1179                        #if '?' in var: badVar = True
    11551180                        if len(eqString[-1]) > maxlen:
    11561181                            eqString.append(' ')
     
    11671192                            eqString[-1] += '{:g}*{:} '.format(m,var)
    11681193                        varMean = G2obj.fmtVarDescr(var)
    1169                         helptext += '\n{:3g} * {:} '.format(m,var) + " ("+ varMean + ")"
     1194                        helptext += '\n  {:3g} * {:} '.format(m,var) + " ("+ varMean + ")"
     1195                        helptext += explain
    11701196                    typeString = 'CONST'
    11711197                    eqString[-1] += ' = '+str(item[-3])
     
    11731199                    if item[0][0] == 0: item[0][0] = 1.0
    11741200                    if item[1][0] == 0: item[1][0] = 1.0
    1175                     var = str(item[0][1])
    1176                     if '?' in var: badVar = True
     1201                    #var = str(item[0][1])
     1202                    var,explain,note,warnmsg = item[0][1].fmtVarByMode(seqmode,note,warnmsg)
     1203                    #if '?' in var: badVar = True
    11771204                    helptext = 'Variable {:} '.format(var) + " ("+ G2obj.fmtVarDescr(var) + ")"
    11781205                    helptext += "\n\nis equivalent to "
    11791206                    m = item[0][0]/item[1][0]
    1180                     var1 = str(item[1][1])
    1181                     helptext += '\n{:3g} * {:} '.format(m,var1) + " ("+ G2obj.fmtVarDescr(var1) + ")"
     1207                    #var1 = str(item[1][1])
     1208                    var1,explain,note,warnmsg = item[1][1].fmtVarByMode(seqmode,note,warnmsg)
     1209                    helptext += '\n  {:3g} * {:} '.format(m,var1) + " ("+ G2obj.fmtVarDescr(var1) + ")"
    11821210                    eqString[-1] += '{:} = {:}'.format(var1,var)
    11831211                    if m != 1:
     
    11891217                    indepterm = item[0][1]
    11901218                    for i,term in enumerate(item[:-3]):
    1191                         var = str(term[1])
    1192                         if '?' in var: badVar = True
     1219                        #var = str(term[1])
     1220                        var,explain,note,warnmsg = term[1].fmtVarByMode(seqmode,note,warnmsg)
     1221                        #if '?' in var: badVar = True
    11931222                        if term[0] == 0: term[0] = 1.0
    11941223                        if len(eqString[-1]) > maxlen:
     
    12071236                        else:
    12081237                            eqString[-1] += '{:g}*{:} '.format(m,var)
    1209                         helptext += '\n{:3g} * {:} '.format(m,var) + " ("+ varMean + ")"
     1238                        helptext += '\n  {:3g} * {:} '.format(m,var) + " ("+ varMean + ")"
    12101239                    eqString[-1] += ' = {:} '.format(indepterm)
    12111240                    typeString = 'EQUIV'
     
    12171246                data[name] = []
    12181247                return constSizer
    1219             constDel = wx.Button(pageDisplay,wx.ID_ANY,'Delete',style=wx.BU_EXACTFIT)
    1220             constDel.Bind(wx.EVT_BUTTON,OnConstDel)
    1221             Indx[constDel.GetId()] = [Id,name]
    1222             constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)             # delete button
     1248            if warnmsg:
     1249                if helptext: helptext += '\n\nNote warning:\n'
     1250                helptext += warnmsg
    12231251            if helptext:
    1224                 ch = G2G.HelpButton(pageDisplay,helptext)
     1252                ch = G2G.HelpButton(panel,helptext)
    12251253                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
    12261254            else:
    12271255                constSizer.Add((-1,-1))
     1256            constDel = wx.CheckBox(panel,label='sel ')
     1257            constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) # delete selection
     1258            panel.delBtn.checkboxList.append([constDel,Id,name])
    12281259            if refineflag:
    1229                 ch = G2G.G2CheckBox(pageDisplay,'',item,-2)
     1260                ch = G2G.G2CheckBox(panel,'vary ',item,-2)
    12301261                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
    12311262            else:
    1232                 constSizer.Add((-1,-1))               
    1233             constSizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,typeString),
     1263                constSizer.Add((-1,-1))
     1264            constSizer.Add(wx.StaticText(panel,wx.ID_ANY,typeString),
    12341265                           0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
    1235             if badVar: eqString[-1] += ' -- Error: variable removed'
    1236             if problemItem: eqString[-1] += ' -- Conflict: see console'
     1266            #if badVar: eqString[-1] += ' -- Error: variable removed'
     1267            #if note: eqString[-1] += '  (' + note + ')'
    12371268            if len(eqString) > 1:
    12381269                Eq = wx.BoxSizer(wx.VERTICAL)
    12391270                for s in eqString:
    1240                     line = wx.StaticText(pageDisplay,wx.ID_ANY,s)
     1271                    line = wx.StaticText(panel,wx.ID_ANY,s)
    12411272                    if problemItem: line.SetBackgroundColour(wx.YELLOW)
    12421273                    Eq.Add(line,0)
    12431274                Eq.Add((-1,4))
    12441275            else:
    1245                 Eq = wx.StaticText(pageDisplay,wx.ID_ANY,eqString[0])
    1246                 if problemItem or badVar: Eq.SetBackgroundColour(wx.YELLOW)
     1276                Eq = wx.StaticText(panel,wx.ID_ANY,eqString[0])
     1277                if problemItem: Eq.SetBackgroundColour(wx.YELLOW)
    12471278            constSizer.Add(Eq,1,WACV)
     1279            constSizer.Add((3,3))
     1280            if note:
     1281                Eq = wx.StaticText(panel,wx.ID_ANY,note)
     1282                if problemItem: Eq.SetBackgroundColour(wx.YELLOW)
     1283            else:
     1284                Eq = (-1,-1)
     1285            constSizer.Add(Eq,1,WACV,3)
     1286        if panel.delBtn.checkboxList:
     1287            panel.delBtn.Enable(True)
     1288        else:
     1289            panel.delBtn.Enable(False)
    12481290        return constSizer
    1249                
     1291
    12501292    def OnConstDel(event):
    1251         'Delete a constraint'
    1252         Obj = event.GetEventObject()
    1253         Id,name = Indx[Obj.GetId()]
    1254         del data[name][Id]
    1255         ConstraintsLoad(data)
    1256         wx.CallAfter(OnPageChanged,None)
     1293        'Delete selected constraints'
     1294        sel = G2frame.constr.GetSelection()
     1295        delList = []
     1296        for obj,Id,name in event.GetEventObject().checkboxList:
     1297            if obj.GetValue(): del data[name][Id]
     1298        wx.CallAfter(UpdateConstraints,G2frame,data,sel,True)
    12571299
    12581300    def OnConstEdit(event):
     
    12611303        '''
    12621304        Obj = event.GetEventObject()
     1305        sel = G2frame.constr.GetSelection()
    12631306        Id,name = Indx[Obj.GetId()]
    12641307        if data[name][Id][-1] == 'f':
     
    13161359        finally:
    13171360            dlg.Destroy()
    1318         wx.CallAfter(OnPageChanged,None)
     1361#        wx.CallAfter(OnPageChanged,None)
     1362        G2frame.dataWindow.ClearData()
     1363        wx.CallAfter(UpdateConstraints,G2frame,data,sel,True)
    13191364   
    13201365    def UpdateConstraintPanel(panel,typ):
     
    13251370        Siz = wx.BoxSizer(wx.VERTICAL)
    13261371        Siz.Add((5,5),0)
     1372        if typ != 'Sym-Generated':
     1373            butSizer = wx.BoxSizer(wx.HORIZONTAL)
     1374            btn = wx.Button(panel, wx.ID_ANY, 'Show Errors')
     1375            btn.Bind(wx.EVT_BUTTON,lambda event: G2G.ShowScrolledInfo(panel,errmsg,header='Error info'))
     1376            butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL)
     1377            btn.Enable(len(errmsg) > 0)
     1378            btn = wx.Button(panel, wx.ID_ANY, 'Show Warnings')
     1379            butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL)
     1380            btn.Bind(wx.EVT_BUTTON,lambda event: G2G.ShowScrolledInfo(panel,warnmsg))
     1381            btn.Enable(len(warnmsg) > 0)
     1382            btn = wx.Button(panel, wx.ID_ANY, 'Show generated constraints')
     1383            butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL)
     1384            btn.Bind(wx.EVT_BUTTON,lambda event:
     1385                         G2G.ShowScrolledInfo(panel,
     1386                        'Constraints after processing\n\n'+G2mv.VarRemapShow(),
     1387                         header='Generated constraints'))
     1388            panel.delBtn = wx.Button(panel, wx.ID_ANY, 'Delete selected')
     1389            butSizer.Add(panel.delBtn,0,wx.ALIGN_CENTER_VERTICAL)
     1390            panel.delBtn.Bind(wx.EVT_BUTTON,OnConstDel)
     1391            panel.delBtn.checkboxList = []
     1392            if G2frame.testSeqRefineMode():
     1393                Siz.Add(butSizer)
     1394                butSizer = wx.BoxSizer(wx.HORIZONTAL)
     1395                butSizer.Add(wx.StaticText(panel,wx.ID_ANY,'  Sequential Ref. Settings.  Wildcard use: '),0,WACV)
     1396                btn = G2G.EnumSelector(panel, data, '_seqmode',
     1397                        ['Set hist # to *', 'Ignore unless hist=*', 'Use as supplied'],
     1398                        ['auto-wildcard',   'wildcards-only',       'use-all'],
     1399                        lambda x: wx.CallAfter(UpdateConstraints, G2frame, data, G2frame.constr.GetSelection(), True))
     1400                butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL)
     1401                butSizer.Add(G2G.HelpButton(panel,helpIndex='Constraints-SeqRef'))
     1402                butSizer.Add(wx.StaticText(panel,wx.ID_ANY,'  Selected histogram: '),0,WACV)
     1403                btn = G2G.EnumSelector(panel, data, '_seqhist',
     1404                        list(seqHistList),list(range(len(seqHistList))),
     1405                        lambda x: wx.CallAfter(UpdateConstraints, G2frame, data, G2frame.constr.GetSelection(), True))
     1406                butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL)
     1407            Siz.Add(butSizer)
     1408            G2G.HorizontalLine(Siz,panel)
     1409#            Siz.Add((5,5),0)
    13271410        Siz.Add(MakeConstraintsSizer(typ,panel),1,wx.EXPAND)
    13281411        panel.SetSizer(Siz,True)
     
    13341417        panel.Show()
    13351418
    1336     def OnPageChanged(event):
     1419    def OnPageChanged(event,selectTab=None):
    13371420        '''Called when a tab is pressed or when a "select tab" menu button is
    13381421        used (see RaisePage), or to refresh the current tab contents (event=None)
     
    13401423        if event:       #page change event!
    13411424            page = event.GetSelection()
     1425        elif selectTab: #reload previous
     1426            page = selectTab
    13421427        else: # called directly, get current page
    13431428            try:
     
    14021487
    14031488    #### UpdateConstraints execution starts here ##############################
    1404     if not data:
    1405         data.update({'Hist':[],'HAP':[],'Phase':[],'Global':[]})       #empty dict - fill it
     1489    if Clear:
     1490        G2frame.dataWindow.ClearData()
     1491    if not data:  # usually created in CheckNotebook
     1492        data.update({'Hist':[],'HAP':[],'Phase':[],'Global':[],
     1493                         '_seqmode':'auto-wildcard', '_seqhist':0})       #empty dict - fill it
    14061494    if 'Global' not in data:                                            #patch
    14071495        data['Global'] = []
     1496    seqHistList = G2frame.testSeqRefineMode()
     1497    # patch: added ~version 5030 -- new mode for wild-card use in seq refs
     1498    # note that default for older sequential fits is 'wildcards-only' but in new GPX is 'auto-wildcard'
     1499    if seqHistList:
     1500        data['_seqmode'] = data.get('_seqmode','wildcards-only')
     1501    else:
     1502        data['_seqmode'] = data.get('_seqmode','auto-wildcard')
     1503    data['_seqhist'] = data.get('_seqhist',0)
     1504    # end patch
     1505   
    14081506    # DEBUG code #=====================================
    14091507    #import GSASIIconstrGUI
     
    14161514    #reload(G2gd)
    14171515    #===================================================
     1516    seqmode = 'use-all'
     1517    seqhistnum = None
     1518    if seqHistList:  # Selections used with sequential refinements
     1519        seqmode = data.get('_seqmode','wildcards-only')
     1520        seqhistnum = min(data.get('_seqhist',0),len(seqHistList)-1)
    14181521    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
    14191522    if not len(Phases) or not len(Histograms):
     
    14821585             
    14831586    # create a list of the hist*phase variables
    1484     seqList = G2frame.testSeqRefineMode()
    1485     if seqList: # for sequential refinement, only process 1st histgram in list
    1486         histDict = {seqList[0]:Histograms[seqList[0]]}
     1587    if seqHistList: # for sequential refinement, only process selected histgram in list
     1588        histDict = {seqHistList[seqhistnum]:Histograms[seqHistList[seqhistnum]]}
    14871589    else:
    14881590        histDict = Histograms
    14891591    hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,histDict,Print=False,resetRefList=False)
    14901592    hapList = sorted([i for i in hapDict.keys() if i.split(':')[2] not in ('Type',)])
    1491     if seqList: # convert histogram # to wildcard
     1593    # derive list of variables
     1594    if seqHistList: # convert histogram # to wildcard
    14921595        wildList = [] # list of variables with "*" for histogram number
    14931596        for i in hapList:
     
    14971600            sj = ':'.join(s)
    14981601            if sj not in wildList: wildList.append(sj)
    1499         hapList = wildList
     1602        if seqmode == 'use-all':
     1603            hapList += wildList       
     1604        else:
     1605            hapList = wildList       
    15001606    histVary,histDict,controlDict = G2stIO.GetHistogramData(histDict,Print=False)
    15011607    histList = list(histDict.keys())
    15021608    histList.sort()
    1503     if seqList: # convert histogram # to wildcard
     1609    if seqHistList: # convert histogram # to wildcard
    15041610        wildList = [] # list of variables with "*" for histogram number
    15051611        for i in histList:
     
    15091615            sj = ':'.join(s)
    15101616            if sj not in wildList: wildList.append(sj)
    1511         histList = wildList       
     1617        if seqmode == 'use-all':
     1618            histList += wildList       
     1619        else:
     1620            histList = wildList       
     1621
    15121622    Indx = {}
    15131623    G2frame.Page = [0,'phs']
     
    15461656    SymConstr = wx.ScrolledWindow(G2frame.constr)
    15471657    G2frame.constr.AddPage(SymConstr,'Sym-Generated')
    1548     wx.CallAfter(OnPageChanged,None)
     1658    wx.CallAfter(OnPageChanged,None,selectTab)
    15491659    G2frame.constr.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
    1550     # validate all the constrants -- should not see any errors here normally
    1551     errmsg,warnmsg = ConstraintsCheck(data)
     1660    errmsg,warnmsg = WarnConstraintLimit()  # check limits & constraints
    15521661    if errmsg:
    15531662        G2frame.ErrorDialog('Constraint Error',
    1554                             'Error in constraints:\n'+errmsg+'\nCheck console output for more information',
     1663                            'Error in constraints.\nCheck console output for more information'+
     1664                            ' or press "Show Errors" & "Show Warnings" buttons',
    15551665                            parent=G2frame)
    1556         print (errmsg)
    1557         print (G2mv.VarRemapShow([],True))
    1558         return
    1559     elif warnmsg:
    1560         print ('Unexpected contraint warning:\n'+warnmsg)
    1561     WarnConstraintLimit()
     1666        if seqhistnum is None:
     1667            print ('\nError message(s):\n',errmsg)
     1668        else:
     1669            print ('\nError message(s) for histogram #{}:\n{}'.format(seqhistnum,errmsg))
     1670        if warnmsg: print ('\nAlso note these constraint warning(s):\n'+warnmsg)
     1671    #elif GSASIIpath.GetConfigValue('debug'):
     1672    #    print ('Generated constraints\n',G2mv.VarRemapShow())
    15621673       
    15631674###### check scale & phase fractions, create constraint if needed #############
     
    15681679    '''
    15691680    histograms, phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
    1570     for i,hist in enumerate(histograms):
    1571         CheckScalePhaseFractions(G2frame,hist,histograms,phases)
     1681    cId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints')
     1682    Constraints = G2frame.GPXtree.GetItemPyData(cId)
     1683
     1684    problems = []
     1685    for h,hist in enumerate(histograms):
     1686        if CheckScalePhaseFractions(G2frame,hist,histograms,phases,Constraints):
     1687            problems.append((h,hist))
     1688    if len(problems) == 0: return
     1689    msg = 'You are refining the scale factor and all phase fractions for histogram(s) #'
     1690    for i,(h,hist) in enumerate(problems):
     1691        if i: msg += ', '
     1692        msg += str(h)
     1693    msg += '. This is not recommended as it will produce an unstable refinement. Do you want to create constrain(s) on the sum of phase fractions to address this? (Press "No" to continue without)'
     1694    dlg = wx.MessageDialog(G2frame,msg,'Warning: Constraint Needed',wx.YES|wx.NO)
     1695    ans = dlg.ShowModal()
     1696    dlg.Destroy()
     1697    if ans == wx.ID_YES:
     1698        for h,hist in problems:
     1699            constr = []
     1700            for p in phases:
     1701                if hist not in phases[p]['Histograms']: continue
     1702                if not phases[p]['Histograms'][hist]['Use']: continue
     1703                constr += [[1.0,G2obj.G2VarObj(':'.join(
     1704                    (str(phases[p]['pId']),
     1705                    str(histograms[hist]['hId']),
     1706                    'Scale')
     1707                    ))]]
     1708            Constraints['HAP'].append(constr+[1.0,None,'c'])
     1709        wx.CallAfter(G2frame.GPXtree.SelectItem,cId) # should call SelectDataTreeItem
     1710        UpdateConstraints(G2frame,Constraints,1,True) # repaint with HAP tab
     1711        return True
     1712    return False
    15721713       
    1573 def CheckScalePhaseFractions(G2frame,hist,histograms,phases):
     1714def CheckScalePhaseFractions(G2frame,hist,histograms,phases,Constraints):
    15741715    '''Check if scale factor and all phase fractions are refined without a constraint
    15751716    for histogram hist, if so, offer the user a chance to create a constraint
    15761717    on the sum of phase fractions
    15771718    '''
    1578     if G2frame.testSeqRefineMode():
    1579         histStr = '*'
    1580     else:
     1719    if G2frame.testSeqRefineMode():  # more work needed due to seqmode
     1720        return False
     1721#        histStr = '*'
     1722    else:
    15811723        histStr = str(histograms[hist]['hId'])
    15821724    # Is this powder?
    1583     if not hist.startswith('PWDR '): return
     1725    if not hist.startswith('PWDR '): return False
    15841726    # do this only if the scale factor is varied
    1585     if not histograms[hist]['Sample Parameters']['Scale'][1]: return
     1727    if not histograms[hist]['Sample Parameters']['Scale'][1]: return False
    15861728    # are all phase fractions varied in all used histograms?
    15871729    phaseCount = 0
     
    15891731        if hist not in phases[p]['Histograms']: continue
    15901732        if phases[p]['Histograms'][hist]['Use'] and not phases[p]['Histograms'][hist]['Scale'][1]:
    1591             return
     1733            return False
    15921734        else:
    15931735            phaseCount += 1
    15941736   
    1595     # all phase fractions and scale factor varied, now scan through constraints
    1596     sub = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints')
    1597     Constraints = G2frame.GPXtree.GetItemPyData(sub)
     1737    # all phase fractions and scale factor varied, now scan for a constraint
    15981738    for c in Constraints.get('HAP',[]):
    15991739        if c[-1] != 'c': continue
     
    16041744        if all([(i[1].name == 'Scale' and i[1].varname().split(':')[1] == histStr) for i in c[:-3]]):
    16051745            # got a constraint, this is OK
    1606             return
    1607     dlg = wx.MessageDialog(G2frame,'You are refining the scale factor and all phase fractions for histogram #'+
    1608         histStr+'. This will produce an unstable refinement. '+
    1609         'Do you want to constrain the sum of phase fractions?','Create constraint?',wx.OK|wx.CANCEL)
    1610     if dlg.ShowModal() != wx.ID_OK:
    1611         dlg.Destroy()
    1612         return
    1613     dlg.Destroy()
    1614 
    1615     constr = []
    1616     for p in phases:
    1617         if hist not in phases[p]['Histograms']: continue
    1618         if not phases[p]['Histograms'][hist]['Use']: continue
    1619         constr += [[1.0,G2obj.G2VarObj(':'.join((str(phases[p]['pId']),histStr,'Scale')))]]
    1620     constr += [1.0,None,'c']
    1621     Constraints['HAP'] += [constr]
     1746            return False
     1747    return True
    16221748       
    16231749#### Make nuclear/magnetic phase transition constraints - called by OnTransform in G2phsGUI ##########
     
    17661892    Aold = G2lat.cell2A(oldPhase['General']['Cell'][1:7])
    17671893    if True: # debug
    1768         constraints['Phase'] += G2lat.GenCellConstraints(Trans,opId,npId,Aold,True)
     1894        constraints['Phase'] += G2lat.GenCellConstraints(Trans,opId,npId,Aold,
     1895                                oldPhase['General']['SGData'],nSGData,True)
    17691896        print('old A*',G2lat.cell2A(oldPhase['General']['Cell'][1:7]))
    17701897        print('new A*',G2lat.cell2A(newPhase['General']['Cell'][1:7]))
     
    17721899        print('new cell',newPhase['General']['Cell'][1:7])
    17731900    else:
    1774         constraints['Phase'] += G2lat.GenCellConstraints(Trans,opId,npId,Aold)
    1775 
     1901        constraints['Phase'] += G2lat.GenCellConstraints(Trans,opId,npId,Aold,
     1902                                oldPhase['General']['SGData'],nSGData,True)
    17761903    # constraints on HAP Scale, etc.
    17771904    for hId,hist in enumerate(UseList):    #HAP - seems OK
     
    31333260        AtInfo = data['Vector']['AtInfo']
    31343261        refChoice = {}
    3135         RefObjs = []
     3262        #RefObjs = []
    31363263
    31373264        GS = VectorRBDisplay.GetSizer()
     
    37173844    sub = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints')
    37183845    Constraints = G2frame.GPXtree.GetItemPyData(sub)
    3719     constDict = {}
     3846    constrDict = {}
    37203847    for c in Constraints['Phase']:
    37213848        if c[-1] != 'f' or not c[-3]: continue
    3722         constDict[str(c[-3])] = c
     3849        constrDict[str(c[-3])] = c
    37233850
    37243851    parmDict,varyList = G2frame.MakeLSParmDict()
     
    37713898                ISO['IsoModeList'],modeVals,ISO['NormList'],ISO['G2ModeList'] ):
    37723899            #GSASIIpath.IPyBreak()
    3773             if str(G2mode) in constDict:
    3774                 ch = G2G.HelpButton(panel2,fmtHelp(constDict[str(G2mode)],var))
     3900            if str(G2mode) in constrDict:
     3901                ch = G2G.HelpButton(panel2,fmtHelp(constrDict[str(G2mode)],var))
    37753902                subSizer2.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
    37763903            else:
     
    38113938                ISO['OccVarList'],deltaList,
    38123939                ISO['OccModeList'],modeVals,ISO['OccNormList'],ISO['G2OccModeList']):
    3813             if str(G2mode) in constDict:
    3814                 ch = G2G.HelpButton(panel2,fmtHelp(constDict[str(G2mode)],var))
     3940            if str(G2mode) in constrDict:
     3941                ch = G2G.HelpButton(panel2,fmtHelp(constrDict[str(G2mode)],var))
    38153942                subSizer2.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1)
    38163943            else:
  • trunk/GSASIIdataGUI.py

    r5028 r5038  
    34653465            new = True
    34663466            sub = self.GPXtree.AppendItem(parent=self.root,text='Constraints')
    3467             self.GPXtree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[]})
     3467            self.GPXtree.SetItemPyData(sub,{'Hist':[],'HAP':[],'Phase':[],
     3468                        'Global':[],'_seqmode':'auto-wildcard', '_seqhist':0})
    34683469        if not GetGPXtreeItemId(self,self.root,'Restraints'):
    34693470            new = True
     
    49334934                UseList = data['Histograms']
    49344935                if Used:
    4935                     usedHistograms[phaseName] = [h for h in UseList if UseList[h]['Use']]
     4936                    usedHistograms[phaseName] = [h for h in UseList if UseList[h].get('Use',True)]
    49364937                else:
    49374938                    usedHistograms[phaseName] = list(UseList.keys())
     
    51195120        for a sequential fit.
    51205121        '''
    5121         G2cnstG.CheckAllScalePhaseFractions(self)
     5122        if G2cnstG.CheckAllScalePhaseFractions(self): return
    51225123        try:
    51235124            parmDict,varyList = self.MakeLSParmDict()
     
    51295130            parmValDict[i] = parmDict[i][0]
    51305131           
    5131         reqVaryList = tuple(varyList) # save requested variables
     5132        reqVaryList = copy.copy(varyList) # save requested variables
    51325133        wx.BeginBusyCursor()
    5133         try:
     5134        try:           
    51345135            # process constraints
     5136            Histograms,Phases = self.GetUsedHistogramsAndPhasesfromTree()
     5137            if not len(Phases) or not len(Histograms):
     5138                print('No constraints processing without phases and histograms defined')
     5139                raise G2mv.ConstraintException               
     5140            G2obj.IndexAllIds(Histograms,Phases)
    51355141            sub = GetGPXtreeItemId(self,self.root,'Constraints')
    51365142            Constraints = self.GPXtree.GetItemPyData(sub)
    5137             constList = []
    5138             for item in Constraints:
    5139                 if item.startswith('_'): continue
    5140                 constList += Constraints[item]
    5141             G2mv.InitVars()
    5142             constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
    5143             msg = G2mv.EvaluateMultipliers(constrDict,parmValDict)
    5144             if msg:
    5145                 print('Unable to interpret multiplier(s):',msg)
    5146             else:
    5147                 G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmValDict)
    5148                 G2mv.Map2Dict(parmValDict,varyList)
     5143            errmsg,warnmsg = G2cnstG.CheckConstraints(self,Phases,Histograms,Constraints,[],reqVaryList)
    51495144        except G2mv.ConstraintException:
     5145            print('Error in Constraint Processing')
    51505146            pass
    51515147        Controls = self.GPXtree.GetItemPyData(GetGPXtreeItemId(self,self.root, 'Controls'))
     
    51535149            if key not in Controls: Controls[key] = {}
    51545150        wx.EndBusyCursor()
    5155         # check for limits on dependent vars
    5156         consVars = [i for i in reqVaryList if i not in varyList]
    5157         impossible = set(
    5158             [str(i) for i in Controls['parmMinDict'] if i in consVars] +
    5159             [str(i) for i in Controls['parmMaxDict'] if i in consVars])
    5160         if impossible:
    5161             msg = ''
    5162             for i in sorted(impossible):
    5163                 if msg: msg += ', '
    5164                 msg += i
    5165             msg =  ' &'.join(msg.rsplit(',',1))
    5166             msg = ('Note: limits on variable(s) '+msg+
    5167             ' will be ignored because they are constrained.')
    5168             G2G.G2MessageBox(self,msg,'Limits ignored for constrained vars')
     5151        # # check for limits on dependent vars
     5152        # consVars = [i for i in reqVaryList if i not in varyList]
     5153        # impossible = set(
     5154        #     [str(i) for i in Controls['parmMinDict'] if i in consVars] +
     5155        #     [str(i) for i in Controls['parmMaxDict'] if i in consVars])
     5156        # if impossible:
     5157        #     msg = ''
     5158        #     for i in sorted(impossible):
     5159        #         if msg: msg += ', '
     5160        #         msg += i
     5161        #     msg =  ' &'.join(msg.rsplit(',',1))
     5162        #     msg = ('Note: limits on variable(s) '+msg+
     5163        #     ' will be ignored because they are constrained.')
     5164        #     G2G.G2MessageBox(self,msg,'Limits ignored for constrained vars')
    51695165        # debug stuff
    51705166        #if GSASIIpath.GetConfigValue('debug'):
     
    51895185            G2G.G2MessageBox(self,'Doing a zero cycle Le Bail refinement first','Le Bail Refinement')           
    51905186            self.OnLeBail(event)
    5191         G2cnstG.CheckAllScalePhaseFractions(self) # can be slow for sequential fits, skip
     5187        if G2cnstG.CheckAllScalePhaseFractions(self):
     5188            return # can be slow for sequential fits, skip
    51925189        self.OnFileSave(event)
    5193         # check that constraints are OK here
    5194         errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
     5190        errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile) # check constraints are OK
     5191        if warnmsg:
     5192            print ('\nNote these constraint warning(s):\n'+warnmsg)
     5193            print ('Generated constraints\n',G2mv.VarRemapShow([],True))
    51955194        if errmsg:
    5196             self.ErrorDialog('Refinement error',errmsg+'\nCheck console output for more information')
     5195            print ('\nError message(s):\n',errmsg)
     5196            self.ErrorDialog('Error in constraints',errmsg)
    51975197            return
    5198         if warnmsg:
    5199             print('Conflict between refinement flag settings and constraints:\n'+
    5200                 warnmsg+'\nRefinement not possible')
    5201             self.ErrorDialog('Refinement Flag Error',
    5202                 'Conflict between refinement flag settings and constraints:\n'+
    5203                 warnmsg+'\nRefinement not possible')
    5204             return
     5198       
    52055199        dlg = wx.ProgressDialog('Residual','All data Rw =',101.0,
    52065200            style = wx.PD_ELAPSED_TIME|wx.PD_AUTO_HIDE|wx.PD_CAN_ABORT|wx.STAY_ON_TOP,parent=self)
     
    54355429                ' histograms that are not used have been removed from the sequential list.',
    54365430                'Histograms removed')
    5437             Controls['Seq Data'] = newseqList
    5438             seqList = newseqList
     5431            seqList = Controls['Seq Data'] = newseqList
    54395432        self.OnFileSave(event)
    5440         # check that constraints are OK here
    5441         errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile)
    5442         #errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile,seqList[0]) # this would be faster, but at present it might not catch all errors
    5443         if errmsg:
    5444             self.ErrorDialog('Refinement error',errmsg)
     5433        allerrors = {}
     5434        allwarnings = {}
     5435        for h in seqList: # check constraints are OK for each histogram to be processed
     5436            errmsg, warnmsg = G2stIO.ReadCheckConstraints(self.GSASprojectfile,h)
     5437            if warnmsg or errmsg:
     5438                print ('\nConstraint warnings/errors for histogram "{}":'.format(h))
     5439            if warnmsg:
     5440                allwarnings[h] = warnmsg
     5441                print ('warning:',warnmsg)
     5442            if errmsg:
     5443                allerrors[h] = errmsg
     5444                print ('Error message: ',errmsg)
     5445        if allerrors:
     5446            if len(allerrors) == len(seqList):
     5447                l = 'all histograms'
     5448            elif len(allerrors) == 1:
     5449                l = 'one histogram'
     5450            else:
     5451                l = str(len(allerrors)) + ' histograms'
     5452            self.ErrorDialog('Error in Constraints',
     5453                             'There were constraint errors in {}. Refinement is not possible. See console output or visit Constraints data tree item for more details.'.format(l))
    54455454            return
    5446         if warnmsg:
    5447             print(u'Conflict between refinment flag settings and constraints:\n'+
    5448                   warnmsg+u'\nRefinement not possible')
    5449             self.ErrorDialog('Refinement Flag Error',
    5450                  u'Conflict between refinment flag settings and constraints:\n'+
    5451                  warnmsg+u'\nRefinement not possible')
    5452             return
     5455        elif allwarnings:
     5456            if len(allwarnings) == len(seqList):
     5457                l = 'all histograms'
     5458            elif len(allwarnings) == 1:
     5459                l = 'one histogram'
     5460            else:
     5461                l = str(len(allwarnings)) + ' histograms'
     5462            msg = 'There were changes to constraints needed in {}. See console output or visit Constraints data tree item for more details. Continue with refinement?'.format(l)
     5463            dlg = wx.MessageDialog(self,msg,caption='Constraint changes',style=wx.YES_NO)
     5464            dlg.CenterOnParent()
     5465            result = wx.ID_NO
     5466            try:
     5467                result = dlg.ShowModal()
     5468            finally:
     5469                dlg.Destroy()
     5470            if result == wx.ID_NO: return
    54535471        self.GPXtree.SaveExposedItems()       
    54545472        dlg = wx.ProgressDialog('Residual for histogram 0','Powder profile Rwp =',101.0,
     
    66766694            except ValueError:  #data changed somehow - start fresh
    66776695                sel = []
    6678             dlg = G2G.G2MultiChoiceDialog(G2frame, 'Select datasets to include',
     6696            dlg = G2G.G2MultiChoiceDialog(G2frame,
     6697                        'Select datasets to include. Select no datasets to end sequential refinements.',
    66796698                        'Sequential refinement selection',choices)
    66806699            dlg.SetSelections(sel)
     
    66866705            dlg.Destroy()
    66876706            G2frame.SetTitleByGPX()
     6707            if names:    # probably not needed (should be set previously)
     6708                item = GetGPXtreeItemId(G2frame,G2frame.root,'Constraints')
     6709                constraints = G2frame.GPXtree.GetItemPyData(item)
     6710                constraints['_seqmode'] = constraints.get('_seqmode','auto-wildcard')
     6711           
    66886712            wx.CallAfter(UpdateControls,G2frame,data)
    66896713           
  • trunk/GSASIIlattice.py

    r4998 r5038  
    2828When the hydrostatic/elastic strain coefficients (*Dij*, :math:`D_{ij}`) are used, they are added to the
    2929*A* tensor terms (Ai, :math:`A_{i}`) so that A is redefined
    30 :math:`A = (\\begin{matrix} A_{0} + D_{11} & A_{1} + D_{22} & A_{2} + D_{33} & A_{3} + 2D_{12} & A_{4} + 2D_{13} & A_{5} + 2D_{23}\\end{matrix})`. See :func:`cellDijFill`.
     30:math:`A = (\\begin{matrix} A_{0} + D_{11} & A_{1} + D_{22} & A_{2} + D_{33} & A_{3} + D_{12} & A_{4} + D_{13} & A_{5} + D_{23}\\end{matrix})`. See :func:`cellDijFill`.
    3131Note that GSAS-II variables ``p:h:Dij`` (i,j = 1, 2, 3) and ``p`` is a phase number
    3232and ``h`` a histogram number are used for the *Dij* values.
     
    444444#   cellXformRelations = fmtCellConstraints(GenerateCellConstraints())
    445445
    446 def GenCellConstraints(Trans,origPhase,newPhase,origA,debug=False):
     446def GenCellConstraints(Trans,origPhase,newPhase,origA,oSGLaue,nSGLaue,debug=False):
    447447    '''Generate the constraints between two unit cells constants for a phase transformed
    448448    by matrix Trans.
     
    454454    :param int newPhase: phase id for the transformed phase to be constrained from
    455455      original phase
    456     :param list origA: reciprocal cell ("A*") tensor
     456    :param list origA: reciprocal cell ("A*") tensor (used for debug only)
     457    :param dict oSGLaue: space group info for original phase
     458    :param dict nSGLaue: space group info for transformed phase
    457459    :param bool debug: If true, the constraint input is used to compute and print A*
    458460      and from that the direct cell for the transformed phase.
     
    462464    Anew = []
    463465    constrList = []
     466    uniqueAnew = cellUnique(nSGLaue)
     467    zeroAorig = cellZeros(oSGLaue)
    464468    for i in range(6):
    465469        constr = [[-1.0,G2obj.G2VarObj('{}::A{}'.format(newPhase,i))]]
     
    468472            const, aTerm, tTerm = item.split('*',2)
    469473            const = float(const) * eval(tTerm)
    470             # ignore terms where either the Transform contribution is zero.
    471             #if abs(const * origA[j]) > 1e-8:
    472             # If A term is zero this may indicate a symmetry constraint or an
    473             # accidentally zero. If required as zero then it will not be refined
    474             # and we do not want to include it, but if accidentally zero we do,
    475             # so include them for now and remove later.
    476             if abs(const) > 1e-8:
    477                 constr.append([const,G2obj.G2VarObj('{}::{}'.format(origPhase,aTerm))])
    478                 mult.append(const)
    479             else:
    480                 mult.append(0)
    481         constrList.append(constr + [0.0,None,'c'])
     474            mult.append(const)
     475            # skip over A terms that are required to be zero
     476            if zeroAorig[int(aTerm[1])]: continue   # only add non-zero terms
     477            # ignore terms where either the Transform contribution is zero [= abs() < 1e-8]
     478            # If the multiplier term is zero I don't think this accidental
     479            # but since it will not change there is no reason to include that
     480            # term in any case
     481            if abs(const) < 1e-8: continue
     482            constr.append([const,G2obj.G2VarObj('{}::{}'.format(origPhase,aTerm))])
     483        if i in uniqueAnew:
     484            constrList.append(constr + [0.0,None,'c'])
    482485        if debug: Anew.append(np.dot(origA,mult))           
    483486    if debug:
     
    485488        print('xformed cell',A2cell(Anew))
    486489    return constrList
     490
     491def cellUnique(SGData):
     492    '''Returns the indices for the unique A tensor terms
     493    based on the Laue class.
     494    Any terms that are determined from others or are zero are not included.
     495
     496    :param dict SGdata: a symmetry object
     497    :returns: a list of 0 to 6 terms with indices of the unique A terms
     498    '''
     499    if SGData['SGLaue'] in ['-1',]:
     500        return [0,1,2,3,4,5]
     501    elif SGData['SGLaue'] in ['2/m',]:
     502        if SGData['SGUniq'] == 'a':
     503            return [0,1,2,5]
     504        elif SGData['SGUniq'] == 'b':
     505            return [0,1,2,4]
     506        else:
     507            return [0,1,2,3]
     508    elif SGData['SGLaue'] in ['mmm',]:
     509        return [0,1,2]
     510    elif SGData['SGLaue'] in ['4/m','4/mmm']:
     511        return [0,2]
     512    elif SGData['SGLaue'] in ['6/m','6/mmm','3m1', '31m', '3']:
     513        return [0,2]
     514    elif SGData['SGLaue'] in ['3R', '3mR']:
     515        return [0,3]
     516    elif SGData['SGLaue'] in ['m3m','m3']:
     517        return [0,]
     518
     519def cellZeros(SGData):
     520    '''Returns a list with the A terms required to be zero based on Laue symmetry
     521
     522    :param dict SGdata: a symmetry object
     523    :returns: A list of six terms where the values are True if the
     524      A term must be zero, False otherwise.
     525    '''
     526    if SGData['SGLaue'] in ['-1',]:
     527        return 6*[False]
     528    elif SGData['SGLaue'] in ['2/m',]:
     529        if SGData['SGUniq'] == 'a':
     530            return [False,False,False,True,True,False]
     531        elif SGData['SGUniq'] == 'b':
     532            return [False,False,False,True,False,True]
     533        else:
     534            return [False,False,False,False,True,True]
     535    elif SGData['SGLaue'] in ['mmm',]:
     536        return [False,False,False,True,True,True]
     537    elif SGData['SGLaue'] in ['4/m','4/mmm']:
     538        return [False,False,False,True,True,True]
     539    elif SGData['SGLaue'] in ['6/m','6/mmm','3m1', '31m', '3']:
     540        return [False,False,False,False,True,True]
     541    elif SGData['SGLaue'] in ['3R', '3mR']:
     542        return 6*[False]
     543    elif SGData['SGLaue'] in ['m3m','m3']:
     544        return [False,False,False,True,True,True]
    487545
    488546def TransformXYZ(XYZ,Trans,Vec):
  • trunk/GSASIImapvars.py

    r4998 r5038  
     1# TODO: revisit SeqRefine and :meth:`GSASIIdataGUI.GSASII.OnSeqRefine` and :func:`GSASIIseqGUI.UpdateSeqResults`
     2
    13# -*- coding: utf-8 -*-
    24########### SVN repository information ###################
     
    1416and parameter simplification contraints.
    1517
     18
     19*Externally-Accessible Routines*
     20---------------------------------
     21
     22To define a set of constrained and unconstrained relations, one
     23defines a list of dictionary defining constraint parameters and their
     24values, a list of fixed values for each constraint and a list of
     25parameters to be varied. In addition, one uses
     26:func:`StoreEquivalence` to define parameters that are equivalent.
     27See the :ref:`Constraints Processing section<Constraints_processing>` for details on how
     28processing of constraints is done.
     29
     30=============================  ===================================================================
     31  routine                      explanation
     32=============================  ===================================================================
     33:func:`InitVars`               This is used to clear out all defined previously
     34                               defined constraint information
     35 
     36:func:`StoreEquivalence`       Implements parameter redefinition.
     37                               This should be called for every set of equivalence relationships.
     38                               Use :func:`StoreEquivalence` before calling
     39                               :func:`GenerateConstraints`
     40
     41:func:`ProcessConstraints`     Initially constraints of all types are maintained in lists of
     42                               dict entries that are stored in the data tree,
     43                               with parameters are stored as
     44                               :class:`~GSASIIobj.G2VarObj` objects so that they can
     45                               be resolved if the phase/histogram order changes.
     46                               :func:`ProcessConstraints` processes this list of dict entries,
     47                               separating the "Equivalence", "Hold", “Const” and “New Var”
     48                               entries for subsequent use.
     49                               See the :ref:`Constraint Reorganization <ProcessConstraints>`
     50                               section for more details.
     51
     52:func:`EvaluateMultipliers`    Convert any string-specified (formula-based) multipliers to
     53                               numbers. Call this before using :func:`GenerateConstraints`.
     54                               At present only values in dict for phase (atom/cell) parameters
     55                               are used to evaluate multipliers containint formulae,
     56                               but this could be changed if needed.
     57
     58:func:`GenerateConstraints`    Generates the internally-used tables from constraints and
     59                               equivalences. Checks for internal consistency and repairs
     60                               problems where possible. See the
     61                               :ref:`Constraint Checking and Grouping <GenerateConstraints>`
     62                               and :ref:`Equivalence Checking<CheckEquivalences>`
     63                               sections for more details.
     64
     65:func:`Map2Dict`               To determine values for any parameters created in this module,
     66                               call Map2Dict. This will not apply contraints.
     67
     68:func:`Dict2Map`               To apply the constraints and equivalences, call this.
     69                               It takes values from the new independent parameters and
     70                               constraints, and applies them to the parameter dict.
     71
     72:func:`Dict2Deriv`             This determines derivatives on independent parameters
     73                               from those on dependent ones.
     74
     75:func:`ComputeDepESD`          Use ComputeDepESD to compute uncertainties on dependent variables.
     76
     77:func:`VarRemapShow`           Use this to show a summary of the parameter remapping.
     78                               Call after :func:`GenerateConstraints`.
     79=============================  ===================================================================
     80
    1681Types of constraints
    1782--------------------
    1883
    1984There are four ways to specify constraints, as listed below.
    20 Note that parameters are initially stored in the
     85Note that constraints are initially stored in the
    2186main section of the GSAS-II data tree under heading ``Constraints``.
    2287This dict has four keys, 'Hist', 'HAP', 'Global', and 'Phase',
     
    2792Note that in the constraints, as stored in the GSAS-II data tree, parameters
    2893are stored as :class:`GSASIIobj.G2VarObj` objects, as these objects allow for
    29 changes in numbering of phases, histograms and atoms.
    30 When they are interpreted (in :func:`GSASIIstrIO.ProcessConstraints`),
    31 references
    32 to numbered objects are resolved using the appropriate random ids and the
    33 parameter object is converted to a string of form ``ph:hst:VARNAM:at``.
     94changes in numbering of phases, histograms and atoms since :class:`GSASIIobj.G2VarObj` objects
     95use random Id's for references.
     96When constraints are interpreted (in :func:`ProcessConstraints`),
     97these references are resolved to the numbered objects by looking up random Id's
     98so that the parameter object is converted to a string of form ``ph:hst:VARNAM:at``.
     99
     100Constraints are initially stored as described in the
     101:ref:`constraint definitions <Constraint_definitions_table>`, where the last value in the
     102list determines which type of constraint is defined.
    34103
    35104Alternate parameters (New Var)
     
    38107Parameter redefinition ("New Var" constraints)
    39108is done by creating an expression that relates several
    40 parameters:
    41 
    42 ::
    43 
    44    Mx1 * Px + My1 * Py +...
    45    Mx2 * Px + Mz2 * Pz + ...
     109parameters: ::
     110
     111   Mx1 * Px + My1 * Py +... = ::newvar1
     112   Mx2 * Px + Mz2 * Pz + ... = ::newvar2
    46113
    47114where Pj is a GSAS-II parameter name and Mjk is a constant (float) multiplier.
     
    50117value of the parameter before the formula is evaluated, so ``'np.cos(0::Ax:2)'`` is a valid
    51118multiplier. At present, only phase (atom/cell) parameters are available for use in
    52 a formula, but this can be expanded if needed.
     119a formula, from the GUI but this can be expanded if needed.
    53120
    54121This type of constraint describes an alternate
     
    63130express "irrep modes" in terms of the fundamental structural parameters.
    64131
    65 These "New Var" constraints are stored as described for type "f" in the
    66 :ref:`constraint definitions table <Constraint_definitions_table>`.
     132The "New Var" constraints are stored as a type "f"
     133:ref:`constraint (see definitions)<Constraint_definitions_table>`.
    67134
    68135Constrained parameters (Const)
     
    88155can be related by a fixed offset (the so called B+1 "rule").
    89156
    90 A "Const" constraint is stored as described for type "c" in the
    91 :ref:`constraint definitions table <Constraint_definitions_table>`.
     157The "Const" constraints are stored as a type "c"
     158:ref:`constraint (see definitions)<Constraint_definitions_table>`.
    92159
    93160Equivalenced parameters (Equiv)
     
    108175where the multipliers can be a formula (str) that will be evaluated prior to the start of
    109176the refinement. In a formula, GSAS-II parameters will be replaced by the value of the
    110 parameter before the formula is evaluate, so ``'np.cos(0::Ax:2)'`` is a valid multiplier.
     177parameter before the formula is evaluated, so a multiplier can be specifed as ``'2*np.cos(0::Ax:2)'``.
    111178At present, only phase (atom/cell) parameters are available for use in
    112 a formula, but this can be expanded if needed.
    113 Note that
    114 the latter constraint expression is conceptually identical to
    115 defining constraint equations. In practice, however,
    116 equivalenced parameters are processed in a
    117 different and more direct manner than constraint equations. The previous
    118 set of equalities could also be written in this way as a set of constraint
    119 equations: ::
     179such a formula, but this can be expanded if needed.
     180
     181The first parameter (P1 above)
     182is considered the independent variable
     183and the remaining parameters are dependent variables. The dependent variables
     184are then set from the independent variable.
     185
     186Note that a constraint expression is conceptually identical to
     187defining constraint equations.
     188The previous set of equalities could also be written as a set of constraint
     189equations in this way: ::
    120190
    121191  C1 * P1 - C2 * P2 = 0
     
    123193  ...
    124194
    125 
    126 The first parameter (P1 above)
    127 is considered the independent variable
    128 and the remaining parameters are dependent variables. The dependent variables
    129 are set from the independent variable.
    130 An example of how this may be used woul be if, for example,
    131 a material has a number of O atoms, all in fairly similar bonding environments
    132 and the diffraction data are sparse, one my reduce the complexity of the model
    133 by defining Uiso for the first O atoms to be identical to the remaining atoms.
     195In practice, however,
     196equivalenced parameters are processed in a
     197different and more direct manner than constraint equations.
     198
     199A parameter can be used in multiple
     200equivalences where it is an independent variable,
     201but if a parameter were used both as a dependent and independent variable then the order that
     202shifts are applied becomes potentially significant. As an example, in this case these two
     203equivalences are "chained"::
     204
     205  C1 * P1 = C2 * P2
     206  C2 * P2 = C3 * P3
     207
     208where P2 is both a dependent and independent variable. Likewise, if a parameter is used both in equivalences
     209and in "New Var" or "Const" constraints, it also becomes unclear how this should be processed. It is
     210possible to specify equivalences that conflict with constraints.
     211Should parameter be used as both a dependent and an independent variable or if a parameter is used both in
     212an the equivalence and in a "New Var" or "Const" constraints, the equivalence
     213is converted to a constraint (Const) to
     214avoid conflicts. The equivalences that require this are addressed in ::func:`GenerateConstraints` where
     215:func:`CheckEquivalences` is used to locate problematic variables in equivalences
     216and then change these equivalences to "Const" equations. Also, unneeded equivalences are removed.
     217
     218For an example of how equivalences may be used, consider
     219a material that has **N** O atoms in the asymmetric unit, all in fairly similar bonding environments
     220and where the diffraction data are sparse. One may wish to reduce the complexity of the model fit to
     221these data by defining Uiso for all O atoms to be the same. This is done by selecting Uiso for any one O atom
     222as an independent variable in a equivalence and setting the remaining **N-1** other O atom Uiso
     223variables as dependent parameters with multipliers of 1. This will require that all O atom Uiso values
     224be identical.
    134225The results of this refinement will be simpler to understand than if a set of
    135 constraint equations is used because the refined parameter will be the independent variable, which will be as ph::Uiso:n, corresponding to the first O atom.
    136 
    137 A parameter can be used in multiple
    138 equivalences as independent variable,
    139 but if parameter is used as both a dependent and independent variable or
    140 a parameter is used in equivalences and in "New Var" or "Const" constraints,
    141 this create conflicts that cannot be resolved within the equivalences implementation
    142 but can be handled as constraint equations.
    143 The equivalences that violate this are discovered in :func:`CheckEquivalences`
    144 and then :func:`MoveConfEquiv` is used to change these equivalences to "Const" equations.
     226constraint equations is used, because the refined parameter (named as ``ph::Uiso:n``) will be the
     227independent variable, corresponding to the first O atom and all other variables would be
     228expressed in terms of that variable with a single Equivalence expression.
     229The alternate would require **N-1** constraint equations, leaving one degree of freedom with a
     230variable would that is likely only indirectly related to the Uiso values.
    145231
    146232Equivalenced parameters ("EQUIV" constraints), when defined by users,
    147 are stored as described for type "e" in the
    148 :ref:`constraint definitions table <Constraint_definitions_table>`.
    149 Other equvalences are generated by symmetry prior
     233or when created to relate phases, are stored as a type "e"
     234:ref:`constraint (see definitions)<Constraint_definitions_table>`.
     235Symmetry-generated equivalences are generated prior
    150236to display or refinement in :func:`GSASIIstrIO.GetPhaseData`.
    151 These are not stored.
    152 
    153 Fixed parameters (Hold)
    154 ^^^^^^^^^^^^^^^^^^^^^^^^
     237These are not stored in the data tree.
     238
     239Hold parameters (Fixed)
     240^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    155241
    156242When parameters are refined where a single refinement flag determines that several variables
     
    160246but under specific conditions, there may be other good reasons to constrain a parameter.
    161247
    162 A "Hold" constraint is stored as described for type "h" in the
    163 :ref:`constraint definitions table <Constraint_definitions_table>`.
     248The "Hold" constraints are stored as a type "h"
     249:ref:`constraint (see definitions)<Constraint_definitions_table>`.
    164250
    165251.. _Constraints_processing:
     
    169255
    170256When constraints will be used or edited, they are processed using a series of
    171 calls:
    172 
    173 * First all of the stored constraints are appended into a single list. They are
    174   initially stored in separate lists only to improve their creation and display
    175   in the GUI.
    176 
    177 * Then :func:`InitVars` is used to initialize the global variables in
    178   this module (:mod:`GSASIImapvars`).
    179 
    180 * Then :func:`GSASIIstrIO.ProcessConstraints` is used to initially process the
    181   constraints, as described below.
    182 
    183 * Symmetry-generated equivalences are then created in
    184   :func:`GSASIIstrIO.GetPhaseData`, which also calls
    185   :func:`GSASIIstrIO.cellVary` and for Pawley refinements
    186   :func:`GSASIIstrIO.GetPawleyConstr`. These are entered directly into this
    187   module's globals using :func:`StoreEquivalence`.
    188 * Constraints/equivalences are then checked for possible conflicts with
    189   :func:`CheckConstraints`, this requires grouping the constraints,
    190   as described below.
    191 * In refinements, :func:`GenerateConstraints` is then called to
    192   create the constraints that will be used, see below for
    193 * For debugging constraints, :func:`VarRemapShow` can be called after
    194   :func:`GenerateConstraints` to display the generated constraints.
    195 
    196 Constraint Reorganization (:func:`~GSASIIstrIO.ProcessConstraints`)
     257calls. This is done in GSAS-II from several locations:
     258
     259* For error checking from the tree in :mod:`GSASIIconstrGUI`,
     260  :func:`GSASIIconstrGUI.CheckConstraints` loads constraints from
     261  the data tree.
     262
     263* When the table of refined parameters is shown, constraints are also
     264  processed in function :func:`GSASIIdataGUI.GSASII.OnShowLSParms` using
     265  :func:`GSASIIconstrGUI.CheckConstraints`
     266
     267* To write parameters in the Export sections of the program,
     268  :func:`GSASIIIO.loadParmDict` loads results as well as constraints
     269  from the tree. This works a bit differently from the above, so it
     270  makes direct calls to the constraints routines.
     271
     272* For error checking from a GPX file
     273  :func:`GSASIIstrIO.ReadCheckConstraints` loads constraints
     274  (called in :mod:`GSASIIdataGUI` and :mod:`GSASIIscriptable`),
     275  which is similar to :func:`GSASIIconstrGUI.CheckConstraints`.
     276  :func:`~GSASIIstrIO.ReadCheckConstraints` is called by
     277  :meth:`GSASIIdataGUI.GSASII.OnRefine` and
     278  :meth:`GSASIIdataGUI.GSASII.OnSeqRefine`
     279  before constraints are generated for use in refinements so they can
     280  be shown in the GUI. This is also called to check for errors in
     281  :class:`GSASIIscriptable.G2Project`.
     282
     283* To create the constraints for use in a refinement, in
     284  :mod:`GSASIIstrMain`, functions :func:`GSASIIstrMain.Refine` and
     285  :func:`GSASIIstrMain.SeqRefine` load and process the constraints again.
     286  This is repeated here because :func:`~GSASIIstrMain.Refine` and
     287  :func:`~GSASIIstrMain.SeqRefine` are intended to operate as stand-alone
     288  routines that may be called directly.
     289
     290* After sequential fits have been completed, the previously processed
     291  constraint info is read from the sequential results section of the
     292  data tree. Function
     293  :func:`GSASIIseqGUI.UpdateSeqResults` displays the sequential results
     294  table
     295also processes constraints.
     296
     297TODO: Note that G2stIO.makeTwinFrConstr is called only in one place. It probably needs to be included in all of the above.
     298
     299When constraints are processed, the following steps are used: 
     300
     301#. Constraints are stored in separate lists in the data tree to
     302   simplify their creation and their GUI display.
     303   In the initial processing, all of the stored constraints are appended
     304   into a single list.
     305
     306#. Then :func:`InitVars` is used to initialize the global variables in
     307   this module (:mod:`GSASIImapvars`). This may be done before the previous
     308   step.
     309
     310#. Then :func:`ProcessConstraints` is used to initially process the
     311   constraints user-supplied constraints (from the data tree),
     312   as described in :ref:`Constraint Reorganization <ProcessConstraints>`.
     313   When constraints are read from a GPX file, rather than the data tree, use
     314   :func:`GSASIIstrIO.ReadConstraints` (which calls :func:`ProcessConstraints`).
     315
     316#. Symmetry-generated equivalences are then created in
     317   :func:`GSASIIstrIO.GetPhaseData`, which also calls
     318   :func:`GSASIIstrIO.cellVary` and for Pawley refinements
     319   :func:`GSASIIstrIO.GetPawleyConstr`. These are entered directly into this
     320   module's globals using :func:`StoreEquivalence`.
     321
     322#. Constraints/equivalences are then checked for possible conflicts with
     323   :func:`GenerateConstraints`, this requires grouping the constraints,
     324   as described below.
     325
     326#. :func:`GenerateConstraints` is then called to
     327   create the constraints that will be used,
     328   :ref:`see below <GenerateConstraints>` for more details.
     329
     330#. For debugging constraints, :func:`VarRemapShow` can be called after
     331   :func:`GenerateConstraints` to display the generated constraints.
     332
     333.. _ProcessConstraints:
     334
     335Constraint Reorganization (:func:`ProcessConstraints`)
    197336^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    198337
    199 :func:`GSASIIstrIO.ProcessConstraints` is used to initially process the
    200 constraints. This does these things:
    201 
    202 1. The "Hold", "Const" and "New Var" expressions are split between two paired lists,
    203    :data:`constDictList` and :data:`fixedList` which are set:
     338:func:`ProcessConstraints` is used to initially process the
     339constraints from the list of dict entries. The "Const" and "New Var" are placed into two
     340lists (:data:`constrDict` and :data:`fixedList`) that are later used for parameter
     341grouping (in :func:`GenerateConstraints`). "Hold" and "Equivalence" constraints are
     342separated into separate storage.
    204343 
    205    * For "Hold" entries a dict with a single entry is placed in constDictList where
    206      the key is the parameter name (associated value is 0.0) and fixedList gets a
    207      value of 0.0.
    208    * For "Const" entries, a dict with multiple entries is placed in constDictList where
    209      the key is the parameter name and the value is the multiplier for the parameter,
    210      while fixedList gets a string value corresponding to the constant value for
     344   For "**Const**" entries,
     345     a dict with multiple entries is placed in :data:`constrDict` where
     346     each dict key is the parameter name and the value is the multiplier for the parameter,
     347     while :data:`fixedList` gets a string value corresponding to the constant value for
    211348     the expression.
    212    * For "New Var" entries, a dict with multiple entries is placed in constDictList
    213      where the key is the parameter name and the value is the multiplier
    214      for the parameter; an additional key "_vary" is given the value of True or False
    215      depending on the refinement flag setting. The corresponding entry in
    216      fixedList is None
    217 
    218    The output from this will have this form where the first entry is a "Const", the
    219    second is a "New Var" and the final is a "Hold".
     349
     350   For "**New Var**" entries,
     351     a dict with multiple entries defined identically to
     352     to that used in "Const" entries. The differences between "New Var" and "Const" entries is
     353     that for "Const" entries, a constant value (as a string) is placed in :data:`fixedList` while
     354     for "New Var" entries corresponding entry in :data:`fixedList` is None.
     355     Also, one or two additional entries are created in the dict for "New Var" constraints:
     356     an entry with key "_vary" is given the value of True or False
     357     depending on the refinement flag setting;
     358     an entry with key "_name" will be created if the "New Var" parameter has a supplied name.
     359
     360   For "**Hold**" entries,
     361     the “Hold” constraints are stored in global variables :data:`holdParmList` by calling
     362     :func:`StoreHold`; holds that are generated by this code are stored in :data:`newHolds`.
     363
     364   **Equivalences** are stored using :func:`StoreEquivalence` into this module's globals
     365     (:data:`dependentParmList`, :data:`arrayList`, :data:`invarrayList`, :data:`indParmList`,
     366     and :data:`symGenList`).
     367     For each equivalence:
     368
     369     * a list with one entry, the name of the independent parameter is placed in :data:`~GSASIImapvars.indParmList`;
     370     * a list with one or more parameter name is placed in :data:`~GSASIImapvars.dependentParmList`;
     371     * the value None is added to :data:`~GSASIImapvars.arrayList`;
     372     * a list of multipliers for each dependent variable is placed in :data:`~GSASIImapvars.invarrayList`
     373     * an entry of either True or False is placed in :data:`~GSASIImapvars.symGenList`, where True indicates that the entry has been generated from symmetry.
     374
     375The output from :func:`ProcessConstraints` will have the form as below,
     376where the first entry is a "Const", the second is a "New Var" and the final is a "Hold".
    220377
    221378  .. code-block:: python
     
    227384    fixedList = ['5.0', None, '0']
    228385
    229 
    230 2. Equivalences are stored using :func:`StoreEquivalence`
    231    into this module's globals (:data:`~GSASIImapvars.arrayList`,
    232    :data:`~GSASIImapvars.invarrayList`,    :data:`~GSASIImapvars.indParmList`,
    233    :data:`~GSASIImapvars.dependentParmList` and  :data:`~GSASIImapvars.symGenList`)
    234 
    235 
    236 Parameter Grouping (:func:`GenerateConstraints`)
    237 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    238 
    239 Functions :func:`CheckConstraints` and :func:`GenerateConstraints` are used to
     386.. _GenerateConstraints:
     387
     388Constraint Checking and Grouping (:func:`GenerateConstraints`)
     389^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     390
     391Function :func:`GenerateConstraints` is used to
    240392process the parameter equivalences and constraint lists created in
    241 :func:`~GSASIIstrIO.ProcessConstraints`. The former is used to generate
    242 error messages and the latter to generate the internal information used to
     393:func:`ProcessConstraints` (``constrDict`` and ``fixedList``). :func:`GenerateConstraints`
     394is used to generate error/warning messages, to set up lists that are used to show this
     395information for the GUI (using :func:`getConstrError`) and to
     396generate the information stored in :ref:`global arrays <GlobalVariables>` that are used later to
    243397apply the constraints.
    244398
    245 Initially, in both a list of parameters that are fixed and those used in
    246 constraint relations are tabulated in :func:`CheckEquivalences`. The equivalence
    247 relations are the scanned for the following potential problems:
    248 
    249 1. a parameter is used as a dependent variable in more than one
    250    equivalence relation
    251 2. a parameter is fixed and used in in an equivalence relation either
    252    as a dependent or independent variable
    253 3. a parameter is used as a dependent variable in one equivalence relation and
    254    as a independent variable in another
    255 4. a parameter is used in in an equivalence relation (either
    256    as a dependent or independent variable) and is used in a constraint
    257    expression
    258 5. a parameter is not defined in a particular refinement, but is used in an
    259    equivalence relation
    260 6. a parameter uses a wildcard for the histogram number (sequential refinements)
    261 
    262 Cases 1 & 2 above cannot be corrected, and result in errors. Cases 3 & 4 are
    263 potentially corrected with :func:`MoveConfEquiv`, as described below.
    264 Case 5 causes the equivalence to
    265 be dropped. Case 6 causes the current histogram number to be substituted for
    266 the wildcard.
    267 
    268 For cases 3 & 4, :func:`MoveConfEquiv` is used to change these equivalences
    269 into "Const" equations. This can potentially mean that additional
    270 equivalences will be problematic, so if there are changes made by
    271 :func:`MoveConfEquiv`, :func:`CheckEquivalences` is repeated. If any problem
    272 cases are noted, the refinement cannot be performed.
    273 
    274 Constraint expressions ("Const" and "New Var") are sorted into
     399When a sequential refinement is in progress, the constraints are scanned for parameters
     400that have a wildcard (*) for the histogram number, such as 1:*:Scale which would refer
     401to the phase fraction for Phase ` in every histogram. The "*" will be replaced with
     402the number of the current histogram.
     403
     404Equivalences are checked with :func:`CheckEquivalences` (described in detail
     405:ref:`below <CheckEquivalences>`). This may result in the creation of additional "Hold"
     406and "Constr" constraints being added to the ``constrDict`` and ``fixedList`` lists.
     407
     408The "Const" and "New Var" constraint expressions are then scanned for problems:
     409
     410Constraints cannot be processed without changes if any of the terms within have the following:
     411
     412* **Hold (Fixed) parameters**; **Unvaried parameters**; **Multiplier of zero**
     413
     414  If all parameters in a constraint are either not refined, or are marked as "Hold", or have a multiplier
     415  that evaluates as 0.0, the constraint can be ignored.
     416
     417  If all but one parameter in a constraint are either not refined, or are marked as "Hold", or have a multiplier
     418  that evaluates as 0.0, the constraint determines a static value for the parameter. The appropriate
     419  constant values are substituted into the constraint equation and the one remaining parameter
     420  is set as "Hold".
     421
     422  If two or more parameter remain in a constraint after removing parameters that are not refined, or
     423  are marked as "Hold", or have a multiplier that evaluates as 0.0, the constraint can still be used.
     424  The appropriate constant values are substituted into the constraint equation, any zero multiplier
     425  terms set as "Hold" and the remaining terms in the constraint are used.
     426
     427* **Undefined parameters**
     428
     429  If all parameters in a constraint are undefined, the constraint can be ignored.
     430
     431  If some, but not all, parameters in a constraint are undefined, the constraint cannot be processed.
     432  The remaining parameters will be set as "Hold" and the constraint will be ignored. One exception:
     433  atom position constraints (p::dA[xyz]:#) will be assumed as zero.
     434
     435Constraint expressions ("Const" and "New Var") are sorted by routine :func:`GroupConstraints` into
    275436groups so that each group contains the minimum number of entries that
    276 ensures each parameter is referenced in only one group in
    277 :func:`GroupConstraints`. This is done by scanning the
    278 list of dicts in :data:`constDictList` one by one and making a list
     437ensures each parameter is referenced in only one group.
     438This is done by scanning the
     439list of dicts in :data:`constrDict` one by one and making a list
    279440of parameters used in that constraint expression. Any expression that contains
    280 a parameter in is in that list is added to the current group and those
     441a parameter in that list is added to the current group and those
    281442parameters are added to this list of parameters. The list of ungrouped
    282443expressions is then scanned again until no more expressions are added to the
    283444current group. This process is repeated until every expression has been
    284445placed in a group. Function :func:`GroupConstraints` returns two lists of lists.
    285 The first has, for each group, a list of the indices in :data:`constDictList`
     446The first has, for each group, a list of the indices in :data:`constrDict`
    286447that comprise the group (there can be only one). The second list contains,
    287448for each group, the unique parameter names in that group.
    288449
    289450Each constraint group is then processed. First, wildcard parameters are
    290 renamed (in a sequential refinement). Any fixed parameters that are used
     451renamed (in a sequential refinement). Any held parameters that are used
    291452in constraints are noted as errors. The number of refined parameters and
    292453the number of parameters that are not defined in the current refinement are
     
    367528in the constraint dict, but at present this is not implemented.) Names are
    368529generated using :data:`paramPrefix` which is set to ``"::constr"``, plus a
    369 number to make the new parameter name unique. Global dict :data:`genVarLookup`
    370 provides a lookup table, where the names of the parameters related to this new
    371 parameter can be looked up easily.
     530number to make the new parameter name unique.
    372531
    373532Finally the parameters used as input to the constraint are placed in
     
    378537constraint matrix is placed in :data:`~GSASIImapvars.invarrayList` and a list
    379538of the "New Var" parameters and a list of the fixed values (as str's)
    380 is placed in :data:`~GSASIImapvars.indParmList`. A lookup table for
    381 fixed values as floats is placed in :data:`~GSASIImapvars.fixedDict`.
     539is placed in :data:`~GSASIImapvars.indParmList`.
    382540Finally the appropriate entry in :data:`~GSASIImapvars.symGenList` is set to
    383541False to indicate that this is not a symmetry generated constraint.
    384542
    385 
    386 *Externally-Accessible Routines*
    387 ---------------------------------
    388 
    389 To define a set of constrained and unconstrained relations, one
    390 defines a list of dictionary defining constraint parameters and their
    391 values, a list of fixed values for each constraint and a list of
    392 parameters to be varied. In addition, one uses
    393 :func:`StoreEquivalence` to define parameters that are equivalent.
    394 Use :func:`EvaluateMultipliers` to convert formula-based constraint/equivalence
    395 multipliers to numbers and then
    396 use :func:`CheckConstraints` to check that the input is
    397 internally consistent and finally :func:`GroupConstraints` and
    398 :func:`GenerateConstraints` to generate the internally used
    399 tables. Routine :func:`Map2Dict` is used to initialize the parameter
    400 dictionary and routine :func:`Dict2Map`, :func:`Dict2Deriv`, and
    401 :func:`ComputeDepESD` are used to apply constraints. Routine
    402 :func:`VarRemapShow` is used to print out the constraint information,
    403 as stored by :func:`GenerateConstraints`. Further information on each routine
    404 is below:
    405 
    406 :func:`InitVars`
    407   This is optionally used to clear out all defined previously defined constraint information
    408  
    409 :func:`StoreEquivalence`
    410   To implement parameter redefinition, one calls StoreEquivalence. This should be called for every set of
    411   equivalence relationships. There is no harm in using StoreEquivalence with the same independent variable:
    412 
    413   .. code-block:: python
    414 
    415        StoreEquivalence('x',('y',))
    416        StoreEquivalence('x',('z',))
    417 
    418   or equivalently
    419 
    420   .. code-block:: python
    421 
    422        StoreEquivalence('x',('y','z'))
    423 
    424   The latter will run more efficiently. Note that mixing independent
    425   and dependent variables would require assignments, such as
    426 
    427   .. code-block:: python
    428 
    429         StoreEquivalence('x',('y',))
    430         StoreEquivalence('y',('z',))
    431 
    432   would require that equivalences be applied in a particular order and
    433   thus is implemented as a constraint equation rather than an equivalence.
    434        
    435   Use StoreEquivalence before calling GenerateConstraints or CheckConstraints
    436 
    437 :func:`CheckConstraints`
    438    check that input in internally consistent
    439 
    440 :func:`GenerateConstraints`
    441    generate the internally used tables from constraints and equivalences
    442 
    443 :func:`EvaluateMultipliers`
    444    Convert any string-specified (formula-based) multipliers to numbers. Call this before
    445    using :func:`CheckConstraints` or :func:`GenerateConstraints`.
    446    At present, the code may pass only the dict for phase (atom/cell) parameters,
    447    but this could be expanded if needed.
    448 
    449 :func:`Map2Dict`
    450    To determine values for the parameters created in this module, one
    451    calls Map2Dict. This will not apply contraints.
    452 
    453 :func:`Dict2Map`
    454    To take values from the new independent parameters and constraints,
    455    one calls Dict2Map and set the parameter array, thus appling contraints.
    456 
    457 :func:`Dict2Deriv`
    458    Use Dict2Deriv to determine derivatives on independent parameters
    459    from those on dependent ones.
    460 
    461 :func:`ComputeDepESD`     
    462    Use ComputeDepESD to compute uncertainties on dependent variables.
    463 
    464 :func:`VarRemapShow`
    465    To show a summary of the parameter remapping, one calls VarRemapShow.
     543.. _CheckEquivalences:
     544
     545Equivalence Checking and Reorganization (:func:`CheckEquivalences`)
     546^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     547
     548Equivalences need to be checked for usages that could be internally conflicted
     549or have possible conflicts with other constraints.
     550
     551**Mixed parameter use:**
     552
     553 **Note** that cycling through the equivalences may be needed to find all mixed-use
     554 parameters, see below.
     555
     556* A parameter should not show up as a dependent variable in two equivalence expressions,
     557  such as::
     558
     559      ::x1 -> ::x3
     560      ::x2 -> ::x3
     561
     562  This will be processed by turning the equivalences into two constraint equations::
     563
     564      ::x1 - ::x3 = 0
     565      ::x2 - ::x3 = 0
     566
     567  which can be satisfied when ``::x1 = ::x2 = ::x3``. If  ``::x1`` and ``::x2`` had been
     568  intended to be independent parameters, then the above equivalences would be conflict and
     569  cannot be statisfied.
     570
     571* If a parameter is used both as an independent and as a dependent variable (*chaining*),
     572  as is in these two equivalence expressions::
     573
     574      ::x1 -> ::x2 & ::x4
     575      ::x2 -> ::x3
     576
     577  This can also be addressed by turning these equivalences into three constraint equations::
     578
     579   ::x1 - ::x2 = 0
     580   ::x1 - ::x4 = 0
     581   ::x2 - ::x3 = 0
     582
     583  which can be satisfied when ``::x1 = ::x2 = ::x3 = ::x4``
     584
     585*  Use of parameters in both equivalences and "Const" or "New Var" constraint expressions makes
     586   logical sense::
     587
     588   ::x1 -> ::x2 & ::x4
     589   ::x2 + ::x3 = 0
     590
     591   This can also be addressed by turning the equivalence into two constraint equations::
     592
     593   ::x1 - ::x2 = 0
     594   ::x1 - ::x4 = 0
     595
     596   With the addition of the "Const" equation (``::x2 + ::x3 = 0``), the solution will require
     597   ``::x1 = ::x2 = -1.0*::x3 = ::x4``
     598
     599* Cycling is needed to find all equivalences that must be converted.
     600  Consider this set of constraints::
     601
     602   ::x2 + ::x3 = 0
     603   ::x1 -> ::x2
     604   ::x1 -> ::x4
     605
     606 In the first pass the equivalence with ``::x2`` would be converted to a "Const" constraint
     607 and in the second pass
     608 the other equivalence with ``::x1`` would be converted.
     609
     610
     611**Mixing Hold (Fixed) parameters in equivalences**
     612
     613* If one parameter is designated as a "Hold" in an equivalence, then all parameters in that
     614  equivalence cannot be varied. Considering this equivalence::
     615
     616      ::x1 -> ::x2 & ::x4
     617
     618  If any of the three parameters (``::x1``, ``::x2``, or `::x4`) are marked as Hold, then
     619  the other two parameters may not be varied and will also be set with a "Hold".
     620
     621
     622**Unvaried parameters in equivalences**
     623
     624* If no parameters in an equivalence are varied, then the equivalence is ignored.
     625
     626* If only some parameters are marked as varied then
     627  *none of the parameters can be varied*; any varied parameters will be set with a "Hold".
     628
     629
     630**Undefined parameters in equivalences**
     631
     632  Parameters may be placed in equivalences that are not actually defined in a project.
     633  This can occur in two ways. If an equivalence is created in the GUI for a parameter that
     634  is later supplanted with a different model (for example, changing from isotropic size
     635  broadening to uniaxial broadening replaces the isotropic broadening term with two different
     636  uniaxial terms) or symmetry may require restrictions on anisotropic ADPs that are not
     637  in use).
     638
     639* If the independent parameter is undefined, then any dependent parameters that are defined
     640  are set as "Hold" and the equivalence is ignored.
     641
     642* If all dependent parameters are undefined, then the equivalence is ignored.
     643
     644* If a dependent parameter is undefined, then that parameter is dropped from the equivalence.
     645
     646
     647**Multiplier of zero in equivalences**
     648
     649  Any dependent parameter that has a multiplier of zero will be dropped from the equivalence.
     650  If no terms remain, then the equivalence is ignored. (Independent parameters do not
     651  have a multiplier).
     652
     653
     654.. _GlobalVariables:
    466655
    467656*Global Variables*
    468657------------------
    469658
    470 dependentParmList:
    471    a list containing group of lists of
    472    parameters used in the group. Note that parameters listed in
    473    dependentParmList should not be refined as they will not affect
    474    the model
    475 
    476 indParmList:
    477      a list containing groups of Independent parameters defined in
    478      each group. This contains both parameters used in parameter
    479      redefinitions as well as names of generated new parameters.
    480 
    481 arrayList:
    482      a list containing group of relationship matrices to relate
    483      parameters in dependentParmList to those in indParmList. Unlikely
    484      to be used externally.
    485 
    486 invarrayList:
    487      a list containing group of relationship matrices to relate
    488      parameters in indParmList to those in dependentParmList. Unlikely
    489      to be used externally.
    490 
    491 fixedVarList:
    492      a list of parameters that have been 'fixed'
    493      by defining them as equal to a constant (::var: = 0). Note that
    494      the constant value is ignored at present. These parameters are
    495      later removed from varyList which prevents them from being refined.
    496      Unlikely to be used externally.
    497 
    498 fixedDict:
    499      a dictionary containing the fixed values corresponding
    500      to parameter equations.  The dict key is an ascii string, but the
    501      dict value is a float.  Unlikely to be used externally.
    502 
    503 symGenList:
    504      a list of boolean values that will be True to indicate that a constraint
    505      (only equivalences) is generated by symmetry (or Pawley overlap)
    506      
    507 problemVars:
    508      a list containing parameters that show up in constraints producing errors
    509 
    510 
    511 
    512 *Routines/variables*
    513 ---------------------
    514 
    515 Note that parameter names in GSAS-II are strings of form ``<ph#>:<hst#>:<nam>`` or ``<ph#>::<nam>:<at#>``.
     659This module uses a number of global variables. One set is used to store the
     660constraints and equivalences after processing by :func:`StoreEquivalence` and
     661:func:`GenerateConstraints`. 
     662These globals are expected to be used only by this module's (:mod:`GSASIImapvars`) internal routines.
     663
     664
     665.. tabularcolumns:: |l|p{4.5in}|
     666
     667=============================  ===================================================================
     668  variable                      explanation
     669=============================  ===================================================================
     670:data:`dependentParmList`        a list containing group of lists of
     671                                 parameters used in the group. Note that parameters listed in
     672                                 dependentParmList will not be included in the Hessian as their
     673                                 derivatives will not affect the model
     674
     675:data:`indParmList`              a list containing groups of Independent parameters defined in
     676                                 each group. This contains both parameters used in parameter
     677                                 redefinitions as well as names of generated new parameters.
     678
     679:data:`arrayList`                a list containing group of relationship matrices to relate
     680                                 parameters in dependentParmList to those in indParmList.
     681
     682:data:`invarrayList`             a list containing group of relationship matrices to relate
     683                                 parameters in indParmList to those in dependentParmList.
     684                                 Unlikely to be used externally.
     685
     686:data:`holdParmList`             a list of parameters that have been marked as "Hold".
     687                                 Unlikely to be used externally. Holds have a single
     688                                 entry in the indParmList parameter list.
     689                                 Set in :func:`StoreHold`. (Also see :data:`newHolds`)
     690
     691:data:`symGenList`               a list of boolean values that will be True to indicate
     692                                 that an equivalence was generated internally GSAS-II
     693                                 meaning it is generated based on symmetry, twining
     694                                 or Pawley overlap.
     695
     696:data:`dependentVars`            a list of dependent variables in equivalences, compiled
     697                                 from (:data:`dependentParmList`).
     698                                 Used within :func:`GetDependentVars`.
     699
     700:data:`independentVars`          a list of dependent variables in equivalences, compiled
     701                                 from (:data:`indParmList`).
     702                                 Used within :func:`GetIndependentVars`.
     703
     704:data:`saveVaryList`             a list of the varied parameters used when constraints
     705                                 were last processed.
     706
     707=============================  ===================================================================
     708
     709
     710A second set of global variables are set in :func:`GenerateConstraints` with lists of parameter
     711names from equivalences and constraints. Used in :func:`CheckEquivalences` and
     712:func:`getConstrError`.
     713
     714.. tabularcolumns:: |l|p{4.5in}|
     715
     716=============================  ===================================================================
     717  variable                      explanation
     718=============================  ===================================================================
     719:data:`depVarList`             a list of the parameters used in equivalences as dependent
     720                               parameters for all equivalences initially specified (including
     721                               those to be reclassified as "Constr" constraints.)
     722:data:`indepVarList`           a list of the parameters used in equivalences as independent
     723                               parameters for all equivalences initially specified (including
     724                               those to be reclassified as "Constr" constraints.)
     725:data:`constrVarList`          a list of the parameters that are used in "Constr" or
     726                               "New Var" constraints. Does not include those in equivalences
     727                               to be reclassified as "Constr" constraints.)
     728=============================  ===================================================================
     729
     730
     731.. tabularcolumns:: |l|p{4.5in}|
     732
     733A third set of global variables to store equivalence warning information.
     734Set in :func:`CheckEquivalences` and :func:`GenerateConstraints`.
     735Used in :func:`getConstrError` to display warning messages.
     736
     737=============================  ===================================================================
     738  variable                      explanation
     739=============================  ===================================================================
     740:data:`convVarList`            parameters in equivalences that were converted to "Const"
     741                               constraints
     742:data:`multdepVarList`         parameters used as dependent parameters in equivalences
     743                               multiple times
     744:data:`newHolds`               parameters to be added as "Hold"s
     745:data:`unvariedParmsList`      parameters used in equivalences and constraints
     746                               that are not varied
     747:data:`undefinedVars`          parameters used in equivalences
     748                               that are not defined in the parameter dict (parmDict)
     749:data:`groupErrors`            parameters in constraints that cause grouping errors
     750=============================  ===================================================================
     751
     752
     753
     754*GSASIImapvars Routines/variables*
     755---------------------------------------
     756
     757Note that parameter names in GSAS-II are strings of form ``<ph#>:<hst#>:<nam>`` or ``<ph#>::<nam>:<at#>``
     758where ``<ph#>`` is a phase number, ``<hst#>`` is a histogram number and ``<at#>`` is an atom number.
     759``<nam>`` is a name that determines the parameter type (see :func:`GSASIIobj.CompileVarDesc`). When
     760stored in the data tree, parameters are saved as :class:`GSASIIobj.G2VarObj` objects
     761so that they can be resolved if the phase/histogram order changes.
     762
    516763"""
    517764
    518765from __future__ import division, print_function
     766import copy
    519767import numpy as np
    520 import sys
    521768import GSASIIpath
    522769GSASIIpath.SetVersionNumber("$Revision$")
     770import GSASIIobj as G2obj
    523771# data used for constraints;
    524772debug = False # turns on printing as constraint input is processed
    525773
    526 # note that constraints are stored listed by contraint groups,
    527 # where each constraint
    528 # group contains those parameters that must be handled together
     774#------------------------------------------------------------------------------------
     775# Global vars used for storing constraints and equivalences after processing
     776#   note that constraints are stored listed by contraint groups,
     777#   where each constraint
     778#   group contains those parameters that must be handled together
     779
    529780dependentParmList = []
    530781'''a list of lists where each item contains a list of parameters in each constraint group.
     
    544795(in :data:`dependentParmList`).
    545796'''
    546 fixedDict = {}
    547 '''A dict lookup-table containing the fixed values corresponding
    548 to defined parameter equations. Note the key is the original ascii string
    549 and the value in the dict is a float.
    550 '''
    551 fixedVarList = []
    552 '''List of parameters that should not be refined.
     797holdParmList = []
     798'''List of parameters that should not be refined ("Hold"s).
     799Set in :func:`StoreHold`. Initialized in :func:`InitVars`.
    553800'''
    554801symGenList = []
    555802'''A list of flags that if True indicates a constraint was generated by symmetry
    556803'''
    557 problemVars = []
    558 '''a list of parameters causing errors
     804dependentVars = []
     805'''A list of dependent variables in equivalences, compiled from (:data:`dependentParmList`).
     806Used within :func:`GetDependentVars`
    559807'''
    560 dependentVars = []
    561 'A list of dependent variables, taken from (:data:`dependentParmList`).'
    562808independentVars = []
    563 'A list of dependent variables, taken from (:data:`indParmList`).'
    564 genVarLookup = {}
    565 'provides a list of parameters that are related to each generated parameter'
     809'''A list of dependent variables in equivalences, compiled from (:data:`indParmList`).
     810Used within :func:`GetIndependentVars`
     811'''
     812saveVaryList = []
     813'''A list of the varied parameters that was last supplied when constraints were
     814processed. This is set in :func:`GenerateConstraints` and updated in
     815:func:`Map2Dict`. Used in :func:`VarRemapShow`
     816'''
     817#------------------------------------------------------------------------------------
     818# Global vars set in :func:`GenerateConstraints`. Used for intermediate processing of
     819# constraints.
     820
     821constrVarList = []
     822'List of parameters used in "Constr" and "New Var" constraints'
     823indepVarList = []
     824'A list of all independent parameters in equivalences'
     825depVarList = []
     826'A list of all dependent parameters in equivalences'
     827
     828#------------------------------------------------------------------------------------
     829# global variables is used in :func:`getConstrError` to store error and warning information.
     830# set in CheckEquivalences and in GenerateConstraints
     831convVarList = []
     832'parameters in equivalences that were converted to "Const" constraints'
     833multdepVarList = []
     834'parameters used as dependents multiple times in equivalences'
     835newHolds = []
     836'parameters that will be added as "Hold"s based on use in equivalences and constraints'
     837unvariedParmsList = []
     838'parameters used in equivalences that are not varied'
     839undefinedVars = []
     840'parameters used in equivalences that are not defined in the parameter dict'
     841groupErrors = []
     842'parameters in constraints where parameter grouping and matrix inversion fails'
     843
     844#------------------------------------------------------------------------------------
    566845paramPrefix = "::constr"
    567846'A prefix for generated parameter names'
     
    569848'The number to be assigned to the next constraint to be created'
    570849
     850#------------------------------------------------------------------------------------
    571851class ConstraintException(Exception):
    572     '''Defines an Exception that is used when an exception is raised processing constraints
     852    '''Defines an Exception that is used when an exception is raised processing constraints.
     853    Raised in :func:`GenerateConstraints` during sequential fits. Possible (but highly unlikely)
     854    to be raised in :func:`CheckEquivalences` (called by :func:`GenerateConstraints`) if an
     855    infinite loop is detected.
     856    Also raised in :func:`GramSchmidtOrtho` and :func:`_SwapColumns` but caught
     857    within :func:`GenerateConstraints`.
    573858    '''
    574859    pass
     
    576861def InitVars():
    577862    '''Initializes all constraint information'''
    578     global dependentParmList,arrayList,invarrayList,indParmList,fixedDict,consNum,symGenList
     863    global dependentParmList,arrayList,invarrayList,indParmList,consNum,symGenList
    579864    dependentParmList = [] # contains a list of parameters in each group
    580865    arrayList = [] # a list of of relationship matrices
    581866    invarrayList = [] # a list of inverse relationship matrices
    582867    indParmList = [] # a list of names for the new parameters
    583     fixedDict = {} # a dictionary containing the fixed values corresponding to defined parameter equations
     868    consNum = 0 # number of the next constraint to be created
    584869    symGenList = [] # Flag if constraint is generated by symmetry
    585     consNum = 0 # number of the next constraint to be created
    586     global genVarLookup
    587     genVarLookup = {}
     870    global holdParmList
     871    holdParmList = []
    588872
    589873def VarKeys(constr):
     
    603887
    604888def GroupConstraints(constrDict):
    605     """divide the constraints into groups that share no parameters.
     889    """Divide the constraints into groups that share no parameters.
    606890
    607891    :param dict constrDict: a list of dicts defining relationships/constraints
     
    619903      * a list containing lists of parameter names contained in each group
    620904     
    621       """
     905    """
    622906    assignedlist = [] # relationships that have been used
    623907    groups = [] # contains a list of grouplists
     
    645929    return groups,ParmList
    646930
    647 def CheckConstraints(varyList,constrDict,fixedList):
    648     '''Takes a list of relationship entries comprising a group of
    649     constraints and checks for inconsistencies such as conflicts in
    650     parameter/variable definitions and or inconsistently varied parameters.
    651 
    652     :param list varyList: a list of parameters names that will be varied
    653 
    654     :param dict constrDict: a list of dicts defining relationships/constraints
    655       (as created in :func:`GSASIIstrIO.ProcessConstraints` and
    656       documented in :func:`GroupConstraints`)
    657 
    658     :param list fixedList: a list of values specifying a fixed value for each
    659       dict in constrDict. Values are either strings that can be converted to
    660       floats or ``None`` if the constraint defines a new parameter rather
    661       than a constant.
    662 
    663     :returns: two strings:
    664 
    665       * the first lists conflicts internal to the specified constraints
    666       * the second lists conflicts where the varyList specifies some
    667         parameters in a constraint, but not all
    668        
    669       If there are no errors, both strings will be empty
    670     '''
    671     import re
    672     global dependentParmList,arrayList,invarrayList,indParmList,consNum
    673     global problemVars
    674     # Process the equivalences
    675     #    If there are conflicting parameters, move them into constraints. This
    676     #    may create new conflicts, requiring movement of other parameters, so
    677     #    loop until there are no more changes to make.
    678     parmsChanged = True
    679     while parmsChanged:
    680         parmsChanged = 0
    681         errmsg,warnmsg,fixVlist,dropVarList,translateTable = CheckEquivalences(
    682             constrDict,varyList)
    683         #print('debug: using MoveConfEquiv to address =',errmsg)
    684         if problemVars: parmsChanged,mvMsg = MoveConfEquiv(constrDict,fixedList)
    685 #    GSASIIpath.IPyBreak()
    686 
    687     groups,parmlist = GroupConstraints(constrDict)
    688     # scan through parameters in each relationship. Are all varied? If only some are
    689     # varied, create a warning message.
    690     for group,varlist in zip(groups,parmlist):
    691         if len(varlist) == 1:   # process fixed (held) variables
    692             var = varlist[0]
    693             if var not in fixedVarList:
    694                 fixedVarList.append(var)
    695             continue
    696         for rel in group:
    697             varied = 0
    698             notvaried = ''
    699             for var in constrDict[rel]:
    700                 if var.startswith('_'): continue
    701                 if not re.match('[0-9]*:[0-9\\*]*:',var):
    702                     warnmsg += "\nParameter "+str(var)+" does not begin with a ':'"
    703                 if var in varyList:
    704                     varied += 1
    705                 else:
    706                     if notvaried: notvaried += ', '
    707                     notvaried += var
    708                 if var in fixVlist:
    709                     errmsg += '\nParameter '+var+" is Fixed and used in a constraint:\n\t"
    710                     if var not in problemVars: problemVars.append(var)
    711                     errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])+"\n"
    712             if varied > 0 and varied != len(VarKeys(constrDict[rel])):
    713                 warnmsg += "\nNot all parameters refined in constraint:\n\t"
    714                 warnmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
    715                 warnmsg += '\nNot refined: ' + notvaried + '\n'
    716     if errmsg or warnmsg:
    717         return errmsg,warnmsg
    718 
    719     # now look for process each group and create the relations that are needed to form
    720     # non-singular square matrix
    721     for group,varlist in zip(groups,parmlist):
    722         if len(varlist) == 1: continue # a constraint group with a single parameter can be ignored
    723         if len(varlist) < len(group): # too many relationships -- no can do
    724             errmsg += "\nOver-constrained input. "
    725             errmsg += "There are more constraints " + str(len(group))
    726             errmsg += "\n\tthan parameters " + str(len(varlist)) + "\n"
    727             for rel in group:
    728                 errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
    729                 errmsg += "\n"
    730                 continue
    731         try:
    732             multarr = _FillArray(group,constrDict,varlist)
    733             _RowEchelon(len(group),multarr,varlist)
    734         except:
    735             errmsg += "\nSingular input. "
    736             errmsg += "There are internal inconsistencies in these constraints\n"
    737             for rel in group:
    738                 errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
    739                 errmsg += "\n"
    740             continue
    741         try:
    742             multarr = _FillArray(group,constrDict,varlist,FillDiagonals=True)
    743             GramSchmidtOrtho(multarr,len(group))
    744         except:
    745             errmsg += "\nUnexpected singularity with constraints (in Gram-Schmidt)\n"
    746             for rel in group:
    747                 errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
    748                 errmsg += "\n"
    749             continue
    750         mapvar = []
    751         group = group[:]
    752         # scan through all generated and input parameters
    753         # Check again for inconsistent parameter use
    754         # for new parameters -- where varied and unvaried parameters get grouped
    755         # together. I don't think this can happen when not flagged before, but
    756         # it does not hurt to check again.
    757         for i in range(len(varlist)):
    758             varied = 0
    759             notvaried = ''
    760             if len(group) > 0:
    761                 rel = group.pop(0)
    762                 fixedval = fixedList[rel]
    763                 for var in VarKeys(constrDict[rel]):
    764                     if var in varyList:
    765                         varied += 1
    766                     else:
    767                         if notvaried: notvaried += ', '
    768                         notvaried += var
    769             else:
    770                 fixedval = None
    771             if fixedval is None:
    772                 varname = paramPrefix + str(consNum) # assign a name to a parameter
    773                 mapvar.append(varname)
    774                 consNum += 1
    775             else:
    776                 mapvar.append(fixedval)
    777             if varied > 0 and notvaried != '':
    778                 warnmsg += "\nNot all parameters refined in generated constraint"
    779                 warnmsg += '\nPlease report this unexpected error\n'
    780                 for rel in group:
    781                     warnmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
    782                     warnmsg += "\n"
    783                 warnmsg += '\n\tNot refined: ' + notvaried + '\n'
    784         try:
    785             np.linalg.inv(multarr)           
    786         except:
    787             errmsg += "\nSingular input. "
    788             errmsg += "The following constraints are not "
    789             errmsg += "linearly independent\n\tor do not "
    790             errmsg += "allow for generation of a non-singular set\n"
    791             errmsg += 'Please report this unexpected error\n'
    792             for rel in group:
    793                 errmsg += _FormatConstraint(constrDict[rel],fixedList[rel])
    794                 errmsg += "\n"
    795     _setVarLists([])
    796     return errmsg,warnmsg
    797 
    798931def GenerateConstraints(varyList,constrDict,fixedList,parmDict=None,SeqHist=None):
    799     '''Takes a list of relationship entries comprising a group of
    800     constraints and builds the relationship lists and their inverse
    801     and stores them in global parameters Also checks for internal
    802     conflicts or inconsistencies in parameter/variable definitions.
     932    '''Takes a list of relationship entries that have been stored by
     933    :func:`ProcessConstraints` into lists ``constrDict`` and ``fixedList``
     934
     935    This routine then calls :func:`CheckEquivalences`
     936    for internal consistency. This includes converting equivalenced variables into
     937    constraints when a variable is used in both.
     938
     939    Once checked, parameters are grouped so that any parameters that are used in
     940    more than one constraint are grouped together. This allows checking for incompatible
     941    logic (for example, when four constraints are specified for three variables).
     942
     943    If parmDict is not None, the parameter groups are checked for constraints where
     944    some parameters are varied, but not others. If so, the value for that unvaried
     945    parameter is subtracted from the constant in the constraint.
     946
     947    Once all checks are complete, the constraints are then
     948    converted to the form used to apply them, saving them as global variables within
     949    this module.
    803950
    804951    :param list varyList: a list of parameters names (strings of form
     
    806953   
    807954    :param dict constrDict: a list of dicts defining relationships/constraints
    808       (as defined in :func:`GroupConstraints`)
     955      (as described in :func:`GroupConstraints`)
    809956
    810957    :param list fixedList: a list of values specifying a fixed value for each
     
    818965      refinement. None (default) otherwise. Wildcard parameter names are
    819966      set to the current histogram, when found if not None.
     967
     968    :returns: errmsg,warning,groups,parmlist
     969
     970      **errmsg**
     971        Is an error message or empty if no errors were found
     972      **warning**
     973        Is a warning message about constraints that have been ignored or changed
     974      **groups**
     975        Lists parameter groups
     976      **parmlist**
     977        Lists parameters in each parameter groups
    820978    '''
     979    warninfo = {'msg':'', 'shown':{}}
     980    def warn(msg,cdict=None,val=None):
     981        if cdict is not None and cdict != warninfo['shown']:
     982            warninfo['shown'] = cdict
     983            if warninfo['msg']: warninfo['msg'] += '\n'
     984            warninfo['msg'] += '\nProblem with constraint: ' + _FormatConstraint(cdict,val)
     985        if warninfo['msg']: warninfo['msg'] += '\n'
     986        warninfo['msg'] += '  ' + msg
     987   
    821988    global dependentParmList,arrayList,invarrayList,indParmList,consNum
    822     global genVarLookup
    823     msg = ''
    824     shortmsg = ''
    825     changed = False
    826 
    827     # Process the equivalences
    828     #    If there are conflicting parameters, move them into constraints. This
    829     #    may create new conflicts, requiring movement of other parameters, so
    830     #    loop until there are no more changes to make.
    831     parmsChanged = True
    832     while parmsChanged:
    833         parmsChanged = 0
    834         errmsg,warnmsg,fixVlist,dropVarList,translateTable = CheckEquivalences(
    835             constrDict,varyList,parmDict,SeqHist)
    836         if problemVars:
    837             parmsChanged,mvMsg = MoveConfEquiv(constrDict,fixedList)
    838             changed = True
    839     if errmsg:
    840         msg = errmsg
    841     if warnmsg:
    842         if msg: msg += '\n'
    843         msg += warnmsg
    844 
    845     # scan through parameters in each relationship. Are all varied? If only some are
    846     # varied, create an error message.
    847     groups,parmlist = GroupConstraints(constrDict)
    848     for group,varlist in zip(groups,parmlist):
    849         if len(varlist) == 1:   # process fixed (held) variables
    850             var = varlist[0]
    851             if var not in fixedVarList:
    852                 fixedVarList.append(var)
    853             continue
    854         for rel in group:
    855             varied = 0
    856             notvaried = ''
    857             unused = 0
    858             notused = ''
    859             for var in constrDict[rel]:
     989    # lists of parameters used for error reporting
     990    global undefinedVars # parameters that are used in equivalences but are not defined
     991    undefinedVars = []
     992    global newHolds # additional parameters that should be held
     993    newHolds = []
     994    global groupErrors # parameters in constraints that cause grouping errors
     995    groupErrors = []
     996    global saveVaryList
     997    saveVaryList = copy.copy(varyList)
     998
     999    errmsg = ''  # save error messages here. If non-blank, constraints cannot be used.
     1000    warning = '' # save informational text messages here.
     1001
     1002    # tabulate a list of all parameters as defined initially setting the following global variables:
     1003    global depVarList # parameters used in equivalences as dependent parameters
     1004    global indepVarList # parameters used in equivalences as independent parameters
     1005    global constrVarList  # list of parameters used in other constraints
     1006    constrVarList = []
     1007    for cdict in constrDict:
     1008        constrVarList += [i for i in cdict if i not in constrVarList and not i.startswith('_')]
     1009    # tabulate all parameters in equivalences by type and look for repeated dependent vars
     1010    global depVarList,indepVarList
     1011    depVarList = []  # list of all dependent parameters in equivalences
     1012    indepVarList = []  # list of all independent parameters in equivalences
     1013    for cnum,(varlist,mapvars,multarr,invmultarr) in enumerate(zip(
     1014            dependentParmList,indParmList,arrayList,invarrayList)):
     1015        if multarr is not None: continue # equivalence
     1016        indepVarList += [mv for mv in mapvars if mv not in indepVarList]
     1017        depVarList += [v for v in varlist if v not in depVarList]
     1018   
     1019    # Translate & generate lookup table for wildcard parameter names (sequential fits only)
     1020    # TODO: where should this be done in the sequence of events? Probably here, so that
     1021    # variable names can be looked up in varyList properly. Also, should names be changed in place
     1022    # or stored in translateTable and then looked up later?     
     1023    #translateTable = {} # lookup table for wildcard-referenced parameters
     1024    if SeqHist is not None:
     1025        for varlist,mapvars,multarr,invmultarr in zip(
     1026            dependentParmList,indParmList,arrayList,invarrayList):
     1027            for i,mv in enumerate(mapvars):
     1028                if mv.split(':')[1] == '*':
     1029                    # convert wildcard var to reference current histogram; save translation in table
     1030                    sv = mv.split(':')
     1031                    sv[1] = str(SeqHist)
     1032                    #mapvars[i][1] = translateTable[mv] = ':'.join(sv)
     1033                    mapvars[i][1] = ':'.join(sv)
     1034            for i,(v,m) in enumerate(zip(varlist,invmultarr)):
     1035                if v.split(':')[1] == '*':
     1036                    # convert wildcard var to reference current histogram; save translation in table
     1037                    sv = v.split(':')
     1038                    sv[1] = str(SeqHist)
     1039                    #varlist[i] = translateTable[v] = ':'.join(sv)
     1040                    varlist[i] = ':'.join(sv)
     1041        for rel in constrDict:
     1042            for i,var in enumerate(rel):
    8601043                if var.startswith('_'): continue
    861                 if var.split(':')[1] == '*' and SeqHist is not None:
    862                     # convert wildcard var to reference current histogram; save translation in table
     1044                if var.split(':')[1] == '*': # convert wildcard var to reference current histogram
    8631045                    sv = var.split(':')
    8641046                    sv[1] = str(SeqHist)
    865                     translateTable[var] = ':'.join(sv)
    866                     var = translateTable[var]
    867                 if parmDict is not None and var not in parmDict:
    868                     unused += 1
    869                     if notvaried: notused += ', '
    870                     notused += var
    871                 if var in varyList:
    872                     varied += 1
     1047                    #rel[i][1] = translateTable[var] = ':'.join(sv)   # add to translation in table for later use
     1048                    rel[i][1] = ':'.join(sv)
     1049
     1050    # Process the equivalences; If there are conflicting parameters, move them into constraints
     1051    warning = CheckEquivalences(constrDict,varyList,fixedList,parmDict)
     1052
     1053    # look through "Constr" and "New Var" constraints looking for zero multipliers and
     1054    # Hold, Unvaried & Undefined parameters
     1055    skipList = []
     1056    for cnum,(cdict,val) in enumerate(zip(constrDict,fixedList)):
     1057        valid = 0          # count of good parameter
     1058        # error reporting
     1059        zeroList = []      # parameters with zero multipliers
     1060        holdList = []      # parameters with "Hold"'s
     1061        noVaryList = []    # parameters not varied
     1062        noWildcardList = [] # wildcard parameters in non-sequential fit
     1063        notDefList = []    # parameters not defined
     1064        # processing to be done
     1065        problem = False    # constraint must be dropped
     1066        dropList = []      # parameters to remove
     1067        setAsHoldList = [] # parameters that will be held if the constraint is invalid
     1068        for i,var in enumerate(cdict):
     1069            if var.startswith('_'): continue
     1070            if cdict[var] == 0:    # zero multiplier
     1071                if var not in zeroList: zeroList.append(var)
     1072                setAsHoldList.append(var)
     1073                dropList.append(var)
     1074            elif var in holdParmList:   # already hold
     1075                #if var not in newHolds: newHolds.append(var)
     1076                holdList.append(var)
     1077                dropList.append(var)
     1078            elif ':*:' in var and SeqHist is None:  # unvaried
     1079                noWildcardList.append(var)
     1080                dropList.append(var)
     1081            elif var not in varyList:  # unvaried
     1082                if var not in unvariedParmsList: unvariedParmsList.append(var)
     1083                noVaryList.append(var)
     1084                setAsHoldList.append(var)
     1085                dropList.append(var)
     1086            elif parmDict is not None and var not in parmDict: # not defined, constraint will not be used
     1087                if var not in undefinedVars: undefinedVars.append(var)
     1088                notDefList.append(var)
     1089                if ':dAx:' in var or ':dAy:' in var or ':dAz:' in var: # undefined atoms
     1090                    dropList.append(var) # these can be ignored
    8731091                else:
    874                     if notvaried: notvaried += ', '
    875                     notvaried += var
    876                 if var in fixedVarList:
    877                     msg += '\nError: parameter '+var+" is Fixed and used in a constraint:\n\t"
    878                     msg += _FormatConstraint(constrDict[rel],fixedList[rel])+"\n"
    879             #if unused > 0:# and unused != len(VarKeys(constrDict[rel])):
    880             if unused > 0 and unused != len(VarKeys(constrDict[rel])):
    881                 #msg += "\nSome (but not all) parameters in constraint are not defined:\n\t"
    882                 #msg += _FormatConstraint(constrDict[rel],fixedList[rel])
    883                 #msg += '\nNot used: ' + notused + '\n'
    884                 shortmsg += notused+" not used in constraint\n\t"+_FormatConstraint(constrDict[rel],fixedList[rel])+'\n'
    885             elif varied > 0 and varied != len(VarKeys(constrDict[rel])):
    886                 #msg += "\nNot all parameters refined in constraint:\n\t"
    887                 #msg += _FormatConstraint(constrDict[rel],fixedList[rel])
    888                 #msg += '\nNot refined: ' + notvaried + '\n'
    889                 shortmsg += notvaried+" not varied in constraint\n\t"+_FormatConstraint(constrDict[rel],fixedList[rel])+'\n'
    890     # if there were errors found, go no farther
    891     if shortmsg and SeqHist is not None:
    892         if msg:
    893             print (' *** ERROR in constraint definitions! ***')
    894             print (msg)
    895             raise ConstraintException
    896         print ('*** Sequential refinement: ignoring constraint definition(s): ***')
    897         print (shortmsg)
    898         msg = ''
    899     elif shortmsg:
    900         msg += shortmsg
    901     if msg:
    902         print (' *** ERROR in constraint definitions! ***')
    903         print (msg)
    904         raise ConstraintException
    905                
     1092                    problem = True
     1093            else:
     1094                valid += 1
     1095        for l,m in ((zeroList,"have zero multipliers"), # show warning
     1096                      (holdList,'set as "Hold"'),
     1097                      (noVaryList,"not varied"),
     1098                      (noWildcardList,"wildcard in non-sequential fit"),
     1099                      (notDefList,"not defined")):
     1100            if l:
     1101                msg = "parameter(s) " + m + ': '
     1102                for i,v in enumerate(l):
     1103                    if i != 0: msg += ', '
     1104                    msg += v
     1105                warn(msg,cdict,val)
     1106        if not valid and (problem or len(dropList) > 0): # no valid entries
     1107            warn('Ignoring constraint',cdict,val)
     1108            skipList.append(cnum)
     1109        elif problem: # mix of valid & refined and undefined items, cannot use this
     1110            warn('Holding varied items & Dropping constraint',cdict,val)
     1111            skipList.append(cnum)
     1112            newHolds += [i for i in holdList if i not in newHolds]
     1113        elif len(dropList) > 0: # mix of valid and problematic items, drop problem vars, but keep rest
     1114            if GSASIIpath.GetConfigValue('debug'):
     1115                msg = ''
     1116                for v in dropList:
     1117                    if msg: msg += ' ,'
     1118                    msg += v
     1119                warn('removing: '+msg,cdict,val)
     1120            value = fixedList[cnum]
     1121            for var in dropList:   # do cleanup
     1122                # TODO: evaluate expressions in constraint multipliers here? I am assuming no
     1123                # as I think G2mv.EvaluateMultipliers does that already. For now assuming
     1124                # otherwise; note SubfromParmDict
     1125                if ':dAx:' in var or ':dAy:' in var or ':dAz:' in var: # undefined atoms can be ignored
     1126                    pass
     1127                elif cdict[var] != 0:
     1128                    value = float(value) - cdict[var]*parmDict[var]
     1129                del cdict[var]
     1130            if float(value) != float(fixedList[cnum]): fixedList[cnum] = str(np.round(value,12))
     1131            if GSASIIpath.GetConfigValue('debug'):
     1132                warn('revised as: '+_FormatConstraint(constrDict[cnum],fixedList[cnum]))
     1133    for i in list(range(len(constrDict)-1,-1,-1)): # remove the dropped constraints
     1134        if i in skipList:
     1135            del constrDict[i]
     1136            del fixedList[i]
     1137           
     1138    if warning: warning += '\n'
     1139    warning += warninfo['msg']
     1140           
     1141    groups,parmlist = GroupConstraints(constrDict)
     1142
    9061143    # now process each group and create the relations that are needed to form
    9071144    # a non-singular square matrix
    908     # If all are varied and this is a constraint equation, then set VaryFree flag
    909     # so that the newly created relationships will be varied
     1145    # Now check that all parameters are varied (probably do not need to do this
     1146    # any more). For constraint equations, if all are varied, set VaryFree to True
     1147    # and all newly created relationships will be varied. For NewVar constraints,
     1148    # vary if the vary flag was set.
    9101149    for group,varlist in zip(groups,parmlist):
    911         if len(varlist) == 1: continue
    912         # for constraints, if all included parameters are refined,
    913         # set the VaryFree flag, and remaining degrees of freedom will be
    914         # varied (since consistency was checked, if any one parameter is
    915         # refined, then assume that all are)
    916         varsList = [] # make a list of all the referenced parameters as well
     1150        if len(varlist) < len(group): # too many relationships -- no can do
     1151            if errmsg: errmsg += '\n'
     1152            errmsg += "Over-constrained input. "
     1153            errmsg += "There are more constraints (" + str(len(group))
     1154            errmsg += ") than parameters (" + str(len(varlist)) + ")\nin these constraints:"
     1155            for rel in group:
     1156                errmsg += '\n\t'+ _FormatConstraint(constrDict[rel],fixedList[rel])
     1157            groupErrors += varlist
     1158            continue # go on to next group
     1159
    9171160        VaryFree = False
    9181161        for rel in group:
    9191162            varied = 0
    920             unused = 0
    9211163            for var in VarKeys(constrDict[rel]):
    922                 var = translateTable.get(var,var) # replace wildcards
    923                 if parmDict is not None and var not in parmDict:
    924                     unused += 1                   
    925                 if var not in varsList: varsList.append(var)
     1164                #var = translateTable.get(var,var) # replace wildcards
     1165                #if parmDict is not None and var not in parmDict:
    9261166                if var in varyList: varied += 1
    9271167            if fixedList[rel] is not None and varied > 0:
    9281168                VaryFree = True
    929         if len(varlist) < len(group): # too many relationships -- no can do
    930             msg = 'too many relationships'
    931             break
    932         # Since we checked before, if any parameters are unused, then all must be.
    933         # If so, this set of relationships can be ignored
    934         if unused:
    935             if debug: print('Constraint ignored (all parameters undefined)')
    936             if debug: print ('    '+_FormatConstraint(constrDict[rel],fixedList[rel]))
    937             continue
     1169               
    9381170        # fill in additional degrees of freedom
    9391171        try:
    9401172            arr = _FillArray(group,constrDict,varlist)
    9411173            _RowEchelon(len(group),arr,varlist)
     1174        except:
     1175            if errmsg: errmsg += '\n'
     1176            errmsg += "\nSingular input. "
     1177            errmsg += "There are internal inconsistencies in these constraints:"
     1178            for rel in group:
     1179                errmsg += '\n\t' + _FormatConstraint(constrDict[rel],fixedList[rel])
     1180            groupErrors += varlist
     1181            continue
     1182
     1183        try:
    9421184            constrArr = _FillArray(group,constrDict,varlist,FillDiagonals=True)
    9431185            GramSchmidtOrtho(constrArr,len(group))
    9441186        except:
    945             msg = 'Singular relationships found while processing constraints group:'
     1187            if errmsg: errmsg += '\n'
     1188            errmsg += "\nUnexpected singularity with constraints group (in Gram-Schmidt)"
    9461189            for rel in group:
    947                 msg += '\n  ' + _FormatConstraint(constrDict[rel],fixedList[rel])
    948             break
     1190                errmsg += '\n\t' + _FormatConstraint(constrDict[rel],fixedList[rel])
     1191            groupErrors += varlist
     1192            continue
     1193       
     1194        try:
     1195            np.linalg.inv(constrArr)
     1196        except:
     1197            if errmsg: errmsg += '\n'
     1198            errmsg += "\nSingular input. "
     1199            errmsg += "The following constraints are not "
     1200            errmsg += "linearly independent\nor do not "
     1201            errmsg += "allow for generation of a non-singular set.\n"
     1202            errmsg += 'This is unexpected. Please report this (toby@anl.gov)'
     1203            for rel in group:
     1204                errmsg += '\n\t' + _FormatConstraint(constrDict[rel],fixedList[rel])
     1205            groupErrors += varlist
     1206            continue
     1207       
    9491208        mapvar = []
    9501209        group = group[:]
     
    9711230                    consNum += 1
    9721231                mapvar.append(varname)
    973                 genVarLookup[varname] = varlist # save list of parameters related to this new var
     1232                #genVarLookup[varname] = varlist # save list of parameters related to this new var
    9741233                # vary the new relationship if it is a degree of freedom in
    9751234                # a set of contraint equations or if a New Var is flagged to be varied.
     
    9791238                    # fix (prevent varying) of all the parameters inside the constraint group
    9801239                    # (dependent vars)
    981                     for var in varsList:
     1240                    for var in varlist:
    9821241                        if var in varyList: varyList.remove(var)
    9831242            else:
    9841243                unused = False
    985                 mapvar.append(fixedval)
     1244                mapvar.append(float(fixedval))
    9861245        if unused: # skip over constraints that don't matter (w/o fixed value or any refined parameters)
    987             if debug: print('Constraint ignored (all parameters unrefined)')
    988             if debug: print ('   '+_FormatConstraint(constrDict[rel],fixedList[rel]))
     1246            if GSASIIpath.GetConfigValue('debug'):
     1247                print('Unexpected: Constraint ignored (all parameters unvaried)')
     1248                print ('   '+_FormatConstraint(constrDict[rel],fixedList[rel]))
    9891249            continue
    990         dependentParmList.append([translateTable.get(var,var) for var in varlist])
     1250        #dependentParmList.append([translateTable.get(var,var) for var in varlist])
     1251        dependentParmList.append(varlist)
    9911252        arrayList.append(constrArr)
    9921253        invarrayList.append(np.linalg.inv(constrArr))
    9931254        indParmList.append(mapvar)
    9941255        symGenList.append(False)
    995     if msg:
     1256    if errmsg and SeqHist is not None:
    9961257        print (' *** ERROR in constraint definitions! ***')
    997         print (msg)
    998         print (VarRemapShow(varyList))
     1258        print (errmsg)
     1259        if warning:
     1260            print (' also note warnings in constraint processing:')
     1261            print (warning)
    9991262        raise ConstraintException
    1000     # setup dictionary containing the fixed values
    1001     global fixedDict
    1002     # key is original ascii string, value is float
    1003     for fixedval in fixedList:
    1004         if fixedval:
    1005             fixedDict[fixedval] = float(fixedval)
    1006     _setVarLists(dropVarList)
    1007     if changed:
    1008         print(60*'=')
    1009         print('Constraints were reclassified to avoid conflicts, as below:')
    1010         print(mvMsg)
    1011         print('New constraints are:')
    1012         print (VarRemapShow(varyList,True))
    1013         print(60*'=')
    1014     return groups,parmlist # saved for sequential fits
    1015    
    1016 def _setVarLists(dropVarList):
    1017     '''Make list of dependent and independent variables (after dropping unused vars in dropVarList)
    1018     '''
    1019     global dependentParmList,indParmList
     1263    elif errmsg:
     1264        return errmsg,warning,None,None
     1265
     1266    # Make list of dependent and independent variables (after possibly dropping unused vars)
    10201267    global dependentVars
    10211268    global independentVars
     
    10241271    for varlist,mapvars in zip(dependentParmList,indParmList):  # process all constraints
    10251272        for mv in mapvars:
    1026             if mv in dropVarList: continue
     1273            if type(mv) is float: continue
    10271274            if mv not in independentVars: independentVars.append(mv)
    10281275        for mv in varlist:
    1029             if mv in dropVarList: continue
    10301276            if mv not in dependentVars: dependentVars.append(mv)
    1031     if debug: # on debug, show what is parsed & generated, semi-readable
    1032         print (50*'-')
    1033         #print (VarRemapShow(varyList))
    1034         #print ('Varied: ',varyList)
    1035         print ('Not Varied: ',fixedVarList)
    1036 
    1037 # def CheckEquivalences(constrDict,varyList):
    1038 #     global dependentParmList,arrayList,invarrayList,indParmList,consNum
    1039 #     global problemVars
    1040 #     warnmsg = ''
    1041 #     errmsg = ''
    1042 #     problemVars = []
    1043 #     # process fixed variables (holds)
    1044 #     fixVlist = [] # list of Fixed vars
    1045 #     constrVars = [] # list of vars in constraint expressions
    1046 #     for cdict in constrDict:
    1047 #         # N.B. No "_" names in holds
    1048 #         if len(cdict) == 1:
    1049 #             fixVlist.append(list(cdict.keys())[0])
    1050 #         else:
    1051 #             constrVars += cdict.keys() # this will include _vary (not a problem)
    1052 #     # process equivalences: make a list of dependent and independent vars
    1053 #     #    and check for repeated uses (repetition of a parameter as an
    1054 #     #    independent var is OK)
    1055 #     indepVarList = []
    1056 #     depVarList = []
    1057 #     multdepVarList = []
    1058 #     for varlist,mapvars,multarr,invmultarr in zip(
    1059 #         dependentParmList,indParmList,arrayList,invarrayList):
    1060 #         if multarr is None: # an equivalence
    1061 #             zeromult = False
    1062 #             for mv in mapvars:
    1063 #                 varied = 0
    1064 #                 notvaried = ''
    1065 #                 if mv in varyList:
    1066 #                     varied += 1
    1067 #                 else:
    1068 #                     if notvaried: notvaried += ', '
    1069 #                     notvaried += mv
    1070 #                 if mv not in indepVarList: indepVarList.append(mv)
    1071 #                 for v,m in zip(varlist,invmultarr):
    1072 #                     if v in indepVarList:
    1073 #                         errmsg += '\nVariable '+v+' is used to set values in a constraint before its value is set in another constraint\n'
    1074 #                         if v not in problemVars: problemVars.append(v)
    1075 #                     if m == 0: zeromult = True
    1076 #                     if v in varyList:
    1077 #                         varied += 1
    1078 #                     else:
    1079 #                         if notvaried: notvaried += ', '
    1080 #                         notvaried += v
    1081 #                     if v in depVarList:
    1082 #                         multdepVarList.append(v)
    1083 #                     else:
    1084 #                         depVarList.append(v)
    1085 #             if varied > 0 and varied != len(varlist)+1:
    1086 #                 warnmsg += "\nNot all variables refined in equivalence:\n\t"
    1087 #                 s = ""
    1088 #                 for v in varlist:
    1089 #                     if s != "": s+= " & "
    1090 #                     s += str(v)           
    1091 #                 warnmsg += str(mv) + " => " + s
    1092 #                 warnmsg += '\nNot refined: ' + notvaried + '\n'
    1093 #             if zeromult:
    1094 #                 errmsg += "\nZero multiplier is invalid in equivalence:\n\t"
    1095 #                 s = ""
    1096 #                 for v in varlist:
    1097 #                     if s != "": s+= " & "
    1098 #                     s += str(v)           
    1099 #                 errmsg += str(mv) + " => " + s + '\n'
    1100 #     # check for errors:
    1101 #     if len(multdepVarList) > 0:
    1102 #         errmsg += "\nThe following parameters(s) are used in conflicting Equivalence relations as dependent variables:\n"
    1103 #         s = ''
    1104 #         for var in sorted(set(multdepVarList)):
    1105 #             if v not in problemVars: problemVars.append(v)
    1106 #             if s != "": s+= ", "
    1107 #             s += str(var)           
    1108 #         errmsg += '\t'+ s + '\n'
    1109 #     equivVarList = list(set(indepVarList).union(set(depVarList)))
    1110 #     if debug: print ('equivVarList',equivVarList)
    1111 #     # check for parameters that are both fixed and in an equivalence (not likely)
    1112 #     inboth = set(fixVlist).intersection(set(equivVarList))
    1113 #     if len(inboth) > 0:
    1114 #         errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n"
    1115 #         s = ''
    1116 #         for var in sorted(inboth):
    1117 #             if var not in problemVars: problemVars.append(var)
    1118 #             if s != "": s+= ", "
    1119 #             s += str(var)
    1120 #         errmsg += '\t'+ s + '\n'
    1121 #     # check for parameters that in both an equivalence and a constraint expression
    1122 #     inboth = set(constrVars).intersection(set(equivVarList))
    1123 #     if len(inboth) > 0:
    1124 #         errmsg += "\nThe following parameter(s) are used in both Equivalence and Equiv or new var constraints:\n"
    1125 #         s = ''
    1126 #         for var in sorted(inboth):
    1127 #             if var not in problemVars: problemVars.append(var)
    1128 #             if s != "": s+= ", "
    1129 #             s += str(var)
    1130 #         errmsg += '\t'+ s + '\n'
    1131 #     return errmsg,warnmsg,fixVlist
    1132 
    1133 def CheckEquivalences(constrDict,varyList,parmDict=None,SeqHist=None):
     1277    saveVaryList = copy.copy(varyList)
     1278
     1279    # if equivMoved:
     1280    #     print(60*'=')
     1281    #     print('Constraints were reclassified to avoid conflicts, as below:')
     1282    #     print(mvMsg)
     1283    #     print('New constraints are:')
     1284    #     print (VarRemapShow(varyList,True))
     1285    #     print(60*'=')
     1286    return errmsg,warning,groups,parmlist # saved for sequential fits
     1287   
     1288def CheckEquivalences(constrDict,varyList,fixedList,parmDict=None):
    11341289    '''Process equivalence constraints, looking for conflicts such as
    11351290    where a parameter is used in both an equivalence and a constraint expression
    11361291    or where chaining is done (A->B and B->C).
    1137     When called during refinements, parmDict is defined, and for sequential refinement
    1138     SeqHist ia also defined.
    1139 
    1140       * parmDict is used to remove equivalences where a parameter is not present
    1141         in a refinement
    1142       * SeqHist is used to rename wild-card parameter names in sequential
    1143         refinements to use the current histogram.
     1292
     1293    Removes equivalences or parameters from equivalences or converts equivalences to
     1294    constraints as described for :ref:`Equivalence Checking and Reorganization <CheckEquivalences>`.
     1295
     1296    :param dict constrDict: a list of dicts defining relationships/constraints
     1297    :param list varyList: list of varied parameters (defined during refinements only)
     1298    :param list fixedList: a list of values specifying a fixed value for each
     1299       dict in constrDict. Values are either strings that can be converted to
     1300       floats or ``None`` if the constraint defines a new parameter rather
     1301       than a constant.
     1302    :param dict parmDict: a dict containing defined parameters and their values. Used to find
     1303       equivalences where a parameter is has been removed from a refinement.
     1304
     1305    :returns: warning messages about changes that need to be made to equivalences
    11441306    '''
    1145     global dependentParmList,arrayList,invarrayList,indParmList,consNum
    1146     global problemVars
    1147     warnmsg = ''
    1148     errmsg = ''
    1149     problemVars = []
    1150     # process fixed parameters (holds)
    1151     fixVlist = [] # list of Fixed vars
    1152     constrVars = [] # list of vars in constraint expressions
    1153     for cdict in constrDict:
    1154         # N.B. No "_" names in holds
    1155         if len(cdict) == 1:
    1156             fixVlist.append(list(cdict.keys())[0])
    1157         else:
    1158             constrVars += cdict.keys() # this will include _vary (not a problem)
     1307   
     1308    warninfo = {'msg':'', 'shown':-1}
     1309    def warn(msg,cnum=None):
     1310        if cnum is not None and cnum != warninfo['shown']:
     1311            warninfo['shown'] = cnum
     1312            if warninfo['msg']: warninfo['msg'] += '\n'
     1313            warninfo['msg'] += '\nProblem with equivalence: ' + _showEquiv(
     1314                dependentParmList[cnum],indParmList[cnum],invarrayList[cnum])
     1315        if warninfo['msg']: warninfo['msg'] += '\n'
     1316        warninfo['msg'] += '  ' + msg
     1317
     1318    global holdParmList # parameters set as "Hold"
     1319    global depVarList # parameters used in equivalences as dependent parameters
     1320    global indepVarList # parameters used in equivalences as independent parameters
     1321    global constrVarList  # parameters used in other constraints
     1322             
     1323    # lists of parameters used for error reporting
     1324    global undefinedVars # parameters that are used in equivalences but are not defined
     1325    global newHolds # additional parameters that should be held
     1326    global convVarList # parameters in equivalences that will be converted to constraints
     1327    convVarList = [] # parameters in equivalences to be made into constraints
     1328    global multdepVarList
     1329    multdepVarList = [] # list of dependent parameters used in more than one equivalence
     1330
     1331    # local vars
     1332    dropVarList = []  # parameters that can be removed from equivalences
     1333    removeList = []  # equivalences that are not needed
     1334    convertList = [] # equivalences that should be converted to "Const" constraints
     1335
    11591336    # process equivalences: make a list of dependent and independent vars
    11601337    #    and check for repeated uses (repetition of a parameter as an
    11611338    #    independent var is OK)
    1162     indepVarList = []
    1163     depVarList = []
    1164     multdepVarList = []
    1165     dropVarList = []
    1166     translateTable = {} # lookup table for wildcard referenced parameters
    1167     for varlist,mapvars,multarr,invmultarr in zip(
    1168         dependentParmList,indParmList,arrayList,invarrayList):
    1169         if multarr is None: # an equivalence
    1170             zeromult = False
    1171             for i,mv in enumerate(mapvars):
    1172                 if mv.split(':')[1] == '*' and SeqHist is not None:
    1173                     # convert wildcard var to reference current histogram; save translation in table
    1174                     sv = mv.split(':')
    1175                     sv[1] = str(SeqHist)
    1176                     mv = translateTable[mv] = ':'.join(sv)
    1177                     mapvars[i] = mv
    1178                 varied = 0
    1179                 notvaried = ''
    1180                 if mv in varyList:
    1181                     varied += 1
     1339    # look for parameters in equivalences that are used more than once as dependent parameters
     1340    seenOnce = []
     1341    for cnum,(varlist,multarr) in enumerate(zip(dependentParmList,arrayList)):
     1342        if multarr is not None: continue # equivalences only
     1343        for v in varlist:
     1344            if v not in seenOnce:
     1345                seenOnce.append(v)
     1346            elif v not in multdepVarList:
     1347                multdepVarList.append(v)               
     1348
     1349    # scan through equivalences looking for other "dual uses". Stop when no new ones are found
     1350    changed = True
     1351    count = 0
     1352    while changed:
     1353        changed = False
     1354        count += 1
     1355        if count > 1000:
     1356            raise ConstraintException("Too many loops in CheckEquivalences")
     1357       
     1358        # look for repeated dependent vars
     1359        convVarList = [] # parameters in equivalences to be made into constraints
     1360        for cnum,(varlist,mapvars,multarr,invmultarr) in enumerate(zip(
     1361            dependentParmList,indParmList,arrayList,invarrayList)):
     1362            if multarr is not None: continue # equivalences only
     1363            if cnum in convertList:
     1364                convVarList += [v for v in mapvars+varlist if v not in convVarList and type(v) is not float]
     1365                continue
     1366           
     1367            # identify equivalences that need to be converted to constraints.
     1368            #  Where parameters:
     1369            #     are used both in equivalences & constraints,
     1370            #     are used as dependent multiple times or
     1371            #     where are used as both dependent and independent (chained)
     1372            msg = False
     1373            for v in mapvars:
     1374                if v in constrVarList+convVarList:
     1375                    changed = True
     1376                    msg = True
     1377                    warn("Independent parameter "+str(v)+' used in constraint',cnum)
     1378                    if cnum not in convertList: convertList.append(cnum)
     1379            for v in varlist:
     1380                if v in multdepVarList:
     1381                    changed = True
     1382                    msg = True
     1383                    warn("Dependent parameter "+str(v)+' repeated',cnum)
     1384                    if cnum not in convertList: convertList.append(cnum)
     1385                elif v in indepVarList:
     1386                    changed = True
     1387                    msg = True
     1388                    warn("Dependent parameter "+str(v)+' used elsewhere as independent',cnum)
     1389                    if cnum not in convertList: convertList.append(cnum)
     1390                elif v in constrVarList+convVarList:
     1391                    changed = True
     1392                    msg = True
     1393                    warn("Dependent parameter "+str(v)+' used in constraint',cnum)
     1394                    if cnum not in convertList: convertList.append(cnum)
     1395            if msg:
     1396                warn('Converting to "Constr"',cnum)
     1397           
     1398    global unvariedParmsList
     1399    unvariedParmsList = []  # parameters in equivalences that are not varied
     1400    # scan equivalences: look for holds
     1401    for cnum,(varlist,mapvars,multarr,invmultarr) in enumerate(zip(
     1402        dependentParmList,indParmList,arrayList,invarrayList)):
     1403        if multarr is not None: continue # not an equivalence
     1404        if cnum in convertList: continue
     1405
     1406        # look for holds
     1407        gotHold = False
     1408        holdList = []
     1409        for v in varlist+mapvars:
     1410            if v in holdParmList:
     1411                gotHold = True
     1412            elif type(v) is not float:
     1413                holdList.append(v)
     1414        if gotHold:
     1415            if holdList:
     1416                msg = 'Some parameters set as "Hold"; setting remainder as "Hold": '
     1417                for i,var in enumerate(holdList):
     1418                    if i != 0: msg += ", "
     1419                    msg += var
     1420                newHolds += [i for i in holdList if i not in newHolds]
     1421                msg += ". "
     1422            else:
     1423                msg = 'All parameters set as "Hold". '
     1424            msg += " Ignoring equivalence"
     1425            warn(msg,cnum)
     1426            removeList.append(cnum)
     1427            continue
     1428       
     1429        # look for unvaried parameters
     1430        gotVary = False
     1431        gotNotVary = False
     1432        holdList = []
     1433        for v in varlist+mapvars:
     1434            if v in varyList:
     1435                gotVary = True
     1436                holdList.append(v)
     1437            elif type(v) is not float:
     1438                gotNotVary = True
     1439                if v not in unvariedParmsList: unvariedParmsList.append(v)
     1440        if gotNotVary:  # at least some unvaried parameters
     1441            if gotVary:  # mix of varied and unvaried parameters
     1442                msg = 'Some parameters not varied; setting remainder as "Hold": '
     1443                for i,var in enumerate(holdList):
     1444                    if i != 0: msg += ", "
     1445                    msg += var
     1446                newHolds += [i for i in holdList if i not in newHolds]
     1447                msg += ". "
     1448            else:
     1449                msg = 'No parameters varied. '               
     1450            msg += " Ignoring equivalence"
     1451            warn(msg,cnum)
     1452            removeList.append(cnum)
     1453            continue
     1454           
     1455        # look for undefined or zero multipliers
     1456        holdList = []
     1457        drop = 0
     1458        for v,m in zip(varlist,invmultarr):   
     1459            if parmDict is not None and v not in parmDict:
     1460                if v not in undefinedVars: undefinedVars.append(v)
     1461                if v not in dropVarList: dropVarList.append(v)
     1462                drop += 1
     1463            elif m == 0:
     1464                warn("Parameter "+str(v)+" has a zero multiplier, dropping",cnum)
     1465                if v not in dropVarList: dropVarList.append(v)
     1466                drop += 1
     1467            else:
     1468                holdList.append(v)
     1469        if drop == len(varlist):
     1470            warn("No dependent parameters defined, ignoring equivalence",cnum)
     1471            removeList.append(cnum)
     1472            continue
     1473        for mv in mapvars:
     1474            if type(mv) is float: continue
     1475            if parmDict is not None and mv not in parmDict:
     1476                # independent parameter is undefined, but some dependent parameters are defined
     1477                # hold them
     1478                if mv not in undefinedVars: undefinedVars.append(mv)
     1479                msg = "Parameter(s) "+str(mv)
     1480                for v in varlist:
     1481                    if v in dropVarList:
     1482                        msg += ', ' + v
     1483                msg += "not defined in this refinement\n"
     1484                msg = "Setting holds for: "
     1485                for i,var in enumerate(holdList):
     1486                    if i != 0: msg += ", "
     1487                    msg += var
     1488                warn(msg,cnum)
     1489                drop += 1
     1490        if drop: # independent var and at least one dependent variable is defined
     1491            msg = "Dropping undefined parameter(s) "
     1492            i = 0
     1493            for v in varlist:
     1494                if v in dropVarList:
     1495                    if i != 0: msg += ', '
     1496                    i += 1
     1497                    msg += v
     1498            warn(msg,cnum)
     1499            msg = "Some parameters not defined. Setting holds for: "
     1500            for i,var in enumerate(holdList):
     1501                if i != 0: msg += ", "
     1502                msg += var
     1503            warn(msg,cnum)
     1504            newHolds += [i for i in holdList if i not in newHolds]
     1505   
     1506    # Convert equivalences where noted
     1507    for cnum,varlist in enumerate(dependentParmList):
     1508        if cnum not in convertList: continue
     1509        indvar = indParmList[cnum][0]
     1510        # msg = '\nChanging equivalence:\n    ' + _showEquiv(
     1511        #     dependentParmList[cnum],indParmList[cnum],invarrayList[cnum])
     1512        for dep,mult in zip(dependentParmList[cnum],invarrayList[cnum]):
     1513            constrDict += [{indvar:-1.,dep:mult[0]}]
     1514            fixedList += ['0.0']
     1515        #msg += '\n  to constraint(s):'
     1516        #msg += '\n    ' + _FormatConstraint(constrDict[-1],fixedList[-1])
     1517        removeList.append(cnum)
     1518    # Drop equivalences where noted
     1519    if removeList:
     1520        for i in sorted(set(removeList),reverse=True):
     1521            del dependentParmList[i],indParmList[i],arrayList[i],invarrayList[i],symGenList[i]
     1522    # Drop variables from remaining equivalences
     1523    for cnum,varlist in enumerate(dependentParmList):
     1524        for j,v in enumerate(varlist):
     1525            drop = []
     1526            if v in dropVarList:
     1527                drop.append(j)
     1528        if drop:
     1529            for j in sorted(drop,reverse=True):
     1530                del indParmList[cnum][j]
     1531    return warninfo['msg']
     1532
     1533def ProcessConstraints(constList,seqmode='use-all',seqhst=None):
     1534    """Interpret the constraints in the constList input into a dictionary, etc.
     1535    All :class:`GSASIIobj.G2VarObj` objects are mapped to the appropriate
     1536    phase/hist/atoms based on the object internals (random Ids). If this can't be
     1537    done (if a phase has been deleted, etc.), the variable is ignored.
     1538    If the constraint cannot be used due to too many dropped variables,
     1539    it is counted as ignored. In the case of sequential refinements,
     1540    the current histogram number is substituted for a histogram number of "*".
     1541
     1542    NB: this processing does not include symmetry imposed constraints
     1543   
     1544    :param list constList: a list of lists where each item in the outer list
     1545      specifies a constraint of some form, as described in the :mod:`GSASIIobj`
     1546      :ref:`Constraint definitions <Constraint_definitions_table>`.
     1547    :param str seqmode: one of 'use-all', 'wildcards-only' or 'auto-wildcard'.
     1548       When seqmode=='wildcards-only' then any constraint with a numerical
     1549       constraint number is skipped. With seqmode=='auto-wildcard',
     1550       any non-null constraint number is set to the selected histogram.
     1551    :param int seqhst: number for current histogram (used for
     1552      'wildcards-only' or 'auto-wildcard' only). Should be None for
     1553      non-sequential fits.
     1554
     1555    :returns:  a tuple of (constrDict,fixedList,ignored) where:
     1556     
     1557      * constrDict (list of dicts) contains the constraint relationships
     1558      * fixedList (list) contains the fixed values for each type
     1559        of constraint.
     1560      * ignored (int) counts the number of invalid constraint items
     1561        (should always be zero!)
     1562    """
     1563    constrDict = []
     1564    fixedList = []
     1565    ignored = 0
     1566    namedVarList = []
     1567    for constr in constList:
     1568        terms = copy.deepcopy(constr[:-3])
     1569        if seqmode == 'wildcards-only' and seqhst is not None:
     1570            skip = False
     1571            for term in terms:
     1572                if term[1].histogram == '*':
     1573                    term[1] = term[1].varname(seqhst)
     1574                elif term[1].histogram:
     1575                    skip = True
     1576            if skip: continue
     1577        elif seqmode == 'auto-wildcard' and seqhst is not None:
     1578            for term in terms:
     1579                term[1] = term[1].varname(seqhst)
     1580        else:
     1581            for term in terms:
     1582                if term[1].histogram == '*' and seqhst is not None:
     1583                    term[1] = term[1].varname(seqhst)
    11821584                else:
    1183                     if notvaried: notvaried += ', '
    1184                     notvaried += mv
    1185                 if parmDict is not None and mv not in parmDict:
    1186                     print ("Dropping equivalence for parameter "+str(mv)+". Not defined in this refinement")
    1187                     if mv not in dropVarList: dropVarList.append(mv)
    1188                 if mv not in indepVarList: indepVarList.append(mv)
    1189             for i,(v,m) in enumerate(zip(varlist,invmultarr)):
    1190                 if v.split(':')[1] == '*' and SeqHist is not None:
    1191                     # convert wildcard var to reference current histogram; save translation in table
    1192                     sv = v.split(':')
    1193                     sv[1] = str(SeqHist)
    1194                     varlist[i] = v = translateTable[v] = ':'.join(sv)
    1195                 if parmDict is not None and v not in parmDict:
    1196                     print ("Dropping equivalence for dep. variable "+str(v)+". Not defined in this refinement")
    1197                     if v not in dropVarList: dropVarList.append(v)
    1198                     continue
    1199                 if m == 0: zeromult = True
    1200                 if v in varyList:
    1201                     varied += 1
     1585                    term[1] = term[1].varname()
     1586               
     1587        if constr[-1] == 'h':
     1588            # process a hold
     1589            var = str(constr[0][1])
     1590            if '?' not in var:
     1591                StoreHold(var)
     1592            else:
     1593                ignored += 1
     1594        elif constr[-1] == 'f':
     1595            # process a new variable
     1596            fixedList.append(None)
     1597            D = {}
     1598            varyFlag = constr[-2]
     1599            varname = constr[-3]
     1600            for term in terms:
     1601                var = str(term[1])
     1602                if '?' not in var:
     1603                    D[var] = term[0]
     1604            if len(D) > 1:
     1605                # add extra dict terms for input variable name and vary flag
     1606                if varname is not None:
     1607                    varname = str(varname) # in case this is a G2VarObj
     1608                    if varname.startswith(':'):
     1609                        D['_name'] = varname
     1610                    else:
     1611                        D['_name'] = '::nv-' + varname
     1612                    D['_name'] = G2obj.MakeUniqueLabel(D['_name'],namedVarList)
     1613                D['_vary'] = varyFlag == True # force to bool
     1614                constrDict.append(D)
     1615            else:
     1616                ignored += 1
     1617            #constFlag[-1] = ['Vary']
     1618        elif constr[-1] == 'c':
     1619            # process a contraint relationship
     1620            D = {}
     1621            for term in terms:
     1622                var = str(term[1])
     1623                if '?' not in var:
     1624                    D[var] = term[0]
     1625            if len(D) >= 1:
     1626                fixedList.append(str(constr[-3]))
     1627                constrDict.append(D)
     1628            else:
     1629                ignored += 1
     1630        elif constr[-1] == 'e':
     1631            # process an equivalence
     1632            firstmult = None
     1633            eqlist = []
     1634            for term in terms:
     1635                if term[0] == 0: term[0] = 1.0
     1636                var = str(term[1])
     1637                if '?' in var: continue
     1638                if firstmult is None:
     1639                    firstmult = term[0]
     1640                    firstvar = var
    12021641                else:
    1203                     if notvaried: notvaried += ', '
    1204                     notvaried += v
    1205                 if v in indepVarList:
    1206                     errmsg += '\nParameter '+v+' is used to set values in a constraint before its value is set in another constraint\n'
    1207                     if v not in problemVars: problemVars.append(v)
    1208                 if v in depVarList:
    1209                     multdepVarList.append(v)
    1210                 else:
    1211                     depVarList.append(v)
    1212             if varied > 0 and varied != len(varlist)+1:
    1213                 warnmsg += "\nNot all parameters refined in equivalence:\n\t"
    1214                 s = ""
    1215                 for v in varlist:
    1216                     if s != "": s+= " & "
    1217                     s += str(v)           
    1218                 warnmsg += str(mv) + " => " + s
    1219                 warnmsg += '\nNot refined: ' + notvaried + '\n'
    1220             if zeromult:
    1221                 errmsg += "\nZero multiplier is invalid in equivalence:\n\t"
    1222                 s = ""
    1223                 for v in varlist:
    1224                     if s != "": s+= " & "
    1225                     s += str(v)           
    1226                 errmsg += str(mv) + " => " + s + '\n'
    1227     # check for errors:
    1228     if len(multdepVarList) > 0:
    1229         errmsg += "\nThe following parameters(s) are used in conflicting Equivalence relations as dependent variables:\n"
    1230         s = ''
    1231         for var in sorted(set(multdepVarList)):
    1232             if v not in problemVars: problemVars.append(v)
    1233             if s != "": s+= ", "
    1234             s += str(var)           
    1235         errmsg += '\t'+ s + '\n'
    1236     equivVarList = list(set(indepVarList).union(set(depVarList)))
    1237     if debug: print ('equivVarList',equivVarList)
    1238     # check for parameters that are both fixed and in an equivalence (not likely)
    1239     inboth = set(fixVlist).intersection(set(equivVarList))
    1240     if len(inboth) > 0:
    1241         errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n"
    1242         s = ''
    1243         for var in sorted(inboth):
    1244             if var not in problemVars: problemVars.append(var)
    1245             if s != "": s+= ", "
    1246             s += str(var)
    1247         errmsg += '\t'+ s + '\n'
    1248     # check for parameters that in both an equivalence and a constraint expression
    1249     inboth = set(constrVars).intersection(set(equivVarList))
    1250     if len(inboth) > 0:
    1251         errmsg += "\nThe following parameter(s) are used in both Equivalence and Equiv or new var constraints:\n"
    1252         s = ''
    1253         for var in sorted(inboth):
    1254             if var not in problemVars: problemVars.append(var)
    1255             if s != "": s+= ", "
    1256             s += str(var)
    1257         errmsg += '\t'+ s + '\n'
    1258     return errmsg,warnmsg,fixVlist,dropVarList,translateTable
    1259 
    1260 def MoveConfEquiv(constrDict,fixedList):
    1261     '''Address conflicts in Equivalence constraints by creating an constraint equation
    1262     that has the same action as the equivalence and removing the Equivalence
     1642                    eqlist.append([var,firstmult/term[0]])
     1643            if len(eqlist) > 0:
     1644                StoreEquivalence(firstvar,eqlist,False)
     1645            else:
     1646                ignored += 1
     1647        else:
     1648            ignored += 1
     1649    return constrDict,fixedList,ignored
     1650
     1651def StoreHold(var,symGen=False):
     1652    '''Takes a variable name and prepares it to be removed from the
     1653    refined variables.
     1654
     1655    Called with user-supplied constraints by :func:`ProcessConstraints`.
     1656    At present symGen is not used, but could be set up to track Holds generated
     1657    by symmetry.
    12631658    '''
    1264     global dependentParmList,arrayList,invarrayList,indParmList,consNum
    1265     global problemVars
    1266     parmsChanged = 0
    1267     msg = ''
    1268     if problemVars:
    1269         msg = 'Conflict: variable(s) used in both equivalences and constraints: '
    1270         for i1,v1 in enumerate(problemVars):
    1271             if i1 > 0: msg += ', '
    1272             msg += v1
    1273     for i,(varlist,mapvars) in enumerate(zip(dependentParmList,indParmList)):
    1274         conf = False
    1275         for mv in mapvars:
    1276             if mv in problemVars:
    1277                 conf = True
    1278                 break
    1279         for v in varlist:
    1280             if v in problemVars:
    1281                 conf = True
    1282                 break
    1283         if conf:
    1284             parmsChanged += 1
    1285             indvar = indParmList[i][0]
    1286             msg += '\n  Removing equivalence:\n    ' + _showEquiv(
    1287                 dependentParmList[i],indParmList[i],invarrayList[i])
    1288             msg += '\n  Creating new constraint(s):'
    1289             for dep,mult in zip(dependentParmList[i],invarrayList[i]):
    1290                 constrDict += [{indvar:-1.,dep:mult[0]}]
    1291                 fixedList += ['0.0']
    1292                 msg += '\n    ' + _FormatConstraint(constrDict[-1],fixedList[-1])
    1293             dependentParmList[i] = None
    1294     if parmsChanged:
    1295         for i in range(len(dependentParmList)-1,-1,-1):
    1296             if dependentParmList[i] is None:
    1297                 del dependentParmList[i],indParmList[i],arrayList[i],invarrayList[i]
    1298     return parmsChanged,msg
    1299 
     1659    global holdParmList
     1660    if var not in holdParmList:
     1661        holdParmList.append(var)
     1662
     1663   
    13001664def StoreEquivalence(independentVar,dependentList,symGen=True):
    13011665    '''Takes a list of dependent parameter(s) and stores their
    13021666    relationship to a single independent parameter (independentVar).
    13031667
    1304     Called with user-supplied constraints by :func:`GSASIIstrIO.ProcessConstraints,
     1668    Called with user-supplied constraints by :func:`ProcessConstraints`,
    13051669    with Pawley constraints from :func:`GSASIIstrIO.GetPawleyConstr`,
    13061670    with Unit Cell constraints from :func:`GSASIIstrIO.cellVary`
    13071671    with symmetry-generated atom constraints from :func:`GSASIIstrIO.GetPhaseData`
     1672
     1673      There is no harm in using StoreEquivalence with the same independent variable::
     1674
     1675       StoreEquivalence('x',('y',))
     1676       StoreEquivalence('x',('z',))
     1677
     1678      but the same outcome can be obtained with a single call::
     1679
     1680       StoreEquivalence('x',('y','z'))
     1681
     1682      The latter will run more efficiently.
     1683
     1684
     1685      Note that mixing independent and dependent variables, such as::
     1686
     1687        StoreEquivalence('x',('y',))
     1688        StoreEquivalence('y',('z',))
     1689
     1690      is a poor choice. The module will attempt to fix this by transforming the equivalence to a
     1691      "Const" constraint.
    13081692
    13091693    :param str independentVar: name of master parameter that will be used to determine the value
     
    13451729    return
    13461730
     1731def SubfromParmDict(s,prmDict):
     1732    for key in prmDict:
     1733        if key in s:
     1734            s = s.replace(key,str(prmDict[key]))
     1735    return eval(s)
     1736
    13471737def EvaluateMultipliers(constList,*dicts):
    13481738    '''Convert multipliers for constraints and equivalences that are specified
     
    13561746       the strings that could not be converted.
    13571747    '''
    1358     def SubfromParmDict(s,prmDict):
    1359         for key in prmDict:
    1360             if key in s:
    1361                 s = s.replace(key,str(prmDict[key]))
    1362         return eval(s)
    13631748    prmDict = {}
    13641749    for d in dicts: prmDict.update(d) # combine all passed parameter dicts
     
    14321817def PrintIndependentVars(parmDict,varyList,sigDict,PrintAll=False,pFile=None):
    14331818    '''Print the values and uncertainties on the independent parameters'''
    1434     global dependentParmList,arrayList,invarrayList,indParmList,fixedDict
     1819    global dependentParmList,arrayList,invarrayList,indParmList
    14351820    printlist = []
    1436     mapvars = GetIndependentVars()
    1437     for i,name in enumerate(mapvars):
    1438         if name in fixedDict: continue
     1821    mvs = GetIndependentVars()
     1822    for i,name in enumerate(mvs):
    14391823        if PrintAll or name in varyList:
    14401824            sig = sigDict.get(name)
     
    14661850            s3 += '%15.5f' % (esd)
    14671851
     1852def getConstrError(constrLst,seqmode,seqhst):
     1853    '''This is used to display error messages for constraints and
     1854    equivalence relations
     1855
     1856    :parm list constrLst: a single constraint or equivalence as saved
     1857      in the data tree
     1858      (see :ref:`constraint definitions <Constraint_definitions_table>`).
     1859    :param str seqmode: one of 'use-all', 'wildcards-only' or 'auto-wildcard'
     1860    :param int seqhst: number for current histogram (used for
     1861      'wildcards-only' or 'auto-wildcard' only). Should be None for
     1862      non-sequential fits.
     1863
     1864    :returns: error, msg where error (bool) is True if the
     1865      constraint/equivalence creates an error, msg (str) can be a warning
     1866      or an error
     1867    '''
     1868    msg = ''
     1869    note = ''
     1870    terms = copy.deepcopy(constrLst[:-3])
     1871    if seqmode == 'wildcards-only' and seqhst is not None:
     1872        if constrLst[-1] == 'e':
     1873            msg = 'equivalence'
     1874        else:
     1875            msg = 'constraint'
     1876        for term in terms:
     1877            if term[1].histogram == '*':
     1878                term[1] = term[1].varname(seqhst)
     1879            elif term[1].histogram:
     1880                return False,"Ignoring non-wildcard "+msg, "Ignore"
     1881    elif seqmode == 'auto-wildcard' and seqhst is not None:
     1882        for term in terms:
     1883            term[1] = term[1].varname(seqhst)
     1884    else:
     1885        for term in terms:
     1886            if term[1].histogram == '*':
     1887                if seqhst is None:
     1888                    msg = "Parameter "+str(terms[0][1])+" contains a wildcard, which are used only sequential refinements. Constraint ignored."
     1889                    return False,msg, "Ignored"
     1890                else:
     1891                    term[1] = term[1].varname(seqhst)
     1892            else:
     1893                term[1] = term[1].varname()
     1894    if constrLst[-1] == 'e':
     1895        # conflicting uses
     1896        if terms[0][1] in constrVarList+convVarList:
     1897            msg = "Parameter "+str(terms[0][1])+" used in constraint. To be recast as constraint."
     1898            return False,msg, "Recast as constraint."
     1899        varList = []
     1900        for m,v in terms:
     1901            if v in constrVarList+convVarList:
     1902                varList.append(str(v))
     1903        if varList:
     1904            msg = "Parameter(s) used in constraint: "
     1905            for i,v in enumerate(varList):
     1906                if i != 0: msg += ', '
     1907                msg += v
     1908            return False,msg,"Recast as constraint."
     1909        varList = []
     1910        for m,v in terms[1:]:
     1911            if v in indepVarList:
     1912                varList.append(str(v))
     1913        if varList:
     1914            msg = "Parameter(s) used elsewhere as independent: "
     1915            for i,v in enumerate(varList):
     1916                if i != 0: msg += ', '
     1917                msg += v
     1918            return False,msg,"Recast as constraint."
     1919        varList = []
     1920        for m,v in terms[1:]:
     1921            if v in multdepVarList:
     1922                varList.append(str(v))
     1923        if varList:
     1924            msg += "Parameter(s) repeated as dependent: "
     1925            for i,v in enumerate(varList):
     1926                if i != 0: msg += ', '
     1927                msg += v
     1928            return False,msg,"Recast as constraint."
     1929
     1930        # zero multiplier
     1931        varList = []
     1932        valid = 0
     1933        for m,v in terms:
     1934            if m == 0:
     1935                varList.append(str(v))
     1936            else:
     1937                valid += 1
     1938        if varList and valid > 1:
     1939            msg += "Parameter(s) with zero multipliers: "
     1940            for i,v in enumerate(varList):
     1941                if i != 0: msg += ', '
     1942                msg += v
     1943            msg += " will be ignored"
     1944        elif varList:
     1945            msg += "Parameter(s) with zero multipliers:"
     1946            for i,v in enumerate(varList):
     1947                if i != 0: msg += ', '
     1948                msg += v
     1949            return False,msg,"Equivalence Ignored."
     1950       
     1951        # hold parameters
     1952        s = ''
     1953        for m,v in terms:
     1954            if v in holdParmList:
     1955                if s: s += ', '
     1956                s += str(v)
     1957        if s:
     1958            if msg: msg += '; '
     1959            msg += "Parameters set as Hold: "+s
     1960            return False,msg,'Has holds: Nothing varied.'
     1961               
     1962        # unrefined parameters
     1963        gotVary = False
     1964        gotNotVary = False
     1965        s = ''
     1966        for m,v in terms:
     1967            if v in unvariedParmsList:
     1968                gotNotVary = True
     1969                if s: s += ', '
     1970                s += str(v)
     1971            else:
     1972                gotVary = True
     1973        if gotNotVary and gotVary:  # mix of varied and unvaried parameters
     1974            if msg: msg += '. '
     1975            msg += 'Unvaried parameter(s): '+s+"; remainder set Hold. All parameters fixed."
     1976            return False,msg, 'Equivalence Ignored.'
     1977        elif gotNotVary:
     1978            if msg: msg += '. '
     1979            msg += 'All parameters not varied. Equivalence Ignored.'
     1980            return False,msg, 'Equivalence Ignored.'
     1981           
     1982        # undefined parameters
     1983        undef = 0
     1984        s = ''
     1985        for m,v in terms[1:]:
     1986            if v in undefinedVars:
     1987                undef += 1
     1988                if s: s += ', '
     1989                s += str(v)
     1990        if undef == len(terms[1:]):
     1991            msg += 'Ignored: None of the dependent parameters are defined'
     1992        elif terms[0][1] in undefinedVars:
     1993            if s:
     1994                s = terms[0][1] + ', ' + s
     1995            else:
     1996                s = terms[0][1]
     1997            msg += 'Undefined parameter(s): '+s+'. Remainder will be fixed'
     1998        elif undef:
     1999            msg += 'Undefined parameter(s): '+s+' will be dropped'
     2000    elif constrLst[-1] == 'h':
     2001        v = terms[0][1]
     2002        if v in undefinedVars: return False,"Parameter is undefined","Ignored"
     2003        if v in unvariedParmsList: return False,"Parameter is not refined","Ignored"
     2004    else:
     2005        # check for post-grouping errors
     2006        for m,v in terms:
     2007            if v in groupErrors:
     2008                return True,'Constraint singularity: see error listing','Singular'
     2009        zeroList = []
     2010        tobeHold = []
     2011        undef = []
     2012        unvar = []
     2013        hold = []
     2014        for m,v in terms: # check for zero multiplier, undefined, unvaried or hold
     2015            if m == 0:
     2016                zeroList.append(str(v))
     2017            elif v in undefinedVars:
     2018                undef.append(str(v))
     2019            elif v in unvariedParmsList:
     2020                unvar.append(str(v))
     2021            elif v in holdParmList:
     2022                hold.append(str(v))
     2023            else:
     2024                tobeHold.append(str(v))
     2025        s = ''
     2026        for v in zeroList:
     2027            if s: s += ', '
     2028            s += str(v)
     2029        if s:
     2030            if msg: msg += '; '
     2031            msg += "Parameter(s) with zero multipliers: "+s
     2032        s = ''
     2033        for v in undef:
     2034            if s: s += ', '
     2035            s += str(v)
     2036        if s:
     2037            if msg: msg += '; '
     2038            msg += "Undefined parameter(s): "+s
     2039        s = ''
     2040        for v in unvar:
     2041            if s: s += ', '
     2042            s += str(v)
     2043        if s:
     2044            if msg: msg += '; '
     2045            msg += "Unrefined parameter(s): "+s
     2046        s = ''
     2047        for v in hold:
     2048            if s: s += ', '
     2049            s += str(v)
     2050        if s:
     2051            if msg: msg += '; '
     2052            msg += '"Hold" parameter(s): '+s
     2053        if undef and tobeHold:
     2054            s = ''
     2055            for v in tobeHold:
     2056                if s: s += ', '
     2057                s += str(v)
     2058            if msg: msg += '; '
     2059            msg += "Adding Holds on "+s+"; Constraint Ignored."
     2060            note = 'Ignored'
     2061        elif undef or (len(tobeHold) == 0 and (zeroList or unvar or hold)):
     2062            if msg: msg += '; '
     2063            msg += "Constraint Ignored."
     2064            note = 'Ignored'
     2065        elif len(tobeHold) == 1 and (zeroList or unvar or hold):
     2066            if msg: msg += '; '
     2067            msg += "One parameter is retained; converted to fixed value."
     2068            note = 'Converted'
     2069        elif zeroList or unvar or hold:
     2070            if msg: msg += ': '
     2071            msg += 'Will be dropped. Constraint retained.'
     2072            note = 'Parameters removed'
     2073    return False,msg,note
     2074       
    14682075def ComputeDepESD(covMatrix,varyList,parmDict):
    14692076    '''Compute uncertainties for dependent parameters from independent ones
     
    14932100def _FormatConstraint(RelDict,RelVal):
    14942101    '''Formats a Constraint or Function for use in a convenient way'''
    1495     linelen = 45
     2102    linelen = 65
    14962103    s = [""]
    14972104    for var,val in RelDict.items():
     
    15042111            s[-1] += ' - '
    15052112            m = abs(m)
    1506         s[-1] += '%.3f*%s '%(m,var)
     2113        if m == 1:
     2114            s[-1] += '%s '%var
     2115        else:
     2116            s[-1] += '%.3f*%s '%(m,var)
    15072117    if len(s[-1]) > linelen: s.append(' ')
    15082118    if RelVal is None:
    15092119        s[-1] += ' = New variable'
    15102120    else:
    1511         s[-1] += ' = ' + RelVal
     2121        s[-1] += ' = ' + str(RelVal)
    15122122    s1 = ''
    15132123    for s2 in s:
     
    15162126    return s1
    15172127
    1518 def _showEquiv(varlist,mapvars,invmultarr):
     2128def _showEquiv(varlist,mapvars,invmultarr,longmsg=False):
    15192129    '''Format an equivalence relationship
    15202130    note that
     
    15242134    '''
    15252135    for i,mv in enumerate(mapvars):
    1526         if len(varlist) == 1:
    1527             s1 = str(mv) + ' is equivalent to '
     2136        s1 = str(mv)
     2137        if not longmsg:
     2138            s1 += ' ==> '
     2139        elif len(varlist) == 1:
     2140            s1 += ' is equivalent to '
    15282141        else:
    1529             s1 = str(mv) + ' is equivalent to parameters: '
     2142            s1 += ' is equivalent to parameters: '
    15302143        j = 0
    15312144        for v,m in zip(varlist,invmultarr):
     
    15392152    return s1
    15402153
    1541 def VarRemapShow(varyList,inputOnly=False):
     2154def VarRemapShow(varyList=None,inputOnly=False):
    15422155    '''List out the saved relationships. This should be done after the constraints have been
    15432156    defined using :func:`StoreEquivalence`, :func:`GroupConstraints` and :func:`GenerateConstraints`.
     
    15452158    :returns: a string containing the details of the contraint relationships
    15462159    '''
     2160    if varyList is None:
     2161        varyList = saveVaryList
    15472162    s = ''
    1548     if len(fixedVarList) > 0:
    1549         s += 'Fixed Parameters:\n'
    1550         for v in fixedVarList:
     2163    if len(holdParmList) > 0:
     2164        s += 'User-supplied Fixed Parameters:\n'
     2165        for v in holdParmList:
    15512166            s += '    ' + v + '\n'
    1552     if not inputOnly:
    1553         s += 'User-supplied parameter mapping relations:\n'
    1554     symout = ''
    1555     global dependentParmList,arrayList,invarrayList,indParmList,fixedDict,symGenList
     2167    if len(newHolds) > 0:
     2168        s += 'Additional Fixed Parameters:\n'
     2169        for v in newHolds:
     2170            s += '    ' + v + '\n'
     2171    userOut = ''
     2172    symOut = ''
     2173    consOut = ''
     2174    varOut = ''
     2175    global dependentParmList,arrayList,invarrayList,indParmList,symGenList
    15562176
    15572177    for varlist,mapvars,multarr,invmultarr,symFlag in zip(
     
    15742194                        s1 += " / " + str(m[0])
    15752195                if symFlag:
    1576                     symout += s1 + '\n'
     2196                    symOut += s1 + '\n'
    15772197                else:
    1578                     s += s1 + '\n'
     2198                    userOut += s1 + '\n'
    15792199                continue
    1580             s += '  %s = ' % mv
    1581             j = 0
    1582             for m,v in zip(multarr[i,:],varlist):
     2200            if mv in varyList:
     2201                lineOut = '  {} (**Varied**) = '.format(mv)
     2202            else:
     2203                lineOut = '  {} = '.format(mv)
     2204            j = 0
     2205            for (m,v) in zip(multarr[i,:],varlist):
    15832206                if m == 0: continue
    1584                 if j > 0: s += ' + '
     2207                if m < 0:
     2208                    lineOut += ' - '
     2209                    m *= -1
     2210                elif j != 0:
     2211                    lineOut += ' + '
    15852212                j += 1
    1586                 s += '(%s * %s)' % (m,v)
    1587             if mv in varyList: s += ' VARY'
    1588             s += '\n'
    1589     if symout:
    1590         s += 'Symmetry-generated relations:\n' + symout
    1591     if inputOnly: return s
    1592     s += 'Inverse parameter mapping relations:\n'
     2213                if len(lineOut) > 60 and type(mv) is float:
     2214                    consOut += lineOut
     2215                    lineOut = '\n\t'
     2216                elif len(lineOut) > 60:
     2217                    varOut += lineOut
     2218                    lineOut = '\n\t'
     2219                if m == 1:
     2220                    lineOut += '{}'.format(v)
     2221                else:
     2222                    lineOut += '({:.4g} * {})'.format(m,v)
     2223            if type(mv) is not float:
     2224                varOut += lineOut + '\n'
     2225            else:
     2226                consOut += lineOut + '\n'
     2227    if userOut:
     2228        s += '\nUser-supplied equivalences:\n' + userOut
     2229    if consOut:
     2230        s += '\nUser-supplied Constraints:\n' + consOut
     2231    if varOut:
     2232        s += '\nUser-input generated New Variables:\n' + varOut
     2233    if symOut:
     2234        s += '\nSymmetry-generated equivalences:\n' + symOut
     2235    if not (userOut or consOut or varOut or symOut):
     2236        return s + '\nNo constraints or equivalences in use'
     2237    elif inputOnly:
     2238        return s
     2239       
     2240    s += '\nInverse parameter mapping relations:\n'
    15932241    for varlist,mapvars,invmultarr in zip(dependentParmList,indParmList,invarrayList):
    15942242        for i,mv in enumerate(varlist):
    1595             s += '  %s = ' % mv
    1596             j = 0
     2243            lineOut = '  {} = '.format(mv)
     2244            j = 0 
    15972245            for m,v in zip(invmultarr[i,:],mapvars):
    15982246                if m == 0: continue
    1599                 if j > 0: s += ' + '
     2247                if v == 0: continue
     2248                if m < 0:
     2249                    lineOut += ' - '
     2250                    m *= -1
     2251                elif j != 0:
     2252                    lineOut += ' + '
    16002253                j += 1
    1601                 s += '(%s * %s)' % (m,v)
    1602             s += '\n'
     2254                if len(lineOut) > 60:
     2255                    s += lineOut
     2256                    lineOut = '\n\t'
     2257                if m == 1:
     2258                    lineOut += '{}'.format(v)
     2259                else:
     2260                    try:
     2261                        lineOut += '{:.4g}'.format(m*v)
     2262                    except:
     2263                        lineOut += '({:.4g} * {})'.format(m,v)
     2264            if j == 0: lineOut += '0'
     2265            s += lineOut + '\n'
    16032266    return s
    16042267
     
    16092272    '''
    16102273    symout = []
    1611     global dependentParmList,arrayList,invarrayList,indParmList,fixedDict,symGenList
     2274    symerr = []
     2275    symhelp = []
     2276    global dependentParmList,arrayList,invarrayList,indParmList,symGenList
    16122277
    16132278    for varlist,mapvars,multarr,invmultarr,symFlag in zip(
    16142279        dependentParmList,indParmList,arrayList,invarrayList,symGenList):
     2280        if not symFlag: continue
    16152281        for i,mv in enumerate(mapvars):
    1616             if not symFlag: continue
     2282            cnstr = [[1,mv]]
    16172283            if multarr is None:
    16182284                s1 = ''
    16192285                s2 = ' = ' + str(mv)
    16202286                j = 0
     2287                helptext = 'Variable {:} '.format(mv) + " ("+ G2obj.fmtVarDescr(mv) + ")"
    16212288                if len(varlist) == 1:
     2289                    cnstr.append([invmultarr[0][0],varlist[0]])
    16222290                    # format the way Bob prefers
    16232291                    if invmultarr[0][0] == 1:
     
    16262294                        s1 = str(varlist[0]) + ' = ' + str(
    16272295                            invmultarr[0][0]) + ' * '+ str(mv)
    1628                     symout.append(s1)
    1629                     continue
    1630                 for v,m in zip(varlist,invmultarr):
    1631                     if debug: print ('v,m[0]: ',v,m[0])
    1632                     if len(s1.split('\n')[-1]) > 75: s1 += '\n        '
    1633                     if j > 0: s1 += ' =  '
    1634                     j += 1
    1635                     s1 += str(v)
    1636                     if m != 1:
    1637                         s1 += " / " + str(m[0])
     2296                    s2 = ''
     2297                   
     2298                    m = 1./invmultarr[0][0]
     2299                    var1 = str(varlist[0])
     2300                    helptext += "\n\nis equivalent to "
     2301                    if m == 1:
     2302                        helptext += '\n  {:} '.format(var1) + " ("+ G2obj.fmtVarDescr(var1) + ")"
     2303                    else:
     2304                        helptext += '\n  {:3g} * {:} '.format(m,var1) + " ("+ G2obj.fmtVarDescr(var1) + ")"
     2305                else:
     2306                    helptext += "\n\nis equivalent to the following:"
     2307                    for v,m in zip(varlist,invmultarr):
     2308                        cnstr.append([m,v])
     2309                        #if debug: print ('v,m[0]: ',v,m[0])
     2310                        if len(s1.split('\n')[-1]) > 75: s1 += '\n        '
     2311                        if j > 0: s1 += ' =  '
     2312                        j += 1
     2313                        s1 += str(v)
     2314                        if m != 1:
     2315                            s1 += " / " + str(m[0])
     2316                            helptext += '\n  {:3g} * {:} '.format(m,v) + " ("+ G2obj.fmtVarDescr(v) + ")"
     2317                        else:
     2318                            helptext += '\n  {:} '.format(v) + " ("+ G2obj.fmtVarDescr(v) + ")"
     2319                err,msg,note = getConstrError(cnstr+[None,None,'e'])
     2320                symerr.append([msg,note])
    16382321                symout.append(s1+s2)
    1639                 continue
     2322                symhelp.append(helptext)
    16402323            else:
    16412324                s = '  %s = ' % mv
     
    16472330                    s += '(%s * %s)' % (m,v)
    16482331                print ('unexpected sym op='+s)
    1649     return symout
     2332    return symout,symerr,symhelp
    16502333
    16512334def Dict2Deriv(varyList,derivDict,dMdv):
     
    16822365
    16832366def Map2Dict(parmDict,varyList):
    1684     '''Create (or update) the Independent Parameters from the original
    1685     set of Parameters
    1686 
    1687     Removes dependent variables from the varyList
    1688 
    1689     This should be done once, after the constraints have been
    1690     defined using :func:`StoreEquivalence`,
    1691     :func:`GroupConstraints` and :func:`GenerateConstraints` and
    1692     before any parameter refinement is done. This completes the parameter
    1693     dictionary by defining independent parameters and it satisfies the
    1694     constraint equations in the initial parameters
     2367    '''Updates the parameter dictionary and the varyList using the
     2368    equivalence and constraint input. This should be called at least once, after
     2369    the constraints have been defined using :func:`StoreEquivalence`,
     2370    :func:`GroupConstraints` and :func:`GenerateConstraints` and before any
     2371    parameter refinement is done.
     2372
     2373    This completes the parameter dictionary by defining values for parameters
     2374    created by constraints based on the constraints that define them
     2375    using the values for the current parameters. It also removes all dependent
     2376    variables from the varyList
    16952377
    16962378    :param dict parmDict: a dict containing parameter values keyed by the
    1697       parameter names.
    1698       This will contain updated values for both dependent and independent
    1699       parameters after Dict2Map is called. It will also contain some
    1700       unexpected entries of every constant value {'0':0.0} & {'1.0':1.0},
    1701       which do not cause any problems.
    1702 
    1703     :param list varyList: a list of parameters names that will be varied
    1704    
    1705 
     2379      parameter names. For new variables created by constraints, entries
     2380      will be added to the dictionary, if not alreay present, or the
     2381      values will be recomputed.
     2382
     2383    :param list varyList: a list of parameters names. Will be modified.
    17062384    '''
    1707     # process the Independent vars: remove dependent ones from varylist
    1708     # and then compute values for the Independent ones from their dependents
    1709     global dependentParmList,arrayList,invarrayList,indParmList,fixedDict
     2385    # remove fixed parameters from the varyList
     2386    for item in holdParmList+newHolds:
     2387        if item in varyList: varyList.remove(item)
     2388           
     2389    # process the independent parameters:
     2390    # * remove dependent ones from varylist
     2391    # * for equivalences apply the independent parameters onto dependent variables
     2392    global dependentParmList,arrayList,invarrayList,indParmList
     2393    for varlist,mapvars,multarr,invmultarr in zip(dependentParmList,indParmList,arrayList,invarrayList):
     2394        for item in varlist:
     2395            if item in varyList: varyList.remove(item)
     2396        if multarr is None:
     2397            #for v,val in zip(  # shows values to be set
     2398            #    varlist,
     2399            #    np.dot(invmultarr,np.array([parmDict[var] for var in mapvars]))
     2400            #    ): print('parmDict set',v,':',val)
     2401            parmDict.update(zip(
     2402                varlist,
     2403                np.dot(invmultarr,np.array([parmDict[var] for var in mapvars]))
     2404                ))
     2405
     2406    # * for the created parameters, compute them from their dependents
    17102407    for varlist,mapvars,multarr in zip(dependentParmList,indParmList,arrayList):
    1711         for item in varlist:
    1712             try:
    1713                 varyList.remove(item)
    1714             except ValueError:
    1715                 pass
    17162408        if multarr is None: continue
    1717         valuelist = [parmDict[var] for var in varlist]
    1718         parmDict.update(zip(mapvars,
    1719                             np.dot(multarr,np.array(valuelist)))
    1720                         )
    1721     # now remove fixed parameters from the varyList
    1722     global fixedVarList
    1723     for item in fixedVarList:
    1724         try:
    1725             varyList.remove(item)
    1726         except ValueError:
    1727             pass
    1728     # Set constrained parameters to their fixed values
    1729     parmDict.update(fixedDict)
    1730 
    1731 def Dict2Map(parmDict,varyList):
     2409        # evaluate all constraints in the forward direction
     2410        for key,value in zip(mapvars,np.dot(multarr,np.array([parmDict[var] for var in varlist]))):
     2411            if type(key) is float: continue
     2412            #print('parmDict set',key,':',value)
     2413            parmDict[key] = value
     2414    global saveVaryList
     2415    saveVaryList = copy.copy(varyList)
     2416           
     2417def Dict2Map(parmDict):
    17322418    '''Applies the constraints defined using :func:`StoreEquivalence`,
    17332419    :func:`GroupConstraints` and :func:`GenerateConstraints` by changing
    17342420    values in a dict containing the parameters. This should be
    1735     done before the parameters are used for any computations
     2421    done after refinement and before the parameters are used for
     2422    any computations
    17362423
    17372424    :param dict parmDict: a dict containing parameter values keyed by the
    1738       parameter names.
    1739       This will contain updated values for both dependent and independent
    1740       parameters after Dict2Map is called. It will also contain some
    1741       unexpected entries of every constant value {'0':0.0} & {'1.0':1.0},
    1742       which do not cause any problems.
    1743 
    1744     :param list varyList: a list of parameters names that will be varied
    1745    
     2425      parameter names. After this is called, all the dependent variables
     2426      will be updated based on constraints and equivalences.
    17462427    '''
    1747     global dependentParmList,arrayList,invarrayList,indParmList,fixedDict
    1748     # reset fixed values (should not be needed, but very quick)
    1749     # - this seems to update parmDict with {'0':0.0} & {'1.0':1.0} - probably not what was intended
    1750     # not needed, but also not a problem - BHT
    1751     parmDict.update(fixedDict)
     2428    global dependentParmList,arrayList,invarrayList,indParmList
    17522429    for varlist,mapvars,invmultarr in zip(dependentParmList,indParmList,invarrayList):
    1753         #if invmultarr is None: continue
    1754         try:
    1755             valuelist = [parmDict[var] for var in mapvars]
    1756         except KeyError:
     2430        if invmultarr is None:  # is this needed?
     2431            if GSASIIpath.GetConfigValue('debug'):
     2432                print('Why does this constraint have None for invmultarr?',varlist,mapvars)
    17572433            continue
    1758         parmDict.update(zip(varlist,np.dot(invmultarr,np.array(valuelist))))
    1759 
     2434        valslist = [parmDict.get(var,var) for var in mapvars]
     2435        #for v,val in zip(varlist,np.dot(invmultarr,np.array(valslist))): print(v,val) # shows what is being set
     2436        parmDict.update(zip(varlist,np.dot(invmultarr,np.array(valslist))))
     2437       
    17602438#======================================================================
    17612439# internal routines follow (these routines are unlikely to be called
     
    18492527    #StoreEquivalence('0:99:Scale',('0:12:Scale',)) # error: equiv & constrained
    18502528    #StoreEquivalence('0:12:Scale',('0:99:Scale',)) # error: equiv & constrained
    1851     varylist = ['2::atomx:3',
     2529    varyList = ['2::atomx:3',
    18522530                '2::C(10,6,1)', '1::C(10,6,1)',
    18532531                '2::atomy:3', '2::atomz:3',
    18542532                '0:12:Scale', '0:11:Scale', '0:14:Scale', '0:13:Scale', '0:0:Scale']
    1855 #    e,w = CheckConstraints([,
    1856 #                     [{'2:0:Scale': 1.0, '5:0:Scale': 1.0, '10:0:Scale': 1.0, '6:0:Scale': 1.0, '9:0:Scale': 1.0, '8:0:Scale': 1.0,# '3:0:Scale': 1.0, '4:0:Scale': 1.0, '7:0:Scale': 1.0, '1:0:Scale': 1.0, '0:0:Scale': 1.0}],
    1857 #                     ['1.0'])
    1858 #    if e: print 'error=',e
    1859 #    if w: print 'error=',w
    18602533#    varyList = ['0::A0', '0::AUiso:0', '0::Afrac:1', '0::Afrac:2', '0::Afrac:3', '0::Afrac:4',
    18612534#       '0::dAx:5', '0::dAy:5', '0::dAz:5', '0::AUiso:5', ':0:Back;0', ':0:Back;1', ':0:Back;2', ':0:Back;3',
     
    18682541#    fixedList = ['40.0', '1.0', '1.0']
    18692542
    1870     errmsg, warnmsg = CheckConstraints(varylist,constrDict,fixedList)
    1871     if errmsg:
    1872         print ("*** Error ********************")
    1873         print (errmsg)
    1874     if warnmsg:
    1875         print ("*** Warning ********************")
    1876         print (warnmsg)
    1877     if errmsg or warnmsg:
    1878         sys.exit()
    1879     groups,parmlist = GroupConstraints(constrDict)
    1880     GenerateConstraints(groups,parmlist,varylist,constrDict,fixedList)
    1881     print (VarRemapShow(varylist))
     2543    msg,warning,groups,parmlist = GenerateConstraints(varyList,constrDict,fixedList,parmdict)
     2544    print (VarRemapShow(varyList))
    18822545    parmdict.update( {
    18832546        '0:12:Scale': 1.0, '0:11:Scale': 1.0, '0:14:Scale': 1.0, '0:13:Scale': 1.0, '0:0:Scale': 2.0,
     
    18902553        })
    18912554    print ('parmdict start',parmdict)
    1892     print ('varylist start',varylist)
     2555    print ('varylist start',varyList)
    18932556    before = parmdict.copy()
    1894     Map2Dict(parmdict,varylist)
     2557    Map2Dict(parmdict,varyList)
    18952558    print ('parmdict before and after Map2Dict')
    18962559    print ('  key / before / after')
    18972560    for key in sorted(list(parmdict.keys())):
    18982561        print ('  '+key,'\t',before.get(key),'\t',parmdict[key])
    1899     print ('varylist after',varylist)
     2562    print ('varylist after',varyList)
    19002563    before = parmdict.copy()
    1901     Dict2Map(parmdict,varylist)
     2564    Dict2Map(parmdict)
    19022565    print ('after Dict2Map')
    19032566    print ('  key / before / after')
  • trunk/GSASIIobj.py

    r5025 r5038  
    4040list is a :ref:`constraint definition objects <Constraint_definitions_table>`.
    4141The constraints in this form are converted in
    42 :func:`GSASIIstrIO.ProcessConstraints` to the form used in :mod:`GSASIImapvars`
     42:func:`GSASIImapvars.ProcessConstraints` to the form used in :mod:`GSASIImapvars`
    4343
    4444The keys in the Constraints dict are:
     
    18681868    'AU([123][123])':'Atomic anisotropic displacement parameter U\\1',
    18691869
    1870     will match ``AU11``, ``AU23``,.. and `U11`, `U23` etc will be displayed
     1870    will match ``AU11``, ``AU23``,... and `U11`, `U23` etc will be displayed
    18711871    in the value when used.
    18721872
     
    21682168        v = []
    21692169        for i in var.split(':'):
    2170             if i == '':
    2171                 v.append(-1)
    2172                 continue
     2170#            if i == '' or i == '*':
     2171#                v.append(-1)
     2172#                continue
    21732173            try:
    21742174                v.append(int(i))
    21752175            except:
    2176                 v.append(i)
     2176                v.append(-1)
    21772177        return v
    21782178    return sorted(varlist,key=cvnnums)
     
    22802280        return hash(self.varname())
    22812281
    2282     def varname(self):
     2282    def varname(self,hist=None):
    22832283        '''Formats the GSAS-II variable name as a "traditional" GSAS-II variable
    22842284        string (p:h:<var>:a) or (p:h:<var>)
    22852285
     2286        :param str/int hist: if specified, overrides the histogram number
     2287          with the specified value
    22862288        :returns: the variable name as a str
    22872289        '''
     
    23022304                else:
    23032305                    a = ":?"
    2304         if self.histogram == "*":
     2306        if hist is not None and self.histogram:
     2307            hist = str(hist)
     2308        elif self.histogram == "*":
    23052309            hist = "*"
    23062310        else:
     
    23592363            return False
    23602364        return True
     2365   
     2366    def fmtVarByMode(self, seqmode, note, warnmsg):
     2367        '''Format a parameter object for display. Note that these changes
     2368        are only temporary and are only shown only when the Constraints
     2369        data tree is selected.
     2370
     2371        * In a non-sequential refinement or where the mode is 'use-all', the
     2372          name is converted unchanged to a str
     2373        * In a sequential refinement when the mode is 'wildcards-only' the
     2374          name is converted unchanged to a str but a warning is added
     2375          for non-wildcarded HAP or Histogram parameters
     2376        * In a sequential refinement or where the mode is 'auto-wildcard',
     2377          a histogram number is converted to a wildcard (*) and then
     2378          converted to str
     2379
     2380        :param str mode: the sequential mode (see above)
     2381        :param str note: value displayed on the line of the constraint/equiv.
     2382        :param str warnmsg: a message saying the constraint is not used
     2383
     2384        :returns: varname, explain, note, warnmsg (all str values) where:
     2385          * varname is the parameter expressed as a string,
     2386          * explain is blank unless there is a warning explanation about
     2387            the parameter or blank
     2388          * note is the previous value unless overridden
     2389          * warnmsg is the previous value unless overridden
     2390        '''
     2391        explain = ''
     2392        s = self.varname()
     2393        if seqmode == 'auto-wildcard':
     2394            if self.histogram: s = self.varname('*')
     2395        elif seqmode == 'wildcards-only' and self.histogram:
     2396            if self.histogram != '*':
     2397                warnmsg = 'Ignored due to use of a non-wildcarded histogram number'
     2398                note = 'Ignored'
     2399                explain = '\nIgnoring: '+self.varname()+' does not contain a wildcard.\n'
     2400        elif seqmode != 'use-all' and seqmode != 'wildcards-only':
     2401            print('Unexpected mode',seqmode,' in fmtVarByMode')
     2402        return s,explain,note,warnmsg
    23612403
    23622404    def _show(self):
  • trunk/GSASIIscriptable.py

    r5025 r5038  
    24772477            # G2strMain.RefineCore(Controls,Histograms,Phases,restraintDict,rigidbodyDict,parmDict,varyList,
    24782478            #      calcControls,pawleyLookup,ifPrint,printFile,dlg)
     2479
     2480            # check that constraints are OK
     2481            errmsg, warnmsg = G2strIO.ReadCheckConstraints(self.filename)
     2482            if errmsg:
     2483                G2fil.G2Print('Constraint error',errmsg)
     2484                raise Exception('Constraint error')
     2485            if warnmsg:
     2486                G2fil.G2Print('\nNote these constraint warning(s):\n'+warnmsg)
     2487                G2fil.G2Print('Generated constraints\n'+G2mv.VarRemapShow([],True))
    24792488            G2strMain.Refine(self.filename, makeBack=makeBack)
    24802489        else:
     
    24972506        errmsg, warnmsg = G2strIO.ReadCheckConstraints(self.filename)
    24982507        if errmsg:
    2499             G2fil.G2Print('Refinement error',errmsg)
     2508            G2fil.G2Print('Constraint error',errmsg)
    25002509            raise Exception('Constraint error')
    25012510        if warnmsg:
    2502             G2fil.G2Print(u'Warning: Conflict between refinment flag settings and constraints:\n'+
    2503                   warnmsg+u'\nRefinement not possible')
    2504             raise Exception('Constraint error')
     2511            G2fil.G2Print('\nNote these constraint warning(s):\n'+warnmsg)
     2512            G2fil.G2Print('Generated constraints\n'+G2mv.VarRemapShow([],True))
    25052513        OK,Msg = G2strMain.SeqRefine(self.filename,None)
    25062514
  • trunk/GSASIIseqGUI.py

    r5000 r5038  
    616616            VparmDict.update({item:0.0 for item in parmDict if 'dA' in item})
    617617            VparmDict[var] += incr
    618             G2mv.Dict2Map(VparmDict,[]) # apply constraints
     618            G2mv.Dict2Map(VparmDict) # apply constraints
    619619            # generate the atom positions and the direct & reciprocal cell values now, because they might
    620620            # needed to evaluate the pseudovar
  • trunk/GSASIIstrIO.py

    r5025 r5038  
    145145    return Controls
    146146
    147 def GetConstraints(GPXfile):
     147def ReadConstraints(GPXfile, seqHist=None):
    148148    '''Read the constraints from the GPX file and interpret them
    149149
     
    157157        raise Exception("No constraints in GPX file")
    158158    fl.seek(pos)
    159     datum = cPickleLoad(fl)[0]
     159    ConstraintsItem = cPickleLoad(fl)[0]
     160    seqmode = 'use-all'
     161    if seqHist is not None:
     162        seqmode = ConstraintsItem[1].get('_seqmode','wildcards-only')
    160163    fl.close()
    161164    constList = []
    162     for item in datum[1]:
     165    for item in ConstraintsItem[1]:
    163166        if item.startswith('_'): continue
    164         constList += datum[1][item]
    165     constDict,fixedList,ignored = ProcessConstraints(constList)
    166     if ignored:
    167         G2fil.G2Print ('Warning: {} Constraints were rejected. Was a constrained phase, histogram or atom deleted?'.format(ignored))
    168     return constDict,fixedList
    169    
    170 def ProcessConstraints(constList):
    171     """Interpret the constraints in the constList input into a dictionary, etc.
    172     All :class:`GSASIIobj.G2VarObj` objects are mapped to the appropriate
    173     phase/hist/atoms based on the object internals (random Ids). If this can't be
    174     done (if a phase has been deleted, etc.), the variable is ignored.
    175     If the constraint cannot be used due to too many dropped variables,
    176     it is counted as ignored.
    177     NB: this processing does not include symmetry imposed constraints
    178    
    179     :param list constList: a list of lists where each item in the outer list
    180       specifies a constraint of some form, as described in the :mod:`GSASIIobj`
    181       :ref:`Constraint definition <Constraint_definitions_table>`.
    182 
    183     :returns:  a tuple of (constDict,fixedList,ignored) where:
    184      
    185       * constDict (list of dicts) contains the constraint relationships
    186       * fixedList (list) contains the fixed values for each type
    187         of constraint.
    188       * ignored (int) counts the number of invalid constraint items
    189         (should always be zero!)
    190     """
    191     constDict = []
    192     fixedList = []
    193     ignored = 0
    194     namedVarList = []
    195     for item in constList:
    196         if item[-1] == 'h':
    197             # process a hold
    198             fixedList.append('0')
    199             var = str(item[0][1])
    200             if '?' not in var:
    201                 constDict.append({var:0.0})
    202             else:
    203                 ignored += 1
    204         elif item[-1] == 'f':
    205             # process a new variable
    206             fixedList.append(None)
    207             D = {}
    208             varyFlag = item[-2]
    209             varname = item[-3]
    210             for term in item[:-3]:
    211                 var = str(term[1])
    212                 if '?' not in var:
    213                     D[var] = term[0]
    214             if len(D) > 1:
    215                 # add extra dict terms for input variable name and vary flag
    216                 if varname is not None:
    217                     varname = str(varname) # in case this is a G2VarObj
    218                     if varname.startswith(':'):
    219                         D['_name'] = varname
    220                     else:
    221                         D['_name'] = '::nv-' + varname
    222                     D['_name'] = G2obj.MakeUniqueLabel(D['_name'],namedVarList)
    223                 D['_vary'] = varyFlag == True # force to bool
    224                 constDict.append(D)
    225             else:
    226                 ignored += 1
    227             #constFlag[-1] = ['Vary']
    228         elif item[-1] == 'c':
    229             # process a contraint relationship
    230             D = {}
    231             for term in item[:-3]:
    232                 var = str(term[1])
    233                 if '?' not in var:
    234                     D[var] = term[0]
    235             if len(D) >= 1:
    236                 fixedList.append(str(item[-3]))
    237                 constDict.append(D)
    238             else:
    239                 ignored += 1
    240         elif item[-1] == 'e':
    241             # process an equivalence
    242             firstmult = None
    243             eqlist = []
    244             for term in item[:-3]:
    245                 if term[0] == 0: term[0] = 1.0
    246                 var = str(term[1])
    247                 if '?' in var: continue
    248                 if firstmult is None:
    249                     firstmult = term[0]
    250                     firstvar = var
    251                 else:
    252                     eqlist.append([var,firstmult/term[0]])
    253             if len(eqlist) > 0:
    254                 G2mv.StoreEquivalence(firstvar,eqlist,False)
    255             else:
    256                 ignored += 1
    257         else:
    258             ignored += 1
    259     return constDict,fixedList,ignored
    260 
     167        constList += ConstraintsItem[1][item]
     168    constrDict,fixedList,ignored = G2mv.ProcessConstraints(constList,seqmode,seqHist)
     169    #if ignored:
     170    #    G2fil.G2Print ('Warning: {} Constraints were rejected. Was a constrained phase, histogram or atom deleted?'.format(ignored))
     171    return constrDict,fixedList
     172   
    261173def ReadCheckConstraints(GPXfile, seqHist=None):
    262174    '''Load constraints and related info and return any error or warning messages
    263175    This is done from the GPX file rather than the tree.
    264176
    265     :param dict seqHist: defines a specific histogram to be loaded for a sequential
    266        refinement, if None (default) all are loaded.
     177    :param str GPXfile: specifies the path to a .gpx file.
     178    :param str seqHist: specifies a histogram to be loaded for
     179      a sequential refinement. If None (default) all are loaded.
    267180    '''
    268181    # init constraints
     
    274187    if not Histograms:
    275188        return 'Error: no diffraction data',''
    276     constrDict,fixedList = GetConstraints(GPXfile) # load user constraints before internally generated ones
    277     if seqHist: Histograms = {seqHist:Histograms[seqHist]}  # sequential fit: only need one histogram
     189    if seqHist:
     190        Histograms = {seqHist:Histograms[seqHist]}  # sequential fit: only need one histogram
     191        hId = Histograms[seqHist]['hId']
     192    else:
     193        hId = None
     194    constrDict,fixedList = ReadConstraints(GPXfile, hId) # load user constraints from file (uses ProcessConstraints)
     195    parmDict = {}
     196    # generate symmetry constraints to check for conflicts
    278197    rigidbodyDict = GetRigidBodies(GPXfile)
    279198    rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
    280199    rbVary,rbDict = GetRigidBodyModels(rigidbodyDict,Print=False)
    281     Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = \
    282         GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
     200    parmDict.update(rbDict)
     201    (Natoms, atomIndx, phaseVary, phaseDict, pawleyLookup, FFtables,
     202         BLtables, MFtables, maxSSwave) = GetPhaseData(
     203             Phases, RestraintDict=None, rbIds=rbIds, Print=False) # generates atom symmetry constraints
     204    parmDict.update(phaseDict)
    283205    hapVary,hapDict,controlDict = GetHistogramPhaseData(Phases,Histograms,Print=False,resetRefList=False)
     206    parmDict.update(hapDict)
    284207    histVary,histDict,controlDict = GetHistogramData(Histograms,Print=False)
     208    parmDict.update(histDict)
    285209    varyList = rbVary+phaseVary+hapVary+histVary
    286210    msg = G2mv.EvaluateMultipliers(constrDict,phaseDict,hapDict,histDict)
    287211    if msg:
    288212        return 'Unable to interpret multiplier(s): '+msg,''
    289     errmsg, warnmsg = G2mv.CheckConstraints(varyList,constrDict,fixedList)
    290     if errmsg:
    291         # print some diagnostic info on the constraints
    292         G2fil.G2Print('Error in constraints:\n'+errmsg+
    293               '\nRefinement not possible due to conflict in constraints, see below:\n')
    294         G2fil.G2Print(G2mv.VarRemapShow(varyList,True),mode='error')
     213    errmsg,warnmsg,groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict)
     214    G2mv.Map2Dict(parmDict,varyList)   # changes varyList
    295215    return errmsg, warnmsg
    296216   
     
    1044964        G2mv.StoreEquivalence(pfx+'A0',(pfx+'A1',pfx+'A2',))
    1045965        return [pfx+'A0',pfx+'A1',pfx+'A2']
    1046        
     966   
    1047967def modVary(pfx,SSGData):
    1048968    vary = []
     
    1057977       
    1058978def GetRigidBodyModels(rigidbodyDict,Print=True,pFile=None):
    1059     'Get Rigid body info from tree entry and print it to .LST file'
    1060    
     979    '''Get Rigid body info from tree entry and print it to .LST file
     980    Adds variables and dict items for vector RBs, but for Residue bodies
     981    this is done in :func:`GetPhaseData`.
     982    '''
     983
    1061984    def PrintResRBModel(RBModel):
    1062985        pFile.write('Residue RB name: %s no.atoms: %d, No. times used: %d\n'%
     
    10901013                    pFile.write('\nVector rigid body model:\n')
    10911014                    PrintVecRBModel(rigidbodyDict['Vector'][item])
    1092     if len(rbIds['Residue']):
    1093         for item in rbIds['Residue']:
    1094             if rigidbodyDict['Residue'][item]['useCount']:
    1095                 if Print:
     1015    if Print:
     1016        if len(rbIds['Residue']):
     1017            for item in rbIds['Residue']:
     1018                if rigidbodyDict['Residue'][item]['useCount']:
    10961019                    pFile.write('\nResidue rigid body model:\n')
    10971020                    PrintResRBModel(rigidbodyDict['Residue'][item])
     
    13791302                    phaseVary += [name,]
    13801303                    equivs[xId[i]].append([name,xCoef[i]])
    1381                 elif symHold is not None: #variable is held due to symmetry
    1382                     symHold.append(name)
     1304                else:
     1305                    if symHold is not None: #variable is held due to symmetry
     1306                        symHold.append(name)
     1307                    G2mv.StoreHold(name)
    13831308        for equiv in equivs:
    13841309            if len(equivs[equiv]) > 1:
     
    15441469                            phaseVary.append(names[j])
    15451470                            equivs[xId[j]].append([names[j],xCoef[j]])
    1546                         elif symHold is not None: #variable is held due to symmetry
    1547                             symHold.append(names[j])
     1471                        else:
     1472                            if symHold is not None: #variable is held due to symmetry
     1473                                symHold.append(names[j])
     1474                            G2mv.StoreHold(names[j])
    15481475                    for equiv in equivs:
    15491476                        if len(equivs[equiv]) > 1:
  • trunk/GSASIIstrMain.py

    r5025 r5038  
    224224        Rvals['chisq'] = np.sum(result[2]['fvec']**2)
    225225        G2stMth.Values2Dict(parmDict, varyList, result[0])
    226         G2mv.Dict2Map(parmDict,varyList)
     226        G2mv.Dict2Map(parmDict)
    227227        Rvals['Nobs'] = Histograms['Nobs']
    228228        Rvals['Nvars'] = len(varyList)
     
    307307    This can be called in one of three ways, from :meth:`GSASIIdataGUI.GSASII.OnRefine` in an
    308308    interactive refinement, where dlg will be a wx.ProgressDialog, or non-interactively from
    309     :meth:`GSASIIscriptable.G2Project.refine` or from :func:`main`, where dlg will be None.
     309    :meth:`GSASIIscriptable.G2Project.refine` or from :func:`do_refine`, where dlg will be None.
    310310    '''
    311311    import GSASIImpsubs as G2mp
     
    323323    calcControls = {}
    324324    calcControls.update(Controls)
    325     constrDict,fixedList = G2stIO.GetConstraints(GPXfile)
     325    constrDict,fixedList = G2stIO.ReadConstraints(GPXfile)
    326326    restraintDict = G2stIO.GetRestraints(GPXfile)
    327327    Histograms,Phases = G2stIO.GetUsedHistogramsAndPhases(GPXfile)
     
    364364        return False,{'msg':'Unable to interpret multiplier(s): '+msg}
    365365    try:
    366         G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict)
    367         #print(G2mv.VarRemapShow(varyList))
    368         #print('DependentVars',G2mv.GetDependentVars())
    369         #print('IndependentVars',G2mv.GetIndependentVars())
     366        errmsg,warnmsg,groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict)
     367        G2mv.Map2Dict(parmDict,varyList)   # changes varyList
    370368    except G2mv.ConstraintException:
    371369        G2fil.G2Print (' *** ERROR - your constraints are internally inconsistent ***')
    372         #errmsg, warnmsg = G2mv.CheckConstraints(varyList,constrDict,fixedList)
    373         #print 'Errors',errmsg
    374         #if warnmsg: print 'Warnings',warnmsg
    375370        return False,{'msg':' Constraint error'}
    376 #    print G2mv.VarRemapShow(varyList)
    377371
    378372    # remove frozen vars from refinement
     
    528522    calcControls = {}
    529523    calcControls.update(Controls)
    530     constrDict,fixedList = G2stIO.GetConstraints(GPXfile)
     524    constrDict,fixedList = G2stIO.ReadConstraints(GPXfile)
    531525    restraintDict = {}
    532526    Histograms,Phases = G2stIO.GetUsedHistogramsAndPhases(GPXfile)
     
    732726           
    733727        G2stIO.GetFprime(calcControls,Histo)
    734         # do constraint processing
    735         #reload(G2mv) # debug
    736         constrDict,fixedList = G2stIO.GetConstraints(GPXfile)
     728        # do constraint processing (again, if called from GSASIIdataGUI.GSASII.OnSeqRefine)
     729        constrDict,fixedList = G2stIO.ReadConstraints(GPXfile,seqHist=hId)
    737730        varyListStart = tuple(varyList) # save the original varyList before dependent vars are removed
    738         msg = G2mv.EvaluateMultipliers(constrDict,parmDict)
     731
     732        msg = G2mv.EvaluateMultipliers(constrDict,phaseDict,hapDict,histDict)
    739733        if msg:
    740734            return False,'Unable to interpret multiplier(s): '+msg
     735     
    741736        try:
    742             groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict,SeqHist=hId)
    743 #            if GSASIIpath.GetConfigValue('debug'): print("DBG_"+
    744 #                G2mv.VarRemapShow(varyList,True))
    745 #            print('DependentVars',G2mv.GetDependentVars())
    746 #            print('IndependentVars',G2mv.GetIndependentVars())
     737            errmsg,warnmsg,groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict,
     738                                                                          SeqHist=hId)
    747739            constraintInfo = (groups,parmlist,constrDict,fixedList,ihst)
     740            G2mv.Map2Dict(parmDict,varyList)   # changes varyList
    748741        except G2mv.ConstraintException:
    749             G2fil.G2Print (' *** ERROR - your constraints are internally inconsistent ***')
    750             #errmsg, warnmsg = G2mv.CheckConstraints(varyList,constrDict,fixedList)
    751             #print 'Errors',errmsg
    752             #if warnmsg: print 'Warnings',warnmsg
     742            G2fil.G2Print (' *** ERROR - your constraints are internally inconsistent for histogram {}***'.format(hId))
    753743            return False,' Constraint error'
    754         #print G2mv.VarRemapShow(varyList)
    755744        if not ihst:
    756745            # first histogram to refine against
     
    12591248    G2fil.G2Print ('\n Best plane RMS X =%8.3f, Y =%8.3f, Z =%8.3f'%(Evec[Order[2]],Evec[Order[1]],Evec[Order[0]]))
    12601249
    1261 def main():
     1250def do_refine(*args):
    12621251    'Called to run a refinement when this module is executed '
    12631252    starttime = time.time()
    12641253    arg = sys.argv
    1265     if len(arg) > 1:
    1266         GPXfile = arg[1]
     1254    if len(args) >= 1:
     1255        files = args
     1256    elif len(sys.argv) > 1:
     1257        files = sys.argv[1:]
     1258    else:
     1259        G2fil.G2Print ('ERROR GSASIIstrMain.do_refine error - missing filename')
     1260        G2fil.G2Print ('Use "python GSASIIstrMain.py f1.gpx [f2.gpx f3.gpx...]" to run')
     1261        G2fil.G2Print ('or call GSASIIstrMain.do_refine directly')
     1262        exit()
     1263    for GPXfile in files:
    12671264        if not ospath.exists(GPXfile):
    1268             G2fil.G2Print ('ERROR - '+GPXfile+" doesn't exist!")
    1269             exit()
    1270     else:
    1271         G2fil.G2Print ('ERROR - missing filename')
    1272         exit()
    1273     # TODO: figure out if this is a sequential refinement and call SeqRefine(GPXfile,None)
    1274     Refine(GPXfile,None)
    1275     G2fil.G2Print("Done. Execution time {:.2f} sec.".format(time.time()-starttime))
     1265            G2fil.G2Print ('ERROR - '+GPXfile+" doesn't exist! Skipping.")
     1266            continue
     1267        # TODO: test below
     1268        # figure out if this is a sequential refinement and call SeqRefine(GPXfile,None)
     1269        #Controls = G2stIO.GetControls(GPXfile)
     1270        #if Controls.get('Seq Data',[]):
     1271            Refine(GPXfile,None)
     1272        #else:
     1273        #    SeqRefine(GPXfile,None)
     1274        G2fil.G2Print("Done with {}.\nExecution time {:.2f} sec.".format(GPXfile,time.time()-starttime))
    12761275
    12771276if __name__ == '__main__':
    12781277    GSASIIpath.InvokeDebugOpts()
    1279     main()
     1278    do_refine()
  • trunk/GSASIIstrMath.py

    r5025 r5038  
    610610                                # extend shift if needed to other parameters
    611611                                if var in G2mv.independentVars:
    612                                     G2mv.Dict2Map(parmDict,[])
     612                                    G2mv.Dict2Map(parmDict)
    613613                                    oneparm = False
    614614                                elif var in G2mv.dependentVars:
     
    39333933    '''
    39343934    parmDict.update(zip(varylist,values))
    3935     G2mv.Dict2Map(parmDict,varylist)
     3935    G2mv.Dict2Map(parmDict)
    39363936    Histograms,Phases,restraintDict,rigidbodyDict = HistoPhases
    39373937    dependentVars = G2mv.GetDependentVars()
     
    39903990    '''
    39913991    parmDict.update(zip(varylist,values))
    3992     G2mv.Dict2Map(parmDict,varylist)
     3992    G2mv.Dict2Map(parmDict)
    39933993    Histograms,Phases,restraintDict,rigidbodyDict = HistoPhases
    39943994    dependentVars = G2mv.GetDependentVars()
     
    41034103    '''
    41044104    Values2Dict(parmDict, varylist, values)
    4105     G2mv.Dict2Map(parmDict,varylist)
     4105    G2mv.Dict2Map(parmDict)
    41064106    Histograms,Phases,restraintDict,rigidbodyDict = HistoPhases
    41074107    M = np.empty(0)
  • trunk/help/gsasII.html

    r4966 r5038  
    30283028
    30293029<p class=MsoNormal style='margin-left:.5in'>This window shows the constraints
    3030 to be used in a refinement. It is organized into three tabbed pages. ‘Phase
    3031 constraints’ contain those involving parameters that describe aspects of the
    3032 crystalline phases to be used in the refinement (e.g. atom coordinates, thermal
    3033 motion and site fraction parameters). ‘Histogram/Phase constraints’ are those
    3034 which describe aspects of the pattern that depend on both the phase and the
    3035 data set used in the refinement (e.g. <span class=SpellE>microstrain</span> and
    3036 crystallite size parameters). ‘Histogram constraints’ are those that depend
    3037 only on the data set (e.g. profile coefficients U, V, W, X and Y). </p>
     3030to be used in a refinement. Constraints are divided with a tab for
     3031each type: Phase, Histogram/Phase, Histogram, Global
     3032  and Sym-Generated. Note that the standard parameters in GSAS-II are divided
     3033  into three classes and appear respectively on the Phase, Histogram
     3034and Histogram/Phase tabs:
     3035  <UL style='margin-left:0.75in'>
     3036  <LI>those pertaining to quantities in each phase (naming pattern
     3037    "<tt><i>p</i>::<i>name</i></tt>"); examples include
     3038    atom coordinates, thermal motion and site fraction parameters;
     3039  <LI>those pertaining to quantities in each histogram (naming pattern
     3040    ":<tt><i>h</i>:<i>name</i></tt>"); such parameters
     3041    are those that depend only on the data set the scale factor and
     3042    profile coefficients (e.g. U, V, W, X and Y);
     3043  <LI>and those pertaining to quanitities defined for each histogram in
     3044  each phase (naming pattern
     3045    "<tt><i>p</i>:<i>h</i>:<i>name</i></tt>");
     3046    these parameters are quantities that can be dependent on both the
     3047    phase properties and the sample or dataset used for the
     3048  measurement. Examples include phase fractions and sample-broadening
     3049  coefficients such as <span class=SpellE>microstrain</span> and
     3050  crystallite size.
     3051  </UL>
     3052  <p></p> 
     3053<p class=MsoNormal style='margin-left:.5in'>
     3054    In addition, if constraints are defined, they create new global
     3055  parameters. Constraints on these will be rare, but can be managed on
     3056  the Globals tab. Finally, some constraints are defined automatically based on
     3057  restrictions determined by the space group. These constraimts can be
     3058  seen, but not changed, using the Sym-Generated tab.
     3059  </p>
    30383060
    30393061<h5 style='margin-left:.5in'>What can I do here?</h5>
    30403062
    3041 <p class=MsoListParagraphCxSpFirst style='margin-left:1.0in;mso-add-space:auto;
    3042 text-indent:-.25in;mso-list:l5 level1 lfo3'><![if !supportLists]><span
    3043 style='mso-fareast-font-family:"Times New Roman"'><span style='mso-list:Ignore'>1.<span
    3044 style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span
    3045 style='mso-fareast-font-family:"Times New Roman"'>Select the tab for the
    3046 parameter types you wish to constrain. Each will have the same possibilities in
    3047 the ‘<b style='mso-bidi-font-weight:normal'>Edit’</b> menu.<o:p></o:p></span></p>
    3048 
    3049 <p class=MsoListParagraphCxSpMiddle style='margin-left:1.0in;mso-add-space:
    3050 auto;text-indent:-.25in;mso-list:l5 level1 lfo3'><![if !supportLists]><span
    3051 style='mso-fareast-font-family:"Times New Roman"'><span style='mso-list:Ignore'>2.<span
    3052 style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span
    3053 style='mso-fareast-font-family:"Times New Roman"'>Menu ‘<b style='mso-bidi-font-weight:
    3054 normal'>Edit</b>’ – <o:p></o:p></span></p>
    3055 
    3056 <p class=MsoListParagraphCxSpMiddle style='margin-left:1.5in;mso-add-space:
    3057 auto;text-indent:-.25in;mso-list:l5 level2 lfo3'><![if !supportLists]><span
    3058 style='mso-fareast-font-family:"Times New Roman"'><span style='mso-list:Ignore'>a.<span
    3059 style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><b
    3060 style='mso-bidi-font-weight:normal'><span style='mso-fareast-font-family:"Times New Roman"'>Add
    3061 Hold</span></b><span style='mso-fareast-font-family:"Times New Roman"'> –
     3063<OL style='margin-left:0.75in'>
     3064  <LI>Select the tab for the parameter type(s) you wish to
     3065  constrain.</LI>
     3066
     3067  <LI>Create new parameters using the "Edit Constr." menu commands:</LI>
     3068  <p></p>
     3069  <UL>
     3070    <LI><B>Add Hold</B>:
    30623071select a parameter that you wish to remain fixed although other parameters of
    30633072the same type may be selected as a group for refinement. For example, if the
     
    30683077select one and then OK to implement it, Cancel will cancel the operation. The
    30693078held parameter will be shown in the constraint window with the keyword ‘FIXED’.
    3070 A Delete button can be used to remove it.<o:p></o:p></span></p>
    3071 
    3072 <p class=MsoListParagraphCxSpMiddle style='margin-left:1.5in;mso-add-space:
    3073 auto;text-indent:-.25in;mso-list:l5 level2 lfo3'><![if !supportLists]><span
    3074 style='mso-fareast-font-family:"Times New Roman"'><span style='mso-list:Ignore'>b.<span
    3075 style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><b
    3076 style='mso-bidi-font-weight:normal'><span style='mso-fareast-font-family:"Times New Roman"'>Add
    3077 equivalence</span></b><span style='mso-fareast-font-family:"Times New Roman"'>
    3078 – select a list of parameters that should have the same value (possibly with a
     3079    <p></p></LI>
     3080
     3081    <LI><B>Add equivalence</B>: select a list of parameters that should have the same value (possibly with a
    30793082non-unitary multiplier for some). Examples are a list of atoms with the same
    30803083thermal motion <span class=SpellE>Uiso</span>, sets of profile coefficients
     
    30853088operation. The equivalenced parameters will show as an equation of the form M<sub>1</sub>*P<sub>1</sub>+M<sub>2</sub>*P<sub>2</sub>=0;
    30863089usually M1=1.0 and M2=-1.0, but can be changed via the ‘Edit’ button. The
    3087 keyword ‘EQUIV’ marks it as an equivalence. A Delete button can be used to
    3088 remove it.<o:p></o:p></span></p>
    3089 
    3090 <p class=MsoListParagraphCxSpMiddle style='margin-left:1.5in;mso-add-space:
    3091 auto;text-indent:-.25in;mso-list:l5 level2 lfo3'><![if !supportLists]><span
    3092 style='mso-fareast-font-family:"Times New Roman"'><span style='mso-list:Ignore'>c.<span
    3093 style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><b
    3094 style='mso-bidi-font-weight:normal'><span style='mso-fareast-font-family:"Times New Roman"'>Add
    3095 constraint</span></b><span style='mso-fareast-font-family:"Times New Roman"'> –
     3090    keyword ‘EQUIV’ marks it as an equivalence.
     3091    <p></p></LI>
     3092
     3093    <LI><B>Add constraint</B>:
    30963094select a list of parameters whose sum (with possible non-unitary multipliers)
    30973095is fixed. For example, the sum of site fractions for atoms on the same site
     
    31033101the multipliers M1, M2, … and C can be changed via the ‘Edit’ button. The
    31043102keyword ‘CONSTR’ marks it as a constraint. A Delete button can be used to
    3105 remove it.<o:p></o:p></span></p>
    3106 
    3107 <p class=MsoListParagraphCxSpLast style='margin-left:1.5in;mso-add-space:auto;
    3108 text-indent:-.25in;mso-list:l5 level2 lfo3'><![if !supportLists]><span
    3109 style='mso-fareast-font-family:"Times New Roman"'><span style='mso-list:Ignore'>d.<span
    3110 style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><b
    3111 style='mso-bidi-font-weight:normal'><span style='mso-fareast-font-family:"Times New Roman"'>Add
    3112 function</span></b><span style='mso-fareast-font-family:"Times New Roman"'> –
     3103    remove it.
     3104    <p></p></LI>
     3105   
     3106    <LI><B>Add New Var</b>
    31133107this is very similar the “Add constraint” type except that the result of the
    3114 sum can be varied in the refinement. The keyword ‘FUNCT’ marks it as a
     3108sum can be varied in the refinement. The keyword ‘New Var’ marks it as a
    31153109function; the ‘Refine?’ box indicates your choice to refine the result of the
    3116 sum. A Delete button can be used to remove it.<o:p></o:p></span></p>
     3110    sum.
     3111    <p></p></LI>
     3112
     3113    <LI><B>Make atoms equivalent</b>: This provides a shortcut for
     3114    establishing constraints when two share a single
     3115    site. Coordinates and Uiso values are constrained to be the same
     3116    and site fractions are constrained to add to 1.
     3117    <p></p></LI>
     3118
     3119    <LI><B>Show ISODISTORT modes</b>: Used after a CIF from the
     3120    ISODISTORT web site is read, which will define normal modes from
     3121    representational analysis.
     3122    <p></p></LI>
     3123  </UL></OL>
     3124 
     3125<P style='margin-left:0.75in'>In addition to menu commands, this
     3126    window also offer the following actions by pressing buttons:</P>
     3127    <UL style='margin-left:1in'>
     3128    <LI><B>Show Errors 
     3129    </B>: this button will appear if serious errors -- that would
     3130    prevent a refinement from being performed -- are encountered
     3131    processing the constraints.
     3132    <p></p></LI>
     3133
     3134    <LI><B>Show Warnings
     3135    </B>: this button will appear if correctable problems are
     3136    encountered in processing the constraints, such as a constraint
     3137    being rejected because a parameter is not varied. These warning
     3138    may indicate that the choice of which parameters will be refined
     3139    is not what was planned.
     3140    <p></p></LI>
     3141 
     3142    <LI><B>Show Generated Constraints
     3143    </B>: After constraints have been processed, a series of
     3144    relationships are developed to determine new variables from the
     3145    current parameters and "inverse" equations that determine
     3146    dependent parameters from the new variables and independent
     3147    parameters. This shows the resulting relationships, as well as
     3148    any "Hold" variables.
     3149    <p></p></LI>
     3150   
     3151    <LI><B>Delete Selected   
     3152    </B>: This button will cause all the selected constraints on the
     3153    current tab to be deleted.
     3154    <p></p></LI>
     3155  </UL>
     3156 
     3157<h5 style='margin-left:0.5in'><a name=Constraints-SeqRef>Sequential
     3158Refinement Constraints</a></h5>
     3159
     3160<p class=MsoNormal style='margin-left:.75in'>
     3161In sequential refinements, each histogram is treated individually and
     3162it becomes possible to define constraints of form
     3163"<tt><i>p</i></tt>:*:<tt><i>name</i></tt>" and
     3164":*:<tt><i>name</i></tt>" (where "<tt><i>p</i></tt>" is a phase
     3165number and <tt><i>name</i></tt> is a parameter name). The "*" here is
     3166called a wildcard and in a constraint or equivalence of this form will be
     3167applied to all histograms.
     3168</p><p class=MsoNormal style='margin-left:.75in'>&nbsp;
     3169</p><p class=MsoNormal style='margin-left:.75in'>
     3170When sequential refinement is selected (via the <a href="#Controls">Controls
     3171tree item</a>), two additional controls are shown in the Constraints
     3172window. The first, which is labeled wildcard use, offers three modes, 
     3173"Set hist # to *", "Ignore unless hist=*", and "Use as
     3174supplied". These have the following meanings:
     3175<DL style='margin-left:1.0in'>
     3176  <DT><B>Set hist # to *</B></DT>
     3177  <DD>Any constraints previously specified with a specific histogram
     3178    number will be changed to apply to all histograms. This is the
     3179    default for new projects.     
     3180  </DD>
     3181  <DT><B>Ignore unless hist=*</B></DT>
     3182  <DD>Any constraints previously specified with a specific histogram
     3183    number will be ignored and constraints with a "*" where for a
     3184    histogram number will be used. Note that constraints on phase
     3185    parameters (of form "<tt><i>p</i></tt>::<tt><i>name</i></tt>" --
     3186    without a histogram number specified) will be used normally.
     3187    Note that this was the normal operating mode for GSAS-II in
     3188    earlier versions.
     3189  </DD>
     3190  <DT><B>Use as supplied</B></DT>
     3191  <DD>If different constraints are to be applied to different
     3192    histograms, it becomes necessary to create constraints with
     3193    specific histogram numbers. In this mode, constraints specified
     3194    with a specific histogram are applied only to that histogram while
     3195    wildcarded ones are applied to all histograms. Note that one
     3196    should not specify two constraints on a single parameter, one with
     3197    a wildcard and one with a specific histogram number as both will
     3198    be applied to the specified histogram which will result in an
     3199    unsatisfiable conflict.
     3200  </DD>
     3201  </DL>
     3202<p class=MsoNormal style='margin-left:.75in'>In sequential
     3203  refinements, constraints are processed for a single histogram at a
     3204  time. If debugging of constraints are needed, using the menu button
     3205  labeled "Selected histogram:" it is possible to select which
     3206  histogram from the sequential list will be used to look for
     3207  constraint errors or warnings.
     3208</p>
    31173209
    31183210<h4 style='margin-left:.25in'><a name=Restraints><span style='mso-fareast-font-family:
     
    41944286
    41954287<p class=MsoNormal style='tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt'><span
    4196 style='mso-fareast-font-family:"Times New Roman"'><!-- hhmts start -->Last
    4197 modified: Sun Jun 20 09:16:21 CDT 2021 <!-- hhmts end --><o:p></o:p></span></p>
     4288style='mso-fareast-font-family:"Times New Roman"'><!-- hhmts start -->Last modified: Fri Sep 24 09:57:36 CDT 2021 <!-- hhmts end --><o:p></o:p></span></p>
    41984289
    41994290</div>
  • trunk/imports/G2phase_CIF.py

    r4877 r5038  
    599599            for lbl in phasenamefields: # get a name for the phase
    600600                try:
    601                     name = blk.get(lbl)[0]
    602                 except TypeError:
    603601                    name = blk.get(lbl)
    604602                except:
  • trunk/versioninfo.txt

    r4922 r5038  
    2323  reflectometry data in GSAS-II.
    2424
     255038: With this version a major revision to the constraints module is
     26released. This provides better diagnostics, works better with
     27sequential fits and treats cases not
     28previously allowed, such as where both refined and unrefined
     29parameters appear in a constraint. %%%Please contact Brian if you find a
     30case where a set of constraints that used to work, but now fails.
Note: See TracChangeset for help on using the changeset viewer.