Changeset 3711

Ignore:
Timestamp:
Oct 29, 2018 4:10:08 PM (4 years ago)
Message:

major constraint update: move conflicting equivs to constraints; allow a formula for multiplier; update docs extensively. New routine EvaluateMultipliers? needed to evaluate formulae

Location:
trunk
Files:
8 edited

Unmodified
Removed
• trunk/GSASIIIO.py

 r3506 print('project load successful') G2frame.NewPlot = True except: except Exception as errmsg: if GSASIIpath.GetConfigValue('debug'): print('\nError reading GPX file:',errmsg) import traceback print (traceback.format_exc()) msg = wx.MessageDialog(G2frame,message="Error reading file "+ str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file", str(G2frame.GSASprojectfile)+". This is not a current GSAS-II .gpx file", caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP) msg.ShowModal() Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData( Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints msg = G2mv.EvaluateMultipliers(constDict,phaseDict) if msg: print('Unable to interpret multiplier(s): '+msg) raise Exception(' *** CIF creation aborted ***') try: groups,parmlist = G2mv.GroupConstraints(constDict) G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict) G2mv.GenerateConstraints(varyList,constDict,fixedList,self.parmDict) #print(G2mv.VarRemapShow(varyList)) except:
• trunk/GSASIIconstrGUI.py

 r3703 Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData( Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints msg = G2mv.EvaluateMultipliers(constDictList,phaseDict) if msg: return 'Unable to interpret multiplier(s): '+msg return G2mv.CheckConstraints('',constDictList,fixedList) 'Error with newly added constraint:\n'+errmsg+ '\nIgnoring newly added constraint',parent=G2frame) # Note no multiplier formulas in GUI, skipping EvaluateMultipliers # reset error status errmsg,warnmsg = CheckConstraints(allcons1) allcons = FindAllCons(data) if not len(allcons): return True # Note no multiplier formulas in GUI, skipping EvaluateMultipliers errmsg,warnmsg = CheckConstraints(allcons) if errmsg: vartype,varList,constrDictEnt = PageSelection(page) if vartype is None: return title1 = "Setup equivalent "+vartype+" variables" title1 = "Create equivalence constraint between "+vartype+" variables" title2 = "Select additional "+vartype+" variable(s) to be equivalent with " if not varList: vartype,varList,constrDictEnt = PageSelection(page) if vartype is None: return title1 = "Setup constraint on "+vartype+" variables" title1 = "Creating constraint on "+vartype+" variables" title2 = "Select additional "+vartype+" variable(s) to include in constraint with " if not varList:
• trunk/GSASIIdataGUI.py

 r3709 G2mv.InitVars() constrDict,fixedList,ignored = G2stIO.ProcessConstraints(constList) groups,parmlist = G2mv.GroupConstraints(constrDict) G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmValDict) G2mv.Map2Dict(parmValDict,varyList) msg = G2mv.EvaluateMultipliers(constrDict,parmValDict) if msg: print('Unable to interpret multiplier(s):',msg) else: G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmValDict) G2mv.Map2Dict(parmValDict,varyList) except G2mv.ConstraintException: pass ################################################################################ #####  Comments ################################################################################ def UpdateComments(G2frame,data): ################################################################################ def UpdateComments(G2frame,data): '''Place comments into the data window ''' lines = "" for line in data: if 'phoenix' in wx.version(): if 'phoenix' in wx.version() and hasattr(line,'decode'): lines += line.decode('latin-1').rstrip()+'\n' else: varyList = data[name]['varyList'] parmDict = data[name]['parmDict'] G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=ihst) msg = G2mv.EvaluateMultipliers(constrDict,parmDict) if msg: print('Unable to interpret multiplier(s) for',name,':',msg) continue G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict,SeqHist=ihst) if 'Dist' in expr: derivs = G2mth.CalcDistDeriv(obj.distance_dict,obj.distance_atoms, parmDict)
