TPCCLIB
Loading...
Searching...
No Matches
extrapolate.c File Reference

Procedures for extrapolating PET TAC data. More...

#include "libtpcmodext.h"

Go to the source code of this file.

Functions

int extrapolate_monoexp (DFT *dft, double *fittime, int *min_nr, int max_nr, double mintime, double extr_to, DFT *ext, FILE *loginfo, char *status)
 
int dftAutointerpolate (DFT *dft, DFT *dft2, double endtime, int verbose)
 
int dftDoubleFrames (DFT *dft, DFT *dft2)
 
int dftDivideFrames (DFT *dft, int voi_index, int add_nr, DFT *dft2, int verbose)
 
int dft_end_line (DFT *dft, double *fittime, int *min_nr, int max_nr, double mintime, int check_impr, FIT *fit, FILE *loginfo, char *status)
 
int dft_ln (DFT *dft1, DFT *dft2)
 

Detailed Description

Procedures for extrapolating PET TAC data.

Author
Vesa Oikonen

Definition in file extrapolate.c.

Function Documentation

◆ dft_end_line()

int dft_end_line ( DFT * dft,
double * fittime,
int * min_nr,
int max_nr,
double mintime,
int check_impr,
FIT * fit,
FILE * loginfo,
char * status )

Fits line to the end-part of TACs. By default, the included data points are determined based on maximum adjusted R^2 from at least three points; regression to the larger number of points is used in case difference in adjusted R^2 values is not larger than 0.0001.

Returns
Returns 0 when successful, otherwise <>0.
Parameters
dftPointer to original TAC data
fittimeBy default, the search for the best line fit is started from the last sample towards the first sample; set fit time to -1 to use this default. However, if the end phase is unreliable or very noisy, you may want to set fittime to include only certain time range from the beginning. Function will write here the fittime that was actually used.
min_nrThe minimum number of samples used in searching the best fit; at least 2, but 3 is recommended. If data is very noisy, then this number may need to be increased. Function will write here the nr of samples that was actually used. This can be used as an alternative to mintime or in addition to it.
max_nrThe maximum number of samples used in searching the best fit; must be higher than min_nr, or set to -1 to not to limit the number.
mintimeMinimum time range used in searching the best fit. If data is very noisy, then this may need to be set, otherwise setting mintime to -1 will use the default. This can be used as an alternative to min_nr or in addition to it.
check_imprLinear fitting can be applied to all data subsets in the fit time range (check_impr=0), or fitting is stopped when increasing n does not improve the adjusted R^2 (check_impr=1); the latter mode is for compatibility with WinNonlin.
fitPointer to data for fitted parameters. Struct must be initiated. Any existing data is deleted. Additionally, adjusted R^2 is written as 3rd (non-documented) parameter.
loginfoGive file pointer (for example stdout) where log information is printed; NULL if not needed.
statusPointer to a string (allocated for at least 64 chars) where error message or other execution status will be written; enter NULL, if not needed.

Definition at line 514 of file extrapolate.c.

