fitref.5 - Reflectivity Fitting for C-Plot

The following is a comprehensive set of notes describing how I wrote the original version of fitref.5.c.  I've included any relavent code to further explain how it was written.
-- Michael Kelley

4-27-98 fitref.5.c has been updated, the following web page has also been updated to reflect those changes.
-- Michael Kelley
6-19-98 Three functions have been added to fitref.5:  pr - Prefilter, po - Postfilter, and p - Print.  They are self explanatory functions, so I won't add them to this page.
-- Mike
6-15-05  Recent Adaptations have been made to the fitref function, in hopes that we may better fit our experimental data.
-- Clay Schumacher

 


This page is divided into the following sections:

  1. The Main Program - Not much to say here.
  2. The Setup Function - Prepares the program.
  3. The Parameters - How are the parameters created. (What do the parameters mean?)
  4. The Model - What is the model function and how is it used.
  5. The Added Functions - These are the functions I have added.
    1. Initialize Parameters
    2. Save Parameters
    3. Let (Set Parameter)
    4. Vary Parameter
    5. Print
    6. Postfilter
    7. Prefilter

The Main Program -
    First of all, I started with prototype.5.c found in the <cplothome>/prototypes directory.  MAXPAR was changed to 36.  I included cplx.h for complex functions (rquired for the reflectivity calculations) and stdio.h for the file input/output.  There were some independent variables not needed for this funciton (only one is necessary) so I deleted the others, leaving:

#define X  (M_flag? Make_x[0]:dp->d_xx[0])

    I also removed many of the comments as I didn't think they helped understand the program more, but just were programming aids.

4-27-98  Four constants have been added: firstP, lastP, firstC, lastC to correspond to the first/last index values of each parameter.


The Setup Function -
    This function sets the prompts:

