diff --git a/applications/test/QRMatrix/Make/files b/applications/test/QRMatrix/Make/files
new file mode 100644
index 0000000000..9825bbfcf3
--- /dev/null
+++ b/applications/test/QRMatrix/Make/files
@@ -0,0 +1,3 @@
+Test-QRMatrix.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-QRMatrix
diff --git a/applications/test/QRMatrix/Make/options b/applications/test/QRMatrix/Make/options
new file mode 100644
index 0000000000..4e772fdf9d
--- /dev/null
+++ b/applications/test/QRMatrix/Make/options
@@ -0,0 +1,2 @@
+/* EXE_INC = -I$(LIB_SRC)/finiteVolume/lnInclude */
+/* EXE_LIBS = -lfiniteVolume */
diff --git a/applications/test/QRMatrix/Test-QRMatrix.C b/applications/test/QRMatrix/Test-QRMatrix.C
new file mode 100644
index 0000000000..fb22d78bde
--- /dev/null
+++ b/applications/test/QRMatrix/Test-QRMatrix.C
@@ -0,0 +1,1484 @@
+/*---------------------------------------------------------------------------*\
+ ========= |
+ \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
+ \\ / O peration |
+ \\ / A nd | www.openfoam.com
+ \\/ M anipulation |
+-------------------------------------------------------------------------------
+ Copyright (C) 2019 OpenCFD Ltd.
+-------------------------------------------------------------------------------
+License
+ This file is part of OpenFOAM.
+
+ OpenFOAM is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with OpenFOAM. If not, see .
+
+\*---------------------------------------------------------------------------*/
+
+#include "MatrixTools.H"
+#include "QRMatrix.H"
+#include "Random.H"
+#include "IOmanip.H"
+
+using namespace Foam;
+using namespace Foam::MatrixTools;
+
+#define equal MatrixTools::equal
+#define RUNALL true
+const bool verbose = true;
+
+
+// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
+
+void horizontalLine()
+{
+ Info<< "+---------+---------+---------+---------+---------+" << nl;
+}
+
+
+// Create random scalar-type matrix
+template
+typename std::enable_if
+<
+ !std::is_same::value,
+ MatrixType
+>::type makeRandomMatrix
+(
+ const labelPair& dims,
+ Random& rndGen
+)
+{
+ MatrixType mat(dims);
+
+ std::generate
+ (
+ mat.begin(),
+ mat.end(),
+ [&]{ return rndGen.GaussNormal(); }
+ );
+
+ return mat;
+}
+
+
+// Create random complex-type matrix
+template
+typename std::enable_if
+<
+ std::is_same::value,
+ MatrixType
+>::type makeRandomMatrix
+(
+ const labelPair& dims,
+ Random& rndGen
+)
+{
+ MatrixType mat(dims);
+
+ std::generate
+ (
+ mat.begin(),
+ mat.end(),
+ [&]
+ {
+ return complex
+ (
+ rndGen.GaussNormal(),
+ rndGen.GaussNormal()
+ );
+ }
+ );
+
+ return mat;
+}
+
+
+// Print OpenFOAM matrix in NumPy format
+template
+void InfoNumPyFormat
+(
+ const MatrixType& mat,
+ const word objName
+)
+{
+ Info<< objName << ": " << mat.m() << "x" << mat.n() << nl;
+ for (label m = 0; m < mat.m(); ++m)
+ {
+ Info<< "[";
+ for (label n = 0; n < mat.n(); ++n)
+ {
+ if (n == mat.n() - 1)
+ {
+ Info<< mat(m,n);
+ }
+ else
+ {
+ Info<< mat(m,n) << ",";
+ }
+ }
+ if (m == mat.m() - 1)
+ {
+ Info << "]" << nl;
+ }
+ else
+ {
+ Info << "]," << nl;
+ }
+ }
+}
+
+
+// Returns true if two scalars are equal within a given tolerance, and v.v.
+bool isEqual
+(
+ const scalar a,
+ const scalar b,
+ const bool verbose = false,
+ const scalar relTol = 1e-5,
+ const scalar absTol = 1e-8
+)
+{
+ if ((absTol + relTol*mag(b)) < mag(a - b))
+ {
+ if (verbose)
+ {
+ Info<< "Elements are not close in terms of tolerances:"
+ << nl << a << tab << b << nl;
+ }
+ return false;
+ }
+
+ if (verbose)
+ {
+ Info<< "All elems are the same within the tolerances" << nl;
+ }
+
+ return true;
+}
+
+
+// Prints true if a given matrix is empty, and v.v.
+template
+void isEmpty
+(
+ const MatrixType& A,
+ const word objName
+)
+{
+ Info<< "Empty " << objName << " = ";
+ if (A.empty())
+ {
+ Info<< "true" << nl;
+ }
+ else
+ {
+ Info<< "false" << nl;
+ }
+}
+
+
+// Checks if Q matrix is a unitary matrix
+template
+void cross_check_QRMatrix
+(
+ const MatrixType& Aorig,
+ const MatrixType& Q
+)
+{
+ InfoNumPyFormat(Q, "Q");
+
+ // mathworld.wolfram.com/UnitaryMatrix.html (Retrieved:18-06-19)
+ Info<< nl << "# Q.T()*Q = Q*Q.T() = I:" << nl;
+ const MatrixType QTQ(Q&Q);
+ const MatrixType QQT(Q^Q);
+ MatrixType IMatrix({Q.m(), Q.n()}, Zero);
+ for (label i = 0; i < IMatrix.n(); ++i)
+ {
+ IMatrix(i,i) = pTraits::one;
+ }
+ equal(QTQ, IMatrix, verbose);
+ equal(QQT, IMatrix, verbose);
+ equal(QTQ, QQT, verbose);
+}
+
+
+// Checks if R matrix is an upper-triangular matrix
+template
+void cross_check_QRMatrix
+(
+ const MatrixType& R
+)
+{
+ InfoNumPyFormat(R, "R");
+
+ // mathworld.wolfram.com/UpperTriangularMatrix.html (Retrieved:18-06-19)
+ Info<< nl << "# R(i, j) = 0 for i > j:" << nl;
+ for (label i = 0; i < R.m(); ++i)
+ {
+ for (label j = 0; j < R.n(); ++j)
+ {
+ if (j < i)
+ {
+ isEqual(mag(R(i, j)), 0.0, verbose);
+ }
+ }
+ }
+}
+
+
+// Checks if the given matrix can be reconstructed by Q and R matrices
+template
+void cross_check_QRMatrix
+(
+ const MatrixType& Aorig,
+ const MatrixType& Q,
+ const MatrixType& R
+)
+{
+ InfoNumPyFormat(Aorig, "Aorig");
+
+ cross_check_QRMatrix(Aorig, Q);
+
+ cross_check_QRMatrix(R);
+
+ // mathworld.wolfram.com/QRDecomposition.html (Retrieved:18-06-19)
+ Info<< nl << "# Q*R = A:" << nl;
+ const MatrixType AReconstruct(Q*R);
+ equal(Aorig, AReconstruct, verbose);
+}
+
+
+// Checks if R matrix is an upper-triangular matrix, and obeys column pivoting
+template
+void cross_check_QRMatrix
+(
+ const MatrixType& Aorig,
+ const labelList& orderP,
+ const SquareMatrix& P,
+ const MatrixType& R
+)
+{
+ InfoNumPyFormat(Aorig, "Aorig");
+
+ InfoNumPyFormat(P, "P");
+
+ cross_check_QRMatrix(R);
+
+ // w.wiki/54m (Retrieved:20-06-19)
+ Info<< nl << "# Diag elems of R non-increasing:"
+ << "|R11| >= |R22| >= ... >= |Rnn|:" << nl;
+ const List diag0(mag(R.diag()));
+ List diag1(diag0);
+ Foam::sort(diag1, std::greater());
+ forAll(diag0, i)
+ {
+ isEqual(diag0[i], diag1[i], verbose);
+ }
+}
+
+
+// Checks if the given matrix can be reconstructed by column-pivoted Q and R
+template
+void cross_check_QRMatrix
+(
+ const MatrixType& Aorig,
+ const MatrixType& Q,
+ const labelList& orderP,
+ const SquareMatrix& P,
+ const MatrixType& R
+)
+{
+ cross_check_QRMatrix(Aorig, orderP, P, R);
+ cross_check_QRMatrix(Aorig, Q);
+
+ // w.wiki/54m (Retrieved:20-06-19)
+ Info<< nl << "# Q*R = A*P:" << nl;
+ const MatrixType AReconstruct(Q*R);
+ const MatrixType AP(Aorig*P);
+ equal(AP, AReconstruct, verbose);
+}
+
+
+// Checks each constructor of QRMatrix type
+template
+void verification_QRMatrix
+(
+ MatrixType& A
+)
+{
+ typedef SquareMatrix SMatrix;
+
+ // Create working copies of matrix A
+ const MatrixType Aorig(A);
+
+ // QRMatrix Constructors
+ #if (0 | RUNALL)
+ {
+ QRMatrix QRNull();
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# FULL_R | OUT_OF_PLACE" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::storeMethods::OUT_OF_PLACE
+ );
+ QRM.decompose(A);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# FULL_R | IN_PLACE" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::storeMethods::IN_PLACE
+ );
+ MatrixType A0(A);
+ QRM.decompose(A0);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ isEmpty(R, "R");
+ cross_check_QRMatrix(A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# FULL_QR | OUT_OF_PLACE" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::OUT_OF_PLACE
+ );
+ QRM.decompose(A);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ cross_check_QRMatrix(Aorig, Q, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# FULL_QR | IN_PLACE" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::IN_PLACE
+ );
+ MatrixType A0(A);
+ QRM.decompose(A0);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(R, "R");
+ cross_check_QRMatrix(Aorig, Q, A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# FULL_R | OUT_OF_PLACE | colPivot = on" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ QRM.decompose(A);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(Aorig, PList, P, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# FULL_R | IN_PLACE | colPivot = on" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ MatrixType A0(A);
+ QRM.decompose(A0);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ isEmpty(R, "R");
+ cross_check_QRMatrix(Aorig, PList, P, A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# FULL_QR | OUT_OF_PLACE | colPivot = on" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ QRM.decompose(A);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ cross_check_QRMatrix(Aorig, Q, PList, P, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# FULL_QR | IN_PLACE | colPivot = on" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ MatrixType A0(A);
+ QRM.decompose(A0);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(R, "R");
+ cross_check_QRMatrix(Aorig, Q, PList, P, A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | FULL_R | OUT_OF_PLACE | colPivot = off" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | FULL_R | IN_PLACE | colPivot = off" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ isEmpty(R, "R");
+ cross_check_QRMatrix(A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | FULL_QR | OUT_OF_PLACE | colPivot = off" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ cross_check_QRMatrix(Aorig, Q, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | FULL_QR | IN_PLACE | colPivot = off" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(R, "R");
+ cross_check_QRMatrix(Aorig, Q, A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | FULL_R | OUT_OF_PLACE | colPivot = on" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(Aorig, PList, P, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | FULL_R | IN_PLACE | colPivot = on" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ isEmpty(R, "R");
+ cross_check_QRMatrix(Aorig, PList, P, A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | FULL_QR | OUT_OF_PLACE | colPivot = on" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ cross_check_QRMatrix(Aorig, Q, PList, P, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | FULL_QR | IN_PLACE | colPivot = on" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(R, "R");
+ cross_check_QRMatrix(Aorig, Q, PList, P, A0);
+ }
+ #endif
+
+ // constructors with the const type specifier
+ #if (0 | RUNALL)
+ {
+ Info<< "# const A | FULL_R | colPivot = off" << nl;
+ const MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# const A | FULL_QR | colPivot = off" << nl;
+ const MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ cross_check_QRMatrix(Aorig, Q, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# const A | FULL_R | colPivot = on" << nl;
+ const MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_R,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(Aorig, PList, P, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# const A | FULL_QR | colPivot = on" << nl;
+ const MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ cross_check_QRMatrix(Aorig, Q, PList, P, R);
+ }
+ #endif
+
+ //
+ #if (0 | RUNALL)
+ {
+ Info<< "# REDUCED_R | OUT_OF_PLACE" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::storeMethods::OUT_OF_PLACE
+ );
+ QRM.decompose(A);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(R);
+
+ // check if full R and reduced R give the same answer
+ if (A.n() < A.m())
+ {
+ QRMatrix fullQRM
+ (
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::OUT_OF_PLACE
+ );
+ fullQRM.decompose(A);
+ const MatrixType& fullR(fullQRM.R());
+ MatrixType fullR2(fullR.subMatrix(0,0,R.m()));
+ equal(fullR2, R, verbose);
+ }
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# REDUCED_R | IN_PLACE" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::storeMethods::IN_PLACE
+ );
+ MatrixType A0(A);
+ QRM.decompose(A0);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ isEmpty(R, "R");
+ cross_check_QRMatrix(A0);
+
+ // check if full R and reduced R give the same answer
+ if (A.n() < A.m())
+ {
+ QRMatrix fullQRM
+ (
+ QRMatrix::outputTypes::FULL_QR,
+ QRMatrix::storeMethods::IN_PLACE
+ );
+ MatrixType A1(A);
+ fullQRM.decompose(A1);
+ MatrixType fullR(A1.subMatrix(0,0,A0.m()));
+ equal(fullR, A0, verbose);
+ }
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# REDUCED_R | OUT_OF_PLACE | colPivot = on" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ QRM.decompose(A);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(Aorig, PList, P, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# REDUCED_R | IN_PLACE | colPivot = on" << nl;
+ QRMatrix QRM
+ (
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ MatrixType A0(A);
+ QRM.decompose(A0);
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ isEmpty(R, "R");
+ cross_check_QRMatrix(Aorig, PList, P, A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | REDUCED_R | OUT_OF_PLACE | colPivot = off" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | REDUCED_R | IN_PLACE | colPivot = off" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ isEmpty(R, "R");
+ cross_check_QRMatrix(A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | REDUCED_R | OUT_OF_PLACE | colPivot = on" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::storeMethods::OUT_OF_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(Aorig, PList, P, R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# A | REDUCED_R | IN_PLACE | colPivot = on" << nl;
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::storeMethods::IN_PLACE,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ isEmpty(R, "R");
+ cross_check_QRMatrix(Aorig, PList, P, A0);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# const A | REDUCED_R | colPivot = off" << nl;
+ const MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::colPivoting::FALSE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(R);
+ }
+ #endif
+
+ #if (0 | RUNALL)
+ {
+ Info<< "# const A | REDUCED_R | colPivot = on" << nl;
+ const MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::REDUCED_R,
+ QRMatrix::colPivoting::TRUE
+ );
+ const MatrixType& Q(QRM.Q());
+ const MatrixType& R(QRM.R());
+ const labelList& PList(QRM.orderP());
+ const SMatrix P(QRM.P());
+ isEmpty(Q, "Q");
+ cross_check_QRMatrix(Aorig, PList, P, R);
+ }
+ #endif
+}
+
+
+// Checks the direct solution of a linear system by QRMatrix
+template
+void verification_QRMatrixSolve
+(
+ MatrixType& A
+)
+{
+ typedef SquareMatrix SMatrix;
+
+ // Create working copies of matrix A
+ const MatrixType Aorig(A);
+
+ // Direct solution of a linear system by QR decomposition
+ #if (0 | RUNALL)
+ {
+ MatrixType A0(A);
+ QRMatrix QRM
+ (
+ A0,
+ QRMatrix::outputTypes::FULL_QR
+ );
+
+ Info<< nl << "# Solution of A*x = b with A = Q*R:" << nl;
+ const scalarField residual(A0.m(), 0);
+ const scalarField source(A0.m(), 1);
+
+ const scalarField x(QRM.solve(source));
+ const scalarField A0xMinusSource(A0*x - source);
+ const scalarField xMinusQRinvSource(x - QRM.inv()*source);
+ const SMatrix QRinvA0(QRM.inv()*A0);
+ const SMatrix identity(A0.m(), I);
+
+ forAll(residual, i)
+ {
+ isEqual(A0xMinusSource[i], residual[i], verbose);
+ isEqual(xMinusQRinvSource[i], residual[i], verbose);
+ }
+ equal(QRinvA0, identity, verbose);
+ }
+ #endif
+}
+
+
+// Checks the parallel tall-skinny QR decomposition
+template
+void verification_tsqr
+(
+ Random& rndGen
+)
+{
+ typedef RectangularMatrix RMatrix;
+
+ // Size of the full matrix and its partitions
+ const label nColsSum = rndGen.position(1, 100);
+ const label qParts = rndGen.position(10, 30);
+ List