/*----------------------------------------------------------------------*\ | Program to implement the modern analog method of estimating past | | environmental parameters by comparing characteristics of fossil | | samples with those of the modern world. | | | | Peter N. Schweitzer (U.S. Geological Survey, Reston, VA 22092) | \*----------------------------------------------------------------------*/ #include #include #include #include "analog.h" struct data_base *modern = NULL; struct data_base *fossil = NULL; struct output report = {"","",1,0,0,NULL}; char distance_measure_name [MAX_NAME_LENGTH] = "squared_chord"; enum measure_type_t measure_type = DISTANCE; int verbose = 0; int search = 1; /*----------------------------------------------------------------------*\ | Given a pair of data bases, create a table that lists matching | | variable names. This is used to ensure that the distances are | | calculated on the same axes. | \*----------------------------------------------------------------------*/ static struct match *create_match_table (struct data_base *p, struct data_base *q) { int i,j,k,n; int d; struct match *match = NULL; n = min (p->data.count,q->data.count); if (match = (struct match *) malloc ((1 + n) * sizeof (struct match))) { i = j = k = 0; while (i < p->data.count && j < q->data.count) { if ((d = strcmp (p->data.name[i],q->data.name[j])) < 0) { sprintf (message,"Warning: data from %s contains variable \"%s\" not found in %s", p->raw.filespec, p->data.name[i], q->raw.filespec ); warning (message); i++; } else if (d > 0) { sprintf (message,"Warning: data from %s contains variable \"%s\" not found in %s", q->raw.filespec, q->data.name[j], p->raw.filespec ); warning (message); j++; } else { match[k].p = i++; match[k].q = j++; k++; } } match[k].p = match[k].q = -1; } else { sprintf (message,"Error: could not allocate space for match table"); error_exit (message); } return (match); } /*----------------------------------------------------------------------*\ | Sorting comparison functions; dcompare sorts distance measures so | | the key values of the resulting array increase, and scompare sorts | | similarity measures, so the key values of the result will decrease. | \*----------------------------------------------------------------------*/ static int dcompare (const void *e1, const void *e2) { struct comparison *L1,*L2; L1 = (struct comparison *) e1; L2 = (struct comparison *) e2; if (L1->d < L2->d) return (-1); else if (L1->d > L2->d) return (1); else return (0); } static int scompare (const void *e1, const void *e2) { struct comparison *L1,*L2; L1 = (struct comparison *) e1; L2 = (struct comparison *) e2; if (L1->d < L2->d) return (1); else if (L1->d > L2->d) return (-1); else return (0); } /*----------------------------------------------------------------------*\ \*----------------------------------------------------------------------*/ main (int argc, char *argv[]) { int i,j,k; char *job_file = NULL; distance_function distance; int sample_count; struct comparison *score = NULL; struct data_base *p,*q; struct sample *f,*m; struct match *match; int (*compare) (const void *e1, const void *e2); if (argc <= 1) { printf ("%s\n",version); printf ("\n"); printf ("A program for estimating paleoclimate parameters\n"); printf ("using the method of modern analogs\n"); printf ("\n"); printf ("Peter N. Schweitzer (U.S. Geological Survey, Reston VA 20192)\n"); printf ("\n"); printf (" Usage: name run description file on the command line\n"); exit (0); } else for (i=1; i < argc; i++) if (memcmp (argv[i],"-search",7) == 0) search = 1; else if (memcmp (argv[i],"-nosearch",7) == 0) search = 0; else job_file = argv[i]; /*------------------------------------------------------------------*\ | If no forward slashes appear in the file name, then assume this | | is a Windows system, and replace backslashes with forwards. | \*------------------------------------------------------------------*/ if (strchr (job_file,'/') == NULL) for (i=0; job_file[i]; i++) if (job_file[i] == '\\') job_file[i] = '/'; parse_run_configuration (job_file); /*------------------------------------------------------------------*\ \*------------------------------------------------------------------*/ if (verbose) { printf ("\n"); printf ("%s\n",version); printf ("\n"); for (p=modern,i=0; p; p=p->next,i++) { printf ("Modern data base %d:\n",i+1); printf (" raw data filespec: \"%s\"\n",p->raw.filespec); if (p->raw.format) printf (" format: \"%s\"\n",p->raw.format); printf (" transform filespec: \"%s\"\n",p->rule.filespec); printf (" meta data filespec: \"%s\"\n",p->meta.filespec); if (p->meta.format) printf (" format: \"%s\"\n",p->meta.format); } printf ("\n"); for (p=fossil,i=0; p; p=p->next,i++) { printf ("Fossil data base %d:\n",i+1); printf (" raw data filespec: \"%s\"\n",p->raw.filespec); if (p->raw.format) printf (" format: \"%s\"\n",p->raw.format); printf (" transform filespec: \"%s\"\n",p->rule.filespec); printf (" meta data filespec: \"%s\"\n",p->meta.filespec); if (p->meta.format) printf (" format: \"%s\"\n",p->meta.format); i++; } printf ("\n"); printf ("Distance measure: \"%s\"\n",distance_measure_name); printf ("\n"); printf ("Output\n"); printf (" filespec: \"%s\"\n",report.filespec); printf (" format: \"%s\"\n",report.format); printf (" closest %d\n",report.closest); if (report.count) printf (" meta data variables to report:\n"); for (i=0; i < report.count; i++) printf (" \"%s\"\n",report.name[i]); } /*------------------------------------------------------------------*\ | Assign the function pointer "distance" by looking up the name of | | the distance measure in a table that relates distance functions | | to the names of their distance measures. | \*------------------------------------------------------------------*/ distance = set_distance_function (distance_measure_name); measure_type = get_measure_type (distance_measure_name); /*------------------------------------------------------------------*\ | Read the data for the modern samples. Read the transformation | | file, if any, the meta data file, if any, and transform the raw | | data if necessary. | \*------------------------------------------------------------------*/ for (p=modern,i=0; p; p=p->next,i++) read_data_base (p); /*------------------------------------------------------------------*\ | Read the data for the fossil samples. Read the transformation | | file, if any, the meta data file, if any, and transform the raw | | data if necessary. | \*------------------------------------------------------------------*/ for (p=fossil,i=0; p; p=p->next,i++) read_data_base (p); /*------------------------------------------------------------------*\ | If the requested output meta data list contains the word "all", | | then we must build a name list that will work for all of the | | modern data bases that were read in. | \*------------------------------------------------------------------*/ for (i=0; i < report.count; i++) if (stricmp (report.name[i],"all") == 0) break; if (i < report.count) { /*--------------------------------------------------------------*\ | "all" occurred in the report.name list. Destroy the list | | and rebuild it, using p->meta.name[j] for all j, for all p. | \*--------------------------------------------------------------*/ for (k=0; k < report.count; k++) if (report.name[k]) { free (report.name[k]); report.name[k] = NULL; } report.count = 0; for (p=modern,i=0; p; p=p->next,i++) { for (j=0; j < p->meta.count; j++) { /*------------------------------------------------------*\ | Determine whether p->meta.name[j] is already in the | | list. The list is not sorted, so the search must be | | linear. | \*------------------------------------------------------*/ for (k=0; k < report.count; k++) if (strcmp (p->meta.name[j],report.name[k]) == 0) break; if (k == report.count) { report.name[report.count++] = p->meta.name[j]; if (report.count >= report.limit) { if (report.name = (char **) realloc (report.name,(report.limit + GRANULARITY) * sizeof (char *))) report.limit += GRANULARITY; else { sprintf (message,"Error: could not enlarge space for output meta data names"); error_exit (message); } } } } } } /*------------------------------------------------------------------*\ | For each fossil sample, calculate its distance from each modern | | sample. Sort the modern samples according to their similarity | | with the fossil sample. Write results as requested by the user. | \*------------------------------------------------------------------*/ if (measure_type == DISTANCE) compare = dcompare; else compare = scompare; sample_count = 0; for (p=modern; p; p=p->next) sample_count += p->count; if (score = (struct comparison *) malloc (sample_count * sizeof (struct comparison))) { match = NULL; for (q=fossil; q; q=q->next) { f = q->sample; for (i=0; i < q->count; i++) { j = 0; for (p=modern; p; p=p->next) { if (i == 0) { if (match) free (match); match = create_match_table (q,p); } m = p->sample; for (k=0; k < p->count; k++) { score[j].s = m; score[j].d = distance (f,m,match); j++; m++; } } qsort (score,sample_count,sizeof(struct comparison),compare); write_results (f,score); f++; } } free (score); } else { sprintf (message,"Error: could not allocate space for comparison array"); error_exit (message); } } /*----------------------------------------------------------------------*\ \*----------------------------------------------------------------------*/