84#define EXPONENTIAL_DECAY 0
85#define EXPONENTIAL_GROW 1
86#define EXPONENTIAL_OPTIONS 2
87static char *expotentialOptions[EXPONENTIAL_OPTIONS] = {
108char *option[N_OPTIONS] = {
124 "sddssinefit [<inputfile>] [<outputfile>] \n"
125 " [-pipe=<input>[,<output>]]\n"
127 " [-columns=<x-name>,<y-name>]\n"
128 " [-tolerance=<value>]\n"
129 " [-limits=evaluations=<number>,passes=<number>]\n"
130 " [-verbosity=<integer>]\n"
131 " [-guess=constant=<constant>,factor=<factor>,frequency=<freq>,phase=<phase>,slope=<slope>,rate=<value>]\n"
132 " [-lockFrequency]\n"
133 " [-addSlope] [-addExponential={grow|decay}\n"
134 " [-majorOrder=row|column]\n\n"
136 " Performs a sinusoidal fit of the form:\n"
137 " y = <constant> + <factor>*sin(2*PI*<freq>*x + <phase>)\n"
138 " y = <constant> + <factor>*sin(2*PI*<freq>*x + <phase>) + <slope>*x\n\n"
140 " y = <constant> + <factor>*sin(2*PI*<freq>*x + <phase>)*exp(<rate>*x) + <slope>*x\n\n"
142 " <inputfile> : Path to the input SDDS file.\n"
143 " <outputfile> : Path to the output SDDS file.\n"
144 " -pipe=<input>,<output> : Use standard input/output for data streams.\n"
145 " -fulloutput : Include full output with residuals.\n"
146 " -columns=<x-name>,<y-name> : Specify the names of the x and y data columns.\n"
147 " -tolerance=<value> : Set the tolerance for the fitting algorithm (default: 1e-6).\n"
148 " -limits=evaluations=<n>,passes=<m> : Set maximum number of evaluations and passes (default: 5000 evaluations, 25 passes).\n"
149 " -verbosity=<integer> : Set verbosity level (default: 0).\n"
150 " -guess=constant=<c>,factor=<f>,frequency=<freq>,phase=<p>,slope=<s>,rate=<r> : Provide initial guesses for fit parameters.\n"
151 " -lockFrequency : Lock the frequency parameter during fitting.\n"
152 " -addSlope : Include a slope term in the fit.\n"
153 " -addExponential : Include exponential decay of the sinusoid.\n"
154 " -majorOrder=row|column : Specify the major order for data processing.\n\n"
157 " (" __DATE__
" " __TIME__
", SVN revision: " SVN_VERSION
")\n";
159static double *xData, *yData;
161static double yMin, yMax, xMin, xMax;
163static short disable[6] = {0,0,0,0,1,1};
164static short addSlope, addExponential;
165static double *fitData, *residualData, rmsResidual;
167double fitFunction(
double *a,
long *invalid);
168void report(
double res,
double *a,
long pass,
long n_eval,
long n_dimen);
169void setupOutputFile(
SDDS_DATASET *OutputTable, int32_t *xIndex, int32_t *yIndex, int32_t *fitIndex,
170 int32_t *residualIndex,
char *output,
long fullOutput,
SDDS_DATASET *InputTable,
171 char *xName,
char *yName,
short columnMajorOrder,
short addSlope,
short addExponential);
172char *makeInverseUnits(
char *units);
176#define GUESS_CONSTANT_GIVEN 0x0001
177#define GUESS_FACTOR_GIVEN 0x0002
178#define GUESS_FREQ_GIVEN 0x0004
179#define GUESS_PHASE_GIVEN 0x0008
180#define GUESS_SLOPE_GIVEN 0x0010
181#define GUESS_RATE_GIVEN 0x0020
183int main(
int argc,
char **argv) {
184 double tolerance, result;
185 int32_t nEvalMax = 5000, nPassMax = 25;
187 double alo[6], ahi[6];
191 long i_arg, fullOutput;
193 char *input, *output, *xName, *yName;
194 int32_t xIndex, yIndex, fitIndex, residualIndex;
196 unsigned long guessFlags, dummyFlags, pipeFlags;
197 double constantGuess, factorGuess, freqGuess, phaseGuess, slopeGuess, rateGuess;
198 double firstZero, lastZero;
199 unsigned long simplexFlags = 0, majorOrderFlag;
200 short columnMajorOrder = -1;
204 argc =
scanargs(&s_arg, argc, argv);
205 if (argc < 2 || argc > (2 + N_OPTIONS))
208 input = output = NULL;
210 verbosity = fullOutput = 0;
211 xName = yName = NULL;
214 constantGuess = factorGuess = freqGuess = phaseGuess = slopeGuess = rateGuess = 0;
216 for (i_arg = 1; i_arg < argc; i_arg++) {
217 if (s_arg[i_arg].arg_type == OPTION) {
218 switch (
match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
219 case SET_MAJOR_ORDER:
221 s_arg[i_arg].n_items--;
222 if (s_arg[i_arg].n_items > 0 &&
223 (!
scanItemList(&majorOrderFlag, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
224 "row", -1, NULL, 0, SDDS_ROW_MAJOR_ORDER,
225 "column", -1, NULL, 0, SDDS_COLUMN_MAJOR_ORDER, NULL)))
226 SDDS_Bomb(
"invalid -majorOrder syntax/values");
227 if (majorOrderFlag & SDDS_COLUMN_MAJOR_ORDER)
228 columnMajorOrder = 1;
229 else if (majorOrderFlag & SDDS_ROW_MAJOR_ORDER)
230 columnMajorOrder = 0;
233 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1],
"%lf", &tolerance) != 1)
234 SDDS_Bomb(
"incorrect -tolerance syntax");
237 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1],
"%ld", &verbosity) != 1)
238 SDDS_Bomb(
"incorrect -verbosity syntax");
241 if (s_arg[i_arg].n_items < 2)
243 s_arg[i_arg].n_items -= 1;
244 if (!
scanItemList(&guessFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
245 "constant",
SDDS_DOUBLE, &constantGuess, 1, GUESS_CONSTANT_GIVEN,
246 "factor",
SDDS_DOUBLE, &factorGuess, 1, GUESS_FACTOR_GIVEN,
247 "frequency",
SDDS_DOUBLE, &freqGuess, 1, GUESS_FREQ_GIVEN,
248 "phase",
SDDS_DOUBLE, &phaseGuess, 1, GUESS_PHASE_GIVEN,
249 "slope",
SDDS_DOUBLE, &slopeGuess, 1, GUESS_SLOPE_GIVEN,
250 "rate",
SDDS_DOUBLE, &rateGuess, 1, GUESS_RATE_GIVEN, NULL,
255 if (s_arg[i_arg].n_items != 3)
257 xName = s_arg[i_arg].list[1];
258 yName = s_arg[i_arg].list[2];
269 case SET_ADD_EXPONENTIAL:
270 if (s_arg[i_arg].n_items!=2)
271 SDDS_Bomb(
"incorrect -addExponential syntax");
272 switch (
match_string(s_arg[i_arg].list[1], expotentialOptions, EXPONENTIAL_OPTIONS, 0)) {
273 case EXPONENTIAL_DECAY:
276 case EXPONENTIAL_GROW:
280 SDDS_Bomb(
"incorrect -addExponential syntax");
285 if (s_arg[i_arg].n_items < 2)
287 s_arg[i_arg].n_items -= 1;
288 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
289 "evaluations",
SDDS_LONG, &nEvalMax, 1, 0,
290 "passes",
SDDS_LONG, &nPassMax, 1, 0, NULL) ||
291 nEvalMax <= 0 || nPassMax <= 0)
295 if (!
processPipeOption(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &pipeFlags))
299 fprintf(stderr,
"Error: Unknown or ambiguous option: %s\n", s_arg[i_arg].list[0]);
305 input = s_arg[i_arg].list[0];
306 else if (output == NULL)
307 output = s_arg[i_arg].list[0];
309 SDDS_Bomb(
"Too many filenames provided.");
318 if ((guessFlags & GUESS_SLOPE_GIVEN) && !addSlope)
319 SDDS_Bomb(
"-guess=slope given but -addSlope not given");
321 if ((guessFlags & GUESS_RATE_GIVEN) && !addExponential)
322 SDDS_Bomb(
"-guess=rate given but -addExponential not given");
326 if (!xName || !yName)
327 SDDS_Bomb(
"-columns option must be specified.");
334 setupOutputFile(&OutputTable, &xIndex, &yIndex, &fitIndex, &residualIndex, output, fullOutput,
335 &InputTable, xName, yName, columnMajorOrder, addSlope, addExponential);
337 fitData = residualData = NULL;
339 alo[0] = -(ahi[0] = DBL_MAX);
341 ahi[1] = ahi[2] = DBL_MAX;
342 alo[3] = -(ahi[3] = PIx2);
344 alo[4] = -(ahi[4] = DBL_MAX);
346 alo[5] = -(ahi[5] = DBL_MAX);
348 firstZero = lastZero = 0;
350 simplexFlags = SIMPLEX_VERBOSE_LEVEL1;
359 fitData =
SDDS_Realloc(fitData,
sizeof(*fitData) * nData);
360 residualData =
SDDS_Realloc(residualData,
sizeof(*residualData) * nData);
365 for (i = 1; i < nData; i++)
366 if (yData[i] * yData[i - 1] <= 0) {
369 firstZero = (xData[i] + xData[i - 1]) / 2;
371 lastZero = (xData[i] + xData[i - 1]) / 2;
376 a[0] = (yMin + yMax) / 2;
377 a[1] = (yMax - yMin) / 2;
379 a[2] = 2 / fabs(xMax - xMin);
381 a[2] = zeroes / (2 * fabs(lastZero - firstZero));
385 if (guessFlags & GUESS_CONSTANT_GIVEN)
386 a[0] = constantGuess;
387 if (guessFlags & GUESS_FACTOR_GIVEN)
389 if (guessFlags & GUESS_FREQ_GIVEN)
391 if (guessFlags & GUESS_PHASE_GIVEN)
393 if (guessFlags & GUESS_SLOPE_GIVEN)
395 if (guessFlags & GUESS_RATE_GIVEN)
399 if (!(da[0] = a[0] * 0.1))
401 if (!(da[1] = a[1] * 0.1))
407 if (addExponential) {
415 alo[2] = ahi[2] = a[2];
420 simplexMin(&result, a, da, alo, ahi, disable, 6, -DBL_MAX, tolerance, fitFunction,
421 (verbosity > 0 ? report : NULL), nEvalMax, nPassMax, 12, 3, 1.0, simplexFlags);
423 for (i = result = 0; i < nData; i++)
424 result += sqr(residualData[i]);
425 rmsResidual = sqrt(result / nData);
427 fprintf(stderr,
"RMS deviation: %.15e\n", rmsResidual);
428 fprintf(stderr,
"(RMS deviation)/(largest value): %.15e\n", rmsResidual / MAX(fabs(yMin), fabs(yMax)));
432 fprintf(stderr,
"Coefficients of fit to the form y = a0 + a1*sin(2*PI*a2*x + a3) + a4*x, a = \n");
433 for (i = 0; i < 5; i++)
434 fprintf(stderr,
"%.8e ", a[i]);
436 fprintf(stderr,
"Coefficients of fit to the form y = a0 + a1*sin(2*PI*a2*x + a3), a = \n");
437 for (i = 0; i < 4; i++)
438 fprintf(stderr,
"%.8e ", a[i]);
440 fprintf(stderr,
"\n");
444 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, xData, nData, xIndex) ||
445 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, fitData, nData, fitIndex) ||
447 "sinefitConstant", a[0],
448 "sinefitFactor", a[1],
449 "sinefitFrequency", a[2],
450 "sinefitPhase", a[3],
451 "sinefitRmsResidual", rmsResidual,
455 "sinefitSlope", a[4], NULL))) ||
458 "sinefitRate", a[5], NULL))) ||
459 (fullOutput && (!
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, yData, nData, yIndex) ||
460 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, residualData, nData, residualIndex))) ||
477void setupOutputFile(
SDDS_DATASET *OutputTable, int32_t *xIndex, int32_t *yIndex, int32_t *fitIndex,
478 int32_t *residualIndex,
char *output,
long fullOutput,
SDDS_DATASET *InputTable,
479 char *xName,
char *yName,
short columnMajorOrder,
short addSlope,
short addExponential) {
480 char *name, *yUnits, *description, *xUnits, *inverse_xUnits;
482 static char *residualNamePart =
"Residual";
483 static char *residualDescriptionPart =
"Residual of sinusoidal fit to ";
494 if (columnMajorOrder != -1)
495 OutputTable->layout.data_mode.column_major = columnMajorOrder;
497 OutputTable->layout.data_mode.column_major = InputTable->layout.data_mode.column_major;
499 name =
tmalloc(
sizeof(*name) * (strlen(yName) + strlen(residualNamePart) + 1));
500 description =
tmalloc(
sizeof(*description) * (strlen(yName) + strlen(residualDescriptionPart) + 1));
507 sprintf(name,
"%s%s", yName, residualNamePart);
508 sprintf(description,
"%s%s", yName, residualDescriptionPart);
513 sprintf(name,
"%sFit", yName);
514 sprintf(description,
"Sinusoidal fit to %s", yName);
518 inverse_xUnits = makeInverseUnits(xUnits);
520 if (
SDDS_DefineParameter(OutputTable,
"sinefitConstant", NULL, yUnits,
"Constant term from sinusoidal fit",
524 SDDS_DefineParameter(OutputTable,
"sinefitFrequency", NULL, inverse_xUnits,
"Frequency from sinusoidal fit",
528 SDDS_DefineParameter(OutputTable,
"sinefitRmsResidual", NULL, yUnits,
"RMS residual from sinusoidal fit",
533 if (
SDDS_DefineParameter(OutputTable,
"sinefitSlope", NULL, yUnits,
"Slope term added to sinusoidal fit",
537 if (addExponential) {
538 if (
SDDS_DefineParameter(OutputTable,
"sinefitRate", NULL, inverse_xUnits,
"Exponential decay rate for sinusoidal fit",
549char *makeInverseUnits(
char *units) {
554 inverseUnits =
tmalloc(
sizeof(*inverseUnits) * (strlen(units) + 5));
556 if (strncmp(units,
"1/(", 3) == 0 && units[strlen(units) - 1] ==
')') {
558 strcpy(inverseUnits, units + 3);
559 inverseUnits[strlen(inverseUnits) - 1] =
'\0';
560 }
else if (!strchr(units,
' ')) {
562 sprintf(inverseUnits,
"1/%s", units);
565 sprintf(inverseUnits,
"1/(%s)", units);
571double fitFunction(
double *a,
long *invalid) {
573 double chi, min_chi, fitValue;
579 fprintf(stderr,
"Trial: a = %e, %e, %e, %e, %e, %e\n", a[0], a[1], a[2], a[3], a[4], a[5]);
581 for (i = chi = 0; i < nData; i++) {
582 fitValue = a[1] * sin(PIx2 * a[2] * xData[i] + a[3]);
584 fitValue *= exp(xData[i]*a[5]);
586 fitValue += a[4] * xData[i];
588 fitData[i] = fitValue;
589 residualData[i] = yData[i] - fitValue;
590 chi += sqr(residualData[i]);
592 if (isnan(chi) || isinf(chi))
595 fprintf(stderr,
" --> chi = %e, invalid = %ld\n", chi, *invalid);
605 fprintf(stderr,
"New best chi = %e: a = %e, %e, %e, %e, %e, %e\n",
606 chi, fit[0], fit[1], fit[2], fit[3], fit[4], fit[5]);
611void report(
double y,
double *x,
long pass,
long nEval,
long n_dimen) {
614 fprintf(stderr,
"Pass %ld, after %ld evaluations: result = %.16e\n", pass, nEval, y);
615 fprintf(stderr,
"a = ");
616 for (i = 0; i < 6; i++)
617 fprintf(stderr,
"%.8e ", x[i]);
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
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_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.
void * SDDS_Realloc(void *old_ptr, size_t new_size)
Reallocates memory to a new size.
#define SDDS_LONG
Identifier for the signed 32-bit integer data type.
#define SDDS_DOUBLE
Identifier for the double data type.
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.
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.
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.