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); }
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; }