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

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/GSASIImapvars.py

    r3649 r3711  
    1212
    1313Module to implements algebraic contraints, parameter redefinition
    14 and parameter simplification contraints.
    15 
    16 Parameter redefinition (new vars) is done by creating one or more relationships
    17 between a set of parameters
     14and parameter simplification contraints.
     15
     16Types of constraints
     17--------------------
     18
     19There are four ways to specify constraints, as listed below.
     20Note that parameters are initially stored in the
     21main section of the GSAS-II data tree under heading ``Constraints``.
     22This dict has four keys, 'Hist', 'HAP', 'Global', and 'Phase',
     23each containing a list of constraints. An additional set of constraints
     24are generated for each phase based on symmetry considerations by calling
     25:func:`GSASIIstrIO.GetPhaseData`.
     26
     27Note that in the constraints, as stored in the GSAS-II data tree, parameters
     28are stored as :class:`GSASIIobj.G2VarObj` objects, as these objects allow for
     29changes in numbering of phases, histograms and atoms.
     30When they are interpreted (in :func:`GSASIIstrIO.ProcessConstraints`),
     31references
     32to numbered objects are resolved using the appropriate random ids and the
     33variable object is expressed as a string of form ``ph:hst:VARNAM:at``.
     34
     35Alternate parameters (New Var)
     36^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     37
     38Parameter redefinition ("New Var" constraints)
     39is done by creating an expression that relates several
     40parameters:
    1841
    1942::
     
    2245   Mx2 * Px + Mz2 * Pz + ...
    2346
    24 where Pj is a parameter name and Mjk is a constant.
    25 
    26 Constant constraint Relations can also be supplied in the form of an equation:
    27 
    28 ::
    29 
    30   nx1 * Px + ny1 * Py +... = C1
    31 
    32 where Cn is a constant. These equations define an algebraic
    33 constrant.
    34 
    35 Parameters can also be "fixed" (held), which prevents them from being refined.
    36 
    37 All of the above three cases are input using routines
    38 GroupConstraints and GenerateConstraints. The input consists of a list of
    39 relationship dictionaries:
    40 
    41 .. code-block:: python
     47where Pj is a GSAS-II parameter name and Mjk is a constant (float) multiplier.
     48Alternately, multipliers Mjk can contain a formula (str) that will be evaluated prior
     49to the start of the refinement. In a formula, GSAS-II parameters will be replaced by the
     50value of the parameter before the formula is evaluated, so ``'np.cos(0::Ax:2)'`` is a valid
     51multiplier.
     52
     53This type of constraint describes an alternate
     54degree of freedom where parameter Px and Py, etc. are varied to keep
     55their ratio
     56fixed according the expression. A new variable is assigned to each degree of
     57freedom when refined. An example where this can be valuable is when
     58two 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.
     59In the later stages of refinement, a second
     60variable, Pd = P1 - P2, can be defined and it can be seen if refining Pd is
     61supported by the data. Another use will be to define parameters that
     62express "irrep modes" in terms of the fundamental structural parameters.
     63
     64These "New Var" constraints are stored as described for type "f" in the
     65:ref:`constraint definitions table <Constraint_definitions_table>`.
     66
     67Constrained parameters (Const)
     68^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     69
     70A constraint on a set of variables can be supplied in the form of a
     71linear algebraic equation: ::
     72
     73  Nx1 * Px + Ny1 * Py +... = C1
     74
     75where Cn is a constant (float), where Pj is a GSAS-II parameter name,
     76and where Njk is a constant multiplier (float) or a formula (str) that will be evaluated prior
     77to the start of the refinement. In a formula, GSAS-II parameters will be replaced by the
     78value of the parameter before the formula is evaluated, so ``'np.cos(0::Ax:2)'`` is a valid
     79multiplier.
     80
     81These equations set an interdependence between variables.
     82Common uses of parameter constraints are to set rules that decrease the number of parameters,
     83such as restricting the sum of fractional occupancies for atoms that share
     84a site to sum to unity, thus reducing the effective number of variables by one.
     85Likewise, the Uiso value for a H atom "riding" on a C, N or O atom
     86can be related by a fixed offset (the so called B+1 "rule").
     87
     88A "Const" constraint is stored as described for type "c" in the
     89:ref:`constraint definitions table <Constraint_definitions_table>`.
     90
     91Equivalenced parameters (Equiv)
     92^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     93
     94A simplifed way to set up a constraint equation is to define an equivalence,
     95which can be of form: ::
     96
     97  C1 * P1 = C2 * Py
     98
     99or::
     100
     101  C1 * P1 = C2 * P2 = C3 * P3 = ...
     102
     103where Cn is a constant (float), where Pj is a GSAS-II parameter name. This
     104means that parameters Py (or P2 and P3) are determined from (or "slaved" to)
     105parameter P1. Alternately, equivalences can be created with :func:`StoreEquivalence`
     106where the multipliers can be a formula (str) that will be evaluated prior to the start of
     107the refinement. In a formula, GSAS-II parameters will be replaced by the value of the
     108parameter before the formula is evaluate, so ``'np.cos(0::Ax:2)'`` is a valid multiplier.
     109
     110Note that
     111the latter constraint expression is conceptually identical to
     112defining constraint equations. In practice, however,
     113equivalenced parameters are processed in a
     114different and more direct manner than constraint equations. The previous
     115set of equalities could also be written in this way as a set of constraint
     116equations: ::
     117
     118  C1 * P1 - C2 * P2 = 0
     119  C1 * P1 - C3 * P3 = 0
     120  ...
     121
     122
     123The first parameter (P1 above)
     124is considered the independent variable
     125and the remaining parameters are dependent variables. The dependent variables
     126are set from the independent variable.
     127An example of how this may be used woul be if, for example,
     128a material has a number of O atoms, all in fairly similar bonding environments
     129and the diffraction data are sparse, one my reduce the complexity of the model
     130by defining Uiso for the first O atoms to be identical to the remaining atoms.
     131The results of this refinement will be simpler to understand than if a set of
     132constraint 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.
     133
     134A parameter can be used in multiple
     135equivalences as independent variable,
     136but if parameter is used as both a dependent and independent variable or
     137a parameter is used in equivalences and in "New Var" or "Const" constraints,
     138this create conflicts that cannot be resolved within the equivalences implementation
     139but can be handled as constraint equations.
     140The equivalences that violate this are discovered in :func:`CheckEquivalences`
     141and then :func:`MoveConfEquiv` is used to change these equivalences to "Const" equations.
     142
     143Equivalenced parameters ("EQUIV" constraints), when defined by users,
     144are stored as described for type "e" in the
     145:ref:`constraint definitions table <Constraint_definitions_table>`.
     146Other equvalences are generated by symmetry prior
     147to display or refinement in :func:`GSASIIstrIO.GetPhaseData`.
     148These are not stored.
     149
     150Fixed parameters (Hold)
     151^^^^^^^^^^^^^^^^^^^^^^^^
     152
     153When variables are refined where a single refinement flag determines that several variables
     154are refined at the same time (examples are: cell parameters, atom positions, anisotropic
     155displacement parameters, magnetic moments,...) it can be useful to specify that a
     156specific parameter should not be varied. These will most commonly be generated due to symmetry,
     157but under specific conditions, there may be other good reasons to constrain a variable.
     158
     159A "Hold" constraint is stored as described for type "h" in the
     160:ref:`constraint definitions table <Constraint_definitions_table>`.
     161
     162Constraint Processing
     163---------------------
     164
     165When constraints will be used or edited, they are processed using a series of
     166calls:
     167
     168* First all of the stored constraints are appended into a single list. They are
     169  initially stored in separate lists only to improve their creation and display
     170  in the GUI.
     171
     172* Then :func:`InitVars` is used to initialize the global variables in
     173  this module (:mod:`GSASIImapvars`).
     174
     175* Then :func:`GSASIIstrIO.ProcessConstraints` is used to initially process the
     176  constraints, as described below.
     177
     178* Symmetry-generated equivalences are then created in
     179  :func:`GSASIIstrIO.GetPhaseData`, which also calls
     180  :func:`GSASIIstrIO.cellVary` and for Pawley refinements
     181  :func:`GSASIIstrIO.GetPawleyConstr`. These are entered directly into this
     182  module's globals using :func:`StoreEquivalence`.
     183* Constraints/equivalences are then checked for possible conflicts with
     184  :func:`CheckConstraints`, this requires grouping the constraints,
     185  as described below.
     186* In refinements, :func:`GenerateConstraints` is then called to
     187  create the constraints that will be used, see below for
     188* For debugging constraints, :func:`VarRemapShow` can be called after
     189  :func:`GenerateConstraints` to display the generated constraints.
     190
     191Constraint Reorganization (:func:`~GSASIIstrIO.ProcessConstraints`)
     192^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     193
     194:func:`GSASIIstrIO.ProcessConstraints` is used to initially process the
     195constraints. This does these things:
     196
     1971. The "Hold", "Const" and "New Var" expressions are split between two paired lists,
     198   :data:`constDictList` and :data:`fixedList` which are set:
     199 
     200   * For "Hold" entries a dict with a single entry is placed in constDictList where
     201     the key is the parameter name (associated value is 0.0) and fixedList gets a
     202     value of 0.0.
     203   * For "Const" entries, a dict with multiple entries is placed in constDictList where
     204     the key is the parameter name and the value is the multiplier for the parameter,
     205     while fixedList gets a string value corresponding to the constant value for
     206     the expression.
     207   * For "New Var" entries, a dict with multiple entries is placed in constDictList
     208     where the key is the parameter name and the value is the multiplier
     209     for the parameter; an additional key "_vary" is given the value of True or False
     210     depending on the refinement flag setting. The corresponding entry in
     211     fixedList is None
     212
     213   The output from this will have this form where the first entry is a "Const", the
     214   second is a "New Var" and the final is a "Hold".
     215
     216  .. code-block:: python
    42217
    43218    constrDict = [
    44219         {'0:12:Scale': 2.0, '0:14:Scale': 4.0, '0:13:Scale': 3.0, '0:0:Scale': 0.5},
    45          {'2::C(10,6,1)': 1.0, '1::C(10,6,1)': 1.0},
     220         {'2::C(10,6,1)': 1.0, '1::C(10,6,1)': 1.0, '_vary':True},
    46221         {'0::A0': 0.0}]
    47222    fixedList = ['5.0', None, '0']
    48223
    49 Where the dictionary defines the first part of an expression and the corresponding fixedList
    50 item is either None (for parameter redefinition) or the constant value, for a constant
    51 constraint equation. A dictionary that contains a single term defines a variable that
    52 will be fixed (held). The multiplier and the fixedList value in this case are ignored.
    53 
    54 Parameters can also be equivalenced or "slaved" to another parameter, such that one
    55 (independent) parameter is equated to several (now dependent) parameters. In
    56 algebraic form this is:
     224
     2252. Equivalences are stored using :func:`StoreEquivalence`
     226   into this module's globals (:data:`~GSASIImapvars.arrayList`,
     227   :data:`~GSASIImapvars.invarrayList`,    :data:`~GSASIImapvars.indParmList`,
     228   :data:`~GSASIImapvars.dependentParmList` and  :data:`~GSASIImapvars.symGenList`)
     229
     230
     231Parameter Grouping (:func:`GenerateConstraints`)
     232^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     233
     234Functions :func:`CheckConstraints` and :func:`GenerateConstraints` are used to
     235process the parameter equivalences and constraint lists created in
     236:func:`~GSASIIstrIO.ProcessConstraints`. The former is used to generate
     237error messages and the latter to generate the internal information used to
     238apply the constraints.
     239
     240Initially, in both a list of parameters that are fixed and those used in
     241constraint relations are tabulated in :func:`CheckEquivalences`. The equivalence
     242relations are the scanned for the following potential problems:
     243
     2441. a parameter is used as a dependent variable in more than one
     245   equivalence relation
     2462. a parameter is fixed and used in in an equivalence relation either
     247   as a dependent or independent variable
     2483. a parameter is used as a dependent variable in one equivalence relation and
     249   as a independent variable in another
     2504. a parameter is used in in an equivalence relation (either
     251   as a dependent or independent variable) and is used in a constraint
     252   expression
     2535. a parameter is not defined in a particular refinement, but is used in an
     254   equivalence relation
     2556. a parameter uses a wildcard for the histogram number (sequential refinements)
     256
     257Cases 1 & 2 above cannot be corrected, and result in errors. Cases 3 & 4 are
     258potentially corrected with :func:`MoveConfEquiv`, as described below.
     259Case 5 causes the equivalence to
     260be dropped. Case 6 causes the current histogram number to be substituted for
     261the wildcard.
     262
     263For cases 3 & 4, :func:`MoveConfEquiv` is used to change these equivalences
     264into "Const" equations. This can potentially mean that additional
     265equivalences will be problematic, so if there are changes made by
     266:func:`MoveConfEquiv`, :func:`CheckEquivalences` is repeated. If any problem
     267cases are noted, the refinement cannot be performed.
     268
     269Constraint expressions ("Const" and "New Var") are sorted into
     270groups so that each group contains the minimum number of entries that
     271ensures each parameter is referenced in only one group in
     272:func:`GroupConstraints`. This is done by scanning the
     273list of dicts in :data:`constDictList` one by one and making a list
     274of parameters used in that constraint expression. Any expression that contains
     275a parameter in is in that list is added to the current group and those
     276parameters are added to this list of parameters. The list of ungrouped
     277expressions is then scanned again until no more expressions are added to the
     278current group. This process is repeated until every expression has been
     279placed in a group. Function :func:`GroupConstraints` returns two lists of lists.
     280The first has, for each group, a list of the indices in :data:`constDictList`
     281that comprise the group (there can be only one). The second list contains,
     282for each group, the unique parameter names in that group.
     283
     284Each constraint group is then processed. First, wildcard parameters are
     285renamed (in a sequential refinement). Any fixed parameters that are used
     286in constraints are noted as errors. The number of refined parameters and
     287the number of parameters that are not defined in the current refinement are
     288also noted. It is fine if all parameters in a group are not defined or all are
     289not varied, but if some are defined and others not or some are varied and
     290others not, this constitutes an error.
     291
     292The contents of each group is then examined. Groups with a single
     293parameter (holds) are ignored. Then for each group, the number
     294of parameters in the group (Np) and the number of expressions in the
     295group (Nc) are counted and for each expression. If Nc > Np, then the constraint
     296is overdetermined, which also constitutes an error.
     297
     298The parameter multipliers for each expression are then assembled:
    57299
    58300::
    59301
    60    P0 = M1 * P1 = M2 * P2 = ...
    61 
    62 Thus parameters P0, P1 and P2,... are linearly equivalent. Routine StoreEquivalence is
    63 used to specify these equivalences.
    64 
    65 Parameter redefinition (new vars) describes a new, independent, parameter, which
    66 is defined in terms of dependent parameters that are defined in the
    67 Model, while fixed constrained relations effectively reduce the complexity
    68 of the Model by removing a degree of freedom. It is possible for a parameter to appear
    69 in both a parameter redefinition expression and a fixed constraint equation, but a
    70 parameter cannot be used a parameter equivalance cannot be used elsewhere (not fixed,
    71 constrained or redefined). Likewise a fixed parameter cannot be used elsewhere (not
    72 equivalanced, constrained or redefined).
    73 
    74 Relationships are grouped so that a set of dependent parameters appear
    75 in only one group (done in routine GroupConstraints.) Note that if a
    76 group contains relationships/equations that involve N dependent
    77 parameters, there must exist N-C new parameters, where C is the number
    78 of contraint equations in the group. Routine GenerateConstraints takes
    79 the output from GroupConstraints and generates the
    80 "missing" relationships and saves that information in the module's
    81 global variables. Each generated parameter is named sequentially using paramPrefix.
    82 
    83 A list of parameters that will be varied is specified as input to GenerateConstraints
    84 (varyList). A fixed parameter will simply be removed from this list preventing that
    85 parameter from being varied. Note that all parameters in a constraint relationship
    86 must specified as varied (appear in varyList) or none can be varied. This is
    87 checked in GenerateConstraints. Likewise, if all parameters in a constraint are
    88 not referenced in a refinement, the constraint is ignored, but if some parameters
    89 in a constraint group are not referenced in a refinement, but others are this
    90 constitutes and error.
    91 
    92 * When a new variable is created, the variable is assigned the name associated
    93   in the constraint definition or it is assigned a default name of form
    94   ``::constr<n>`` (see paramPrefix). The vary setting for variables used in the
    95   constraint are ignored.
    96   Note that any generated "missing" relations are not varied. Only
    97   the input relations can be are varied.
    98  
    99 * If all parameters in a fixed constraint equation are varied, the generated "missing"
    100   relations in the group are all varied. This provides the N-C degrees of freedom.
    101 
    102 *External Routines*
    103 -------------------
     302   M1a * P1 + M2a * P2 +... Mka * Pk
     303   M1b * P1 + M2b * P2 +... Mkb * Pk
     304   ...
     305   M1j * P1 + M2j * P2 +... Mkj * Pk
     306
     307
     308From this it becomes possible to create a Nc x Np matrix, which is
     309called the constraint matrix:
     310
     311 .. math::
     312
     313   \\left( \\begin{matrix}
     314   M_{1a}  & M_{2a} &... & M_{ka} \\\\
     315   M_{1b}  & M_{2b} &... & M_{kb} \\\\
     316   ...  \\\\
     317   M_{1j}  & M_{2j}  &... & M_{kj}
     318   \\end{matrix}\\right)
     319
     320When Nc<Np, then additional rows need to be added to the matrix and to
     321the vector that contains the value for each row (:data:`fixedList`) where
     322values are ``None`` for New Vars and a constant for fixed values.
     323This then can describe a system of Np simultaneous equations:
     324
     325 .. math::
     326
     327   \\left( \\begin{matrix}
     328   M_{1a}  & M_{2a} &... & M_{ka} \\\\
     329   M_{1b}  & M_{2b} &... & M_{kb} \\\\
     330   ...  \\\\
     331   M_{1j}  & M_{2j}  &... & M_{kj}
     332   \\end{matrix}\\right)
     333   \\left( \\begin{matrix}
     334   P_{1} \\\\
     335   P_{2} \\\\
     336   ...  \\\\
     337   P_{k}
     338   \\end{matrix}\\right)
     339   =
     340   \\left( \\begin{matrix}
     341   C_{1} & C_{2} &  ... & C_{k}
     342   \\end{matrix}\\right)
     343
     344This is done by creating a square matrix from the group using
     345:func:`_FillArray` with parameter ``FillDiagonals=False`` (the default). Any
     346unspecified rows are left as all zero. The first Nc rows in the
     347array are then coverted to row-echelon form using :func:`_RowEchelon`. This
     348will create an Exception if any two rows are linearly dependent (which means
     349that no matter what values are used for the remaining rows, that the matrix
     350will be singular). :func:`_FillArray` is then called with parameter
     351``FillDiagonals=True``, which again creates a square matrix but where
     352unspecified rows are zero except for the diagonal elements. The 
     353`Gram-Schmidt process <http://en.wikipedia.org/wiki/Gram-Schmidt>`_,
     354implemented  in :func:`GramSchmidtOrtho`, is used to find orthonormal unit
     355vectors for the remaining Np-Nc rows of the matrix. This will fail with
     356a ``ConstraintException`` if this is not possible (singular matrix) or
     357the result is placed in :data:`constrArr` as a numpy array.
     358
     359Rows in the matrix corresponding to "New Var" constraints and those that
     360were generated by the Gram-Schmidt process are provided with variable names
     361(this can be specified if a "New Var" entry by using a ``"_name"`` element
     362in the constraint dict, but at present this is not implemented.) Names are
     363generated using :data:`paramPrefix` which is set to ``"::constr"``, plus a
     364number to make the new parameter name unique. Global dict :data:`genVarLookup`
     365provides a lookup table, where the names of the parameters related to this new
     366parameter can be looked up easily.
     367
     368Finally the parameters used as input to the constraint are placed in
     369this module's globals
     370:data:`~GSASIImapvars.dependentParmList` and the constraint matrix is
     371placed in into  :data:`~GSASIImapvars.arrayList`. This can be used to compute
     372the initial values for "New Var" parameters. The inverse of the
     373constraint matrix is placed in :data:`~GSASIImapvars.invarrayList` and a list
     374of the "New Var" parameters and a list of the fixed values (as str's)
     375is placed in :data:`~GSASIImapvars.indParmList`. A lookup table for
     376fixed values as floats is placed in :data:`~GSASIImapvars.fixedDict`.
     377Finally the appropriate entry in :data:`~GSASIImapvars.symGenList` is set to
     378False to indicate that this is not a symmetry generated constraint.
     379
     380
     381*Externally-Accessible Routines*
     382---------------------------------
    104383
    105384To define a set of constrained and unconstrained relations, one
     
    107386values, a list of fixed values for each constraint and a list of
    108387parameters to be varied. In addition, one uses
    109 :func:`StoreEquivalence` to define parameters that are equivalent. One
    110 can then use :func:`CheckConstraints` to check that the input is
     388:func:`StoreEquivalence` to define parameters that are equivalent.
     389Use :func:`EvaluateMultipliers` to convert formula-based constraint/equivalence
     390multipliers to numbers and then
     391use :func:`CheckConstraints` to check that the input is
    111392internally consistent and finally :func:`GroupConstraints` and
    112393:func:`GenerateConstraints` to generate the internally used
    113 tables. Routines :func:`Map2Dict` is used to initialize the parameter
    114 dictionary and :func:`Dict2Map`, :func:`Dict2Deriv`, and
     394tables. Routine :func:`Map2Dict` is used to initialize the parameter
     395dictionary and routine :func:`Dict2Map`, :func:`Dict2Deriv`, and
    115396:func:`ComputeDepESD` are used to apply constraints. Routine
    116397:func:`VarRemapShow` is used to print out the constraint information,
    117 as stored by :func:`GenerateConstraints`.
     398as stored by :func:`GenerateConstraints`. Further information on each routine
     399is below:
    118400
    119401:func:`InitVars`
     
    135417       StoreEquivalence('x',('y','z'))
    136418
    137   The latter will run more efficiently. Note that mixing independent and dependent variables would
    138   require assignments to be done in a particular order and thus is not is not allowed. This will cause an
    139   error:
     419  The latter will run more efficiently. Note that mixing independent
     420  and dependent variables would require assignments, such as
    140421
    141422  .. code-block:: python
     
    143424        StoreEquivalence('x',('y',))
    144425        StoreEquivalence('y',('z',))
     426
     427  would require that equivalences be applied in a particular order and
     428  thus is implemented as a constraint equation rather than an equivalence.
    145429       
    146430  Use StoreEquivalence before calling GenerateConstraints or CheckConstraints
    147431
    148432:func:`CheckConstraints`
    149    To check that input in internally consistent, use CheckConstraints
     433   check that input in internally consistent
     434
     435:func:`GenerateConstraints`
     436   generate the internally used tables from constraints and equivalences
     437
     438:func:`EvaluateMultipliers`
     439   Convert any string-specified multipliers to numbers. Call this before
     440   using :func:`CheckConstraints` or :func:`GenerateConstraints`.
    150441
    151442:func:`Map2Dict`
     
    155446:func:`Dict2Map`
    156447   To take values from the new independent parameters and constraints,
    157    one calls Dict2Map. This will apply contraints.
     448   one calls Dict2Map and set the parameter array, thus appling contraints.
    158449
    159450:func:`Dict2Deriv`
    160451   Use Dict2Deriv to determine derivatives on independent parameters
    161    from those on dependent ones
     452   from those on dependent ones.
    162453
    163454:func:`ComputeDepESD`     
    164    Use ComputeDepESD to compute uncertainties on dependent variables
     455   Use ComputeDepESD to compute uncertainties on dependent variables.
    165456
    166457:func:`VarRemapShow`
    167    To show a summary of the parameter remapping, one calls VarRemapShow
     458   To show a summary of the parameter remapping, one calls VarRemapShow.
    168459
    169460*Global Variables*
     
    171462
    172463dependentParmList:
    173    contains a list by group of lists of
     464   a list containing group of lists of
    174465   parameters used in the group. Note that parameters listed in
    175466   dependentParmList should not be refined as they will not affect
     
    177468
    178469indParmList:
    179      a list of groups of Independent parameters defined in
     470     a list containing groups of Independent parameters defined in
    180471     each group. This contains both parameters used in parameter
    181472     redefinitions as well as names of generated new parameters.
     473
     474arrayList:
     475     a list containing group of relationship matrices to relate
     476     parameters in dependentParmList to those in indParmList. Unlikely
     477     to be used externally.
     478
     479invarrayList:
     480     a list containing group of relationship matrices to relate
     481     parameters in indParmList to those in dependentParmList. Unlikely
     482     to be used externally.
    182483
    183484fixedVarList:
     
    188489     Unlikely to be used externally.
    189490
    190 arrayList:
    191      a list by group of relationship matrices to relate
    192      parameters in dependentParmList to those in indParmList. Unlikely
    193      to be used externally.
    194 
    195 invarrayList:
    196      a list by group of relationship matrices to relate
    197      parameters in indParmList to those in dependentParmList. Unlikely
    198      to be used externally.
    199 
    200491fixedDict:
    201492     a dictionary containing the fixed values corresponding
     
    210501     a list containing variables that show up in constraints producing errors
    211502
    212 *Routines*
    213 ----------
    214 
    215 Note that parameter names in GSAS-II are strings of form ``<ph>:<hst>:<nam>``
    216 
     503
     504
     505*Routines/variables*
     506---------------------
     507
     508Note that parameter names in GSAS-II are strings of form ``<ph#>:<hst#>:<nam>`` or ``<ph#>::<nam>:<at#>``.
    217509"""
    218510
     
    224516# data used for constraints;
    225517debug = False # turns on printing as constraint input is processed
    226 # note that constraints are stored listed by contraint groups, where each constraint
     518
     519# note that constraints are stored listed by contraint groups,
     520# where each constraint
    227521# group contains those parameters that must be handled together
    228 dependentParmList = [] # contains a list of parameters in each group
    229 # note that parameters listed in dependentParmList should not be refined
    230 arrayList = [] # a list of of relationship matrices
    231 invarrayList = [] # a list of inverse relationship matrices
     522dependentParmList = []
     523'''a list of lists where each item contains a list of parameters in each constraint group.
     524note that parameters listed in dependentParmList should not be refined directly.'''
    232525indParmList = [] # a list of names for the new parameters
    233 fixedDict = {} # a dictionary containing the fixed values corresponding to defined parameter equations
    234                # key is original ascii string, value is float
    235 fixedVarList = [] # List of variables that should not be refined
    236 symGenList = [] # Flag if constraint is generated by symmetry
    237 problemVars = [] # variables causing errors
     526'''a list of lists where each item contains a list for each constraint group with
     527fixed values for constraint equations and names of generated (New Var) parameters.
     528'''
     529arrayList = []
     530'''a list of of relationship matrices that map model parameters in each
     531constraint group (in :data:`dependentParmList`) to
     532generated (New Var) parameters.
     533'''
     534invarrayList = []
     535'''a list of of inverse-relationship matrices that map constrained values and
     536generated (New Var) parameters (in :data:`indParmList`) to model parameters
     537(in :data:`dependentParmList`).
     538'''
     539fixedDict = {}
     540'''A dict lookup-table containing the fixed values corresponding
     541to defined parameter equations. Note the key is the original ascii string
     542and the value in the dict is a float.
     543'''
     544fixedVarList = []
     545'''List of variables that should not be refined.
     546'''
     547symGenList = []
     548'''A list of flags that if True indicates a constraint was generated by symmetry
     549'''
     550problemVars = []
     551'''a list of variables causing errors
     552'''
    238553dependentVars = []
     554'A list of dependent variables, taken from (:data:`dependentParmList`).'
    239555independentVars = []
    240 
    241 # prefix for parameter names
     556'A list of dependent variables, taken from (:data:`indParmList`).'
     557genVarLookup = {}
     558'provides a list of variables that are related to each generated variable'
    242559paramPrefix = "::constr"
    243 consNum = 0 # number of the next constraint to be created
     560'A prefix for generated parameter names'
     561consNum = 0
     562'The number to be assigned to the next constraint to be created'
    244563
    245564class ConstraintException(Exception):
     
    258577    symGenList = [] # Flag if constraint is generated by symmetry
    259578    consNum = 0 # number of the next constraint to be created
     579    global genVarLookup
     580    genVarLookup = {}
    260581
    261582def VarKeys(constr):
     
    343664    import re
    344665    global dependentParmList,arrayList,invarrayList,indParmList,consNum
    345     errmsg = ''
    346     warnmsg = ''
    347666    global problemVars
    348     problemVars = []
    349     fixVlist = []
    350     # process fixed variables (holds)
    351     for cdict in constrDict:
    352         # N.B. No "_" names in holds
    353         if len(cdict) == 1:
    354             fixVlist.append(list(cdict.keys())[0])
    355    
    356     # process equivalences: make a list of dependent and independent vars
    357     #    and check for repeated uses (repetition of a parameter as an
    358     #    independent var is OK)
    359     indepVarList = []
    360     depVarList = []
    361     multdepVarList = []
    362     for varlist,mapvars,multarr,invmultarr in zip(
    363         dependentParmList,indParmList,arrayList,invarrayList):
    364         if multarr is None: # an equivalence
    365             zeromult = False
    366             for mv in mapvars:
    367                 varied = 0
    368                 notvaried = ''
    369                 if mv in varyList:
    370                     varied += 1
    371                 else:
    372                     if notvaried: notvaried += ', '
    373                     notvaried += mv
    374                 if mv not in indepVarList: indepVarList.append(mv)
    375                 for v,m in zip(varlist,invmultarr):
    376                     if v in indepVarList:
    377                         errmsg += '\nVariable '+v+' is used to set values in a constraint before its value is set in another constraint\n'
    378                         if v not in problemVars: problemVars.append(v)
    379                     if m == 0: zeromult = True
    380                     if v in varyList:
    381                         varied += 1
    382                     else:
    383                         if notvaried: notvaried += ', '
    384                         notvaried += v
    385                     if v in depVarList:
    386                         multdepVarList.append(v)
    387                     else:
    388                         depVarList.append(v)
    389             if varied > 0 and varied != len(varlist)+1:
    390                 warnmsg += "\nNot all variables refined in equivalence:\n\t"
    391                 s = ""
    392                 for v in varlist:
    393                     if s != "": s+= " & "
    394                     s += str(v)           
    395                 warnmsg += str(mv) + " => " + s
    396                 warnmsg += '\nNot refined: ' + notvaried + '\n'
    397             if zeromult:
    398                 errmsg += "\nZero multiplier is invalid in equivalence:\n\t"
    399                 s = ""
    400                 for v in varlist:
    401                     if s != "": s+= " & "
    402                     s += str(v)           
    403                 errmsg += str(mv) + " => " + s + '\n'
    404 
    405     # check for errors:
    406     if len(multdepVarList) > 0:
    407         errmsg += "\nThe following parameters(s) are used in conflicting Equivalence relations as dependent variables:\n"
    408         s = ''
    409         for var in sorted(set(multdepVarList)):
    410             if v not in problemVars: problemVars.append(v)
    411             if s != "": s+= ", "
    412             s += str(var)           
    413         errmsg += '\t'+ s + '\n'
    414     equivVarList = list(set(indepVarList).union(set(depVarList)))
    415     if debug: print ('equivVarList',equivVarList)
    416     inboth = set(fixVlist).intersection(set(equivVarList))
    417     if len(inboth) > 0:
    418         errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n"
    419         s = ''
    420         for var in sorted(inboth):
    421             if var not in problemVars: problemVars.append(var)
    422             if s != "": s+= ", "
    423             s += str(var)
    424         errmsg += '\t'+ s + '\n'
     667    # Process the equivalences
     668    #    If there are conflicting parameters, move them into constraints. This
     669    #    may create new conflicts, requiring movement of other parameters, so
     670    #    loop until there are no more changes to make.
     671    parmsChanged = True
     672    while parmsChanged:
     673        parmsChanged = 0
     674        errmsg,warnmsg,fixVlist,dropVarList,translateTable = CheckEquivalences(
     675            constrDict,varyList)
     676        #print('debug: using MoveConfEquiv to address =',errmsg)
     677        if problemVars: parmsChanged = MoveConfEquiv(constrDict,fixedList)
     678#    GSASIIpath.IPyBreak()
    425679
    426680    groups,parmlist = GroupConstraints(constrDict)
     
    531785    return errmsg,warnmsg
    532786
    533 def GenerateConstraints(groups,parmlist,varyList,constrDict,fixedList,parmDict=None,SeqHist=None):
     787def GenerateConstraints(varyList,constrDict,fixedList,parmDict=None,SeqHist=None):
    534788    '''Takes a list of relationship entries comprising a group of
    535789    constraints and builds the relationship lists and their inverse
    536790    and stores them in global variables Also checks for internal
    537791    conflicts or inconsistencies in parameter/variable definitions.
    538 
    539     :param list groups: a list of grouped contraints where each constraint
    540       grouped containts a list of indices for constraint constrDict entries,
    541       created in :func:`GroupConstraints` (returned as 1st value)
    542 
    543     :param list parmlist: a list containing lists of parameter names
    544       contained in each group, created in :func:`GroupConstraints`
    545       (returned as 2nd value)
    546792
    547793    :param list varyList: a list of parameters names (strings of form
     
    563809    '''
    564810    global dependentParmList,arrayList,invarrayList,indParmList,consNum
     811    global genVarLookup
    565812    msg = ''
    566813    shortmsg = ''
    567 
    568     # process fixed (held) variables
    569     for cdict in constrDict:
    570         if len(cdict) == 1:
    571             fixedVarList.append(list(cdict.keys())[0])
    572    
    573     # process equivalences: make a list of dependent and independent vars
    574     #    and check for repeated uses (repetition of a parameter as an
    575     #    independent var is OK [A=B; A=C], but chaining: [A=B; B=C] is not good)
    576     dropVarList = []
    577     translateTable = {} # lookup table for wildcard referenced variables
    578     for varlist,mapvars,multarr,invmultarr in zip(       # process equivalences
    579         dependentParmList,indParmList,arrayList,invarrayList):
    580         if multarr is None: # true only if an equivalence
    581             zeromult = False
    582             for i,mv in enumerate(mapvars):
    583                 if mv.split(':')[1] == '*' and SeqHist is not None:
    584                     # convert wildcard var to reference current histogram; save translation in table
    585                     sv = mv.split(':')
    586                     sv[1] = str(SeqHist)
    587                     mv = translateTable[mv] = ':'.join(sv)
    588                     mapvars[i] = mv
    589                 #s = ''
    590                 varied = 0
    591                 notvaried = ''
    592                 if mv in varyList:
    593                     varied += 1
    594                 else:
    595                     if notvaried: notvaried += ', '
    596                     notvaried += mv
    597                 if parmDict is not None and mv not in parmDict:
    598                     print ("Dropping equivalence for variable "+str(mv)+". Not defined in this refinement")
    599                     if mv not in dropVarList: dropVarList.append(mv)
    600                     #msg += "\nCannot equivalence to variable "+str(mv)+". Not defined in this refinement"
    601                     #continue
    602             for i,(v,m) in enumerate(zip(varlist,invmultarr)):
    603                 if v.split(':')[1] == '*' and SeqHist is not None:
    604                     # convert wildcard var to reference current histogram; save translation in table
    605                     sv = v.split(':')
    606                     sv[1] = str(SeqHist)
    607                     varlist[i] = v = translateTable[v] = ':'.join(sv)
    608                 if parmDict is not None and v not in parmDict:
    609                     print ("Dropping equivalence for dep. variable "+str(v)+". Not defined in this refinement")
    610                     if v not in dropVarList: dropVarList.append(v)
    611                     continue
    612                 if m == 0: zeromult = True
    613                 if v in varyList:
    614                     varied += 1
    615                 else:
    616                     if notvaried: notvaried += ', '
    617                     notvaried += v
    618             if varied > 0 and varied != len(varlist)+1:
    619                 msg += "\nNot all variables refined in equivalence:\n\t"
    620                 s = ""
    621                 for v in varlist:
    622                     if s != "": s+= " & "
    623                     s += str(v)           
    624                 msg += str(mv) + " => " + s
    625                 msg += '\nNot refined: ' + notvaried + '\n'
    626             if zeromult:
    627                 msg += "\nZero multiplier is invalid in equivalence:\n\t"
    628                 s = ""
    629                 for v in varlist:
    630                     if s != "": s+= " & "
    631                     s += str(v)           
    632                 msg += str(mv) + " => " + s + '\n'
     814    changed = False
     815    # Process the equivalences
     816    #    If there are conflicting parameters, move them into constraints. This
     817    #    may create new conflicts, requiring movement of other parameters, so
     818    #    loop until there are no more changes to make.
     819    parmsChanged = True
     820    while parmsChanged:
     821        parmsChanged = 0
     822        errmsg,warnmsg,fixVlist,dropVarList,translateTable = CheckEquivalences(
     823            constrDict,varyList,parmDict,SeqHist)
     824        if problemVars:
     825            parmsChanged = MoveConfEquiv(constrDict,fixedList)
     826            changed = True
     827    if errmsg:
     828        msg = errmsg
     829    if warnmsg:
     830        if msg: msg += '\n'
     831        msg += warnmsg
    633832
    634833    # scan through parameters in each relationship. Are all varied? If only some are
    635834    # varied, create an error message.
     835    groups,parmlist = GroupConstraints(constrDict)
    636836    for group,varlist in zip(groups,parmlist):
    637837        if len(varlist) == 1: continue
     
    753953                    consNum += 1
    754954                mapvar.append(varname)
     955                genVarLookup[varname] = varlist # save list of variables related to this new var
    755956                # vary the new relationship if it is a degree of freedom in
    756957                # a set of contraint equations or if a New Var is flagged to be varied.
     
    786987            fixedDict[fixedval] = float(fixedval)
    787988    _setVarLists(dropVarList)
     989    if changed:
     990        print(60*'=')
     991        print('Constraints were reclassified to avoid conflicts, as below:')
     992        print (VarRemapShow(varyList,True))
     993        print(60*'=')
    788994   
    789995def _setVarLists(dropVarList):
     
    8041010    if debug: # on debug, show what is parsed & generated, semi-readable
    8051011        print (50*'-')
    806         print (VarRemapShow(varyList))
    807         print ('Varied: ',varyList)
     1012        #print (VarRemapShow(varyList))
     1013        #print ('Varied: ',varyList)
    8081014        print ('Not Varied: ',fixedVarList)
     1015
     1016# def CheckEquivalences(constrDict,varyList):
     1017#     global dependentParmList,arrayList,invarrayList,indParmList,consNum
     1018#     global problemVars
     1019#     warnmsg = ''
     1020#     errmsg = ''
     1021#     problemVars = []
     1022#     # process fixed variables (holds)
     1023#     fixVlist = [] # list of Fixed vars
     1024#     constrVars = [] # list of vars in constraint expressions
     1025#     for cdict in constrDict:
     1026#         # N.B. No "_" names in holds
     1027#         if len(cdict) == 1:
     1028#             fixVlist.append(list(cdict.keys())[0])
     1029#         else:
     1030#             constrVars += cdict.keys() # this will include _vary (not a problem)
     1031#     # process equivalences: make a list of dependent and independent vars
     1032#     #    and check for repeated uses (repetition of a parameter as an
     1033#     #    independent var is OK)
     1034#     indepVarList = []
     1035#     depVarList = []
     1036#     multdepVarList = []
     1037#     for varlist,mapvars,multarr,invmultarr in zip(
     1038#         dependentParmList,indParmList,arrayList,invarrayList):
     1039#         if multarr is None: # an equivalence
     1040#             zeromult = False
     1041#             for mv in mapvars:
     1042#                 varied = 0
     1043#                 notvaried = ''
     1044#                 if mv in varyList:
     1045#                     varied += 1
     1046#                 else:
     1047#                     if notvaried: notvaried += ', '
     1048#                     notvaried += mv
     1049#                 if mv not in indepVarList: indepVarList.append(mv)
     1050#                 for v,m in zip(varlist,invmultarr):
     1051#                     if v in indepVarList:
     1052#                         errmsg += '\nVariable '+v+' is used to set values in a constraint before its value is set in another constraint\n'
     1053#                         if v not in problemVars: problemVars.append(v)
     1054#                     if m == 0: zeromult = True
     1055#                     if v in varyList:
     1056#                         varied += 1
     1057#                     else:
     1058#                         if notvaried: notvaried += ', '
     1059#                         notvaried += v
     1060#                     if v in depVarList:
     1061#                         multdepVarList.append(v)
     1062#                     else:
     1063#                         depVarList.append(v)
     1064#             if varied > 0 and varied != len(varlist)+1:
     1065#                 warnmsg += "\nNot all variables refined in equivalence:\n\t"
     1066#                 s = ""
     1067#                 for v in varlist:
     1068#                     if s != "": s+= " & "
     1069#                     s += str(v)           
     1070#                 warnmsg += str(mv) + " => " + s
     1071#                 warnmsg += '\nNot refined: ' + notvaried + '\n'
     1072#             if zeromult:
     1073#                 errmsg += "\nZero multiplier is invalid in equivalence:\n\t"
     1074#                 s = ""
     1075#                 for v in varlist:
     1076#                     if s != "": s+= " & "
     1077#                     s += str(v)           
     1078#                 errmsg += str(mv) + " => " + s + '\n'
     1079#     # check for errors:
     1080#     if len(multdepVarList) > 0:
     1081#         errmsg += "\nThe following parameters(s) are used in conflicting Equivalence relations as dependent variables:\n"
     1082#         s = ''
     1083#         for var in sorted(set(multdepVarList)):
     1084#             if v not in problemVars: problemVars.append(v)
     1085#             if s != "": s+= ", "
     1086#             s += str(var)           
     1087#         errmsg += '\t'+ s + '\n'
     1088#     equivVarList = list(set(indepVarList).union(set(depVarList)))
     1089#     if debug: print ('equivVarList',equivVarList)
     1090#     # check for parameters that are both fixed and in an equivalence (not likely)
     1091#     inboth = set(fixVlist).intersection(set(equivVarList))
     1092#     if len(inboth) > 0:
     1093#         errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n"
     1094#         s = ''
     1095#         for var in sorted(inboth):
     1096#             if var not in problemVars: problemVars.append(var)
     1097#             if s != "": s+= ", "
     1098#             s += str(var)
     1099#         errmsg += '\t'+ s + '\n'
     1100#     # check for parameters that in both an equivalence and a constraint expression
     1101#     inboth = set(constrVars).intersection(set(equivVarList))
     1102#     if len(inboth) > 0:
     1103#         errmsg += "\nThe following parameter(s) are used in both Equivalence and Equiv or new var constraints:\n"
     1104#         s = ''
     1105#         for var in sorted(inboth):
     1106#             if var not in problemVars: problemVars.append(var)
     1107#             if s != "": s+= ", "
     1108#             s += str(var)
     1109#         errmsg += '\t'+ s + '\n'
     1110#     return errmsg,warnmsg,fixVlist
     1111
     1112def CheckEquivalences(constrDict,varyList,parmDict=None,SeqHist=None):
     1113    '''Process equivalence constraints, looking for conflicts such as
     1114    where a parameter is used in both an equivalence and a constraint expression
     1115    or where chaining is done (A->B and B->C).
     1116    When called during refinements, parmDict is defined, and for sequential refinement
     1117    SeqHist ia also defined.
     1118
     1119      * parmDict is used to remove equivalences where a parameter is not present
     1120        in a refinement
     1121      * SeqHist is used to rename wild-card parameter names in sequential
     1122        refinements to use the current histogram.
     1123    '''
     1124    global dependentParmList,arrayList,invarrayList,indParmList,consNum
     1125    global problemVars
     1126    warnmsg = ''
     1127    errmsg = ''
     1128    problemVars = []
     1129    # process fixed variables (holds)
     1130    fixVlist = [] # list of Fixed vars
     1131    constrVars = [] # list of vars in constraint expressions
     1132    for cdict in constrDict:
     1133        # N.B. No "_" names in holds
     1134        if len(cdict) == 1:
     1135            fixVlist.append(list(cdict.keys())[0])
     1136        else:
     1137            constrVars += cdict.keys() # this will include _vary (not a problem)
     1138    # process equivalences: make a list of dependent and independent vars
     1139    #    and check for repeated uses (repetition of a parameter as an
     1140    #    independent var is OK)
     1141    indepVarList = []
     1142    depVarList = []
     1143    multdepVarList = []
     1144    dropVarList = []
     1145    translateTable = {} # lookup table for wildcard referenced variables
     1146    for varlist,mapvars,multarr,invmultarr in zip(
     1147        dependentParmList,indParmList,arrayList,invarrayList):
     1148        if multarr is None: # an equivalence
     1149            zeromult = False
     1150            for i,mv in enumerate(mapvars):
     1151                if mv.split(':')[1] == '*' and SeqHist is not None:
     1152                    # convert wildcard var to reference current histogram; save translation in table
     1153                    sv = mv.split(':')
     1154                    sv[1] = str(SeqHist)
     1155                    mv = translateTable[mv] = ':'.join(sv)
     1156                    mapvars[i] = mv
     1157                varied = 0
     1158                notvaried = ''
     1159                if mv in varyList:
     1160                    varied += 1
     1161                else:
     1162                    if notvaried: notvaried += ', '
     1163                    notvaried += mv
     1164                if parmDict is not None and mv not in parmDict:
     1165                    print ("Dropping equivalence for variable "+str(mv)+". Not defined in this refinement")
     1166                    if mv not in dropVarList: dropVarList.append(mv)
     1167                if mv not in indepVarList: indepVarList.append(mv)
     1168            for i,(v,m) in enumerate(zip(varlist,invmultarr)):
     1169                if v.split(':')[1] == '*' and SeqHist is not None:
     1170                    # convert wildcard var to reference current histogram; save translation in table
     1171                    sv = v.split(':')
     1172                    sv[1] = str(SeqHist)
     1173                    varlist[i] = v = translateTable[v] = ':'.join(sv)
     1174                if parmDict is not None and v not in parmDict:
     1175                    print ("Dropping equivalence for dep. variable "+str(v)+". Not defined in this refinement")
     1176                    if v not in dropVarList: dropVarList.append(v)
     1177                    continue
     1178                if m == 0: zeromult = True
     1179                if v in varyList:
     1180                    varied += 1
     1181                else:
     1182                    if notvaried: notvaried += ', '
     1183                    notvaried += v
     1184                if v in indepVarList:
     1185                    errmsg += '\nVariable '+v+' is used to set values in a constraint before its value is set in another constraint\n'
     1186                    if v not in problemVars: problemVars.append(v)
     1187                if v in depVarList:
     1188                    multdepVarList.append(v)
     1189                else:
     1190                    depVarList.append(v)
     1191            if varied > 0 and varied != len(varlist)+1:
     1192                warnmsg += "\nNot all variables refined in equivalence:\n\t"
     1193                s = ""
     1194                for v in varlist:
     1195                    if s != "": s+= " & "
     1196                    s += str(v)           
     1197                warnmsg += str(mv) + " => " + s
     1198                warnmsg += '\nNot refined: ' + notvaried + '\n'
     1199            if zeromult:
     1200                errmsg += "\nZero multiplier is invalid in equivalence:\n\t"
     1201                s = ""
     1202                for v in varlist:
     1203                    if s != "": s+= " & "
     1204                    s += str(v)           
     1205                errmsg += str(mv) + " => " + s + '\n'
     1206    # check for errors:
     1207    if len(multdepVarList) > 0:
     1208        errmsg += "\nThe following parameters(s) are used in conflicting Equivalence relations as dependent variables:\n"
     1209        s = ''
     1210        for var in sorted(set(multdepVarList)):
     1211            if v not in problemVars: problemVars.append(v)
     1212            if s != "": s+= ", "
     1213            s += str(var)           
     1214        errmsg += '\t'+ s + '\n'
     1215    equivVarList = list(set(indepVarList).union(set(depVarList)))
     1216    if debug: print ('equivVarList',equivVarList)
     1217    # check for parameters that are both fixed and in an equivalence (not likely)
     1218    inboth = set(fixVlist).intersection(set(equivVarList))
     1219    if len(inboth) > 0:
     1220        errmsg += "\nThe following parameter(s) are used in both Equivalence and Fixed constraints:\n"
     1221        s = ''
     1222        for var in sorted(inboth):
     1223            if var not in problemVars: problemVars.append(var)
     1224            if s != "": s+= ", "
     1225            s += str(var)
     1226        errmsg += '\t'+ s + '\n'
     1227    # check for parameters that in both an equivalence and a constraint expression
     1228    inboth = set(constrVars).intersection(set(equivVarList))
     1229    if len(inboth) > 0:
     1230        errmsg += "\nThe following parameter(s) are used in both Equivalence and Equiv or new var constraints:\n"
     1231        s = ''
     1232        for var in sorted(inboth):
     1233            if var not in problemVars: problemVars.append(var)
     1234            if s != "": s+= ", "
     1235            s += str(var)
     1236        errmsg += '\t'+ s + '\n'
     1237    return errmsg,warnmsg,fixVlist,dropVarList,translateTable
     1238
     1239def MoveConfEquiv(constrDict,fixedList):
     1240    '''Address conflicts in Equivalence constraints by creating an constraint equation
     1241    that has the same action as the equivalence and removing the Equivalence
     1242    '''
     1243    global dependentParmList,arrayList,invarrayList,indParmList,consNum
     1244    global problemVars
     1245    parmsChanged = 0
     1246    for i,(varlist,mapvars) in enumerate(zip(dependentParmList,indParmList)):
     1247        conf = False
     1248        for mv in mapvars:
     1249            if mv in problemVars:
     1250                conf = True
     1251                break
     1252        for v in varlist:
     1253            if v in problemVars:
     1254                conf = True
     1255                break
     1256        if conf:
     1257            parmsChanged += 1
     1258            indvar = indParmList[i][0]
     1259            for dep,mult in zip(dependentParmList[i],invarrayList[i]):
     1260                #print('debug replacing equiv with constraint equation 0=',{indvar:-1.,dep:mult[0]})
     1261                constrDict += [{indvar:-1.,dep:mult[0]}]
     1262                fixedList += ['0.0']
     1263            dependentParmList[i] = None
     1264    if parmsChanged:
     1265        for i in range(len(dependentParmList)-1,-1,-1):
     1266            if dependentParmList[i] is None:
     1267                del dependentParmList[i],indParmList[i],arrayList[i],invarrayList[i]
     1268    return parmsChanged
    8091269
    8101270def StoreEquivalence(independentVar,dependentList,symGen=True):
     
    8251285    mapList = []
    8261286    multlist = []
     1287    allfloat = True
    8271288    for var in dependentList:
    8281289        if isinstance(var, str):
     
    8331294            raise Exception("Cannot parse "+repr(var) + " as var or (var,multiplier)")
    8341295        mapList.append(var)
    835         multlist.append(tuple((mult,)))
     1296        try:           
     1297            multlist.append(tuple((float(mult),)))
     1298        except:
     1299            allfloat = False
     1300            multlist.append(tuple((mult,)))
    8361301    # added relationships to stored values
    8371302    arrayList.append(None)
    838     invarrayList.append(np.array(multlist))
     1303    if allfloat:
     1304        invarrayList.append(np.array(multlist))
     1305    else:
     1306        invarrayList.append(multlist)
    8391307    indParmList.append(list((independentVar,)))
    8401308    dependentParmList.append(mapList)
    8411309    symGenList.append(symGen)
    8421310    return
     1311
     1312def EvaluateMultipliers(constList,*dicts):
     1313    '''Convert multipliers for constraints and equivalences that are specified
     1314    as strings into values. The strings can specify values in the parameter dicts as
     1315    well as normal Python functions, such as "2*np.cos(0::Ax:2/2.)"
     1316   
     1317    :param list constList: a list of dicts containing constraint expressions
     1318    :param \*dict1: one or more dicts containing GSAS-II parameters and their values
     1319       can be specified
     1320    :returns: an empty string if there were no errors, or an error message listing
     1321       the strings that could not be converted.
     1322    '''
     1323    def SubfromParmDict(s,prmDict):
     1324        for key in prmDict:
     1325            if key in s:
     1326                s = s.replace(key,str(prmDict[key]))
     1327        return eval(s)
     1328    prmDict = {}
     1329    for d in dicts: prmDict.update(d) # combine all passed parameter dicts
     1330    problemList = ""
     1331    # loop through multipliers in contraint expressions
     1332    for const in constList:
     1333        for key in const:
     1334            try:
     1335                1+const[key]
     1336                continue
     1337            except:
     1338                pass
     1339            try:
     1340                newval = SubfromParmDict(const[key][:],prmDict)
     1341                if GSASIIpath.GetConfigValue('debug'):
     1342                    print('Changing ',const[key],'to',newval)
     1343                const[key] = newval               
     1344            except:
     1345                if problemList: problemList += ", "
     1346                problemList += const[key]
     1347    # loop through multipliers in equivalences
     1348    global arrayList,invarrayList
     1349    for i,(a,valList) in enumerate(zip(arrayList,invarrayList)):
     1350        if a is not None: continue # ignore if not equiv
     1351        try:
     1352            valList.shape
     1353            continue # ignore if already a numpy array
     1354        except:
     1355            pass
     1356        repList = []
     1357        for v in valList:
     1358            try:
     1359                1+v[0]
     1360                repList.append(tuple((v[0],)))
     1361                continue
     1362            except:
     1363                pass
     1364            try:
     1365                newval = SubfromParmDict(v[0][:],prmDict)
     1366                if GSASIIpath.GetConfigValue('debug'):
     1367                    print('Changing ',v[0],'to',newval)
     1368                repList.append(tuple((newval,)))
     1369            except:
     1370                if problemList: problemList += ", "
     1371                problemList += v[0]
     1372                repList.append(tuple(('error',)))
     1373        invarrayList[i] = np.array(repList)
     1374    return problemList
    8431375
    8441376def GetDependentVars():
     
    9591491        for v in fixedVarList:
    9601492            s += '    ' + v + '\n'
    961     s += 'User-supplied variable mapping relations:\n'
     1493    if not inputOnly:
     1494        s += 'User-supplied variable mapping relations:\n'
    9621495    symout = ''
    9631496    global dependentParmList,arrayList,invarrayList,indParmList,fixedDict,symGenList
Note: See TracChangeset for help on using the changeset viewer.