552 {
553 int fi, ret, n, to, from, to_min, from_min, orig_max_nr;
554 int fitNr, snr;
555 double *cx, *cy;
556 double adj_r2, slope, slope_sd, ic, ic_sd, r, f, ySD_min;
557 double adj_r2_max, adj_r2_prev=-10.0, ic_min, slope_min;
558
559
560 /* Check the input */
561 if(status!=NULL) sprintf(status, "program error");
562 if(dft==NULL || fit==NULL) return -1;
563 if(*min_nr<2) *min_nr=3; // Setting erroneous value to a recommended value
564 if(max_nr>-1 && max_nr<*min_nr) return -2;
565 orig_max_nr=max_nr;
566
567 /* Set the last sample to be fitted */
568 fitNr=dft->frameNr;
569 if(*fittime>0.0) {
570 while(dft->x[fitNr-1]>*fittime && fitNr>0) fitNr--;
571 if(loginfo!=NULL) fprintf(loginfo, "fitNr := %d\nTime range := %g - %g\n",
572 fitNr, dft->x[0], dft->x[fitNr-1]);
573 }
574 *fittime=dft->x[fitNr-1];
575 /* Check that there are at least 3 samples */
576 if(fitNr<3) { /* min_nr is checked later */
577 if(status!=NULL) sprintf(status, "too few samples for linear fit");
578 return(2);
579 }
580
581 /* If mintime was set, then get min_nr based on that */
582 if(mintime>0.0) {
583 fi=0; while(dft->x[fi]<(*fittime-mintime) && fi<fitNr) fi++;
584 if(--fi<=0) {
585 if(status!=NULL) sprintf(status, "required minimum fit range too large");
586 return(2);
587 }
588 *min_nr=((fitNr-fi)>*min_nr?fitNr-fi:*min_nr);
589 }
590 if(loginfo!=NULL) fprintf(loginfo, "final_min_nr := %d\n", *min_nr);
591
592 /* Allocate data for fits */
593 if((ret=fit_allocate_with_dft(fit, dft))!=0) {
594 if(loginfo!=NULL) fprintf(loginfo, "Error %d: cannot allocate memory for fits.\n", ret);
595 if(status!=NULL) sprintf(status, "cannot allocate memory for fits");
596 return 4;
597 }
598 /* and for x,y data to be fitted */
599 cx=(double*)malloc(2*fitNr*sizeof(double)); if(cx==NULL) {
600 if(status!=NULL) sprintf(status, "out of memory");
601 return(6);
602 }
603 cy=cx+fitNr;
604
605 /* Fit each TAC */
606 if(loginfo!=NULL) fprintf(loginfo, "linear fitting\n");
607 for(int ri=0; ri<dft->voiNr; ri++) {
608 max_nr=orig_max_nr;
609 /* Print TAC name, if more than one was found */
610 if(dft->voiNr>1 && loginfo!=NULL)
611 fprintf(loginfo, "%s :\n", dft->voi[ri].name);
612 /* Set header */
613 fit->voi[ri].parNr=2;
614 fit->voi[ri].type=101;
615 /* Copy appropriate TAC data */
616 for(fi=n=0; fi<fitNr; fi++)
617 if(dft->x[fi]>0.0 && !isnan(dft->voi[ri].y[fi])) {
618 cx[n]=dft->x[fi]; cy[n++]=dft->voi[ri].y[fi];}
619 if(n<*min_nr) {
620 if(status!=NULL) sprintf(status, "check the datafile (%d<%d)", n, *min_nr);
621 free(cx); return(7);
622 }
623 if(max_nr<=0) max_nr=n;
624 /* Search the plot range that gives the max adjusted R^2 */
625 from_min=to_min=-1; adj_r2_max=-9.99E+99; ic_min=slope_min=0.0; ySD_min=0.0;
626 for(from=n-*min_nr, to=n-1; from>=n-max_nr; from--) {
627 snr=(to-from)+1;
628 /* Calculation of linear regression using pearson() */
629 ret=pearson(
630 cx+from, cy+from, snr, &slope, &slope_sd, &ic, &ic_sd, &r, &f
631 );
632 if(ret==0) {
633 adj_r2= 1.0 - ((1.0-r*r)*(double)(snr-1)) / (double)(snr-2);
634 if(adj_r2<0.0 && loginfo!=NULL)
635 fprintf(loginfo, " r=%g; snr=%d\n", r, snr);
636 } else {
637 adj_r2=-9.99E+99;
638 }
639 if(adj_r2>adj_r2_max-0.0001) {
640 adj_r2_max=adj_r2; from_min=from; to_min=to;
641 ic_min=ic; slope_min=slope; ySD_min=f;
642 }
643 if(loginfo!=NULL)
644 fprintf(loginfo, " adj_r2=%g from=%d (%g)\n",
645 adj_r2, from, *(cx+from) );
646 /* check for improvement, if required */
647 if(check_impr!=0 && adj_r2_prev>-1.0 && adj_r2>0.0 && adj_r2<adj_r2_prev)
648 break;
649
650 adj_r2_prev=adj_r2;
651 }
652 if(from_min<0) {
653 if(status!=NULL) sprintf(status, "check the datafile");
654 free(cx); return(7);
655 }
656 if(loginfo!=NULL) fprintf(loginfo, " adj_r2_max=%g.\n", adj_r2_max );
657 /* Set fit line parameters */
658 fit->voi[ri].p[0]=ic_min;
659 fit->voi[ri].p[1]=slope_min;
660 fit->voi[ri].p[2]=adj_r2_max;
661 fit->voi[ri].wss=ySD_min;
662 fit->voi[ri].start=cx[from_min]; fit->voi[ri].end=cx[to_min];
663 fit->voi[ri].dataNr=(to_min-from_min)+1;
664 } // next curve
665
666
667 free(cx);
668 if(status!=NULL) sprintf(status, "ok");
669 return 0;
670}
int fit_allocate_with_dft(FIT *fit, DFT *dft)
Definition fitres.c:14
int pearson(double *x, double *y, int nr, double *k, double *kSD, double *b, double *bSD, double *r, double *ySD)
Definition pearson.c:14
Voi * voi
int voiNr
int frameNr
double * x
FitVOI * voi
double wss
double end
double start
double p[MAX_FITPARAMS]
double * y
char name[MAX_REGIONNAME_LEN+1]