• trunk/GSASIImapvars.py

 r3649 Module to implements algebraic contraints, parameter redefinition and parameter simplification contraints. Parameter redefinition (new vars) is done by creating one or more relationships between a set of parameters and parameter simplification contraints. Types of constraints -------------------- There are four ways to specify constraints, as listed below. Note that parameters are initially stored in the main section of the GSAS-II data tree under heading Constraints. This dict has four keys, 'Hist', 'HAP', 'Global', and 'Phase', each containing a list of constraints. An additional set of constraints are generated for each phase based on symmetry considerations by calling :func:GSASIIstrIO.GetPhaseData. Note that in the constraints, as stored in the GSAS-II data tree, parameters are stored as :class:GSASIIobj.G2VarObj objects, as these objects allow for changes in numbering of phases, histograms and atoms. When they are interpreted (in :func:GSASIIstrIO.ProcessConstraints), references to numbered objects are resolved using the appropriate random ids and the variable object is expressed as a string of form ph:hst:VARNAM:at. Alternate parameters (New Var) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Parameter redefinition ("New Var" constraints) is done by creating an expression that relates several parameters: :: Mx2 * Px + Mz2 * Pz + ... where Pj is a parameter name and Mjk is a constant. Constant constraint Relations can also be supplied in the form of an equation: :: nx1 * Px + ny1 * Py +... = C1 where Cn is a constant. These equations define an algebraic constrant. Parameters can also be "fixed" (held), which prevents them from being refined. All of the above three cases are input using routines GroupConstraints and GenerateConstraints. The input consists of a list of relationship dictionaries: .. code-block:: python where Pj is a GSAS-II parameter name and Mjk is a constant (float) multiplier. Alternately, multipliers Mjk can contain a formula (str) that will be evaluated prior to the start of the refinement. In a formula, GSAS-II parameters will be replaced by the value of the parameter before the formula is evaluated, so 'np.cos(0::Ax:2)' is a valid multiplier. This type of constraint describes an alternate degree of freedom where parameter Px and Py, etc. are varied to keep their ratio fixed according the expression. A new variable is assigned to each degree of freedom when refined. An example where this can be valuable is when two parameters, P1 and P2, have similar values and are highly correlated. It is often better to create a new variable, Ps = P1 + P2, and refine Ps. In the later stages of refinement, a second variable, Pd = P1 - P2, can be defined and it can be seen if refining Pd is supported by the data. Another use will be to define parameters that express "irrep modes" in terms of the fundamental structural parameters. These "New Var" constraints are stored as described for type "f" in the :ref:constraint definitions table . Constrained parameters (Const) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A constraint on a set of variables can be supplied in the form of a linear algebraic equation: :: Nx1 * Px + Ny1 * Py +... = C1 where Cn is a constant (float), where Pj is a GSAS-II parameter name, and where Njk is a constant multiplier (float) or a formula (str) that will be evaluated prior to the start of the refinement. In a formula, GSAS-II parameters will be replaced by the value of the parameter before the formula is evaluated, so 'np.cos(0::Ax:2)' is a valid multiplier. These equations set an interdependence between variables. Common uses of parameter constraints are to set rules that decrease the number of parameters, such as restricting the sum of fractional occupancies for atoms that share a site to sum to unity, thus reducing the effective number of variables by one. Likewise, the Uiso value for a H atom "riding" on a C, N or O atom can be related by a fixed offset (the so called B+1 "rule"). A "Const" constraint is stored as described for type "c" in the :ref:constraint definitions table . Equivalenced parameters (Equiv) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ A simplifed way to set up a constraint equation is to define an equivalence, which can be of form: :: C1 * P1 = C2 * Py or:: C1 * P1 = C2 * P2 = C3 * P3 = ... where Cn is a constant (float), where Pj is a GSAS-II parameter name. This means that parameters Py (or P2 and P3) are determined from (or "slaved" to) parameter P1. Alternately, equivalences can be created with :func:StoreEquivalence where the multipliers can be a formula (str) that will be evaluated prior to the start of the refinement. In a formula, GSAS-II parameters will be replaced by the value of the parameter before the formula is evaluate, so 'np.cos(0::Ax:2)' is a valid multiplier. Note that the latter constraint expression is conceptually identical to defining constraint equations. In practice, however, equivalenced parameters are processed in a different and more direct manner than constraint equations. The previous set of equalities could also be written in this way as a set of constraint equations: :: C1 * P1 - C2 * P2 = 0 C1 * P1 - C3 * P3 = 0 ... The first parameter (P1 above) is considered the independent variable and the remaining parameters are dependent variables. The dependent variables are set from the independent variable. An example of how this may be used woul be if, for example, a material has a number of O atoms, all in fairly similar bonding environments and the diffraction data are sparse, one my reduce the complexity of the model by defining Uiso for the first O atoms to be identical to the remaining atoms. The results of this refinement will be simpler to understand than if a set of 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. A parameter can be used in multiple equivalences as independent variable, but if parameter is used as both a dependent and independent variable or a parameter is used in equivalences and in "New Var" or "Const" constraints, this create conflicts that cannot be resolved within the equivalences implementation but can be handled as constraint equations. The equivalences that violate this are discovered in :func:CheckEquivalences and then :func:MoveConfEquiv is used to change these equivalences to "Const" equations. Equivalenced parameters ("EQUIV" constraints), when defined by users, are stored as described for type "e" in the :ref:constraint definitions table . Other equvalences are generated by symmetry prior to display or refinement in :func:GSASIIstrIO.GetPhaseData. These are not stored. Fixed parameters (Hold) ^^^^^^^^^^^^^^^^^^^^^^^^ When variables are refined where a single refinement flag determines that several variables are refined at the same time (examples are: cell parameters, atom positions, anisotropic displacement parameters, magnetic moments,...) it can be useful to specify that a specific parameter should not be varied. These will most commonly be generated due to symmetry, but under specific conditions, there may be other good reasons to constrain a variable. A "Hold" constraint is stored as described for type "h" in the :ref:constraint definitions table . Constraint Processing --------------------- When constraints will be used or edited, they are processed using a series of calls: * First all of the stored constraints are appended into a single list. They are initially stored in separate lists only to improve their creation and display in the GUI. * Then :func:InitVars is used to initialize the global variables in this module (:mod:GSASIImapvars). * Then :func:GSASIIstrIO.ProcessConstraints is used to initially process the constraints, as described below. * Symmetry-generated equivalences are then created in :func:GSASIIstrIO.GetPhaseData, which also calls :func:GSASIIstrIO.cellVary and for Pawley refinements :func:GSASIIstrIO.GetPawleyConstr. These are entered directly into this module's globals using :func:StoreEquivalence. * Constraints/equivalences are then checked for possible conflicts with :func:CheckConstraints, this requires grouping the constraints, as described below. * In refinements, :func:GenerateConstraints is then called to create the constraints that will be used, see below for * For debugging constraints, :func:VarRemapShow can be called after :func:GenerateConstraints to display the generated constraints. Constraint Reorganization (:func:~GSASIIstrIO.ProcessConstraints) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :func:GSASIIstrIO.ProcessConstraints is used to initially process the constraints. This does these things: 1. The "Hold", "Const" and "New Var" expressions are split between two paired lists, :data:constDictList and :data:fixedList which are set: * For "Hold" entries a dict with a single entry is placed in constDictList where the key is the parameter name (associated value is 0.0) and fixedList gets a value of 0.0. * For "Const" entries, a dict with multiple entries is placed in constDictList where the key is the parameter name and the value is the multiplier for the parameter, while fixedList gets a string value corresponding to the constant value for the expression. * For "New Var" entries, a dict with multiple entries is placed in constDictList where the key is the parameter name and the value is the multiplier for the parameter; an additional key "_vary" is given the value of True or False depending on the refinement flag setting. The corresponding entry in fixedList is None The output from this will have this form where the first entry is a "Const", the second is a "New Var" and the final is a "Hold". .. code-block:: python constrDict = [ {'0:12:Scale': 2.0, '0:14:Scale': 4.0, '0:13:Scale': 3.0, '0:0:Scale': 0.5}, {'2::C(10,6,1)': 1.0, '1::C(10,6,1)': 1.0}, {'2::C(10,6,1)': 1.0, '1::C(10,6,1)': 1.0, '_vary':True}, {'0::A0': 0.0}] fixedList = ['5.0', None, '0'] Where the dictionary defines the first part of an expression and the corresponding fixedList item is either None (for parameter redefinition) or the constant value, for a constant constraint equation. A dictionary that contains a single term defines a variable that will be fixed (held). The multiplier and the fixedList value in this case are ignored. Parameters can also be equivalenced or "slaved" to another parameter, such that one (independent) parameter is equated to several (now dependent) parameters. In algebraic form this is: 2. Equivalences are stored using :func:StoreEquivalence into this module's globals (:data:~GSASIImapvars.arrayList, :data:~GSASIImapvars.invarrayList,    :data:~GSASIImapvars.indParmList, :data:~GSASIImapvars.dependentParmList and  :data:~GSASIImapvars.symGenList) Parameter Grouping (:func:GenerateConstraints) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Functions :func:CheckConstraints and :func:GenerateConstraints are used to process the parameter equivalences and constraint lists created in :func:~GSASIIstrIO.ProcessConstraints. The former is used to generate error messages and the latter to generate the internal information used to apply the constraints. Initially, in both a list of parameters that are fixed and those used in constraint relations are tabulated in :func:CheckEquivalences. The equivalence relations are the scanned for the following potential problems: 1. a parameter is used as a dependent variable in more than one equivalence relation 2. a parameter is fixed and used in in an equivalence relation either as a dependent or independent variable 3. a parameter is used as a dependent variable in one equivalence relation and as a independent variable in another 4. a parameter is used in in an equivalence relation (either as a dependent or independent variable) and is used in a constraint expression 5. a parameter is not defined in a particular refinement, but is used in an equivalence relation 6. a parameter uses a wildcard for the histogram number (sequential refinements) Cases 1 & 2 above cannot be corrected, and result in errors. Cases 3 & 4 are potentially corrected with :func:MoveConfEquiv, as described below. Case 5 causes the equivalence to be dropped. Case 6 causes the current histogram number to be substituted for the wildcard. For cases 3 & 4, :func:MoveConfEquiv is used to change these equivalences into "Const" equations. This can potentially mean that additional equivalences will be problematic, so if there are changes made by :func:MoveConfEquiv, :func:CheckEquivalences is repeated. If any problem cases are noted, the refinement cannot be performed. Constraint expressions ("Const" and "New Var") are sorted into groups so that each group contains the minimum number of entries that ensures each parameter is referenced in only one group in :func:GroupConstraints. This is done by scanning the list of dicts in :data:constDictList one by one and making a list of parameters used in that constraint expression. Any expression that contains a parameter in is in that list is added to the current group and those parameters are added to this list of parameters. The list of ungrouped expressions is then scanned again until no more expressions are added to the current group. This process is repeated until every expression has been placed in a group. Function :func:GroupConstraints returns two lists of lists. The first has, for each group, a list of the indices in :data:constDictList that comprise the group (there can be only one). The second list contains, for each group, the unique parameter names in that group. Each constraint group is then processed. First, wildcard parameters are renamed (in a sequential refinement). Any fixed parameters that are used in constraints are noted as errors. The number of refined parameters and the number of parameters that are not defined in the current refinement are also noted. It is fine if all parameters in a group are not defined or all are not varied, but if some are defined and others not or some are varied and others not, this constitutes an error. The contents of each group is then examined. Groups with a single parameter (holds) are ignored. Then for each group, the number of parameters in the group (Np) and the number of expressions in the group (Nc) are counted and for each expression. If Nc > Np, then the constraint is overdetermined, which also constitutes an error. The parameter multipliers for each expression are then assembled: :: P0 = M1 * P1 = M2 * P2 = ... Thus parameters P0, P1 and P2,... are linearly equivalent. Routine StoreEquivalence is used to specify these equivalences. Parameter redefinition (new vars) describes a new, independent, parameter, which is defined in terms of dependent parameters that are defined in the Model, while fixed constrained relations effectively reduce the complexity of the Model by removing a degree of freedom. It is possible for a parameter to appear in both a parameter redefinition expression and a fixed constraint equation, but a parameter cannot be used a parameter equivalance cannot be used elsewhere (not fixed, constrained or redefined). Likewise a fixed parameter cannot be used elsewhere (not equivalanced, constrained or redefined). Relationships are grouped so that a set of dependent parameters appear in only one group (done in routine GroupConstraints.) Note that if a group contains relationships/equations that involve N dependent parameters, there must exist N-C new parameters, where C is the number of contraint equations in the group. Routine GenerateConstraints takes the output from GroupConstraints and generates the "missing" relationships and saves that information in the module's global variables. Each generated parameter is named sequentially using paramPrefix. A list of parameters that will be varied is specified as input to GenerateConstraints (varyList). A fixed parameter will simply be removed from this list preventing that parameter from being varied. Note that all parameters in a constraint relationship must specified as varied (appear in varyList) or none can be varied. This is checked in GenerateConstraints. Likewise, if all parameters in a constraint are not referenced in a refinement, the constraint is ignored, but if some parameters in a constraint group are not referenced in a refinement, but others are this constitutes and error. * When a new variable is created, the variable is assigned the name associated in the constraint definition or it is assigned a default name of form ::constr (see paramPrefix). The vary setting for variables used in the constraint are ignored. Note that any generated "missing" relations are not varied. Only the input relations can be are varied. * If all parameters in a fixed constraint equation are varied, the generated "missing" relations in the group are all varied. This provides the N-C degrees of freedom. *External Routines* ------------------- M1a * P1 + M2a * P2 +... Mka * Pk M1b * P1 + M2b * P2 +... Mkb * Pk ... M1j * P1 + M2j * P2 +... Mkj * Pk From this it becomes possible to create a Nc x Np matrix, which is called the constraint matrix: .. math:: \\left( \\begin{matrix} M_{1a}  & M_{2a} &... & M_{ka} \\\\ M_{1b}  & M_{2b} &... & M_{kb} \\\\ ...  \\\\ M_{1j}  & M_{2j}  &... & M_{kj} \\end{matrix}\\right) When Nc_, implemented  in :func:GramSchmidtOrtho, is used to find orthonormal unit vectors for the remaining Np-Nc rows of the matrix. This will fail with a ConstraintException if this is not possible (singular matrix) or the result is placed in :data:constrArr as a numpy array. Rows in the matrix corresponding to "New Var" constraints and those that were generated by the Gram-Schmidt process are provided with variable names (this can be specified if a "New Var" entry by using a "_name" element in the constraint dict, but at present this is not implemented.) Names are generated using :data:paramPrefix which is set to "::constr", plus a number to make the new parameter name unique. Global dict :data:genVarLookup provides a lookup table, where the names of the parameters related to this new parameter can be looked up easily. Finally the parameters used as input to the constraint are placed in this module's globals :data:~GSASIImapvars.dependentParmList and the constraint matrix is placed in into  :data:~GSASIImapvars.arrayList. This can be used to compute the initial values for "New Var" parameters. The inverse of the constraint matrix is placed in :data:~GSASIImapvars.invarrayList and a list of the "New Var" parameters and a list of the fixed values (as str's) is placed in :data:~GSASIImapvars.indParmList. A lookup table for fixed values as floats is placed in :data:~GSASIImapvars.fixedDict. Finally the appropriate entry in :data:~GSASIImapvars.symGenList is set to False to indicate that this is not a symmetry generated constraint. *Externally-Accessible Routines* --------------------------------- To define a set of constrained and unconstrained relations, one values, a list of fixed values for each constraint and a list of parameters to be varied. In addition, one uses :func:StoreEquivalence to define parameters that are equivalent. One can then use :func:CheckConstraints to check that the input is :func:StoreEquivalence to define parameters that are equivalent. Use :func:EvaluateMultipliers to convert formula-based constraint/equivalence multipliers to numbers and then use :func:CheckConstraints to check that the input is internally consistent and finally :func:GroupConstraints and :func:GenerateConstraints to generate the internally used tables. Routines :func:Map2Dict is used to initialize the parameter dictionary and :func:Dict2Map, :func:Dict2Deriv, and tables. Routine :func:Map2Dict is used to initialize the parameter dictionary and routine :func:Dict2Map, :func:Dict2Deriv, and :func:ComputeDepESD are used to apply constraints. Routine :func:VarRemapShow is used to print out the constraint information, as stored by :func:GenerateConstraints. as stored by :func:GenerateConstraints. Further information on each routine is below: :func:InitVars StoreEquivalence('x',('y','z')) The latter will run more efficiently. Note that mixing independent and dependent variables would require assignments to be done in a particular order and thus is not is not allowed. This will cause an error: The latter will run more efficiently. Note that mixing independent and dependent variables would require assignments, such as .. code-block:: python StoreEquivalence('x',('y',)) StoreEquivalence('y',('z',)) would require that equivalences be applied in a particular order and thus is implemented as a constraint equation rather than an equivalence. Use StoreEquivalence before calling GenerateConstraints or CheckConstraints :func:CheckConstraints To check that input in internally consistent, use CheckConstraints check that input in internally consistent :func:GenerateConstraints generate the internally used tables from constraints and equivalences :func:EvaluateMultipliers Convert any string-specified multipliers to numbers. Call this before using :func:CheckConstraints or :func:GenerateConstraints. :func:Map2Dict :func:Dict2Map To take values from the new independent parameters and constraints, one calls Dict2Map. This will apply contraints. one calls Dict2Map and set the parameter array, thus appling contraints. :func:Dict2Deriv Use Dict2Deriv to determine derivatives on independent parameters from those on dependent ones from those on dependent ones. :func:ComputeDepESD Use ComputeDepESD to compute uncertainties on dependent variables Use ComputeDepESD to compute uncertainties on dependent variables. :func:VarRemapShow To show a summary of the parameter remapping, one calls VarRemapShow To show a summary of the parameter remapping, one calls VarRemapShow. *Global Variables* dependentParmList: contains a list by group of lists of a list containing group of lists of parameters used in the group. Note that parameters listed in dependentParmList should not be refined as they will not affect indParmList: a list of groups of Independent parameters defined in a list containing groups of Independent parameters defined in each group. This contains both parameters used in parameter redefinitions as well as names of generated new parameters. arrayList: a list containing group of relationship matrices to relate parameters in dependentParmList to those in indParmList. Unlikely to be used externally. invarrayList: a list containing group of relationship matrices to relate parameters in indParmList to those in dependentParmList. Unlikely to be used externally. fixedVarList: Unlikely to be used externally. arrayList: a list by group of relationship matrices to relate parameters in dependentParmList to those in indParmList. Unlikely to be used externally. invarrayList: a list by group of relationship matrices to relate parameters in indParmList to those in dependentParmList. Unlikely to be used externally. fixedDict: a dictionary containing the fixed values corresponding a list containing variables that show up in constraints producing errors *Routines* ---------- Note that parameter names in GSAS-II are strings of form :: *Routines/variables* --------------------- Note that parameter names in GSAS-II are strings of form :: or :::. """ # data used for constraints; debug = False # turns on printing as constraint input is processed # note that constraints are stored listed by contraint groups, where each constraint # note that constraints are stored listed by contraint groups, # where each constraint # group contains those parameters that must be handled together dependentParmList = [] # contains a list of parameters in each group # note that parameters listed in dependentParmList should not be refined arrayList = [] # a list of of relationship matrices invarrayList = [] # a list of inverse relationship matrices dependentParmList = [] '''a list of lists where each item contains a list of parameters in each constraint group. note that parameters listed in dependentParmList should not be refined directly.''' indParmList = [] # a list of names for the new parameters fixedDict = {} # a dictionary containing the fixed values corresponding to defined parameter equations # key is original ascii string, value is float fixedVarList = [] # List of variables that should not be refined symGenList = [] # Flag if constraint is generated by symmetry problemVars = [] # variables causing errors '''a list of lists where each item contains a list for each constraint group with fixed values for constraint equations and names of generated (New Var) parameters. ''' arrayList = [] '''a list of of relationship matrices that map model parameters in each constraint group (in :data:dependentParmList) to generated (New Var) parameters. ''' invarrayList = [] '''a list of of inverse-relationship matrices that map constrained values and generated (New Var) parameters (in :data:indParmList) to model parameters (in :data:dependentParmList). ''' fixedDict = {} '''A dict lookup-table containing the fixed values corresponding to defined parameter equations. Note the key is the original ascii string and the value in the dict is a float. ''' fixedVarList = [] '''List of variables that should not be refined. ''' symGenList = [] '''A list of flags that if True indicates a constraint was generated by symmetry ''' problemVars = [] '''a list of variables causing errors ''' dependentVars = [] 'A list of dependent variables, taken from (:data:dependentParmList).' independentVars = [] # prefix for parameter names 'A list of dependent variables, taken from (:data:indParmList).' genVarLookup = {} 'provides a list of variables that are related to each generated variable' paramPrefix = "::constr" consNum = 0 # number of the next constraint to be created 'A prefix for generated parameter names' consNum = 0 'The number to be assigned to the next constraint to be created' class ConstraintException(Exception): symGenList = [] # Flag if constraint is generated by symmetry consNum = 0 # number of the next constraint to be created global genVarLookup genVarLookup = {} def VarKeys(constr): import re global dependentParmList,arrayList,invarrayList,indParmList,consNum errmsg = '' warnmsg = '' global problemVars problemVars = [] fixVlist = [] # process fixed variables (holds) for cdict in constrDict: # N.B. No "_" names in holds if len(cdict) == 1: fixVlist.append(list(cdict.keys())[0]) # process equivalences: make a list of dependent and independent vars #    and check for repeated uses (repetition of a parameter as an #    independent var is OK) indepVarList = [] depVarList = [] multdepVarList = [] for varlist,mapvars,multarr,invmultarr in zip( dependentParmList,indParmList,arrayList,invarrayList): if multarr is None: # an equivalence zeromult = False for mv in mapvars: varied = 0 notvaried = '' if mv in varyList: varied += 1 else: if notvaried: notvaried += ', ' notvaried += mv if mv not in indepVarList: indepVarList.append(mv) for v,m in zip(varlist,invmultarr): if v in indepVarList: errmsg += '\nVariable '+v+' is used to set values in a constraint before its value is set in another constraint\n' if v not in problemVars: problemVars.append(v) if m == 0: zeromult = True if v in varyList: varied += 1 else: if notvaried: notvaried += ', ' notvaried += v if v in depVarList: multdepVarList.append(v) else: depVarList.append(v) if varied > 0 and varied != len(varlist)+1: warnmsg += "\nNot all variables refined in equivalence:\n\t" s = "" for v in varlist: if s != "": s+= " & " s += str(v) warnmsg += str(mv) + " => " + s warnmsg += '\nNot refined: ' + notvaried + '\n' if zeromult: errmsg += "\nZero multiplier is invalid in equivalence:\n\t" s = "" for v in varlist: if s != "": s+= " & " s += str(v) errmsg += str(mv) + " => " + s + '\n' # check for errors: if len(multdepVarList) > 0: errmsg += "\nThe following parameters(s) are used in conflicting Equivalence relations as dependent variables:\n" s = '' for var in sorted(set(multdepVarList)): if v not in problemVars: problemVars.append(v) if s != "": s+= ", " s += str(var) errmsg += '\t'+ s + '\n' equivVarList = list(set(indepVarList).union(set(depVarList))) if debug: print ('equivVarList',equivVarList) inboth = set(fixVlist).intersection(set(equivVarList)) if len(inboth) > 0: errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n" s = '' for var in sorted(inboth): if var not in problemVars: problemVars.append(var) if s != "": s+= ", " s += str(var) errmsg += '\t'+ s + '\n' # Process the equivalences #    If there are conflicting parameters, move them into constraints. This #    may create new conflicts, requiring movement of other parameters, so #    loop until there are no more changes to make. parmsChanged = True while parmsChanged: parmsChanged = 0 errmsg,warnmsg,fixVlist,dropVarList,translateTable = CheckEquivalences( constrDict,varyList) #print('debug: using MoveConfEquiv to address =',errmsg) if problemVars: parmsChanged = MoveConfEquiv(constrDict,fixedList) #    GSASIIpath.IPyBreak() groups,parmlist = GroupConstraints(constrDict) return errmsg,warnmsg def GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict=None,SeqHist=None): def GenerateConstraints(varyList,constrDict,fixedList,parmDict=None,SeqHist=None): '''Takes a list of relationship entries comprising a group of constraints and builds the relationship lists and their inverse and stores them in global variables Also checks for internal conflicts or inconsistencies in parameter/variable definitions. :param list groups: a list of grouped contraints where each constraint grouped containts a list of indices for constraint constrDict entries, created in :func:GroupConstraints (returned as 1st value) :param list parmlist: a list containing lists of parameter names contained in each group, created in :func:GroupConstraints (returned as 2nd value) :param list varyList: a list of parameters names (strings of form ''' global dependentParmList,arrayList,invarrayList,indParmList,consNum global genVarLookup msg = '' shortmsg = '' # process fixed (held) variables for cdict in constrDict: if len(cdict) == 1: fixedVarList.append(list(cdict.keys())[0]) # process equivalences: make a list of dependent and independent vars #    and check for repeated uses (repetition of a parameter as an #    independent var is OK [A=B; A=C], but chaining: [A=B; B=C] is not good) dropVarList = [] translateTable = {} # lookup table for wildcard referenced variables for varlist,mapvars,multarr,invmultarr in zip(       # process equivalences dependentParmList,indParmList,arrayList,invarrayList): if multarr is None: # true only if an equivalence zeromult = False for i,mv in enumerate(mapvars): if mv.split(':')[1] == '*' and SeqHist is not None: # convert wildcard var to reference current histogram; save translation in table sv = mv.split(':') sv[1] = str(SeqHist) mv = translateTable[mv] = ':'.join(sv) mapvars[i] = mv #s = '' varied = 0 notvaried = '' if mv in varyList: varied += 1 else: if notvaried: notvaried += ', ' notvaried += mv if parmDict is not None and mv not in parmDict: print ("Dropping equivalence for variable "+str(mv)+". Not defined in this refinement") if mv not in dropVarList: dropVarList.append(mv) #msg += "\nCannot equivalence to variable "+str(mv)+". Not defined in this refinement" #continue for i,(v,m) in enumerate(zip(varlist,invmultarr)): if v.split(':')[1] == '*' and SeqHist is not None: # convert wildcard var to reference current histogram; save translation in table sv = v.split(':') sv[1] = str(SeqHist) varlist[i] = v = translateTable[v] = ':'.join(sv) if parmDict is not None and v not in parmDict: print ("Dropping equivalence for dep. variable "+str(v)+". Not defined in this refinement") if v not in dropVarList: dropVarList.append(v) continue if m == 0: zeromult = True if v in varyList: varied += 1 else: if notvaried: notvaried += ', ' notvaried += v if varied > 0 and varied != len(varlist)+1: msg += "\nNot all variables refined in equivalence:\n\t" s = "" for v in varlist: if s != "": s+= " & " s += str(v) msg += str(mv) + " => " + s msg += '\nNot refined: ' + notvaried + '\n' if zeromult: msg += "\nZero multiplier is invalid in equivalence:\n\t" s = "" for v in varlist: if s != "": s+= " & " s += str(v) msg += str(mv) + " => " + s + '\n' changed = False # Process the equivalences #    If there are conflicting parameters, move them into constraints. This #    may create new conflicts, requiring movement of other parameters, so #    loop until there are no more changes to make. parmsChanged = True while parmsChanged: parmsChanged = 0 errmsg,warnmsg,fixVlist,dropVarList,translateTable = CheckEquivalences( constrDict,varyList,parmDict,SeqHist) if problemVars: parmsChanged = MoveConfEquiv(constrDict,fixedList) changed = True if errmsg: msg = errmsg if warnmsg: if msg: msg += '\n' msg += warnmsg # scan through parameters in each relationship. Are all varied? If only some are # varied, create an error message. groups,parmlist = GroupConstraints(constrDict) for group,varlist in zip(groups,parmlist): if len(varlist) == 1: continue consNum += 1 mapvar.append(varname) genVarLookup[varname] = varlist # save list of variables related to this new var # vary the new relationship if it is a degree of freedom in # a set of contraint equations or if a New Var is flagged to be varied. fixedDict[fixedval] = float(fixedval) _setVarLists(dropVarList) if changed: print(60*'=') print('Constraints were reclassified to avoid conflicts, as below:') print (VarRemapShow(varyList,True)) print(60*'=') def _setVarLists(dropVarList): if debug: # on debug, show what is parsed & generated, semi-readable print (50*'-') print (VarRemapShow(varyList)) print ('Varied: ',varyList) #print (VarRemapShow(varyList)) #print ('Varied: ',varyList) print ('Not Varied: ',fixedVarList) # def CheckEquivalences(constrDict,varyList): #     global dependentParmList,arrayList,invarrayList,indParmList,consNum #     global problemVars #     warnmsg = '' #     errmsg = '' #     problemVars = [] #     # process fixed variables (holds) #     fixVlist = [] # list of Fixed vars #     constrVars = [] # list of vars in constraint expressions #     for cdict in constrDict: #         # N.B. No "_" names in holds #         if len(cdict) == 1: #             fixVlist.append(list(cdict.keys())[0]) #         else: #             constrVars += cdict.keys() # this will include _vary (not a problem) #     # process equivalences: make a list of dependent and independent vars #     #    and check for repeated uses (repetition of a parameter as an #     #    independent var is OK) #     indepVarList = [] #     depVarList = [] #     multdepVarList = [] #     for varlist,mapvars,multarr,invmultarr in zip( #         dependentParmList,indParmList,arrayList,invarrayList): #         if multarr is None: # an equivalence #             zeromult = False #             for mv in mapvars: #                 varied = 0 #                 notvaried = '' #                 if mv in varyList: #                     varied += 1 #                 else: #                     if notvaried: notvaried += ', ' #                     notvaried += mv #                 if mv not in indepVarList: indepVarList.append(mv) #                 for v,m in zip(varlist,invmultarr): #                     if v in indepVarList: #                         errmsg += '\nVariable '+v+' is used to set values in a constraint before its value is set in another constraint\n' #                         if v not in problemVars: problemVars.append(v) #                     if m == 0: zeromult = True #                     if v in varyList: #                         varied += 1 #                     else: #                         if notvaried: notvaried += ', ' #                         notvaried += v #                     if v in depVarList: #                         multdepVarList.append(v) #                     else: #                         depVarList.append(v) #             if varied > 0 and varied != len(varlist)+1: #                 warnmsg += "\nNot all variables refined in equivalence:\n\t" #                 s = "" #                 for v in varlist: #                     if s != "": s+= " & " #                     s += str(v) #                 warnmsg += str(mv) + " => " + s #                 warnmsg += '\nNot refined: ' + notvaried + '\n' #             if zeromult: #                 errmsg += "\nZero multiplier is invalid in equivalence:\n\t" #                 s = "" #                 for v in varlist: #                     if s != "": s+= " & " #                     s += str(v) #                 errmsg += str(mv) + " => " + s + '\n' #     # check for errors: #     if len(multdepVarList) > 0: #         errmsg += "\nThe following parameters(s) are used in conflicting Equivalence relations as dependent variables:\n" #         s = '' #         for var in sorted(set(multdepVarList)): #             if v not in problemVars: problemVars.append(v) #             if s != "": s+= ", " #             s += str(var) #         errmsg += '\t'+ s + '\n' #     equivVarList = list(set(indepVarList).union(set(depVarList))) #     if debug: print ('equivVarList',equivVarList) #     # check for parameters that are both fixed and in an equivalence (not likely) #     inboth = set(fixVlist).intersection(set(equivVarList)) #     if len(inboth) > 0: #         errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n" #         s = '' #         for var in sorted(inboth): #             if var not in problemVars: problemVars.append(var) #             if s != "": s+= ", " #             s += str(var) #         errmsg += '\t'+ s + '\n' #     # check for parameters that in both an equivalence and a constraint expression #     inboth = set(constrVars).intersection(set(equivVarList)) #     if len(inboth) > 0: #         errmsg += "\nThe following parameter(s) are used in both Equivalence and Equiv or new var constraints:\n" #         s = '' #         for var in sorted(inboth): #             if var not in problemVars: problemVars.append(var) #             if s != "": s+= ", " #             s += str(var) #         errmsg += '\t'+ s + '\n' #     return errmsg,warnmsg,fixVlist def CheckEquivalences(constrDict,varyList,parmDict=None,SeqHist=None): '''Process equivalence constraints, looking for conflicts such as where a parameter is used in both an equivalence and a constraint expression or where chaining is done (A->B and B->C). When called during refinements, parmDict is defined, and for sequential refinement SeqHist ia also defined. * parmDict is used to remove equivalences where a parameter is not present in a refinement * SeqHist is used to rename wild-card parameter names in sequential refinements to use the current histogram. ''' global dependentParmList,arrayList,invarrayList,indParmList,consNum global problemVars warnmsg = '' errmsg = '' problemVars = [] # process fixed variables (holds) fixVlist = [] # list of Fixed vars constrVars = [] # list of vars in constraint expressions for cdict in constrDict: # N.B. No "_" names in holds if len(cdict) == 1: fixVlist.append(list(cdict.keys())[0]) else: constrVars += cdict.keys() # this will include _vary (not a problem) # process equivalences: make a list of dependent and independent vars #    and check for repeated uses (repetition of a parameter as an #    independent var is OK) indepVarList = [] depVarList = [] multdepVarList = [] dropVarList = [] translateTable = {} # lookup table for wildcard referenced variables for varlist,mapvars,multarr,invmultarr in zip( dependentParmList,indParmList,arrayList,invarrayList): if multarr is None: # an equivalence zeromult = False for i,mv in enumerate(mapvars): if mv.split(':')[1] == '*' and SeqHist is not None: # convert wildcard var to reference current histogram; save translation in table sv = mv.split(':') sv[1] = str(SeqHist) mv = translateTable[mv] = ':'.join(sv) mapvars[i] = mv varied = 0 notvaried = '' if mv in varyList: varied += 1 else: if notvaried: notvaried += ', ' notvaried += mv if parmDict is not None and mv not in parmDict: print ("Dropping equivalence for variable "+str(mv)+". Not defined in this refinement") if mv not in dropVarList: dropVarList.append(mv) if mv not in indepVarList: indepVarList.append(mv) for i,(v,m) in enumerate(zip(varlist,invmultarr)): if v.split(':')[1] == '*' and SeqHist is not None: # convert wildcard var to reference current histogram; save translation in table sv = v.split(':') sv[1] = str(SeqHist) varlist[i] = v = translateTable[v] = ':'.join(sv) if parmDict is not None and v not in parmDict: print ("Dropping equivalence for dep. variable "+str(v)+". Not defined in this refinement") if v not in dropVarList: dropVarList.append(v) continue if m == 0: zeromult = True if v in varyList: varied += 1 else: if notvaried: notvaried += ', ' notvaried += v if v in indepVarList: errmsg += '\nVariable '+v+' is used to set values in a constraint before its value is set in another constraint\n' if v not in problemVars: problemVars.append(v) if v in depVarList: multdepVarList.append(v) else: depVarList.append(v) if varied > 0 and varied != len(varlist)+1: warnmsg += "\nNot all variables refined in equivalence:\n\t" s = "" for v in varlist: if s != "": s+= " & " s += str(v) warnmsg += str(mv) + " => " + s warnmsg += '\nNot refined: ' + notvaried + '\n' if zeromult: errmsg += "\nZero multiplier is invalid in equivalence:\n\t" s = "" for v in varlist: if s != "": s+= " & " s += str(v) errmsg += str(mv) + " => " + s + '\n' # check for errors: if len(multdepVarList) > 0: errmsg += "\nThe following parameters(s) are used in conflicting Equivalence relations as dependent variables:\n" s = '' for var in sorted(set(multdepVarList)): if v not in problemVars: problemVars.append(v) if s != "": s+= ", " s += str(var) errmsg += '\t'+ s + '\n' equivVarList = list(set(indepVarList).union(set(depVarList))) if debug: print ('equivVarList',equivVarList) # check for parameters that are both fixed and in an equivalence (not likely) inboth = set(fixVlist).intersection(set(equivVarList)) if len(inboth) > 0: errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n" s = '' for var in sorted(inboth): if var not in problemVars: problemVars.append(var) if s != "": s+= ", " s += str(var) errmsg += '\t'+ s + '\n' # check for parameters that in both an equivalence and a constraint expression inboth = set(constrVars).intersection(set(equivVarList)) if len(inboth) > 0: errmsg += "\nThe following parameter(s) are used in both Equivalence and Equiv or new var constraints:\n" s = '' for var in sorted(inboth): if var not in problemVars: problemVars.append(var) if s != "": s+= ", " s += str(var) errmsg += '\t'+ s + '\n' return errmsg,warnmsg,fixVlist,dropVarList,translateTable def MoveConfEquiv(constrDict,fixedList): '''Address conflicts in Equivalence constraints by creating an constraint equation that has the same action as the equivalence and removing the Equivalence ''' global dependentParmList,arrayList,invarrayList,indParmList,consNum global problemVars parmsChanged = 0 for i,(varlist,mapvars) in enumerate(zip(dependentParmList,indParmList)): conf = False for mv in mapvars: if mv in problemVars: conf = True break for v in varlist: if v in problemVars: conf = True break if conf: parmsChanged += 1 indvar = indParmList[i][0] for dep,mult in zip(dependentParmList[i],invarrayList[i]): #print('debug replacing equiv with constraint equation 0=',{indvar:-1.,dep:mult[0]}) constrDict += [{indvar:-1.,dep:mult[0]}] fixedList += ['0.0'] dependentParmList[i] = None if parmsChanged: for i in range(len(dependentParmList)-1,-1,-1): if dependentParmList[i] is None: del dependentParmList[i],indParmList[i],arrayList[i],invarrayList[i] return parmsChanged def StoreEquivalence(independentVar,dependentList,symGen=True): mapList = [] multlist = [] allfloat = True for var in dependentList: if isinstance(var, str): raise Exception("Cannot parse "+repr(var) + " as var or (var,multiplier)") mapList.append(var) multlist.append(tuple((mult,))) try: multlist.append(tuple((float(mult),))) except: allfloat = False multlist.append(tuple((mult,))) # added relationships to stored values arrayList.append(None) invarrayList.append(np.array(multlist)) if allfloat: invarrayList.append(np.array(multlist)) else: invarrayList.append(multlist) indParmList.append(list((independentVar,))) dependentParmList.append(mapList) symGenList.append(symGen) return def EvaluateMultipliers(constList,*dicts): '''Convert multipliers for constraints and equivalences that are specified as strings into values. The strings can specify values in the parameter dicts as well as normal Python functions, such as "2*np.cos(0::Ax:2/2.)" :param list constList: a list of dicts containing constraint expressions :param \*dict1: one or more dicts containing GSAS-II parameters and their values can be specified :returns: an empty string if there were no errors, or an error message listing the strings that could not be converted. ''' def SubfromParmDict(s,prmDict): for key in prmDict: if key in s: s = s.replace(key,str(prmDict[key])) return eval(s) prmDict = {} for d in dicts: prmDict.update(d) # combine all passed parameter dicts problemList = "" # loop through multipliers in contraint expressions for const in constList: for key in const: try: 1+const[key] continue except: pass try: newval = SubfromParmDict(const[key][:],prmDict) if GSASIIpath.GetConfigValue('debug'): print('Changing ',const[key],'to',newval) const[key] = newval except: if problemList: problemList += ", " problemList += const[key] # loop through multipliers in equivalences global arrayList,invarrayList for i,(a,valList) in enumerate(zip(arrayList,invarrayList)): if a is not None: continue # ignore if not equiv try: valList.shape continue # ignore if already a numpy array except: pass repList = [] for v in valList: try: 1+v[0] repList.append(tuple((v[0],))) continue except: pass try: newval = SubfromParmDict(v[0][:],prmDict) if GSASIIpath.GetConfigValue('debug'): print('Changing ',v[0],'to',newval) repList.append(tuple((newval,))) except: if problemList: problemList += ", " problemList += v[0] repList.append(tuple(('error',))) invarrayList[i] = np.array(repList) return problemList def GetDependentVars(): for v in fixedVarList: s += '    ' + v + '\n' s += 'User-supplied variable mapping relations:\n' if not inputOnly: s += 'User-supplied variable mapping relations:\n' symout = '' global dependentParmList,arrayList,invarrayList,indParmList,fixedDict,symGenList
• trunk/GSASIIobj.py

 r3674 generated constraints) * is True or False for New variable (constype=f`) constraints or is None. This will be implemented in the future to indicate if these variables should be refined. or is None. This indicates if this variable should be refined. * is one of four letters, 'e', 'c', 'h', 'f' that determines the type of constraint:
• trunk/GSASIIstrIO.py

 r3577 '''Load constraints and related info and return any error or warning messages This is done from the GPX file rather than the tree. This this is called before a refinement is launched (OnRefine and OnSeqRefine), where the tree could be used. This is called before a refinement is launched (OnRefine and OnSeqRefine), where the tree could be used. :param dict seqHist: defines a specific histogram to be loaded for a sequential histVary,histDict,controlDict = GetHistogramData(Histograms,Print=False) varyList = rbVary+phaseVary+hapVary+histVary msg = G2mv.EvaluateMultipliers(constDict,phaseDict,hapDict,histDict) if msg: return 'Unable to interpret multiplier(s): '+msg,'' errmsg, warnmsg = G2mv.CheckConstraints(varyList,constrDict,fixedList) if errmsg: hapDict[pfx+'newLeBail'] = hapData.get('newLeBail',True) if Phases[phase]['General']['Type'] == 'magnetic': dmin = max(dmin,Phases[phase]['General']['MagDmin']) dmin = max(dmin,Phases[phase]['General'].get('MagDmin',0.)) for item in ['Scale','Extinction']: hapDict[pfx+item] = hapData[item][0]
• trunk/GSASIIstrMain.py

 r3579 # do constraint processing varyListStart = tuple(varyList) # save the original varyList before dependent vars are removed msg = G2mv.EvaluateMultipliers(constrDict,parmDict) if msg: return False,'Unable to interpret multiplier(s): '+msg try: groups,parmlist = G2mv.GroupConstraints(constrDict) G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict) G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict) #print G2mv.VarRemapShow(varyList) #print 'DependentVars',G2mv.GetDependentVars() constrDict,fixedList = G2stIO.GetConstraints(GPXfile) varyListStart = tuple(varyList) # save the original varyList before dependent vars are removed msg = G2mv.EvaluateMultipliers(constDict,parmDict) if msg: return False,'Unable to interpret multiplier(s): '+msg try: groups,parmlist = G2mv.GroupConstraints(constrDict) G2mv.GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict,SeqHist=hId) G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict,SeqHist=hId) #            if GSASIIpath.GetConfigValue('debug'): print("DBG_"+ #                G2mv.VarRemapShow(varyList,True))
• trunk/testDeriv.py

 r3415 self.delt = [max(abs(self.parmDict[name])*0.0001,1e-6) for name in self.varylist+self.depVarList] file.close() groups,parmlist = G2mv.GroupConstraints(self.constrDict) G2mv.GenerateConstraints(groups,parmlist,self.varylist,self.constrDict,self.fixedList,self.parmDict) msg = G2mv.EvaluateMultipliers(self.constrDict,self.parmDict) if msg: print('Unable to interpret multiplier(s): '+msg) raise Exception G2mv.GenerateConstraints(self.varylist,self.constrDict,self.fixedList,self.parmDict) print(G2mv.VarRemapShow(self.varylist)) print('Dependent Vary List:',self.depVarList)
Note: See TracChangeset for help on using the changeset viewer.