linear_solver.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// The actual solve functions for dense LU linear solvers.
27
28// Config header
29#ifdef HAVE_CONFIG_H
30#include <oomph-lib-config.h>
31#endif
32
33#ifdef OOMPH_HAS_MPI
34#include "mpi.h"
35#endif
36
37// oomph-lib includes
38#include "Vector.h"
39#include "linear_solver.h"
40#include "matrices.h"
41#include "problem.h"
42#ifdef OOMPH_HAS_MUMPS
43#include "mumps_solver.h"
44#endif
46
47
48namespace oomph
49{
50 //=============================================================================
51 /// Solver: Takes pointer to problem and returns the results Vector
52 /// which contains the solution of the linear system defined by
53 /// the problem's fully assembled Jacobian and residual Vector.
54 //=============================================================================
55 void DenseLU::solve(Problem* const& problem_pt, DoubleVector& result)
56 {
57 // Initialise timer
59
60 // Find # of degrees of freedom (variables)
61 const unsigned n_dof = problem_pt->ndof();
62
63 // Allocate storage for the residuals vector and the jacobian matrix
65 DenseDoubleMatrix jacobian(n_dof);
66
67 // initialise timer
69
70 // Get the full jacobian and residuals of the problem
71 problem_pt->get_jacobian(residuals, jacobian);
72
73 // compute jacobian setup time
76
77 // Report the time
78 if (Doc_time)
79 {
80 oomph_info << std::endl
81 << "CPU for setup of Dense Jacobian: "
84 << std::endl;
85 }
86
87 // Solve by dense LU decomposition VERY INEFFICIENT!
88 solve(&jacobian, residuals, result);
89
90 // Set the sign of the determinant of the jacobian
92
93 // Finalise/doc timings
94 double t_end = TimingHelpers::timer();
95 double total_time = t_end - t_start;
96 if (Doc_time)
97 {
98 oomph_info << "CPU for DenseLU LinearSolver: "
100 << std::endl
101 << std::endl;
102 }
103 }
104
105
106 //=============================================================================
107 /// Delete the storage that has been allocated for the LU factors, if
108 /// the matrix data is not itself being overwritten.
109 //=============================================================================
111 {
112 // delete the Distribution_pt
113 this->clear_distribution();
114
115 // Clean up the LU factor storage, if it has been allocated
116 // N.B. we don't need to check the index storage as well.
117 if (LU_factors != 0)
118 {
119 // Delete the pointer to the LU factors
120 delete[] LU_factors;
121 // Null out the vector
122 LU_factors = 0;
123 // Delete the pointer to the Index
124 delete[] Index;
125 // Null out
126 Index = 0;
127 }
128 }
129
130 //=============================================================================
131 /// LU decompose the matrix.
132 /// WARNING: this class does not perform any PARANOID checks on the vectors -
133 /// these are all performed in the solve(...) method.
134 //=============================================================================
135 void DenseLU::factorise(DoubleMatrixBase* const& matrix_pt)
136 {
137 // Set the number of unknowns
138 const unsigned long n = matrix_pt->nrow();
139
140 // Small constant
141 const double small_number = 1.0e-20;
142
143 // Vector scaling stores the implicit scaling of each row
144 Vector<double> scaling(n);
145
146 // Integer to store the sign that must multiply the determinant as
147 // a consequence of the row/column interchanges
148 int signature = 1;
149
150 // Loop over rows to get implicit scaling information
151 for (unsigned long i = 0; i < n; i++)
152 {
153 double largest_entry = 0.0;
154 for (unsigned long j = 0; j < n; j++)
155 {
156 double tmp = std::fabs((*matrix_pt)(i, j));
158 }
159 if (largest_entry == 0.0)
160 {
161 throw OomphLibError(
163 }
164 // Save the scaling
165 scaling[i] = 1.0 / largest_entry;
166 }
167
168 // Firsly, we shall delete any previous LU storage.
169 // If the user calls this function twice without changing the matrix
170 // then it is their own inefficiency, not ours (this time).
172
173 // Allocate storage for the LU factors, the index and store
174 // the number of unknowns
175 LU_factors = new double[n * n];
176 Index = new long[n];
177
178 // Now we know that memory has been allocated, copy over
179 // the matrix values
180 unsigned count = 0;
181 for (unsigned long i = 0; i < n; i++)
182 {
183 for (unsigned long j = 0; j < n; j++)
184 {
185 LU_factors[count] = (*matrix_pt)(i, j);
186 ++count;
187 }
188 }
189
190 // Loop over columns
191 for (unsigned long j = 0; j < n; j++)
192 {
193 // Initialise imax
194 unsigned long imax = 0;
195
196 for (unsigned long i = 0; i < j; i++)
197 {
198 double sum = LU_factors[n * i + j];
199 for (unsigned long k = 0; k < i; k++)
200 {
201 sum -= LU_factors[n * i + k] * LU_factors[n * k + j];
202 }
203 LU_factors[n * i + j] = sum;
204 }
205
206 // Initialise search for largest pivot element
207 double largest_entry = 0.0;
208 for (unsigned long i = j; i < n; i++)
209 {
210 double sum = LU_factors[n * i + j];
211 for (unsigned long k = 0; k < j; k++)
212 {
213 sum -= LU_factors[n * i + k] * LU_factors[n * k + j];
214 }
215 LU_factors[n * i + j] = sum;
216 // Set temporary
217 double tmp = scaling[i] * std::fabs(sum);
218 if (tmp >= largest_entry)
219 {
221 imax = i;
222 }
223 }
224
225 // Test to see if we need to interchange rows
226 if (j != imax)
227 {
228 for (unsigned long k = 0; k < n; k++)
229 {
230 double tmp = LU_factors[n * imax + k];
231 LU_factors[n * imax + k] = LU_factors[n * j + k];
232 LU_factors[n * j + k] = tmp;
233 }
234 // Change the parity of signature
236
237 // Interchange scale factor
238 scaling[imax] = scaling[j];
239 }
240
241 // Set the index
242 Index[j] = imax;
243 if (LU_factors[n * j + j] == 0.0)
244 {
245 LU_factors[n * j + j] = small_number;
246 }
247 // Divide by pivot element
248 if (j != n - 1)
249 {
250 double tmp = 1.0 / LU_factors[n * j + j];
251 for (unsigned long i = j + 1; i < n; i++)
252 {
253 LU_factors[n * i + j] *= tmp;
254 }
255 }
256
257 } // End of loop over columns
258
259
260 // Now multiply all the diagonal terms together to get the determinant
261 // Note that we need to use the mantissa, exponent formulation to
262 // avoid underflow errors
263 double determinant_mantissa = 1.0;
264 int determinant_exponent = 0, iexp;
265 for (unsigned i = 0; i < n; i++)
266 {
267 // Multiply by the next diagonal entry's mantissa
268 // and return the exponent
270
271 // Add the new exponent to the current exponent
273
274 // normalise
277 }
278
279 // If paranoid issue a warning that the matrix is near singular
280 // #ifdef PARANOID
281 // int tiny_exponent = -60;
282 // if(determinant_exponent < tiny_exponent)
283 // {
284 // std::ostringstream warning_stream;
285 // warning_stream << "The determinant of the matrix is very close to
286 // zero.\n"
287 // << "It is " << determinant_mantissa << " x 2^"
288 // << determinant_exponent << "\n";
289 // warning_stream << "The results will depend on the exact details of
290 // the\n"
291 // << "floating point implementation ... just to let you
292 // know\n";
293 // OomphLibWarning(warning_stream.str(),
294 // "DenseLU::factorise()",
295 // OOMPH_EXCEPTION_LOCATION);
296 // }
297 // #endif
298
299 // Integer to store the sign of the determinant
300 int sign = 0;
301
302 // Find the sign of the determinant
303 if (determinant_mantissa > 0.0)
304 {
305 sign = 1;
306 }
307 if (determinant_mantissa < 0.0)
308 {
309 sign = -1;
310 }
311
312 // Multiply the sign by the signature
313 sign *= signature;
314
315 // Return the sign of the determinant
317 }
318
319 //=============================================================================
320 /// Do the backsubstitution for the DenseLU solver.
321 /// WARNING: this class does not perform any PARANOID checks on the vectors -
322 /// these are all performed in the solve(...) method.
323 //=============================================================================
325 {
326 // Get pointers to first entries
327 const double* rhs_pt = rhs.values_pt();
328 double* result_pt = result.values_pt();
329
330 // Copy the rhs vector into the result vector
331 const unsigned long n = rhs.nrow();
332 for (unsigned long i = 0; i < n; ++i)
333 {
334 result_pt[i] = rhs_pt[i];
335 }
336
337 // Loop over all rows for forward substition
338 unsigned long k = 0;
339 for (unsigned long i = 0; i < n; i++)
340 {
341 unsigned long ip = Index[i];
342 double sum = result_pt[ip];
344 if (k != 0)
345 {
346 for (unsigned long j = k - 1; j < i; j++)
347 {
348 sum -= LU_factors[n * i + j] * result_pt[j];
349 }
350 }
351 else if (sum != 0.0)
352 {
353 k = i + 1;
354 }
355 result_pt[i] = sum;
356 }
357
358 // Now do the back substitution
359 for (long i = long(n) - 1; i >= 0; i--)
360 {
361 double sum = result_pt[i];
362 for (long j = i + 1; j < long(n); j++)
363 {
364 sum -= LU_factors[n * i + j] * result_pt[j];
365 }
366 result_pt[i] = sum / LU_factors[n * i + i];
367 }
368 }
369
370 //=============================================================================
371 /// Do the backsubstitution for the DenseLU solver.
372 /// WARNING: this class does not perform any PARANOID checks on the vectors -
373 /// these are all performed in the solve(...) method. So, if you call backsub
374 /// directly, you have been warned...
375 //=============================================================================
377 {
378 // Copy the rhs vector into the result vector
379 const unsigned long n = rhs.size();
380 for (unsigned long i = 0; i < n; ++i)
381 {
382 result[i] = rhs[i];
383 }
384
385 // Loop over all rows for forward substition
386 unsigned long k = 0;
387 for (unsigned long i = 0; i < n; i++)
388 {
389 unsigned long ip = Index[i];
390 double sum = result[ip];
391 result[ip] = result[i];
392 if (k != 0)
393 {
394 for (unsigned long j = k - 1; j < i; j++)
395 {
396 sum -= LU_factors[n * i + j] * result[j];
397 }
398 }
399 else if (sum != 0.0)
400 {
401 k = i + 1;
402 }
403 result[i] = sum;
404 }
405
406 // Now do the back substitution
407 for (long i = long(n) - 1; i >= 0; i--)
408 {
409 double sum = result[i];
410 for (long j = i + 1; j < long(n); j++)
411 {
412 sum -= LU_factors[n * i + j] * result[j];
413 }
414 result[i] = sum / LU_factors[n * i + i];
415 }
416 }
417
418
419 //=============================================================================
420 /// Linear-algebra-type solver: Takes pointer to a matrix and rhs
421 /// vector and returns the solution of the linear system.
422 //============================================================================
423 void DenseLU::solve(DoubleMatrixBase* const& matrix_pt,
424 const DoubleVector& rhs,
426 {
427#ifdef PARANOID
428 // check that the rhs vector is not distributed
429 if (rhs.distribution_pt()->distributed())
430 {
431 std::ostringstream error_message_stream;
433 << "The vectors rhs and result must not be distributed";
437 }
438
439 // check that the matrix is square
440 if (matrix_pt->nrow() != matrix_pt->ncol())
441 {
442 std::ostringstream error_message_stream;
443 error_message_stream << "The matrix at matrix_pt must be square.";
447 }
448 // check that the matrix and the rhs vector have the same nrow()
449 if (matrix_pt->nrow() != rhs.nrow())
450 {
451 std::ostringstream error_message_stream;
453 << "The matrix and the rhs vector must have the same number of rows.";
457 }
458
459 // if the matrix is distributable then it too should have the same
460 // communicator as the rhs vector and should not be distributed
462 dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt);
463 if (dist_matrix_pt != 0)
464 {
465 if (dist_matrix_pt->distribution_pt()->communicator_pt()->nproc() > 1 &&
466 dist_matrix_pt->distribution_pt()->distributed() == true)
467 {
468 throw OomphLibError(
469 "Matrix must not be distributed or only one processor",
472 }
473 OomphCommunicator temp_comm(*rhs.distribution_pt()->communicator_pt());
474 if (!(temp_comm == *dist_matrix_pt->distribution_pt()->communicator_pt()))
475 {
476 std::ostringstream error_message_stream;
478 << "The matrix matrix_pt must have the same communicator as the "
479 "vectors"
480 << " rhs and result must have the same communicator";
484 }
485 }
486 // if the result vector is setup then check it is not distributed and has
487 // the same communicator as the rhs vector
488 if (result.distribution_built())
489 {
490 if (!(*result.distribution_pt() == *rhs.distribution_pt()))
491 {
492 std::ostringstream error_message_stream;
494 << "The result vector distribution has been setup; it must have the "
495 << "same distribution as the rhs vector.";
499 }
500 }
501#endif
502
503 if (!result.distribution_built())
504 {
505 result.build(rhs.distribution_pt(), 0.0);
506 }
507
508 // set the distribution
510
511 // Time the solver
512 double t_start = TimingHelpers::timer();
513
514 // factorise
515 factorise(matrix_pt);
516
517 // backsubstitute
519
520 // Doc time for solver
521 double t_end = TimingHelpers::timer();
522
524 if (Doc_time)
525 {
526 oomph_info << std::endl
527 << "CPU for solve with DenseLU : "
530 << std::endl
531 << std::endl;
532 }
533
534 // If we are not resolving then delete storage
535 if (!Enable_resolve)
536 {
538 }
539 }
540
541 //=============================================================================
542 /// Linear-algebra-type solver: Takes pointer to a matrix and rhs
543 /// vector and returns the solution of the linear system.
544 //=============================================================================
545 void DenseLU::solve(DoubleMatrixBase* const& matrix_pt,
546 const Vector<double>& rhs,
548 {
549 // Time the solver
551
552 factorise(matrix_pt);
554
555 // Doc time for solver
556 clock_t t_end = clock();
557
559 if (Doc_time)
560 {
561 oomph_info << "CPU for solve with DenseLU : "
564 << std::endl;
565 }
566
567 // If we are not resolving then delete storage
568 if (!Enable_resolve)
569 {
571 }
572 }
573
574 //==================================================================
575 /// Solver: Takes pointer to problem and returns the results Vector
576 /// which contains the solution of the linear system defined by
577 /// the problem's residual Vector. (Jacobian assembled by FD).
578 //==================================================================
579 void FD_LU::solve(Problem* const& problem_pt, DoubleVector& result)
580 {
581 // Initialise timer
583
584#ifdef PARANOID
585 // if the result vector is setup then check it is not distributed and has
586 // the same communicator as the rhs vector
587 if (result.built())
588 {
589 if (result.distributed())
590 {
591 std::ostringstream error_message_stream;
592 error_message_stream << "The result vector must not be distributed";
596 }
597 }
598#endif
599
600 // Find # of degrees of freedom
601 unsigned long n_dof = problem_pt->ndof();
602
603 // Allocate storage for the residuals vector and the jacobian matrix
605 DenseDoubleMatrix jacobian(n_dof);
606
607 {
608 // initialise timer
610
611 // Get the full jacobian by finite differencing) VERY INEFFICIENT!
612 problem_pt->get_fd_jacobian(residuals, jacobian);
613
614 // compute jacobian setup time
615 clock_t t_end = clock();
617
618 // Report the time
619 if (Doc_time)
620 {
621 oomph_info << std::endl
622 << "CPU for setup of Dense Jacobian: "
625 << std::endl
626 << std::endl;
627 }
628 }
629
630 // Solve by dense LU decomposition (not efficient)
631 solve(&jacobian, residuals, result);
632
633 // Set the sign of the determinant of the jacobian
635
636 // Finalise/doc timings
637 clock_t t_end = clock();
639 if (Doc_time)
640 {
641 oomph_info << "CPU for FD DenseLU LinearSolver: "
643 << std::endl
644 << std::endl;
645 }
646 }
647
648
649 //===================================================================
650 // Interface to SuperLU wrapper
651 //===================================================================
652 extern "C"
653 {
654 int superlu(int*,
655 int*,
656 int*,
657 int*,
658 double*,
659 int*,
660 int*,
661 double*,
662 int*,
663 int*,
664 int*,
665 void*,
666 int*);
667 }
668
669
670#ifdef OOMPH_HAS_MPI
671 //===================================================================
672 // Interface to SuperLU_DIST wrapper
673 //===================================================================
674 extern "C"
675 {
676 // Interface to distributed SuperLU solver where each processor
677 // holds the entire matrix
680 int n,
681 int nnz,
682 double* values,
683 int* row_index,
684 int* col_start,
685 double* b,
686 int nprow,
687 int npcol,
688 int doc,
689 void** data,
690 int* info,
691 MPI_Comm comm);
692
693 // Interface to distributed SuperLU solver where each processor
694 // holds part of the matrix
697 int n,
698 int nnz_local,
699 int nrow_local,
700 int first_row,
701 double* values,
702 int* col_index,
703 int* row_start,
704 double* b,
705 int nprow,
706 int npcol,
707 int doc,
708 void** data,
709 int* info,
710 MPI_Comm comm);
711
712 // helper method - just calls the superlu method dCompRow_to_CompCol to
713 // convert the c-style vectors of a cr matrix to a cc matrix
714 void superlu_cr_to_cc(int nrow,
715 int ncol,
716 int nnz,
717 double* cr_values,
718 int* cr_index,
719 int* cr_start,
720 double** cc_values,
721 int** cc_index,
722 int** cc_start);
723 }
724#endif
725
726
727 //===================================================================
728 // Interface to SuperLU wrapper extras
729 //===================================================================
730 extern "C"
731 {
732 /// Function to calculate the number of bytes used to store the
733 /// LU factors
735
736 /// Function to calculate the number of bytes used in calculating
737 /// and storing the LU factors
739 }
740
741#ifdef OOMPH_HAS_MPI
742 //===================================================================
743 // Interface to SuperLU_DIST wrapper extras
744 //===================================================================
745 extern "C"
746 {
747 /// Function to calculate the number of bytes used to store the
748 /// LU factors
750
751 /// Function to calculate the number of bytes used in calculating
752 /// and storing the LU factors
754 }
755#endif
756
757 //=============================================================================
758 /// How much memory do the LU factors take up? In bytes
759 /// NOTE: This has been scraped from dQuerySpace(...) in dmemory.c in
760 /// external_src/oomph_superlu_4.3
761 //=============================================================================
763 {
764 // If we're using the non-distributed version of SuperLU and the LU
765 // factors have also been computed
766 if ((Solver_type != Distributed) && (Serial_f_factors != 0))
767 {
769 }
770#ifdef OOMPH_HAS_MPI
771 // If we're using SuperLU dist and the LU factors have been computed
773 {
775 }
776#endif
777 // If the factors haven't been computed we can't do anything
778 else
779 {
780 return 0.0;
781 }
782 } // End of get_memory_usage_for_lu_factors
783
784
785 //=============================================================================
786 /// How much memory was used in total? In bytes
787 /// NOTE: This has been scraped from dQuerySpace(...) in dmemory.c in
788 /// external_src/oomph_superlu_4.3
789 //=============================================================================
791 {
792 // If we're using the non-distributed version of SuperLU and the LU
793 // factors have also been computed
794 if ((Solver_type != Distributed) && (Serial_f_factors != 0))
795 {
797 }
798#ifdef OOMPH_HAS_MPI
799 // If we're using SuperLU dist and the LU factors have been computed
801 {
803 }
804#endif
805 // If the factors haven't been computed we can't do anything
806 else
807 {
808 return 0.0;
809 }
810 } // End of get_total_needed_memory
811
812
813 //==========================================================================
814 /// Solver: Takes pointer to problem and returns the results Vector
815 /// which contains the solution of the linear system defined by
816 /// the problem's fully assembled Jacobian and residual Vector.
817 //==========================================================================
819 {
820 // wipe memory
821 this->clean_up_memory();
822
823#ifdef OOMPH_HAS_MPI
824 // USING SUPERLU DIST
825 /////////////////////
826 if (Solver_type == Distributed ||
827 (Solver_type == Default && problem_pt->communicator_pt()->nproc() > 1))
828 {
829 // init the timers
830 double t_start = TimingHelpers::timer();
831
832 // number of dofs
833 unsigned n_dof = problem_pt->ndof();
834
835 // set the distribution
838 this->build_distribution(dist);
839
840 // Take a copy of Delete_matrix_data
842
843 // Set Delete_matrix to true
845
846 // Use the distributed version of SuperLU_DIST?
848 {
849 // Initialise timer
850 double t_start = TimingHelpers::timer();
851
852 // Storage for the residuals vector
854
855 // Get the sparse jacobian and residuals of the problem
856 CRDoubleMatrix jacobian(this->distribution_pt());
857 problem_pt->get_jacobian(residuals, jacobian);
858
859 // Doc time for setup
860 double t_end = TimingHelpers::timer();
862 if (Doc_time)
863 {
864 oomph_info << "Time to set up CRDoubleMatrix Jacobian : "
867 << std::endl;
868 }
869
870 // Now call the linear algebra solve, if desired
871 if (!Suppress_solve)
872 {
873 // If the distribution of the result has been build and
874 // does not match that of
875 // the solver then redistribute before the solve and return
876 // to the incoming distribution afterwards.
877 if ((result.built()) &&
878 (!(*result.distribution_pt() == *this->distribution_pt())))
879 {
881 result.distribution_pt());
882 result.build(this->distribution_pt(), 0.0);
883 solve(&jacobian, residuals, result);
884 result.redistribute(&temp_global_dist);
885 }
886 else
887 {
888 solve(&jacobian, residuals, result);
889 }
890 }
891 }
892 // Otherwise its the global solve version
893 else
894 {
895 // Storage for the residuals vector
896 // A non-distriubted residuals vector
898 problem_pt->communicator_pt(), problem_pt->ndof(), false);
900 CRDoubleMatrix jacobian(&dist);
901
902 // Get the sparse jacobian and residuals of the problem
903 problem_pt->get_jacobian(residuals, jacobian);
904
905 // Doc time for setup
906 double t_end = TimingHelpers::timer();
908 if (Doc_time)
909 {
910 oomph_info << "Time to set up CR Jacobian : "
913 << std::endl;
914 }
915
916 // Now call the linear algebra solve, if desired
917 if (!Suppress_solve)
918 {
919 // If the result distribution has been built and
920 // does not match the global distribution
921 // the redistribute before the solve and then return to the
922 // distributed version afterwards
923 if ((result.built()) && (!(*result.distribution_pt() == dist)))
924 {
926 result.distribution_pt());
927 result.build(&dist, 0.0);
928 solve(&jacobian, residuals, result);
929 result.redistribute(&temp_global_dist);
930 }
931 else
932 {
933 solve(&jacobian, residuals, result);
934 }
935 }
936 }
937 // Set Delete_matrix back to original value
939 }
940
941 // OTHERWISE WE ARE USING SUPERLU (SERIAL)
942 //////////////////////////////////////////
943 else
944#endif
945 {
946 // set the solver distribution
948 problem_pt->communicator_pt(), problem_pt->ndof(), false);
949 this->build_distribution(dist);
950
951 // Allocate storage for the residuals vector
953
954 // Use the compressed row version?
956 {
957 // Initialise timer
958 double t_start = TimingHelpers::timer();
959
960 // Get the sparse jacobian and residuals of the problem
962 problem_pt->get_jacobian(residuals, CR_jacobian);
963
964 // If we want to compute the gradient for the globally convergent
965 // Newton method, then do it here
967 {
968 // Compute it
969 CR_jacobian.multiply_transpose(residuals,
971 // Set the flag
973 }
974
975 // Doc time for setup
976 double t_end = TimingHelpers::timer();
978 if (Doc_time)
979 {
980 oomph_info << std::endl
981 << "Time to set up CRDoubleMatrix Jacobian : "
984 << std::endl;
985 }
986
987 // Now call the linear algebra solve, if desired
988 if (!Suppress_solve)
989 {
990 // If the result vector is built and distributed
991 // then need to redistribute into the same form as the
992 // RHS (non-distributed)
993 if ((result.built()) &&
994 (!(*result.distribution_pt() == *this->distribution_pt())))
995 {
997 result.distribution_pt());
998 result.build(this->distribution_pt(), 0.0);
999 solve(&CR_jacobian, residuals, result);
1000 result.redistribute(&temp_global_dist);
1001 }
1002 // Otherwise just solve
1003 else
1004 {
1006 }
1007 }
1008 }
1009 // Otherwise its the compressed column version
1010 else
1011 {
1012 // Initialise timer
1013 double t_start = TimingHelpers::timer();
1014
1015 // Get the sparse jacobian and residuals of the problem
1017 problem_pt->get_jacobian(residuals, CC_jacobian);
1018
1019 // If we want to compute the gradient for the globally convergent
1020 // Newton method, then do it here
1021 if (Compute_gradient)
1022 {
1023 // Compute it
1024 CC_jacobian.multiply_transpose(residuals,
1026 // Set the flag
1028 }
1029
1030 // Doc time for setup
1031 double t_end = TimingHelpers::timer();
1033 if (Doc_time)
1034 {
1035 oomph_info << "\nTime to set up CCDoubleMatrix Jacobian: "
1038 << std::endl;
1039 }
1040
1041 // Now call the linear algebra solve, if desired
1042 if (!Suppress_solve)
1043 {
1044 // If the result vector is built and distributed
1045 // then need to redistribute into the same form as the
1046 // RHS
1047 if ((result.built()) &&
1048 (!(*result.distribution_pt() == *this->distribution_pt())))
1049 {
1051 result.distribution_pt());
1052 result.build(this->distribution_pt(), 0.0);
1053 solve(&CC_jacobian, residuals, result);
1054 result.redistribute(&temp_global_dist);
1055 }
1056 // Otherwise just solve
1057 else
1058 {
1060 }
1061 }
1062 }
1063
1064 // Set the sign of the jacobian
1065 //(this is computed in the LU decomposition phase)
1067 }
1068 }
1069
1070 //=========================================================================
1071 /// Linear-algebra-type solver: Takes pointer to a matrix and rhs
1072 /// vector and returns the solution of the linear system. Problem pointer
1073 /// defaults to NULL and can be omitted. The function returns the global
1074 /// result Vector.
1075 /// Note: if Delete_matrix_data is true the function
1076 /// matrix_pt->clean_up_memory() will be used to wipe the matrix data.
1077 //=========================================================================
1079 const DoubleVector& rhs,
1081 {
1082 // Initialise timer
1083 double t_start = TimingHelpers::timer();
1084
1085 // Pointer used in various places
1086 CRDoubleMatrix* cr_pt = 0;
1087
1088
1089#ifdef PARANOID
1090 // check that the rhs vector is setup
1091 if (!rhs.built())
1092 {
1093 std::ostringstream error_message_stream;
1094 error_message_stream << "The vectors rhs must be setup";
1098 }
1099
1100 // check that the matrix is square
1101 if (matrix_pt->nrow() != matrix_pt->ncol())
1102 {
1103 std::ostringstream error_message_stream;
1104 error_message_stream << "The matrix at matrix_pt must be square.";
1108 }
1109
1110 // check that the matrix has some entries, and so has a values_pt that
1111 // makes sense (only for CR because CC is never used I think dense
1112 // matrices will be safe since they don't use a values pointer).
1113 cr_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt);
1114 if (cr_pt != 0)
1115 {
1116 if (cr_pt->nnz() == 0)
1117 {
1118 std::ostringstream error_message_stream;
1120 << "Attempted to call SuperLu on a CRDoubleMatrix with no entries, "
1121 << "SuperLU would segfault (because the values array pt is "
1122 << "uninitialised or null).";
1126 }
1127 }
1128
1129 // check that the matrix and the rhs vector have the same nrow()
1130 if (matrix_pt->nrow() != rhs.nrow())
1131 {
1132 std::ostringstream error_message_stream;
1134 << "The matrix and the rhs vector must have the same number of rows.";
1138 }
1139
1140 // if the matrix is distributable then should have the same distribution
1141 // as the rhs vector
1143 dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt);
1144 if (dist_matrix_pt != 0)
1145 {
1146 if (!(*dist_matrix_pt->distribution_pt() == *rhs.distribution_pt()))
1147 {
1148 std::ostringstream error_message_stream;
1150 << "The matrix matrix_pt must have the same distribution as the "
1151 << "rhs vector.";
1155 }
1156 }
1157 // if the matrix is not distributable then it the rhs vector should not be
1158 // distributed
1159 else
1160 {
1161 if (rhs.distribution_pt()->distributed())
1162 {
1163 std::ostringstream error_message_stream;
1165 << "The matrix (matrix_pt) is not distributable and therefore the rhs"
1166 << " vector must not be distributed";
1170 }
1171 }
1172 // if the result vector is setup then check it has the same distribution
1173 // as the rhs
1174 if (result.built())
1175 {
1176 if (!(*result.distribution_pt() == *rhs.distribution_pt()))
1177 {
1178 std::ostringstream error_message_stream;
1180 << "The result vector distribution has been setup; it must have the "
1181 << "same distribution as the rhs vector.";
1185 }
1186 }
1187#endif
1188
1189 // set the distribution
1190 if (dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt))
1191 {
1192 // the solver has the same distribution as the matrix if possible
1193 this->build_distribution(
1194 dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt)
1195 ->distribution_pt());
1196 }
1197 else
1198 {
1199 // the solver has the same distribution as the RHS
1201 }
1202
1203 // Doc time for solve
1205
1206 // Keep this for debugging
1207 if (1 == 0)
1208 {
1209 std::string filename =
1211 "_proc" +
1213 ".dat";
1214 CRDoubleMatrix* cr_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt);
1215 cr_pt->sparse_indexed_output_with_offset(filename);
1216 oomph_info << "output matrix " << Global_unsigned::Number << std::endl;
1218 }
1219
1220
1221 // Factorise the matrix
1222 factorise(matrix_pt);
1223
1224 // Doc the end time
1226
1227 // How long did the factorisation take?
1229
1230 // Try and upcast the matrix to a CRDoubleMatrix
1231 // CRDoubleMatrix*
1232 cr_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt);
1233
1234 // If the input matrix is a CRDoubleMatrix
1235 if (cr_pt != 0)
1236 {
1237 // ...and actually has an entry
1238 if (cr_pt->nnz() != 0)
1239 {
1240 // Find out how many rows there are in the global Jacobian
1241 unsigned n_row = cr_pt->nrow();
1242
1243 // And how many non-zeros there are in the global Jacobian
1244 unsigned n_nnz = cr_pt->nnz();
1245
1246 // Get the memory usage (in bytes) for the global Jacobian storage
1248 ((2 * ((n_row + 1) * sizeof(int))) +
1249 (n_nnz * (sizeof(int) + sizeof(double))));
1250
1251 // Get the memory usage (in bytes) for storage of the LU factors in
1252 // SuperLU
1254
1255 // Get the memory usage (in bytes) for storage of the LU factors in
1256 // SuperLU
1257 double total_memory_usage =
1259
1260
1261 // How much memory have we used?
1262 if (Doc_stats)
1263 {
1264 oomph_info << "\nMemory statistics:"
1265 << "\n - Memory used to store the Jacobian (MB): "
1266 << memory_usage_for_jacobian / 1.0e+06
1267 << "\n - Memory used to store the LU factors (MB): "
1268 << memory_usage_for_lu_storage / 1.0e+06
1269 << "\n - Total memory used for matrix storage (MB): "
1270 << total_memory_usage / 1.0e+06 << "\n"
1271 << std::endl;
1272 }
1273 }
1274 } // if (cr_pt!=0)
1275
1276 // Doc the start time
1278
1279 // Now do the back solve
1280 backsub(rhs, result);
1281
1282 // Doc the end time
1284
1285 // How long did the back substitution take?
1287
1288 // Doc time for solve
1289 double t_end = TimingHelpers::timer();
1291 if (Doc_time)
1292 {
1294 << "Time for LU factorisation : "
1296 << "\nTime for back-substitution: "
1298 << "\nTime for SuperLUSolver solve (ndof=" << matrix_pt->nrow() << "): "
1300 << std::endl;
1301 }
1302
1303 // If we are not storing the solver data for resolves, delete it
1304 if (!Enable_resolve)
1305 {
1307 }
1308 }
1309
1310
1311 //=============================================================================
1312 /// Solver: Takes pointer to problem and returns the results Vector
1313 /// which contains the solution of the linear system defined by
1314 /// the problem's fully assembled Jacobian and residual Vector.
1315 //=============================================================================
1318 {
1319 // wipe memory
1320 this->clean_up_memory();
1321
1322#ifdef OOMPH_HAS_MPI
1323 // USING SUPERLU DIST
1324 /////////////////////
1325 if (Solver_type == Distributed ||
1326 (Solver_type == Default && problem_pt->communicator_pt()->nproc() > 1))
1327 {
1328 // init the timers
1329 double t_start = TimingHelpers::timer();
1330
1331 // number of dofs
1332 unsigned n_dof = problem_pt->ndof();
1333
1334 // set the distribution
1337 this->build_distribution(dist);
1338
1339 // Take a copy of Delete_matrix_data
1341
1342 // Set Delete_matrix to true
1344
1345 // Use the distributed version of SuperLU_DIST?
1347 {
1348 // Initialise timer
1349 double t_start = TimingHelpers::timer();
1350
1351 // Storage for the residuals vector
1353
1354 // Get the sparse jacobian and residuals of the problem
1355 CRDoubleMatrix jacobian(this->distribution_pt());
1356 problem_pt->get_jacobian(residuals, jacobian);
1357
1358 // Doc time for setup
1359 double t_end = TimingHelpers::timer();
1361 if (Doc_time)
1362 {
1363 oomph_info << "Time to set up CRDoubleMatrix Jacobian : "
1366 << std::endl;
1367 }
1368
1369 // Now call the linear algebra solve, if desired
1370 if (!Suppress_solve)
1371 {
1372 // If the distribution of the result has been build and
1373 // does not match that of
1374 // the solver then redistribute before the solve and return
1375 // to the incoming distribution afterwards.
1376 if ((result.built()) &&
1377 (!(*result.distribution_pt() == *this->distribution_pt())))
1378 {
1380 result.distribution_pt());
1381 result.build(this->distribution_pt(), 0.0);
1382 solve_transpose(&jacobian, residuals, result);
1383 result.redistribute(&temp_global_dist);
1384 }
1385 else
1386 {
1387 solve_transpose(&jacobian, residuals, result);
1388 }
1389 }
1390 }
1391 // Otherwise its the global solve version
1392 else
1393 {
1394 // Storage for the residuals vector
1395 // A non-distriubted residuals vector
1397 problem_pt->communicator_pt(), problem_pt->ndof(), false);
1399 CRDoubleMatrix jacobian(&dist);
1400
1401 // Get the sparse jacobian and residuals of the problem
1402 problem_pt->get_jacobian(residuals, jacobian);
1403
1404 // Doc time for setup
1405 double t_end = TimingHelpers::timer();
1407 if (Doc_time)
1408 {
1409 oomph_info << "Time to set up CR Jacobian : "
1412 << std::endl;
1413 }
1414
1415 // Now call the linear algebra solve, if desired
1416 if (!Suppress_solve)
1417 {
1418 // If the result distribution has been built and
1419 // does not match the global distribution
1420 // the redistribute before the solve and then return to the
1421 // distributed version afterwards
1422 if ((result.built()) && (!(*result.distribution_pt() == dist)))
1423 {
1425 result.distribution_pt());
1426 result.build(&dist, 0.0);
1427 solve_transpose(&jacobian, residuals, result);
1428 result.redistribute(&temp_global_dist);
1429 }
1430 else
1431 {
1432 solve_transpose(&jacobian, residuals, result);
1433 }
1434 }
1435 }
1436 // Set Delete_matrix back to original value
1438 }
1439
1440 // OTHERWISE WE ARE USING SUPERLU (SERIAL)
1441 //////////////////////////////////////////
1442 else
1443#endif
1444 {
1445 // set the solver distribution
1447 problem_pt->communicator_pt(), problem_pt->ndof(), false);
1448 this->build_distribution(dist);
1449
1450 // Allocate storage for the residuals vector
1452
1453 // Use the compressed row version?
1455 {
1456 // Initialise timer
1457 double t_start = TimingHelpers::timer();
1458
1459 // Get the sparse jacobian and residuals of the problem
1461 problem_pt->get_jacobian(residuals, CR_jacobian);
1462
1463 // If we want to compute the gradient for the globally convergent
1464 // Newton method, then do it here
1465 if (Compute_gradient)
1466 {
1467 // Compute it
1468 CR_jacobian.multiply_transpose(residuals,
1470 // Set the flag
1472 }
1473
1474 // Doc time for setup
1475 double t_end = TimingHelpers::timer();
1477 if (Doc_time)
1478 {
1479 oomph_info << std::endl
1480 << "Time to set up CRDoubleMatrix Jacobian: "
1483 << std::endl;
1484 }
1485
1486 // Now call the linear algebra solve, if desired
1487 if (!Suppress_solve)
1488 {
1489 // If the result vector is built and distributed
1490 // then need to redistribute into the same form as the
1491 // RHS (non-distributed)
1492 if ((result.built()) &&
1493 (!(*result.distribution_pt() == *this->distribution_pt())))
1494 {
1496 result.distribution_pt());
1497 result.build(this->distribution_pt(), 0.0);
1498 solve_transpose(&CR_jacobian, residuals, result);
1499 result.redistribute(&temp_global_dist);
1500 }
1501 // Otherwise just solve
1502 else
1503 {
1505 }
1506 }
1507 }
1508 // Otherwise its the compressed column version
1509 else
1510 {
1511 // Initialise timer
1512 double t_start = TimingHelpers::timer();
1513
1514 // Get the sparse jacobian and residuals of the problem
1516 problem_pt->get_jacobian(residuals, CC_jacobian);
1517
1518 // If we want to compute the gradient for the globally convergent
1519 // Newton method, then do it here
1520 if (Compute_gradient)
1521 {
1522 // Compute it
1523 CC_jacobian.multiply_transpose(residuals,
1525 // Set the flag
1527 }
1528
1529 // Doc time for setup
1530 double t_end = TimingHelpers::timer();
1532 if (Doc_time)
1533 {
1534 oomph_info << "\nTime to set up CCDoubleMatrix Jacobian: "
1537 << std::endl;
1538 }
1539
1540 // Now call the linear algebra solve, if desired
1541 if (!Suppress_solve)
1542 {
1543 // If the result vector is built and distributed
1544 // then need to redistribute into the same form as the
1545 // RHS
1546 if ((result.built()) &&
1547 (!(*result.distribution_pt() == *this->distribution_pt())))
1548 {
1550 result.distribution_pt());
1551 result.build(this->distribution_pt(), 0.0);
1552 solve_transpose(&CC_jacobian, residuals, result);
1553 result.redistribute(&temp_global_dist);
1554 }
1555 // Otherwise just solve
1556 else
1557 {
1559 }
1560 }
1561 }
1562
1563 // Set the sign of the jacobian
1564 //(this is computed in the LU decomposition phase)
1566 }
1567 }
1568
1569 //=========================================================================
1570 /// Linear-algebra-type solver: Takes pointer to a matrix and rhs
1571 /// vector and returns the solution of the linear system. Problem pointer
1572 /// defaults to NULL and can be omitted. The function returns the global
1573 /// result Vector.
1574 /// Note: if Delete_matrix_data is true the function
1575 /// matrix_pt->clean_up_memory() will be used to wipe the matrix data.
1576 //=========================================================================
1578 const DoubleVector& rhs,
1580 {
1581 // Initialise timer
1582 double t_start = TimingHelpers::timer();
1583
1584 // Pointer used in various places
1585 CRDoubleMatrix* cr_pt = 0;
1586
1587#ifdef PARANOID
1588 // check that the rhs vector is setup
1589 if (!rhs.built())
1590 {
1591 std::ostringstream error_message_stream;
1592 error_message_stream << "The vectors rhs must be setup";
1596 }
1597
1598 // check that the matrix is square
1599 if (matrix_pt->nrow() != matrix_pt->ncol())
1600 {
1601 std::ostringstream error_message_stream;
1602 error_message_stream << "The matrix at matrix_pt must be square.";
1606 }
1607
1608 // check that the matrix has some entries, and so has a values_pt that
1609 // makes sense (only for CR because CC is never used I think dense
1610 // matrices will be safe since they don't use a values pointer).
1611 cr_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt);
1612 if (cr_pt != 0)
1613 {
1614 if (cr_pt->nnz() == 0)
1615 {
1616 std::ostringstream error_message_stream;
1618 << "Attempted to call SuperLu on a CRDoubleMatrix with no entries, "
1619 << "SuperLU would segfault (because the values array pt is "
1620 << "uninitialised or null).";
1624 }
1625 }
1626
1627 // check that the matrix and the rhs vector have the same nrow()
1628 if (matrix_pt->nrow() != rhs.nrow())
1629 {
1630 std::ostringstream error_message_stream;
1632 << "The matrix and the rhs vector must have the same number of rows.";
1636 }
1637
1638 // if the matrix is distributable then should have the same distribution
1639 // as the rhs vector
1641 dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt);
1642 if (dist_matrix_pt != 0)
1643 {
1644 if (!(*dist_matrix_pt->distribution_pt() == *rhs.distribution_pt()))
1645 {
1646 std::ostringstream error_message_stream;
1648 << "The matrix matrix_pt must have the same distribution as the "
1649 << "rhs vector.";
1653 }
1654 }
1655 // if the matrix is not distributable then it the rhs vector should not be
1656 // distributed
1657 else
1658 {
1659 if (rhs.distribution_pt()->distributed())
1660 {
1661 std::ostringstream error_message_stream;
1663 << "The matrix (matrix_pt) is not distributable and therefore the rhs"
1664 << " vector must not be distributed";
1668 }
1669 }
1670 // if the result vector is setup then check it has the same distribution
1671 // as the rhs
1672 if (result.built())
1673 {
1674 if (!(*result.distribution_pt() == *rhs.distribution_pt()))
1675 {
1676 std::ostringstream error_message_stream;
1678 << "The result vector distribution has been setup; it must have the "
1679 << "same distribution as the rhs vector.";
1683 }
1684 }
1685#endif
1686
1687 // set the distribution
1688 if (dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt))
1689 {
1690 // the solver has the same distribution as the matrix if possible
1691 this->build_distribution(
1692 dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt)
1693 ->distribution_pt());
1694 }
1695 else
1696 {
1697 // the solver has the same distribution as the RHS
1699 }
1700
1701 // Doc time for solve
1703
1704 // Factorise the matrix
1705 factorise(matrix_pt);
1706
1707 // Doc the end time
1709
1710 // How long did the factorisation take?
1712
1713 // Try and upcast the matrix to a CRDoubleMatrix
1714 // CRDoubleMatrix*
1715 cr_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt);
1716
1717 // If the input matrix is a CRDoubleMatrix
1718 if (cr_pt != 0)
1719 {
1720 // ...and actually has an entry
1721 if (cr_pt->nnz() != 0)
1722 {
1723 // Find out how many rows there are in the global Jacobian
1724 unsigned n_row = cr_pt->nrow();
1725
1726 // And how many non-zeros there are in the global Jacobian
1727 unsigned n_nnz = cr_pt->nnz();
1728
1729 // Get the memory usage (in bytes) for the global Jacobian storage
1731 ((2 * ((n_row + 1) * sizeof(int))) +
1732 (n_nnz * (sizeof(int) + sizeof(double))));
1733
1734 // Get the memory usage (in bytes) for storage of the LU factors in
1735 // SuperLU
1737
1738 // Get the memory usage (in bytes) for storage of the LU factors in
1739 // SuperLU
1740 double total_memory_usage =
1742
1743 // How much memory have we used?
1744 if (Doc_stats)
1745 {
1746 oomph_info << "\nMemory statistics:"
1747 << "\n - Memory used to store the Jacobian (MB): "
1748 << memory_usage_for_jacobian / 1.0e+06
1749 << "\n - Memory used to store the LU factors (MB): "
1750 << memory_usage_for_lu_storage / 1.0e+06
1751 << "\n - Total memory used for matrix storage (MB): "
1752 << total_memory_usage / 1.0e+06 << "\n"
1753 << std::endl;
1754 }
1755 }
1756 } // if (cr_pt!=0)
1757
1758 // Doc the start time
1760
1761 // Now do the back solve
1763
1764 // Doc the end time
1766
1767 // How long did the back substitution take?
1769
1770 // Doc time for solve
1771 double t_end = TimingHelpers::timer();
1773 if (Doc_time)
1774 {
1776 << "Time for LU factorisation : "
1778 << "\nTime for back-substitution: "
1780 << "\nTime for SuperLUSolver solve (ndof=" << matrix_pt->nrow() << "): "
1782 << std::endl;
1783 }
1784
1785 // If we are not storing the solver data for resolves, delete it
1786 if (!Enable_resolve)
1787 {
1789 }
1790 } // End of solve_transpose
1791
1792 //===============================================================
1793 /// Resolve the system for a given RHS
1794 //===============================================================
1796 {
1797 // Store starting time for solve
1798 double t_start = TimingHelpers::timer();
1799
1800 // backsub
1801 backsub(rhs, result);
1802
1803 // Doc time for solve
1804 double t_end = TimingHelpers::timer();
1806 if (Doc_time)
1807 {
1808 oomph_info << "Time for SuperLUSolver solve (ndof=" << rhs.nrow() << "): "
1810 t_start)
1811 << std::endl;
1812 }
1813 }
1814
1815
1816 //===============================================================
1817 /// Resolve the (transposed) system for a given RHS
1818 //===============================================================
1821 {
1822 // Store starting time for solve
1823 double t_start = TimingHelpers::timer();
1824
1825 // Backsub (but solve the transposed system)
1827
1828 // Doc time for solve
1829 double t_end = TimingHelpers::timer();
1831 if (Doc_time)
1832 {
1833 oomph_info << "Time for SuperLUSolver solve (ndof=" << rhs.nrow() << "): "
1835 t_start)
1836 << std::endl;
1837 }
1838 }
1839
1840
1841 //===================================================================
1842 /// LU decompose the matrix addressed by matrix_pt by using
1843 /// the SuperLU solver. The resulting matrix factors are stored
1844 /// internally.
1845 //===================================================================
1847 {
1848 // wipe memory
1849 this->clean_up_memory();
1850
1851 // if we have mpi and the solver is distributed or default and nproc
1852 // gt 1
1853#ifdef OOMPH_HAS_MPI
1855 dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt);
1856 unsigned nproc = 1;
1857 if (dist_matrix_pt != 0)
1858 {
1859 nproc = dist_matrix_pt->distribution_pt()->communicator_pt()->nproc();
1860 }
1861 if (Solver_type == Distributed || (Solver_type == Default && nproc > 1 &&
1863 {
1864 // if the matrix is a distributed linear algebra object then use SuperLU
1865 // dist
1866 if (dist_matrix_pt != 0)
1867 {
1868 factorise_distributed(matrix_pt);
1869 Using_dist = true;
1870 }
1871 else
1872 {
1873 factorise_serial(matrix_pt);
1874 Using_dist = false;
1875 }
1876 }
1877 else
1878#endif
1879 {
1880 factorise_serial(matrix_pt);
1881 Using_dist = false;
1882 }
1883 }
1884
1885#ifdef OOMPH_HAS_MPI
1886 //=============================================================================
1887 /// LU decompose the matrix addressed by matrix_pt using
1888 /// the SuperLU_DIST solver. The resulting matrix factors are stored
1889 /// internally.
1890 //=============================================================================
1892 {
1893 // Check that we have a square matrix
1894#ifdef PARANOID
1895 int m = matrix_pt->ncol();
1896 int n = matrix_pt->nrow();
1897 if (n != m)
1898 {
1899 std::ostringstream error_message_stream;
1900 error_message_stream << "Can only solve for square matrices\n"
1901 << "N, M " << n << " " << m << std::endl;
1902
1906 }
1907#endif
1908
1909 // number of processors
1910 unsigned nproc = MPI_Helpers::communicator_pt()->nproc();
1911 if (dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt) != 0)
1912 {
1913 nproc = dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt)
1914 ->distribution_pt()
1915 ->communicator_pt()
1916 ->nproc();
1917 }
1918
1919 // Find number of rows and columns for the process grid
1920 // First guess at number of rows:
1921 int nprow = int(sqrt(double(nproc)));
1922
1923 // Does this evenly divide the processor grid?
1924 while (nprow > 1)
1925 {
1926 if (nproc % nprow == 0) break;
1927 nprow -= 1;
1928 }
1929
1930 // Store Number of rows/columns for process grid
1931 Dist_nprow = nprow;
1933
1934 // Make sure any existing factors are deleted
1936
1937 // Doc (0/1) = (true/false)
1938 int doc = !Doc_stats;
1939
1940 // Rset Info
1941 Dist_info = 0;
1942
1943 // Flag for row and column permutations
1945
1946 // Is it a DistributedCRDoubleMatrix?
1947 if (dynamic_cast<CRDoubleMatrix*>(matrix_pt) != 0)
1948 {
1949 // Get a cast pointer to the matrix
1950 CRDoubleMatrix* cr_matrix_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt);
1951
1952 // Get the distribution from the matrix
1953 this->build_distribution(cr_matrix_pt->distribution_pt());
1954
1955#ifdef PARANOID
1956 // paranoid check that the matrix has been setup
1957 if (!cr_matrix_pt->built())
1958 {
1959 throw OomphLibError(
1960 "To apply SuperLUSolver to a CRDoubleMatrix - it must be built",
1963 }
1964#endif
1965
1966 // if the matrix is distributed then setup setup superlu dist distributed
1967 if (cr_matrix_pt->distributed())
1968 {
1969 // Find the number of non-zero entries in the matrix
1970 const int nnz_local = int(cr_matrix_pt->nnz());
1971
1972 // Set up the pointers to the matrix.
1973 // NOTE: these arrays (accessed via value_pt, index_pt and
1974 // start_pt) may be modified by the SuperLU_DIST routines, and so
1975 // a copy must be taken if the matrix is to be preserved.
1976
1977 // Copy values
1978 Dist_value_pt = new double[nnz_local];
1979 double* matrix_value_pt = cr_matrix_pt->value();
1980 for (int i = 0; i < nnz_local; i++)
1981 {
1983 }
1984
1985 // Copy column indices
1986 Dist_index_pt = new int[nnz_local];
1987 int* matrix_index_pt = cr_matrix_pt->column_index();
1988 for (int i = 0; i < nnz_local; i++)
1989 {
1991 }
1992
1993 // Copy row starts
1994 int nrow_local = cr_matrix_pt->nrow_local();
1995 Dist_start_pt = new int[nrow_local + 1];
1996 int* matrix_start_pt = cr_matrix_pt->row_start();
1997 for (int i = 0; i <= nrow_local; i++)
1998 {
2000 }
2001
2002 // cache
2003 int ndof = cr_matrix_pt->distribution_pt()->nrow();
2004 int first_row = cr_matrix_pt->first_row();
2005
2006 // Now delete the matrix if we are allowed
2007 if (Dist_delete_matrix_data == true)
2008 {
2009 cr_matrix_pt->clear();
2010 }
2011
2012 // Factorize
2014 1,
2016 ndof,
2017 nnz_local,
2018 nrow_local,
2019 first_row,
2023 0,
2024 Dist_nprow,
2025 Dist_npcol,
2026 doc,
2028 &Dist_info,
2029 this->distribution_pt()->communicator_pt()->mpi_comm());
2030
2031 // Record that data is stored
2033 }
2034 // else the CRDoubleMatrix is not distributed
2035 else
2036 {
2037 // Find the number of non-zero entries in the matrix
2038 const int nnz = int(cr_matrix_pt->nnz());
2039
2040 // cache the number of rows
2041 int nrow = cr_matrix_pt->nrow();
2042
2043 // Set up the pointers to the matrix.
2044 // NOTE: these arrays (accessed via value_pt, index_pt and
2045 // start_pt) may be modified by the SuperLU_DIST routines, and so
2046 // a copy must be taken if the matrix is to be preserved.
2047
2048 // create the corresponing cc matrix
2050 nrow,
2051 nnz,
2052 cr_matrix_pt->value(),
2053 cr_matrix_pt->column_index(),
2054 cr_matrix_pt->row_start(),
2057 &Dist_start_pt);
2058
2059 // Delete the matrix if we are allowed
2060 if (Dist_delete_matrix_data == true)
2061 {
2062 cr_matrix_pt->clear();
2063 }
2064
2065 // do the factorization
2067 1,
2069 nrow,
2070 nnz,
2074 0,
2075 Dist_nprow,
2076 Dist_npcol,
2077 doc,
2079 &Dist_info,
2080 this->distribution_pt()->communicator_pt()->mpi_comm());
2081
2082 // Record that data is stored
2084 }
2085 }
2086
2087 // Or is it a CCDoubleMatrix?
2088 else if (dynamic_cast<CCDoubleMatrix*>(matrix_pt))
2089 {
2090 // Get a cast pointer to the matrix
2092 dynamic_cast<CCDoubleMatrix*>(matrix_pt);
2093
2094 // Find the number of non-zero entries in the matrix
2095 const int nnz = int(serial_matrix_pt->nnz());
2096
2097 // Find # of degrees of freedom (variables)
2098 int ndof = int(serial_matrix_pt->nrow());
2099
2100 // Find the local number of degrees of freedom in the linear system
2101 int ndof_local = ndof;
2102
2103 // Set up the pointers to the matrix.
2104 // NOTE: these arrays (accessed via value_pt, index_pt and
2105 // start_pt) may be modified by the SuperLU_DIST routines, and so
2106 // a copy must be taken if the matrix is to be preserved.
2107
2108 // Copy values
2109 Dist_value_pt = new double[nnz];
2110 double* matrix_value_pt = serial_matrix_pt->value();
2111 for (int i = 0; i < nnz; i++)
2112 {
2114 }
2115
2116 // copy row indices
2117 Dist_index_pt = new int[nnz];
2118 int* matrix_index_pt = serial_matrix_pt->row_index();
2119 for (int i = 0; i < nnz; i++)
2120 {
2122 }
2123
2124 // copy column starts
2125 Dist_start_pt = new int[ndof_local + 1];
2126 int* matrix_start_pt = serial_matrix_pt->column_start();
2127 for (int i = 0; i <= ndof_local; i++)
2128 {
2130 }
2131
2132 // Delete the matrix if we are allowed
2133 if (Dist_delete_matrix_data == true)
2134 {
2135 serial_matrix_pt->clean_up_memory();
2136 }
2137
2138 // do the factorization
2140 1,
2142 ndof,
2143 nnz,
2147 0,
2148 Dist_nprow,
2149 Dist_npcol,
2150 doc,
2152 &Dist_info,
2153 this->distribution_pt()->communicator_pt()->mpi_comm());
2154
2155 // Record that data is stored
2157 }
2158 // Otherwise throw an error
2159 else
2160 {
2161 std::ostringstream error_message_stream;
2162 error_message_stream << "SuperLUSolver implemented only for "
2163 << " CCDoubleMatrix, CRDoubleMatrix\n"
2164 << "and DistributedCRDoubleMatrix matrices\n";
2168 }
2169
2170 // Throw an error if superLU returned an error status in info.
2171 if (Dist_info != 0)
2172 {
2173 std::ostringstream error_msg;
2174 error_msg << "SuperLU returned the error status code " << Dist_info
2175 << " . See the SuperLU documentation for what this means.";
2176 throw OomphLibError(
2178 }
2179 }
2180#endif
2181
2182 //===================================================================
2183 /// LU decompose the matrix addressed by matrix_pt by using
2184 /// the SuperLU solver. The resulting matrix factors are stored
2185 /// internally.
2186 //===================================================================
2188 {
2189#ifdef PARANOID
2190 // PARANOID check that if the matrix is distributable then it should not be
2191 // then it should not be distributed
2192 if (dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt) != 0)
2193 {
2194 if (dynamic_cast<DistributableLinearAlgebraObject*>(matrix_pt)
2195 ->distributed())
2196 {
2197 std::ostringstream error_message_stream;
2198 error_message_stream << "The matrix must not be distributed.";
2202 }
2203 }
2204#endif
2205
2206 // Find # of degrees of freedom (variables)
2207 int n = matrix_pt->nrow();
2208
2209 // Check that we have a square matrix
2210#ifdef PARANOID
2211 int m = matrix_pt->ncol();
2212 if (n != m)
2213 {
2214 std::ostringstream error_message_stream;
2215 error_message_stream << "Can only solve for square matrices\n"
2216 << "N, M " << n << " " << m << std::endl;
2217
2221 }
2222#endif
2223
2224 // Storage for the values, rows and column indices
2225 // required by SuplerLU
2226 double* value = 0;
2227 int *index = 0, *start = 0;
2228
2229 // Integer used to represent compressed row or column format
2230 // Default compressed row
2231 int transpose = 0;
2232
2233 // Number of non-zero entries in the matrix
2234 int nnz = 0;
2235
2236 // Doc flag (convert to int for SuperLU)
2237 int doc = Doc_stats;
2238
2239 // Is it a CR matrix
2240 if (dynamic_cast<CRDoubleMatrix*>(matrix_pt))
2241 {
2242 // Set the appropriate row flags
2244 transpose = 1;
2245 // Get a cast pointer to the matrix
2246 CRDoubleMatrix* CR_matrix_pt = dynamic_cast<CRDoubleMatrix*>(matrix_pt);
2247
2248 // Now set the pointers to the interanally stored values
2249 // and indices
2250 nnz = CR_matrix_pt->nnz();
2251 value = CR_matrix_pt->value();
2252 index = CR_matrix_pt->column_index();
2253 start = CR_matrix_pt->row_start();
2254 }
2255 // Otherwise is it the compressed column version?
2256 else if (dynamic_cast<CCDoubleMatrix*>(matrix_pt))
2257 {
2258 // Set the compressed row flag to false
2260 // Get a cast pointer to the matrix
2261 CCDoubleMatrix* CC_matrix_pt = dynamic_cast<CCDoubleMatrix*>(matrix_pt);
2262
2263 // Now set the pointers to the interanally stored values
2264 // and indices
2265 nnz = CC_matrix_pt->nnz();
2266 value = CC_matrix_pt->value();
2267 index = CC_matrix_pt->row_index();
2268 start = CC_matrix_pt->column_start();
2269 }
2270 // Otherwise throw and error
2271 else
2272 {
2273 throw OomphLibError("SuperLU only works with CR or CC Double matrices",
2276 }
2277
2278 // Clean up any previous storage so that if this is called twice with
2279 // the same matrix, we don't get a memory leak
2281
2282 // Perform the lu decompose phase (i=1)
2283 int i = 1;
2285 &n,
2286 &nnz,
2287 0,
2288 value,
2289 index,
2290 start,
2291 0,
2292 &n,
2293 &transpose,
2294 &doc,
2296 &Serial_info);
2297
2298 // Throw an error if superLU returned an error status in info.
2299 if (Serial_info != 0)
2300 {
2301 std::ostringstream error_msg;
2302 error_msg << "SuperLU returned the error status code " << Serial_info
2303 << " . See the SuperLU documentation for what this means.";
2304 throw OomphLibError(
2306 }
2307
2308
2309 // Set the number of degrees of freedom in the linear system
2310 Serial_n_dof = n;
2311 }
2312
2313 //=============================================================================
2314 /// Do the backsubstitution for SuperLUSolver.
2315 /// Note - this method performs no paranoid checks - these are all performed
2316 /// in solve(...) and resolve(...)
2317 //=============================================================================
2319 {
2320#ifdef OOMPH_HAS_MPI
2321 if (Using_dist)
2322 {
2324 }
2325 else
2326#endif
2327 {
2329 }
2330 }
2331
2332
2333 //=============================================================================
2334 /// Do the backsubstitution of the transposed system for SuperLUSolver.
2335 /// Note - this method performs no paranoid checks - these are all performed
2336 /// in solve(...) and resolve(...)
2337 //=============================================================================
2340 {
2341#ifdef OOMPH_HAS_MPI
2342 if (Using_dist)
2343 {
2345 }
2346 else
2347#endif
2348 {
2350 }
2351 }
2352
2353#ifdef OOMPH_HAS_MPI
2354 //=========================================================================
2355 /// Static warning to suppress warnings about incorrect distribution of
2356 /// RHS vector. Default is false
2357 //=========================================================================
2359 false;
2360
2361 //=============================================================================
2362 /// Do the backsubstitution for SuperLU solver.
2363 /// Note - this method performs no paranoid checks - these are all performed
2364 /// in solve(...) and resolve(...)
2365 //=============================================================================
2368 {
2369#ifdef PARANOID
2370 // check that the rhs vector is setup
2371 if (!rhs.distribution_pt()->built())
2372 {
2373 std::ostringstream error_message_stream;
2374 error_message_stream << "The vectors rhs must be setup";
2378 }
2379#endif
2380 // check that the rhs distribution is the same as the distribution as this
2381 // solver. If not redistribute and issue a warning
2383 if (!(*rhs.distribution_pt() == *this->distribution_pt()))
2384 {
2386 {
2387 std::ostringstream warning_stream;
2388 warning_stream << "The distribution of rhs vector does not match that "
2389 "ofthe solver.\n";
2390 warning_stream << "The rhs will be redistributed, which is likely to "
2391 "be inefficient\n";
2393 << "To remove this warning you can either:\n"
2394 << " i) Ensure that the rhs vector has the correct distribution\n"
2395 << " before calling the resolve() function\n"
2396 << "or ii) Set the flag \n"
2397 << " SuperLUSolver::Suppress_incorrect_rhs_distribution_warning_in_"
2398 "resolve\n"
2399 << " to be true\n\n";
2400
2402 "SuperLUSolver::resolve()",
2404 }
2405
2406 // Have to cast away const-ness (which tells us that we shouldn't really
2407 // be doing this!)
2408 const_cast<DoubleVector&>(rhs).redistribute(this->distribution_pt());
2409 }
2410
2411#ifdef PARANOID
2412 // if the result vector is setup then check it has the same distribution
2413 // as the rhs
2414 if (result.distribution_built())
2415 {
2416 if (!(*result.distribution_pt() == *rhs.distribution_pt()))
2417 {
2418 std::ostringstream error_message_stream;
2420 << "The result vector distribution has been setup; it must have the "
2421 << "same distribution as the rhs vector.";
2425 }
2426 }
2427#endif
2428 // Doc (0/1) = (true/false)
2429 int doc = !Doc_stats;
2430
2431 // Reset Info
2432 Dist_info = 0;
2433
2434 // number of DOFs
2435 int ndof = this->distribution_pt()->nrow();
2436
2437 // Copy the rhs values to result
2438 result = rhs;
2439
2440 // Do the backsubsitition phase
2442 {
2443 // Call distributed solver
2445 2,
2446 -1,
2447 ndof,
2448 0,
2449 0,
2450 0,
2451 0,
2452 0,
2453 0,
2454 result.values_pt(),
2455 Dist_nprow,
2456 Dist_npcol,
2457 doc,
2459 &Dist_info,
2460 this->distribution_pt()->communicator_pt()->mpi_comm());
2461 }
2463 {
2464 // Call global solver
2466 2,
2467 -1,
2468 ndof,
2469 0,
2470 0,
2471 0,
2472 0,
2473 result.values_pt(),
2474 Dist_nprow,
2475 Dist_npcol,
2476 doc,
2478 &Dist_info,
2479 this->distribution_pt()->communicator_pt()->mpi_comm());
2480 }
2481 else
2482 {
2483 throw OomphLibError("The matrix factors have not been stored",
2486 }
2487
2488 // Throw an error if superLU returned an error status in info.
2489 if (Dist_info != 0)
2490 {
2491 std::ostringstream error_msg;
2492 error_msg << "SuperLU returned the error status code " << Dist_info
2493 << " . See the SuperLU documentation for what this means.";
2494 throw OomphLibError(
2496 }
2497
2498 // Redistribute to original distribution
2499 // Have to cast away const-ness (which tells us that we shouldn't really
2500 // be doing this!)
2501 const_cast<DoubleVector&>(rhs).redistribute(&rhs_distribution);
2502 }
2503
2504 //=============================================================================
2505 /// Do the backsubstitution for SuperLU solver.
2506 /// Note - this method performs no paranoid checks - these are all performed
2507 /// in solve(...) and resolve(...)
2508 //=============================================================================
2511 {
2512 // Create an output stream
2513 std::ostringstream error_message_stream;
2514
2515 // Create the error message
2516 error_message_stream << "This function hasn't been implemented yet. If you "
2517 << "need it, implement it!" << std::endl;
2518
2519 // Throw the error message
2523 }
2524#endif
2525
2526 //================================================================
2527 /// Do the backsubstitution for SuperLU
2528 //================================================================
2531 {
2532 // Find the number of unknowns
2533 int n = rhs.nrow();
2534
2535#ifdef PARANOID
2536 // PARANOID check that this rhs distribution is setup
2537 if (!rhs.built())
2538 {
2539 std::ostringstream error_message_stream;
2540 error_message_stream << "The rhs vector distribution must be setup.";
2544 }
2545 // PARANOID check that the rhs has the right number of global rows
2546 if (static_cast<int>(Serial_n_dof) != n)
2547 {
2548 throw OomphLibError(
2549 "RHS does not have the same dimension as the linear system",
2552 }
2553 // PARANOID check that the rhs is not distributed
2554 if (rhs.distribution_pt()->distributed())
2555 {
2556 std::ostringstream error_message_stream;
2557 error_message_stream << "The rhs vector must not be distributed.";
2561 }
2562 // PARANOID check that if the result is setup it matches the distribution
2563 // of the rhs
2564 if (result.built())
2565 {
2566 if (!(*rhs.distribution_pt() == *result.distribution_pt()))
2567 {
2568 std::ostringstream error_message_stream;
2569 error_message_stream << "If the result distribution is setup then it "
2570 "must be the same as the "
2571 << "rhs distribution";
2575 }
2576 }
2577#endif
2578
2579 // copy result to rhs
2580 result = rhs;
2581
2582 // Number of RHSs
2583 int nrhs = 1;
2584
2585 // Cast the boolean flags to ints for SuperLU
2587 int doc = Doc_stats;
2588
2589 // Do the backsubsitition phase
2590 int i = 2;
2591 superlu(&i,
2592 &n,
2593 0,
2594 &nrhs,
2595 0,
2596 0,
2597 0,
2598 result.values_pt(),
2599 &n,
2600 &transpose,
2601 &doc,
2603 &Serial_info);
2604
2605 // Throw an error if superLU returned an error status in info.
2606 if (Serial_info != 0)
2607 {
2608 std::ostringstream error_msg;
2609 error_msg << "SuperLU returned the error status code " << Serial_info
2610 << " . See the SuperLU documentation for what this means.";
2611 throw OomphLibError(
2613 }
2614 }
2615
2616 //================================================================
2617 /// Do the backsubstitution for SuperLU
2618 //================================================================
2621 {
2622 // Find the number of unknowns
2623 int n = rhs.nrow();
2624
2625#ifdef PARANOID
2626 // PARANOID check that this rhs distribution is setup
2627 if (!rhs.built())
2628 {
2629 std::ostringstream error_message_stream;
2630 error_message_stream << "The rhs vector distribution must be setup.";
2634 }
2635 // PARANOID check that the rhs has the right number of global rows
2636 if (static_cast<int>(Serial_n_dof) != n)
2637 {
2638 throw OomphLibError(
2639 "RHS does not have the same dimension as the linear system",
2642 }
2643 // PARANOID check that the rhs is not distributed
2644 if (rhs.distribution_pt()->distributed())
2645 {
2646 std::ostringstream error_message_stream;
2647 error_message_stream << "The rhs vector must not be distributed.";
2651 }
2652 // PARANOID check that if the result is setup it matches the distribution
2653 // of the rhs
2654 if (result.built())
2655 {
2656 if (!(*rhs.distribution_pt() == *result.distribution_pt()))
2657 {
2658 std::ostringstream error_message_stream;
2659 error_message_stream << "If the result distribution is setup then it "
2660 "must be the same as the "
2661 << "rhs distribution";
2665 }
2666 }
2667#endif
2668
2669 // copy result to rhs
2670 result = rhs;
2671
2672 // Number of RHSs
2673 int nrhs = 1;
2674
2675 // Cast the boolean flags to ints for SuperLU
2677 int doc = Doc_stats;
2678
2679 // Do the backsubsitition phase
2680 int i = 2;
2681 superlu(&i,
2682 &n,
2683 0,
2684 &nrhs,
2685 0,
2686 0,
2687 0,
2688 result.values_pt(),
2689 &n,
2690 &transpose,
2691 &doc,
2693 &Serial_info);
2694
2695 // Throw an error if superLU returned an error status in info.
2696 if (Serial_info != 0)
2697 {
2698 std::ostringstream error_msg;
2699 error_msg << "SuperLU returned the error status code " << Serial_info
2700 << " . See the SuperLU documentation for what this means.";
2701 throw OomphLibError(
2703 }
2704 }
2705
2706 //=============================================================================
2707 /// Clean up the memory
2708 //=============================================================================
2710 {
2711 // If we have non-zero LU factors stored
2712 if (Serial_f_factors != 0)
2713 {
2714 // Clean up those factors
2715 int i = 3;
2717 superlu(&i,
2718 0,
2719 0,
2720 0,
2721 0,
2722 0,
2723 0,
2724 0,
2725 0,
2726 &transpose,
2727 0,
2729 &Serial_info);
2730
2731 // Set the F_factors to zero
2732 Serial_f_factors = 0;
2733 Serial_n_dof = 0;
2734 }
2735
2736#ifdef OOMPH_HAS_MPI
2737 // If we have non-zero LU factors stored
2738 if (Dist_solver_data_pt != 0)
2739 {
2740 // Clean up any stored solver data
2741
2742 // Doc (0/1) = (true/false)
2743 int doc = !Doc_stats;
2744
2745 // Reset Info flag
2746 Dist_info = 0;
2747
2748 // number of DOFs
2749 int ndof = this->distribution_pt()->nrow();
2750
2752 {
2754 3,
2755 -1,
2756 ndof,
2757 0,
2758 0,
2759 0,
2760 0,
2761 0,
2762 0,
2763 0,
2764 Dist_nprow,
2765 Dist_npcol,
2766 doc,
2768 &Dist_info,
2769 this->distribution_pt()->communicator_pt()->mpi_comm());
2771 }
2773 {
2775 3,
2776 -1,
2777 ndof,
2778 0,
2779 0,
2780 0,
2781 0,
2782 0,
2783 Dist_nprow,
2784 Dist_npcol,
2785 doc,
2787 &Dist_info,
2788 this->distribution_pt()->communicator_pt()->mpi_comm());
2790 }
2791
2793
2794 // Delete internal copy of the matrix
2795 delete[] Dist_value_pt;
2796 delete[] Dist_index_pt;
2797 delete[] Dist_start_pt;
2798 Dist_value_pt = 0;
2799 Dist_index_pt = 0;
2800 Dist_start_pt = 0;
2801
2802 // and the distribution
2803 this->clear_distribution();
2804 }
2805#endif
2806 }
2807
2808
2809 /////////////////////////////////////////////////////////////////////////
2810 /////////////////////////////////////////////////////////////////////////
2811 /////////////////////////////////////////////////////////////////////////
2812
2813
2814 /// Namespace containing functions required to create exact preconditioner
2815 namespace ExactPreconditionerFactory
2816 {
2817
2818 /// Factory function to create suitable exact preconditioner
2820 {
2821#if defined(OOMPH_HAS_MUMPS) && \
2822 defined(OOMPH_ENABLE_MUMPS_AS_DEFAULT_LINEAR_SOLVER)
2823 return new MumpsPreconditioner;
2824#else
2825 return new SuperLUPreconditioner;
2826#endif
2827 }
2828
2829 } // namespace ExactPreconditionerFactory
2830
2831
2832} // namespace oomph
cstr elem_len * i
Definition cfortran.h:603
A class for compressed column matrices that store doubles.
Definition matrices.h:2791
A class for compressed row matrices. This is a distributable object.
Definition matrices.h:888
Class of matrices containing doubles, and stored as a DenseMatrix<double>, but with solving functiona...
Definition matrices.h:1271
double Jacobian_setup_time
Jacobian setup time.
void backsub(const DoubleVector &rhs, DoubleVector &result)
Do the backsubstitution step to solve the system LU result = rhs.
void clean_up_memory()
Clean up the stored LU factors.
long * Index
Pointer to storage for the index of permutations in the LU solve.
void factorise(DoubleMatrixBase *const &matrix_pt)
Perform the LU decomposition of the matrix.
double * LU_factors
Pointer to storage for the LU decomposition.
double Solution_time
Solution time.
void solve(Problem *const &problem_pt, DoubleVector &result)
Solver: Takes pointer to problem and returns the results Vector which contains the solution of the li...
int Sign_of_determinant_of_matrix
Sign of the determinant of the matrix (obtained during the LU decomposition)
Base class for any linear algebra object that is distributable. Just contains storage for the LinearA...
void clear_distribution()
clear the distribution of this distributable linear algebra object
bool distributed() const
distribution is serial or distributed
unsigned nrow() const
access function to the number of global rows.
bool distribution_built() const
if the communicator_pt is null then the distribution is not setup then false is returned,...
unsigned nrow_local() const
access function for the num of local rows on this processor.
unsigned first_row() const
access function for the first row on this processor
LinearAlgebraDistribution * distribution_pt() const
access to the LinearAlgebraDistribution
void build_distribution(const LinearAlgebraDistribution *const dist_pt)
setup the distribution of this distributable linear algebra object
Abstract base class for matrices of doubles – adds abstract interfaces for solving,...
Definition matrices.h:261
virtual unsigned long ncol() const =0
Return the number of columns of the matrix.
virtual unsigned long nrow() const =0
Return the number of rows of the matrix.
A vector in the mathematical sense, initially developed for linear algebra type applications....
void solve(Problem *const &problem_pt, DoubleVector &result)
Solver: Takes pointer to problem and returns the results Vector which contains the solution of the li...
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
Describes the distribution of a distributable linear algebra type object. Typically this is a contain...
OomphCommunicator * communicator_pt() const
const access to the communicator pointer
unsigned nrow() const
access function to the number of global rows.
bool Doc_time
Boolean flag that indicates whether the time taken.
bool Enable_resolve
Boolean that indicates whether the matrix (or its factors, in the case of direct solver) should be st...
bool Gradient_has_been_computed
flag that indicates whether the gradient was computed or not
DoubleVector Gradient_for_glob_conv_newton_solve
DoubleVector storing the gradient for the globally convergent Newton method.
bool Compute_gradient
flag that indicates whether the gradient required for the globally convergent Newton method should be...
static bool mpi_has_been_initialised()
return true if MPI has been initialised
static OomphCommunicator * communicator_pt()
access to global communicator. This is the oomph-lib equivalent of MPI_COMM_WORLD
An interface to allow Mumps to be used as an (exact) Preconditioner.
An oomph-lib wrapper to the MPI_Comm communicator object. Just contains an MPI_Comm object (which is ...
An OomphLibError object which should be thrown when an run-time error is encountered....
An OomphLibWarning object which should be created as a temporary object to issue a warning....
Preconditioner base class. Gives an interface to call all other preconditioners through and stores th...
////////////////////////////////////////////////////////////////// //////////////////////////////////...
Definition problem.h:154
unsigned long ndof() const
Return the number of dofs.
Definition problem.h:1704
OomphCommunicator * communicator_pt()
access function to the oomph-lib communicator
Definition problem.h:1266
virtual void get_jacobian(DoubleVector &residuals, DenseDoubleMatrix &jacobian)
Return the fully-assembled Jacobian and residuals for the problem Interface for the case when the Jac...
Definition problem.cc:3935
void get_fd_jacobian(DoubleVector &residuals, DenseMatrix< double > &jacobian)
Return the fully-assembled Jacobian and residuals, generated by finite differences.
Definition problem.cc:7814
int & sign_of_jacobian()
Access function for the sign of the global jacobian matrix. This will be set by the linear solver,...
Definition problem.h:2529
An interface to allow SuperLU to be used as an (exact) Preconditioner.
bool Doc_stats
Set to true to output statistics (false by default).
bool Dist_delete_matrix_data
Delete_matrix_data flag. SuperLU_dist needs its own copy of the input matrix, therefore a copy must b...
void solve_transpose(Problem *const &problem_pt, DoubleVector &result)
Solver: Takes pointer to problem and returns the results Vector which contains the solution of the li...
static bool Suppress_incorrect_rhs_distribution_warning_in_resolve
Static flag that determines whether the warning about incorrect distribution of RHSs will be printed ...
int * Dist_index_pt
Pointer for storage of matrix rows or column indices required by SuperLU_DIST.
Type Solver_type
the solver type. see SuperLU_solver_type for details.
void backsub_serial(const DoubleVector &rhs, DoubleVector &result)
backsub method for SuperLU (serial)
void backsub_transpose_distributed(const DoubleVector &rhs, DoubleVector &result)
backsub method for SuperLU Dist
double Solution_time
Solution time.
double * Dist_value_pt
Pointer for storage of the matrix values required by SuperLU_DIST.
bool Serial_compressed_row_flag
Use compressed row version?
void solve(Problem *const &problem_pt, DoubleVector &result)
Solver: Takes pointer to problem and returns the results Vector which contains the solution of the li...
void factorise(DoubleMatrixBase *const &matrix_pt)
Do the factorisation stage Note: if Delete_matrix_data is true the function matrix_pt->clean_up_memor...
void factorise_serial(DoubleMatrixBase *const &matrix_pt)
factorise method for SuperLU (serial)
void backsub_distributed(const DoubleVector &rhs, DoubleVector &result)
backsub method for SuperLU Dist
bool Using_dist
boolean flag indicating whether superlu dist is being used
int * Dist_start_pt
Pointers for storage of matrix column or row starts.
unsigned long Serial_n_dof
The number of unknowns in the linear system.
void * Serial_f_factors
Storage for the LU factors as required by SuperLU.
void resolve_transpose(const DoubleVector &rhs, DoubleVector &result)
Resolve the (transposed) system defined by the last assembled Jacobian and the specified rhs vector i...
bool Suppress_solve
Suppress solve?
int Serial_sign_of_determinant_of_matrix
Sign of the determinant of the matrix.
bool Dist_use_global_solver
Flag that determines whether the MPIProblem based solve function uses the global or distributed versi...
double get_memory_usage_for_lu_factors()
How much memory do the LU factors take up? In bytes.
void factorise_distributed(DoubleMatrixBase *const &matrix_pt)
factorise method for SuperLU Dist
bool Dist_allow_row_and_col_permutations
If true then SuperLU_DIST is allowed to permute matrix rows and columns during factorisation....
int Dist_info
Info flag for the SuperLU solver.
bool Dist_global_solve_data_allocated
Flag is true if solve data has been generated for a global matrix.
double get_total_needed_memory()
How much memory was allocated by SuperLU? In bytes.
double Jacobian_setup_time
Jacobian setup time.
void backsub_transpose_serial(const DoubleVector &rhs, DoubleVector &result)
backsub method for SuperLU (serial)
void resolve(const DoubleVector &rhs, DoubleVector &result)
Resolve the system defined by the last assembled jacobian and the specified rhs vector if resolve has...
void * Dist_solver_data_pt
Storage for the LU factors and other data required by SuperLU.
bool Dist_distributed_solve_data_allocated
Flag is true if solve data has been generated for distributed matrix.
int Dist_npcol
Number of columns for the process grid.
void backsub(const DoubleVector &rhs, DoubleVector &result)
Do the backsubstitution for SuperLU solver Note: returns the global result Vector.
void clean_up_memory()
Clean up the memory allocated by the solver.
int Dist_nprow
Number of rows for the process grid.
void backsub_transpose(const DoubleVector &rhs, DoubleVector &result)
Do the back-substitution for transposed system of the SuperLU solver Note: Returns the global result ...
int Serial_info
Info flag for the SuperLU solver.
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
Preconditioner * create_exact_preconditioner()
Factory function to create suitable exact preconditioner.
unsigned Number
The unsigned.
std::string to_string(T object, unsigned float_precision=8)
Conversion function that should work for anything with operator<< defined (at least all basic types).
double timer()
returns the time in seconds after some point in past
std::string convert_secs_to_formatted_string(const double &time_in_sec)
Returns a nicely formatted string from an input time in seconds; the format depends on the size of ti...
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
double get_total_memory_usage_in_bytes()
Function to calculate the number of bytes used in calculating and storing the LU factors.
double get_lu_factor_memory_usage_in_bytes_dist()
Function to calculate the number of bytes used to store the LU factors.
double get_total_memory_usage_in_bytes_dist()
Function to calculate the number of bytes used in calculating and storing the LU factors.
void superlu_dist_global_matrix(int opt_flag, int allow_permutations, int n, int nnz, double *values, int *row_index, int *col_start, double *b, int nprow, int npcol, int doc, void **data, int *info, MPI_Comm comm)
void superlu_cr_to_cc(int nrow, int ncol, int nnz, double *cr_values, int *cr_index, int *cr_start, double **cc_values, int **cc_index, int **cc_start)
int superlu(int *, int *, int *, int *, double *, int *, int *, double *, int *, int *, int *, void *, int *)
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...
double get_lu_factor_memory_usage_in_bytes()
Function to calculate the number of bytes used to store the LU factors.
void superlu_dist_distributed_matrix(int opt_flag, int allow_permutations, int n, int nnz_local, int nrow_local, int first_row, double *values, int *col_index, int *row_start, double *b, int nprow, int npcol, int doc, void **data, int *info, MPI_Comm comm)