◆ dft_ln()

int dft_ln ( DFT * dft1,
DFT * dft2 )

Natural logarithm (ln) transformation for TAC concentrations.

Returns
Returns 0 when successful, otherwise <>0.
Parameters
dft1Pointer to original TAC data.
dft2Pointer to allocated memory for ln transformed TAC data; enter NULL, if original data is to be overwritten by ln transformed values.

Definition at line 677 of file extrapolate.c.

683 {
684 DFT *out;
685
686 if(dft1==NULL || dft1->voiNr<1 || dft1->frameNr<1) return 1;
687 if(dft2!=NULL) out=dft2; else out=dft1;
688
689 int ok_nr=0;
690 for(int ri=0; ri<dft1->voiNr; ri++) for(int fi=0; fi<dft1->frameNr; fi++) {
691 if(!isnan(dft1->voi[ri].y[fi]) && dft1->voi[ri].y[fi]>0.0) {
692 out->voi[ri].y[fi]=log(dft1->voi[ri].y[fi]); ok_nr++;
693 } else {
694 out->voi[ri].y[fi]=nan("");
695 }
696 }
697 if(ok_nr>0) return 0; else return 2;
698}

◆ dftAutointerpolate()

int dftAutointerpolate ( DFT * dft,
DFT * dft2,
double endtime,
int verbose )

Interpolates TACs to automatically determined sample times with smaller intervals in the beginning. Only data in y arrays are interpolated; data in y2 and y3 are not used.

Returns
Function returns 0 when succesful, else a value >= 1.
Parameters
dftData to be interpolated is read from this structure.
dft2Interpolated data is written in this struct; must be initiated; any previous content is deleted.
endtimeThe length of interpolated/extrapolated data.
verboseVerbose level; if zero, then nothing is printed to stderr or stdout.

Definition at line 243 of file extrapolate.c.

