Example #1
0
int main(int argc, string argv[])
{
  stream istr;
  string bodytags[] = { PosTag, NULL }, intags[MaxBodyFields];
  bodyptr btab = NULL, bp;
  int nbody, nshell, n;
  real tnow, vals[3];
  matrix tmpm, qmat;
  vector v1, v2, v3;

  initparam(argv, defv);
  istr = stropen(getparam("in"), "r");
  get_history(istr);
  layout_body(bodytags, Precision, NDIM);
  printf("#%11s %3s %11s %11s %11s\n",
	 "time", "n", "r_rms", "c/a", "b/a");
  while (get_snap(istr, &btab, &nbody, &tnow, intags, FALSE)) {
    if (! set_member(intags, PosTag))
      error("%s: %s data missing\n", getargv0(), PosTag);
    if (nbody % getiparam("nbin") != 0)
      error("%s: nbin does not divide number of bodies\n", getargv0());
    nshell = nbody / getiparam("nbin");
    for (n = 0; n < nbody; n += nshell) {
      CLRM(qmat);
      for (bp = NthBody(btab, n); bp < NthBody(btab, n + nshell);
	   bp = NextBody(bp)) {
	OUTVP(tmpm, Pos(bp), Pos(bp));
	ADDM(qmat, qmat, tmpm);
      }
      eigensolve(v1, v2, v3, vals, qmat);
      printf(" %11.6f %3d %11.6f %11.6f %11.6f\n",
	     tnow, n / nshell, rsqrt(tracem(qmat) / nshell),
	     rsqrt(vals[2] / vals[0]), rsqrt(vals[1] / vals[0]));
      if (getbparam("listvec")) {
	printf("#\t\t\t\t\t\t\t%8.5f  %8.5f  %8.5f\n", v1[0], v1[1], v1[2]);
	printf("#\t\t\t\t\t\t\t%8.5f  %8.5f  %8.5f\n", v2[0], v2[1], v2[2]);
	printf("#\t\t\t\t\t\t\t%8.5f  %8.5f  %8.5f\n", v3[0], v3[1], v3[2]);
      }
    }
  }
  return (0);
}
Example #2
0
void 
coarsen (
/* Coarsen until nvtxs <= vmax, compute and uncoarsen. */
    struct vtx_data **graph,	/* array of vtx data for graph */
    int nvtxs,		/* number of vertices in graph */
    int nedges,		/* number of edges in graph */
    int using_vwgts,		/* are vertices weights being used? */
    int using_ewgts,		/* are edge weights being used? */
    float *term_wgts[],		/* terminal weights */
    int igeom,		/* dimension for geometric information */
    float **coords,		/* coordinates for vertices */
    double **yvecs,		/* eigenvectors returned */
    int ndims,		/* number of eigenvectors to calculate */
    int solver_flag,		/* which eigensolver to use */
    int vmax,			/* largest subgraph to stop coarsening */
    double eigtol,		/* tolerence in eigen calculation */
    int nstep,		/* number of coarsenings between RQI steps */
    int step,			/* current step number */
    int give_up		/* has coarsening bogged down? */
)
{
    extern FILE *Output_File;	/* output file or null */
    extern int DEBUG_COARSEN;	/* debug flag for coarsening */
    extern int PERTURB;		/* was matrix perturbed in Lanczos? */
    extern double COARSEN_RATIO_MIN;	/* min vtx reduction for coarsening */
    extern int COARSEN_VWGTS;	/* use vertex weights while coarsening? */
    extern int COARSEN_EWGTS;	/* use edge weights while coarsening? */
    extern double refine_time;	/* time for RQI/Symmlq iterative refinement */
    struct vtx_data **cgraph;	/* array of vtx data for coarsened graph */
    struct orthlink *orthlist;	/* list of lower evecs to suppress */
    struct orthlink *newlink;	/* lower evec to suppress */
    double   *cyvecs[MAXDIMS + 1];	/* eigenvectors for subgraph */
    double    evals[MAXDIMS + 1];	/* eigenvalues returned */
    double    goal[MAXSETS];	/* needed for convergence mode = 1 */
    double   *r1, *r2, *work;	/* space needed by symmlq/RQI */
    double   *v, *w, *x, *y;	/* space needed by symmlq/RQI */
    double   *gvec;		/* rhs vector in extended eigenproblem */
    double    evalest;		/* eigenvalue estimate returned by RQI */
    double    maxdeg;		/* maximum weighted degree of a vertex */
    float   **ccoords;		/* coordinates for coarsened graph */
    float    *cterm_wgts[MAXSETS];	/* coarse graph terminal weights */
    float    *new_term_wgts[MAXSETS];	/* terminal weights for Bui's method*/
    float   **real_term_wgts;	/* one of the above */
    float    *twptr;		/* loops through term_wgts */
    float    *twptr_save;	/* copy of twptr */
    float    *ctwptr;		/* loops through cterm_wgts */
    double   *vwsqrt = NULL;	/* square root of vertex weights */
    double    norm, alpha;	/* values used for orthogonalization */
    double    initshift;	/* initial shift for RQI */
    double    total_vwgt;	/* sum of all the vertex weights */
    double    w1, w2;		/* weights of two sets */
    double    sigma;		/* norm of rhs in extended eigenproblem */
    double    term_tot;		/* sum of all terminal weights */
    int    *space;		/* room for assignment in Lanczos */
    int      *morespace;	/* room for assignment in Lanczos */
    int      *v2cv;		/* mapping from vertices to coarse vtxs */
    int       vwgt_max;		/* largest vertex weight */
    int       oldperturb;	/* saves PERTURB value */
    int       cnvtxs;		/* number of vertices in coarsened graph */
    int       cnedges;		/* number of edges in coarsened graph */
    int       nextstep;		/* next step in RQI test */
    int       nsets;		/* number of sets being created */
    int       i, j;		/* loop counters */
    double    time;		/* time marker */

    double   dot(), ch_normalize(), find_maxdeg(), seconds();
    struct orthlink *makeorthlnk();
    void      makevwsqrt(), eigensolve(), coarsen1(), orthogvec(), rqi_ext();
    void      ch_interpolate(), orthog1(), rqi(), scadd(), free_graph();

    if (DEBUG_COARSEN > 0) {
	printf("<Entering coarsen, step=%d, nvtxs=%d, nedges=%d, vmax=%d>\n",
	       step, nvtxs, nedges, vmax);
    }

    nsets = 1 << ndims;

    /* Is problem small enough to solve? */
    if (nvtxs <= vmax || give_up) {
	if (using_vwgts) {
	    vwsqrt = smalloc((nvtxs + 1) * sizeof(double));
	    makevwsqrt(vwsqrt, graph, nvtxs);
	}
	else
	    vwsqrt = NULL;
	maxdeg = find_maxdeg(graph, nvtxs, using_ewgts, (float *) NULL);

	if (using_vwgts) {
	    vwgt_max = 0;
	    total_vwgt = 0;
	    for (i = 1; i <= nvtxs; i++) {
		if (graph[i]->vwgt > vwgt_max)
		    vwgt_max = graph[i]->vwgt;
		total_vwgt += graph[i]->vwgt;
	    }
	}
	else {
	    vwgt_max = 1;
	    total_vwgt = nvtxs;
	}
	for (i = 0; i < nsets; i++)
	    goal[i] = total_vwgt / nsets;

	space = smalloc((nvtxs + 1) * sizeof(int));

	/* If not coarsening ewgts, then need care with term_wgts. */
	if (!using_ewgts && term_wgts[1] != NULL && step != 0) {
	    twptr = smalloc((nvtxs + 1) * (nsets - 1) * sizeof(float));
	    twptr_save = twptr;
	    for (j = 1; j < nsets; j++) {
	        new_term_wgts[j] = twptr;
	        twptr += nvtxs + 1;
	    }

	    for (j = 1; j < nsets; j++) {
	        twptr = term_wgts[j];
	        ctwptr = new_term_wgts[j];
	        for (i = 1; i <= nvtxs; i++) {
		    if (twptr[i] > .5) ctwptr[i] = 1;
		    else if (twptr[i] < -.5) ctwptr[i] = -1;
		    else ctwptr[i] = 0;
		}
	    }
	    real_term_wgts = new_term_wgts;
	}
	else {
	    real_term_wgts = term_wgts;
	    new_term_wgts[1] = NULL;
	}

	eigensolve(graph, nvtxs, nedges, maxdeg, vwgt_max, vwsqrt,
		   using_vwgts, using_ewgts, real_term_wgts, igeom, coords,
		   yvecs, evals, 0, space, goal,
		   solver_flag, FALSE, 0, ndims, 3, eigtol);

	if (real_term_wgts != term_wgts && new_term_wgts[1] != NULL) {
	    sfree(real_term_wgts[1]);
	}
	sfree(space);
	if (vwsqrt != NULL)
	    sfree(vwsqrt);
	return;
    }

    /* Otherwise I have to coarsen. */

    if (coords != NULL) {
	ccoords = smalloc(igeom * sizeof(float *));
    }
    else {
	ccoords = NULL;
    }
    coarsen1(graph, nvtxs, nedges, &cgraph, &cnvtxs, &cnedges,
	     &v2cv, igeom, coords, ccoords, using_ewgts);

    /* If coarsening isn't working very well, give up and partition. */
    give_up = FALSE;
    if (nvtxs * COARSEN_RATIO_MIN < cnvtxs && cnvtxs > vmax ) {
	printf("WARNING: Coarsening not making enough progress, nvtxs = %d, cnvtxs = %d.\n",
	    nvtxs, cnvtxs);
	printf("         Recursive coarsening being stopped prematurely.\n");
	if (Output_File != NULL) {
	    fprintf(Output_File,
		"WARNING: Coarsening not making enough progress, nvtxs = %d, cnvtxs = %d.\n",
	        nvtxs, cnvtxs);
	    fprintf(Output_File,
		"         Recursive coarsening being stopped prematurely.\n");
	}
	give_up = TRUE;
    }

    /* Create space for subgraph yvecs. */
    for (i = 1; i <= ndims; i++) {
	cyvecs[i] = smalloc((cnvtxs + 1) * sizeof(double));
    }

    /* Make coarse version of terminal weights. */
    if (term_wgts[1] != NULL) {
	twptr = smalloc((cnvtxs + 1) * (nsets - 1) * sizeof(float));
	twptr_save = twptr;
	for (i = (cnvtxs + 1) * (nsets - 1); i ; i--) {
	    *twptr++ = 0;
	}
	twptr = twptr_save;
	for (j = 1; j < nsets; j++) {
	    cterm_wgts[j] = twptr;
	    twptr += cnvtxs + 1;
	}
	for (j = 1; j < nsets; j++) {
	    ctwptr = cterm_wgts[j];
	    twptr = term_wgts[j];
	    for (i = 1; i < nvtxs; i++){
	        ctwptr[v2cv[i]] += twptr[i];
	    }
	}
    }
    else {
	cterm_wgts[1] = NULL;
    }

    /* Now recurse on coarse subgraph. */
    nextstep = step + 1;
    coarsen(cgraph, cnvtxs, cnedges, COARSEN_VWGTS, COARSEN_EWGTS, cterm_wgts,
	    igeom, ccoords, cyvecs, ndims, solver_flag, vmax, eigtol,
	    nstep, nextstep, give_up);

    ch_interpolate(yvecs, cyvecs, ndims, graph, nvtxs, v2cv, using_ewgts);

    sfree(cterm_wgts[1]);
    sfree(v2cv);

    /* I need to do Rayleigh Quotient Iteration each nstep stages. */
    time = seconds();
    if (!(step % nstep)) {
	oldperturb = PERTURB;
	PERTURB = FALSE;
	/* Should I do some orthogonalization here against vwsqrt? */
	if (using_vwgts) {
	    vwsqrt = smalloc((nvtxs + 1) * sizeof(double));
	    makevwsqrt(vwsqrt, graph, nvtxs);

	    for (i = 1; i <= ndims; i++)
		orthogvec(yvecs[i], 1, nvtxs, vwsqrt);
	}
	else
	    for (i = 1; i <= ndims; i++)
		orthog1(yvecs[i], 1, nvtxs);

	/* Allocate space that will be needed in RQI. */
	r1 = smalloc(7 * (nvtxs + 1) * sizeof(double));
	r2 = &r1[nvtxs + 1];
	v = &r1[2 * (nvtxs + 1)];
	w = &r1[3 * (nvtxs + 1)];
	x = &r1[4 * (nvtxs + 1)];
	y = &r1[5 * (nvtxs + 1)];
	work = &r1[6 * (nvtxs + 1)];

	if (using_vwgts) {
	    vwgt_max = 0;
	    total_vwgt = 0;
	    for (i = 1; i <= nvtxs; i++) {
		if (graph[i]->vwgt > vwgt_max)
		    vwgt_max = graph[i]->vwgt;
		total_vwgt += graph[i]->vwgt;
	    }
	}
	else {
	    vwgt_max = 1;
	    total_vwgt = nvtxs;
	}
	for (i = 0; i < nsets; i++)
	    goal[i] = total_vwgt / nsets;

	space = smalloc((nvtxs + 1) * sizeof(int));
	morespace = smalloc((nvtxs) * sizeof(int));

	initshift = 0;
	orthlist = NULL;
	for (i = 1; i < ndims; i++) {
	    ch_normalize(yvecs[i], 1, nvtxs);
	    rqi(graph, yvecs, i, nvtxs, r1, r2, v, w, x, y, work,
		eigtol, initshift, &evalest, vwsqrt, orthlist,
		0, nsets, space, morespace, 3, goal, vwgt_max, ndims);

	    /* Now orthogonalize higher yvecs against this one. */
	    norm = dot(yvecs[i], 1, nvtxs, yvecs[i]);
	    for (j = i + 1; j <= ndims; j++) {
		alpha = -dot(yvecs[j], 1, nvtxs, yvecs[i]) / norm;
		scadd(yvecs[j], 1, nvtxs, alpha, yvecs[i]);
	    }

	    /* Now prepare for next pass through loop. */
	    initshift = evalest;
	    newlink = makeorthlnk();
	    newlink->vec = yvecs[i];
	    newlink->pntr = orthlist;
	    orthlist = newlink;

	}
	ch_normalize(yvecs[ndims], 1, nvtxs);

	if (term_wgts[1] != NULL && ndims == 1) {
	    /* Solve extended eigen problem */

	    /* If not coarsening ewgts, then need care with term_wgts. */
	    if (!using_ewgts && term_wgts[1] != NULL && step != 0) {
	        twptr = smalloc((nvtxs + 1) * (nsets - 1) * sizeof(float));
	        twptr_save = twptr;
	        for (j = 1; j < nsets; j++) {
	            new_term_wgts[j] = twptr;
	            twptr += nvtxs + 1;
	        }

	        for (j = 1; j < nsets; j++) {
	            twptr = term_wgts[j];
	            ctwptr = new_term_wgts[j];
	            for (i = 1; i <= nvtxs; i++) {
		        if (twptr[i] > .5) ctwptr[i] = 1;
		        else if (twptr[i] < -.5) ctwptr[i] = -1;
		        else ctwptr[i] = 0;
		    }
	        }
	        real_term_wgts = new_term_wgts;
	    }
	    else {
	        real_term_wgts = term_wgts;
	        new_term_wgts[1] = NULL;
	    }

	    /* Following only works for bisection. */
	    w1 = goal[0];
	    w2 = goal[1];
	    sigma = sqrt(4*w1*w2/(w1+w2));
	    gvec = smalloc((nvtxs+1)*sizeof(double));
	    term_tot = sigma;	/* Avoids lint warning for now. */
	    term_tot = 0;
	    for (j=1; j<=nvtxs; j++) term_tot += (real_term_wgts[1])[j];
	    term_tot /= (w1+w2);
	    if (using_vwgts) {
	        for (j=1; j<=nvtxs; j++) {
		    gvec[j] = (real_term_wgts[1])[j]/graph[j]->vwgt - term_tot;
		}
	    }
	    else {
	        for (j=1; j<=nvtxs; j++) {
		    gvec[j] = (real_term_wgts[1])[j] - term_tot;
		}
	    }

	    rqi_ext();

	    sfree(gvec);
	    if (real_term_wgts != term_wgts && new_term_wgts[1] != NULL) {
		sfree(new_term_wgts[1]);
	    }
	}
	else {
	    rqi(graph, yvecs, ndims, nvtxs, r1, r2, v, w, x, y, work,
	        eigtol, initshift, &evalest, vwsqrt, orthlist,
	        0, nsets, space, morespace, 3, goal, vwgt_max, ndims);
	}
	refine_time += seconds() - time;

	/* Free the space allocated for RQI. */
	sfree(morespace);
	sfree(space);
	while (orthlist != NULL) {
	    newlink = orthlist->pntr;
	    sfree(orthlist);
	    orthlist = newlink;
	}
	sfree(r1);
	if (vwsqrt != NULL)
	    sfree(vwsqrt);
	PERTURB = oldperturb;
    }
    if (DEBUG_COARSEN > 0) {
	printf(" Leaving coarsen, step=%d\n", step);
    }

    /* Free the space that was allocated. */
    if (ccoords != NULL) {
	for (i = 0; i < igeom; i++)
	    sfree(ccoords[i]);
	sfree(ccoords);
    }
    for (i = ndims; i > 0; i--)
	sfree(cyvecs[i]);
    free_graph(cgraph);
}
double mindampingrate(double k, void *fnparams)
{
    /* This function calculates the minimumdamping rate for given k and
       params. Because all of our routines are set up to calculate
       growth rate, gamma, we find the maximum growth rate, and then
       return the negative of that as the minimum damping rate. This
       form is required since we want to find the peak growth rate, but
       it's easiest to use a function minimizer to find the minimum
       damping rate.
    */

    //Get a pointer casting the params as a function_params structure
    //so I can address the elements.
    struct FUNCTION_PARAMS *p = (struct FUNCTION_PARAMS *) fnparams;

    //Define these things locally so I'm not addressing p all the time.
    //So I should have local pointers to everything.

    PARAMS_STRUCT *params = p->params;
    GRID_STRUCT *grid = p->grid;
    ROTATION_STRUCT *rotation = p->rotation;
    COMPRESSED_MATRIX *matrix = p->matrix;
    ARPACK_CONTROL *arpack_params = p->arpack_params;
    RESULTS_STRUCT *results;

    double max_gr = 0;

    //Now change k in the params
    params->k = k;
    params->kva = params->k*params->va;

    //And I have to recalculate the quantities in the grid,
    //since I have the matrix elements of the diffuse terms in there
    free(grid->r);
    free(grid->x);
    free(grid->r2inv);
    free(grid);
    grid = gridgen(params);

    if(params->VERBOSE) {
        printf("Running with k=%g\n", params->k);
    }

    //Now I'm ready to find that eigenvalue!
    arpack_params->sigma = find_sigma(matrix, params, grid, rotation,
                                      arpack_params);
    results = eigensolve(matrix, params, grid, rotation, arpack_params);

    if (results->nconv < 1) {
        fprintf(stderr, "Error! No eigenvalues found for k=%g.\n", params->k);

    } else {
        //Find the eigenvalue with the largest real part
        max_gr = results->lambda[0];
        for (int i = 0; i < results->nconv; i++) {
            if (creal(results->lambda[i]) > creal(max_gr)) {
                max_gr = results->lambda[i];
            }
        }
    }

    //Now free up the results structure
    free(results->lambda);
    free(results->z);
    free(results);

    return -max_gr;
}
void shearlayerkcrit_driver(char *input_file_name)
{
    PARAMS_STRUCT *params;
    GRID_STRUCT *grid;
    ROTATION_STRUCT *rotation;
    COMPRESSED_MATRIX *matrix;
    ARPACK_CONTROL *arpack_params;
    RESULTS_STRUCT *results;
    OUTPUT_CONTROL *output_control;

    double shear_width, shear_radius, E;

    //Parameters needed for the root-finding routine
    int status;
    int iter=0, max_iter=50;
    const gsl_root_fsolver_type *Troot;
    const gsl_min_fminimizer_type *Tmin;
    gsl_root_fsolver *sroot;
    gsl_min_fminimizer *smin;
    double k_low, k_high, k_guess;
    double k_min = NAN;
    double k_max = NAN;
    double k_peak = NAN;
    double gr_peak;
    double errabs, errrel;
    double width_prefactor;
    gsl_function F;
    struct FUNCTION_PARAMS function_params;

    //Get the physical parameters for the computation
    params = malloc(sizeof(PARAMS_STRUCT));
    probgen(input_file_name, params);

    //Set up the grid, based on the physical parameters
    grid = gridgen(params);

    //Set up the rotation profile of a shear layer. Derive the width
    //from the Ekman number, E=\nu/\Omega r^2, width = rE^(1/4)
    //Use r = (r2-r1) and Omega = (Omega1-Omega2)/2.

    shear_radius = get_dparam("shear_radius", input_file_name);
    /*
    width_prefactor = get_dparam("width_prefactor", input_file_name);
    E = params->nu/(0.5*fabs(params->omega1 - params->omega2) *
    	  pow((params->r2-params->r1),2));
    shear_width = width_prefactor*(params->r2-params->r1)*pow(E, 0.25);
    printf("Using shear layer width %g cm\n", shear_width);
    */
    shear_width = get_dparam("shear_width", input_file_name);
    rotation = shearlayer(params, grid, shear_width, shear_radius);

    //Set up the matrix structure for the computations.
    matrix = create_matrix(5*grid->numcells);

    //Setup the ARPACK parameters
    arpack_params = setup_arpack(input_file_name);

    //Setup the output control structure
    output_control = malloc(sizeof(OUTPUT_CONTROL));

    //Pull the error params from the input file to decide when
    //we have converged
    errabs = get_dparam("errabs", input_file_name);
    errrel = get_dparam("errrel", input_file_name);

    //Put pointers to all of our control structures in function_params
    function_params.params = params;
    function_params.grid = grid;
    function_params.rotation = rotation;
    function_params.matrix = matrix;
    function_params.arpack_params = arpack_params;

    //Assign the evaluation function and params structure to
    //the gsl_function
    F.function = &mindampingrate;
    F.params = &function_params;

    gsl_set_error_handler(&err_handler);

    /* Now we find the peak of the growth rate, by minimizing the
       damping rate. We set what we hope are reasonable numbers
       for the bounds and initial guess.
    */

    k_low = 0.01;
    k_high = 1000;
    k_guess = params->k;
    Tmin = gsl_min_fminimizer_brent;
    smin = gsl_min_fminimizer_alloc(Tmin);
    status = gsl_min_fminimizer_set(smin, &F, k_guess, k_low, k_high);
    //Make sure that we didn't thrown an error on initialization
    if (status == GSL_SUCCESS) {
        //Now iterate!
        iter = 0;
        do
        {
            iter++;
            status = gsl_min_fminimizer_iterate(smin);
            //Make sure that we didn't thrown an error in the iteration routine
            if (status != GSL_SUCCESS) {
                fprintf(stderr, "Aborted attempt to find k_peak.\n");
                break;
            }

            params->k = gsl_min_fminimizer_x_minimum(smin);
            k_low = gsl_min_fminimizer_x_lower(smin);
            k_high = gsl_min_fminimizer_x_upper(smin);
            status = gsl_min_test_interval(k_low, k_high, errabs, errrel);

            if(status == GSL_SUCCESS && params->VERBOSE) {
                printf("Converged with k_peak=%g\n", params->k);
            }
        }
        while (status == GSL_CONTINUE && iter < max_iter);
        //Save the peak growth rate for printing later, then free the solver
        gr_peak = -gsl_min_fminimizer_f_minimum(smin);
    } else {
        fprintf(stderr, "Aborted attempt to find k_peak.\n");
    }
    gsl_min_fminimizer_free(smin);

    //Check to make sure we converged. If not, don't save the results.
    if (status == GSL_SUCCESS) {
        k_peak = params->k;

        //Make sure everything is set up correctly for normal run
        params->kva = params->k*params->va;
        free(grid->r);
        free(grid->x);
        free(grid->r2inv);
        free(grid);
        grid = gridgen(params);

        //Now do a normal run with the chosen k
        arpack_params->sigma = find_sigma(matrix, params, grid, rotation,
                                          arpack_params);
        results = eigensolve(matrix, params, grid, rotation, arpack_params);

        //Setup the structures needed to output the data files, and write them.
        get_sparam("basefilename", input_file_name, output_control->basefilename);
        strcat(output_control->basefilename, "_kpeak");
        wnetcdf(params, grid, rotation, output_control, arpack_params, results);

        free(results->lambda);
        free(results->z);
        free(results->residual);
        free(results);
    }


    /* Now do a root finding search for k_min. */

    /*
    //Set up the root solver.
    Troot = gsl_root_fsolver_brent;
    sroot = gsl_root_fsolver_alloc(Troot);

    //Set the initial bounds for the search. We're searching for k_min,
    //so search from 0 up to k_peak.
    k_low = 0;
    k_high = k_peak;
    status = gsl_root_fsolver_set(sroot, &F, k_low, k_high);
    //Make sure that we didn't thrown an error on initialization
    if (status == GSL_SUCCESS) {
      //Now iterate!
      iter = 0;
      do
        {
    iter++;
    status = gsl_root_fsolver_iterate(sroot);
    //Make sure that we didn't thrown an error in the iteration routine
    if (status != GSL_SUCCESS) {
      fprintf(stderr, "Aborted attempt to find k_min.\n");
      break;
    }

    params->k = gsl_root_fsolver_root(sroot);
    k_low = gsl_root_fsolver_x_lower(sroot);
    k_high = gsl_root_fsolver_x_upper(sroot);
    status = gsl_root_test_interval(k_low, k_high, errabs, errrel);

    if(status == GSL_SUCCESS && params->VERBOSE) {
      printf("Converged with k_min=%g\n", params->k);
    }
        }
      while (status == GSL_CONTINUE && iter < max_iter);
    } else {
      fprintf(stderr, "Aborted attempt to find k_min.\n");
    }
    gsl_root_fsolver_free (sroot);

    //Check to make sure we converged. If not, don't save the results.
    if (status == GSL_SUCCESS) {
      k_min = params->k;

      //Make sure everything is set up correctly for the normal run
      params->kva = params->k*params->va;
      free(grid->r);
      free(grid->x);
      free(grid->r2inv);
      free(grid);
      grid = gridgen(params);

      //Now do a normal run with the chosen k
      arpack_params->sigma = find_sigma(matrix, params, grid, rotation,
    			      arpack_params);
      results = eigensolve(matrix, params, grid, rotation, arpack_params);

      //Set the new file name, and write the output
      get_sparam("basefilename", input_file_name, output_control->basefilename);
      strcat(output_control->basefilename, "_kmin");
      wnetcdf(params, grid, rotation, output_control, arpack_params, results);

      free(results->lambda);
      free(results->z);
      free(results->residual);
      free(results);
    }
    */

    /* Now move on to solving for k_max. */
    Troot = gsl_root_fsolver_brent;
    sroot = gsl_root_fsolver_alloc(Troot);

    //Set the initial bounds for the search. We're searching for k_max,
    //so search from k_peak to a large number
    k_low = k_peak;
    k_high = 10000;
    status = gsl_root_fsolver_set(sroot, &F, k_low, k_high);
    //Make sure that we didn't thrown an error on initialization
    if (status == GSL_SUCCESS) {
        //Now iterate!
        iter = 0;
        do
        {
            iter++;
            status = gsl_root_fsolver_iterate(sroot);
            //Make sure that we didn't thrown an error in the iteration routine
            if (status != GSL_SUCCESS) {
                fprintf(stderr, "Aborted attempt to find k_max.\n");
                break;
            }

            params->k = gsl_root_fsolver_root(sroot);
            k_low = gsl_root_fsolver_x_lower(sroot);
            k_high = gsl_root_fsolver_x_upper(sroot);
            status = gsl_root_test_interval(k_low, k_high, errabs, errrel);

            if(status == GSL_SUCCESS && params->VERBOSE) {
                printf("Converged with k_max=%g\n", params->k);
            }
        }
        while (status == GSL_CONTINUE && iter < max_iter);
    } else {
        fprintf(stderr, "Aborted attempt to find k_max.\n");
    }
    gsl_root_fsolver_free (sroot);

    //Check to make sure we converged. If not, don't save the results.
    if (status == GSL_SUCCESS) {
        k_max = params->k;

        //Make sure everything is set up correctly for the normal run
        params->kva = params->k*params->va;
        free(grid->r);
        free(grid->x);
        free(grid->r2inv);
        free(grid);
        grid = gridgen(params);

        //Now do a normal run with the chosen k
        arpack_params->sigma = find_sigma(matrix, params, grid, rotation,
                                          arpack_params);
        results = eigensolve(matrix, params, grid, rotation, arpack_params);

        //Set the new file name, and write the output
        get_sparam("basefilename", input_file_name, output_control->basefilename);
        strcat(output_control->basefilename, "_kmax");
        wnetcdf(params, grid, rotation, output_control, arpack_params, results);

        free(results->lambda);
        free(results->z);
        free(results->residual);
        free(results);
    }

    printf("Found k_min = %g, k_peak = %g, k_max = %g\n", k_min, k_peak, k_max);
    printf("Peak growth rate: %g\n", gr_peak);

    free(matrix->A);
    free(matrix->B);
    free(matrix->Bb);
    free(matrix);
    free(params);
    free(grid->r);
    free(grid->x);
    free(grid->r2inv);
    free(grid);
    free(rotation->omega);
    free(rotation);
    free(output_control);

    return;
}