diff --git a/applications/test/Matrix/Test-Matrix.C b/applications/test/Matrix/Test-Matrix.C index c372cf9b9a7a03ffc137bb2419b9bcb7d86d57ce..1cbc60254538df916b33a17d12bb5a579795fe18 100644 --- a/applications/test/Matrix/Test-Matrix.C +++ b/applications/test/Matrix/Test-Matrix.C @@ -37,6 +37,7 @@ License using namespace Foam; using namespace Foam::MatrixTools; +#define RUNALL true #define isEqual MatrixTools::equal void horizontalLine() @@ -113,7 +114,7 @@ int main(int argc, char *argv[]) Random rndGen(1234); - #if 1 + #if (0 | RUNALL) { horizontalLine(); @@ -218,7 +219,7 @@ int main(int argc, char *argv[]) #endif - #if 1 + #if (0 | RUNALL) { horizontalLine(); @@ -254,7 +255,7 @@ int main(int argc, char *argv[]) #endif - #if 1 + #if (0 | RUNALL) { horizontalLine(); @@ -299,7 +300,7 @@ int main(int argc, char *argv[]) #endif - #if 1 + #if (0 | RUNALL) { horizontalLine(); @@ -410,7 +411,7 @@ int main(int argc, char *argv[]) #endif - #if 1 + #if (0 | RUNALL) { horizontalLine(); @@ -494,7 +495,7 @@ int main(int argc, char *argv[]) #endif - #if 1 + #if (0 | RUNALL) { horizontalLine(); @@ -683,7 +684,7 @@ int main(int argc, char *argv[]) #endif - #if 1 + #if (0 | RUNALL) { horizontalLine(); @@ -804,7 +805,7 @@ int main(int argc, char *argv[]) << nl; } - #if 1 + #if (0 | RUNALL) { Info<< nl << "# Implicit inner/outer products:" << nl; @@ -860,7 +861,65 @@ int main(int argc, char *argv[]) #endif - #if 1 + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "## Query to determine if a square matrix is symmetric:" << nl; + + { + const label mRows = 100; + SMatrix A(makeRandomMatrix<SMatrix>({mRows, mRows}, rndGen)); + + Info<< "# SquareMatrix<scalar>.symmetric():" << nl + << A.symmetric() << nl; + + // Symmetrise + for (label n = 0; n < A.n() - 1; ++n) + { + for (label m = A.m() - 1; n < m; --m) + { + A(n, m) = A(m, n); + } + } + + Info<< "# SquareMatrix<scalar>.symmetric():" << nl + << A.symmetric() << nl; + } + + { + const label mRows = 100; + + SCMatrix A(mRows); + + for (auto& val : A) + { + val.Re() = rndGen.GaussNormal<scalar>(); + val.Im() = rndGen.GaussNormal<scalar>(); + } + + Info<< "# SquareMatrix<complex>.symmetric():" << nl + << A.symmetric() << nl; + + // Symmetrise + for (label n = 0; n < A.n() - 1; ++n) + { + for (label m = A.m() - 1; n < m; --m) + { + A(n, m) = A(m, n); + } + } + + Info<< "# SquareMatrix<complex>.symmetric():" << nl + << A.symmetric() << nl; + } + + horizontalLine(); + } + #endif + + + #if (0 | RUNALL) { horizontalLine(); @@ -898,7 +957,7 @@ int main(int argc, char *argv[]) #endif - #if 1 + #if (0 | RUNALL) { scalarSquareMatrix squareMatrix(3, Zero); diff --git a/applications/test/QRMatrix/Make/files b/applications/test/QRMatrix/Make/files new file mode 100644 index 0000000000000000000000000000000000000000..9825bbfcf351043bd03d02a5d50dd924e73a1d41 --- /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 0000000000000000000000000000000000000000..4e772fdf9d7bc94221d127458f9d2ca32850fe69 --- /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 0000000000000000000000000000000000000000..fb22d78bde3f9c8316a2a51485fdd9d61a5daaf7 --- /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 <http://www.gnu.org/licenses/>. + +\*---------------------------------------------------------------------------*/ + +#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<class MatrixType> +typename std::enable_if +< + !std::is_same<complex, typename MatrixType::cmptType>::value, + MatrixType +>::type makeRandomMatrix +( + const labelPair& dims, + Random& rndGen +) +{ + MatrixType mat(dims); + + std::generate + ( + mat.begin(), + mat.end(), + [&]{ return rndGen.GaussNormal<typename MatrixType::cmptType>(); } + ); + + return mat; +} + + +// Create random complex-type matrix +template<class MatrixType> +typename std::enable_if +< + std::is_same<complex, typename MatrixType::cmptType>::value, + MatrixType +>::type makeRandomMatrix +( + const labelPair& dims, + Random& rndGen +) +{ + MatrixType mat(dims); + + std::generate + ( + mat.begin(), + mat.end(), + [&] + { + return complex + ( + rndGen.GaussNormal<scalar>(), + rndGen.GaussNormal<scalar>() + ); + } + ); + + return mat; +} + + +// Print OpenFOAM matrix in NumPy format +template<class MatrixType> +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<class MatrixType> +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<class MatrixType> +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<typename MatrixType::cmptType>::one; + } + equal(QTQ, IMatrix, verbose); + equal(QQT, IMatrix, verbose); + equal(QTQ, QQT, verbose); +} + + +// Checks if R matrix is an upper-triangular matrix +template<class MatrixType> +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<class MatrixType> +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<class MatrixType> +void cross_check_QRMatrix +( + const MatrixType& Aorig, + const labelList& orderP, + const SquareMatrix<typename MatrixType::cmptType>& 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<scalar> diag0(mag(R.diag())); + List<scalar> diag1(diag0); + Foam::sort(diag1, std::greater<scalar>()); + forAll(diag0, i) + { + isEqual(diag0[i], diag1[i], verbose); + } +} + + +// Checks if the given matrix can be reconstructed by column-pivoted Q and R +template<class MatrixType> +void cross_check_QRMatrix +( + const MatrixType& Aorig, + const MatrixType& Q, + const labelList& orderP, + const SquareMatrix<typename MatrixType::cmptType>& 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<class MatrixType> +void verification_QRMatrix +( + MatrixType& A +) +{ + typedef SquareMatrix<typename MatrixType::cmptType> SMatrix; + + // Create working copies of matrix A + const MatrixType Aorig(A); + + // QRMatrix Constructors + #if (0 | RUNALL) + { + QRMatrix<MatrixType> QRNull(); + } + #endif + + #if (0 | RUNALL) + { + Info<< "# FULL_R | OUT_OF_PLACE" << nl; + QRMatrix<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_R, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::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<MatrixType> fullQRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::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<MatrixType> fullQRM + ( + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::storeMethods::OUT_OF_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::storeMethods::IN_PLACE, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::outputTypes::REDUCED_R, + QRMatrix<MatrixType>::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<class MatrixType> +void verification_QRMatrixSolve +( + MatrixType& A +) +{ + typedef SquareMatrix<typename MatrixType::cmptType> 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<MatrixType> QRM + ( + A0, + QRMatrix<MatrixType>::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<class Type> +void verification_tsqr +( + Random& rndGen +) +{ + typedef RectangularMatrix<Type> RMatrix; + + // Size of the full matrix and its partitions + const label nColsSum = rndGen.position(1, 100); + const label qParts = rndGen.position(10, 30); + List<label> mRowsList(qParts, Zero); + + label mRowsSum = 0; + for (label i = 0; i < qParts; ++i) + { + const label mRows = rndGen.position(nColsSum, 10*nColsSum); + mRowsList[i] = mRows; + mRowsSum += mRows; + } + + # if 0 + Info<< "mRowsSum = " << mRowsSum << nl; + Info<< "nColsSum = " << nColsSum << nl; + Info<< "mRowsList = " << mRowsList << nl; + #endif + + if (mRowsSum < nColsSum) + { + FatalErrorInFunction + << "Tall skinny QR decomposition cannot be executed for matrices " + << "with number of columns (nCols) = " << nColsSum << " > " + << "than number of rows (mRows) = " << mRowsSum << "." + << "Yet nCols > mRows is allowed for a submatrix." + << exit(FatalError); + } + + // Perform the QR decomposition for the full matrix + const RMatrix A(makeRandomMatrix<RMatrix>({mRowsSum, nColsSum}, rndGen)); + QRMatrix<RMatrix> QRM + ( + A, + QRMatrix<RMatrix>::outputTypes::REDUCED_R, + QRMatrix<RMatrix>::colPivoting::FALSE + ); + const RMatrix& R = QRM.R(); + + RMatrix RMag(R); + for (auto& val : RMag) + { + val = mag(val); + } + + // Partition the full matrix, + // and perform the QR decomposition on each partition + RMatrix subRs({qParts*nColsSum, nColsSum}, Zero); + label mRowsIndex = 0; + for (label i = 0; i < qParts; ++i) + { + RMatrix subA({mRowsList[i], nColsSum}, Zero); + subA = A.subMatrix(mRowsIndex, 0, mRowsList[i]); + mRowsIndex += mRowsList[i]; + + QRMatrix<RMatrix> subQRM + ( + subA, + QRMatrix<RMatrix>::outputTypes::REDUCED_R, + QRMatrix<RMatrix>::colPivoting::FALSE + ); + const RMatrix& subR = subQRM.R(); + + // Append subR + subRs.subMatrix(i*nColsSum, 0, nColsSum) = subR; + } + + // Perform the tall-skinny QR decomposition + QRMatrix<RMatrix> TSQR + ( + subRs, + QRMatrix<RMatrix>::outputTypes::REDUCED_R, + QRMatrix<RMatrix>::colPivoting::FALSE + ); + const RMatrix& TSQRR = TSQR.R(); + + RMatrix TSQRRMag(TSQRR); + for (auto& val : TSQRRMag) + { + val = mag(val); + } + + // Compare magnitude of R, since R is not unique up to the sign + equal(RMag, TSQRRMag, verbose); + + #if 0 + InfoNumPyFormat(R, "R"); + InfoNumPyFormat(TSQRR, "TSQRR"); + #endif +} + + +// Checks the back substitution function +template<class Type> +void verification_backSubstitution +( + const SquareMatrix<Type>& A, + const RectangularMatrix<Type>& b +) +{ + // Ax = b + typedef RectangularMatrix<Type> RMatrix; + + QRMatrix<RMatrix> QRNull; + const RMatrix X(QRNull.backSubstitution(A, b)); + + #if 1 + { + InfoNumPyFormat(A, "A"); + InfoNumPyFormat(b, "b"); + InfoNumPyFormat(X, "x"); + } + #endif + + #if (0 | RUNALL) + { + Info<< nl << "# A*X = b:" << nl; + const RMatrix AX(A*X); + equal(AX, b, verbose, 1e-3, 1e-3); + } + #endif +} + + +// Checks the Moore-Penrose pseudo-inverse function +template<class MatrixType> +void verification_pinv +( + const MatrixType& A +) +{ + Info<< "### Moore-Penrose pseudo-inverse: pinv()" << nl << nl; + const MatrixType APlus(pinv(A)); + + #if 0 + InfoNumPyFormat(A, "A"); + InfoNumPyFormat(APlus, "A^+"); + #endif + + if (0 | RUNALL) + { + Info<< nl << "# (A^+)^+ = A:" << nl; + const MatrixType APlusPlus(pinv(APlus)); + equal(APlusPlus, A, verbose); + } + + // The four Moore-Penrose conditions + // w.wiki/5RL (Retrieved: 29-06-19) + if (0 | RUNALL) + { + Info<< nl << "# A*(A^+)*A = A:" << nl; + const MatrixType AAPlusA((A*APlus)*A); + equal(AAPlusA, A, verbose); + } + + if (0 | RUNALL) + { + Info<< nl << "# (A^+)*A*(A^+) = A:" << nl; + const MatrixType APlusAAPlus((APlus*A)*APlus); + equal(APlusAAPlus, APlus, verbose); + } + + if (0 | RUNALL) + { + Info<< nl << "# (A*(A^+)).T() = A*(A^+):" << nl; + const MatrixType AAPlus(A*APlus); + const MatrixType AAPlusT(AAPlus.T()); + equal(AAPlusT, AAPlus, verbose); + } + + if (0 | RUNALL) + { + Info<< nl << "# ((A^+)*A = ((A^+)*A).T():" << nl; + const MatrixType APlusA(APlus*A); + const MatrixType APlusAT(APlusA.T()); + equal(APlusA, APlusAT, verbose); + } +} + + +// * * * * * * * * * * * * * * * Main Program * * * * * * * * * * * * * * * // + +int main(int argc, char *argv[]) +{ + typedef SquareMatrix<scalar> SMatrix; + typedef RectangularMatrix<scalar> RMatrix; + typedef SquareMatrix<complex> SCMatrix; + typedef RectangularMatrix<complex> RCMatrix; + + Info<< setprecision(15); + Random rndGen(1234); + label numberOfTests = 10; + + Info<< "### QR Decomposition:" << nl << nl; + + // SquareMatrix<scalar> + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "## " << numberOfTests << " SquareMatrix<scalar> A:" << nl; + + for (label i = 0; i < numberOfTests; ++i) + { + const label mRows = rndGen.position(1, 50); + Info<< nl << nl << "# Random A with random mRows = " << mRows << nl; + + SMatrix A(makeRandomMatrix<SMatrix>({mRows, mRows}, rndGen)); + #if (0 | RUNALL) + { + const SMatrix B(A); + verification_pinv(B); + } + #endif + verification_QRMatrix(A); + verification_QRMatrixSolve(A); + + } + + horizontalLine(); + } + #endif + + + // RectangularMatrix<scalar> + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "## " << numberOfTests << " RectangularMatrix<scalar> A:" << nl; + + for (label i = 0; i < numberOfTests; ++i) + { + const label mRows = rndGen.position(1, 50); + const label nCols = rndGen.position(1, 50); + Info<< nl << nl << "# Random matrix A with" + << " random mRows = " << mRows + << " random nCols = " << nCols << nl; + + RMatrix A(makeRandomMatrix<RMatrix>({mRows, nCols}, rndGen)); + + #if (0 | RUNALL) + { + const RMatrix B(A); + verification_pinv(B); + } + #endif + + verification_QRMatrix(A); + } + + horizontalLine(); + } + #endif + + + // SquareMatrix<complex(scalar, 0.0)> + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "## " << numberOfTests << " SquareMatrix<complex, 0> A:" << nl; + + for (label i = 0; i < numberOfTests; ++i) + { + const label mRows = rndGen.position(1, 50); + Info<< nl << nl << "# Random A with random mRows = " << mRows << nl; + + SCMatrix A({mRows, mRows}, complex(0, 0)); + + for (auto& val : A) + { + val.Re() = rndGen.GaussNormal<scalar>(); + } + + verification_QRMatrix(A); + } + + horizontalLine(); + } + #endif + + + // SquareMatrix<complex(scalar, scalar)> + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "## " << numberOfTests << " SquareMatrix<complex> A:" << nl; + + for (label i = 0; i < numberOfTests; ++i) + { + const label mRows = rndGen.position(1, 50); + Info<< nl << nl << "# Random A with random mRows = " << mRows << nl; + + SCMatrix A(makeRandomMatrix<SCMatrix>({mRows, mRows}, rndGen)); + + #if (0 | RUNALL) + { + const SCMatrix B(A); + verification_pinv(B); + } + #endif + + verification_QRMatrix(A); + } + + horizontalLine(); + } + #endif + + + // RectangularMatrix<complex(scalar, scalar)> + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "## " << numberOfTests << " RectangularMatrix<complex> A:" << nl; + + for (label i = 0; i < numberOfTests; ++i) + { + const label mRows = rndGen.position(1, 50); + const label nCols = rndGen.position(1, 50); + Info<< nl << nl << "# Random matrix A with" + << " random mRows = " << mRows + << " random nCols = " << nCols << nl; + + RCMatrix A(makeRandomMatrix<RCMatrix>({mRows, nCols}, rndGen)); + + #if (0 | RUNALL) + { + const RCMatrix B(A); + verification_pinv(B); + } + #endif + + verification_QRMatrix(A); + } + + horizontalLine(); + } + #endif + + + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "### Parallel direct tall-skinny QR decomposition:" << nl; + /* + Parallel direct tall-skinny QR decomposition: + Benson, A. R., Gleich, D. F., & Demmel, J. (2013). + Direct QR factorizations for tall-and-skinny matrices in MapReduce + architectures. + 2013 IEEE International Conference on Big Data. + DOI:10.1109/bigdata.2013.6691583 + + Demmel, J., Grigori, L., Hoemmen, M., & Langou, J. (2012). + Communication-optimal parallel and sequential QR and LU + factorizations. + SIAM Journal on Scientific Computing, 34(1), A206-A239. + DOI:10.1137/080731992 + */ + // (Benson et al. 2013, Fig. 5) & (Demmel et al. 2012, Fig. 2) + + Info<< "## " << numberOfTests << " <scalar> tests:" << nl; + for (label i = 0; i < numberOfTests; ++i) + { + verification_tsqr<scalar>(rndGen); + } + + Info<< nl << "## " << numberOfTests << " <complex> tests:" << nl; + for (label i = 0; i < numberOfTests; ++i) + { + verification_tsqr<complex>(rndGen); + } + + horizontalLine(); + } + #endif + + + #if (0 | RUNALL) + { + Info<< "### Back substitution:" << nl << nl; + + numberOfTests = 100; + + // <scalar> + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "## " << numberOfTests << " <scalar>:" << nl; + + for (label i = 0; i < numberOfTests; ++i) + { + const label mRows = rndGen.position(20, 50); + const label pCols = rndGen.position(20, 50); + Info<< nl << nl << "# Random square matrix A with" + << " random mRows = " << mRows + << " random nCols = " << mRows << nl; + + SMatrix A(makeRandomMatrix<SMatrix>({mRows, mRows}, rndGen)); + + // Zeroise the lower diagonal, + // so that A becomes an upper-triangular matrix + for (label i = 0; i < mRows; ++i) + { + for (label j = 0; j < i; ++j) + { + A(i, j) = 0.0; + } + } + + // det(triangularA) = prod(diag(triangularA)) + scalar det = 1; + const List<scalar> diag0(A.diag()); + for (const auto& val : diag0) + { + det *= val; + } + + if (1e-8 < det) + { + Info<< "# Random matrix b with" + << " random mRows = " << mRows + << " random nCols = " << pCols << nl; + + const RMatrix b + ( + makeRandomMatrix<RMatrix>({mRows, pCols}, rndGen) + ); + Info<< "det(A) = " << det << nl; + verification_backSubstitution(A, b); + } + else + { + Info<< "Random matrix A is a nearly-singular matrix." + << " Skipping back-substitution" << nl; + } + } + + horizontalLine(); + } + #endif + + // <complex> + #if (0 | RUNALL) + { + horizontalLine(); + + Info<< "## " << numberOfTests << " <complex>:" << nl; + + for (label i = 0; i < numberOfTests; ++i) + { + const label mRows = rndGen.position(20, 50); + const label pCols = rndGen.position(20, 50); + Info<< nl << nl << "# Random square matrix A with" + << " random mRows = " << mRows + << " random nCols = " << mRows << nl; + + SCMatrix A(makeRandomMatrix<SCMatrix>({mRows, mRows}, rndGen)); + + // Zeroise the lower diagonal, + // so that A becomes an upper-triangular matrix + for (label i = 0; i < mRows; ++i) + { + for (label j = 0; j < i; ++j) + { + A(i, j) = pTraits<complex>::zero; + } + } + + // det(triangularA) = prod(diag(triangularA)) + complex det = pTraits<complex>::one; + const List<complex> diag0(A.diag()); + for (const auto& val : diag0) + { + det *= val; + } + + if (1e-8 < mag(det)) + { + Info<< "# Random matrix b with" + << " random mRows = " << mRows + << " random nCols = " << pCols << nl; + + const RCMatrix b + ( + makeRandomMatrix<RCMatrix>({mRows, pCols}, rndGen) + ); + Info<< "det(A) = " << det << nl; + verification_backSubstitution(A, b); + } + else + { + Info<< "Random matrix A is a nearly-singular matrix." + << " Skipping back-substitution" << nl; + } + } + + horizontalLine(); + } + #endif + } + #endif + + + Info<< nl << "End" << nl; + + return 0; +} + + +// ************************************************************************* // diff --git a/src/OpenFOAM/matrices/Matrix/MatrixTools.C b/src/OpenFOAM/matrices/Matrix/MatrixTools.C index cf9cb1813072ad487e31c564e4729cac557b107f..30dbce057147b3a16bb106bf0c25f44162e35e78 100644 --- a/src/OpenFOAM/matrices/Matrix/MatrixTools.C +++ b/src/OpenFOAM/matrices/Matrix/MatrixTools.C @@ -35,6 +35,7 @@ bool Foam::MatrixTools::equal const Matrix<Form1, Type>& A, const Matrix<Form2, Type>& B, const bool verbose, + const label lenDiffs, const scalar relTol, const scalar absTol ) @@ -54,6 +55,8 @@ bool Foam::MatrixTools::equal auto iter1 = A.cbegin(); auto iter2 = B.cbegin(); + label j = 0; + for (label i = 0; i < len; ++i) { if ((absTol + relTol*mag(*iter2)) < Foam::mag(*iter1 - *iter2)) @@ -63,8 +66,15 @@ bool Foam::MatrixTools::equal Info<< "Matrix element " << i << " differs beyond tolerance: " << *iter1 << " vs " << *iter2 << nl; + ++j; + } + if (lenDiffs < j) + { + Info<< "Number of different elements exceeds = " << lenDiffs + << " Ceasing comparisons for the remaining of elements." + << nl; + return false; } - return false; } ++iter1; diff --git a/src/OpenFOAM/matrices/Matrix/MatrixTools.H b/src/OpenFOAM/matrices/Matrix/MatrixTools.H index 89e74388570d30e89d54ac8a2c4bb596ac397190..58bdac3ef482662637ae340201dfd1568d9cab50 100644 --- a/src/OpenFOAM/matrices/Matrix/MatrixTools.H +++ b/src/OpenFOAM/matrices/Matrix/MatrixTools.H @@ -62,6 +62,7 @@ bool equal const Matrix<Form1, Type>& A, const Matrix<Form2, Type>& B, const bool verbose = false, + const label lenDiffs = 10, const scalar relTol = 1e-5, const scalar absTol = 1e-8 ); diff --git a/src/OpenFOAM/matrices/QRMatrix/QRMatrix.C b/src/OpenFOAM/matrices/QRMatrix/QRMatrix.C index f4e5fbb996b90590c0d9b694cfa9f58c75147f2d..4394700e0e282b7b13d302ef78f08bbeff47ea8b 100644 --- a/src/OpenFOAM/matrices/QRMatrix/QRMatrix.C +++ b/src/OpenFOAM/matrices/QRMatrix/QRMatrix.C @@ -28,132 +28,99 @@ License #include "QRMatrix.H" -// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // template<class MatrixType> -Foam::QRMatrix<MatrixType>::QRMatrix(const MatrixType& mat) +void Foam::QRMatrix<MatrixType>::qr +( + MatrixType& A +) { - decompose(mat); -} + const label nIter = min(A.m() - 1, A.n()); + // Loop through all subcolumns of which the diagonal elem is the first elem + for (label k = 0; k < nIter; ++k) + { + const RMatrix u(householderReflector(A.subColumn(k, k))); + + applyHouseholder(A, u, k); + } + + if (outputType_ == outputTypes::REDUCED_R) + { + A.resize(A.n(), A.n()); + } +} -// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // template<class MatrixType> -void Foam::QRMatrix<MatrixType>::decompose(const MatrixType& mat) +void Foam::QRMatrix<MatrixType>::qrPivot +( + MatrixType& A +) { - const label m = mat.m(); - const label n = mat.n(); - - // Initialize the R-matrix to M - R_ = mat; + const label nCols = A.n(); + const label nIter = min(A.m() - 1, A.n()); - // Initialize the Q-matrix to I - Q_.setSize(m); - Q_ = I; + // Initialise permutation vector, and column norm vector + P_ = identity(nCols); - // Pre-allocate temporary storage for the Householder steps - QMatrixType Qk(m); - QMatrixType Rk(m); - Field<cmptType> uk(m); + // Initialise vector norms of each column of A + List<scalar> colNorms(nCols); + for (label k = 0; k < nCols; ++k) + { + colNorms[k] = A.columnNorm(k, true); + } - for (label k=0; k<n; k++) + // Loop through all subcolumns of which the diagonal elem is the first elem + for (label k = 0; k < nIter; ++k) { - // alpha = -|column k of Rk| - cmptType alpha = Zero; - for (label bi=k; bi<m; bi++) + const labelRange colRange(k, nCols); + const SubList<scalar> subColNorms(colNorms, colRange); + + // Column pivoting + const label maxColNormi = + std::distance + ( + subColNorms.cbegin(), + std::max_element(subColNorms.cbegin(), subColNorms.cend()) + ); + + // Swap R_, P_ and colNorms_ according to pivot column if the current + // column is not the max norm column by avoiding any column swap where + // the leading elem is very small + if (maxColNormi != 0 && SMALL < mag(A(k, k + maxColNormi))) { - alpha += sqr(R_(bi, k)); - } - alpha = sqrt(alpha); + const RMatrix R1(A.subColumn(k)); + const RMatrix R2(A.subColumn(maxColNormi + k)); + A.subColumn(k) = R2; + A.subColumn(maxColNormi + k) = R1; - if (R_(k, k) > 0) - { - alpha = -alpha; + Swap(P_[k], P_[maxColNormi + k]); + Swap(colNorms[k], colNorms[maxColNormi + k]); } - // uk = column k of Rk - alpha*ek - // rSumSqrUk = 2/sum(sqr(uk)) - uk[k] = R_(k, k) - alpha; - cmptType rSumSqrUk = sqr(uk[k]); - for (label bi=k+1; bi<m; bi++) { - uk[bi] = R_(bi, k); - rSumSqrUk += sqr(uk[bi]); - } - rSumSqrUk = 2/rSumSqrUk; + const RMatrix u(householderReflector(A.subColumn(k, k))); - // Qk = I - 2*u*uT/sum(sqr(uk)) - for (label bi=k; bi<m; bi++) - { - for (label bj=k; bj<m; bj++) - { - Qk(bi, bj) = -rSumSqrUk*uk[bi]*uk[bj]; - } - Qk(bi, bi) += 1; + applyHouseholder(A, u, k); } - // Rk = Qk*R - for (label bi=k; bi<m; bi++) + // Update norms + if (k < nIter - 1) { - for (label bk=k; bk<n; bk++) + label q = k + 1; + for (const auto& val : RMatrix(A.subRow(k, q))) { - Rk(bi, bk) = Zero; - for (label bj=k; bj<n; bj++) - { - Rk(bi, bk) += Qk(bi, bj)*R_(bj, bk); - } - } - } - - // Ensure diagonal is positive - if (R_(k, k) < 0) - { - // R = -Rk - // Q = -Q - for (label bi=k; bi<m; bi++) - { - for (label bj=k; bj<n; bj++) - { - R_(bi, bj) = -Rk(bi, bj); - } - for (label bj=k; bj<m; bj++) - { - Q_(bi, bj) = -Q_(bi, bj); - } - } - } - else - { - // R = Rk - for (label bi=k; bi<m; bi++) - { - for (label bj=k; bj<n; bj++) - { - R_(bi, bj) = Rk(bi, bj); - } + colNorms[q] -= magSqr(val); + ++q; } } + } - // Q = Q*Qk (using Rk as temporary storage) - for (label bi=0; bi<m; bi++) - { - for (label bk=k; bk<m; bk++) - { - Rk(bi, bk) = Zero; - for (label bj=k; bj<m; bj++) - { - Rk(bi, bk) += Q_(bi, bj)*Qk(bj, bk); - } - } - } - for (label bi=0; bi<m; bi++) - { - for (label bj=k; bj<n; bj++) - { - Q_(bi, bj) = Rk(bi, bj); - } - } + if (outputType_ == outputTypes::REDUCED_R) + { + A.resize(A.n(), A.n()); } } @@ -167,7 +134,7 @@ void Foam::QRMatrix<MatrixType>::solvex { const label n = R_.n(); - for (label i = n - 1; i >= 0; --i) + for (label i = n - 1; 0 <= i; --i) { cmptType sum = x[i]; @@ -199,7 +166,7 @@ void Foam::QRMatrix<MatrixType>::solveImpl // Assert (&x != &source) ? const label m = Q_.m(); - // x = Q_.T()*source; ie, Q_.Tmul(source) + // x = Q_.T()*source; i.e., Q_.Tmul(source) for (label i = 0; i < m; ++i) { x[i] = 0; @@ -213,6 +180,179 @@ void Foam::QRMatrix<MatrixType>::solveImpl } +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +template<class MatrixType> +Foam::QRMatrix<MatrixType>::QRMatrix() +: + outputType_(), + storeMethod_(), + colPivot_() +{} + + +template<class MatrixType> +Foam::QRMatrix<MatrixType>::QRMatrix +( + const outputTypes outputType, + const storeMethods storeMethod, + const colPivoting colPivot +) +: + outputType_(outputType), + storeMethod_(storeMethod), + colPivot_(colPivot), + Q_(), + R_(), + P_() +{} + + +template<class MatrixType> +Foam::QRMatrix<MatrixType>::QRMatrix +( + MatrixType& A, + const outputTypes outputType, + const storeMethods storeMethod, + const colPivoting colPivot +) +: + outputType_(outputType), + storeMethod_(storeMethod), + colPivot_(colPivot), + Q_(), + R_(), + P_() +{ + decompose(A); +} + + +template<class MatrixType> +Foam::QRMatrix<MatrixType>::QRMatrix +( + const MatrixType& A, + const outputTypes outputType, + const colPivoting colPivot +) +: + outputType_(outputType), + storeMethod_(storeMethods::OUT_OF_PLACE), + colPivot_(colPivot), + Q_(), + R_(), + P_() +{ + decompose(A); +} + + +// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * // + +template<class MatrixType> +void Foam::QRMatrix<MatrixType>::decompose +( + MatrixType& A +) +{ + // Check whether settings and input are consistent for reduced QRMatrix + if (A.m() <= A.n() && outputType_ == outputTypes::REDUCED_R) + { + outputType_ = outputTypes::FULL_R; + + #if FULLDEBUG + WarningInFunction + << "Reduced QR decomposition is by definition limited to matrices" + << " wherein rows > columns, thus computing FULL decomposition." + << nl; + #endif + } + + // Allocate resources for Q_, if need be + if (outputType_ == outputTypes::FULL_QR) + { + Q_ = MatrixType({A.m(), A.m()}, I); + } + + // Allocate resources for R_ and execute decomposition + switch (storeMethod_) + { + case storeMethods::IN_PLACE: + { + if (colPivot_) + { + qrPivot(A); + } + else + { + qr(A); + } + break; + } + + case storeMethods::OUT_OF_PLACE: + { + R_ = A; + + if (colPivot_) + { + qrPivot(R_); + } + else + { + qr(R_); + } + break; + } + } +} + + +template<class MatrixType> +void Foam::QRMatrix<MatrixType>::decompose +( + const MatrixType& A +) +{ + if (storeMethod_ == storeMethods::IN_PLACE) + { + WarningInFunction + << "const type qualifier invalidates storeMethods::IN_PLACE." << nl; + } + + // Check whether settings and input are consistent for reduced QRMatrix + if (A.m() <= A.n() && outputType_ == outputTypes::REDUCED_R) + { + outputType_ = outputTypes::FULL_R; + + #if FULLDEBUG + WarningInFunction + << "Reduced QR decomposition is by definition limited to matrices" + << " wherein rows > columns, thus computing FULL decomposition." + << nl; + #endif + } + + // Allocate resources for Q_, if need be + if (outputType_ == outputTypes::FULL_QR) + { + Q_ = MatrixType({A.m(), A.m()}, I); + } + + // Allocate resources for R_ and execute decomposition + R_ = A; + + if (colPivot_) + { + qrPivot(R_); + } + else + { + qr(R_); + } +} + + template<class MatrixType> void Foam::QRMatrix<MatrixType>::solve ( @@ -268,13 +408,13 @@ Foam::QRMatrix<MatrixType>::solve template<class MatrixType> -typename Foam::QRMatrix<MatrixType>::QMatrixType +typename Foam::QRMatrix<MatrixType>::SMatrix Foam::QRMatrix<MatrixType>::inv() const { const label m = Q_.m(); Field<cmptType> x(m); - QMatrixType inv(m); + SMatrix inv(m); for (label i = 0; i < m; ++i) { @@ -290,4 +430,168 @@ Foam::QRMatrix<MatrixType>::inv() const } +template<class MatrixType> +Foam::RectangularMatrix<typename MatrixType::cmptType> +Foam::QRMatrix<MatrixType>::backSubstitution +( + const SMatrix& A, + const RMatrix& rhs +) +{ + const label mRows = A.m(); + const label nCols = A.n(); + const label pCols = rhs.n(); + + #ifdef FULLDEBUG + { + const label qRows = rhs.m(); + if (mRows != qRows) + { + FatalErrorInFunction + << "Linear system is not solvable since the number of rows of" + << "A and rhs are not equal:" << tab << mRows << "vs." << qRows + << abort(FatalError); + } + + const List<cmptType> diag(A.diag()); + for (const auto& val : diag) + { + if (mag(val) < SMALL) + { + WarningInFunction + << "SMALL-valued diagonal elem in back-substitution." << nl; + } + } + } + #endif + + RMatrix X({nCols, pCols}, Zero); + + for (label i = 0; i < pCols; ++i) + { + for (label j = mRows - 1; -1 < j; --j) + { + cmptType alpha(rhs(j, i)); + + for (label k = j + 1; k < mRows; ++k) + { + alpha -= X(k, i)*A(j, k); + } + + X(j, i) = alpha/A(j, j); + } + } + + return X; +} + + +// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * // + +template<class MatrixType> +MatrixType Foam::pinv +( + const MatrixType& A, + const scalar tol +) +{ + typedef typename MatrixType::cmptType cmptType; + + #if FULLDEBUG + if (A.empty()) + { + FatalErrorInFunction + << "Empty matrix." << abort(FatalError); + } + + // Check if A is full-rank + #endif + + QRMatrix<MatrixType> QRM + ( + A, + QRMatrix<MatrixType>::outputTypes::FULL_QR, + QRMatrix<MatrixType>::colPivoting::TRUE + ); + const MatrixType& R(QRM.R()); + const MatrixType& Q(QRM.Q()); + + // R1 (KP:p. 648) + // Find the first diagonal element index with (almost) zero value in R + label firstZeroElemi = 0; + { + const List<cmptType> diag(R.diag()); + + auto lessT = [&](const cmptType& x) { return mag(x) < tol; }; + + firstZeroElemi = + std::distance + ( + diag.cbegin(), + std::find_if(diag.cbegin(), diag.cend(), lessT) + ); + } + + if (firstZeroElemi == 0) + { + FatalErrorInFunction + << "The largest (magnitude) diagonal element is (almost) zero." + << abort(FatalError); + } + + // Exclude the first (almost) zero diagonal row and the rows below + // since R diagonal is already descending due to the QR column pivoting + const RectangularMatrix<cmptType> R1(R.subMatrix(0, 0, firstZeroElemi)); + + // R2 (KP:p. 648) + if (R1.n() < R1.m()) + { + const SquareMatrix<cmptType> C(R1&R1); + + QRMatrix<SquareMatrix<cmptType>> QRSolve + ( + C, + QRMatrix<SquareMatrix<cmptType>>::outputTypes::FULL_QR + ); + + RectangularMatrix<cmptType> R2 + ( + QRSolve.backSubstitution + ( + QRSolve.R(), + QRSolve.Q() & (R1.T()) + ) + ); + + // R3 (KP:p. 648) + R2.resize(R.m(), R.n()); + + return MatrixType((QRM.P()^R2)^Q); + } + else + { + const SquareMatrix<cmptType> C(R1^R1); + + QRMatrix<SquareMatrix<cmptType>> QRSolve + ( + C, + QRMatrix<SquareMatrix<cmptType>>::outputTypes::FULL_QR + ); + + RectangularMatrix<cmptType> R2 + ( + QRSolve.backSubstitution + ( + QRSolve.R(), + QRSolve.Q() & R1 + ) + ); + + // R3 + R2.resize(R.m(), R.n()); + + return MatrixType((QRM.P()^R2)^Q); + } +} + // ************************************************************************* // diff --git a/src/OpenFOAM/matrices/QRMatrix/QRMatrix.H b/src/OpenFOAM/matrices/QRMatrix/QRMatrix.H index 9058cd027ea6c234936344d49efd3f77ddbd584b..415fbba853c3cc73527a4974df120212d2989fe4 100644 --- a/src/OpenFOAM/matrices/QRMatrix/QRMatrix.H +++ b/src/OpenFOAM/matrices/QRMatrix/QRMatrix.H @@ -28,19 +28,117 @@ Class Foam::QRMatrix Description - Class templated on matrix type to perform the QR decomposition using - Householder reflections on a square or rectangular matrix. + QRMatrix (i.e. QR decomposition, QR factorisation or orthogonal-triangular + decomposition) decomposes a scalar/complex matrix \c A into the following + matrix product: + + \verbatim + A = Q*R, + \endverbatim + + where + \c Q is a unitary similarity matrix, + \c R is an upper triangular matrix. + + Reference: + \verbatim + QR decomposition: + mathworld.wolfram.com/QRDecomposition.html (Retrieved:17-06-19) + w.wiki/52X (Retrieved: 17-06-19) + + QR decomposition with Householder reflector (tag:M): + Monahan, J. F. (2011). + Numerical methods of statistics. + Cambridge: Cambridge University Press. + DOI:10.1017/CBO9780511977176 + + QR decomposition with column pivoting (tag:QSB): + Quintana-OrtÃ, G., Sun, X., & Bischof, C. H. (1998). + A BLAS-3 version of the QR factorization with column pivoting. + SIAM Journal on Scientific Computing, 19(5), 1486-1494. + DOI:10.1137/S1064827595296732 + + Moore-Penrose inverse algorithm (tags:KP; KPP): + Katsikis, V. N., & Pappas, D. (2008). + Fast computing of the Moore-Penrose inverse matrix. + Electronic Journal of Linear Algebra, 17(1), 637-650. + DOI:10.13001/1081-3810.1287 + + Katsikis, V. N., Pappas, D., & Petralias, A. (2011). + An improved method for the computation of + the Moore–Penrose inverse matrix. + Applied Mathematics and Computation, 217(23), 9828-9834. + DOI:10.1016/j.amc.2011.04.080 + + Tolerance for the Moore-Penrose inverse algorithm (tag:TA): + Toutounian, F., & Ataei, A. (2009). + A new method for computing Moore–Penrose inverse matrices. + Journal of Computational and applied Mathematics, 228(1), 412-417. + DOI:10.1016/j.cam.2008.10.008 + + Operands of QR decomposition: + mathworld.wolfram.com/UnitaryMatrix.html (Retrieved:13-06-19) + mathworld.wolfram.com/UpperTriangularMatrix.html (Retrieved:16-06-19) + mathworld.wolfram.com/PermutationMatrix.html (Retrieved:20-06-19) + + Back substitution: + mathworld.wolfram.com/GaussianElimination.html (Retrieved:15-06-19) + \endverbatim + +Usage + Input types: + - \c A can be a \c SquareMatrix<Type> or \c RectangularMatrix<Type> + + Output types: + - \c Q is always of the type of the matrix \c A + - \c R is always of the type of the matrix \c A + + Options for the output forms of \c QRMatrix (for an (m-by-n) input matrix + \c A with k = min(m, n)): + - outputTypes::FULL_R: computes only \c R (m-by-n) + - outputTypes::FULL_QR: computes both \c R and \c Q (m-by-m) + - outputTypes::REDUCED_R: computes only reduced \c R (k-by-n) + + Options where to store \c R: + - storeMethods::IN_PLACE: replaces input matrix content with \c R + - storeMethods::OUT_OF_PLACE: creates new object of \c R + + Options for the computation of column pivoting: + - colPivoting::FALSE: switches off column pivoting + - colPivoting::TRUE: switches on column pivoting + + Direct solution of linear systems A x = b is possible by solve() alongside + the following limitations: + - \c A = a scalar square matrix + - output type = outputTypes::FULL_QR + - store method = storeMethods::IN_PLACE + +Notes + - QR decomposition is not unique if \c R is not positive diagonal \c R. + - The option combination: + - outputTypes::REDUCED_R + - storeMethods::IN_PLACE + will not modify the rows of input matrix \c A after its nth row. + - Both FULL_R and REDUCED_R QR decompositions execute the same number of + operations. Yet REDUCED_R QR decomposition returns only the first n rows + of \c R if m > n for an input m-by-n matrix \c A. + - For m <= n, FULL_R and REDUCED_R will produce the same matrices. + +See also + Test-QRMatrix.C SourceFiles - QRMatrixI.H QRMatrix.C + QRMatrixI.H \*---------------------------------------------------------------------------*/ #ifndef QRMatrix_H #define QRMatrix_H +#include "RectangularMatrix.H" #include "SquareMatrix.H" +#include "complex.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -48,32 +146,90 @@ namespace Foam { /*---------------------------------------------------------------------------*\ - Class QRMatrix Declaration + Class QRMatrix Declaration \*---------------------------------------------------------------------------*/ template<class MatrixType> class QRMatrix { + public: typedef typename MatrixType::cmptType cmptType; - typedef SquareMatrix<cmptType> QMatrixType; - typedef MatrixType RMatrixType; + typedef SquareMatrix<cmptType> SMatrix; + typedef RectangularMatrix<cmptType> RMatrix; + + //- Options for the output matrix forms of QRMatrix + enum outputTypes : uint8_t + { + FULL_R = 1, //!< computes only \c R + FULL_QR = 2, //!< computes both \c R and \c Q + REDUCED_R = 3 //!< computes only reduced \c R + }; + + //- Options where to store R + enum storeMethods : uint8_t + { + IN_PLACE = 1, //!< replaces input matrix content with \c R + OUT_OF_PLACE = 2 //!< creates new object of \c R + }; + + //- Options for the computation of column pivoting + enum colPivoting : bool + { + FALSE = false, //!< switches off column pivoting + TRUE = true //!< switches on column pivoting + }; private: // Private Data - //- The Q-matrix - QMatrixType Q_; + //- Selected option for the output matrix forms of QRMatrix + outputTypes outputType_; + + //- Selected option where to store R + const storeMethods storeMethod_; + + //- Selected option for the computation of column pivoting + const colPivoting colPivot_; - //- The R-matrix - RMatrixType R_; + //- Unitary similarity matrix + MatrixType Q_; + + //- Upper triangular matrix + MatrixType R_; + + //- Permutation list (if column-pivoting is on) + labelList P_; // Private Member Functions + //- Compute the QR decomposition without the column pivoting + void qr + ( + MatrixType& A + ); + + //- Compute the QR decomposition with the column pivoting + // (QSB:Fig. 1) + void qrPivot + ( + MatrixType& A + ); + + //- Compute output matrices in selected forms + //- using Householder reflectors + // (M:Section 4) + inline void applyHouseholder + ( + MatrixType& A, + const RMatrix& reflector, + const label k = 0 + ); + //- Solve the linear system with the Field argument x initialized to //- the appropriate transformed source (e.g. Q.T()*source) //- and return the solution in x @@ -90,27 +246,99 @@ private: ) const; +protected: + + // Protected Member Functions + + //- Compute Householder reflector on a given matrix column, u + // (M:Eq.6.814) + inline RMatrix householderReflector + ( + RMatrix u + ); + + //- Apply (in-place) Householder reflectors from the left side: u*A + inline void applyLeftReflector + ( + MatrixType& A, + const RMatrix& reflector, + const label k = 0, + const label k1 = 0 + ); + + //- Apply (in-place) Householder reflectors from the right side: (u*A)*u + inline void applyRightReflector + ( + MatrixType& A, + const RMatrix& reflector, + const label k = 0 + ); + + public: // Constructors //- Construct null - inline QRMatrix(); + QRMatrix(); - //- Construct decomposing given matrix - QRMatrix(const MatrixType& M); + //- Construct QRMatrix without performing the decomposition + QRMatrix + ( + const outputTypes outputType, + const storeMethods storeMethod = storeMethods::OUT_OF_PLACE, + const colPivoting colPivot = colPivoting::FALSE + ); + + //- Construct QRMatrix and perform the QR decomposition + QRMatrix + ( + MatrixType& A, + const outputTypes outputType, + const storeMethods storeMethod = storeMethods::OUT_OF_PLACE, + const colPivoting colPivot = colPivoting::FALSE + ); + + //- Construct QRMatrix and perform the QR decomposition + QRMatrix + ( + const MatrixType& A, + const outputTypes outputType, + const colPivoting colPivot = colPivoting::FALSE + ); // Member Functions - //- Return Q-matrix - inline const QMatrixType& Q() const; + // Information + + //- Return the unitary similarity matrix + // Includes implicit round-to-zero as mutable operation + inline const MatrixType& Q() const; + + //- Return the upper triangular matrix + inline const MatrixType& R() const; + + //- Return the permutation order (P) as a list + inline const labelList& orderP() const; + + //- Create and return the permutation matrix + inline SMatrix P() const; - //- Return R-matrix - inline const RMatrixType& R() const; - //- Decompose given matrix - void decompose(const MatrixType& M); + // Algorithm + + //- Compute QR decomposition according to constructor settings + void decompose + ( + MatrixType& A + ); + + //- Compute QR decomposition according to constructor settings + void decompose + ( + const MatrixType& A + ); //- Solve the linear system with the given source //- and return the solution in the argument x @@ -145,10 +373,38 @@ public: ) const; //- Return the inverse of (Q*R), so that solving x = (Q*R).inv()*source - QMatrixType inv() const; + SMatrix inv() const; + + //- Solve a row-echelon-form linear system starting from the bottom + // Back substitution: Ax = b where: + // A = Non-singular upper-triangular square matrix (m-by-m) + // x = Solution (m-by-p) + // b = Source (m-by-p) + RMatrix backSubstitution + ( + const SMatrix& A, + const RMatrix& b + ); }; +// * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * * // + +//- (Out-of-place) Moore-Penrose inverse of singular/non-singular +//- square/rectangular scalar/complex matrices +// (KPP:p. 9834; KP:p. 648) +// The tolerance (i.e. tol) to ensure the R1 matrix full-rank is given as 1e-13 +// in the original paper (KPP:p. 9834). Nonetheless, the tolerance = 1e-5 +// given by (TA; mentioned in (KPP:p. 9832)) was observed to be more robust +// for the tested scenarios. +template<class MatrixType> +MatrixType pinv +( + const MatrixType& A, + const scalar tol = 1e-5 +); + + // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace Foam diff --git a/src/OpenFOAM/matrices/QRMatrix/QRMatrixI.H b/src/OpenFOAM/matrices/QRMatrix/QRMatrixI.H index 69102fa722e9459ad63c09d6f50573da292f777a..929f8b6fc89f986388923bf6f51d52718343de9f 100644 --- a/src/OpenFOAM/matrices/QRMatrix/QRMatrixI.H +++ b/src/OpenFOAM/matrices/QRMatrix/QRMatrixI.H @@ -26,29 +26,167 @@ License \*---------------------------------------------------------------------------*/ -// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // template<class MatrixType> -inline Foam::QRMatrix<MatrixType>::QRMatrix() -{} +inline void Foam::QRMatrix<MatrixType>::applyHouseholder +( + MatrixType& A, + const RMatrix& reflector, + const label k +) +{ + applyLeftReflector(A, reflector, k, k); + + if (outputType_ == outputTypes::FULL_QR) + { + applyRightReflector(Q_, reflector, k); + } +} + + +// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * // +template<class MatrixType> +inline Foam::RectangularMatrix<typename MatrixType::cmptType> +Foam::QRMatrix<MatrixType>::householderReflector +( + RMatrix u +) +{ + #ifdef FULLDEBUG + // Check if the given RectangularMatrix is effectively a column vector + if (u.n() != 1) + { + FatalErrorInFunction + << "Input matrix is not a column vector." << exit(FatalError); + } + #endif + + scalar magnitude(mag(u(0,0))); + if (magnitude < VSMALL) + { + magnitude = SMALL; + #if FULLDEBUG + FatalErrorInFunction + << "Almost zero leading elem in Householder matrix." + << abort(FatalError); + #endif + } + + u(0,0) += u(0,0)/magnitude*u.columnNorm(0); + + scalar colNorm(u.columnNorm(0)); + if (colNorm < VSMALL) + { + colNorm = SMALL; + #if FULLDEBUG + FatalErrorInFunction + << "Almost zero norm in the Householder matrix." + << abort(FatalError); + #endif + } + + u /= cmptType(colNorm); + + return u; +} + + +template<class MatrixType> +inline void Foam::QRMatrix<MatrixType>::applyLeftReflector +( + MatrixType& A, + const RMatrix& reflector, + const label k, + const label k1 +) +{ + // const RMatrix& A0(A.subMatrix(k1, k)); + // A0 -= (cmptType(2)*reflector)*(reflector & A0); + + for (label j = k; j < A.n(); ++j) + { + cmptType sum = Zero; + for (label i = 0; i < reflector.m(); ++i) + { + sum += Detail::conj(reflector(i, 0))*A(i + k1, j); + } + + sum *= cmptType(2); + for (label i = 0; i < reflector.m(); ++i) + { + A(i + k1, j) -= reflector(i, 0)*sum; + } + } +} + + +template<class MatrixType> +inline void Foam::QRMatrix<MatrixType>::applyRightReflector +( + MatrixType& A, + const RMatrix& reflector, + const label k +) +{ + // const RMatrix A0(A.subMatrix(0, k)); + // A0 -= ((A0*reflector)^(cmptType(2)*reflector)) + + for (label i = 0; i < A.m(); ++i) + { + cmptType sum = Zero; + for (label j = 0; j < reflector.m(); ++j) + { + sum += A(i, j + k)*reflector(j, 0); + } + + sum *= cmptType(2); + for (label j = 0; j < reflector.m(); ++j) + { + A(i, j + k) -= Detail::conj(reflector(j, 0))*sum; + } + } +} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // template<class MatrixType> -inline const typename Foam::QRMatrix<MatrixType>::QMatrixType& -Foam::QRMatrix<MatrixType>::Q() const +inline const MatrixType& Foam::QRMatrix<MatrixType>::Q() const { + const_cast<MatrixType&>(Q_).round(); + return Q_; } template<class MatrixType> -inline const typename Foam::QRMatrix<MatrixType>::RMatrixType& -Foam::QRMatrix<MatrixType>::R() const +inline const MatrixType& Foam::QRMatrix<MatrixType>::R() const { return R_; } +template<class MatrixType> +inline const Foam::labelList& Foam::QRMatrix<MatrixType>::orderP() const +{ + return P_; +} + + +template<class MatrixType> +inline Foam::SquareMatrix<typename Foam::QRMatrix<MatrixType>::cmptType> +Foam::QRMatrix<MatrixType>::P() const +{ + SquareMatrix<cmptType> permMat(P_.size(), Zero); + + forAll(P_, jcol) + { + permMat(P_[jcol], jcol) = pTraits<cmptType>::one; + } + + return permMat; +} + + // ************************************************************************* // diff --git a/src/OpenFOAM/matrices/RectangularMatrix/RectangularMatrix.H b/src/OpenFOAM/matrices/RectangularMatrix/RectangularMatrix.H index bb1877e078b585dab9565e6d0eab2a42bd1724be..5b09ea7568b0e153706f494c4b40122a2a4b2843 100644 --- a/src/OpenFOAM/matrices/RectangularMatrix/RectangularMatrix.H +++ b/src/OpenFOAM/matrices/RectangularMatrix/RectangularMatrix.H @@ -80,6 +80,11 @@ public: //- initializing all elements to the given value inline RectangularMatrix(const label m, const label n, const Type& val); + //- Construct for given number of rows/columns + //- initializing all elements to zero, and diagonal to one + template<class AnyType> + inline RectangularMatrix(const labelPair& dims, const Identity<AnyType>); + //- Construct given number of rows/columns inline explicit RectangularMatrix(const labelPair& dims); diff --git a/src/OpenFOAM/matrices/RectangularMatrix/RectangularMatrixI.H b/src/OpenFOAM/matrices/RectangularMatrix/RectangularMatrixI.H index b014e290699e85f54a09937c4ca13d57d56f0db7..7cef3593e77c223e6e24c17022a14b6797ab9374 100644 --- a/src/OpenFOAM/matrices/RectangularMatrix/RectangularMatrixI.H +++ b/src/OpenFOAM/matrices/RectangularMatrix/RectangularMatrixI.H @@ -80,6 +80,23 @@ inline Foam::RectangularMatrix<Type>::RectangularMatrix {} +template<class Type> +template<class AnyType> +inline Foam::RectangularMatrix<Type>::RectangularMatrix +( + const labelPair& dims, + const Identity<AnyType> +) +: + Matrix<RectangularMatrix<Type>, Type>(dims.first(), dims.second(), Zero) +{ + for (label i = 0; i < min(dims.first(), dims.second()); ++i) + { + this->operator()(i, i) = pTraits<Type>::one; + } +} + + template<class Type> inline Foam::RectangularMatrix<Type>::RectangularMatrix ( diff --git a/src/OpenFOAM/matrices/SquareMatrix/SquareMatrix.H b/src/OpenFOAM/matrices/SquareMatrix/SquareMatrix.H index b3ac5eb9f3bafa3378396e04b07ac70abad96563..e404910e069a8e4fad048a10d2056e064fcd4ed8 100644 --- a/src/OpenFOAM/matrices/SquareMatrix/SquareMatrix.H +++ b/src/OpenFOAM/matrices/SquareMatrix/SquareMatrix.H @@ -85,6 +85,9 @@ public: template<class AnyType> inline SquareMatrix(const label n, const Identity<AnyType>); + template<class AnyType> + inline SquareMatrix(const labelPair& dims, const Identity<AnyType>); + //- Construct given number of rows/columns (checked to be equal) // For constructor consistency in rectangular matrices inline explicit SquareMatrix(const labelPair& dims); @@ -129,12 +132,23 @@ public: //- Resize the matrix preserving the elements inline void resize(const label m); + //- Resize the matrix preserving the elements (compatibility) + inline void resize(const label m, const label n); + //- Resize the matrix preserving the elements inline void setSize(const label m); //- Resize the matrix without reallocating storage (unsafe) inline void shallowResize(const label m); + // Check + + //- Return true if the square matrix is effectively symmetric/Hermitian + inline bool symmetric() const; + + //- Return true if the square matrix is reduced tridiagonal + inline bool tridiagonal() const; + // Member Operators diff --git a/src/OpenFOAM/matrices/SquareMatrix/SquareMatrixI.H b/src/OpenFOAM/matrices/SquareMatrix/SquareMatrixI.H index b41b5e97bea1814744e284cb82e6676f6a6aacd5..75e893e068f8c9d91e32d83aaec652364b967455 100644 --- a/src/OpenFOAM/matrices/SquareMatrix/SquareMatrixI.H +++ b/src/OpenFOAM/matrices/SquareMatrix/SquareMatrixI.H @@ -90,6 +90,25 @@ inline Foam::SquareMatrix<Type>::SquareMatrix } +template<class Type> +template<class AnyType> +inline Foam::SquareMatrix<Type>::SquareMatrix +( + const labelPair& dims, + const Identity<AnyType> +) +: + Matrix<SquareMatrix<Type>, Type>(dims, Zero) +{ + CHECK_MATRIX_IS_SQUARE(dims.first(), dims.second()); + + for (label i = 0; i < dims.first(); ++i) + { + this->operator()(i, i) = pTraits<Type>::one; + } +} + + template<class Type> inline Foam::SquareMatrix<Type>::SquareMatrix ( @@ -206,6 +225,19 @@ inline void Foam::SquareMatrix<Type>::resize(const label m) } +template<class Type> +inline void Foam::SquareMatrix<Type>::resize(const label m, const label n) +{ + if (m != n) + { + FatalErrorInFunction<< "Disallowed use of resize() for SquareMatrix" + << abort(FatalError); + } + + Matrix<SquareMatrix<Type>, Type>::resize(m, m); +} + + template<class Type> inline void Foam::SquareMatrix<Type>::setSize(const label m) { @@ -220,6 +252,49 @@ inline void Foam::SquareMatrix<Type>::shallowResize(const label m) } +template<class Type> +inline bool Foam::SquareMatrix<Type>::symmetric() const +{ + for (label n = 0; n < this->n() - 1; ++n) + { + for (label m = this->m() - 1; n < m; --m) + { + if (SMALL < mag((*this)(n, m) - (*this)(m, n))) + { + return false; + } + } + } + return true; +} + + +template<class Type> +inline bool Foam::SquareMatrix<Type>::tridiagonal() const +{ + for (label i = 0; i < this->m(); ++i) + { + for (label j = 0; j < this->n(); ++j) + { + const Type& val = (*this)(i, j); + + if ((i == j) || (i - 1 == j) || (i + 1 == j)) + { + if (mag(val) < SMALL) + { + return false; + } + } + else if (SMALL < mag(val)) + { + return false; + } + } + } + return true; +} + + // * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * // template<class Type> diff --git a/src/TurbulenceModels/incompressible/turbulentTransportModels/RAS/kkLOmega/kkLOmega.C b/src/TurbulenceModels/incompressible/turbulentTransportModels/RAS/kkLOmega/kkLOmega.C index 34462139a55c0637b3b01bffc1bab06286e63291..31498a6be4415c91e7ca642997be32532db413c0 100644 --- a/src/TurbulenceModels/incompressible/turbulentTransportModels/RAS/kkLOmega/kkLOmega.C +++ b/src/TurbulenceModels/incompressible/turbulentTransportModels/RAS/kkLOmega/kkLOmega.C @@ -636,7 +636,7 @@ void kkLOmega::correct() *fINT() *Cmu(sqrt(S2))*sqrt(ktS)*lambdaEff ); - const volScalarField Pkt(nuts*S2); + volScalarField Pkt(this->GName(), nuts*S2); const volScalarField ktL(kt_ - ktS); const volScalarField ReOmega(sqr(y_)*Omega/nu()); diff --git a/src/TurbulenceModels/incompressible/turbulentTransportModels/RAS/kkLOmega/kkLOmega.H b/src/TurbulenceModels/incompressible/turbulentTransportModels/RAS/kkLOmega/kkLOmega.H index 934911e6ea5315cc56ce7e1a4c686222246fb8bd..b7fe7c295c6b056d6ab304382b75b94c8f2a5163 100644 --- a/src/TurbulenceModels/incompressible/turbulentTransportModels/RAS/kkLOmega/kkLOmega.H +++ b/src/TurbulenceModels/incompressible/turbulentTransportModels/RAS/kkLOmega/kkLOmega.H @@ -34,25 +34,22 @@ Description Low Reynolds-number k-kl-omega turbulence model for incompressible flows. - This turbulence model is described in: + Reference: \verbatim - Walters, D. K., & Cokljat, D. (2008). - A three-equation eddy-viscosity model for Reynolds-averaged - Navier–Stokes simulations of transitional flow. - Journal of Fluids Engineering, 130(12), 121401. + Standard model: + Walters, D. K., & Cokljat, D. (2008). + A three-equation eddy-viscosity model for Reynolds-averaged + Navier–Stokes simulations of transitional flow. + Journal of Fluids Engineering, 130(12), 121401. + DOI:10.1115/1.2979230 + + Typo corrections: + Furst, J. (2013). + Numerical simulation of transitional flows + with laminar kinetic energy. + Engineering MECHANICS, 20(5), 379-388. \endverbatim - however the paper contains several errors which must be corrected for the - model to operation correctly as explained in - - \verbatim - Furst, J. (2013). - Numerical simulation of transitional flows with laminar kinetic energy. - Engineering MECHANICS, 20(5), 379-388. - \endverbatim - - All these corrections and updates are included in this implementation. - The default model coefficients are \verbatim kkLOmegaCoeffs diff --git a/src/TurbulenceModels/turbulenceModels/laminar/Maxwell/Maxwell.H b/src/TurbulenceModels/turbulenceModels/laminar/Maxwell/Maxwell.H index 98fefe5ad0fb27c1fb7caa570f86506878110d02..1f3ce3c6863ed322761e1bcc4b7a2d2a5306698a 100644 --- a/src/TurbulenceModels/turbulenceModels/laminar/Maxwell/Maxwell.H +++ b/src/TurbulenceModels/turbulenceModels/laminar/Maxwell/Maxwell.H @@ -47,6 +47,7 @@ Description Comparison of different formulations for the numerical calculation of unsteady incompressible viscoelastic fluid flow. Adv. Appl. Math. Mech, 4, 483-502. + DOI:10.4208/aamm.10-m1010 \endverbatim SourceFiles @@ -78,7 +79,7 @@ class Maxwell protected: - // Protected data + // Protected Data // Model coefficients @@ -153,7 +154,7 @@ public: ) const; //- Solve the turbulence equations and correct eddy-Viscosity and - // related properties + //- related properties virtual void correct(); }; diff --git a/src/TurbulenceModels/turbulenceModels/laminar/Stokes/Stokes.H b/src/TurbulenceModels/turbulenceModels/laminar/Stokes/Stokes.H index 3ef35711287e25f6123cc676a0fe5bbcd6e9b91c..a2da356cd3a7a9f1414f1aa3fa9e20a3810c1e89 100644 --- a/src/TurbulenceModels/turbulenceModels/laminar/Stokes/Stokes.H +++ b/src/TurbulenceModels/turbulenceModels/laminar/Stokes/Stokes.H @@ -25,7 +25,10 @@ License along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. Class - Foam::Stokes + Foam::laminarModels::Stokes + +Group + grpLaminar Description Turbulence model for Stokes flow. @@ -127,7 +130,7 @@ public: virtual tmp<volScalarField> k() const; //- Return the turbulence kinetic energy dissipation rate, - // i.e. 0 for Stokes flow + //- i.e. 0 for Stokes flow virtual tmp<volScalarField> epsilon() const; //- Return the Reynolds stress tensor, i.e. 0 for Stokes flow diff --git a/tutorials/combustion/XiDyMFoam/annularCombustorTurbine/Allrun b/tutorials/combustion/XiDyMFoam/annularCombustorTurbine/Allrun index 20ddac1d57e38d7142c839a8399a7398ba48a3c9..3e78f257360ab88b5b01d28577b875ad4180c81a 100755 --- a/tutorials/combustion/XiDyMFoam/annularCombustorTurbine/Allrun +++ b/tutorials/combustion/XiDyMFoam/annularCombustorTurbine/Allrun @@ -11,8 +11,6 @@ runApplication decomposePar -force runParallel potentialFoam -pName pPotential -initialiseUBCs -rm -f processor*/0/phi - runParallel XiDyMFoam #------------------------------------------------------------------------------ diff --git a/tutorials/incompressible/adjointOptimisationFoam/motorBike/Allrun b/tutorials/incompressible/adjointOptimisationFoam/motorBike/Allrun index 3b20001a5058dccc502892ff555d5ae05bbc714a..46c60b78fc43150f4cdd38f64eb9a489adff7fae 100755 --- a/tutorials/incompressible/adjointOptimisationFoam/motorBike/Allrun +++ b/tutorials/incompressible/adjointOptimisationFoam/motorBike/Allrun @@ -33,7 +33,7 @@ runParallel $decompDict snappyHexMesh -overwrite restore0Dir -processor runParallel $decompDict patchSummary -runParallel $decompDict potentialFoam +runParallel $decompDict potentialFoam -writephi runParallel $decompDict checkMesh -writeFields '(nonOrthoAngle)' -constant runParallel $decompDict $(getApplication) diff --git a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/Allclean b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/Allclean index d670ed7d367cbac27a704cb51c2587d991e01cea..cc7e6894a0f7174c1708520e12a307fa97022eb7 100755 --- a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/Allclean +++ b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/Allclean @@ -4,8 +4,7 @@ cd "${0%/*}" || exit # Run from this directory #------------------------------------------------------------------------------ cleanCase -rm -rf *.dat validation/*.eps - +rm -rf *.{dat,png,txt} system/controlDict constant/turbulenceProperties results wclean validation/WatersKing #------------------------------------------------------------------------------ diff --git a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/Allrun b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/Allrun index 893cbf8c2a16f8ca0c2205557cc42fd7ffab2a96..9b2ab05be8ec105b1d3a174faa103ab68e4bb86e 100755 --- a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/Allrun +++ b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/Allrun @@ -1,14 +1,97 @@ #!/bin/sh cd "${0%/*}" || exit # Run from this directory . ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions +. ${WM_PROJECT_DIR:?}/bin/tools/CleanFunctions # Tutorial clean functions #------------------------------------------------------------------------------ -runApplication blockMesh -runApplication $(getApplication) +models=" +Maxwell +Stokes +" -wmake validation/WatersKing -runApplication WatersKing +endTime=2 +if notTest $@ +then + endTime=25 +fi -( cd validation && ./createGraph ) +#------------------------------------------------------------------------------ + +# Compute the case in 'serial' mode, and collect the data +# +# $1 = endTime +# $* = models +# ---- +serialRun() { + endTime=$1 + shift 1 + models=$* + + sed "s|END_TIME|$endTime|g" system/controlDict.template \ + > system/controlDict + resultDir="results" + + runApplication blockMesh + wmake validation/WatersKing + + for model in $models + do + sed "s|LAMINAR_MODEL|$model|g" constant/turbulenceProperties.template \ + > constant/turbulenceProperties + + # Compute numerical data + runApplication $(getApplication) + tail -n +4 postProcessing/probes/0/U | \ + tr -s " " | tr -d '(' | cut -d " " -f2-3 > "${model}.txt" + + # Collect numerical data + modelDir="$resultDir/$model" + [ -d "$modelDir" ] || mkdir -p "$modelDir" + mv -f postProcessing log.* "$modelDir" + cleanTimeDirectories + done + + runApplication WatersKing +} + + +# Plot streamwise flow speed at y=1.0 [m] as a function of time +# +# $* = models +# ---- +plot() { + # Require gnuplot + command -v gnuplot >/dev/null || { + echo "gnuplot not found - skipping graph creation" 1>&2 + exit 1 + } + + models=$* + endTime=$(foamDictionary -entry endTime -value system/controlDict) + + gnuplot<<PLT + set terminal pngcairo font "helvetica,16" size 800,600 + set output "planarPoiseuille.png" + set grid + set key right top + set xrange [0:"$endTime"] + set yrange [0:8] + set xlabel "t [s]" + set ylabel "U_x [m/s]" rotate by 0 offset 3,0,0 + + results=system("ls *.txt") + names="${models[*]}" + plot \ + "WatersKing.dat" w lines t "Analytical" lt -1, \ + for [i=1:words(results)] word(results, i) t word(names, i) \ + w linespoints pointinterval 100 lt i pt 6 ps 1.5 + +PLT +} + +#------------------------------------------------------------------------------ + +serialRun $endTime $models +plot $models #------------------------------------------------------------------------------ diff --git a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/constant/turbulenceProperties b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/constant/turbulenceProperties.template similarity index 96% rename from tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/constant/turbulenceProperties rename to tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/constant/turbulenceProperties.template index c782dcde8b1f160fab70d0526e4c9011a5f661d8..aefd35ba5d94d9dce492e05f9608191c09c22c07 100644 --- a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/constant/turbulenceProperties +++ b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/constant/turbulenceProperties.template @@ -18,7 +18,7 @@ simulationType laminar; laminar { - laminarModel Maxwell; + laminarModel LAMINAR_MODEL; MaxwellCoeffs { diff --git a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/controlDict b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/controlDict.template similarity index 97% rename from tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/controlDict rename to tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/controlDict.template index f26ec0685b2cb6d7660a061c37480546e5c0b880..6e8a4bada10ba9ab0bbf1d9f872d7f4778ba3a8f 100644 --- a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/controlDict +++ b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/controlDict.template @@ -22,7 +22,7 @@ startTime 0; stopAt endTime; -endTime 25; +endTime END_TIME; deltaT 5e-3; diff --git a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/fvSchemes b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/fvSchemes index 8b06c3af277151032b975cbf3962f7f4fd70c318..15a53aa38a4b12b2b2908e8a611f6db49e00c537 100644 --- a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/fvSchemes +++ b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/system/fvSchemes @@ -27,13 +27,12 @@ gradSchemes divSchemes { default none; - div(phi,U) Gauss linearUpwind grad(U); div(phi,sigma) Gauss vanAlbada; - div(sigma) Gauss linear; div((nu*dev2(T(grad(U))))) Gauss linear; div((nuM*grad(U))) Gauss linear; + div((nuEff*dev2(T(grad(U))))) Gauss linear; } laplacianSchemes diff --git a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/validation/WatersKing/WatersKing.C b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/validation/WatersKing/WatersKing.C index b215bc8d1b5742f7f134229f814f80430886c8e3..8e78c17f15892b0dc43b21cb7a9e5f4c2d827f85 100644 --- a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/validation/WatersKing/WatersKing.C +++ b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/validation/WatersKing/WatersKing.C @@ -37,9 +37,10 @@ Description Rheologica Acta, 9, 345-355. Amoreira, L. J., & Oliveira, P. J. (2010). - Comparison of different formulations for the numerical - calculation of unsteady incompressible viscoelastic fluid - flow. Adv. Appl. Math. Mech, 4, 483-502. + Comparison of different formulations for the numerical calculation of + unsteady incompressible viscoelastic fluid flow. + Adv. Appl. Math. Mech, 4, 483-502. + DOI:10.4208/aamm.10-m1010 \endverbatim \*---------------------------------------------------------------------------*/ diff --git a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/validation/createGraph b/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/validation/createGraph deleted file mode 100755 index 77a23fedf430171167919a073cbe4700fc7e4fed..0000000000000000000000000000000000000000 --- a/tutorials/incompressible/pimpleFoam/laminar/planarPoiseuille/validation/createGraph +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/sh - -tail -n +4 ../postProcessing/probes/0/U | \ - tr -s " " | tr -d '(' | cut -d " " -f2-3 > ../Numerical.dat - -# Require gnuplot -command -v gnuplot >/dev/null || { - echo "gnuplot not found - skipping graph creation" 1>&2 - exit 1 -} - -gnuplot<<EOF - set terminal postscript eps color enhanced "Helvetica,20" - set output "planarPoiseuille.eps" - set xlabel "Time / [s]" font "Helvetica,24" - set ylabel "Velocity / [m/s]" font "Helvetica,24" - set grid - set key right top - set xrange [0:25] - set yrange [0:8] - plot \ - "../Numerical.dat" t "OpenFOAM (every 100 pts)" \ - with linespoints pointinterval 100 lt 1 pt 6 ps 1.5, \ - "../WatersKing.dat" with lines t "Analytical" lt -1 -EOF - -#------------------------------------------------------------------------------ diff --git a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/Allrun b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/Allrun index 78c665597a5ab2611c7c04aa995e05ec36b33b96..22cc513515f7491f02fc135d62d47fb6882decd5 100755 --- a/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/Allrun +++ b/tutorials/incompressible/pisoFoam/LES/motorBike/motorBike/Allrun @@ -22,7 +22,7 @@ find . -iname '*level*' -type f -delete restore0Dir -processor runParallel renumberMesh -overwrite -runParallel potentialFoam -initialiseUBCs +runParallel potentialFoam -initialiseUBCs -writephi runParallel checkMesh -writeFields '(nonOrthoAngle)' -constant runParallel $(getApplication) diff --git a/tutorials/incompressible/simpleFoam/motorBike/Allrun b/tutorials/incompressible/simpleFoam/motorBike/Allrun index 5646a4fbb34f762071744f10769e67ea952c5dd9..1dcc3f559be43e31dcee5f778a9c8496a1e52e7e 100755 --- a/tutorials/incompressible/simpleFoam/motorBike/Allrun +++ b/tutorials/incompressible/simpleFoam/motorBike/Allrun @@ -33,7 +33,7 @@ runParallel $decompDict snappyHexMesh -overwrite restore0Dir -processor runParallel $decompDict patchSummary -runParallel $decompDict potentialFoam +runParallel $decompDict potentialFoam -writephi runParallel $decompDict checkMesh -writeFields '(nonOrthoAngle)' -constant runParallel $decompDict $(getApplication) diff --git a/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun index 815dc6f3d0a6ce5f5ff4032f110b294f18ccfcfa..9ec0596bab62acca9301776a93baa8fb237ea0cb 100755 --- a/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun +++ b/tutorials/lagrangian/reactingParcelFoam/verticalChannel/Allrun @@ -11,8 +11,6 @@ restore0Dir # initialise with potentialFoam solution runApplication potentialFoam -rm -f 0/phi - # run the solver runApplication $(getApplication) diff --git a/tutorials/lagrangian/reactingParcelFoam/verticalChannelLTS/Allrun b/tutorials/lagrangian/reactingParcelFoam/verticalChannelLTS/Allrun index 815dc6f3d0a6ce5f5ff4032f110b294f18ccfcfa..9ec0596bab62acca9301776a93baa8fb237ea0cb 100755 --- a/tutorials/lagrangian/reactingParcelFoam/verticalChannelLTS/Allrun +++ b/tutorials/lagrangian/reactingParcelFoam/verticalChannelLTS/Allrun @@ -11,8 +11,6 @@ restore0Dir # initialise with potentialFoam solution runApplication potentialFoam -rm -f 0/phi - # run the solver runApplication $(getApplication) diff --git a/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/Allrun b/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/Allrun index eaf8f1bdb01339e76aad4962d03efb534c97e523..a5d9a2cb057a3a418c92f0fb6eac32e2eef1c21f 100755 --- a/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/Allrun +++ b/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/Allrun @@ -9,9 +9,6 @@ runApplication blockMesh runApplication potentialFoam -# Remove incompatible (volumetric) flux field -rm -f 0/phi - runApplication $(getApplication) #------------------------------------------------------------------------------ diff --git a/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/Allrun-parallel b/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/Allrun-parallel index 2392ee3aec70c2a5a8951f2ac33fe27582e3b718..8bb64db8194a2d9f23ba608c072195988d995a34 100755 --- a/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/Allrun-parallel +++ b/tutorials/lagrangian/simpleReactingParcelFoam/verticalChannel/Allrun-parallel @@ -9,9 +9,6 @@ runApplication blockMesh runApplication potentialFoam -# Remove incompatible (volumetric) flux field -rm -f 0/phi - runApplication decomposePar runParallel $(getApplication) diff --git a/tutorials/multiphase/interPhaseChangeFoam/cavitatingBullet/Allrun b/tutorials/multiphase/interPhaseChangeFoam/cavitatingBullet/Allrun index b2e91a27a0ea2ed5f85acf1bf125f42448055156..41ff036dccd17c69303d84fa7bf09a835c05b957 100755 --- a/tutorials/multiphase/interPhaseChangeFoam/cavitatingBullet/Allrun +++ b/tutorials/multiphase/interPhaseChangeFoam/cavitatingBullet/Allrun @@ -15,7 +15,7 @@ runApplication snappyHexMesh -overwrite restore0Dir # Initialise with potentialFoam solution -runApplication potentialFoam -pName p_rgh +runApplication potentialFoam -pName p_rgh -writephi # Run the solver runApplication $(getApplication) diff --git a/tutorials/preProcessing/createZeroDirectory/motorBike/Allrun b/tutorials/preProcessing/createZeroDirectory/motorBike/Allrun index 77d273e307d5a0e4bdc32ce7d840f7e54a6f0ca1..b99d37cc556fcb3dc651b5781677e9d23f5bfba6 100755 --- a/tutorials/preProcessing/createZeroDirectory/motorBike/Allrun +++ b/tutorials/preProcessing/createZeroDirectory/motorBike/Allrun @@ -15,7 +15,7 @@ runParallel snappyHexMesh -overwrite runParallel createZeroDirectory runParallel patchSummary -runParallel potentialFoam -noFunctionObjects +runParallel potentialFoam -noFunctionObjects -writephi runParallel $(getApplication) runApplication reconstructParMesh -constant