253 {
254 int i, newnr, maxNr=10000;
255 double t, dt;
256
257
258 if(verbose>0) printf("dftAutointerpolate(dft1, dft2, %g)\n", endtime);
259 /* Check the arguments */
260 if(dft->frameNr<1 || dft->voiNr<1) return 1;
261 if(endtime<1.0 || endtime>1.0E12) return 2;
263 return 10;
264
265 /* Calculate the number of interpolated data points */
266 t=0.0; dt=0.02; newnr=1;
267 while(t+dt<endtime && newnr<maxNr-1) {
268 t+=dt; dt*=1.05; newnr++;
269 }
270 dt=endtime-t; t=endtime; newnr++;
271 if(verbose>1) printf("newnr := %d\n", newnr);
272
273 /* Allocate memory for interpolated data */
274 dftEmpty(dft2); if(dftSetmem(dft2, newnr, dft->voiNr)) return 3;
275 /* Copy header info */
276 (void)dftCopymainhdr(dft, dft2); dft2->voiNr=dft->voiNr;
277 for(i=0; i<dft->voiNr; i++) if(dftCopyvoihdr(dft, i, dft2, i)) return 4;
278
279 /* Set times */
280 if(dft->timetype==DFT_TIME_STARTEND) {
281
283 t=0.0; dt=0.02; i=0;
284 if(verbose>1) printf("%05d: %12.5f %10.5f\n", i, t, dt);
285 dft2->x1[i]=t; dft2->x2[i]=t+dt; dft2->x[i]=0.5*(dft2->x1[i]+dft2->x2[i]);
286 i++;
287 while((t+2.5*dt)<endtime && newnr<maxNr-2) {
288 t+=dt; dt*=1.05;
289 if(verbose>1) printf("%05d: %12.5f %10.5f\n", i, t, dt);
290 dft2->x1[i]=t; dft2->x2[i]=t+dt; dft2->x[i]=0.5*(dft2->x1[i]+dft2->x2[i]);
291 i++;
292 }
293 t+=dt; dt=endtime-t;
294 if(verbose>1) printf("%05d: %12.5f %10.5f\n", i, t, dt);
295 dft2->x1[i]=t; dft2->x2[i]=t+dt; dft2->x[i]=0.5*(dft2->x1[i]+dft2->x2[i]);
296 i++; dft2->frameNr=i;
297
298 /* Interpolate */
299 for(i=0; i<dft->voiNr; i++)
300 if(interpolate4pet(dft->x, dft->voi[i].y, dft->frameNr,
301 dft2->x1, dft2->x2, dft2->voi[i].y, NULL, NULL, dft2->frameNr)) {
302 dftEmpty(dft2); return 5;
303 }
304
305 } else if(dft->timetype==DFT_TIME_MIDDLE) {
306
308 t=0.0; dt=0.02; i=0;
309 if(verbose>1) printf("%05d: %12.5f %10.5f\n", i, t, dt);
310 dft2->x1[i]=t; dft2->x2[i]=t+dt; dft2->x[i]=0.5*(dft2->x1[i]+dft2->x2[i]);
311 i++;
312 while((t+2.5*dt)<endtime && newnr<maxNr-2) {
313 t+=dt; dt*=1.05;
314 if(verbose>1) printf("%05d: %12.5f %10.5f\n", i, t, dt);
315 dft2->x1[i]=t; dft2->x2[i]=t+dt; dft2->x[i]=0.5*(dft2->x1[i]+dft2->x2[i]);
316 i++;
317 }
318 t+=dt; dt=endtime-t;
319 if(verbose>1) printf("%05d: %12.5f %10.5f\n", i, t, dt);
320 dft2->x1[i]=t; dft2->x2[i]=t+2.0*dt;
321 dft2->x[i]=0.5*(dft2->x1[i]+dft2->x2[i]);
322 i++; dft2->frameNr=i;
323
324 /* Interpolate */
325 for(i=0; i<dft->voiNr; i++)
326 if(interpolate4pet(dft->x, dft->voi[i].y, dft->frameNr,
327 dft2->x1, dft2->x2, dft2->voi[i].y, NULL, NULL, dft2->frameNr)) {
328 dftEmpty(dft2); return 5;
329 }
330
331 }
332
333 return 0;
334}
int dftCopyvoihdr(DFT *dft1, int from, DFT *dft2, int to)
Definition dft.c:623
int dftSetmem(DFT *data, int frameNr, int voiNr)
Definition dft.c:57
void dftEmpty(DFT *data)
Definition dft.c:20
int dftCopymainhdr(DFT *dft1, DFT *dft2)
Definition dft.c:561
int interpolate4pet(double *x, double *y, int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, int newnr)
Interpolate and integrate TAC to PET frames.
Definition integr.c:510
#define DFT_TIME_MIDDLE
#define DFT_TIME_STARTEND
int timetype
double * x1
double * x2

◆ dftDivideFrames()

int dftDivideFrames ( DFT * dft,
int voi_index,
int add_nr,
DFT * dft2,
int verbose )

Interpolates TACs to automatically determined sample times with smaller intervals in the beginning. Only data in y arrays are interpolated; data in y2 and y3 are not used.

Returns
Function returns 0 when succesful, else a value >= 1.
Parameters
dftData to be interpolated into more time frames is read from this struct.
voi_indexRegion index [0..voiNr-1] that is interpolated; <0 means all VOIs.
add_nrNr of extra time frames that are created from each original frame; valid numers are 1-100; 1 doubles the frame number.
dft2Interpolated data is written in this struct; must be initiated, may be allocated but do not need to be; any previous content is deleted.
verboseVerbose level; if zero, then nothing is printed to stderr or stdout.

Definition at line 406 of file extrapolate.c.

