two_d_poisson_compare_solvers.cc
Go to the documentation of this file.
1//LIC// ====================================================================
2//LIC// This file forms part of oomph-lib, the object-oriented,
3//LIC// multi-physics finite-element library, available
4//LIC// at http://www.oomph-lib.org.
5//LIC//
6//LIC// Copyright (C) 2006-2025 Matthias Heil and Andrew Hazel
7//LIC//
8//LIC// This library is free software; you can redistribute it and/or
9//LIC// modify it under the terms of the GNU Lesser General Public
10//LIC// License as published by the Free Software Foundation; either
11//LIC// version 2.1 of the License, or (at your option) any later version.
12//LIC//
13//LIC// This library is distributed in the hope that it will be useful,
14//LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15//LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16//LIC// Lesser General Public License for more details.
17//LIC//
18//LIC// You should have received a copy of the GNU Lesser General Public
19//LIC// License along with this library; if not, write to the Free Software
20//LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21//LIC// 02110-1301 USA.
22//LIC//
23//LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24//LIC//
25//LIC//====================================================================
26//Driver for a simple 2D poisson problem -- compare various linear solvers
27
28//Generic routines
29#include "generic.h"
30
31// The Poisson equations
32#include "poisson.h"
33
34// The mesh
35#include "meshes/simple_rectangular_quadmesh.h"
36
37using namespace std;
38
39using namespace oomph;
40
41//===== start_of_namespace=============================================
42/// Namespace for exact solution for Poisson equation with "sharp step"
43//=====================================================================
44namespace TanhSolnForPoisson
45{
46
47 /// Parameter for steepness of "step"
48 double Alpha=1.0;
49
50 /// Parameter for angle Phi of "step"
51 double TanPhi=0.0;
52
53 /// Exact solution as a Vector
54 void get_exact_u(const Vector<double>& x, Vector<double>& u)
55 {
56 u[0]=tanh(1.0-Alpha*(TanPhi*x[0]-x[1]));
57 }
58
59 /// Source function required to make the solution above an exact solution
60 void get_source(const Vector<double>& x, double& source)
61 {
62 source = 2.0*tanh(-1.0+Alpha*(TanPhi*x[0]-x[1]))*
63 (1.0-pow(tanh(-1.0+Alpha*(TanPhi*x[0]-x[1])),2.0))*
64 Alpha*Alpha*TanPhi*TanPhi+2.0*tanh(-1.0+Alpha*(TanPhi*x[0]-x[1]))*
65 (1.0-pow(tanh(-1.0+Alpha*(TanPhi*x[0]-x[1])),2.0))*Alpha*Alpha;
66 }
67
68} // end of namespace
69
70
71
72
73
74
75
76
77//====== start_of_problem_class=======================================
78/// 2D Poisson problem on rectangular domain, discretised with
79/// 2D QPoisson elements. The specific type of element is
80/// specified via the template parameter.
81//====================================================================
82template<class ELEMENT>
83class PoissonProblem : public Problem
84{
85
86public:
87
88 /// Constructor: Pass pointer to source function and flag to indicate
89 /// if order of elements should be messed up. nel_1d is the number of
90 /// elements along the 1d mesh edges -- total number of elements is
91 /// nel_1d x nel_1d.
92 PoissonProblem(PoissonEquations<2>::PoissonSourceFctPt source_fct_pt,
93 const unsigned& nel_1d,
94 const bool& mess_up_order);
95
96 /// Destructor (empty)
98
99 /// Update the problem specs before solve: Reset boundary conditions
100 /// to the values from the exact solution.
102
103 /// Update the problem after solve (empty)
105
106 /// Doc the solution. DocInfo object stores flags/labels for where the
107 /// output gets written to
108 void doc_solution(DocInfo& doc_info);
109
110private:
111
112 /// Pointer to source function
113 PoissonEquations<2>::PoissonSourceFctPt Source_fct_pt;
114
115}; // end of problem class
116
117
118
119
120//=====start_of_constructor===============================================
121/// Constructor for Poisson problem: Pass pointer to source function
122/// and a flag that specifies if the order of the elements should
123/// be messed up. nel_1d is the number of elements along
124/// the 1d mesh edges -- total number of elements is nel_1d x nel_1d.
125//========================================================================
126template<class ELEMENT>
128PoissonProblem(PoissonEquations<2>::PoissonSourceFctPt source_fct_pt,
129 const unsigned& nel_1d, const bool& mess_up_order)
130 : Source_fct_pt(source_fct_pt)
131{
132
133 // Setup mesh
134
135 // # of elements in x-direction
136 unsigned n_x=nel_1d;
137
138 // # of elements in y-direction
139 unsigned n_y=nel_1d;
140
141 // Domain length in x-direction
142 double l_x=1.0;
143
144 // Domain length in y-direction
145 double l_y=2.0;
146
147 // Build and assign mesh
148 Problem::mesh_pt() = new SimpleRectangularQuadMesh<ELEMENT>(n_x,n_y,l_x,l_y);
149
150
151
152 // Mess up order of elements in the Problem's mesh:
153 //-------------------------------------------------
154 if (mess_up_order)
155 {
156 unsigned n_element=mesh_pt()->nelement();
157
158 // Make copy
159 Vector<ELEMENT*> tmp_element_pt(n_element);
160 for (unsigned e=0;e<n_element;e++)
161 {
162 tmp_element_pt[e]=dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(e));
163 }
164
165 // Reorder
166 unsigned e_half=unsigned(0.5*double(n_element));
167 unsigned e_lo=e_half-3;
168 unsigned e_up=n_element-e_lo;
169 unsigned count=0;
170 for (unsigned e=0;e<e_lo;e++)
171 {
172 mesh_pt()->element_pt(count)=tmp_element_pt[e];
173 count++;
174 mesh_pt()->element_pt(count)=tmp_element_pt[n_element-e-1];
175 count++;
176 }
177 for (unsigned e=e_lo;e<e_up;e++)
178 {
179 mesh_pt()->element_pt(count)=tmp_element_pt[e];
180 count++;
181 }
182 }
183
184 // Set the boundary conditions for this problem: All nodes are
185 // free by default -- only need to pin the ones that have Dirichlet conditions
186 // here.
187 unsigned num_bound = mesh_pt()->nboundary();
188 for(unsigned ibound=0;ibound<num_bound;ibound++)
189 {
190 unsigned num_nod= mesh_pt()->nboundary_node(ibound);
191 for (unsigned inod=0;inod<num_nod;inod++)
192 {
193 mesh_pt()->boundary_node_pt(ibound,inod)->pin(0);
194 }
195 }
196
197 // Complete the build of all elements so they are fully functional
198
199 // Loop over the elements to set up element-specific
200 // things that cannot be handled by the (argument-free!) ELEMENT
201 // constructor: Pass pointer to source function
202 unsigned n_element = mesh_pt()->nelement();
203 for(unsigned i=0;i<n_element;i++)
204 {
205 // Upcast from GeneralsedElement to the present element
206 ELEMENT *el_pt = dynamic_cast<ELEMENT*>(mesh_pt()->element_pt(i));
207
208 //Set the source function pointer
209 el_pt->source_fct_pt() = Source_fct_pt;
210 }
211
212
213 // Setup equation numbering scheme
214 cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
215
216} // end of constructor
217
218
219
220
221//========================================start_of_actions_before_newton_solve===
222/// Update the problem specs before solve: (Re-)set boundary conditions
223/// to the values from the exact solution.
224//========================================================================
225template<class ELEMENT>
227{
228 // How many boundaries are there?
229 unsigned num_bound = mesh_pt()->nboundary();
230
231 //Loop over the boundaries
232 for(unsigned ibound=0;ibound<num_bound;ibound++)
233 {
234 // How many nodes are there on this boundary?
235 unsigned num_nod=mesh_pt()->nboundary_node(ibound);
236
237 // Loop over the nodes on boundary
238 for (unsigned inod=0;inod<num_nod;inod++)
239 {
240 // Get pointer to node
241 Node* nod_pt=mesh_pt()->boundary_node_pt(ibound,inod);
242
243 // Extract nodal coordinates from node:
244 Vector<double> x(2);
245 x[0]=nod_pt->x(0);
246 x[1]=nod_pt->x(1);
247
248 // Compute the value of the exact solution at the nodal point
249 Vector<double> u(1);
251
252 // Assign the value to the one (and only) nodal value at this node
253 nod_pt->set_value(0,u[0]);
254 }
255 }
256} // end of actions before solve
257
258
259
260//===============start_of_doc=============================================
261/// Doc the solution: doc_info contains labels/output directory etc.
262//========================================================================
263template<class ELEMENT>
264void PoissonProblem<ELEMENT>::doc_solution(DocInfo& doc_info)
265{
266
267 ofstream some_file;
268 char filename[100];
269
270 // Number of plot points: npts x npts
271 unsigned npts=5;
272
273 // Output solution
274 //-----------------
275 snprintf(filename, sizeof(filename), "%s/soln%i.dat",doc_info.directory().c_str(),
276 doc_info.number());
277 some_file.open(filename);
278 mesh_pt()->output(some_file,npts);
279 some_file.close();
280
281
282 // Output exact solution
283 //----------------------
284 snprintf(filename, sizeof(filename), "%s/exact_soln%i.dat",doc_info.directory().c_str(),
285 doc_info.number());
286 some_file.open(filename);
287 mesh_pt()->output_fct(some_file,npts,TanhSolnForPoisson::get_exact_u);
288 some_file.close();
289
290 // Doc error and return of the square of the L2 error
291 //---------------------------------------------------
292 double error,norm;
293 snprintf(filename, sizeof(filename), "%s/error%i.dat",doc_info.directory().c_str(),
294 doc_info.number());
295 some_file.open(filename);
296 mesh_pt()->compute_error(some_file,TanhSolnForPoisson::get_exact_u,
297 error,norm);
298 some_file.close();
299
300 // Doc L2 error and norm of solution
301 cout << "\nNorm of error : " << sqrt(error) << std::endl;
302 cout << "Norm of solution: " << sqrt(norm) << std::endl << std::endl;
303
304} // end of doc
305
306
307
308
309
310
311//===== start_of_run======================================================
312/// Build and run problem with specified linear solver. Also pass
313/// flag to specify if order of elements should be messed up.
314/// nel_1d is the number of elements along the 1D mesh edge.
315/// Total number of elements is nel_1d x nel_1d.
316//========================================================================
317void run(const string& dir_name,
318 LinearSolver* linear_solver_pt,
319 const unsigned nel_1d,
320 bool mess_up_order)
321{
322
323 //Set up the problem
324 //------------------
325
326 // Create the problem with 2D nine-node elements from the
327 // QPoissonElement family. Pass pointer to source function
328 // and flag to specify if order of elements should be messed up.
330 problem(&TanhSolnForPoisson::get_source,nel_1d,mess_up_order);
331
332
333 /// Set linear solver:
334 //--------------------
335 problem.linear_solver_pt() = linear_solver_pt;
336
337
338 // Create label for output
339 //------------------------
340 DocInfo doc_info;
341
342 // Set output directory
343 doc_info.set_directory(dir_name);
344
345 // Step number
346 doc_info.number()=0;
347
348 // Check if we're ready to go:
349 //----------------------------
350 cout << "\n\n\nProblem self-test:";
351 if (problem.self_test()==0)
352 {
353 cout << "passed: Problem can be solved." << std::endl;
354 }
355 else
356 {
357 throw OomphLibError("Self test failed",
358 OOMPH_CURRENT_FUNCTION,
359 OOMPH_EXCEPTION_LOCATION);
360 }
361
362
363 // Set the orientation of the "step" to 45 degrees
365
366 // Initial value for the steepness of the "step"
368
369 // Solve the problem
370 problem.newton_solve();
371
372 //Output the solution
373 problem.doc_solution(doc_info);
374
375
376} //end of run
377
378
379
380
381
382//===== start_of_main=====================================================
383/// Driver code for 2D Poisson problem -- compare different solvers
384//========================================================================
385int main()
386{
387
388 // Pointer to linear solver
389 LinearSolver* linear_solver_pt;
390
391 // Result directory
392 string dir_name;
393
394 // Cpu start/end times
395 clock_t cpu_start,cpu_end;
396
397 // Storage for cpu times with and without messed up order
398 Vector<double> superlu_cr_cpu(2);
399 Vector<double> superlu_cc_cpu(2);
400 Vector<double> hsl_ma42_cpu(2);
401 Vector<double> hsl_ma42_reordered_cpu(2);
402 Vector<double> dense_lu_cpu(2);
403 Vector<double> fd_lu_cpu(2);
404
405 // Flag to indicate if order should be messed up:
406 bool mess_up_order;
407
408 // Number of elements along 1D edge of mesh; total number of elements
409 // is nel_1d x nel_1d
410 unsigned nel_1d;
411
412 // Do run with and without messed up elements
413 for (unsigned mess=0;mess<2;mess++)
414 {
415
416 // Mess up order?
417 if (mess==0)
418 {
419 mess_up_order=true;
420 }
421 else
422 {
423 mess_up_order=false;
424 }
425
426 /// Use SuperLU with compressed row storage
427 //-----------------------------------------
428
429 cout << std::endl;
430 cout << " Use SuperLU with compressed row storage: " << std::endl;
431 cout << "========================================= " << std::endl;
432 cout << std::endl;
433
434 // Start cpu clock
435 cpu_start=clock();
436
437 // Build linear solver
438 linear_solver_pt = new SuperLUSolver;
439
440 /// Use compressed row storage
441 static_cast<SuperLUSolver*>(linear_solver_pt)
442 ->use_compressed_row_for_superlu_serial();
443
444 /// Switch on full doc
445 static_cast<SuperLUSolver*>(linear_solver_pt)->enable_doc_stats();
446
447 // Choose result directory
448 dir_name="RESLT_cr";
449
450 // Number of elements along 1D edge of mesh; total number of elements
451 // is nel_1d x nel_1d
452 nel_1d=20;
453
454 // Run it
455 run(dir_name,linear_solver_pt,nel_1d,mess_up_order);
456
457 // Note: solver does not have to be deleted -- it's killed automatically
458 // in the problem destructor.
459
460 // End cpu clock
461 cpu_end=clock();
462
463 // Timing
464 superlu_cr_cpu[mess]=double(cpu_end-cpu_start)/CLOCKS_PER_SEC;
465
466
467
468
469 /// Use SuperLU with compressed column storage
470 //--------------------------------------------
471
472
473 cout << std::endl;
474 cout << " Use SuperLU with compressed column storage: " << std::endl;
475 cout << "============================================ " << std::endl;
476 cout << std::endl;
477
478
479 // Start cpu clock
480 cpu_start=clock();
481
482 // Build linear solver
483 linear_solver_pt = new SuperLUSolver;
484
485 /// Use compressed row storage
486 static_cast<SuperLUSolver*>(linear_solver_pt)
487 ->use_compressed_column_for_superlu_serial();
488
489 /// Switch on full doc
490 static_cast<SuperLUSolver*>(linear_solver_pt)->enable_doc_stats();
491
492 // Choose result directory
493 dir_name="RESLT_cc";
494
495 // Number of elements along 1D edge of mesh; total number of elements
496 // is nel_1d x nel_1d
497 nel_1d=20;
498
499 // Run it
500 run(dir_name,linear_solver_pt,nel_1d,mess_up_order);
501
502 // Note: solver does not have to be deleted -- it's killed automatically
503 // in the problem destructor.
504
505 // End cpu clock
506 cpu_end=clock();
507
508 // Timing
509 superlu_cc_cpu[mess]=double(cpu_end-cpu_start)/CLOCKS_PER_SEC;
510
511
512#ifdef HAVE_HSL_SOURCES
513
514 /// Use HSL frontal solver MA42 without element re-ordering
515 //---------------------------------------------------------
516
517 cout << std::endl;
518 cout << " Use HSL frontal solver MA42 without element re-ordering: " << std::endl;
519 cout << "========================================================= " << std::endl;
520 cout << std::endl;
521
522 // Start cpu clock
523 cpu_start=clock();
524
525 // Build linear solver
526 linear_solver_pt = new HSL_MA42;
527
528 /// Switch on full doc
529 static_cast<HSL_MA42*>(linear_solver_pt)->enable_doc_stats();
530
531 // Choose result directory
532 dir_name="RESLT_frontal";
533
534
535 // Number of elements along 1D edge of mesh; total number of elements
536 // is nel_1d x nel_1d
537 nel_1d=20;
538
539 // Run it
540 run(dir_name,linear_solver_pt,nel_1d,mess_up_order);
541
542 // Note: solver does not have to be deleted -- it's killed automatically
543 // in the problem destructor.
544
545
546 // End cpu clock
547 cpu_end=clock();
548
549 // Timing
550 hsl_ma42_cpu[mess]=double(cpu_end-cpu_start)/CLOCKS_PER_SEC;
551
552
553
554 /// Use HSL frontal solver MA42 with element reordering by Sloan's algorithm
555 //--------------------------------------------------------------------------
556
557 cout << std::endl;
558 cout << " Use HSL frontal solver MA42 with element re-ordering: " << std::endl;
559 cout << "====================================================== " << std::endl;
560 cout << std::endl;
561
562 // Start cpu clock
563 cpu_start=clock();
564
565 // Build linear solver
566 linear_solver_pt = new HSL_MA42;
567
568 /// Switch on full doc
569 static_cast<HSL_MA42*>(linear_solver_pt)->enable_doc_stats();
570
571
572 /// Switch on re-ordering
573 static_cast<HSL_MA42*>(linear_solver_pt)->enable_reordering();
574
575 // Choose result directory
576 dir_name="RESLT_frontal_reordered";
577
578
579 // Number of elements along 1D edge of mesh; total number of elements
580 // is nel_1d x nel_1d
581 nel_1d=20;
582
583 // Run it
584 run(dir_name,linear_solver_pt,nel_1d,mess_up_order);
585
586 // Note: solver does not have to be deleted -- it's killed automatically
587 // in the problem destructor.
588
589
590 // End cpu clock
591 cpu_end=clock();
592
593 // Timing
594 hsl_ma42_reordered_cpu[mess]=double(cpu_end-cpu_start)/CLOCKS_PER_SEC;
595
596
597#endif
598
599 /// Use dense matrix LU decomposition
600 //-----------------------------------
601
602 cout << std::endl;
603 cout << " Use dense matrix LU decomposition: " << std::endl;
604 cout << "=================================== " << std::endl;
605 cout << std::endl;
606
607 // Start cpu clock
608 cpu_start=clock();
609
610 // Build linear solver
611 linear_solver_pt = new DenseLU;
612
613 // Choose result directory
614 dir_name="RESLT_dense_LU";
615
616 // Number of elements along 1D edge of mesh; total number of elements
617 // is nel_1d x nel_1d
618 nel_1d=4;
619
620 // Run it
621 run(dir_name,linear_solver_pt,nel_1d,mess_up_order);
622
623 // Note: solver does not have to be deleted -- it's killed automatically
624 // in the problem destructor.
625
626
627 // End cpu clock
628 cpu_end=clock();
629
630 // Timing
631 dense_lu_cpu[mess]=double(cpu_end-cpu_start)/CLOCKS_PER_SEC;
632
633
634
635
636 /// Use dense matrix LU decomposition
637 //-----------------------------------
638
639 cout << std::endl;
640 cout << " Use dense FD-ed matrix LU decomposition: " << std::endl;
641 cout << "========================================= " << std::endl;
642 cout << std::endl;
643
644 // Start cpu clock
645 cpu_start=clock();
646
647 // Build linear solver
648 linear_solver_pt = new FD_LU;
649
650 // Choose result directory
651 dir_name="RESLT_FD_LU";
652
653 // Number of elements along 1D edge of mesh; total number of elements
654 // is nel_1d x nel_1d
655 nel_1d=4;
656
657 // Run it
658 run(dir_name,linear_solver_pt,nel_1d,mess_up_order);
659
660 // Note: solver does not have to be deleted -- it's killed automatically
661 // in the problem destructor.
662
663 // End cpu clock
664 cpu_end=clock();
665
666 // Timing
667 fd_lu_cpu[mess]=double(cpu_end-cpu_start)/CLOCKS_PER_SEC;
668
669 }
670
671
672 // Doc timings with and without messed up elements
673 for (unsigned mess=0;mess<2;mess++)
674 {
675 cout << std::endl << std::endl << std::endl ;
676 if (mess==0)
677 {
678 cout << "TIMINGS WITH MESSED UP ORDERING OF ELEMENTS: " << std::endl;
679 cout << "============================================ " << std::endl;
680
681 }
682 else
683 {
684 cout << "TIMINGS WITHOUT MESSED UP ORDERING OF ELEMENTS: " << std::endl;
685 cout << "=============================================== " << std::endl;
686 }
687
688 cout << "CPU time with SuperLU compressed row : "
689 << superlu_cr_cpu[mess] << std::endl;
690 cout << "CPU time with SuperLU compressed col : "
691 << superlu_cc_cpu[mess] << std::endl;
692#ifdef HAVE_HSL_SOURCES
693 cout << "CPU time with MA42 frontal solver : "
694 << hsl_ma42_cpu[mess] << std::endl;
695 cout << "CPU time with MA42 frontal solver (incl. reordering) : "
696 << hsl_ma42_reordered_cpu[mess] << std::endl;
697#endif
698 cout << "CPU time with dense LU solver (fewer elements!) : "
699 << dense_lu_cpu[mess] << std::endl;
700 cout << "CPU time with dense LU solver & FD (fewer elements!) : "
701 << fd_lu_cpu[mess] << std::endl;
702 cout << std::endl << std::endl << std::endl ;
703 }
704
705
706
707
708} //end of main
709
710
711
712
713
2D Poisson problem on rectangular domain, discretised with 2D QPoisson elements. The specific type of...
PoissonEquations< 2 >::PoissonSourceFctPt Source_fct_pt
Pointer to source function.
void actions_before_newton_solve()
Update the problem specs before solve: Reset boundary conditions to the values from the exact solutio...
void actions_after_newton_solve()
Update the problem after solve (empty)
PoissonProblem(PoissonEquations< 2 >::PoissonSourceFctPt source_fct_pt)
Constructor: Pass pointer to source function.
void doc_solution(DocInfo &doc_info)
Doc the solution. DocInfo object stores flags/labels for where the output gets written to.
~PoissonProblem()
Destructor (empty)
Namespace for exact solution for Poisson equation with "sharp step".
double TanPhi
Parameter for angle Phi of "step".
void get_source(const Vector< double > &x, double &source)
Source function required to make the solution above an exact solution.
double Alpha
Parameter for steepness of "step".
void get_exact_u(const Vector< double > &x, Vector< double > &u)
Exact solution as a Vector.
void run(const string &dir_name, LinearSolver *linear_solver_pt, const unsigned nel_1d, bool mess_up_order)
Build and run problem with specified linear solver. Also pass flag to specify if order of elements sh...
int main()
Driver code for 2D Poisson problem – compare different solvers.