QRMatrix.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2016 OpenFOAM Foundation
9 Copyright (C) 2019-2022 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "QRMatrix.H"
30
31// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
32
33template<class MatrixType>
35(
36 const MatrixType& A
37) const
38{
39 if (mode_ == modes::FULL)
40 {
41 return A.m();
42 }
43 else if (mode_ == modes::ECONOMY)
44 {
45 return min(A.m(), A.n());
46 }
47
48 return 0;
49}
50
51
52template<class MatrixType>
54(
55 MatrixType& AT
56)
57{
58 const label n = AT.m();
59 const label m = AT.n();
60
61 List<cmptType> Rdiag(n, Zero);
62
63 for (label k = 0; k < n; ++k)
64 {
65 // Compute 2-norm of k-th column without under/overflow
66 scalar nrm = 0;
67
68 for (label i = k; i < m; ++i)
69 {
70 nrm = Foam::hypot(nrm, mag(AT(k,i)));
71 }
72
73 // Avoid divide-by-zero. Use compare with == 0 not mag or VSMALL etc,
74 // otherwise wrong results (may need more investigation)
75 if (nrm != scalar(0))
76 {
77 // Form k-th Householder vector
78 if (mag(AT(k,k)) < 0)
79 {
80 nrm *= -1;
81 }
82
83 for (label i = k; i < m; ++i)
84 {
85 AT(k,i) /= nrm;
86 }
87
88 AT(k,k) += scalar(1);
89
90 // Apply transformation to remaining columns
91 for (label j = k + 1; j < n; ++j)
92 {
93 cmptType s(Zero);
94
95 for (label i = k; i < m; ++i)
96 {
97 s += Detail::conj(AT(k,i))*AT(j,i);
98 }
99
100 if (mag(AT(k,k)) > SMALL)
101 {
102 s /= -AT(k,k);
103 }
104
105 for (label i = k; i < m; ++i)
106 {
107 AT(j,i) += s*AT(k,i);
108 }
109 }
110 }
111
112 Rdiag[k] = -nrm;
113 }
114
115 calcQ(AT);
116
117 calcR(AT, Rdiag);
118}
119
120
121template<class MatrixType>
123(
124 MatrixType& AT,
125 const bool pivoting
126)
127{
128 const label n = AT.m();
129 const label m = AT.n();
130 const label sz = min(m,n);
131
132 // Initialise permutation vector, and column norm vector
133 p_ = identity(n);
134
135 // Initialise vector norms of each column of A
136 List<scalar> norms(n, Zero);
137 for (label k = 0; k < n; ++k)
138 {
139 for (label i = 0; i < m; ++i)
140 {
141 norms[k] += magSqr(AT(k, i));
142 }
143 }
144
145 List<cmptType> Rdiag(n, Zero);
146
147 for (label k = 0; k < sz; ++k)
148 {
149 const auto it = std::next(norms.cbegin(), k);
150 const label maxNormi =
151 std::distance
152 (
153 it,
154 std::max_element(it, norms.cend())
155 );
156
157 // Swap A, P and norms according to pivot column if the current
158 // column is not the max norm column. Also avoid any column swaps
159 // where the leading element is very small
160 if (mag(AT(k + maxNormi,k)) > SMALL && maxNormi != 0)
161 {
162 const RMatrix R1(AT.subRow(k));
163 const RMatrix R2(AT.subRow(maxNormi + k));
164
165 AT.subRow(k) = R2;
166 AT.subRow(maxNormi + k) = R1;
167
168 Swap(p_[k], p_[maxNormi + k]);
169 Swap(norms[k], norms[maxNormi + k]);
170 }
171
172 {
173 // Compute 2-norm of k-th column without under/overflow
174 scalar nrm = 0;
175
176 for (label i = k; i < m; ++i)
177 {
178 nrm = Foam::hypot(nrm, mag(AT(k,i)));
179 }
180
181 if (nrm != scalar(0))
182 {
183 // Form k-th Householder vector
184 if (mag(AT(k,k)) < 0)
185 {
186 nrm *= -1;
187 }
188
189 for (label i = k; i < m; ++i)
190 {
191 AT(k,i) /= nrm;
192 }
193
194 AT(k,k) += scalar(1);
195
196 // Apply transformation to remaining columns
197 for (label j = k + 1; j < n; ++j)
198 {
199 cmptType s(Zero);
200
201 for (label i = k; i < m; ++i)
202 {
203 s += Detail::conj(AT(k,i))*AT(j,i);
204 }
205
206 if (mag(AT(k,k)) > SMALL)
207 {
208 s /= -AT(k,k);
209 }
210
211 for (label i = k; i < m; ++i)
212 {
213 AT(j,i) += s*AT(k,i);
214 }
215 }
216 }
217
218 Rdiag[k] = -nrm;
219 }
220
221 // Update norms
222 if (k < sz - 1)
223 {
224 label q = k + 1;
225 for (const auto& val : RMatrix(AT.subColumn(k, q)))
226 {
227 norms[q] -= magSqr(val);
228 ++q;
229 }
230 }
231 }
232
233 calcQ(AT);
234
235 calcR(AT, Rdiag);
236}
237
238
239template<class MatrixType>
241(
242 const MatrixType& AT
243)
244{
245 if (output_ == outputs::ONLY_R)
246 {
247 return;
248 }
249
250 const label n = AT.m();
251 const label m = AT.n();
252
253 Q_.resize(m, sz_);
254
255 MatrixType QT(Q_.transpose());
256
257 for (label k = sz_ - 1; k >= 0; --k)
258 {
259 for (label i = 0; i < m; ++i)
260 {
261 QT(k,i) = Zero;
262 }
263
264 QT(k,k) = scalar(1);
265
266 for (label j = k; j < sz_; ++j)
267 {
268 if (n > k && mag(AT(k,k)) > SMALL)
269 {
270 cmptType s(Zero);
271
272 for (label i = k; i < m; ++i)
273 {
274 s += AT(k,i)*QT(j,i);
275 }
276
277 s /= -AT(k,k);
278
279 for (label i = k; i < m; ++i)
280 {
281 QT(j,i) += s*Detail::conj(AT(k,i));
282 }
283 }
284 }
285 }
286
287 Q_ = QT.T();
288}
289
290
291template<class MatrixType>
293(
294 const MatrixType& AT,
295 const List<cmptType>& diag
296)
297{
298 if (output_ == outputs::ONLY_Q)
299 {
300 return;
301 }
302
303 const label n = AT.m();
304
305 R_.resize(sz_, n);
306
307 for (label i = 0; i < sz_; ++i)
308 {
309 for (label j = 0; j < n; ++j)
310 {
311 if (i < j)
312 {
313 R_(i,j) = AT(j,i);
314 }
315 else if (i == j)
316 {
317 R_(i,j) = diag[i];
318 }
319 }
320 }
321}
322
323
324template<class MatrixType>
325template<template<typename> class ListContainer>
327(
328 ListContainer<cmptType>& x
329) const
330{
331 const label n = R_.n();
332
333 for (label i = n - 1; 0 <= i; --i)
334 {
335 cmptType sum = x[i];
336
337 for (label j = i + 1; j < n; ++j)
338 {
339 sum -= x[j]*R_(i, j);
340 }
341
342 if (mag(R_(i, i)) < SMALL)
343 {
345 << "Back-substitution failed due to small diagonal"
346 << abort(FatalError);
347 }
348
349 x[i] = sum/R_(i, i);
350 }
351}
352
353
354template<class MatrixType>
355template<template<typename> class ListContainer>
357(
358 List<cmptType>& x,
359 const ListContainer<cmptType>& source
360) const
361{
362 // Assert (&x != &source) ?
363 const label m = Q_.m();
364
365 // x = Q_.T()*source; i.e., Q_.Tmul(source)
366 for (label i = 0; i < m; ++i)
367 {
368 x[i] = 0;
369 for (label j = 0; j < m; ++j)
370 {
371 x[i] += Q_(j, i)*source[j];
372 }
373 }
374
375 solvex(x);
376}
377
378
379// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
380
381template<class MatrixType>
383(
384 const modes mode,
385 const outputs output,
386 const bool pivoting,
387 MatrixType& A
388)
389:
390 mode_(mode),
391 output_(output),
392 sz_(calcShapeFactor(A)),
393 Q_(),
394 R_(),
395 p_()
396{
397 // Non-conjugate transpose of input matrix
398 MatrixType AT(A.transpose());
399 A.clear();
400
401 if (pivoting)
402 {
403 decompose(AT, pivoting);
404 }
405 else
406 {
407 decompose(AT);
408 }
409
410 AT.clear();
411}
412
413
414template<class MatrixType>
416(
417 const MatrixType& A,
418 const modes mode,
419 const outputs output,
420 const bool pivoting
421)
422:
423 mode_(mode),
424 output_(output),
425 sz_(calcShapeFactor(A)),
426 Q_(),
427 R_(),
428 p_()
429{
430 MatrixType AT(A.transpose());
431
432 if (pivoting)
433 {
434 decompose(AT, pivoting);
435 }
436 else
437 {
438 decompose(AT);
439 }
440
441 AT.clear();
442}
443
444
445// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
446
447template<class MatrixType>
449(
451 const UList<cmptType>& source
452) const
453{
454 solveImpl(x, source);
455}
456
457
458template<class MatrixType>
459template<class Addr>
461(
464) const
465{
466 solveImpl(x, source);
467}
468
469
470template<class MatrixType>
473(
474 const UList<cmptType>& source
475) const
476{
477 auto tresult(Q_.Tmul(source));
478
479 solvex(tresult.ref());
480
481 return tresult;
482}
483
484
485template<class MatrixType>
486template<class Addr>
489(
491) const
492{
493 auto tresult(Q_.Tmul(source));
494
495 solvex(tresult.ref());
496
497 return tresult;
498}
499
500
501template<class MatrixType>
504(
505 const RMatrix& rhs
506)
507{
508 const label mRows = R_.m();
509 const label nCols = R_.n();
510 const label pCols = rhs.n();
511
512 #ifdef FULLDEBUG
513 {
514 const label qRows = rhs.m();
515 if (mRows != qRows)
516 {
518 << "Linear system is not solvable since the number of rows of"
519 << "A and rhs are not equal:" << tab << mRows << "vs." << qRows
520 << abort(FatalError);
521 }
522
523 const List<cmptType> diag(R_.diag());
524 for (const auto& val : diag)
525 {
526 if (mag(val) < SMALL)
527 {
529 << "SMALL-valued diagonal elem in back-substitution."
530 << endl;
531 }
532 }
533 }
534 #endif
535
536 RMatrix b({nCols, pCols}, Zero);
537
538 for (label i = 0; i < pCols; ++i)
539 {
540 for (label j = mRows - 1; -1 < j; --j)
541 {
542 cmptType alpha(rhs(j, i));
543
544 for (label k = j + 1; k < mRows; ++k)
545 {
546 alpha -= b(k, i)*R_(j, k);
547 }
548
549 b(j, i) = alpha/R_(j, j);
550 }
551 }
552
553 return b;
554}
555
556
557template<class MatrixType>
560{
561 const label m = Q_.m();
562
564 SMatrix inv(m);
565
566 for (label i = 0; i < m; ++i)
567 {
568 for (label j = 0; j < m; ++j)
569 {
570 x[j] = Q_(i, j);
571 }
572 solvex(x);
573 inv.subColumn(i) = x;
574 }
575
576 return inv;
577}
578
579
580// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
581
582template<class MatrixType>
584(
585 const MatrixType& A,
586 scalar tol
587)
588{
589 typedef typename MatrixType::cmptType cmptType;
590
591 if (A.empty())
592 {
594 << "Empty matrix found."
595 << abort(FatalError);
596 }
597
598 if (A.size() == 1)
599 {
600 if (A(0,0) == cmptType(0))
601 {
602 return MatrixType({1, 1}, cmptType(0));
603 }
604
605 return MatrixType({1, 1}, cmptType(1)/(A(0,0) + cmptType(VSMALL)));
606 }
607
609 (
610 A,
614 );
615 const MatrixType& R = QRM.R();
616 const MatrixType& Q = QRM.Q();
617
618 // R1 (KP:p. 648)
619 // Find the first diagonal element index with (almost) zero value in R
620 label elemi = 0;
621 label i = 0;
622 while (i < 2)
623 {
624 const List<cmptType> diag(R.diag());
625
626 auto lessThan = [=](const cmptType& x) { return tol > mag(x); };
627
628 elemi =
629 std::distance
630 (
631 diag.cbegin(),
632 std::find_if(diag.cbegin(), diag.cend(), lessThan)
633 );
634
635 if (elemi == 0)
636 {
637 if (i == 0)
638 {
640 << "The largest diagonal element magnitude is nearly zero. "
641 << "Tightening the tolerance."
642 << endl;
643
644 tol = 1e-13;
645 ++i;
646 }
647 else
648 {
650 << "The largest diagonal element magnitude is nearly zero. "
651 << "Returning a zero matrix."
652 << endl;
653 ++i;
654
655 return MatrixType({A.n(), A.m()}, Zero);
656 }
657 }
658 else
659 {
660 i += 2;
661 }
662 }
663
664 // Exclude the first (almost) zero diagonal row and the rows below
665 // since R diagonal is already descending due to the QR column pivoting
666 const RectangularMatrix<cmptType> R1(R.subMatrix(0, 0, elemi));
667
668 // R2 (KP:p. 648)
669 if (R1.n() < R1.m())
670 {
671 const SquareMatrix<cmptType> C(R1&R1);
672
674 (
675 C,
676 QRMatrix<SquareMatrix<cmptType>>::modes::FULL,
677 QRMatrix<SquareMatrix<cmptType>>::outputs::BOTH_QR
678 );
679
681 (
682 QRSolve.solve
683 (
684 QRSolve.Q() & R1.T()
685 )
686 );
687
688 // R3 (KP:p. 648)
689 R2.resize(R.m(), R.n());
690
691 return MatrixType((QRM.P()^R2)^Q);
692 }
693 else
694 {
695 const SquareMatrix<cmptType> C(R1^R1);
696
698 (
699 C,
700 QRMatrix<SquareMatrix<cmptType>>::modes::FULL,
701 QRMatrix<SquareMatrix<cmptType>>::outputs::BOTH_QR
702 );
703
705 (
706 QRSolve.solve
707 (
708 QRSolve.Q() & R1
709 )
710 );
711
712 // R3
713 R2.resize(R.m(), R.n());
714
715 return MatrixType((QRM.P()^R2)^Q);
716 }
717}
718
719
720// ************************************************************************* //
static const Foam::dimensionedScalar A("", Foam::dimPressure, 611.21)
label k
#define R(A, B, C, D, E, F, K, M)
label n
Graphite solid properties.
Definition: C.H:53
Generic templated field type.
Definition: Field.H:82
Base for lists with indirect addressing, templated on the list contents type and the addressing type....
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:77
label n() const noexcept
The number of columns.
Definition: MatrixI.H:103
label m() const noexcept
The number of rows.
Definition: MatrixI.H:96
void resize(const label m, const label n)
Change Matrix dimensions, preserving the elements.
Definition: Matrix.C:324
QRMatrix computes QR decomposition of a given scalar/complex matrix A into the following:
Definition: QRMatrix.H:212
SMatrix inv() const
Return the inverse of (Q*R), solving x = (Q*R).inv()*source.
Definition: QRMatrix.C:559
void solve(List< cmptType > &x, const UList< cmptType > &source) const
Definition: QRMatrix.C:449
QRMatrix()=delete
Construct null.
pivoting
Options for the column pivoting.
Definition: QRMatrix.H:236
SMatrix P() const
Create and return the permutation matrix.
Definition: QRMatrixI.H:33
outputs
Options for the output types.
Definition: QRMatrix.H:228
modes
Options for the decomposition mode.
Definition: QRMatrix.H:221
MatrixType::cmptType cmptType
Definition: QRMatrix.H:215
const MatrixType & Q() const noexcept
Return const reference to Q.
Definition: QRMatrix.H:341
const MatrixType & R() const noexcept
Return const reference to R.
Definition: QRMatrix.H:353
A templated (M x N) rectangular matrix of objects of <Type>, containing M*N elements,...
A 1D vector of objects of type <T>, where the size of the vector is known and can be used for subscri...
Definition: UList.H:94
A class for managing temporary objects.
Definition: tmp.H:65
bool decompose() const noexcept
Query the decompose flag (normally off)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
#define WarningInFunction
Report a warning using Foam::Warning.
MatrixType pinv(const MatrixType &A, scalar tol=1e-5)
Definition: QRMatrix.C:584
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:38
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
mode_t mode(const fileName &name, const bool followLink=true)
Return the file mode, normally following symbolic links.
Definition: MSwindows.C:572
void Swap(DynamicList< T, SizeMinA > &a, DynamicList< T, SizeMinB > &b)
Definition: DynamicList.H:408
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:372
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
dimensionedScalar hypot(const dimensionedScalar &x, const dimensionedScalar &y)
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
errorManip< error > abort(error &err)
Definition: errorManip.H:144
static Ostream & output(Ostream &os, const IntRange< T > &range)
Definition: IntRanges.C:66
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
error FatalError
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
static bool lessThan(const scalar &val, const scalar &upper)
void diag(pointPatchField< vector > &, const pointPatchField< tensor > &)
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
constexpr char tab
The tab '\t' character(0x09)
Definition: Ostream.H:52
volScalarField & alpha
CEqn solve()
volScalarField & b
Definition: createFields.H:27
volScalarField & e
Definition: createFields.H:11