Parametric Fitting and Pseudo Variables for Sequential Fits


  • A video version of this tutorial is available at
  • Exercise files are created in this tutorial

    Once a sequential fit has been performed, any function of the fit parameters from the fit and their estimated uncertainty (s.u.) can be computed by defining one or more "pseudo-variables". Note that the covariance matrix from the fit is used to provide appropriate treatment of the correlation in parameters with respect to uncertainty estimation.  In addition, parametric equations in terms of sample parameters such as temperature and pressure, etc. can be fit to either the parameters from the fit or the pseudo-variables.


    This tutorial assumes that the steps in the Sequential refinement of multiple datasets tutorial have already been performed. If you have not done so already, start GSAS-II.

    Step 1. Open the previous Sequential Fit


    1.      Select the File/Open Project menu item to load file named SeqTut.gpx you created in the previous tutorial.

    2.      Select the Sequential results item in the data tree. The sequential refinement results data table is displayed, as below.

    If you look through the table, you will see how each variable changed through the temperature series. In particular the CuCrO4 b & c lattice parameters become nearly identical above ~120K (PWDR OH_24.fxye Bank 1). Select these two columns (0::b & 0::c) and do Columns/Plot selected

    Select the plot & press the ‘s key; the popup will show

    Select Temperature and press OK; do Columns/Plot selected again. The x-axis will be temperature

    It would seem that there is a phase transition where the orthorhombic low temperature phase is transformed to a miss-indexed tetragonal phase where b=c. This phase transition has been previously explored by Suchomel, Shoemaker & Ribaud (Phys. Rev. B86, 054406, 2012) and the data used in that study is used here in this tutorial. We will be investigating this transition with the parametric fitting tools in GSAS-II

    Step 2. Create a Pseudo Variable


    While one can plot a, b and c axis lengths by clicking on the appropriate column of the table; it will be more useful to have some mathematical relationship among them. We can define pseudo-variables that allow computation of an arbitrarily defined function of combinations of parameters that are displayed in the sequential refinement table. Any valid Python language expression can be used to define them. For this phase transition it is most useful to view the ratio of b and c.


    1.      Click on the Pseudo Vars/Add Formula menu item to create a new pseudo-variable. A window opens to define a formula



    In this window, type a Python expression, "b/c", the variables b and c are arbitrary labels. After entering the formula, press the Validate button or wait a few seconds and the validation will be tested automatically. Note that if you pause too long, while typing, an error message may be displayed as the incomplete formula is found to be invalid. If so, make corrections or keep typing until the entire expression is entered. 


    When the expression is complete, a new table appears where the arbitrary b and c labels will be assigned.



    2.      Assign the label for the numerator by clicking on the menu button next to label b; this will bring up a list of variable types, from this select Phase to bring up a list of phase variables, use ‘lattice’ as a filter and from that list select variable 0::b, which is the b lattice parameter for phase 0 (CuCr2O4). Press OK. Repeat this process to select assign to label c variable 0::c.



    As each variable is set, the window shows the value for each variable from the first refinement and when the expression can be evaluated, the value for the expression, evaluated for that first refinement. Only when the expression can be evaluated can the OK button be pressed.



    Press OK to close the window and add the pseudo-variable. Note that a new column (‘b/c’) appears at the end of the sequential refinement data table.


    Also note that positioning the mouse above any cell in the table and waiting for a few seconds causes the s.u. to be shown as a tooltip.


    Double clicking on the b/c column will cause the pseudo-variable to be plotted:



    The main feature is the b/c ratio falls to ~1 at ~120K and is then essentially constant at higher temperatures. It looks like a second order phase transition since the lattice parameter change isn’t confined to a single temperature. Notice that the b/c ratio shows some instability above the transition. This is because the refinement is somewhat ill conditioned after the phase transition because of parameter degeneracies. This can be improved by modifying the Singular Value Decomposition (SVD) zero tolerance. Go to the Controls main tree item and change this parameter to 0.0001.

    Then repeat the sequential refinement (perhaps twice); the new b/c plot is now much cleaner after the phase transition.


    Step 3: Fitting a simple parametric equation to the unit cell volume


    In the next few steps we will fit increasingly sophisticated equations of state to different parameters. As a simple example, we can double-click on 0::Vol, to plot the unit volume for the first phase vs. T, and fit this to a straight line (even though it obviously isn’t ‘straight’) 


    1.      Click on the Parametric Fit/Add equation menu item, which opens a window similar to what was used before to create a pseudo-variable, except that here an equation is defined. 



    We will want to fit the unit cell volume to an equation of form y = mx+b, where y will be the volume and x temperature and m and b will be best fit.


    2.      Click the empty button in the upper left corner to select a dependent variable for "y". From the phase list (use ‘vol’ as a filter), select the unit cell volume for the CuCr2O4 phase, select 0::Vol and then press OK.


    The Expression Editor will be updated to show the new y parameter


    3.      Now we enter the equation using Python syntax, which is simply "m*x + b", note that the use of spaces and the choice of labels is arbitrary. If one were to accidentally leave out the "*" and typed "mx" then that would be considered a label, if "m x" were typed, an error message would be displayed.



    Then press Validate or wait a few seconds and a window changes, as above, allowing assignment of the three new labels (m, x, and b).


    4.      Assign b and m as refinable “free” parameters: Click on the button next to b and select Free. Then repeat this for m.


    It is not necessary to change the default variable names, the initial values, or the refine flag.


    5.      To set x as the temperature, click on the button next to x, select Global, and then select Temperature from the list and click on OK.




    The window then changes, showing the values of the dependent variable and the expression for the first refinement, as below.  Optionally, the values for m and b could be changed manually and the displayed value will be updated when Validate is pressed or after a short delay.



    6.      Press the OK button to save this parametric fit. The b and m values are then refined and the console shows the results:



    Also, the plot shows lines for the data values and the fit. Note that while the values are close to a straight line, the deviations are huge compared to the estimated uncertainty values for the unit cell volume (shown as very small error bars, unless the plot is magnified).  NB: If a linear relationship had been defined as a constraint (as can be done with other software packages) the unit cell parameters would have been forced onto this line with no obvious indication that this was not a correct result.


    Step 4: Fitting to a Pseudovariable


    Here we will look at how more sophisticated Python capabilities can be used to fit more complex behavior and use a pseudovariable 

    1.      Double-click on the b/c column. We will be trying to extract the critical exponent for this 2nd order phase transition from this data.


    2.      As a simple (but incorrect) example, we will now fit b/c as a function of form 1/(1 + exp[k(t-t0)]). Select the Parametric Fit/Add equation menu item (as before). For the dependent variable, click on Global and then select b/c. 


    3.      We then enter expression


    a + b / (1+np.exp(k*(t-t0)))


    Note that use of spaces here is arbitrary, but all parentheses are required. Also note that the exponential function [exp(x) or ex] is supplied in Python in the numpy modules as np.exp(). Further, there are also a small number of common functions that are defined without the prefix for convenience [these include pi, exp(), sqrt(), sind(), cosd(), tand() where d indicates an argument in degrees.]


    4.      Assign free variables to a, b, k and t0, similar as to what was done before. Note that the variable name for the b label was set automatically to b_1 because the symbol ‘b’ was used in the previous equation for fitting the volume. If the same variable name (b) was used again here, the same value would be used to fit both equations, which is certainly not what we want here.  Finally, since this equation is too complex to fit without some reasonable starting values, specify an approximate value, 120, for the vicinity of where the c lattice parameter starts to change (t0) and non-zero values for variables c and k.



    5.      Optionally, press the Fit button to see if the equation can be refined with starting values of zero for the other three parameters.  A fit is obtained and is plotted; the fit is quite poor.


    To give it more reasonable starting values, set a=1.0, b=0.1,k=0.1 and t0=120. Press Fit again; the plot shows a better (but not right) fit.


    6.      Press OK to add this equation to the parametric fits and both equations are refit. The fit is ok but this is not the correct expression for critical phenomena.



    If we look at the above plot, it appears there are two regions. We can treat the lower range as a power-law decay, Tk, and the upper region as linear (m*T + b).


    1.      As before, add an equation (or edit an existing one). For the dependent variable, select Global to bring up that selection of variables and select b/c and press OK.


    2.      Then enter the equation (you can copy and paste the equation above into the text box) as:


    a+((T0-T)/T0)**k if T < T0 else d+e*T


    3.      Assign the free and fixed variables, as shown below. Note: if no option under Phase for ‘Temperature’, then select variable type Global and select Global Temperature. Note that the exponent (k) and scaling factor (a), cannot start as zero; T0 should be set to 120.



    4.      Press the Fit button and note that all the free variables are adjusted




    Also, that the plotted result is close to the values from the refinement.




    This completes this tutorial on use of Pseudo Variables and parametric fitting.


    Advanced Use: Externally-Defined Functions


    The parametric refinement section of GSAS-II is intended to be quite flexible, but for very complex fitting, a user may wish to define a custom Python function. Such a function can be defined in a separate module that will be accessed with a parametric equation (or pseudo variable).


    As an example of that, if we create a file called and locate that file in any directory in the Python path (most simply, in the directory where the project file, SeqTut.gpx, is located) and place a routine fitfxn in that module:



    def fitfxn(T,T0,a,k,d,e):      

        if T < T0:                 

            return a+((T0-T)/T0)**k


             return d+e*T


               The function fittest.fitfxn can then be used to duplicate the previous example:



    Note that module fittest is automatically located and loaded, provided it is found in the path. No changes to the standard distributed GSAS-II code are needed. Care to avoid use of a module name already found in GSAS-II or Python is wise.