/* lspline.c ********************************************************************************
* lspline.c -- routines for linear spline functions
*
* User routines:
* Pro_LSplineFunc(series) creates a function that returns the
* value interpolated between the nearest
* entries in a series.
*
* Mark Showalter & Neil Heather, PDS Ring-Moon Systems Node, April 1998
*******************************************************************************/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include "profile.h"
#include "fortran.h"
/********************
* Type definitions *
********************/
typedef struct ZPRO_LSPLINE_STRUCT {
XPRO_CLASS class;
PRO_OBJECT *series;
RL_INT4 kmax;
} ZPRO_LSPLINE;
/********************
* Static variables *
********************/
static XPRO_CLASS lspline_class = {XPRO_LSPLINE_CLASS, "linear-spline", NULL};
/********************************
* Internal function prototypes *
********************************/
static RL_FLT8 ZPro_EvalLSpline RL_PROTO((RL_VOID *pointer, RL_FLT8 x));
static void ZPro_FreeLSpline RL_PROTO((RL_VOID *pointer));
static void ZPro_PrintLSpline RL_PROTO((RL_VOID *pointer));
/*
********************************************************************************
* EXPORTED USER ROUTINES
********************************************************************************
*$ Component_name:
* Pro_LSplineFunc (lspline.c)
*$ Abstract:
* This routine generates a new linear spline function object, which
* performs linear interpolation between the nearest valid series values.
*$ Keywords:
* PROFILE, FUNCTION
* C, PUBLIC, SUBROUTINE
*$ Declarations:
* PRO_OBJECT *Pro_LSplineFunc(series)
* PRO_OBJECT *series;
*$ Inputs:
* series pointer to the series object
*$ Outputs:
* none
*$ Returns:
* a pointer to a new linear spline object, or NULL on non-fatal error.
*$ Detailed_description:
* This routine generates a new linear spline function object, which
* performs linear interpolation between the nearest valid series values.
*$ External_references:
* Profile toolkit
*$ Side_effects:
* Memory is allocated. The new object retains a link to the original
* series.
*$ Examples:
* This snippet of code takes a series that contains only three valid
* samples, and uses it to construct an linear spline function. The
* function returns f(x)=x for 1 <= x <= 5.
*
* PRO_OBJECT *series, *func;
* RL_FLT8 table[] = {1., 2., 98., 99., 5.};
*
* series = Pro_ArraySeries(table, 1, 5, 1., 1.,
* 98., 99., TRUE, FALSE);
* func = Pro_LSplineFunc(series);
*$ Error_handling:
* Profile library error handling is in effect.
*
* Conditions raised:
* PRO_CLASS_ERROR if series object is NULL or not a series.
* PRO_SETUP_FAILURE if series contains less than two valid values.
* RL_MEMORY_ERROR on memory allocation error.
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: March 1998
*$ Change_history:
* none
*******************************************************************************/
PRO_OBJECT *Pro_LSplineFunc(series)
PRO_OBJECT *series;
{
ZPRO_LSPLINE *lspline;
PRO_OBJECT *new;
RL_INT4 k, k1, k2, flag;
RL_FLT8 x1, x2, temp;
/* Skip over invalid entries at each end of series */
(void) Pro_SeriesIndices(series, &k1, &k2);
for (k = k1; k <= k2; k++) {
(void) Pro_SeriesValue(series, k, &flag);
if (flag == 0) break;
}
k1 = k;
for (k = k2; k >= k1; k--) {
(void) Pro_SeriesValue(series, k, &flag);
if (flag == 0) break;
}
k2 = k;
if (k1 >= k2) {
(void) sprintf(xpro_message, "function setup failure\n\
insufficient samples to set up linear spline function\n\
series \"%s\" contains fewer than two valid samples",
Pro_ObjectName(series,0));
RL_RaiseError("PRO_SETUP_FAILURE", xpro_message);
}
/* Determine domain of function */
x1 = Pro_SeriesXValue(series, (RL_FLT8) k1);
x2 = Pro_SeriesXValue(series, (RL_FLT8) k2);
if (x1 > x2) {temp = x1; x1 = x2; x2 = temp;}
/* Allocate and initialize structure */
lspline = (ZPRO_LSPLINE *) XRL_Malloc(sizeof(ZPRO_LSPLINE));
if (lspline == NULL) return NULL;
lspline->class = lspline_class;
lspline->series = series;
lspline->kmax = k2;
/* Create function object */
new = XPro_MakeFunc(x1, x2,
ZPro_EvalLSpline, ZPro_FreeLSpline, ZPro_PrintLSpline,
lspline);
/* Enslave the series and check for error */
if (new != NULL && XPro_EnslaveObject(new, series)) {
Pro_FreeObject(new);
}
/* Transfer coordinate names to new function */
Pro_RenameObject(new, 1, Pro_ObjectName(series,1));
Pro_RenameObject(new, 2, Pro_ObjectName(series,2));
return new;
}
/*
********************************************************************************
* INTERNAL ROUTINES
********************************************************************************
* ZPro_EvalLSpline(pointer, x)
*
* This internal routine calculates the value of a linear spline function.
*
* Inputs:
* pointer pointer to a ZPRO_LSPLINE data structure.
* x location at which to evaluate linear spline function.
*
* Return: value of function at x.
*
* Errors:
* PRO_INTERPOLATION_ERROR on interpolation failure due to lack of valid
* data above or below x-value. In this situation, the
* nearest valid y-value is returned if possible; otherwise
* zero. In general, this should not happen because the
* series is checked for valid end points when it is
* created.
*******************************************************************************/
static RL_FLT8 ZPro_EvalLSpline(pointer, x)
RL_VOID *pointer;
RL_FLT8 x;
{
ZPRO_LSPLINE *lspline;
RL_INT4 k1, k2, kmin, kmax, k, lower_flag, upper_flag;
RL_FLT8 findex, x1, x2, y1, y2;
/* Get linear spline pointer */
lspline = (ZPRO_LSPLINE *) pointer;
/* Retrieve the domain of indexes for the function */
Pro_SeriesIndices(lspline->series, &kmin, &kmax);
/* Find index range that straddles x */
findex = Pro_SeriesIndex(lspline->series, x);
k1 = (RL_INT4) floor(findex);
k2 = k1 + 1;
/* Handle special case of evaluation at upper endpoint */
if (k2 > lspline->kmax) {k1--; k2--;}
/* Find first unflagged series value below x */
for (k = k1; k >= kmin; k--) {
y1 = Pro_SeriesValue(lspline->series, k, &lower_flag);
if (lower_flag == 0) break;
}
x1 = Pro_SeriesXValue(lspline->series, (RL_FLT8) k);
/* Find first unflagged series value above x */
for (k = k2; k <= kmax; k++) {
y2 = Pro_SeriesValue(lspline->series, k, &upper_flag);
if (upper_flag == 0) break;
}
x2 = Pro_SeriesXValue(lspline->series, (RL_FLT8) k);
/* Handle case of successful interpolation */
if (lower_flag == 0 && upper_flag == 0)
return y1 + (y2-y1) * (x-x1)/(x2-x1);
/* This situation should never occur since the end points of the domain are
* checked for validity when the object is created. However, somebody could
* conceivably muck around with the series values after the linear spline
* function has been created.
*/
(void) sprintf(xpro_message, "function evaluation failure\n\
missing adjacent value for linear spline interpolation\n\
values near index %#g (x = %#g) have been changed in series \"%s\"",
findex, x, Pro_ObjectName(lspline->series,0));
RL_RaiseError("PRO_EVALUATION_FAILURE", xpro_message);
/* Return best possible value */
if (lower_flag == 0) return y1;
if (upper_flag == 0) return y2;
return 0.;
}
/*
********************************************************************************
* ZPro_FreeLSpline(pointer)
*
* This internal routine frees the memory used by a ZPRO_LSPLINE structure.
*
* Inputs:
* pointer pointer to the ZPRO_LSPLINE data structure.
*******************************************************************************/
static void ZPro_FreeLSpline(pointer)
RL_VOID *pointer;
{
XRL_Free(pointer);
}
/*
********************************************************************************
* ZPro_PrintLSpline(pointer)
*
* This internal routine prints information about the ZPRO_LSPLINE structure.
*
* Input:
* pointer pointer to soft function object.
****************************************************************************/
static void ZPro_PrintLSpline(pointer)
RL_VOID *pointer;
{
ZPRO_LSPLINE *lspline;
lspline = (ZPRO_LSPLINE *) pointer;
/* Make sure object is not NULL */
if (lspline == NULL) {
printf("PRINT ERROR: Linear spline function pointer is NULL\n");
return;
}
/* Make sure object is a linear spline function */
if (lspline->class.id != XPRO_LSPLINE_CLASS) {
printf("PRINT ERROR: Object is not a linear spline function\n");
return;
}
/* Print object info... */
printf("\nLinear spline function parameters...\n");
printf("series = "); XPro_PrintInfo(lspline->series);
return;
}
/*
********************************************************************************
* FORTRAN INTERFACE ROUTINES
********************************************************************************
*$ Component_name:
* FPro_LSplineFunc (lspline.c)
*$ Abstract:
* This routine generates a new linear spline function object, which
* performs linear interpolation between the nearest valid series values.
*$ Keywords:
* PROFILE, FUNCTION
* FORTRAN, PUBLIC, SUBROUTINE
*$ Declarations:
* integer*4 function FPro_LSplineFunc(series)
* integer*4 series
*$ Inputs:
* series FORTRAN pointer to the series object.
*$ Outputs:
* none
*$ Returns:
* FORTRAN pointer to a new linear spline object, or 0 on non-fatal error.
*$ Detailed_description:
* This routine generates a new linear spline function object, which
* performs linear interpolation between the nearest valid series values.
*$ External_references:
* Profile toolkit
*$ Side_effects:
* Memory is allocated. The new object retains a link to the original
* series.
*$ Examples:
* This snippet of code takes a series that contains only three valid
* samples, and uses it to construct an linear spline function. The
* function returns f(x)=x for 1 <= x <= 5.
*
* integer*4 series, func
* real*8 table(5)/ 1.d0, 2.d0, 98.d0, 99.d0, 5.d0 /
*
* series = FPro_ArraySeries(table, 1, 5, 1.d0, 1.d0,
* & 98.d0, 99.d0, .TRUE., .FALSE.)
* func = FPro_LSplineFunc(series)
*$ Error_handling:
* Profile library error handling is in effect.
*
* Conditions raised:
* PRO_CLASS_ERROR if series object is NULL or not a series.
* PRO_SETUP_FAILURE if series contains less than two valid values.
* RL_MEMORY_ERROR on memory allocation error.
* FORTRAN_POINTER_ERROR if series is not a valid FORTRAN pointer.
*$ Limitations:
* none
*$ Author_and_institution:
* Mark R. Showalter
* NASA/Ames Research Center
*$ Version_and_date:
* 1.0: March 1998
*$ Change_history:
* none
*******************************************************************************/
RL_INT4 FORTRAN_NAME(fpro_lsplinefunc) (series)
RL_INT4 *series;
{
RL_VOID *ptr1, *ptr2;
RL_INT4 index;
/* Look up series pointer */
ptr1 = FORT_GetPointer(*series);
if (ptr1 == NULL) return 0;
/* Call function */
ptr2 = (RL_VOID *) Pro_LSplineFunc((PRO_OBJECT *) ptr1);
if (ptr2 == NULL) return 0;
/* Save new pointer */
index = FORT_AddPointer(ptr2);
if (index == 0) Pro_FreeObject((PRO_OBJECT *) ptr2);
return index;
}
/*******************************************************************************
*/