/* @(#)p_proto.5.c 4.4 02 Jul 1993 CSS * Certified Scientific Software's C-PLOT * * Type 5 User-Function Prototype for Nonlinear Least-Squares Fitting * See below for some programming aids. * Sample code for a fit to a straight line is illustrated. * */ #define VERSION 5 /** Adjust the following 4 parameters for the size of your problem. **/ #define NUM_XS 1 /* The number of independent variables (no limit) */ #define MAXPAR 10 /* The number of parameters */ #define MAXPTS 2048 /* The most points for fitting */ #define MAXPLT 2048 /* In-core points for plot (pg, ps, md, mr and sA) */ #define firstP 0 #define lastP 9 #include #include #include /** * The title is printed when the fit process starts and with the `V' command. **/ char *title = "Fit to a simple polynomial: P(0)*x^0 + P(1)*x^1 + P(2)*x^2 + P(3)*x^3 ..."; /** * The comment is added to the sF output file. **/ char *comment = ""; /** * Indicate for each parameter: * o The parameter name, * o Whether you provide the analytic-derivative (1=yes, 0=no), * o Whether to fit (as the initial default), * o The default initial value of the parameter. * These next three are optional, and can be modified while running: * o Whether to constrain the parameter within limits. If set to 1, * constrain lower; if 2, constrain upper; if 3, constrain both. * o The lower constraint * o The upper constraint **/ struct init_4 initial[] = { /* Name Deriv? Fit? Initial Limit? Low High */ { "P(0)", 0, 0, 1.0, 0, 0, 0, }, { "P(1)", 0, 1, 0.0, 0, 0, 0, }, { "P(2)", 0, 1, 0.0, 0, 0, 0, }, { "P(3)", 0, 0, 0.0, 0, 0, 0, }, { "P(4)", 0, 0, 0.0, 0, 0, 0, }, { "P(5)", 0, 0, 0.0, 0, 0, 0, }, { "P(6)", 0, 0, 0.0, 0, 0, 0, }, { "P(7)", 0, 0, 0.0, 0, 0, 0, }, { "P(8)", 0, 0, 0.0, 0, 0, 0, }, { "P(9)", 0, 0, 0.0, 0, 0, 0, }, }; /** * Refer to the parameters using the following structure members: * * fpar[i].p_b - The current value of the parameter. * fpar[i].p_fit - When non-zero, the parameter is being fit. * fpar[i].p_p - Assign analytic derivative to this. * * `p_fit' and `p_p' are only relevant for parameters that have the * analytic-derivative flag set. * The following `defines' may be useful. **/ #define p0 fpar[0].p_b #define fp0 fpar[0].p_fit #define dp0 fpar[0].p_p #define p1 fpar[1].p_b #define fp1 fpar[1].p_fit #define dp1 fpar[1].p_p #define p2 fpar[2].p_b #define fp2 fpar[2].p_fit #define dp2 fpar[2].p_p #define p3 fpar[3].p_b #define fp3 fpar[3].p_fit #define dp3 fpar[3].p_p #define p4 fpar[4].p_b #define fp4 fpar[4].p_fit #define dp4 fpar[4].p_p #define p5 fpar[5].p_b #define fp5 fpar[5].p_fit #define dp5 fpar[5].p_p #define p6 fpar[6].p_b #define fp6 fpar[6].p_fit #define dp6 fpar[6].p_p #define p7 fpar[7].p_b #define fp7 fpar[7].p_fit #define dp7 fpar[7].p_p #define p8 fpar[8].p_b #define fp8 fpar[8].p_fit #define dp8 fpar[8].p_p #define p9 fpar[9].p_b #define fp9 fpar[9].p_fit #define dp9 fpar[9].p_p double *parameterP[lastP+1]; /** * setup() is called once when the fit process starts. * Use it for one-time-only code. **/ setup() { /* * Shown is optional user initialization of prompts. * Second argument must point to static storage. */ set_prompt(0, "POLY"); /* Main prompt */ set_prompt(1, "poly"); /* Command-file prompt */ /* These next arrays are pointers to the parameters to make user defined functions easier to write (you decide) !!!--!!!*/ parameterP[0]=&p0; parameterP[1]=&p1; parameterP[2]=&p2; parameterP[3]=&p3; parameterP[4]=&p4; parameterP[5]=&p5; parameterP[6]=&p6; parameterP[7]=&p7; parameterP[8]=&p8; parameterP[9]=&p9; } /** * In the model equation, you obtain the value of the current point * with the following expressions for the independent variables. * (`M_flag' is 1 when making data, 0 when fitting.) **/ #define X (M_flag? Make_x[0]:dp->d_xx[0]) /** or for multiple independent variables **/ #define X1 (M_flag? Make_x[0]:dp->d_xx[0]) #define X2 (M_flag? Make_x[1]:dp->d_xx[1]) #define X3 (M_flag? Make_x[2]:dp->d_xx[2]) /** etc. **/ /* * model() returns the value of the model equation at the current * point using the current parameters. If `deriv_flag' is nonzero, * model() must also calculate the analytic derivative of the model * equation with respect to each parameter that has the `fpar[i].p_fit' * flag set. */ double model(deriv_flag) int deriv_flag; { double x, yfit, i; x = X; yfit = 0.0; for(i = firstP; i <= lastP; i++){ yfit += *parameterP[(int)i] * pow(x,i); } /* *if (deriv_flag) { * if (fp0) * dCONST = 1; * if (fp1) * dLINEAR = x; *} */ return(yfit); } /** * You may add new commands to the fit program here. Simply add a line * to the structure initializations for each new command and write the * routine. You will be warned at run time if your command uses the * same mnemonic as one of the built-in commands. * Use 0 or '\0' for the second letter of a one-letter mneumonic. * The examples shown can be eliminated, but the structure must remain * with at least one entry (the one containing a zero). **/ int prefilter(), postfilter(), setParam(), saveParam(), initParam(), varyParam(); void toggle(); 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[] = { { 'p', 'o', postfilter }, { 'p', 'r', prefilter }, { 'l', 't', setParam }, { 'S', 'P', saveParam }, { 'i', 'p', initParam }, { 'v', 'a', varyParam }, 0, /* must be terminated with a zero */ }; /*User commands are entered below! !!!--!!!*/ prefilter() { /** * (Here is how to loop through all the data) * register int i; * register struct f_data *d; * * for (d = f_data, i = 0; i < npts; i++, INC(d)) { * d->d_xx[0] = (independent variable) * d->d_xx[1] = (optional 2nd independent variable) * d->d_xx[2] = (and etc.) * d->d_y = (dependent variable) * d->d_w2 = (square of weight for this point) * } **/ } postfilter() { } /*********************************************************************************/ int setParam() { char *cmd = get_cmdbuf(); /* This is a pointer to the command line */ char name[21]="\0", par; double val; int n, i, len; 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[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; } } name[0] = toupper(name[0]); if (name[1]=='[') sscanf(name, "%c[%d]=%lf", &par, &n, &val); if (name[1]=='(') sscanf(name, "%c(%d)=%lf", &par, &n, &val); switch (par) { case 'P': /* This is the parameter P */ if (n>=firstP && n<=lastP) *parameterP[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; } printf("\n%c[%d]=%lf\n", par, n, val); return 0; } /*********************************************************************************/ int saveParam() { 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[10]; 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); of = fopen(fileName, "w"); if (!of) { printf("There was an error opening the file %s!\n", fileName); return 0; } fprintf(of, "# This file (%s) created by fitpoly.5\n\n", fileName); fprintf(of, "# Begin parameter list\n\n"); for (i=firstP; i<=lastP; i++) fprintf(of, "P[%d]=%lf\n", i, *parameterP[i]); fprintf(of, "\n# End parameter list\n\n# End file\n"); fclose(of); printf("Parameters written to file: %s\n", fileName); return 0; } /*********************************************************************************/ int initParam() { FILE *inf; char *cmd = get_cmdbuf(); char ch, dummy, PfileName[255]="defaultparameters.dat"; int n; /* The array index */ float val; /* The array value */ if (strlen(cmd)>3) /* There must be a file name typed */ sscanf(cmd, "ip %s", PfileName); else get_snum("Name of the parameter file to load", PfileName); inf = fopen(PfileName, "r"); if (!inf) { fprintf(stderr,"There was an error opening the file %s!\n", PfileName); return 0; } do { ch=fgetc(inf); switch (toupper(ch)) { case '#': /* Ignore comment lines */ do { dummy=fgetc(inf); } while (dummy!='\n' && dummy!=EOF); break; case 'P': /* This is the parameter P */ while (fgetc(inf)!='['); /* Skip all characters until a [ is reached */ fscanf(inf, "%d", &n); /* The array index */ while (fgetc(inf)!='='); /* Skip all characters until an = is reached */ fscanf(inf, "%f", &val); /* The array value */ if (n>=firstP && n<=lastP) *parameterP[n]=val; break; default: /* Ignore any other characters */ break; } } while (ch!=EOF); /* Continue until the end of file marker */ fclose(inf); printf("Parameters read from file: %s\n", PfileName); return 0; } /*********************************************************************************/ int varyParam() { char *cmd = get_cmdbuf(); /* A pointer to the commmand buffer used to call the function */ char name[21], par; int n, i, len; 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; } } name[0]=toupper(name[0]); if (name[1]=='[') sscanf(name, "%c[%d", &par, &n); if (name[1]=='(') sscanf(name, "%c(%d", &par, &n); /* There needs to be a case statement for every parameter (i.e. case 0 corresponds to P(0) !!!--!!! */ switch (par) { case 'P': /* This is the parameter P */ switch (n) { case 0: toggle(&fp0, name); break; case 1: toggle(&fp1, name); break; case 2: toggle(&fp2, name); break; case 3: toggle(&fp3, name); break; case 4: toggle(&fp4, name); break; case 5: toggle(&fp5, name); break; case 6: toggle(&fp6, name); break; case 7: toggle(&fp7, name); break; case 8: toggle(&fp8, name); break; case 9: toggle(&fp9, name); break; default: printf("I don't recognize or use %s. Reason: Wrong index value.\n", name); break; } break; default: printf("I don't recognize or use %s. Reason: Unrecognizable parameter.\n", name); break; } return 0; } /*********************************************************************************/ 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); } } /*** PROGRAMMING AIDS (You can remove from here to the end of the file) -+- Quiet mode: The integer `quiet_flag' is set when plot's `zq' mode for no echoing during graphics filter is in effect. To prevent output while quiet mode is on use: if (!quiet_flag) printf("It is safe to write to the screen now.\n"); or use the built-in function msg() which checks quiet mode for you. msg("This appears only when quiet mode is off.\n"); The functions msg() works just as printf() in other respects. -+- Functions for getting input from the user that work with command files: get_dnum(prompt, num) input a double from the user. char *prompt; double *num; get_inum(prompt, num) input an integer from the user. char *prompt; int *num; get_snum(prompt, num) input a string from the user. char *prompt, *num; If `prompt' is nonzero, the string it points to is printed along with the current value of the thing pointed to by `num'. A line of text is then read from the keyboard (or command file) and scanned for something to stuff into the location pointed to by `num'. If no appropriate thing is found on the line of text, the contents of `num' remain unchanged. For example, double temp = 98.6; get_dnum("What is the temperature", &temp); produces What is the temperature (98.6)? The user may enter a new value or simply hit return. The return values of these functions are as follows: 1 means the user simply hit return (`*num' is unchanged). 0 means the user typed some input. -1 means end-of-file on the input (the user hit a <^D>). (Also the extern variable `eofflag' is set nonzero.) yesno(prompt) check for affirmative response. char *prompt; If `prompt' is nonzero, the string it points to is printed. The function returns 1 if the input from the keyboard (or command file) begins with `y', `Y', or `1'. If nothing is entered `yesno()' returns 0. For anything else, -1 is returned. Check if `eofflag' is set to distinguish ^D or EOF. For example, if (yesno("Normalize the data (YES)") >= 0) { -- normalize the data -- } if (eofflag) return; For user added commands, you can examine the characters the user typed by retrieving a pointer to the command buffer using the function: char *get_cmdbuf(); ***/