73char *option[N_OPTIONS] = {
89 "Usage: sddsexpfit [<inputfile>] [<outputfile>]\n"
90 " -pipe=[input][,output]\n"
92 " -columns=<x-name>,<y-name>[,ySigma=<name>]\n"
93 " -tolerance=<value>\n"
94 " -verbosity=<integer>\n"
95 " -clue={grows|decays}\n"
96 " -guess=<constant>,<factor>,<rate>\n"
97 " -startValues=[constant=<value>][,factor=<value>][,rate=<value>]\n"
98 " -fixValue=[constant=<value>][,factor=<value>][,rate=<value>]\n"
100 " -limits=[evaluations=<number>][,passes=<number>]\n"
101 " -majorOrder=row|column\n\n"
102 "Performs an exponential fit of the form y = <constant> + <factor> * exp(<rate> * x).\n\n"
103 "Program by Michael Borland. (" __DATE__
" " __TIME__
", SVN revision: " SVN_VERSION
")\n";
105static double *xData, *yData, *syData;
107static double yMin, yMax, xMin, xMax;
110double fitFunction(
double *a,
long *invalid);
111void report(
double res,
double *a,
long pass,
long n_eval,
long n_dimen);
112void setupOutputFile(
SDDS_DATASET *OutputTable,
long *xIndex,
long *yIndex,
long *fitIndex,
long *residualIndex,
char *output,
long fullOutput,
SDDS_DATASET *InputTable,
char *xName,
char *yName,
short columnMajorOrder);
113char *makeInverseUnits(
char *units);
115#define START_CONSTANT_GIVEN 0x0001
116#define FIX_CONSTANT_GIVEN (0x0001 << 3)
117#define START_FACTOR_GIVEN 0x0002
118#define FIX_FACTOR_GIVEN (0x0002 << 3)
119#define START_RATE_GIVEN 0x0004
120#define FIX_RATE_GIVEN (0x0004 << 3)
124#define N_CLUE_TYPES 2
125char *clue_name[N_CLUE_TYPES] = {
132int main(
int argc,
char **argv) {
133 double tolerance, result, chiSqr, sigLevel;
134 int32_t nEvalMax = 5000, nPassMax = 100;
137 double alo[3], ahi[3];
138 long n_dimen = 3, guessGiven, startGiven;
141 long i_arg, clue, fullOutput;
143 char *input, *output, *xName, *yName, *syName;
144 long xIndex, yIndex, fitIndex, residualIndex, retval;
145 double *fitData, *residualData, rmsResidual;
146 unsigned long guessFlags, pipeFlags, dummyFlags, majorOrderFlag;
147 double constantStart, factorStart, rateStart;
148 short disable[3] = {0, 0, 0};
149 short autoOffset = 0;
150 short columnMajorOrder = -1;
153 argc =
scanargs(&s_arg, argc, argv);
154 if (argc < 2 || argc > (2 + N_OPTIONS))
157 input = output = NULL;
159 verbosity = fullOutput = guessGiven = startGiven = 0;
161 xName = yName = syName = NULL;
162 pipeFlags = guessFlags = 0;
163 constantStart = factorStart = rateStart = 0;
165 for (i_arg = 1; i_arg < argc; i_arg++) {
166 if (s_arg[i_arg].arg_type == OPTION) {
167 switch (
match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
168 case SET_MAJOR_ORDER:
170 s_arg[i_arg].n_items--;
171 if (s_arg[i_arg].n_items > 0 && (!
scanItemList(&majorOrderFlag, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
"row", -1, NULL, 0, SDDS_ROW_MAJOR_ORDER,
"column", -1, NULL, 0, SDDS_COLUMN_MAJOR_ORDER, NULL)))
172 SDDS_Bomb(
"invalid -majorOrder syntax/values");
173 if (majorOrderFlag & SDDS_COLUMN_MAJOR_ORDER)
174 columnMajorOrder = 1;
175 else if (majorOrderFlag & SDDS_ROW_MAJOR_ORDER)
176 columnMajorOrder = 0;
182 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1],
"%lf", &tolerance) != 1)
183 SDDS_Bomb(
"incorrect -tolerance syntax");
186 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1],
"%ld", &verbosity) != 1)
187 SDDS_Bomb(
"incorrect -verbosity syntax");
190 if (s_arg[i_arg].n_items != 2 || (clue =
match_string(s_arg[i_arg].list[1], clue_name, N_CLUE_TYPES, 0)) < 0)
195 SDDS_Bomb(
"can't have -startValues and -guess at once");
196 if (s_arg[i_arg].n_items != 4 || sscanf(s_arg[i_arg].list[1],
"%lf", guess + 0) != 1 || sscanf(s_arg[i_arg].list[2],
"%lf", guess + 1) != 1 || sscanf(s_arg[i_arg].list[3],
"%lf", guess + 2) != 1)
200 case SET_STARTVALUES:
201 if (s_arg[i_arg].n_items < 2)
202 SDDS_Bomb(
"incorrect -startValues syntax");
204 SDDS_Bomb(
"can't have -startValues and -guess at once");
205 s_arg[i_arg].n_items -= 1;
206 dummyFlags = guessFlags;
207 if (!
scanItemList(&guessFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
"constant",
SDDS_DOUBLE, &constantStart, 1, START_CONSTANT_GIVEN,
"factor",
SDDS_DOUBLE, &factorStart, 1, START_FACTOR_GIVEN,
"rate",
SDDS_DOUBLE, &rateStart, 1, START_RATE_GIVEN, NULL))
209 if ((dummyFlags >> 3) & (guessFlags))
210 SDDS_Bomb(
"can't have -fixValue and -startValue for the same item");
211 guessFlags |= dummyFlags;
215 if (s_arg[i_arg].n_items < 2)
217 s_arg[i_arg].n_items -= 1;
218 dummyFlags = guessFlags;
219 if (!
scanItemList(&guessFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
"constant",
SDDS_DOUBLE, &constantStart, 1, FIX_CONSTANT_GIVEN,
"factor",
SDDS_DOUBLE, &factorStart, 1, FIX_FACTOR_GIVEN,
"rate",
SDDS_DOUBLE, &rateStart, 1, FIX_RATE_GIVEN, NULL))
221 if ((dummyFlags) & (guessFlags >> 3))
222 SDDS_Bomb(
"can't have -fixValue and -startValue for the same item");
223 guessFlags |= dummyFlags;
226 if (s_arg[i_arg].n_items != 3 && s_arg[i_arg].n_items != 4)
228 xName = s_arg[i_arg].list[1];
229 yName = s_arg[i_arg].list[2];
230 s_arg[i_arg].n_items -= 3;
231 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 3, &s_arg[i_arg].n_items, 0,
"ysigma",
SDDS_STRING, &syName, 1, 0, NULL))
238 if (!
processPipeOption(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &pipeFlags))
242 if (s_arg[i_arg].n_items < 2)
244 s_arg[i_arg].n_items -= 1;
245 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
"evaluations",
SDDS_LONG, &nEvalMax, 1, 0,
"passes",
SDDS_LONG, &nPassMax, 1, 0, NULL) || nEvalMax <= 0 || nPassMax <= 0)
249 fprintf(stderr,
"error: unknown/ambiguous option: %s\n", s_arg[i_arg].list[0]);
255 input = s_arg[i_arg].list[0];
256 else if (output == NULL)
257 output = s_arg[i_arg].list[0];
265 for (i = 0; i < 3; i++) {
266 if ((guessFlags >> 3) & (1 << i)) {
271 if (!xName || !yName)
272 SDDS_Bomb(
"-columns option must be given");
278 setupOutputFile(&OutputTable, &xIndex, &yIndex, &fitIndex, &residualIndex, output, fullOutput, &InputTable, xName, yName, columnMajorOrder);
281 fitData = residualData = NULL;
282 xData = yData = syData = NULL;
290 if (xData[0] > xData[nData - 1])
291 fputs(
"warning: data reverse-ordered", stderr);
295 for (i = 0; i < nData; i++)
302 if (clue == CLUE_GROWS) {
303 a[0] = 0.9 * yData[0];
304 a[1] = yData[nData - 1] - yData[0];
305 a[2] = 1 / (xData[nData - 1] - xData[0]);
311 }
else if (clue == CLUE_DECAYS) {
312 a[0] = 0.9 * yData[nData - 1];
313 a[1] = yData[0] - yData[nData - 1];
331 if (guessFlags & (START_CONSTANT_GIVEN + FIX_CONSTANT_GIVEN))
332 a[0] = constantStart;
333 if (guessFlags & (START_FACTOR_GIVEN + FIX_FACTOR_GIVEN))
335 if (guessFlags & (START_RATE_GIVEN + FIX_RATE_GIVEN))
338 da[0] = da[1] = fabs(a[1] - a[0]) / 20.0;
339 da[2] = 0.1 / (xData[nData - 1] - xData[0]);
341 fprintf(stderr,
"starting guess: %e, %e, %e\n", a[0], a[1], a[2]);
343 simplexMin(&result, a, da, alo, ahi, disable, n_dimen, -DBL_MAX, tolerance, fitFunction, (verbosity > 0 ? report : NULL), nEvalMax, nPassMax, 12, 3, 1.0, 0);
348 simplexMin(&result, a, da, alo, ahi, disable, n_dimen, -DBL_MAX, tolerance, fitFunction, (verbosity > 0 ? report : NULL), nEvalMax, nPassMax, 12, 3, 1.0, 0);
352 a[1] *= exp(-a[2] * xMin);
353 for (i = 0; i < nData; i++)
357 fitData =
trealloc(fitData,
sizeof(*fitData) * nData);
358 residualData =
trealloc(residualData,
sizeof(*residualData) * nData);
359 for (i = result = 0; i < nData; i++) {
360 fitData[i] = a[0] + a[1] * exp(a[2] * xData[i]);
361 residualData[i] = yData[i] - fitData[i];
362 result += sqr(residualData[i]);
364 rmsResidual = sqrt(result / nData);
366 for (i = chiSqr = 0; i < nData; i++)
367 chiSqr += sqr(residualData[i] / syData[i]);
370 sy2 = result / (nData - 3);
371 for (i = chiSqr = 0; i < nData; i++)
372 chiSqr += sqr(residualData[i]) / sy2;
376 fprintf(stderr,
"RMS deviation: %.15e\n", rmsResidual);
377 fprintf(stderr,
"(RMS deviation)/(largest value): %.15e\n", rmsResidual / MAX(fabs(yMin), fabs(yMax)));
379 fprintf(stderr,
"Significance level: %.5e\n", sigLevel);
382 fprintf(stderr,
"coefficients of fit to the form y = a0 + a1*exp(a2*x), a = \n");
383 for (i = 0; i < 3; i++)
384 fprintf(stderr,
"%.8e ", a[i]);
385 fprintf(stderr,
"\n");
390 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, xData, nData, xIndex) ||
391 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, fitData, nData, fitIndex) ||
393 "expfitConstant", a[0],
394 "expfitFactor", a[1],
396 "expfitRmsResidual", rmsResidual,
397 "expfitSigLevel", sigLevel, NULL) ||
398 (fullOutput && (!
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, yData, nData, yIndex) ||
399 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, residualData, nData, residualIndex))) ||
420void setupOutputFile(
SDDS_DATASET *OutputTable,
long *xIndex,
long *yIndex,
long *fitIndex,
long *residualIndex,
char *output,
long fullOutput,
SDDS_DATASET *InputTable,
char *xName,
char *yName,
short columnMajorOrder) {
421 char *name, *yUnits, *description, *xUnits, *inverse_xUnits;
423 static char *residualNamePart =
"Residual";
424 static char *residualDescriptionPart =
"Residual of exponential fit to ";
436 if (columnMajorOrder != -1)
437 OutputTable->layout.data_mode.column_major = columnMajorOrder;
439 OutputTable->layout.data_mode.column_major = InputTable->layout.data_mode.column_major;
441 name =
tmalloc(
sizeof(*name) * (strlen(yName) + strlen(residualNamePart) + 1));
442 description =
tmalloc(
sizeof(*name) * (strlen(yName) + strlen(residualDescriptionPart) + 1));
449 sprintf(name,
"%s%s", yName, residualNamePart);
450 sprintf(description,
"%s%s", yName, residualDescriptionPart);
455 sprintf(name,
"%sFit", yName);
456 sprintf(description,
"Exponential fit to %s", yName);
460 inverse_xUnits = makeInverseUnits(xUnits);
462 if (
SDDS_DefineParameter(OutputTable,
"expfitConstant", NULL, yUnits,
"Constant term from exponential fit",
466 SDDS_DefineParameter(OutputTable,
"expfitRate", NULL, inverse_xUnits,
"Rate from exponential fit",
468 SDDS_DefineParameter(OutputTable,
"expfitRmsResidual", NULL, yUnits,
"RMS residual from exponential fit",
476char *makeInverseUnits(
char *units) {
481 inverseUnits =
tmalloc(
sizeof(*inverseUnits) * (strlen(units) + 5));
483 if (strncmp(units,
"1/(", 3) == 0 && units[strlen(units) - 1] ==
')') {
485 strcpy(inverseUnits, units + 3);
486 inverseUnits[strlen(inverseUnits) - 1] =
'\0';
487 }
else if (!strchr(units,
' ')) {
489 sprintf(inverseUnits,
"1/%s", units);
492 sprintf(inverseUnits,
"1/(%s)", units);
498double fitFunction(
double *a,
long *invalid) {
500 double chi = 0.0, diff;
501 static double min_chi;
506 for (i = 0; i < nData; i++) {
507 diff = yData[i] - (a[0] + a[1] * exp(a[2] * xData[i]));
512 if (isnan(chi) || isinf(chi))
515 fprintf(stderr,
"trial: a = %e, %e, %e --> chi = %e, invalid = %ld\n", a[0], a[1], a[2], chi, *invalid);
522 fprintf(stderr,
"new best chi = %e: a = %e, %e, %e\n", chi, fit[0], fit[1], fit[2]);
527void report(
double y,
double *x,
long pass,
long nEval,
long n_dimen) {
530 fprintf(stderr,
"Pass %ld, after %ld evaluations: result = %.16e\na = ", pass, nEval, y);
531 for (i = 0; i < n_dimen; i++)
532 fprintf(stderr,
"%.8e ", x[i]);
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_CopyParameters(SDDS_DATASET *SDDS_target, SDDS_DATASET *SDDS_source)
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int32_t SDDS_SetParameters(SDDS_DATASET *SDDS_dataset, int32_t mode,...)
int32_t SDDS_SetColumn(SDDS_DATASET *SDDS_dataset, int32_t mode, void *data, int64_t rows,...)
Sets the values for one data column in the current data table of an SDDS dataset.
int32_t SDDS_ChangeColumnInformation(SDDS_DATASET *SDDS_dataset, char *field_name, void *memory, int32_t mode,...)
Modifies a specific field in a column definition within the SDDS dataset.
int32_t SDDS_GetColumnInformation(SDDS_DATASET *SDDS_dataset, char *field_name, void *memory, int32_t mode,...)
Retrieves information about a specified column in the SDDS dataset.
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_DefineColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *symbol, const char *units, const char *description, const char *format_string, int32_t type, int32_t field_length)
Defines a data column within the SDDS dataset.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
int32_t SDDS_DefineParameter(SDDS_DATASET *SDDS_dataset, const char *name, const char *symbol, const char *units, const char *description, const char *format_string, int32_t type, char *fixed_value)
Defines a data parameter with a fixed string value.
int32_t SDDS_TransferColumnDefinition(SDDS_DATASET *target, SDDS_DATASET *source, char *name, char *newName)
Transfers a column definition from a source dataset to a target dataset.
int32_t SDDS_TransferAllParameterDefinitions(SDDS_DATASET *SDDS_target, SDDS_DATASET *SDDS_source, uint32_t mode)
Transfers all parameter definitions from a source dataset to a target dataset.
int32_t SDDS_GetColumnIndex(SDDS_DATASET *SDDS_dataset, char *name)
Retrieves the index of a named column in the SDDS dataset.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
void SDDS_RegisterProgramName(const char *name)
Registers the executable program name for use in error messages.
int32_t SDDS_StringIsBlank(char *s)
Checks if a string is blank (contains only whitespace characters).
void SDDS_Bomb(char *message)
Terminates the program after printing an error message and recorded errors.
#define SDDS_STRING
Identifier for the string data type.
#define SDDS_LONG
Identifier for the signed 32-bit integer data type.
#define SDDS_DOUBLE
Identifier for the double data type.
void * trealloc(void *old_ptr, uint64_t size_of_block)
Reallocates a memory block to a new size.
void * tmalloc(uint64_t size_of_block)
Allocates a memory block of the specified size with zero initialization.
void bomb(char *error, char *usage)
Reports error messages to the terminal and aborts the program.
void fill_double_array(double *array, long n, double value)
Fills a double array with the specified value.
int find_min_max(double *min, double *max, double *list, int64_t n)
Finds the minimum and maximum values in a list of doubles.
long match_string(char *string, char **option, long n_options, long mode)
Matches a given string against an array of option strings based on specified modes.
int scanargs(SCANNED_ARG **scanned, int argc, char **argv)
long processPipeOption(char **item, long items, unsigned long *flags)
void processFilenames(char *programName, char **input, char **output, unsigned long pipeFlags, long noWarnings, long *tmpOutputUsed)
long scanItemList(unsigned long *flags, char **item, long *items, unsigned long mode,...)
Scans a list of items and assigns values based on provided keywords and types.
double ChiSqrSigLevel(double ChiSquared0, long nu)
Computes the probability that a chi-squared variable exceeds a given value.
long simplexMin(double *yReturn, double *xGuess, double *dxGuess, double *xLowerLimit, double *xUpperLimit, short *disable, long dimensions, double target, double tolerance, double(*func)(double *x, long *invalid), void(*report)(double ymin, double *xmin, long pass, long evals, long dims), long maxEvaluations, long maxPasses, long maxDivisions, double divisorFactor, double passRangeFactor, unsigned long flags)
Top-level convenience function for simplex-based minimization.