43char *option[N_OPTIONS] = {
58 "Usage: sddssinefit [<inputfile>] [<outputfile>] \n"
59 " [-pipe=<input>[,<output>]]\n"
61 " [-columns=<x-name>,<y-name>]\n"
62 " [-tolerance=<value>]\n"
63 " [-limits=evaluations=<number>,passes=<number>]\n"
64 " [-verbosity=<integer>]\n"
65 " [-guess=constant=<constant>,factor=<factor>,frequency=<freq>,phase=<phase>,slope=<slope>]\n"
68 " [-majorOrder=row|column]\n\n"
70 " Performs a sinusoidal fit of the form:\n"
71 " y = <constant> + <factor>*sin(2*PI*<freq>*x + <phase>)\n"
73 " y = <constant> + <factor>*sin(2*PI*<freq>*x + <phase>) + <slope>*x\n\n"
75 " <inputfile> : Path to the input SDDS file.\n"
76 " <outputfile> : Path to the output SDDS file.\n"
77 " -pipe=<input>,<output> : Use standard input/output for data streams.\n"
78 " -fulloutput : Include full output with residuals.\n"
79 " -columns=<x-name>,<y-name> : Specify the names of the x and y data columns.\n"
80 " -tolerance=<value> : Set the tolerance for the fitting algorithm (default: 1e-6).\n"
81 " -limits=evaluations=<n>,passes=<m> : Set maximum number of evaluations and passes (default: 5000 evaluations, 25 passes).\n"
82 " -verbosity=<integer> : Set verbosity level (default: 0).\n"
83 " -guess=constant=<c>,factor=<f>,frequency=<freq>,phase=<p>,slope=<s> : Provide initial guesses for fit parameters.\n"
84 " -lockFrequency : Lock the frequency parameter during fitting.\n"
85 " -addSlope : Include a slope term in the fit.\n"
86 " -majorOrder=row|column : Specify the major order for data processing.\n\n"
89 " (" __DATE__
" " __TIME__
", SVN revision: " SVN_VERSION
")\n";
91static double *xData, *yData;
93static double yMin, yMax, xMin, xMax;
96double fitFunction(
double *a,
long *invalid);
97double fitFunctionWithSlope(
double *a,
long *invalid);
98void report(
double res,
double *a,
long pass,
long n_eval,
long n_dimen);
99void setupOutputFile(
SDDS_DATASET *OutputTable, int32_t *xIndex, int32_t *yIndex, int32_t *fitIndex,
100 int32_t *residualIndex,
char *output,
long fullOutput,
SDDS_DATASET *InputTable,
101 char *xName,
char *yName,
short columnMajorOrder,
short addSlope);
102char *makeInverseUnits(
char *units);
106#define GUESS_CONSTANT_GIVEN 0x0001
107#define GUESS_FACTOR_GIVEN 0x0002
108#define GUESS_FREQ_GIVEN 0x0004
109#define GUESS_PHASE_GIVEN 0x0008
110#define GUESS_SLOPE_GIVEN 0x0010
112int main(
int argc,
char **argv) {
113 double tolerance, result;
114 int32_t nEvalMax = 5000, nPassMax = 25;
116 double alo[5], ahi[5];
121 long i_arg, fullOutput;
123 char *input, *output, *xName, *yName;
124 int32_t xIndex, yIndex, fitIndex, residualIndex;
126 double *fitData, *residualData, rmsResidual;
127 unsigned long guessFlags, dummyFlags, pipeFlags;
128 double constantGuess, factorGuess, freqGuess, phaseGuess, slopeGuess;
129 double firstZero, lastZero;
130 unsigned long simplexFlags = 0, majorOrderFlag;
131 short columnMajorOrder = -1;
136 argc =
scanargs(&s_arg, argc, argv);
137 if (argc < 2 || argc > (2 + N_OPTIONS))
140 input = output = NULL;
142 verbosity = fullOutput = 0;
143 xName = yName = NULL;
147 for (i_arg = 1; i_arg < argc; i_arg++) {
148 if (s_arg[i_arg].arg_type == OPTION) {
149 switch (
match_string(s_arg[i_arg].list[0], option, N_OPTIONS, 0)) {
150 case SET_MAJOR_ORDER:
152 s_arg[i_arg].n_items--;
153 if (s_arg[i_arg].n_items > 0 &&
154 (!
scanItemList(&majorOrderFlag, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
155 "row", -1, NULL, 0, SDDS_ROW_MAJOR_ORDER,
156 "column", -1, NULL, 0, SDDS_COLUMN_MAJOR_ORDER, NULL)))
157 SDDS_Bomb(
"invalid -majorOrder syntax/values");
158 if (majorOrderFlag & SDDS_COLUMN_MAJOR_ORDER)
159 columnMajorOrder = 1;
160 else if (majorOrderFlag & SDDS_ROW_MAJOR_ORDER)
161 columnMajorOrder = 0;
164 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1],
"%lf", &tolerance) != 1)
165 SDDS_Bomb(
"incorrect -tolerance syntax");
168 if (s_arg[i_arg].n_items != 2 || sscanf(s_arg[i_arg].list[1],
"%ld", &verbosity) != 1)
169 SDDS_Bomb(
"incorrect -verbosity syntax");
172 if (s_arg[i_arg].n_items < 2)
174 s_arg[i_arg].n_items -= 1;
175 if (!
scanItemList(&guessFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
176 "constant",
SDDS_DOUBLE, &constantGuess, 1, GUESS_CONSTANT_GIVEN,
177 "factor",
SDDS_DOUBLE, &factorGuess, 1, GUESS_FACTOR_GIVEN,
178 "frequency",
SDDS_DOUBLE, &freqGuess, 1, GUESS_FREQ_GIVEN,
179 "phase",
SDDS_DOUBLE, &phaseGuess, 1, GUESS_PHASE_GIVEN,
180 "slope",
SDDS_DOUBLE, &slopeGuess, 1, GUESS_SLOPE_GIVEN, NULL))
184 if (s_arg[i_arg].n_items != 3)
186 xName = s_arg[i_arg].list[1];
187 yName = s_arg[i_arg].list[2];
200 if (s_arg[i_arg].n_items < 2)
202 s_arg[i_arg].n_items -= 1;
203 if (!
scanItemList(&dummyFlags, s_arg[i_arg].list + 1, &s_arg[i_arg].n_items, 0,
204 "evaluations",
SDDS_LONG, &nEvalMax, 1, 0,
205 "passes",
SDDS_LONG, &nPassMax, 1, 0, NULL) ||
206 nEvalMax <= 0 || nPassMax <= 0)
210 if (!
processPipeOption(s_arg[i_arg].list + 1, s_arg[i_arg].n_items - 1, &pipeFlags))
214 fprintf(stderr,
"Error: Unknown or ambiguous option: %s\n", s_arg[i_arg].list[0]);
220 input = s_arg[i_arg].list[0];
221 else if (output == NULL)
222 output = s_arg[i_arg].list[0];
224 SDDS_Bomb(
"Too many filenames provided.");
230 if (!xName || !yName)
231 SDDS_Bomb(
"-columns option must be specified.");
238 setupOutputFile(&OutputTable, &xIndex, &yIndex, &fitIndex, &residualIndex, output, fullOutput,
239 &InputTable, xName, yName, columnMajorOrder, addSlope);
241 fitData = residualData = NULL;
243 alo[0] = -(ahi[0] = DBL_MAX);
245 ahi[1] = ahi[2] = DBL_MAX;
246 alo[3] = -(ahi[3] = PIx2);
248 alo[4] = -(ahi[4] = DBL_MAX);
250 firstZero = lastZero = 0;
261 for (i = 1; i < nData; i++)
262 if (yData[i] * yData[i - 1] <= 0) {
265 firstZero = (xData[i] + xData[i - 1]) / 2;
267 lastZero = (xData[i] + xData[i - 1]) / 2;
270 a[0] = (yMin + yMax) / 2;
271 a[1] = (yMax - yMin) / 2;
273 a[2] = 2 / fabs(xMax - xMin);
275 a[2] = zeroes / (2 * fabs(lastZero - firstZero));
280 if (guessFlags & GUESS_CONSTANT_GIVEN)
281 a[0] = constantGuess;
282 if (guessFlags & GUESS_FACTOR_GIVEN)
284 if (guessFlags & GUESS_FREQ_GIVEN)
286 if (guessFlags & GUESS_PHASE_GIVEN)
288 if (guessFlags & GUESS_SLOPE_GIVEN)
292 if (!(da[0] = a[0] * 0.1))
294 if (!(da[1] = a[1] * 0.1))
303 alo[2] = ahi[2] = a[2];
308 simplexMin(&result, a, da, alo, ahi, NULL, n_dimen, -DBL_MAX, tolerance, fitFunctionWithSlope,
309 (verbosity > 0 ? report : NULL), nEvalMax, nPassMax, 12, 3, 1.0, simplexFlags);
311 simplexMin(&result, a, da, alo, ahi, NULL, n_dimen, -DBL_MAX, tolerance, fitFunction,
312 (verbosity > 0 ? report : NULL), nEvalMax, nPassMax, 12, 3, 1.0, simplexFlags);
314 fitData =
trealloc(fitData,
sizeof(*fitData) * nData);
315 residualData =
trealloc(residualData,
sizeof(*residualData) * nData);
317 for (i = result = 0; i < nData; i++) {
318 fitData[i] = a[0] + a[1] * sin(PIx2 * a[2] * xData[i] + a[3]) + a[4] * xData[i];
319 residualData[i] = yData[i] - fitData[i];
320 result += sqr(residualData[i]);
323 for (i = result = 0; i < nData; i++) {
324 fitData[i] = a[0] + a[1] * sin(PIx2 * a[2] * xData[i] + a[3]);
325 residualData[i] = yData[i] - fitData[i];
326 result += sqr(residualData[i]);
329 rmsResidual = sqrt(result / nData);
331 fprintf(stderr,
"RMS deviation: %.15e\n", rmsResidual);
332 fprintf(stderr,
"(RMS deviation)/(largest value): %.15e\n", rmsResidual / MAX(fabs(yMin), fabs(yMax)));
336 fprintf(stderr,
"Coefficients of fit to the form y = a0 + a1*sin(2*PI*a2*x + a3) + a4*x, a = \n");
337 for (i = 0; i < 5; i++)
338 fprintf(stderr,
"%.8e ", a[i]);
340 fprintf(stderr,
"Coefficients of fit to the form y = a0 + a1*sin(2*PI*a2*x + a3), a = \n");
341 for (i = 0; i < 4; i++)
342 fprintf(stderr,
"%.8e ", a[i]);
344 fprintf(stderr,
"\n");
348 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, xData, nData, xIndex) ||
349 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, fitData, nData, fitIndex) ||
351 "sinefitConstant", a[0],
352 "sinefitFactor", a[1],
353 "sinefitFrequency", a[2],
354 "sinefitPhase", a[3],
355 "sinefitRmsResidual", rmsResidual,
359 "sinefitSlope", a[4], NULL))) ||
360 (fullOutput && (!
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, yData, nData, yIndex) ||
361 !
SDDS_SetColumn(&OutputTable, SDDS_SET_BY_INDEX, residualData, nData, residualIndex))) ||
378void setupOutputFile(
SDDS_DATASET *OutputTable, int32_t *xIndex, int32_t *yIndex, int32_t *fitIndex,
379 int32_t *residualIndex,
char *output,
long fullOutput,
SDDS_DATASET *InputTable,
380 char *xName,
char *yName,
short columnMajorOrder,
short addSlope) {
381 char *name, *yUnits, *description, *xUnits, *inverse_xUnits;
383 static char *residualNamePart =
"Residual";
384 static char *residualDescriptionPart =
"Residual of sinusoidal fit to ";
395 if (columnMajorOrder != -1)
396 OutputTable->layout.data_mode.column_major = columnMajorOrder;
398 OutputTable->layout.data_mode.column_major = InputTable->layout.data_mode.column_major;
400 name =
tmalloc(
sizeof(*name) * (strlen(yName) + strlen(residualNamePart) + 1));
401 description =
tmalloc(
sizeof(*description) * (strlen(yName) + strlen(residualDescriptionPart) + 1));
408 sprintf(name,
"%s%s", yName, residualNamePart);
409 sprintf(description,
"%s%s", yName, residualDescriptionPart);
414 sprintf(name,
"%sFit", yName);
415 sprintf(description,
"Sinusoidal fit to %s", yName);
419 inverse_xUnits = makeInverseUnits(xUnits);
421 if (
SDDS_DefineParameter(OutputTable,
"sinefitConstant", NULL, yUnits,
"Constant term from sinusoidal fit",
425 SDDS_DefineParameter(OutputTable,
"sinefitFrequency", NULL, inverse_xUnits,
"Frequency from sinusoidal fit",
429 SDDS_DefineParameter(OutputTable,
"sinefitRmsResidual", NULL, yUnits,
"RMS residual from sinusoidal fit",
434 if (
SDDS_DefineParameter(OutputTable,
"sinefitSlope", NULL, yUnits,
"Slope term added to sinusoidal fit",
445char *makeInverseUnits(
char *units) {
450 inverseUnits =
tmalloc(
sizeof(*inverseUnits) * (strlen(units) + 5));
452 if (strncmp(units,
"1/(", 3) == 0 && units[strlen(units) - 1] ==
')') {
454 strcpy(inverseUnits, units + 3);
455 inverseUnits[strlen(inverseUnits) - 1] =
'\0';
456 }
else if (!strchr(units,
' ')) {
458 sprintf(inverseUnits,
"1/%s", units);
461 sprintf(inverseUnits,
"1/(%s)", units);
467double fitFunction(
double *a,
long *invalid) {
470 static double min_chi;
475 for (i = chi = 0; i < nData; i++)
476 chi += sqr(yData[i] - (a[0] + a[1] * sin(PIx2 * a[2] * xData[i] + a[3])));
477 if (isnan(chi) || isinf(chi))
480 fprintf(stderr,
"Trial: a = %e, %e, %e, %e --> chi = %e, invalid = %ld\n", a[0], a[1], a[2], a[3], chi, *invalid);
488 fprintf(stderr,
"New best chi = %e: a = %e, %e, %e, %e\n", chi, fit[0], fit[1], fit[2], fit[3]);
493double fitFunctionWithSlope(
double *a,
long *invalid) {
496 static double min_chi;
501 for (i = chi = 0; i < nData; i++)
502 chi += sqr(yData[i] - (a[0] + a[1] * sin(PIx2 * a[2] * xData[i] + a[3]) + a[4] * xData[i]));
503 if (isnan(chi) || isinf(chi))
506 fprintf(stderr,
"Trial: a = %e, %e, %e, %e, %e --> chi = %e, invalid = %ld\n",
507 a[0], a[1], a[2], a[3], a[4], chi, *invalid);
516 fprintf(stderr,
"New best chi = %e: a = %e, %e, %e, %e, %e\n",
517 chi, fit[0], fit[1], fit[2], fit[3], fit[4]);
522void report(
double y,
double *x,
long pass,
long nEval,
long n_dimen) {
525 fprintf(stderr,
"Pass %ld, after %ld evaluations: result = %.16e\n", pass, nEval, y);
526 fprintf(stderr,
"a = ");
527 for (i = 0; i < n_dimen; i++)
528 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.
#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.
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.