420 {
421 int ret, fi, fj, i, new_frameNr=0, new_voiNr=0;
422 double fdur;
423
424 if(verbose>0) {
425 printf("dftDivideFrames(*dft, %d, %d, *dft2)\n", voi_index, add_nr);
426 fflush(stdout);
427 }
428 /* Check the arguments */
429 if(dft==NULL || dft2==NULL) return 1;
430 if(dft->frameNr<1 || dft->voiNr<1) return 2;
431 if(add_nr<1 || add_nr>100) return 3;
432 if(voi_index>=dft->voiNr) return 4;
433
434 /* Calculate how many frames and TACs will be needed */
435 if(dft->timetype==DFT_TIME_STARTEND) new_frameNr=(add_nr+1)*dft->frameNr;
436 else new_frameNr=(add_nr+1)*dft->frameNr-1;
437 if(voi_index<0) new_voiNr=dft->voiNr; else new_voiNr=1;
438 if(verbose>1) {
439 printf("new_frameNr := %d\n", new_frameNr);
440 printf("new_voiNr := %d\n", new_voiNr);
441 fflush(stdout);
442 }
443
444 /* Allocate memory for interpolated data if necessary */
445 if(new_frameNr>dft2->frameNr || new_voiNr>dft2->_voidataNr) {
446 if(verbose>1) {
447 printf("deleting old data and allocating new\n"); fflush(stdout);}
448 dftEmpty(dft2);
449 if(dftSetmem(dft2, new_frameNr, dft->voiNr)) return 11;
450 }
451
452 /* Copy header info */
453 ret=dftCopymainhdr(dft, dft2); if(ret!=0) return 12;
454 dft2->voiNr=new_voiNr; dft2->frameNr=new_frameNr;
455 if(voi_index>=0) ret=dftCopyvoihdr(dft, voi_index, dft2, 0);
456 else {
457 for(int ri=0; ri<dft->voiNr; ri++) {
458 ret=dftCopyvoihdr(dft, ri, dft2, ri); if(ret!=0) break;}
459 }
460 if(ret!=0) return 13;
461
462 /* Set new frame times and interpolate */
463 ret=0;
464 if(dft->timetype==DFT_TIME_STARTEND) {
465 for(fi=0, fj=0; fi<dft->frameNr; fi++) {
466 fdur=(dft->x2[fi]-dft->x1[fi])/(double)(add_nr+1);
467 for(i=0; i<add_nr+1; i++) {
468 fj=fi*(add_nr+1)+i;
469 dft2->x1[fj]=dft->x1[fi]+fdur*(double)i;
470 dft2->x2[fj]=dft2->x1[fj]+fdur;
471 dft2->x[fj]=0.5*(dft2->x1[fj]+dft2->x2[fj]);
472 if(voi_index>=0)
473 dft2->voi[0].y[fj]=dft->voi[voi_index].y[fi];
474 else
475 for(int ri=0; ri<dft->voiNr; ri++) dft2->voi[ri].y[fj]=dft->voi[ri].y[fi];
476 }
477 }
478 dft2->frameNr=fj+1;
479 } else {
480 dft2->x[0]=dft->x[0];
481 for(fi=1, fj=0; fi<dft->frameNr; fi++) {
482 fdur=(dft->x[fi]-dft->x[fi-1])/(double)(add_nr+1);
483 for(i=0; i<add_nr+1; i++) {
484 fj=(fi-1)*(add_nr+1) + i + 1;
485 dft2->x[fj]=dft->x[fi-1]+fdur*(double)(i+1);
486 }
487 }
488 dft2->frameNr=fj+1;
489 if(voi_index>=0) {
490 ret=interpolate(dft->x, dft->voi[voi_index].y, dft->frameNr,
491 dft2->x, dft2->voi[0].y, NULL, NULL, dft2->frameNr);
492 } else {
493 for(int ri=0; ri<dft->voiNr; ri++) {
494 ret=interpolate(dft->x, dft->voi[ri].y, dft->frameNr,
495 dft2->x, dft2->voi[ri].y, NULL, NULL, dft2->frameNr);
496 if(ret) break;
497 }
498 }
499 }
500 if(ret!=0) return 21;
501
502 return 0;
503}
int interpolate(double *x, double *y, int nr, double *newx, double *newy, double *newyi, double *newyii, int newnr)
Linear interpolation and integration.
Definition integr.c:28

◆ dftDoubleFrames()

int dftDoubleFrames ( DFT * dft,
DFT * dft2 )

Doubles the TAC sample number by making each sample/frame into two by linear interpolation. Only data in y arrays are interpolated; data in y2 and y3 are not used.

Returns
Function returns 0 when succesful, else a value >= 1.
Parameters
dftData to be interpolated is read from this structure.
dft2Interpolated data is written in this struct; must be initiated; any previous content is deleted.

Definition at line 343 of file extrapolate.c.