setup() {

 set_prompt(0, "FitRef (? for help)");  /* Main prompt */
 set_prompt(1, "FitRef");  /* Command-file prompt */

    And then assigns addresses to the parameter pointers (see The Parameters):

/* These next arrays are pointers to the parameters to make user defined
   functions easier to write */

 parameterP[1]=&p1;
 parameterP[2]=&p2;
 parameterP[3]=&p3;     etc...


The Parameters -
    Next is the parameter list.  The 36 parameters are P(1) through P(28), and C(1) through C(8).  These are placed in the initial[] variable:

struct init_4 initial[] = {
       /* Name    Deriv? Fit?  Initial  Limit? Low  High */
        { "P(1)",   0,    0,       1,     0,    0,    0, },
        { "P(2)",   0,    1,       1,     1,  -88,   88, },
        { "P(3)",   0,    0,       1,     0,    0,    0, },
        { "P(4)",   0,    1,   0.334,     1,    0,    3, },
        { "P(5)",   0,    0,       0,     0,    0,    0, },
        { "P(6)",   0,    1,    17.5,     1,    0,  100, },
        { "P(7)",   0,    1,     .35,     1,    0,    3, },  etc.

(All these attributes are merely initial values and can be changed by the user, except for Deriv and Initial)

  • Deriv - 0 because the analytic-derivative is not supplied.
  • Fit - 0 for hold constant, 1 for variable.
  • Initial - The initial value for the parameter.
  • Limit - 0 for no limit, 1 for lower limit, 2 for upper limit, 3 for both.
  • Low - The lower limit.
  • High - The upper limit.

Next a mnemonic is assigned to the variables:

#define p1   fpar[0].p_b
#define fp1  fpar[0].p_fit
#define dp1  fpar[0].p_p

#define c1   fpar[28].p_b
#define fc1  fpar[28].p_fit
#define dc1  fpar[28].p_p

  • p# (or c#) - the value of the parameter.
  • fp# (or fc#) - the fit flag (0 for constant, 1 for variable).
  • dp# (or dc#) - the analytic-derivative flag.

After the definitions, two global pointer arrays are defined: *parameterP[29] and *parameterC[8].  In setup() these are assigned to the addresses of the parameters.  I believe doing this makes user functions easier to write and read, but that is up to you to decide (see The Setup Function);

 parameterP[1]=&p1;
 parameterP[2]=&p2;
 parameterP[3]=&p3;
 parameterP[4]=&p4;
 parameterP[5]=&p5;
... etc. ...
 parameterC[5]=&c5;
 parameterC[6]=&c6;
 parameterC[7]=&c7;

What do the parameters mean?


The Model -
    The model is the function to fit the data. model() returns the value of the model equation at the current point with the current parameters.

    My model() first declares two arrays, P and C, and lets these equal the parameters' current values:

 for (i=1; i<29; i++)
   P[i]=*parameterP[i];

 for (i=1; i<8; i++)
   C[i]=*parameterC[i];

    It then calculates the reflectivity using the function calculateReflec() which returns yfit, the value of the model.  X is the dependent variable from C-Plot:

 calculateReflec(P, C, X, &yfit);

calculateReflec() uses the same reflectivity function ref1.4.c uses.

4-27-98  The previous for statements now read:

 for (i=firstP; i<=lastP; i++)
   P[i]=*parameterP[i];

 for (i=firstC; i<=lastC; i++)
   C[i]=*parameterC[i];


The Added Functions -
    These functions are those that I have added to improve the functionality of C-Plot's fitting program.

    First the function prototypes are declared.  C-Plot requires they be type int, but the value returned means nothing:

int initParam();
int setParam();
int newhelp();
int saveParam();
int varyParam();
void toggle(int *param, char name[]);

    Next they are assigned a mnemonic in the structure user_cmds[]:

 struct  user_cmds {
     char    c_one;                  /* The first letter of the command */
     char    c_two;                  /* The second letter (or 0) */
     int     (*c_func)();            /* The name of the function */
} user_cmds[] = {
     { 'i', 'p', initParam },
     { 'l', 't', setParam },
     { '?', '\0', newhelp },
     { 'S', 'P', saveParam },
     { 'v', 'a', varyParam },
     0,                              /* must be terminated with a zero */
};

  • ip - Initialize Parameters:  Reads the parameters from a file.
  • SP - Save Parameters:  Saves the parameters to a file readable by ip.
  • lt - Let:  Changes the value of a parameter.
  • va - Vary Parameter:  Toggles the vary flag for a parameter.
  • ? - Displays a simple help screen.

NOTE:  toggle() is used in varyParam().


Initialize Parameters -
    Reads the parameters from a file.

    First the variables are declared:

  FILE *inf;
  char ch, dummy, PfileName[255]="defaultparameters.dat";
  int n;       /* The array index */
  float val;   /* The array value */

    Next the function get_snum() is called to get the name of the file to open.  get_snum() is decribed in the C-Plot User Manual, Chapter 12:

  get_snum("Name of the parameter file to load", PfileName);

    It opens the file (unless there is an error).  I will not describe the file processing algorithm here, but the file syntax can be found in parameters.dat.


Let (Set Parameter) -
    Changes the value of a parameter.

    Define variables:

  char *cmd = get_cmdbuf();  /* This is a pointer to the command line */
  char name[21]="\0", par;
  double val;
  int n, i, len;

    cmd is a pointer to the command buffer, the string the user typed in to run the function.  If this string is longer than 3 characters then the name of a parameter must have been entered (hopefully).  E.g., compare these two ways of acessing the function:"lt c[7]=2" and "lt".  If there is one, it copies the command line parameter (the string minus the first three characters 'l', 't', and ' ') and finishes it with a null zero:

  if ((len=strlen(cmd))>3) {

    for (i=3; i<=len && i<20; i++)
      name[i-3] = cmd[i];      /* Copy the cmdline to the name array */
    name[i+1]='\0';

  } else {

    Else the user must now supply the command string (what parameter to change and what to change it to).  Inspecting this code and knowing that get_snum() returns a zero if the value of name is unchanged (in this case from "i.e. C[7]=4.0") reveals that if nothing is entered at the prompt, the function quits:

    strcpy(name, "i.e. C[7]=4.0");

    printf("\nYou MUST use the following format:  p[n]=val or p(n)=val.");
    printf("\n      Where p is the parameter, n is the index number, and val is the value.");
    printf("\n      IMPORTANT:  Do not add any spaces or other characters.");

    if (!get_snum("\nCommand string", name)) {
      printf("\nNo values changed.\n");
      return 0;
    }
  }

    Next, the parameter, index number, and new value is read from the command string with sscanf():

  if (name[1]=='[') sscanf(name, "%c[%d]=%lf", &par, &n, &val);
  if (name[1]=='(') sscanf(name, "%c(%d)=%lf", &par, &n, &val);

    If the index value and parameter is correct, the parameter is changed:

  switch (par) {
  case 'P':                                           /* This is the parameter P */
    if (n>0 && n<29)
      *parameterP[n]=val;
    else
      printf("I don't recognize or use %s.  Reason: Wrong index value.\n", name);
    break;

  case 'C':
    if (n>0 && n<8)
      *parameterC[n]=val;
    else
      printf("I don't recognize or use %s.  Reason: Wrong index value.\n", name);
    break;

  default:
      printf("I don't recognize or use %s.  Reason: Unrecognizable parameter.\n", name);
      break;
    }

4-27-98  The previous if statements now read:
  case 'P':                                           /* This is the parameter P */
    if (n>=firstP && n<lastP)
...
 case 'C':
    if (n>=firstC && n<lastC)


Save Parameters -
    This saves the parameters to a file readable by ip and ref1.4.c

    Define variables:

  FILE * of;
  char *cmd = get_cmdbuf();  /* A pointer to the commmand buffer used to call the function */
  char fileName[255]="parameters.dat";
  int i;
  double P[30], C[10];

    The default value of "parameters.dat" is assigned to fileName.

    If the length of cmd > 3, a file name must have been typed in (see Let), use this one.  Otherwise, prompt the user with get_snum() (see C-Plot User Manual Chapter 12 for a description of this function):

  if (strlen(cmd)>3)  /* There must be a file name typed */
    sscanf(cmd, "SP %s", fileName);
  else
    get_snum("What is the file name to save parameters to", fileName);

    Open the file, and print the parameters.


Vary Parameters -
    va toggles a parameter from variable/constant to constant/variable.

    First define the variables as every good C programmer should:

  char *cmd = get_cmdbuf();  /* A pointer to the commmand buffer used to call the function */
  char name[21], par;
  int n, i, len;

    cmd is used the same way as in Save Parameters and Let.  If cmd > 3 assume there is a parameter to be changed in the command line.  Otherwise, prompt for one with get_snum() (which is found in the C-Plot User Manual Chapter 12):

 if ((len=strlen(cmd))>3) {   /* If length > 3 the user must have type a parameter into cmd */
    for (i=3; i<=len && i<20; i++)
      name[i-3] = cmd[i];      /* Copy the cmdline to the name array */
    name[i+1]='\0';

  } else {
    strcpy(name, "i.e. c(1)");

    printf("\nYou MUST use the following format:  p[n] or p(n).");
    printf("\n      Where n is a number and p is the parameter.");

    if (!get_snum("\nWhat is the parameter to toggle", name)) {
      printf("\nNo parameters toggled.\n");
      return 0;
    }
  }

    Get the parameter name and index value:

  name[0]=toupper(name[0]);

  if (name[1]=='[') sscanf(name, "%c[%d", &par, &n);
  if (name[1]=='(') sscanf(name, "%c(%d", &par, &n);

    Toggle its status using toggle():

  switch (par) {
  case 'P':                                           /* This is the parameter P */
      switch (n) {
        case 1: toggle(&fp1, name); break;
        case 2: toggle(&fp2, name); break;
        case 3: toggle(&fp3, name); break;

Toggle -
    A fairly simple function.  It makes param a pointer to the location of the fit flag (see The Parameters above) and changes it's truth:

void toggle(int *param, char name[])
{
  if (*param) {
    *param=0;
    printf("\n%s toggled from fit to constant.\n", name);
  } else {
    *param=1;
    printf("\n%s toggled from constant to fit.\n", name);
  }
}


Recent Adaptations -
Many new versions of fitref have been created in order to better fit new data. These are detailed below, see source for more documentation:

fitref12.5: This function was adapted from fitref7, which used a monolayer model, so that it would fit a bilayer model, such as Sphingomyelin.
fitref13.5: Added convolution to fitref12.
fitref14.5: Added a linear term to the window for convolution, and adapted the function to account for a non-uniform number of layers throughout the sample by making c[1] the "average" last layer, and then using two factors, alpha1 and alpha2, of the electron density of the average last layer, and the extra partial layer, to account for their incompletion.
fitref16.5: Abandoned the factors model for a more useful model with an independent section encompassing the last two bilayers.
fitref17.5: Added more interfaces to the bilayer, making a total of 7. These two new interfaces account for a lower density section of the chains due to their non-uniformity.
fitref18.5: A simplified model, with just two repeated interfaces, one for the chains, the other for head groups/water. This may help by providing approximations calculable from the parameters it returns, while fitting the data accurately and quickly (relatively speaking).
fitref19.5: A version of fitref18 adapted for a pure cholesterol model, where the interfaces are cholesterol and water, respectively. The first section (which represented the first head group) was removed, and it is recommended to vary P[2], the roughness between the subface and first interface.

Clay Schumacher


Michael S Kelley