GetFEM  5.4.3
laplacian.cc
Go to the documentation of this file.
1 /*===========================================================================
2 
3  Copyright (C) 2002-2020 Yves Renard, Julien Pommier.
4 
5  This file is a part of GetFEM
6 
7  GetFEM is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version along with the GCC Runtime Library
11  Exception either version 3.1 or (at your option) any later version.
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15  License and GCC Runtime Library Exception for more details.
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program; if not, write to the Free Software Foundation,
18  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20 ===========================================================================*/
21 
22 /**@file laplacian.cc
23  @brief Laplacian (Poisson) problem.
24 
25  The laplace equation is solved on a regular mesh of the unit
26  square, and is compared to the analytical solution.
27 
28  This program is used to check that getfem++ is working. This is
29  also a good example of use of GetFEM. This program does not use the
30  model bricks intentionally in order to serve as an example of solving
31  a pde directly with the assembly procedures.
32 */
33 
35 #include "getfem/getfem_export.h"
38 #include "gmm/gmm.h"
39 using std::endl; using std::cout; using std::cerr;
40 using std::ends; using std::cin;
41 
42 /* some GetFEM types that we will be using */
43 using bgeot::base_small_vector; /* special class for small (dim<16) vectors */
44 using bgeot::base_node; /* geometrical nodes (derived from base_small_vector)*/
45 using bgeot::scalar_type; /* = double */
46 using bgeot::size_type; /* = unsigned long */
47 
48 /* definition of some matrix/vector types. These ones are built
49  * using the predefined types in Gmm++
50  */
52 typedef gmm::row_matrix<sparse_vector_type> sparse_matrix_type;
53 typedef gmm::col_matrix<sparse_vector_type> col_sparse_matrix_type;
54 typedef std::vector<scalar_type> plain_vector;
55 
56 /* Definitions for the exact solution of the Laplacian problem,
57  * i.e. Delta(sol_u) + sol_f = 0
58  */
59 
60 base_small_vector sol_K; /* a coefficient */
61 /* exact solution */
62 scalar_type sol_u(const base_node &x) { return sin(gmm::vect_sp(sol_K, x)); }
63 /* righ hand side */
64 scalar_type sol_f(const base_node &x)
65 { return gmm::vect_sp(sol_K, sol_K) * sin(gmm::vect_sp(sol_K, x)); }
66 /* gradient of the exact solution */
67 base_small_vector sol_grad(const base_node &x)
68 { return sol_K * cos(gmm::vect_sp(sol_K, x)); }
69 
70 /*
71  structure for the Laplacian problem
72 */
73 struct laplacian_problem {
74 
75  enum { DIRICHLET_BOUNDARY_NUM = 0, NEUMANN_BOUNDARY_NUM = 1};
76  getfem::mesh mesh; /* the mesh */
77  getfem::mesh_im mim; /* the integration methods. */
78  getfem::mesh_fem mf_u; /* the main mesh_fem, for the Laplacian solution */
79  getfem::mesh_fem mf_rhs; /* the mesh_fem for the right hand side(f(x),..) */
80  getfem::mesh_fem mf_coef; /* the mesh_fem to represent pde coefficients */
81 
82  scalar_type residual; /* max residual for the iterative solvers */
83  size_type N;
84  bool gen_dirichlet;
85 
86  sparse_matrix_type SM; /* stiffness matrix. */
87  std::vector<scalar_type> U, B; /* main unknown, and right hand side */
88 
89  std::vector<scalar_type> Ud; /* reduced sol. for gen. Dirichlet condition. */
90  col_sparse_matrix_type NS; /* Dirichlet NullSpace
91  * (used if gen_dirichlet is true)
92  */
93  std::string datafilename;
94  bgeot::md_param PARAM;
95 
96  void assembly(void);
97  bool solve(void);
98  void init(void);
99  void compute_error();
100  laplacian_problem(void) : mim(mesh), mf_u(mesh), mf_rhs(mesh),
101  mf_coef(mesh) {}
102 };
103 
104 /* Read parameters from the .param file, build the mesh, set finite element
105  * and integration methods and selects the boundaries.
106  */
107 void laplacian_problem::init(void) {
108 
109  std::string MESH_TYPE = PARAM.string_value("MESH_TYPE","Mesh type ");
110  std::string FEM_TYPE = PARAM.string_value("FEM_TYPE","FEM name");
111  std::string INTEGRATION = PARAM.string_value("INTEGRATION",
112  "Name of integration method");
113 
114  cout << "MESH_TYPE=" << MESH_TYPE << "\n";
115  cout << "FEM_TYPE=" << FEM_TYPE << "\n";
116  cout << "INTEGRATION=" << INTEGRATION << "\n";
117 
118  /* First step : build the mesh */
121  N = pgt->dim();
122  std::vector<size_type> nsubdiv(N);
123  std::fill(nsubdiv.begin(),nsubdiv.end(),
124  PARAM.int_value("NX", "Nomber of space steps "));
125  getfem::regular_unit_mesh(mesh, nsubdiv, pgt,
126  PARAM.int_value("MESH_NOISED") != 0);
127 
128  bgeot::base_matrix M(N,N);
129  for (size_type i=0; i < N; ++i) {
130  static const char *t[] = {"LX","LY","LZ"};
131  M(i,i) = (i<3) ? PARAM.real_value(t[i],t[i]) : 1.0;
132  }
133  if (N>1) { M(0,1) = PARAM.real_value("INCLINE") * PARAM.real_value("LY"); }
134 
135  /* scale the unit mesh to [LX,LY,..] and incline it */
136  mesh.transformation(M);
137 
138  datafilename = PARAM.string_value("ROOTFILENAME","Base name of data files.");
139  scalar_type FT = PARAM.real_value("FT", "parameter for exact solution");
140  residual = PARAM.real_value("RESIDUAL");
141  if (residual == 0.) residual = 1e-10;
142  sol_K.resize(N);
143  for (size_type j = 0; j < N; j++)
144  sol_K[j] = ((j & 1) == 0) ? FT : -FT;
145 
146  /* set the finite element on the mf_u */
147  getfem::pfem pf_u = getfem::fem_descriptor(FEM_TYPE);
148  getfem::pintegration_method ppi = getfem::int_method_descriptor(INTEGRATION);
149 
150  mim.set_integration_method(mesh.convex_index(), ppi);
151  mf_u.set_finite_element(mesh.convex_index(), pf_u);
152 
153  /* set the finite element on mf_rhs (same as mf_u is DATA_FEM_TYPE is
154  not used in the .param file */
155  std::string data_fem_name = PARAM.string_value("DATA_FEM_TYPE");
156  if (data_fem_name.size() == 0) {
157  GMM_ASSERT1(pf_u->is_lagrange(), "You are using a non-lagrange FEM. "
158  << "In that case you need to set "
159  << "DATA_FEM_TYPE in the .param file");
160  mf_rhs.set_finite_element(mesh.convex_index(), pf_u);
161  } else {
162  mf_rhs.set_finite_element(mesh.convex_index(),
163  getfem::fem_descriptor(data_fem_name));
164  }
165 
166  /* set the finite element on mf_coef. Here we use a very simple element
167  * since the only function that need to be interpolated on the mesh_fem
168  * is f(x)=1 ... */
169  mf_coef.set_finite_element(mesh.convex_index(),
170  getfem::classical_fem(pgt,0));
171 
172  /* set boundary conditions
173  * (Neuman on the upper face, Dirichlet elsewhere) */
174  gen_dirichlet = PARAM.int_value("GENERIC_DIRICHLET");
175 
176  if (!pf_u->is_lagrange() && !gen_dirichlet)
177  GMM_WARNING2("With non lagrange fem prefer the generic "
178  "Dirichlet condition option");
179 
180  cout << "Selecting Neumann and Dirichlet boundaries\n";
181  getfem::mesh_region border_faces;
182  getfem::outer_faces_of_mesh(mesh, border_faces);
183  for (getfem::mr_visitor i(border_faces); !i.finished(); ++i) {
184  assert(i.is_face());
185  base_node un = mesh.normal_of_face_of_convex(i.cv(), i.f());
186  un /= gmm::vect_norm2(un);
187  if (gmm::abs(un[N-1] - 1.0) < 1.0E-7) { // new Neumann face
188  mesh.region(NEUMANN_BOUNDARY_NUM).add(i.cv(), i.f());
189  } else {
190  mesh.region(DIRICHLET_BOUNDARY_NUM).add(i.cv(), i.f());
191  }
192  }
193 }
194 
195 void laplacian_problem::assembly(void) {
196  size_type nb_dof = mf_u.nb_dof();
197  size_type nb_dof_rhs = mf_rhs.nb_dof();
198 
199  gmm::resize(B, nb_dof); gmm::clear(B);
200  gmm::resize(U, nb_dof); gmm::clear(U);
201  gmm::resize(SM, nb_dof, nb_dof); gmm::clear(SM);
202 
203  cout << "Number of dof : " << nb_dof << endl;
204  cout << "Assembling stiffness matrix" << endl;
205  getfem::asm_stiffness_matrix_for_laplacian(SM, mim, mf_u, mf_coef,
206  std::vector<scalar_type>(mf_coef.nb_dof(), 1.0));
207 
208  cout << "Assembling source term" << endl;
209  std::vector<scalar_type> F(nb_dof_rhs);
210  getfem::interpolation_function(mf_rhs, F, sol_f);
211  getfem::asm_source_term(B, mim, mf_u, mf_rhs, F);
212 
213  cout << "Assembling Neumann condition" << endl;
214  gmm::resize(F, nb_dof_rhs*N);
215  getfem::interpolation_function(mf_rhs, F, sol_grad);
216  getfem::asm_normal_source_term(B, mim, mf_u, mf_rhs, F,
217  NEUMANN_BOUNDARY_NUM);
218 
219  cout << "take Dirichlet condition into account" << endl;
220  if (!gen_dirichlet) {
221  std::vector<scalar_type> D(nb_dof);
222  getfem::interpolation_function(mf_u, D, sol_u);
223  getfem::assembling_Dirichlet_condition(SM, B, mf_u,
224  DIRICHLET_BOUNDARY_NUM, D);
225  } else {
226  gmm::resize(F, nb_dof_rhs);
227  getfem::interpolation_function(mf_rhs, F, sol_u);
228 
229  gmm::resize(Ud, nb_dof);
230  gmm::resize(NS, nb_dof, nb_dof);
231  col_sparse_matrix_type H(nb_dof_rhs, nb_dof);
232  std::vector<scalar_type> R(nb_dof_rhs);
233  std::vector<scalar_type> RHaux(nb_dof);
234 
235  /* build H and R such that U mush satisfy H*U = R */
236  getfem::asm_dirichlet_constraints(H, R, mim, mf_u, mf_rhs,
237  mf_rhs, F, DIRICHLET_BOUNDARY_NUM);
238 
239  gmm::clean(H, 1e-12);
240 // cout << "H = " << H << endl;
241 // cout << "R = " << R << endl;
242  int nbcols = int(getfem::Dirichlet_nullspace(H, NS, R, Ud));
243  // cout << "Number of irreductible unknowns : " << nbcols << endl;
244  gmm::resize(NS, gmm::mat_ncols(H),nbcols);
245 
246  gmm::mult(SM, Ud, gmm::scaled(B, -1.0), RHaux);
247  gmm::resize(B, nbcols);
248  gmm::resize(U, nbcols);
249  gmm::mult(gmm::transposed(NS), gmm::scaled(RHaux, -1.0), B);
250  sparse_matrix_type SMaux(nbcols, nb_dof);
251  gmm::mult(gmm::transposed(NS), SM, SMaux);
252  gmm::resize(SM, nbcols, nbcols);
253  /* NSaux = NS, but is stored by rows instead of by columns */
254  sparse_matrix_type NSaux(nb_dof, nbcols); gmm::copy(NS, NSaux);
255  gmm::mult(SMaux, NSaux, SM);
256  }
257 }
258 
259 
260 bool laplacian_problem::solve(void) {
261 
262  // see_schmidt(SM, U, B);
263 
264  cout << "Compute preconditionner\n";
265  gmm::iteration iter(residual, 1, 40000);
266  double time = gmm::uclock_sec();
267  if (1) {
268  // gmm::identity_matrix P;
269  // gmm::diagonal_precond<sparse_matrix_type> P(SM);
270  // gmm::mr_approx_inverse_precond<sparse_matrix_type> P(SM, 10, 10E-17);
271  // gmm::ildlt_precond<sparse_matrix_type> P(SM);
272  // gmm::ildltt_precond<sparse_matrix_type> P(SM, 20, 1E-6);
274  // gmm::ilutp_precond<sparse_matrix_type> P(SM, 20, 1E-6);
275  // gmm::ilu_precond<sparse_matrix_type> P(SM);
276  cout << "Time to compute preconditionner : "
277  << gmm::uclock_sec() - time << " seconds\n";
278 
279 
280  //gmm::HarwellBoeing_IO::write("SM", SM);
281 
282  // gmm::cg(SM, U, B, P, iter);
283  gmm::gmres(SM, U, B, P, 50, iter);
284  } else {
285  double rcond;
286 #if defined(GMM_USES_SUPERLU)
287  gmm::SuperLU_solve(SM, U, B, rcond);
288 #endif
289  cout << "cond = " << 1/rcond << "\n";
290  }
291 
292  cout << "Total time to solve : "
293  << gmm::uclock_sec() - time << " seconds\n";
294 
295  if (gen_dirichlet) {
296  std::vector<scalar_type> Uaux(mf_u.nb_dof());
297  gmm::mult(NS, U, Ud, Uaux);
298  gmm::resize(U, mf_u.nb_dof());
299  gmm::copy(Uaux, U);
300  }
301 
302  return (iter.converged());
303 }
304 
305 /* compute the error with respect to the exact solution */
306 void laplacian_problem::compute_error() {
307  std::vector<scalar_type> V(mf_rhs.nb_basic_dof());
308  getfem::interpolation(mf_u, mf_rhs, U, V);
309  for (size_type i = 0; i < mf_rhs.nb_basic_dof(); ++i)
310  V[i] -= sol_u(mf_rhs.point_of_basic_dof(i));
311  cout.precision(16);
312  cout << "L2 error = " << getfem::asm_L2_norm(mim, mf_rhs, V) << endl
313  << "H1 error = " << getfem::asm_H1_norm(mim, mf_rhs, V) << endl
314  << "Linfty error = " << gmm::vect_norminf(V) << endl;
315 }
316 
317 /**************************************************************************/
318 /* main program. */
319 /**************************************************************************/
320 
321 int main(int argc, char *argv[]) {
322 
323  GETFEM_MPI_INIT(argc, argv);
324  GMM_SET_EXCEPTION_DEBUG; // Exceptions make a memory fault, to debug.
325  FE_ENABLE_EXCEPT; // Enable floating point exception for Nan.
326 
327  try {
328  laplacian_problem p;
329  p.PARAM.read_command_line(argc, argv);
330  p.init();
331  p.mesh.write_to_file(p.datafilename + ".mesh");
332  p.assembly();
333  if (!p.solve()) GMM_ASSERT1(false, "Solve procedure has failed");
334  p.compute_error();
335  }
336  GMM_STANDARD_CATCH_ERROR;
337 
338  GETFEM_MPI_FINALIZE;
339 
340  return 0;
341 }
Describe a finite element method linked to a mesh.
Describe an integration method linked to a mesh.
"iterator" class for regions.
structure used to hold a set of convexes and/or convex faces.
Describe a mesh (collection of convexes (elements) and points).
Definition: getfem_mesh.h:99
Incomplete LU with threshold and K fill-in Preconditioner.
The Iteration object calculates whether the solution has reached the desired accuracy,...
Definition: gmm_iter.h:53
sparse vector built upon std::vector.
Definition: gmm_vector.h:963
Miscelleanous assembly routines for common terms. Use the low-level generic assembly....
Compute the gradient of a field on a getfem::mesh_fem.
Export solutions to various formats.
Build regular meshes.
Include common gmm files.
void copy(const L1 &l1, L2 &l2)
*‍/
Definition: gmm_blas.h:978
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norm2(const V &v)
Euclidean norm of a vector.
Definition: gmm_blas.h:558
number_traits< typename linalg_traits< V >::value_type >::magnitude_type vect_norminf(const V &v)
Infinity norm of a vector.
Definition: gmm_blas.h:694
void clear(L &l)
clear (fill with zeros) a vector or matrix.
Definition: gmm_blas.h:59
void resize(V &v, size_type n)
*‍/
Definition: gmm_blas.h:210
void clean(L &l, double threshold)
Clean a vector or matrix (replace near-zero entries with zeroes).
void mult(const L1 &l1, const L2 &l2, L3 &l3)
*‍/
Definition: gmm_blas.h:1664
strongest_value_type< V1, V2 >::value_type vect_sp(const V1 &v1, const V2 &v2)
*‍/
Definition: gmm_blas.h:264
void gmres(const Mat &A, Vec &x, const VecB &b, const Precond &M, int restart, iteration &outer, Basis &KS)
Generalized Minimum Residual.
void asm_stiffness_matrix_for_laplacian(MAT &M, const mesh_im &mim, const mesh_fem &mf, const mesh_fem &mf_data, const VECT &A, const mesh_region &rg=mesh_region::all_convexes())
assembly of , where is scalar.
size_type Dirichlet_nullspace(const MAT1 &H, MAT2 &NS, const VECT1 &R, VECT2 &U0)
Build an orthogonal basis of the kernel of H in NS, gives the solution of minimal norm of H*U = R in ...
void asm_source_term(const VECT1 &B, const mesh_im &mim, const mesh_fem &mf, const mesh_fem &mf_data, const VECT2 &F, const mesh_region &rg=mesh_region::all_convexes())
source term (for both volumic sources and boundary (Neumann) sources).
scalar_type asm_L2_norm(const mesh_im &mim, const mesh_fem &mf, const VEC &U, const mesh_region &rg=mesh_region::all_convexes())
compute , U might be real or complex
scalar_type asm_H1_norm(const mesh_im &mim, const mesh_fem &mf, const VEC &U, const mesh_region &rg=mesh_region::all_convexes())
compute the H1 norm of U.
void asm_dirichlet_constraints(MAT &H, VECT1 &R, const mesh_im &mim, const mesh_fem &mf_u, const mesh_fem &mf_mult, const mesh_fem &mf_r, const VECT2 &r_data, const mesh_region &region, int version=ASMDIR_BUILDALL)
Assembly of Dirichlet constraints in a weak form.
void asm_normal_source_term(VECT1 &B, const mesh_im &mim, const mesh_fem &mf, const mesh_fem &mf_data, const VECT2 &F, const mesh_region &rg)
Normal source term (for boundary (Neumann) condition).
void APIDECL outer_faces_of_mesh(const mesh &m, const dal::bit_vector &cvlst, convex_face_ct &flist)
returns a list of "exterior" faces of a mesh (i.e.
Definition: getfem_mesh.cc:822
std::shared_ptr< const getfem::virtual_fem > pfem
type of pointer on a fem description
Definition: getfem_fem.h:244
pfem fem_descriptor(const std::string &name)
get a fem descriptor from its string name.
Definition: getfem_fem.cc:4660
pfem classical_fem(bgeot::pgeometric_trans pgt, short_type k, bool complete=false)
Give a pointer on the structures describing the classical polynomial fem of degree k on a given conve...
Definition: getfem_fem.cc:4567
pgeometric_trans geometric_trans_descriptor(std::string name)
Get the geometric transformation from its string name.
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:49
std::shared_ptr< const bgeot::geometric_trans > pgeometric_trans
pointer type for a geometric transformation
void interpolation_function(mesh_fem &mf_target, const VECT &VV, F &f, mesh_region rg=mesh_region::all_convexes())
interpolation of a function f on mf_target.
void regular_unit_mesh(mesh &m, std::vector< size_type > nsubdiv, bgeot::pgeometric_trans pgt, bool noised=false)
Build a regular mesh of the unit square/cube/, etc.
void interpolation(const mesh_fem &mf_source, const mesh_fem &mf_target, const VECTU &U, VECTV &V, int extrapolation=0, double EPS=1E-10, mesh_region rg_source=mesh_region::all_convexes(), mesh_region rg_target=mesh_region::all_convexes())
interpolation/extrapolation of (mf_source, U) on mf_target.
pintegration_method int_method_descriptor(std::string name, bool throw_if_not_found=true)
Get an integration method from its name .