349 {
350 int ri, fi, fj, ret;
351 double f;
352
353
354 /* Check the arguments */
355 if(dft==NULL || dft2==NULL) return 1;
356 if(dft->frameNr<1 || dft->voiNr<1) return 2;
357 if(dft->timetype!=DFT_TIME_STARTEND && dft->x[0]<0.0) return 3;
358
359 /* Allocate memory for interpolated data */
360 dftEmpty(dft2); if(dftSetmem(dft2, 2*dft->frameNr, dft->voiNr)) return 11;
361 /* Copy header info */
362 (void)dftCopymainhdr(dft, dft2);
363 dft2->voiNr=dft->voiNr; dft2->frameNr=2*dft->frameNr;
364 for(ri=0; ri<dft->voiNr; ri++) if(dftCopyvoihdr(dft, ri, dft2, ri)) return 12;
365
366 /* Set new time frames and interpolate */
367 if(dft->timetype==DFT_TIME_STARTEND) {
368 for(fi=fj=0; fi<dft->frameNr; fi++, fj+=2) {
369 f=0.5*(dft->x1[fi]+dft->x2[fi]);
370 dft2->x1[fj]=dft->x1[fi]; dft2->x2[fj]=f;
371 dft2->x[fj]=0.5*(dft2->x1[fj]+dft2->x2[fj]);
372 dft2->x1[fj+1]=f; dft2->x2[fj+1]=dft->x2[fi];
373 dft2->x[fj+1]=0.5*(dft2->x1[fj+1]+dft2->x2[fj+1]);
374 for(ri=0; ri<dft->voiNr; ri++)
375 dft2->voi[ri].y[fj]=dft2->voi[ri].y[fj+1]=dft->voi[ri].y[fi];
376 }
377 ret=0;
378 } else {
379 for(fi=fj=0; fi<dft->frameNr; fi++) {
380 if(dft->x[fi]<=0.0) {
381 /* just copy, no doubles */
382 dft2->x[fj++]=dft->x[fi]; continue;
383 }
384 if(fi==0) f=0.5*dft->x[fi];
385 else f=0.5*(dft->x[fi-1]+dft->x[fi]);
386 dft2->x[fj++]=f; dft2->x[fj++]=dft->x[fi];
387 }
388 dft2->frameNr=fj;
389 for(ri=0, ret=0; ri<dft->voiNr; ri++) {
390 ret=interpolate(dft->x, dft->voi[ri].y, dft->frameNr,
391 dft2->x, dft2->voi[ri].y, NULL, NULL, dft2->frameNr);
392 if(ret) break;
393 }
394 }
395 if(ret) return 20+ret;
396 return 0;
397}

◆ extrapolate_monoexp()

int extrapolate_monoexp ( DFT * dft,
double * fittime,
int * min_nr,
int max_nr,
double mintime,
double extr_to,
DFT * ext,
FILE * loginfo,
char * status )

Extrapolation of exponentially decreasing tail of PET radiotracer plasma curves. This is accomplished by fitting line to the end-part of the plot of the natural logarithm of tracer concentration against time. By default, the included data points are determined based on maximum adjusted R^2 from at least three points; fit to the larger number of points is used in case difference in adjusted R^2 values is not larger than 0.0001. This function is used and tested with program extrapol.

Returns
Returns 0 when successful, otherwise <>0.
Parameters
dftPointer to original TAC data.
fittimeBy default, the search for the best line fit is started from the last sample towards the first sample; set fit time to -1 to use this default. However, if the end phase is unreliable or very noisy, you may want to set fittime to include only certain time range from the beginning. Function will write here the fittime that was actually used.
min_nrThe minimum number of samples used in searching the best fit; at least 2, but 3 is recommended. If data is very noisy, then this number may need to be increased. Function will write here the nr of samples that was actually used. This can be used as an alternative to mintime or in addition to it.
max_nrThe maximum number of samples used in searching the best fit; must be higher than min_nr, or set to -1 to not to limit the number.
mintimeMinimum time range used in searching the best fit. If data is very noisy, then this may need to be set, otherwise setting mintime to -1 will use the default. This can be used as an alternative to min_nr or in addition to it.
extr_toLast extrapolated sample time in same time units than in the data.
extPointer to data for extrapolated TACs. Struct must be initiated. Any existing data is deleted.
loginfoGive file pointer (for example stdout) where log information is printed; NULL if not needed.
statusPointer to a string (allocated for at least 64 chars) where error message or other execution status will be written; enter NULL, if not needed.

Definition at line 22 of file extrapolate.c.

