Exemple #1
0
void HypreSolver2D::solve(double *x)
{

	set_rhs(x);

	HYPRE_BoomerAMGSolve(solver, parcsr_A, par_b, par_x);

	HYPRE_IJVectorGetValues(hv_x, local_size, &rows[0], x);

	HYPRE_BoomerAMGGetNumIterations(solver, &num_iterations);
	HYPRE_BoomerAMGGetFinalRelativeResidualNorm(solver, &final_res_norm);

}
Exemple #2
0
int main (int argc, char *argv[])
{
   int myid, num_procs;
   int n;
   double gamma, h;
   int vis;

   HYPRE_SStructGrid     grid;
   HYPRE_SStructGraph    graph;
   HYPRE_SStructMatrix   A;
   HYPRE_SStructVector   b;
   HYPRE_SStructVector   x;

   HYPRE_Solver          solver;

   /* Initialize MPI */
   MPI_Init(&argc, &argv);
   MPI_Comm_rank(MPI_COMM_WORLD, &myid);
   MPI_Comm_size(MPI_COMM_WORLD, &num_procs);

   /* Set default parameters */
   n = 10;
   vis = 0;

   /* Parse command line */
   {
      int arg_index = 0;
      int print_usage = 0;

      while (arg_index < argc)
      {
         if ( strcmp(argv[arg_index], "-n") == 0 )
         {
            arg_index++;
            n = atoi(argv[arg_index++]);
         }
         else if ( strcmp(argv[arg_index], "-vis") == 0 )
         {
            arg_index++;
            vis = 1;
         }
         else if ( strcmp(argv[arg_index], "-help") == 0 )
         {
            print_usage = 1;
            break;
         }
         else
         {
            arg_index++;
         }
      }

      if ((print_usage) && (myid == 0))
      {
         printf("\n");
         printf("Usage: %s [<options>]\n", argv[0]);
         printf("\n");
         printf("  -n <n>              : problem size per processor (default: 10)\n");
         printf("  -vis                : save the solution for GLVis visualization\n");
         printf("\n");
      }

      if (print_usage)
      {
         MPI_Finalize();
         return (0);
      }
   }

   /* Set the rhombus angle, gamma, and the mesh size, h, depending on the
      number of processors np and the given n */
   if (num_procs < 3)
   {
      if (myid ==0) printf("Must run with at least 3 processors!\n");
      MPI_Finalize();
      exit(1);
   }
   gamma = 2*M_PI/num_procs;
   h = 1.0/n;

   /* 1. Set up the grid.  We will set up the grid so that processor X owns
         part X.  Note that each part has its own index space numbering. Later
         we relate the parts to each other. */
   {
      int ndim = 2;
      int nparts = num_procs;

      /* Create an empty 2D grid object */
      HYPRE_SStructGridCreate(MPI_COMM_WORLD, ndim, nparts, &grid);

      /* Set the extents of the grid - each processor sets its grid boxes.  Each
         part has its own relative index space numbering */
      {
         int part = myid;
         int ilower[2] = {1,1}; /* lower-left cell touching the origin */
         int iupper[2] = {n,n}; /* upper-right cell */

         HYPRE_SStructGridSetExtents(grid, part, ilower, iupper);
      }

      /* Set the variable type and number of variables on each part.  These need
         to be set in each part which is neighboring or contains boxes owned by
         the processor. */
      {
         int i;
         int nvars = 1;

         HYPRE_SStructVariable vartypes[1] = {HYPRE_SSTRUCT_VARIABLE_NODE};
         for (i = 0; i < nparts; i++)
            HYPRE_SStructGridSetVariables(grid, i, nvars, vartypes);
      }

      /* Set the ordering of the variables in the finite element problem.  This
         is done by listing the variable offset directions relative to the
         element's center.  See the Reference Manual for more details. */
      {
         int part = myid;
         int ordering[12] = { 0, -1, -1,    /*    [3]------[2] */
                              0, +1, -1,    /*    /        /   */
                              0, +1, +1,    /*   /        /    */
                              0, -1, +1 };  /* [0]------[1]    */

         HYPRE_SStructGridSetFEMOrdering(grid, part, ordering);
      }

      /* Now we need to set the spatial relation between each of the parts.
         Since we are using nodal variables, we have to use SetSharedPart to
         establish the connection at the origin. */
      {
         /* Relation to the clockwise-previous neighbor part, e.g. 0 and 1 for
            the case of 6 parts.  Note that we could have used SetNeighborPart
            here instead of SetSharedPart. */
         {
            int part = myid;
            /* the box of cells intersecting the boundary in the current part */
            int ilower[2] = {1,1}, iupper[2] = {1,n};
            /* share all data on the left side of the box */
            int offset[2] = {-1,0};

            int shared_part = (myid+1) % num_procs;
            /* the box of cells intersecting the boundary in the neighbor */
            int shared_ilower[2] = {1,1}, shared_iupper[2] = {n,1};
            /* share all data on the bottom of the box */
            int shared_offset[2] = {0,-1};

            /* x/y-direction on the current part is -y/x on the neighbor */
            int index_map[2] = {1,0};
            int index_dir[2] = {-1,1};

            HYPRE_SStructGridSetSharedPart(grid, part, ilower, iupper, offset,
                                           shared_part, shared_ilower,
                                           shared_iupper, shared_offset,
                                           index_map, index_dir);
         }

         /* Relation to the clockwise-following neighbor part, e.g. 0 and 5 for
            the case of 6 parts.  Note that we could have used SetNeighborPart
            here instead of SetSharedPart. */
         {
            int part = myid;
            /* the box of cells intersecting the boundary in the current part */
            int ilower[2] = {1,1}, iupper[2] = {n,1};
            /* share all data on the bottom of the box */
            int offset[2] = {0,-1};

            int shared_part = (myid+num_procs-1) % num_procs;
            /* the box of cells intersecting the boundary in the neighbor */
            int shared_ilower[2] = {1,1}, shared_iupper[2] = {1,n};
            /* share all data on the left side of the box */
            int shared_offset[2] = {-1,0};

            /* x/y-direction on the current part is y/-x on the neighbor */
            int index_map[2] = {1,0};
            int index_dir[2] = {1,-1};

            HYPRE_SStructGridSetSharedPart(grid, part, ilower, iupper, offset,
                                           shared_part, shared_ilower,
                                           shared_iupper, shared_offset,
                                           index_map, index_dir);
         }

         /* Relation to all other parts, e.g. 0 and 2,3,4.  This can be
            described only by SetSharedPart. */
         {
            int part = myid;
            /* the (one cell) box that touches the origin */
            int ilower[2] = {1,1}, iupper[2] = {1,1};
            /* share all data in the bottom left corner (i.e. the origin) */
            int offset[2] = {-1,-1};

            int shared_part;
            /* the box of one cell that touches the origin */
            int shared_ilower[2] = {1,1}, shared_iupper[2] = {1,1};
            /* share all data in the bottom left corner (i.e. the origin) */
            int shared_offset[2] = {-1,-1};

            /* x/y-direction on the current part is -x/-y on the neighbor, but
               in this case the arguments are not really important since we are
               only sharing a point */
            int index_map[2] = {0,1};
            int index_dir[2] = {-1,-1};

            for (shared_part = 0; shared_part < myid-1; shared_part++)
               HYPRE_SStructGridSetSharedPart(grid, part, ilower, iupper, offset,
                                              shared_part, shared_ilower,
                                              shared_iupper, shared_offset,
                                              index_map, index_dir);

            for (shared_part = myid+2; shared_part < num_procs; shared_part++)
               HYPRE_SStructGridSetSharedPart(grid, part, ilower, iupper, offset,
                                              shared_part, shared_ilower,
                                              shared_iupper, shared_offset,
                                              index_map, index_dir);
         }
      }

      /* Now the grid is ready to be used */
      HYPRE_SStructGridAssemble(grid);
   }

   /* 2. Set up the Graph - this determines the non-zero structure of the
         matrix. */
   {
      int part;

      /* Create the graph object */
      HYPRE_SStructGraphCreate(MPI_COMM_WORLD, grid, &graph);

      /* See MatrixSetObjectType below */
      HYPRE_SStructGraphSetObjectType(graph, HYPRE_PARCSR);

      /* Indicate that this problem uses finite element stiffness matrices and
         load vectors, instead of stencils. */
      for (part = 0; part < num_procs; part++)
         HYPRE_SStructGraphSetFEM(graph, part);

      /* The local stiffness matrix is full, so there is no need to call
         HYPRE_SStructGraphSetFEMSparsity to set its sparsity pattern. */

      /* Assemble the graph */
      HYPRE_SStructGraphAssemble(graph);
   }

   /* 3. Set up the SStruct Matrix and right-hand side vector */
   {
      int part = myid;

      /* Create the matrix object */
      HYPRE_SStructMatrixCreate(MPI_COMM_WORLD, graph, &A);
      /* Use a ParCSR storage */
      HYPRE_SStructMatrixSetObjectType(A, HYPRE_PARCSR);
      /* Indicate that the matrix coefficients are ready to be set */
      HYPRE_SStructMatrixInitialize(A);

      /* Create an empty vector object */
      HYPRE_SStructVectorCreate(MPI_COMM_WORLD, grid, &b);
      /* Use a ParCSR storage */
      HYPRE_SStructVectorSetObjectType(b, HYPRE_PARCSR);
      /* Indicate that the vector coefficients are ready to be set */
      HYPRE_SStructVectorInitialize(b);

      /* Set the matrix and vector entries by finite element assembly */
      {
         /* local stifness matrix and load vector */
         double S[4][4], F[4];

         int i, j, k;
         int index[2];

         /* set the values in the interior cells */
         {
            ComputeFEMRhombus(S, F, gamma, h);

            for (i = 1; i <= n; i++)
               for (j = 1; j <= n; j++)
               {
                  index[0] = i;
                  index[1] = j;
                  HYPRE_SStructMatrixAddFEMValues(A, part, index, &S[0][0]);
                  HYPRE_SStructVectorAddFEMValues(b, part, index, F);
               }
         }

         /* cells having nodes 1,2 on the domain boundary */
         {
            ComputeFEMRhombus(S, F, gamma, h);

            /* eliminate nodes 1,2 from S and F */
            for (k = 0; k < 4; k++)
            {
               S[1][k] = S[k][1] = 0.0;
               S[2][k] = S[k][2] = 0.0;
            }
            S[1][1] = 1.0;
            S[2][2] = 1.0;
            F[1] = 0.0;
            F[2] = 0.0;

            for (i = n; i <= n; i++)
               for (j = 1; j <= n; j++)
               {
                  index[0] = i;
                  index[1] = j;
                  HYPRE_SStructMatrixAddFEMValues(A, part, index, &S[0][0]);
                  HYPRE_SStructVectorAddFEMValues(b, part, index, F);
               }
         }

         /* cells having nodes 2,3 on the domain boundary */
         {
            ComputeFEMRhombus(S, F, gamma, h);

            /* eliminate nodes 2,3 from S and F */
            for (k = 0; k < 4; k++)
            {
               S[2][k] = S[k][2] = 0.0;
               S[3][k] = S[k][3] = 0.0;
            }
            S[2][2] = 1.0;
            S[3][3] = 1.0;
            F[2] = 0.0;
            F[3] = 0.0;

            for (i = 1; i <= n; i++)
               for (j = n; j <= n; j++)
               {
                  index[0] = i;
                  index[1] = j;
                  HYPRE_SStructMatrixAddFEMValues(A, part, index, &S[0][0]);
                  HYPRE_SStructVectorAddFEMValues(b, part, index, F);
               }

         }

         /* cells having nodes 1,2,3 on the domain boundary */
         {
            ComputeFEMRhombus(S, F, gamma, h);

            /* eliminate nodes 2,3 from S and F */
            for (k = 0; k < 4; k++)
            {
               S[1][k] = S[k][1] = 0.0;
               S[2][k] = S[k][2] = 0.0;
               S[3][k] = S[k][3] = 0.0;
            }
            S[1][1] = 1.0;
            S[2][2] = 1.0;
            S[3][3] = 1.0;
            F[1] = 0.0;
            F[2] = 0.0;
            F[3] = 0.0;

            for (i = n; i <= n; i++)
               for (j = n; j <= n; j++)
               {
                  index[0] = i;
                  index[1] = j;
                  HYPRE_SStructMatrixAddFEMValues(A, part, index, &S[0][0]);
                  HYPRE_SStructVectorAddFEMValues(b, part, index, F);
               }
         }
      }
   }

   /* Collective calls finalizing the matrix and vector assembly */
   HYPRE_SStructMatrixAssemble(A);
   HYPRE_SStructVectorAssemble(b);

   /* 4. Set up SStruct Vector for the solution vector x */
   {
      int part = myid;
      int var = 0;
      int nvalues = (n+1)*(n+1);
      double *values;

      /* Since the SetBoxValues() calls below set the values of the nodes in
         the upper-right corners of the cells, the nodal box should start
         from (0,0) instead of (1,1). */
      int ilower[2] = {0,0};
      int iupper[2] = {n,n};

      values = calloc(nvalues, sizeof(double));

      /* Create an empty vector object */
      HYPRE_SStructVectorCreate(MPI_COMM_WORLD, grid, &x);
      /* Set the object type to ParCSR */
      HYPRE_SStructVectorSetObjectType(x, HYPRE_PARCSR);
      /* Indicate that the vector coefficients are ready to be set */
      HYPRE_SStructVectorInitialize(x);
      /* Set the values for the initial guess */
      HYPRE_SStructVectorSetBoxValues(x, part, ilower, iupper, var, values);

      free(values);

      /* Finalize the vector assembly */
      HYPRE_SStructVectorAssemble(x);
   }

   /* 5. Set up and call the solver (Solver options can be found in the
         Reference Manual.) */
   {
      double final_res_norm;
      int its;

      HYPRE_ParCSRMatrix    par_A;
      HYPRE_ParVector       par_b;
      HYPRE_ParVector       par_x;

      /* Extract the ParCSR objects needed in the solver */
      HYPRE_SStructMatrixGetObject(A, (void **) &par_A);
      HYPRE_SStructVectorGetObject(b, (void **) &par_b);
      HYPRE_SStructVectorGetObject(x, (void **) &par_x);

      /* Here we construct a BoomerAMG solver.  See the other SStruct examples
         as well as the Reference manual for additional solver choices. */
      HYPRE_BoomerAMGCreate(&solver);
      HYPRE_BoomerAMGSetCoarsenType(solver, 6);
      HYPRE_BoomerAMGSetStrongThreshold(solver, 0.25);
      HYPRE_BoomerAMGSetTol(solver, 1e-6);
      HYPRE_BoomerAMGSetPrintLevel(solver, 2);
      HYPRE_BoomerAMGSetMaxIter(solver, 50);

      /* call the setup */
      HYPRE_BoomerAMGSetup(solver, par_A, par_b, par_x);

      /* call the solve */
      HYPRE_BoomerAMGSolve(solver, par_A, par_b, par_x);

      /* get some info */
      HYPRE_BoomerAMGGetNumIterations(solver, &its);
      HYPRE_BoomerAMGGetFinalRelativeResidualNorm(solver,
                                                  &final_res_norm);
      /* clean up */
      HYPRE_BoomerAMGDestroy(solver);

      /* Gather the solution vector */
      HYPRE_SStructVectorGather(x);

      /* Save the solution for GLVis visualization, see vis/glvis-ex13.sh */
      if (vis)
      {
         FILE *file;
         char filename[255];

         int i, part = myid, var = 0;
         int nvalues = (n+1)*(n+1);
         double *values = calloc(nvalues, sizeof(double));
         int ilower[2] = {0,0};
         int iupper[2] = {n,n};

         /* get all local data (including a local copy of the shared values) */
         HYPRE_SStructVectorGetBoxValues(x, part, ilower, iupper,
                                         var, values);

         sprintf(filename, "%s.%06d", "vis/ex14.sol", myid);
         if ((file = fopen(filename, "w")) == NULL)
         {
            printf("Error: can't open output file %s\n", filename);
            MPI_Finalize();
            exit(1);
         }

         /* finite element space header */
         fprintf(file, "FiniteElementSpace\n");
         fprintf(file, "FiniteElementCollection: H1_2D_P1\n");
         fprintf(file, "VDim: 1\n");
         fprintf(file, "Ordering: 0\n\n");

         /* save solution */
         for (i = 0; i < nvalues; i++)
            fprintf(file, "%.14e\n", values[i]);

         fflush(file);
         fclose(file);
         free(values);

         /* save local finite element mesh */
         GLVis_PrintLocalRhombusMesh("vis/ex14.mesh", n, myid, gamma);

         /* additional visualization data */
         if (myid == 0)
         {
            sprintf(filename, "%s", "vis/ex14.data");
            file = fopen(filename, "w");
            fprintf(file, "np %d\n", num_procs);
            fflush(file);
            fclose(file);
         }
      }

      if (myid == 0)
      {
         printf("\n");
         printf("Iterations = %d\n", its);
         printf("Final Relative Residual Norm = %g\n", final_res_norm);
         printf("\n");
      }
   }

   /* Free memory */
   HYPRE_SStructGridDestroy(grid);
   HYPRE_SStructGraphDestroy(graph);
   HYPRE_SStructMatrixDestroy(A);
   HYPRE_SStructVectorDestroy(b);
   HYPRE_SStructVectorDestroy(x);

   /* Finalize MPI */
   MPI_Finalize();

   return 0;
}
Exemple #3
0
int main (int argc, char *argv[])
{
   HYPRE_Int i;
   int myid, num_procs;
   int N, n;

   HYPRE_Int ilower, iupper;
   HYPRE_Int local_size, extra;

   int solver_id;
   int print_solution, print_system;

   double h, h2;

   HYPRE_IJMatrix A;
   HYPRE_ParCSRMatrix parcsr_A;
   HYPRE_IJVector b;
   HYPRE_ParVector par_b;
   HYPRE_IJVector x;
   HYPRE_ParVector par_x;

   HYPRE_Solver solver, precond;

   /* Initialize MPI */
   MPI_Init(&argc, &argv);
   MPI_Comm_rank(MPI_COMM_WORLD, &myid);
   MPI_Comm_size(MPI_COMM_WORLD, &num_procs);

   /* Default problem parameters */
   n = 33;
   solver_id = 0;
   print_solution  = 0;
   print_system = 0;


   /* Parse command line */
   {
      int arg_index = 0;
      int print_usage = 0;

      while (arg_index < argc)
      {
         if ( strcmp(argv[arg_index], "-n") == 0 )
         {
            arg_index++;
            n = atoi(argv[arg_index++]);
         }
         else if ( strcmp(argv[arg_index], "-solver") == 0 )
         {
            arg_index++;
            solver_id = atoi(argv[arg_index++]);
         }
         else if ( strcmp(argv[arg_index], "-print_solution") == 0 )
         {
            arg_index++;
            print_solution = 1;
         }
         else if ( strcmp(argv[arg_index], "-print_system") == 0 )
         {
            arg_index++;
            print_system = 1;
         }


         else if ( strcmp(argv[arg_index], "-help") == 0 )
         {
            print_usage = 1;
            break;
         }
         else
         {
            arg_index++;
         }
      }

      if ((print_usage) && (myid == 0))
      {
         printf("\n");
         printf("Usage: %s [<options>]\n", argv[0]);
         printf("\n");
         printf("  -n <n>              : problem size in each direction (default: 33)\n");
         printf("  -solver <ID>        : solver ID\n");
         printf("                        0  - AMG (default) \n");
         printf("                        1  - AMG-PCG\n");
         printf("                        8  - ParaSails-PCG\n");
         printf("                        50 - PCG\n");
         printf("                        61 - AMG-FlexGMRES\n");
         printf("  -print_solution     : print the solution vector\n");
         printf("  -print_system       : print the matrix and rhs\n");
         printf("\n");
      }

      if (print_usage)
      {
         MPI_Finalize();
         return (0);
      }
   }

   /* Preliminaries: want at least one processor per row */
   if (n*n < num_procs) n = sqrt(num_procs) + 1;
   N = n*n; /* global number of rows */
   h = 1.0/(n+1); /* mesh size*/
   h2 = h*h;

   /* Each processor knows only of its own rows - the range is denoted by ilower
      and upper.  Here we partition the rows. We account for the fact that
      N may not divide evenly by the number of processors. */
   local_size = N/num_procs;
   extra = N - local_size*num_procs;

   ilower = local_size*myid;
   ilower += hypre_min(myid, extra);

   iupper = local_size*(myid+1);
   iupper += hypre_min(myid+1, extra);
   iupper = iupper - 1;

   /* How many rows do I have? */
   local_size = iupper - ilower + 1;

   /* Create the matrix.
      Note that this is a square matrix, so we indicate the row partition
      size twice (since number of rows = number of cols) */
   HYPRE_IJMatrixCreate(MPI_COMM_WORLD, ilower, iupper, ilower, iupper, &A);

   /* Choose a parallel csr format storage (see the User's Manual) */
   HYPRE_IJMatrixSetObjectType(A, HYPRE_PARCSR);

   /* Initialize before setting coefficients */
   HYPRE_IJMatrixInitialize(A);

   /* Now go through my local rows and set the matrix entries.
      Each row has at most 5 entries. For example, if n=3:

      A = [M -I 0; -I M -I; 0 -I M]
      M = [4 -1 0; -1 4 -1; 0 -1 4]

      Note that here we are setting one row at a time, though
      one could set all the rows together (see the User's Manual).
   */
   {
      HYPRE_Int nnz;
      double values[5];
      HYPRE_Int cols[5];

      for (i = ilower; i <= iupper; i++)
      {
         nnz = 0;

         /* The left identity block:position i-n */
         if ((i-n)>=0)
         {
            cols[nnz] = i-n;
            values[nnz] = -1.0;
            nnz++;
         }

         /* The left -1: position i-1 */
         if (i%n)
         {
            cols[nnz] = i-1;
            values[nnz] = -1.0;
            nnz++;
         }

         /* Set the diagonal: position i */
         cols[nnz] = i;
         values[nnz] = 4.0;
         nnz++;

         /* The right -1: position i+1 */
         if ((i+1)%n)
         {
            cols[nnz] = i+1;
            values[nnz] = -1.0;
            nnz++;
         }

         /* The right identity block:position i+n */
         if ((i+n)< N)
         {
            cols[nnz] = i+n;
            values[nnz] = -1.0;
            nnz++;
         }

         /* Set the values for row i */
         HYPRE_IJMatrixSetValues(A, 1, &nnz, &i, cols, values);
      }
   }

   /* Assemble after setting the coefficients */
   HYPRE_IJMatrixAssemble(A);

   /* Note: for the testing of small problems, one may wish to read
      in a matrix in IJ format (for the format, see the output files
      from the -print_system option).
      In this case, one would use the following routine:
      HYPRE_IJMatrixRead( <filename>, MPI_COMM_WORLD,
                          HYPRE_PARCSR, &A );
      <filename>  = IJ.A.out to read in what has been printed out
      by -print_system (processor numbers are omitted).
      A call to HYPRE_IJMatrixRead is an *alternative* to the
      following sequence of HYPRE_IJMatrix calls:
      Create, SetObjectType, Initialize, SetValues, and Assemble
   */


   /* Get the parcsr matrix object to use */
   HYPRE_IJMatrixGetObject(A, (void**) &parcsr_A);


   /* Create the rhs and solution */
   HYPRE_IJVectorCreate(MPI_COMM_WORLD, ilower, iupper,&b);
   HYPRE_IJVectorSetObjectType(b, HYPRE_PARCSR);
   HYPRE_IJVectorInitialize(b);

   HYPRE_IJVectorCreate(MPI_COMM_WORLD, ilower, iupper,&x);
   HYPRE_IJVectorSetObjectType(x, HYPRE_PARCSR);
   HYPRE_IJVectorInitialize(x);

   /* Set the rhs values to h^2 and the solution to zero */
   {
      double *rhs_values, *x_values;
      HYPRE_Int *rows;

      rhs_values = calloc(local_size, sizeof(double));
      x_values = calloc(local_size, sizeof(double));
      rows = calloc(local_size, sizeof(HYPRE_Int));

      for (i=0; i<local_size; i++)
      {
         rhs_values[i] = h2;
         x_values[i] = 0.0;
         rows[i] = ilower + i;
      }

      HYPRE_IJVectorSetValues(b, local_size, rows, rhs_values);
      HYPRE_IJVectorSetValues(x, local_size, rows, x_values);

      free(x_values);
      free(rhs_values);
      free(rows);
   }


   HYPRE_IJVectorAssemble(b);
   /*  As with the matrix, for testing purposes, one may wish to read in a rhs:
       HYPRE_IJVectorRead( <filename>, MPI_COMM_WORLD,
                                 HYPRE_PARCSR, &b );
       as an alternative to the
       following sequence of HYPRE_IJVectors calls:
       Create, SetObjectType, Initialize, SetValues, and Assemble
   */
   HYPRE_IJVectorGetObject(b, (void **) &par_b);

   HYPRE_IJVectorAssemble(x);
   HYPRE_IJVectorGetObject(x, (void **) &par_x);


  /*  Print out the system  - files names will be IJ.out.A.XXXXX
       and IJ.out.b.XXXXX, where XXXXX = processor id */
   if (print_system)
   {
      HYPRE_IJMatrixPrint(A, "IJ.out.A");
      HYPRE_IJVectorPrint(b, "IJ.out.b");
   }


   /* Choose a solver and solve the system */

   /* AMG */
   if (solver_id == 0)
   {
      HYPRE_Int num_iterations;
      double final_res_norm;

      /* Create solver */
      HYPRE_BoomerAMGCreate(&solver);

      /* Set some parameters (See Reference Manual for more parameters) */
      HYPRE_BoomerAMGSetPrintLevel(solver, 3);  /* print solve info + parameters */
      HYPRE_BoomerAMGSetCoarsenType(solver, 6); /* Falgout coarsening */
      HYPRE_BoomerAMGSetRelaxType(solver, 3);   /* G-S/Jacobi hybrid relaxation */
      HYPRE_BoomerAMGSetNumSweeps(solver, 1);   /* Sweeeps on each level */
      HYPRE_BoomerAMGSetMaxLevels(solver, 20);  /* maximum number of levels */
      HYPRE_BoomerAMGSetTol(solver, 1e-7);      /* conv. tolerance */

      /* Now setup and solve! */
      HYPRE_BoomerAMGSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_BoomerAMGSolve(solver, parcsr_A, par_b, par_x);

      /* Run info - needed logging turned on */
      HYPRE_BoomerAMGGetNumIterations(solver, &num_iterations);
      HYPRE_BoomerAMGGetFinalRelativeResidualNorm(solver, &final_res_norm);
      if (myid == 0)
      {
         printf("\n");
         printf("Iterations = %lld\n", num_iterations);
         printf("Final Relative Residual Norm = %e\n", final_res_norm);
         printf("\n");
      }

      /* Destroy solver */
      HYPRE_BoomerAMGDestroy(solver);
   }
   /* PCG */
   else if (solver_id == 50)
   {
      HYPRE_Int num_iterations;
      double final_res_norm;

      /* Create solver */
      HYPRE_ParCSRPCGCreate(MPI_COMM_WORLD, &solver);

      /* Set some parameters (See Reference Manual for more parameters) */
      HYPRE_PCGSetMaxIter(solver, 1000); /* max iterations */
      HYPRE_PCGSetTol(solver, 1e-7); /* conv. tolerance */
      HYPRE_PCGSetTwoNorm(solver, 1); /* use the two norm as the stopping criteria */
      HYPRE_PCGSetPrintLevel(solver, 2); /* prints out the iteration info */
      HYPRE_PCGSetLogging(solver, 1); /* needed to get run info later */

      /* Now setup and solve! */
      HYPRE_ParCSRPCGSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_ParCSRPCGSolve(solver, parcsr_A, par_b, par_x);

      /* Run info - needed logging turned on */
      HYPRE_PCGGetNumIterations(solver, &num_iterations);
      HYPRE_PCGGetFinalRelativeResidualNorm(solver, &final_res_norm);
      if (myid == 0)
      {
         printf("\n");
         printf("Iterations = %lld\n", num_iterations);
         printf("Final Relative Residual Norm = %e\n", final_res_norm);
         printf("\n");
      }

      /* Destroy solver */
      HYPRE_ParCSRPCGDestroy(solver);
   }
   /* PCG with AMG preconditioner */
   else if (solver_id == 1)
   {
      HYPRE_Int num_iterations;
      double final_res_norm;

      /* Create solver */
      HYPRE_ParCSRPCGCreate(MPI_COMM_WORLD, &solver);

      /* Set some parameters (See Reference Manual for more parameters) */
      HYPRE_PCGSetMaxIter(solver, 1000); /* max iterations */
      HYPRE_PCGSetTol(solver, 1e-7); /* conv. tolerance */
      HYPRE_PCGSetTwoNorm(solver, 1); /* use the two norm as the stopping criteria */
      HYPRE_PCGSetPrintLevel(solver, 2); /* print solve info */
      HYPRE_PCGSetLogging(solver, 1); /* needed to get run info later */

      /* Now set up the AMG preconditioner and specify any parameters */
      HYPRE_BoomerAMGCreate(&precond);
      HYPRE_BoomerAMGSetPrintLevel(precond, 1); /* print amg solution info */
      HYPRE_BoomerAMGSetCoarsenType(precond, 6);
      HYPRE_BoomerAMGSetRelaxType(precond, 6); /* Sym G.S./Jacobi hybrid */
      HYPRE_BoomerAMGSetNumSweeps(precond, 1);
      HYPRE_BoomerAMGSetTol(precond, 0.0); /* conv. tolerance zero */
      HYPRE_BoomerAMGSetMaxIter(precond, 1); /* do only one iteration! */

      /* Set the PCG preconditioner */
      HYPRE_PCGSetPrecond(solver, (HYPRE_PtrToSolverFcn) HYPRE_BoomerAMGSolve,
                          (HYPRE_PtrToSolverFcn) HYPRE_BoomerAMGSetup, precond);

      /* Now setup and solve! */
      HYPRE_ParCSRPCGSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_ParCSRPCGSolve(solver, parcsr_A, par_b, par_x);

      /* Run info - needed logging turned on */
      HYPRE_PCGGetNumIterations(solver, &num_iterations);
      HYPRE_PCGGetFinalRelativeResidualNorm(solver, &final_res_norm);
      if (myid == 0)
      {
         printf("\n");
         printf("Iterations = %lld\n", num_iterations);
         printf("Final Relative Residual Norm = %e\n", final_res_norm);
         printf("\n");
      }

      /* Destroy solver and preconditioner */
      HYPRE_ParCSRPCGDestroy(solver);
      HYPRE_BoomerAMGDestroy(precond);
   }
   /* PCG with Parasails Preconditioner */
   else if (solver_id == 8)
   {
      HYPRE_Int num_iterations;
      double final_res_norm;

      int      sai_max_levels = 1;
      double   sai_threshold = 0.1;
      double   sai_filter = 0.05;
      int      sai_sym = 1;

      /* Create solver */
      HYPRE_ParCSRPCGCreate(MPI_COMM_WORLD, &solver);

      /* Set some parameters (See Reference Manual for more parameters) */
      HYPRE_PCGSetMaxIter(solver, 1000); /* max iterations */
      HYPRE_PCGSetTol(solver, 1e-7); /* conv. tolerance */
      HYPRE_PCGSetTwoNorm(solver, 1); /* use the two norm as the stopping criteria */
      HYPRE_PCGSetPrintLevel(solver, 2); /* print solve info */
      HYPRE_PCGSetLogging(solver, 1); /* needed to get run info later */

      /* Now set up the ParaSails preconditioner and specify any parameters */
      HYPRE_ParaSailsCreate(MPI_COMM_WORLD, &precond);

      /* Set some parameters (See Reference Manual for more parameters) */
      HYPRE_ParaSailsSetParams(precond, sai_threshold, sai_max_levels);
      HYPRE_ParaSailsSetFilter(precond, sai_filter);
      HYPRE_ParaSailsSetSym(precond, sai_sym);
      HYPRE_ParaSailsSetLogging(precond, 3);

      /* Set the PCG preconditioner */
      HYPRE_PCGSetPrecond(solver, (HYPRE_PtrToSolverFcn) HYPRE_ParaSailsSolve,
                          (HYPRE_PtrToSolverFcn) HYPRE_ParaSailsSetup, precond);

      /* Now setup and solve! */
      HYPRE_ParCSRPCGSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_ParCSRPCGSolve(solver, parcsr_A, par_b, par_x);


      /* Run info - needed logging turned on */
      HYPRE_PCGGetNumIterations(solver, &num_iterations);
      HYPRE_PCGGetFinalRelativeResidualNorm(solver, &final_res_norm);
      if (myid == 0)
      {
         printf("\n");
         printf("Iterations = %lld\n", num_iterations);
         printf("Final Relative Residual Norm = %e\n", final_res_norm);
         printf("\n");
      }

      /* Destory solver and preconditioner */
      HYPRE_ParCSRPCGDestroy(solver);
      HYPRE_ParaSailsDestroy(precond);
   }
   /* Flexible GMRES with  AMG Preconditioner */
   else if (solver_id == 61)
   {
      HYPRE_Int num_iterations;
      double final_res_norm;
      int    restart = 30;
      int    modify = 1;


      /* Create solver */
      HYPRE_ParCSRFlexGMRESCreate(MPI_COMM_WORLD, &solver);

      /* Set some parameters (See Reference Manual for more parameters) */
      HYPRE_FlexGMRESSetKDim(solver, restart);
      HYPRE_FlexGMRESSetMaxIter(solver, 1000); /* max iterations */
      HYPRE_FlexGMRESSetTol(solver, 1e-7); /* conv. tolerance */
      HYPRE_FlexGMRESSetPrintLevel(solver, 2); /* print solve info */
      HYPRE_FlexGMRESSetLogging(solver, 1); /* needed to get run info later */


      /* Now set up the AMG preconditioner and specify any parameters */
      HYPRE_BoomerAMGCreate(&precond);
      HYPRE_BoomerAMGSetPrintLevel(precond, 1); /* print amg solution info */
      HYPRE_BoomerAMGSetCoarsenType(precond, 6);
      HYPRE_BoomerAMGSetRelaxType(precond, 6); /* Sym G.S./Jacobi hybrid */
      HYPRE_BoomerAMGSetNumSweeps(precond, 1);
      HYPRE_BoomerAMGSetTol(precond, 0.0); /* conv. tolerance zero */
      HYPRE_BoomerAMGSetMaxIter(precond, 1); /* do only one iteration! */

      /* Set the FlexGMRES preconditioner */
      HYPRE_FlexGMRESSetPrecond(solver, (HYPRE_PtrToSolverFcn) HYPRE_BoomerAMGSolve,
                          (HYPRE_PtrToSolverFcn) HYPRE_BoomerAMGSetup, precond);


      if (modify)
      /* this is an optional call  - if you don't call it, hypre_FlexGMRESModifyPCDefault
         is used - which does nothing.  Otherwise, you can define your own, similar to
         the one used here */
         HYPRE_FlexGMRESSetModifyPC( solver,
                                     (HYPRE_PtrToModifyPCFcn) hypre_FlexGMRESModifyPCAMGExample);


      /* Now setup and solve! */
      HYPRE_ParCSRFlexGMRESSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_ParCSRFlexGMRESSolve(solver, parcsr_A, par_b, par_x);

      /* Run info - needed logging turned on */
      HYPRE_FlexGMRESGetNumIterations(solver, &num_iterations);
      HYPRE_FlexGMRESGetFinalRelativeResidualNorm(solver, &final_res_norm);
      if (myid == 0)
      {
         printf("\n");
         printf("Iterations = %lld\n", num_iterations);
         printf("Final Relative Residual Norm = %e\n", final_res_norm);
         printf("\n");
      }

      /* Destory solver and preconditioner */
      HYPRE_ParCSRFlexGMRESDestroy(solver);
      HYPRE_BoomerAMGDestroy(precond);

   }
   else
   {
      if (myid ==0) printf("Invalid solver id specified.\n");
   }

   /* Print the solution */
   if (print_solution)
      HYPRE_IJVectorPrint(x, "ij.out.x");

   /* Clean up */
   HYPRE_IJMatrixDestroy(A);
   HYPRE_IJVectorDestroy(b);
   HYPRE_IJVectorDestroy(x);

   /* Finalize MPI*/
   MPI_Finalize();

   return(0);
}
HYPRE_Int main (HYPRE_Int argc, char *argv[])
{
   HYPRE_Int i;
   HYPRE_Int myid, num_procs;
   HYPRE_Int N, n;

   HYPRE_Int ilower, iupper;
   HYPRE_Int local_size, extra;

   HYPRE_Int solver_id;
   HYPRE_Int print_solution;

   double h, h2;

#ifdef HYPRE_FORTRAN
   hypre_F90_Obj A;
   hypre_F90_Obj parcsr_A;
   hypre_F90_Obj b;
   hypre_F90_Obj par_b;
   hypre_F90_Obj x;
   hypre_F90_Obj par_x;

   hypre_F90_Obj solver, precond;

   hypre_F90_Obj long_temp_COMM;
        HYPRE_Int temp_COMM;
        HYPRE_Int precond_id;

        HYPRE_Int one = 1;
        HYPRE_Int two = 2;
        HYPRE_Int three = 3;
        HYPRE_Int six = 6;
        HYPRE_Int twenty = 20;
        HYPRE_Int thousand = 1000;
        HYPRE_Int hypre_type = HYPRE_PARCSR;

     double oo1 = 1.e-3;
     double tol = 1.e-7;
#else
   HYPRE_IJMatrix A;
   HYPRE_ParCSRMatrix parcsr_A;
   HYPRE_IJVector b;
   HYPRE_ParVector par_b;
   HYPRE_IJVector x;
   HYPRE_ParVector par_x;

   HYPRE_Solver solver, precond;
#endif

   /* Initialize MPI */
   hypre_MPI_Init(&argc, &argv);
   hypre_MPI_Comm_rank(hypre_MPI_COMM_WORLD, &myid);
   hypre_MPI_Comm_size(hypre_MPI_COMM_WORLD, &num_procs);

   /* Default problem parameters */
   n = 33;
   solver_id = 0;
   print_solution  = 0;

   /* Parse command line */
   {
      HYPRE_Int arg_index = 0;
      HYPRE_Int print_usage = 0;

      while (arg_index < argc)
      {
         if ( strcmp(argv[arg_index], "-n") == 0 )
         {
            arg_index++;
            n = atoi(argv[arg_index++]);
         }
         else if ( strcmp(argv[arg_index], "-solver") == 0 )
         {
            arg_index++;
            solver_id = atoi(argv[arg_index++]);
         }
         else if ( strcmp(argv[arg_index], "-print_solution") == 0 )
         {
            arg_index++;
            print_solution = 1;
         }
         else if ( strcmp(argv[arg_index], "-help") == 0 )
         {
            print_usage = 1;
            break;
         }
         else
         {
            arg_index++;
         }
      }

      if ((print_usage) && (myid == 0))
      {
         hypre_printf("\n");
         hypre_printf("Usage: %s [<options>]\n", argv[0]);
         hypre_printf("\n");
         hypre_printf("  -n <n>              : problem size in each direction (default: 33)\n");
         hypre_printf("  -solver <ID>        : solver ID\n");
         hypre_printf("                        0  - AMG (default) \n");
         hypre_printf("                        1  - AMG-PCG\n");
         hypre_printf("                        8  - ParaSails-PCG\n");
         hypre_printf("                        50 - PCG\n");
         hypre_printf("  -print_solution     : print the solution vector\n");
         hypre_printf("\n");
      }

      if (print_usage)
      {
         hypre_MPI_Finalize();
         return (0);
      }
   }

   /* Preliminaries: want at least one processor per row */
   if (n*n < num_procs) n = sqrt(num_procs) + 1;
   N = n*n; /* global number of rows */
   h = 1.0/(n+1); /* mesh size*/
   h2 = h*h;

   /* Each processor knows only of its own rows - the range is denoted by ilower
      and upper.  Here we partition the rows. We account for the fact that
      N may not divide evenly by the number of processors. */
   local_size = N/num_procs;
   extra = N - local_size*num_procs;

   ilower = local_size*myid;
   ilower += hypre_min(myid, extra);

   iupper = local_size*(myid+1);
   iupper += hypre_min(myid+1, extra);
   iupper = iupper - 1;

   /* How many rows do I have? */
   local_size = iupper - ilower + 1;

   /* Create the matrix.
      Note that this is a square matrix, so we indicate the row partition
      size twice (since number of rows = number of cols) */
#ifdef HYPRE_FORTRAN
   long_temp_COMM = (hypre_F90_Obj) hypre_MPI_COMM_WORLD;
   temp_COMM = (HYPRE_Int) hypre_MPI_COMM_WORLD;
   HYPRE_IJMatrixCreate(&long_temp_COMM, &ilower, &iupper, &ilower, &iupper, &A);
#else
   HYPRE_IJMatrixCreate(hypre_MPI_COMM_WORLD, ilower, iupper, ilower, iupper, &A);
#endif

   /* Choose a parallel csr format storage (see the User's Manual) */
#ifdef HYPRE_FORTRAN
   HYPRE_IJMatrixSetObjectType(&A, &hypre_type);
#else
   HYPRE_IJMatrixSetObjectType(A, HYPRE_PARCSR);
#endif

   /* Initialize before setting coefficients */
#ifdef HYPRE_FORTRAN
   HYPRE_IJMatrixInitialize(&A);
#else
   HYPRE_IJMatrixInitialize(A);
#endif

   /* Now go through my local rows and set the matrix entries.
      Each row has at most 5 entries. For example, if n=3:

      A = [M -I 0; -I M -I; 0 -I M]
      M = [4 -1 0; -1 4 -1; 0 -1 4]

      Note that here we are setting one row at a time, though
      one could set all the rows together (see the User's Manual).
   */
   {
      HYPRE_Int nnz;
      double values[5];
      HYPRE_Int cols[5];

      for (i = ilower; i <= iupper; i++)
      {
         nnz = 0;

         /* The left identity block:position i-n */
         if ((i-n)>=0)
         {
	    cols[nnz] = i-n;
	    values[nnz] = -1.0;
	    nnz++;
         }

         /* The left -1: position i-1 */
         if (i%n)
         {
            cols[nnz] = i-1;
            values[nnz] = -1.0;
            nnz++;
         }

         /* Set the diagonal: position i */
         cols[nnz] = i;
         values[nnz] = 4.0;
         nnz++;

         /* The right -1: position i+1 */
         if ((i+1)%n)
         {
            cols[nnz] = i+1;
            values[nnz] = -1.0;
            nnz++;
         }

         /* The right identity block:position i+n */
         if ((i+n)< N)
         {
            cols[nnz] = i+n;
            values[nnz] = -1.0;
            nnz++;
         }

         /* Set the values for row i */
#ifdef HYPRE_FORTRAN
         HYPRE_IJMatrixSetValues(&A, &one, &nnz, &i, &cols[0], &values[0]);
#else
         HYPRE_IJMatrixSetValues(A, 1, &nnz, &i, cols, values);
#endif
      }
   }

   /* Assemble after setting the coefficients */
#ifdef HYPRE_FORTRAN
   HYPRE_IJMatrixAssemble(&A);
#else
   HYPRE_IJMatrixAssemble(A);
#endif
   /* Get the parcsr matrix object to use */
#ifdef HYPRE_FORTRAN
   HYPRE_IJMatrixGetObject(&A, &parcsr_A);
   HYPRE_IJMatrixGetObject(&A, &parcsr_A);
#else
   HYPRE_IJMatrixGetObject(A, (void**) &parcsr_A);
   HYPRE_IJMatrixGetObject(A, (void**) &parcsr_A);
#endif

   /* Create the rhs and solution */
#ifdef HYPRE_FORTRAN
   HYPRE_IJVectorCreate(&temp_COMM, &ilower, &iupper, &b);
   HYPRE_IJVectorSetObjectType(&b, &hypre_type);
   HYPRE_IJVectorInitialize(&b);
#else
   HYPRE_IJVectorCreate(hypre_MPI_COMM_WORLD, ilower, iupper,&b);
   HYPRE_IJVectorSetObjectType(b, HYPRE_PARCSR);
   HYPRE_IJVectorInitialize(b);
#endif

#ifdef HYPRE_FORTRAN
   HYPRE_IJVectorCreate(&temp_COMM, &ilower, &iupper, &x);
   HYPRE_IJVectorSetObjectType(&x, &hypre_type);
   HYPRE_IJVectorInitialize(&x);
#else
   HYPRE_IJVectorCreate(hypre_MPI_COMM_WORLD, ilower, iupper,&x);
   HYPRE_IJVectorSetObjectType(x, HYPRE_PARCSR);
   HYPRE_IJVectorInitialize(x);
#endif

   /* Set the rhs values to h^2 and the solution to zero */
   {
      double *rhs_values, *x_values;
      HYPRE_Int    *rows;

      rhs_values = calloc(local_size, sizeof(double));
      x_values = calloc(local_size, sizeof(double));
      rows = calloc(local_size, sizeof(HYPRE_Int));

      for (i=0; i<local_size; i++)
      {
         rhs_values[i] = h2;
         x_values[i] = 0.0;
         rows[i] = ilower + i;
      }
#ifdef HYPRE_FORTRAN
      HYPRE_IJVectorSetValues(&b, &local_size, &rows[0], &rhs_values[0]);
      HYPRE_IJVectorSetValues(&x, &local_size, &rows[0], &x_values[0]);
#else
      HYPRE_IJVectorSetValues(b, local_size, rows, rhs_values);
      HYPRE_IJVectorSetValues(x, local_size, rows, x_values);
#endif

      free(x_values);
      free(rhs_values);
      free(rows);
   }

#ifdef HYPRE_FORTRAN
   HYPRE_IJVectorAssemble(&b);
   HYPRE_IJVectorGetObject(&b, &par_b);
#else
   HYPRE_IJVectorAssemble(b);
   HYPRE_IJVectorGetObject(b, (void **) &par_b);
#endif

#ifdef HYPRE_FORTRAN
   HYPRE_IJVectorAssemble(&x);
   HYPRE_IJVectorGetObject(&x, &par_x);
#else
   HYPRE_IJVectorAssemble(x);
   HYPRE_IJVectorGetObject(x, (void **) &par_x);
#endif

   /* Choose a solver and solve the system */

   /* AMG */
   if (solver_id == 0)
   {
      HYPRE_Int num_iterations;
      double final_res_norm;

      /* Create solver */
#ifdef HYPRE_FORTRAN
      HYPRE_BoomerAMGCreate(&solver);
#else
      HYPRE_BoomerAMGCreate(&solver);
#endif

      /* Set some parameters (See Reference Manual for more parameters) */
#ifdef HYPRE_FORTRAN
      HYPRE_BoomerAMGSetPrintLevel(&solver, &three);  /* print solve info + parameters */
      HYPRE_BoomerAMGSetCoarsenType(&solver, &six); /* Falgout coarsening */
      HYPRE_BoomerAMGSetRelaxType(&solver, &three);   /* G-S/Jacobi hybrid relaxation */
      HYPRE_BoomerAMGSetNumSweeps(&solver, &one);   /* Sweeeps on each level */
      HYPRE_BoomerAMGSetMaxLevels(&solver, &twenty);  /* maximum number of levels */
      HYPRE_BoomerAMGSetTol(&solver, &tol);      /* conv. tolerance */
#else
      HYPRE_BoomerAMGSetPrintLevel(solver, 3);  /* print solve info + parameters */
      HYPRE_BoomerAMGSetCoarsenType(solver, 6); /* Falgout coarsening */
      HYPRE_BoomerAMGSetRelaxType(solver, 3);   /* G-S/Jacobi hybrid relaxation */
      HYPRE_BoomerAMGSetNumSweeps(solver, 1);   /* Sweeeps on each level */
      HYPRE_BoomerAMGSetMaxLevels(solver, 20);  /* maximum number of levels */
      HYPRE_BoomerAMGSetTol(solver, 1e-7);      /* conv. tolerance */
#endif

      /* Now setup and solve! */
#ifdef HYPRE_FORTRAN
      HYPRE_BoomerAMGSetup(&solver, &parcsr_A, &par_b, &par_x);
      HYPRE_BoomerAMGSolve(&solver, &parcsr_A, &par_b, &par_x);
#else
      HYPRE_BoomerAMGSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_BoomerAMGSolve(solver, parcsr_A, par_b, par_x);
#endif

      /* Run info - needed logging turned on */
#ifdef HYPRE_FORTRAN
      HYPRE_BoomerAMGGetNumIterations(&solver, &num_iterations);
      HYPRE_BoomerAMGGetFinalRelativeResidualNorm(&solver, &final_res_norm);
#else
      HYPRE_BoomerAMGGetNumIterations(solver, &num_iterations);
      HYPRE_BoomerAMGGetFinalRelativeResidualNorm(solver, &final_res_norm);
#endif
      if (myid == 0)
      {
         hypre_printf("\n");
         hypre_printf("Iterations = %d\n", num_iterations);
         hypre_printf("Final Relative Residual Norm = %e\n", final_res_norm);
         hypre_printf("\n");
      }

      /* Destroy solver */
#ifdef HYPRE_FORTRAN
      HYPRE_BoomerAMGDestroy(&solver);
#else
      HYPRE_BoomerAMGDestroy(solver);
#endif
   }
   /* PCG */
   else if (solver_id == 50)
   {
      HYPRE_Int num_iterations;
      double final_res_norm;

      /* Create solver */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGCreate(&temp_COMM, &solver);
#else
      HYPRE_ParCSRPCGCreate(hypre_MPI_COMM_WORLD, &solver);
#endif

      /* Set some parameters (See Reference Manual for more parameters) */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGSetMaxIter(&solver, &thousand); /* max iterations */
      HYPRE_ParCSRPCGSetTol(&solver, &tol); /* conv. tolerance */
      HYPRE_ParCSRPCGSetTwoNorm(&solver, &one); /* use the two norm as the stopping criteria */
      HYPRE_ParCSRPCGSetPrintLevel(&solver, &two); /* prints out the iteration info */
#else
      HYPRE_PCGSetMaxIter(solver, 1000); /* max iterations */
      HYPRE_PCGSetTol(solver, 1e-7); /* conv. tolerance */
      HYPRE_PCGSetTwoNorm(solver, 1); /* use the two norm as the stopping criteria */
      HYPRE_PCGSetPrintLevel(solver, 2); /* prints out the iteration info */
      HYPRE_PCGSetLogging(solver, 1); /* needed to get run info later */
#endif

      /* Now setup and solve! */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGSetup(&solver, &parcsr_A, &par_b, &par_x);
      HYPRE_ParCSRPCGSolve(&solver, &parcsr_A, &par_b, &par_x);
#else
      HYPRE_ParCSRPCGSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_ParCSRPCGSolve(solver, parcsr_A, par_b, par_x);
#endif

      /* Run info - needed logging turned on */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGGetNumIterations(&solver, &num_iterations);
      HYPRE_ParCSRPCGGetFinalRelativeResidualNorm(&solver, &final_res_norm);
#else
      HYPRE_PCGGetNumIterations(solver, &num_iterations);
      HYPRE_PCGGetFinalRelativeResidualNorm(solver, &final_res_norm);
#endif
      if (myid == 0)
      {
         hypre_printf("\n");
         hypre_printf("Iterations = %d\n", num_iterations);
         hypre_printf("Final Relative Residual Norm = %e\n", final_res_norm);
         hypre_printf("\n");
      }

      /* Destroy solver */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGDestroy(&solver);
#else
      HYPRE_ParCSRPCGDestroy(solver);
#endif
   }
   /* PCG with AMG preconditioner */
   else if (solver_id == 1)
   {
      HYPRE_Int num_iterations;
      double final_res_norm;

      /* Create solver */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGCreate(&temp_COMM, &solver);
#else
      HYPRE_ParCSRPCGCreate(hypre_MPI_COMM_WORLD, &solver);
#endif

      /* Set some parameters (See Reference Manual for more parameters) */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGSetMaxIter(&solver, &thousand); /* max iterations */
      HYPRE_ParCSRPCGSetTol(&solver, &tol); /* conv. tolerance */
      HYPRE_ParCSRPCGSetTwoNorm(&solver, &one); /* use the two norm as the stopping criteria */
      HYPRE_ParCSRPCGSetPrintLevel(&solver, &two); /* print solve info */
#else
      HYPRE_PCGSetMaxIter(solver, 1000); /* max iterations */
      HYPRE_PCGSetTol(solver, 1e-7); /* conv. tolerance */
      HYPRE_PCGSetTwoNorm(solver, 1); /* use the two norm as the stopping criteria */
      HYPRE_PCGSetPrintLevel(solver, 2); /* print solve info */
      HYPRE_PCGSetLogging(solver, 1); /* needed to get run info later */
#endif

      /* Now set up the AMG preconditioner and specify any parameters */
#ifdef HYPRE_FORTRAN
      HYPRE_BoomerAMGCreate(&precond);
      HYPRE_BoomerAMGSetPrintLevel(&precond, &one); /* print amg solution info*/
      HYPRE_BoomerAMGSetCoarsenType(&precond, &six);
      HYPRE_BoomerAMGSetRelaxType(&precond, &three);
      HYPRE_BoomerAMGSetNumSweeps(&precond, &one);
      HYPRE_BoomerAMGSetTol(&precond, &oo1);
#else
      HYPRE_BoomerAMGCreate(&precond);
      HYPRE_BoomerAMGSetPrintLevel(precond, 1); /* print amg solution info*/
      HYPRE_BoomerAMGSetCoarsenType(precond, 6);
      HYPRE_BoomerAMGSetRelaxType(precond, 3);
      HYPRE_BoomerAMGSetNumSweeps(precond, 1);
      HYPRE_BoomerAMGSetTol(precond, 1e-3);
#endif

      /* Set the PCG preconditioner */
#ifdef HYPRE_FORTRAN
      precond_id = 2;
      HYPRE_ParCSRPCGSetPrecond(&solver, &precond_id, &precond);
#else
      HYPRE_PCGSetPrecond(solver, (HYPRE_PtrToSolverFcn) HYPRE_BoomerAMGSolve,
                          (HYPRE_PtrToSolverFcn) HYPRE_BoomerAMGSetup, precond);
#endif

      /* Now setup and solve! */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGSetup(&solver, &parcsr_A, &par_b, &par_x);
      HYPRE_ParCSRPCGSolve(&solver, &parcsr_A, &par_b, &par_x);
#else
      HYPRE_ParCSRPCGSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_ParCSRPCGSolve(solver, parcsr_A, par_b, par_x);
#endif

      /* Run info - needed logging turned on */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGGetNumIterations(&solver, &num_iterations);
      HYPRE_ParCSRPCGGetFinalRelativeResidualNorm(&solver, &final_res_norm);
#else
      HYPRE_PCGGetNumIterations(solver, &num_iterations);
      HYPRE_PCGGetFinalRelativeResidualNorm(solver, &final_res_norm);
#endif
      if (myid == 0)
      {
         hypre_printf("\n");
         hypre_printf("Iterations = %d\n", num_iterations);
         hypre_printf("Final Relative Residual Norm = %e\n", final_res_norm);
         hypre_printf("\n");
      }

      /* Destroy solver and preconditioner */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGDestroy(&solver);
      HYPRE_BoomerAMGDestroy(&precond);
#else
      HYPRE_ParCSRPCGDestroy(solver);
      HYPRE_BoomerAMGDestroy(precond);
#endif
   }
   /* PCG with Parasails Preconditioner */
   else if (solver_id == 8)
   {
      HYPRE_Int    num_iterations;
      double final_res_norm;

      HYPRE_Int      sai_max_levels = 1;
      double   sai_threshold = 0.1;
      double   sai_filter = 0.05;
      HYPRE_Int      sai_sym = 1;

      /* Create solver */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGCreate(&temp_COMM, &solver);
#else
      HYPRE_ParCSRPCGCreate(hypre_MPI_COMM_WORLD, &solver);
#endif

      /* Set some parameters (See Reference Manual for more parameters) */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGSetMaxIter(&solver, &thousand); /* max iterations */
      HYPRE_ParCSRPCGSetTol(&solver, &tol); /* conv. tolerance */
      HYPRE_ParCSRPCGSetTwoNorm(&solver, &one); /* use the two norm as the stopping criteria */
      HYPRE_ParCSRPCGSetPrintLevel(&solver, &two); /* print solve info */
#else
      HYPRE_PCGSetMaxIter(solver, 1000); /* max iterations */
      HYPRE_PCGSetTol(solver, 1e-7); /* conv. tolerance */
      HYPRE_PCGSetTwoNorm(solver, 1); /* use the two norm as the stopping criteria */
      HYPRE_PCGSetPrintLevel(solver, 2); /* print solve info */
      HYPRE_PCGSetLogging(solver, 1); /* needed to get run info later */
#endif

      /* Now set up the ParaSails preconditioner and specify any parameters */
#ifdef HYPRE_FORTRAN
      HYPRE_ParaSailsCreate(&temp_COMM, &precond);
#else
      HYPRE_ParaSailsCreate(hypre_MPI_COMM_WORLD, &precond);
#endif

      /* Set some parameters (See Reference Manual for more parameters) */
#ifdef HYPRE_FORTRAN
      HYPRE_ParaSailsSetParams(&precond, &sai_threshold, &sai_max_levels);
      HYPRE_ParaSailsSetFilter(&precond, &sai_filter);
      HYPRE_ParaSailsSetSym(&precond, &sai_sym);
      HYPRE_ParaSailsSetLogging(&precond, &three);
#else
      HYPRE_ParaSailsSetParams(precond, sai_threshold, sai_max_levels);
      HYPRE_ParaSailsSetFilter(precond, sai_filter);
      HYPRE_ParaSailsSetSym(precond, sai_sym);
      HYPRE_ParaSailsSetLogging(precond, 3);
#endif

      /* Set the PCG preconditioner */
#ifdef HYPRE_FORTRAN
      precond_id = 4;
      HYPRE_ParCSRPCGSetPrecond(&solver, &precond_id, &precond);
#else
      HYPRE_PCGSetPrecond(solver, (HYPRE_PtrToSolverFcn) HYPRE_ParaSailsSolve,
                          (HYPRE_PtrToSolverFcn) HYPRE_ParaSailsSetup, precond);
#endif

      /* Now setup and solve! */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGSetup(&solver, &parcsr_A, &par_b, &par_x);
      HYPRE_ParCSRPCGSolve(&solver, &parcsr_A, &par_b, &par_x);
#else
      HYPRE_ParCSRPCGSetup(solver, parcsr_A, par_b, par_x);
      HYPRE_ParCSRPCGSolve(solver, parcsr_A, par_b, par_x);
#endif


      /* Run info - needed logging turned on */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGGetNumIterations(&solver, &num_iterations);
      HYPRE_ParCSRPCGGetFinalRelativeResidualNorm(&solver, &final_res_norm);
#else
      HYPRE_PCGGetNumIterations(solver, &num_iterations);
      HYPRE_PCGGetFinalRelativeResidualNorm(solver, &final_res_norm);
#endif
      if (myid == 0)
      {
         hypre_printf("\n");
         hypre_printf("Iterations = %d\n", num_iterations);
         hypre_printf("Final Relative Residual Norm = %e\n", final_res_norm);
         hypre_printf("\n");
      }

      /* Destory solver and preconditioner */
#ifdef HYPRE_FORTRAN
      HYPRE_ParCSRPCGDestroy(&solver);
      HYPRE_ParaSailsDestroy(&precond);
#else
      HYPRE_ParCSRPCGDestroy(solver);
      HYPRE_ParaSailsDestroy(precond);
#endif
   }
   else
   {
      if (myid ==0) hypre_printf("Invalid solver id specified.\n");
   }

   /* Print the solution */
#ifdef HYPRE_FORTRAN
   if (print_solution)
      HYPRE_IJVectorPrint(&x, "ij.out.x");
#else
   if (print_solution)
      HYPRE_IJVectorPrint(x, "ij.out.x");
#endif

   /* Clean up */
#ifdef HYPRE_FORTRAN
   HYPRE_IJMatrixDestroy(&A);
   HYPRE_IJVectorDestroy(&b);
   HYPRE_IJVectorDestroy(&x);
#else
   HYPRE_IJMatrixDestroy(A);
   HYPRE_IJVectorDestroy(b);
   HYPRE_IJVectorDestroy(x);
#endif

   /* Finalize MPI*/
   hypre_MPI_Finalize();

   return(0);
}
int main(int argc, char *argv[]) {
  // printf("start\n");
  int i, j, pi, pj;
  int myid, num_procs;
  int Nx, Ny, nx, ny;
  //nx and ny are number of mesh cells
  int solver_id;
  int vis;
  int ilower[2], iupper[2];
  int nparts = 1;
  int part = 0;

  double hx, hy;

  HYPRE_SStructGraph graph;
  HYPRE_SStructGrid grid;
  HYPRE_SStructStencil stencil;
  HYPRE_SStructMatrix A;
  HYPRE_ParCSRMatrix parcsr_A;
  HYPRE_SStructVector b;
  HYPRE_ParVector par_b;
  HYPRE_SStructVector x;
  HYPRE_ParVector par_x;

  HYPRE_Solver solver;
  // printf("variables initialized\n");
   
  //Initialize MPI
  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &myid);
  MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
  // printf("mpi initialized\n");
   
  // Set default problem parameters
  nx = 100;
  ny = 100;
  Nx = 1;
  solver_id = 0;
  vis = 0;
  // printf("defaults set\n");
   
  // Parse command line

  int arg_index = 0;

  while (arg_index < argc) {
    if (strcmp(argv[arg_index], "-nx") == 0) {
      arg_index++;
      nx = atoi(argv[arg_index++]);
      // printf("nx set\n");
    } else if (strcmp(argv[arg_index], "-ny") == 0) {
      arg_index++;
      ny = atoi(argv[arg_index++]);
      // printf("ny set\n");
    } else if (strcmp(argv[arg_index], "-Nx") == 0) {
      arg_index++;
      Nx = atoi(argv[arg_index++]);
      // printf("Nx set\n");
    } else if (strcmp(argv[arg_index], "-solver") == 0) {
      arg_index++;
      solver_id = atoi(argv[arg_index++]);
      // printf("solver set\n");
    } else if (strcmp(argv[arg_index], "-vis") == 0) {
      arg_index++;
      vis = 1;
      // printf("print solution\n");
    } else {
      arg_index++;
      // printf("plus plus\n");
    }
  }

  // printf("command line parsed\n");
   
  // Set up processors in grid 
  Ny = num_procs / Nx;
  hx = 1.0 / (Nx * nx);
  hy = 1.0 / (Ny * ny);

  if (Ny <= Nx) {
    pj = myid / Nx;
    pi = myid - pj * Ny;
  } else {
    pi = myid / Ny;
    pj = myid - pi * Nx;
  }
  // printf("processors assigned\n");

  // Determine each processor's piece of the grid 
  ilower[0] = pi * nx;
  ilower[1] = pj * ny;

  iupper[0] = ilower[0] + nx - 1;
  iupper[1] = ilower[1] + ny - 1;

  // printf("upper and lowers assigned\n");
  HYPRE_SStructGridCreate(MPI_COMM_WORLD, 2, nparts, &grid);
  // printf("grid created\n");
  HYPRE_SStructGridSetExtents(grid, part, ilower, iupper);
  // printf("grid set\n");
  
  int nvars = 1;

  HYPRE_SStructVariable vartypes[1] = { HYPRE_SSTRUCT_VARIABLE_CELL };
  // printf("variable type set\n");
  for (i = 0; i < nparts; i++) {
    HYPRE_SStructGridSetVariables(grid, i, nvars, vartypes);
  }
  // printf("variable type set\n");
  HYPRE_SStructGridAssemble(grid);
  // printf("grid assembled\n");
  
  // Set up the stencil 
  HYPRE_SStructStencilCreate(2, 5, &stencil);
  int entry;
  int offsets[5][2] = { { 0, 0 }, { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
  int var = 0;

  for (entry = 0; entry < 5; entry++) {
    HYPRE_SStructStencilSetEntry(stencil, entry, offsets[entry], var);
  }

  // Create the graph 
  HYPRE_SStructGraphCreate(MPI_COMM_WORLD, grid, &graph);

  int object_type = HYPRE_PARCSR;

  HYPRE_SStructGraphSetObjectType(graph, object_type);

  HYPRE_SStructGraphSetStencil(graph, part, var, stencil);
  HYPRE_SStructGraphAssemble(graph);

  int nentries = 5;
  int nvalues = nentries * nx * ny;
  double *values;
  int stencil_indices[5];

  // Setup the matrix 
  HYPRE_SStructMatrixCreate(MPI_COMM_WORLD, graph, &A);
  HYPRE_SStructMatrixSetObjectType(A, HYPRE_PARCSR);
  HYPRE_SStructMatrixInitialize(A);

  // Set the matrix coefficients 
  values = calloc(nvalues, sizeof(double));
  for (j = 0; j < nentries; j++) {
    stencil_indices[j] = j;
  }

  // Account for differences in hx and hy 
  double hx2 = hx * hx;
  double hy2 = hy * hy;
  double hx2inv = 1 / hx2;
  double hy2inv = 1 / hy2;

  for (i = 0; i < nvalues; i += nentries) {
    values[i] = -2.0 * hx2inv - 2.0 * hy2inv;
    values[i + 1] = hx2inv;
    values[i + 2] = hx2inv;
    values[i + 3] = hy2inv;
    values[i + 4] = hy2inv;
  }

  HYPRE_SStructMatrixSetBoxValues(A, part, ilower, iupper, var, nentries,
                                  stencil_indices, values);

  // Set the dirchelet boundary condition
  {
    int bc_ilower[2];
    int bc_iupper[2];
    int nentries = 1;
    int nvaluesx = nentries * nx; /*  number of stencil entries times the length
                                      of one side of my grid box */
    int nvaluesy = nentries * ny;
    double *valuesx, *valuesy, *center_valuesx, *center_valuesy;
    int stencil_indices[1];
    int center_index[1];
    center_index[0] = 0;

    valuesx = calloc(nvaluesx, sizeof(double));
    center_valuesx = calloc(nvaluesx, sizeof(double));
    for (j = 0; j < nvaluesx; j++){
      valuesx[j] = 0.0;
      center_valuesx[j] = -hx2inv;
    }

    valuesy = calloc(nvaluesy, sizeof(double));
    center_valuesy = calloc(nvaluesy, sizeof(double));
    for (j = 0; j < nvaluesy; j++){
      valuesy[j] = 0.0;
      center_valuesy[j] = -hy2inv;
     }
 

    // Recall: pi and pj describe position in the processor grid 
    if (pj == 0) {
      // Bottom row of grid points 
      bc_ilower[0] = pi * nx;
      bc_ilower[1] = pj * ny;

      bc_iupper[0] = bc_ilower[0] + nx - 1;
      bc_iupper[1] = bc_ilower[1];

      stencil_indices[0] = 3;

      HYPRE_SStructMatrixSetBoxValues(A, part, bc_ilower, bc_iupper, var,
                                      nentries, stencil_indices, valuesy);
      HYPRE_SStructMatrixAddToBoxValues(A, part, bc_ilower, bc_iupper, var, nentries, center_index, center_valuesy);
      // printf("Bottom row zeroed\n");
    }

    if (pj == Ny - 1) {
      // upper row of grid points 
      bc_ilower[0] = pi * nx;
      bc_ilower[1] = pj * ny + ny - 1;

      bc_iupper[0] = bc_ilower[0] + ny - 1;
      bc_iupper[1] = bc_ilower[1];

      stencil_indices[0] = 4;

      HYPRE_SStructMatrixSetBoxValues(A, part, bc_ilower, bc_iupper, var,
                                      nentries, stencil_indices, valuesy);
      HYPRE_SStructMatrixAddToBoxValues(A, part, bc_ilower, bc_iupper, var, nentries, center_index, center_valuesy);
      // printf("Upper row zeroed\n");
    }
    if (pi == 0) {
      // Left row of grid points 
      bc_ilower[0] = pi * nx;
      bc_ilower[1] = pj * ny;

      bc_iupper[0] = bc_ilower[0];
      bc_iupper[1] = bc_ilower[1] + ny - 1;

      stencil_indices[0] = 1;

      HYPRE_SStructMatrixSetBoxValues(A, part, bc_ilower, bc_iupper, var,
                                      nentries, stencil_indices, valuesx);
      HYPRE_SStructMatrixAddToBoxValues(A, part, bc_ilower, bc_iupper, var, nentries, center_index, center_valuesx); 
      // printf("left collumn zeroed\n");
    }

    if (pi == Nx - 1) {
      // Right row of grid points 
      bc_ilower[0] = pi * nx + nx - 1;
      bc_ilower[1] = pj * ny;

      bc_iupper[0] = bc_ilower[0];
      bc_iupper[1] = bc_ilower[1] + ny - 1;

      stencil_indices[0] = 2;

      HYPRE_SStructMatrixSetBoxValues(A, part, bc_ilower, bc_iupper, var,
                                      nentries, stencil_indices, valuesx);
      HYPRE_SStructMatrixAddToBoxValues(A, part, bc_ilower, bc_iupper, var, nentries, center_index, center_valuesx);
      // printf("right collumn zeroed\n");
    }
    free(valuesx);
    free(center_valuesx);
    free(valuesy);
    free(center_valuesy);
  }

  HYPRE_SStructMatrixAssemble(A);
  HYPRE_SStructMatrixGetObject(A, (void **)&parcsr_A);

  // Create the right hand side and solution vectors 
  HYPRE_SStructVectorCreate(MPI_COMM_WORLD, grid, &b);
  HYPRE_SStructVectorSetObjectType(b, HYPRE_PARCSR);
  HYPRE_SStructVectorInitialize(b);

  HYPRE_SStructVectorCreate(MPI_COMM_WORLD, grid, &x);
  HYPRE_SStructVectorSetObjectType(x, HYPRE_PARCSR);
  HYPRE_SStructVectorInitialize(x);

  // Set the right hand side, exact solution, and initial guess values 
  double *rhs_values, *x_values, *exactsolution;
  nvalues = nx * ny;

  rhs_values = calloc(nvalues, sizeof(double));
  x_values = calloc(nvalues, sizeof(double));
  exactsolution = calloc(nvalues, sizeof(double));
  // printf("RHS calculation begin\n");
    for (j = 0; j < ny; j++) {
    double y =  (ilower[1] + j + .5) * hy;
    for (i = 0; i < nx; i++) {
      double x = (ilower[0] + i + .5) * hx;
      rhs_values[i + j * nx] =
          -8. * PI2 * sin(2. * M_PI * x) * sin(2. * M_PI * y);
      exactsolution[i + j * nx] = 1.0 * sin(2. * M_PI * x) * sin(2. * M_PI * y);
      x_values[i + j * nx] = 0.0;
    }
  }
  // printf("RHS calculation ended\n");
  HYPRE_SStructVectorSetBoxValues(b, part, ilower, iupper, var, rhs_values);
  HYPRE_SStructVectorSetBoxValues(x, part, ilower, iupper, var, x_values);


  //printf("%d  %f\n", (ny*nx-1), exactsolution[ny*nx-1]);
  

  free(x_values);
  free(rhs_values);

  HYPRE_SStructVectorAssemble(b);
  // HYPRE_SStructVectorPrint("ss.initial.b", b, 0);
  HYPRE_SStructVectorGetObject(b, (void **)&par_b);
  HYPRE_SStructVectorAssemble(x);
  HYPRE_SStructVectorGetObject(x, (void **)&par_x);

  // Print out initial vectors 
  HYPRE_SStructMatrixPrint("SStructExact/ss.initial.A", A, 0);
  HYPRE_SStructVectorPrint("SStructExact/ss.initial.b", b, 0);
  // printf("About to solve\n");

  // Select a solver 
  if (solver_id == 0) {
    int num_iterations;
    double final_res_norm;

    // Create AMG solver 
    HYPRE_BoomerAMGCreate(&solver);

    // Set solver parameters 
    HYPRE_BoomerAMGSetPrintLevel(solver, 3);
    HYPRE_BoomerAMGSetCoarsenType(solver, 6);
    HYPRE_BoomerAMGSetRelaxType(solver, 3);
    HYPRE_BoomerAMGSetNumSweeps(solver, 1);
    HYPRE_BoomerAMGSetMaxLevels(solver, 20);
    HYPRE_BoomerAMGSetTol(solver, 1e-12);
    HYPRE_BoomerAMGSetMaxIter(solver, 100);
    // Set up and solve 
    HYPRE_BoomerAMGSetup(solver, parcsr_A, par_b, par_x);
    HYPRE_BoomerAMGSolve(solver, parcsr_A, par_b, par_x);
    //printf("solution obtained\n");
  
    // Run information 
    HYPRE_BoomerAMGGetNumIterations(solver, &num_iterations);
    HYPRE_BoomerAMGGetFinalRelativeResidualNorm(solver, &final_res_norm);
    if (myid == 0) {
      printf("\n");
      printf("Iterations = %d\n", num_iterations);
      printf("Final Relative Residual Norm = %e\n", final_res_norm);
      printf("\n");
    }

    // Destroy solver 
    HYPRE_BoomerAMGDestroy(solver);
    // HYPRE_ParVectorPrint(par_x, "SStructExact/ss.final.x");
    char solutionfile[255];
    sprintf(solutionfile, "%s.%06d", "ss.final.x", myid);
    HYPRE_ParVectorPrint(par_x, solutionfile);
  }

  // Save the solution for visualization and L2 norm calculation  
  if (vis) {
    FILE *file;
    FILE *solution;

    char filename[255];
    char solutionfile[255];

    sprintf(solutionfile, "%s.%06d.%d", "ss.final.x", myid, myid);

    int nvalues = nx * ny;

    double sum = 0;
    double diff, diff2, L2;

    // get the localally calculated and exact solutions)
    double *values = calloc(nvalues+1, sizeof(double));

    // Opens the local solution file, reads the solution to the array, values
    // and closes the file. 

    // Opens the solution file 
    if ((solution = fopen(solutionfile, "rt")) == NULL) {
      printf("Error: can't open output file %s\n", solutionfile);
      MPI_Finalize();
      exit(1);
    }

    // Reads the file to the array values 
    for (i = 0; i < nvalues + 1; i++) {
      fscanf(solution, "%lf", &values[i]);
    }

    // Close the file 
    fflush(solution);
    fclose(solution);

    MPI_Barrier(MPI_COMM_WORLD);

    // Calcluates the sum of the squared differences for the exact and numerical solutions on each processor   
  for (i = 1; i < nvalues + 1; i++) {
      diff = values[i] - exactsolution[i-1];
      diff2 = diff * diff;
      if (diff2 > 10){
       printf("%+6f       %+6f     %+6d\n", values[i], exactsolution[i-1],
       i-1);
      }
      sum += diff2;
      }
    //printf("Myid is: %d and mysum is: %f\n", myid, sum);
    
   //  Ensure all processors are caught up 
    MPI_Barrier(MPI_COMM_WORLD);
 
    // Passes the individual sums to processor 0 and sums them. 
    double *sendbuffer, *recvbuffer;
    sendbuffer = calloc(1,  sizeof(double));
    sendbuffer[0] = sum;
    recvbuffer = calloc(1, sizeof(double));
    int root = 0;
    int count = 1;
    MPI_Reduce(sendbuffer, recvbuffer, count, MPI_DOUBLE, MPI_SUM, root, MPI_COMM_WORLD);
    MPI_Barrier(MPI_COMM_WORLD);
 
  // Calculates the L2 norm  
  if (myid == 0){
      //printf("%f\n" , recvbuffer[myid]);
      // nvalues = (nx-2)*(ny-2);      
      double totalvalues = nvalues * num_procs * 1.0; 

      L2 = sqrt(recvbuffer[0] / totalvalues);      
      printf("The L2 norm is %e\n", L2);    
    }
   
    sprintf(filename, "%s.%06d", "vis/ss.sol", myid);

    if ((file = fopen(filename, "w")) == NULL) {
      printf("Error: can't open output file %s\n", filename);
      MPI_Finalize();
      exit(1);
    }

    // Save solution 
    for (i = 1; i < nvalues+1; i++) {
      fprintf(file, "%.14e\n", values[i]);
    }

    // Vis Cleanup
    fflush(file);
    fclose(file);

    free(values);
    free(exactsolution);
    free(sendbuffer);
    free(recvbuffer);

    // save global finite element mesh 
    if (myid == 0) {
      GLVis_PrintGlobalMesh("vis/ss.mesh", Nx, Ny, nx, ny, hx, hy);
    }
 } 

  // Clean up 

  HYPRE_SStructMatrixDestroy(A);
  HYPRE_SStructVectorDestroy(b);
  HYPRE_SStructVectorDestroy(x);

  // Finalize MPI*/
  
  MPI_Finalize();

  return (0);
}