home *** CD-ROM | disk | FTP | other *** search
- /*--------------------------------------------------------------------
- * The GMT-system: @(#)gmt_vector.c 2.5 2/17/95
- *
- * Copyright (c) 1991-1995 by P. Wessel and W. H. F. Smith
- * See README file for copying and redistribution conditions.
- *--------------------------------------------------------------------*/
-
- /* jacobi.c
- *
- * Find eigenvalues & eigenvectors of a square symmetric matrix by Jacobi's
- * method, which is a convergent series of Givens rotations.
- * Modified from Numerical Recipes FORTRAN edition.
- * Returns integer 0 if OK, -1 if failure to converge in MAX_SWEEPS.
- *
- * programmer: W. H. F. Smith, 7 June, 1991-1995.
- *
- * Caveat Emptor! Assumes underflows return zero without killing execution.
- * I am not sure what happens if the eigenvalues are degenerate or not distinct.
- */
-
- #include "gmt.h"
-
- #define MAX_SWEEPS 50
-
- int jacobi(a, n, m, d, v, b, z, nrots)
-
- double a[]; /* Sent. n by n matrix in full storage mode.
- On return, superdiagonal elements are destroyed. */
- int *n; /* Sent. row and column dimension of a as used. */
- int *m; /* Sent. row and column dimension of a and v as
- allocated, so that a(i,j) is at a[i + (*m)*j]. */
- double d[]; /* Returned. vector of n eigenvalues of a. */
- double v[]; /* Returned. n x n matrix of eigenvectors of a,
- with row dimension m */
- double b[]; /* Work vector of n elements must be supplied. */
- double z[]; /* Another work vector of n elements must be supplied. */
- int *nrots; /* Returned. number of Givens rotations performed. */
-
- {
- int ip, iq, nsweeps, i, j, k;
- double sum, threshold, g, h, t, theta, c, s, tau, p;
-
-
- /* Begin by initializing v, b, d, and z. v = identity matrix,
- b = d = diag(a), and z = 0: */
-
- for (ip = 0; ip < (*n); ip++) {
- for (iq = 0; iq < (*n); iq++) {
- v[ip + (*m)*iq] = 0.0;
- }
- v[ip + (*m)*ip] = 1.0;
- b[ip] = a[ip + (*m)*ip];
- d[ip] = b[ip];
- z[ip] = 0.0;
- }
-
- /* End of initializations. Set counters and begin: */
-
- (*nrots) = 0;
- nsweeps = 0;
-
- while (nsweeps < MAX_SWEEPS) {
-
- /* Convergence test:
- Sum off-diagonal elements of upper triangle.
- When sum == 0.0 (underflow !) we have converged.
- In this case, break out of while loop. */
-
- sum = 0.0;
- for (ip = 0; ip < (*n)-1; ip++) {
- for (iq = ip+1; iq < (*n); iq++) {
- sum += fabs(a[ip + (*m)*iq]);
- }
- }
- if (sum == 0.0) break;
-
- /* Now we are not converged.
- If (nsweeps < 3) do only the big elements;
- else do them all. */
- if (nsweeps < 3) {
- threshold = 0.2 * sum / ( (*n) * (*n) );
- }
- else {
- threshold = 0.0;
- }
-
- /* Now sweep whole upper triangle doing Givens rotations: */
-
- for (ip = 0; ip < (*n) - 1; ip++) {
- for (iq = ip+1; iq < (*n); iq++) {
-
- /* After four sweeps, if the off-diagonal
- element is "small", skip the rotation
- and just set it to zero. "Small" is
- a relative test by addition: */
-
- g = 100.0 * fabs(a[ip + (*m)*iq]);
-
- if ( (nsweeps > 3) && ( (fabs(d[ip])+g) == fabs(d[ip]) ) && ( (fabs(d[iq])+g) == fabs(d[iq]) ) ) {
- a[ip + (*m)*iq] = 0.0;
- }
- else if (fabs(a[ip + (*m)*iq]) > threshold) {
-
- h = d[iq] - d[ip];
-
- if ( (fabs(h)+g) == fabs(h) ) {
- /* I think this could divide by zero if a(i,j) = a(i,i) = a(j,j) = 0.0.
- Would this occur only in a degenerate matrix? */
- t = a[ip + (*m)*iq] / h;
- }
- else {
- theta = 0.5 * h / a[ip + (*m)*iq];
- t = 1.0 / (fabs(theta) + sqrt(1.0 + theta*theta) );
- if (theta < 0.0) t = -t;
- }
-
- c = 1.0 / sqrt(1.0 + t*t);
- s = t * c;
- tau = s / (1.0 + c);
-
- h = t * a[ip + (*m)*iq];
- z[ip] -= h;
- z[iq] += h;
- d[ip] -= h;
- d[iq] += h;
- a[ip + (*m)*iq] = 0.0;
-
- for (j = 0; j < ip; j++) {
- g = a[j + (*m)*ip];
- h = a[j + (*m)*iq];
- a[j + (*m)*ip] = g - s * (h + g * tau);
- a[j + (*m)*iq] = h + s * (g - h * tau);
- }
- for (j = ip+1; j < iq; j++) {
- g = a[ip + (*m)*j];
- h = a[j + (*m)*iq];
- a[ip + (*m)*j] = g - s * (h + g * tau);
- a[j + (*m)*iq] = h + s * (g - h * tau);
- }
- for (j = iq+1; j < (*n); j++) {
- g = a[ip + (*m)*j];
- h = a[iq + (*m)*j];
- a[ip + (*m)*j] = g - s * (h + g * tau);
- a[iq + (*m)*j] = h + s * (g - h * tau);
- }
-
- for (j = 0; j < (*n); j++) {
- g = v[j + (*m)*ip];
- h = v[j + (*m)*iq];
- v[j + (*m)*ip] = g - s * (h + g * tau);
- v[j + (*m)*iq] = h + s * (g - h * tau);
- }
-
- (*nrots)++;
- }
- }
- }
-
- for (ip = 0; ip < (*n); ip++) {
- b[ip] += z[ip];
- d[ip] = b[ip];
- z[ip] = 0.0;
- }
-
- nsweeps++;
- }
-
- /* Get here via break when converged, or when nsweeps == MAX_SWEEPS.
- Sort eigenvalues by insertion: */
-
- for (i = 0; i < (*n)-1; i++) {
- k = i;
- p = d[i];
- for (j = i+1; j < (*n); j++) { /* Find max location */
- if (d[j] >= p) {
- k = j;
- p = d[j];
- }
- }
- if (k != i) { /* Need to swap value and vector */
- d[k] = d[i];
- d[i] = p;
- for (j = 0; j < (*n); j++) {
- p = v[j + (*m)*i];
- v[j + (*m)*i] = v[j + (*m)*k];
- v[j + (*m)*k] = p;
- }
- }
- }
-
- /* Return 0 if converged; else print warning and return -1: */
-
- if (nsweeps == MAX_SWEEPS) {
- fprintf(stderr,"jacobi: Failed to converge in %d sweeps\n", nsweeps);
- return(-1);
- }
- return(0);
- }
-
- /* cartesian_stuff.c -- bits and pieces for doing spherical trig
- * in terms of dot and cross products.
- *
- * w. h. f. smith, 16 June. 1989
- *
- */
-
- double dot3v(a,b)
- double *a, *b;
- {
- return(a[0]*b[0] + a[1]*b[1] + a[2]*b[2]);
- }
-
- double mag3v(a)
- double *a;
- {
- return(d_sqrt(a[0]*a[0] + a[1]*a[1] + a[2]*a[2]));
- }
-
- void normalize3v(a)
- double *a;
- {
- double r_length;
- r_length = (mag3v(a));
- if (r_length != 0.0) {
- r_length = 1.0 / r_length;
- a[0] *= r_length;
- a[1] *= r_length;
- a[2] *= r_length;
- }
- }
-
- void cross3v(a,b,c)
- double *a, *b, *c;
- {
- c[0] = a[1] * b[2] - a[2] * b[1];
- c[1] = a[2] * b[0] - a[0] * b[2];
- c[2] = a[0] * b[1] - a[1] * b[0];
- }
-
- void geo_to_cart(alat, alon, a, rads)
- double *alat, *alon, *a;
- int rads;
- {
- /* Convert geographic latitude and longitude (alat, alon)
- to a 3-vector of unit length (a). rads = TRUE if we
- need to convert alat, alon from degrees to radians */
-
- double cos_lat;
-
- if (rads) {
- *alat *= D2R;
- *alon *= D2R;
- }
-
- cos_lat = cos(*alat);
- a[0] = cos_lat * cos(*alon);
- a[1] = cos_lat * sin(*alon);
- a[2] = sin(*alat);
- }
-
- void cart_to_geo(alat, alon, a, rads)
- double *alat, *alon, *a;
- int rads;
- {
- /* Convert a 3-vector (a) of unit length into geographic
- coordinates (alat, alon). rads = TRUE if we want the
- lat and lon converted from radians into degrees. */
-
- if(rads) {
- *alat = R2D * d_asin(a[2]);
- *alon = R2D * d_atan2(a[1], a[0]);
- }
- else {
- *alat = d_asin(a[2]);
- *alon = d_atan2(a[1],a[0]);
- }
- }
-
- int fix_up_path (a_lon, a_lat, n, greenwich, step)
- double *a_lon[], *a_lat[];
- int n;
- BOOLEAN greenwich; /* TRUE if we cross Greenwich */
- double step; /* Add points when step degrees are exceeded */
- {
-
- /* Takes pointers to a list of lon/lat pairs and adds auxiliary points if
- * the great circle distance between two given points exceeds
- * <step> spherical degree [ 1 degree].
- */
-
- int i, j, n_tmp, n_insert = 0, n_alloc;
- double *lon_tmp, *lat_tmp, *old;
- double a[3], b[3], x[3], *lon, *lat;
- double c, d, fraction, theta, i_step;
-
- lon = *a_lon;
- lat = *a_lat;
-
- n_alloc = n;
- lon_tmp = (double *) memory (CNULL, n_alloc, sizeof (double), "fix_up_path");
- lat_tmp = (double *) memory (CNULL, n_alloc, sizeof (double), "fix_up_path");
-
- geo_to_cart (&lat[0], &lon[0], a, TRUE);
- lon_tmp[0] = (lon[0] >= M_PI) ? lon[0] - 2.0*M_PI : lon[0]; lat_tmp[0] = lat[0];
- n_tmp = 1;
- if (step <= 0.0) step = 1.0;
- i_step = 1.0 / step;
-
- for (i = 1; i < n; i++) {
-
- geo_to_cart (&lat[i],&lon[i], b, TRUE);
-
- if ((theta = d_acos (dot3v (a, b))) == M_PI) { /* trouble, no unique great circle */
- fprintf (stderr, "GMT Warning: Two points in input list are antipodal!\n");
- }
- else if ((n_insert = floor (theta * R2D * i_step))) { /* Must insert n_insert points */
- fraction = step * D2R / theta;
- for (j = 1; j <= n_insert; j++) {
- c = j * fraction;
- d = 1 - c;
- x[0] = a[0] * d + b[0] * c;
- x[1] = a[1] * d + b[1] * c;
- x[2] = a[2] * d + b[2] * c;
- normalize3v (x);
- cart_to_geo (&lat_tmp[n_tmp], &lon_tmp[n_tmp], x, FALSE);
- n_tmp++;
- if (n_tmp == n_alloc) {
- n_alloc += GMT_CHUNK;
- lon_tmp = (double *) memory ((char *) lon_tmp, n_alloc, sizeof (double), "fix_up_path");
- lat_tmp = (double *) memory ((char *) lat_tmp, n_alloc, sizeof (double), "fix_up_path");
- }
- }
- }
- lon_tmp[n_tmp] = (lon[i] >= M_PI) ? lon[i] - 2.0 * M_PI : lon[i]; lat_tmp[n_tmp] = lat[i];
- n_tmp++;
- if (n_tmp == n_alloc) {
- n_alloc += GMT_CHUNK;
- lon_tmp = (double *) memory ((char *) lon_tmp, n_alloc, sizeof (double), "fix_up_path");
- lat_tmp = (double *) memory ((char *) lat_tmp, n_alloc, sizeof (double), "fix_up_path");
- }
- a[0] = b[0]; a[1] = b[1]; a[2] = b[2];
- }
- lon_tmp = (double *) memory ((char *) lon_tmp, n_tmp, sizeof (double), "fix_up_path");
- lat_tmp = (double *) memory ((char *) lat_tmp, n_tmp, sizeof (double), "fix_up_path");
-
- old = lon;
- lon = lon_tmp;
- free ((char *) old);
- old = lat;
- lat = lat_tmp;
- free ((char *) old);
- for (i = 0; i < n_tmp; i++) {
- lon[i] *= R2D;
- if (!greenwich && lon[i] < 0.0) lon[i] += 360.0;
- lat[i] *= R2D;
- }
- *a_lon = lon;
- *a_lat = lat;
- return (n_tmp);
- }
-