56 {
57 int fi, ret, n, to, from, to_min, from_min, orig_max_nr;
58 int fitNr, snr;
59 double kel, c0=0.0, *cx, *cy;
60 double slope, slope_sd, ic, ic_sd, r, f, adj_r2, adj_r2_max;
61 double extr_sampl;
62
63
64 /* Check the input */
65 if(status!=NULL) sprintf(status, "program error");
66 if(dft==NULL || ext==NULL) return -1;
67 if(*min_nr<2) *min_nr=3; // Setting erroneous value to a recommended value
68 if(max_nr>-1 && max_nr<*min_nr) return -2;
69 orig_max_nr=max_nr;
70
71 /* Set the last sample to be fitted */
72 fitNr=dft->frameNr;
73 if(*fittime>0.0) {
74 while(dft->x[fitNr-1]>*fittime && fitNr>0) fitNr--;
75 if(loginfo!=NULL) fprintf(loginfo, "fitNr := %d\nTime range := %g - %g\n",
76 fitNr, dft->x[0], dft->x[fitNr-1]);
77 }
78 *fittime=dft->x[fitNr-1];
79 /* Check that there are at least 3 samples */
80 if(fitNr<3) { /* min_nr is checked later */
81 if(status!=NULL) sprintf(status, "too few samples for extrapolation");
82 return(2);
83 }
84
85 /* If mintime was set, then get min_nr based on that */
86 if(mintime>0.0) {
87 fi=0; while(dft->x[fi]<(*fittime-mintime) && fi<fitNr) fi++;
88 if(--fi<=0) {
89 if(status!=NULL) sprintf(status, "required minimum fit range too large");
90 return(2);
91 }
92 *min_nr=((fitNr-fi)>*min_nr?fitNr-fi:*min_nr);
93 }
94 if(loginfo!=NULL) fprintf(loginfo, "final_min_nr := %d\n", *min_nr);
95
96
97 /*
98 * Initiate data for extrapolated data
99 */
100 /* Determine the sampling time for extrapolated range */
101 extr_sampl=0.5*(dft->x[dft->frameNr-1]-dft->x[dft->frameNr-3]);
102 if(loginfo!=NULL) fprintf(loginfo, "extr_sampl=%g\n", extr_sampl);
103 if(extr_sampl<1.0E-8) {
104 if(status!=NULL) sprintf(status, "check sample times");
105 return(2);
106 }
107 /* Determine the nr of extrapolated samples */
108 f=extr_to-dft->x[dft->frameNr-1];
109 if(f<=0.0) {
110 if(status!=NULL) sprintf(status, "no extrapolation is needed");
111 return(2);
112 }
113 n=ceil(f/extr_sampl);
114 if(loginfo!=NULL) fprintf(loginfo, " extr_sampl=%g n=%d\n", extr_sampl, n);
115 /* Allocate memory */
116 dftEmpty(ext);
117 ret=dftSetmem(ext, dft->frameNr+n, dft->voiNr);
118 if(ret) {
119 if(status!=NULL) sprintf(status, "error in memory allocation.\n");
120 return(4);
121 }
122 ext->frameNr=dft->frameNr+n; ext->voiNr=dft->voiNr;
123 /* Set sample times */
124 for(fi=0; fi<dft->frameNr; fi++) {
125 ext->x[fi]=dft->x[fi]; ext->x1[fi]=dft->x1[fi]; ext->x2[fi]=dft->x2[fi];}
126 if(dft->timetype==DFT_TIME_MIDDLE) {
127 for(; fi<ext->frameNr; fi++) {
128 ext->x[fi]=ext->x[fi-1]+extr_sampl;
129 ext->x1[fi]=ext->x2[fi-1]; ext->x2[fi]=ext->x[fi]+0.5*extr_sampl;
130 }
131 } else {
132 for(; fi<ext->frameNr; fi++) {
133 ext->x1[fi]=ext->x2[fi-1]; ext->x2[fi]=ext->x1[fi]+extr_sampl;
134 ext->x[fi]=0.5*(ext->x1[fi]+ext->x2[fi]);
135 }
136 }
137 /* Copy "header" information */
138 dftCopymainhdr(dft, ext);
139 for(int ri=0; ri<dft->voiNr; ri++) dftCopyvoihdr(dft, ri, ext, ri);
140 /* Copy existing values */
141 for(int ri=0; ri<dft->voiNr; ri++)
142 for(int fi=0; fi<dft->frameNr; fi++)
143 ext->voi[ri].y[fi]=dft->voi[ri].y[fi];
144
145
146 /*
147 * Make ln transformation for TACs
148 */
149 if(loginfo!=NULL) fprintf(loginfo, "ln transformation\n");
150 for(int ri=0; ri<dft->voiNr; ri++)
151 for(int fi=0; fi<dft->frameNr; fi++) {
152 if(!isnan(dft->voi[ri].y[fi]) && dft->voi[ri].y[fi]>0.0)
153 dft->voi[ri].y2[fi]=log(dft->voi[ri].y[fi]);
154 else
155 dft->voi[ri].y2[fi]=nan("");
156 }
157
158
159 /*
160 * Compute best linear fit to the end of ln transformed TACs
161 */
162 if(loginfo!=NULL) fprintf(loginfo, "linear fitting\n");
163 cx=(double*)malloc(2*fitNr*sizeof(double)); if(cx==NULL) {
164 if(status!=NULL) sprintf(status, "out of memory");
165 return(6);
166 }
167 cy=cx+fitNr;
168 for(int ri=0; ri<dft->voiNr; ri++) {
169 kel=0.0; max_nr=orig_max_nr;
170 /* Print TAC name, if more than one was found */
171 if(dft->voiNr>1 && loginfo!=NULL)
172 fprintf(loginfo, "%s :\n", dft->voi[ri].name);
173
174 /* Copy appropriate TAC data */
175 for(fi=n=0; fi<fitNr; fi++)
176 if(dft->x[fi]>0.0 && !isnan(dft->voi[ri].y2[fi])) {
177 cx[n]=dft->x[fi]; cy[n++]=dft->voi[ri].y2[fi];}
178 if(n<*min_nr) {
179 if(status!=NULL) sprintf(status, "check the datafile (%d<%d)", n, *min_nr);
180 free(cx); return(7);
181 }
182 if(max_nr<=0) max_nr=n;
183 /* Search the plot range that gives the max adjusted R^2 */
184 from_min=to_min=-1; adj_r2_max=-9.99E+99;
185 for(from=n-max_nr, to=n-1; from<1+n-*min_nr; from++) {
186 snr=(to-from)+1;
187 /* Calculation of linear regression using pearson() */
188 ret=pearson(
189 cx+from, cy+from, snr, &slope, &slope_sd, &ic, &ic_sd, &r, &f
190 );
191 if(ret==0) {
192 adj_r2= 1.0 - ((1.0-r*r)*(double)(snr-1)) / (double)(snr-2);
193 } else {
194 adj_r2=-9.99E+99;
195 }
196 //if(cv<cv_min) {
197 if(adj_r2>adj_r2_max+0.0001) {
198 adj_r2_max=adj_r2; from_min=from; to_min=to; kel=-slope; c0=exp(ic);
199 }
200 if(loginfo!=NULL)
201 fprintf(loginfo, " adj_r2=%g from=%d (%g)\n", adj_r2, from, *(cx+from) );
202 }
203 if(from_min<0) {
204 if(status!=NULL) sprintf(status, "check the datafile");
205 free(cx); return(7);
206 }
207 /* Check the sign of slope */
208 if(kel>=0.0) {
209 /* negative slope i.e. positive kel is ok */
210 if(loginfo!=NULL)
211 fprintf(loginfo, " k(el)=%g adj_r2=%g C(0)=%g; %d data points.\n",
212 kel, adj_r2_max, c0, (to_min-from_min)+1 );
213 /* Calculate the extrapolated values */
214 for(fi=fitNr; fi<ext->frameNr; fi++) {
215 ext->voi[ri].y[fi]=c0*exp(-kel*ext->x[fi]);
216 }
217 } else {
218 /* If slope is positive, then extrapolate with line f(x)=b+0*t */
219 for(fi=fitNr-1, f=0.0, n=0; fi>=0 && n<3; fi--)
220 if(!isnan(dft->voi[ri].y[fi])) {f+=dft->voi[ri].y[fi]; n++;}
221 if(n>0) f/=(double)n;
222 if(status!=NULL)
223 sprintf(status, "curve end is not descending; extrapolation with horizontal line determined as avg of %d samples", n);
224 /* Calculate the extrapolated values */
225 for(fi=fitNr; fi<ext->frameNr; fi++) {
226 ext->voi[ri].y[fi]=f;
227 }
228 }
229 } /* next curve */
230 free(cx);
231
232 if(status!=NULL) sprintf(status, "ok");
233 return 0;
234}
double * y2