private static void Main(string[] args) //****************************************************************************80 // // Purpose: // // MAIN is the main program for FEM2D_POISSON_CG. // // Discussion: // // This program is a variant of FEM2D_POISSON. That program is // particularly limited because of its use of banded matrix storage and // solving routines. // // This program discards the banded approach. Instead, it uses a // sparse matrix storage format and a conjugate gradient solver, // which allow this program to solve larger problems faster. // // This program solves the Poisson equation // // -DEL H(X,Y) DEL U(X,Y) + K(X,Y) * U(X,Y) = F(X,Y) // // in a triangulated region in the plane. // // Along the boundary of the region, Dirichlet conditions // are imposed: // // U(X,Y) = G(X,Y) // // The code uses continuous piecewise linear basis functions on // triangles. // // Problem specification: // // The user defines the geometry by supplying two data files // which list the node coordinates, and list the nodes that make up // each element. // // The user specifies the right hand side of the Dirichlet boundary // conditions by supplying a function // // void dirichlet_condition ( int node_num, double node_xy[2*node_num], // double node_bc[node_num] ) // // The user specifies the coefficient function H(X,Y) of the Poisson // equation by supplying a routine of the form // // void h_coef ( int node_num, double node_xy[2*node_num], // double node_h[node_num] ) // // The user specifies the coefficient function K(X,Y) of the Poisson // equation by supplying a routine of the form // // void k_coef ( int node_num, double node_xy[2*node_num], // double node_k[node_num] ) // // The user specifies the right hand side of the Poisson equation // by supplying a routine of the form // // void rhs ( int node_num, double node_xy[2*node_num], // double node_f[node_num] ) // // Usage: // // fem2d_poisson_cg prefix // // where 'prefix' is the common filename prefix so that: // // * prefix_nodes.txt contains the coordinates of the nodes; // * prefix_elements.txt contains the indices of nodes forming each element. // // Files created include: // // * prefix_nodes.eps, an image of the nodes; // * prefix_elements.eps, an image of the elements; // * prefix_values.txt, the value of the solution at every node. // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 25 January 2013 // // Author: // // John Burkardt // // Local parameters: // // Local, double A[NZ_NUM], the coefficient matrix. // // Local, int ELEMENT_NODE[3*ELEMENT_NUM]; // ELEMENT_NODE(I,J) is the global index of local node I in element J. // // Local, int ELEMENT_NUM, the number of elements. // // Local, integer ELEMENT_ORDER, the element order. // // Local, double F[NODE_NUM], the right hand side. // // Local, int IA[NZ_NUM], the row indices of the nonzero entries // of the coefficient matrix. // // Local, int JA[NZ_NUM], the column indices of the nonzero entries // of the coefficient matrix. // // Local, bool NODE_BOUNDARY[NODE_NUM], is TRUE if the node is // found to lie on the boundary of the region. // // Local, int NODE_CONDITION[NODE_NUM], // indicates the condition used to determine the variable at a node. // 0, there is no condition (and no variable) at this node. // 1, a finite element equation is used; // 2, a Dirichlet condition is used. // 3, a Neumann condition is used. // // Local, int NODE_NUM, the number of nodes. // // Local, double NODE_U[NODE_NUM], the finite element coefficients. // // Local, double NODE_XY[2*NODE_NUM], the coordinates of nodes. // // Local, int NZ_NUM, the number of nonzero entries // in the coefficient matrix. // // Local, integer QUAD_NUM, the number of quadrature points used for // assembly. This is currently set to 3, the lowest reasonable value. // Legal values are 1, 3, 4, 6, 7, 9, 13, and for some problems, a value // of QUAD_NUM greater than 3 may be appropriate. // { const bool debug = false; int node; string prefix; const int quad_num = 3; Console.WriteLine(""); Console.WriteLine("FEM2D_POISSON_CG:"); Console.WriteLine(""); Console.WriteLine(" A version of FEM2D_POISSON using sparse storage"); Console.WriteLine(" and a conjugate gradient solver."); Console.WriteLine(""); Console.WriteLine(" Solution of the Poisson equation in an arbitrary region"); Console.WriteLine(" in 2 dimensions."); Console.WriteLine(""); Console.WriteLine(" - DEL H(x,y) DEL U(x,y) + K(x,y) * U(x,y) = F(x,y) in the region"); Console.WriteLine(""); Console.WriteLine(" U(x,y) = G(x,y) on the boundary."); Console.WriteLine(""); Console.WriteLine(" The finite element method is used,"); Console.WriteLine(" with triangular elements,"); Console.WriteLine(" which must be a 3 node linear triangle."); // // Get the filename prefix. // try { prefix = args[0]; } catch { Console.WriteLine(""); Console.WriteLine(" Please enter the filename prefix:"); prefix = Console.ReadLine(); } if (prefix != "baffle" && prefix != "ell" && prefix != "lake") { Console.WriteLine("Supported prefix value in this test is one of : baffle, ell, lake"); return; } // // Create the file names. // string node_filename = prefix + "_nodes.txt"; string element_filename = prefix + "_elements.txt"; string solution_filename = prefix + "_values.txt"; Console.WriteLine(""); Console.WriteLine(" Node file is \"" + node_filename + "\"."); Console.WriteLine(" Element file is \"" + element_filename + "\"."); // // Read the node coordinate file. // TableHeader h = typeMethods.r8mat_header_read(node_filename); int dim_num = h.m; int node_num = h.n; Console.WriteLine(" Number of nodes = " + node_num + ""); int[] node_condition = new int[node_num]; double[] node_xy = typeMethods.r8mat_data_read(node_filename, dim_num, node_num); typeMethods.r8mat_transpose_print_some(dim_num, node_num, node_xy, 1, 1, 2, 10, " First 10 nodes"); // // Read the triangle description file. // h = typeMethods.i4mat_header_read(element_filename); int element_order = h.m; int element_num = h.n; Console.WriteLine(""); Console.WriteLine(" Element order = " + element_order + ""); Console.WriteLine(" Number of elements = " + element_num + ""); if (element_order != 3) { Console.WriteLine(""); Console.WriteLine("FEM2D_POISSON_CG - Fatal error!"); Console.WriteLine(" The input triangulation has order " + element_order + ""); Console.WriteLine(" However, a triangulation of order 3 is required."); return; } int[] element_node = typeMethods.i4mat_data_read(element_filename, element_order, element_num); typeMethods.i4mat_transpose_print_some(3, element_num, element_node, 1, 1, 3, 10, " First 10 elements"); Console.WriteLine(""); Console.WriteLine(" Quadrature order = " + quad_num + ""); // // Determine which nodes are boundary nodes and which have a // finite element unknown. Then set the boundary values. // bool[] node_boundary = Boundary.triangulation_order3_boundary_node(node_num, element_num, element_node); // // Determine the node conditions. // For now, we'll just assume all boundary nodes are Dirichlet. // for (node = 0; node < node_num; node++) { node_condition[node] = node_boundary[node] switch { true => 2, _ => 1 }; } // // Determine the element neighbor array, just so we can estimate // the nonzeros. // int[] element_neighbor = Neighbor.triangulation_order3_neighbor_triangles(element_num, element_node); // // Count the number of nonzeros. // int[] adj_col = new int[node_num + 1]; int nz_num = Adjacency.triangulation_order3_adj_count(node_num, element_num, element_node, element_neighbor, adj_col); Console.WriteLine(""); Console.WriteLine(" Number of nonzero coefficients NZ_NUM = " + nz_num + ""); // // Set up the sparse row and column index vectors. // int[] ia = new int[nz_num]; int[] ja = new int [nz_num]; Adjacency.triangulation_order3_adj_set2(node_num, element_num, element_node, element_neighbor, nz_num, adj_col, ia, ja); switch (debug) { case true: typeMethods.i4vec2_print(nz_num, ia, ja, " Adjacency pairs:"); break; } // // Index the diagonal elements for use by the CG solver. // int[] diag = Diagonal.diag_index(nz_num, ia, ja, node_num); switch (debug) { case true: typeMethods.i4vec_print(node_num, diag, " Diagonal adjacency vector:"); break; } // // Allocate space for the coefficient matrix A and right hand side F. // double[] a = new double[nz_num]; double[] f = new double[node_num]; double[] node_u = new double[node_num]; // // Assemble the finite element coefficient matrix A and the right-hand side F. // switch (prefix) { case "baffle": DSP.assemble_poisson_dsp(node_num, node_xy, element_num, element_node, quad_num, nz_num, ia, ja, ref a, ref f, baffle.rhs, baffle.h_coef, baffle.k_coef); break; case "ell": DSP.assemble_poisson_dsp(node_num, node_xy, element_num, element_node, quad_num, nz_num, ia, ja, ref a, ref f, ell.rhs, ell.h_coef, ell.k_coef); break; case "lake": DSP.assemble_poisson_dsp(node_num, node_xy, element_num, element_node, quad_num, nz_num, ia, ja, ref a, ref f, lake.rhs, lake.h_coef, lake.k_coef); break; } switch (debug) { // // Print a portion of the matrix. // case true: DSP.dsp_print_some(node_num, node_num, nz_num, ia, ja, a, 1, 1, 10, 10, " Part of Finite Element matrix A:"); typeMethods.r8vec_print_some(node_num, f, 1, 10, " Part of right hand side vector F:"); break; } // // Adjust the linear system to account for Dirichlet boundary conditions. // switch (prefix) { case "baffle": DSP.dirichlet_apply_dsp(node_num, node_xy, node_condition, nz_num, ia, ja, ref a, ref f, baffle.dirichlet_condition); break; case "ell": DSP.dirichlet_apply_dsp(node_num, node_xy, node_condition, nz_num, ia, ja, ref a, ref f, ell.dirichlet_condition); break; case "lake": DSP.dirichlet_apply_dsp(node_num, node_xy, node_condition, nz_num, ia, ja, ref a, ref f, lake.dirichlet_condition); break; } switch (debug) { case true: DSP.dsp_print_some(node_num, node_num, nz_num, ia, ja, a, 1, 1, 10, 10, " Part of finite Element matrix A after boundary adjustments:"); typeMethods.r8vec_print_some(node_num, f, 1, 10, " Part of right hand side vector F:"); break; } // // Solve the linear system using the conjugate gradient method. // node_u = ConjugateGradient.solve_cg(node_num, diag, nz_num, ia, ja, a, f); typeMethods.r8vec_print_some(node_num, node_u, 1, 10, " Part of the solution vector vector U:"); // // Write an ASCII file that can be read into MATLAB. // typeMethods.r8mat_write(solution_filename, 1, node_num, node_u); Console.WriteLine(""); Console.WriteLine(" Wrote an ASCII file"); Console.WriteLine(" \"" + solution_filename + "\"."); Console.WriteLine(" of the form"); Console.WriteLine(" U ( X(I), Y(I) )"); Console.WriteLine(" which can be used for plotting."); switch (debug) { case true: typeMethods.r8vec_print_some(node_num, node_u, 1, 10, " Part of the solution vector:"); break; } Console.WriteLine(""); Console.WriteLine("FEM2D_POISSON_CG:"); Console.WriteLine(" Normal end of execution."); Console.WriteLine(""); } }