preCICE
Loading...
Searching...
No Matches
IQNIMVJAcceleration.cpp
Go to the documentation of this file.
1#ifndef PRECICE_NO_MPI
2
3#include <Eigen/Core>
4#include <algorithm>
5#include <fstream>
6#include <map>
7#include <memory>
8#include <string>
9#include <utility>
10
15#include "com/Communication.hpp"
19#include "logging/LogMacros.hpp"
21#include "profiling/Event.hpp"
23#include "utils/IntraComm.hpp"
24#include "utils/assertion.hpp"
25
27
28namespace precice::acceleration {
29
30// ==================================================================================
32 double initialRelaxation,
33 bool forceInitialRelaxation,
34 int maxIterationsUsed,
35 int pastTimeWindowsReused,
36 int filter,
37 double singularityLimit,
38 std::vector<int> dataIDs,
39 OnBoundViolation onBoundViolation,
40 const impl::PtrPreconditioner &preconditioner,
41 bool alwaysBuildJacobian,
42 int imvjRestartType,
43 int chunkSize,
44 int RSLSreusedTimeWindows,
45 double RSSVDtruncationEps,
46 bool reducedTimeGrid)
47 : BaseQNAcceleration(initialRelaxation, forceInitialRelaxation, maxIterationsUsed, pastTimeWindowsReused,
48 filter, singularityLimit, std::move(dataIDs), onBoundViolation, preconditioner, reducedTimeGrid),
49 // _secondaryOldXTildes(),
52 _Wtil(),
53 _WtilChunk(),
58 _parMatrixOps(nullptr),
59 _svdJ(RSSVDtruncationEps, preconditioner),
60 _alwaysBuildJacobian(alwaysBuildJacobian),
61 _imvjRestartType(imvjRestartType),
62 _imvjRestart(imvjRestartType > 0),
63 _chunkSize(chunkSize),
64 _RSLSreusedTimeWindows(RSLSreusedTimeWindows),
65 _nbRestarts(0),
66 _avgRank(0)
67{
68}
69
70// ==================================================================================
72
73// ==================================================================================
75 const DataMap &cplData)
76{
81
83
84 // call the base method for common update of V, W matrices
85 // important that base method is called before updating _Wtil
87
88 // update _Wtil if the efficient computation of the quasi-Newton update is used
89 // or update current Wtil if the restart mode of imvj is used
92 // do nothing: constant relaxation
93 } else {
94 if (not _firstIteration) {
95 // Update matrix _Wtil = (W - J_prev*V) with newest information
96
97 Eigen::VectorXd v = _matrixV.col(0);
98 Eigen::VectorXd w = _matrixW.col(0);
99
100 // here, we check for _Wtil.cols() as the matrices V, W need to be updated before hand
101 // and thus getLSSystemCols() does not yield the correct result.
102 bool columnLimitReached = _Wtil.cols() == _maxIterationsUsed;
103 bool overdetermined = _Wtil.cols() <= getLSSystemRows();
104
105 Eigen::VectorXd wtil = Eigen::VectorXd::Zero(_matrixW.rows());
106
107 // add column: Wtil(:,0) = W(:,0) - sum_q [ Wtil^q * ( Z^q * V(:,0)) ]
108 // |--- J_prev ---|
109 // iterate over all stored Wtil and Z matrices in current chunk
110 if (_imvjRestart) {
111 for (int i = 0; i < static_cast<int>(_WtilChunk.size()); i++) {
112 int colsLSSystemBackThen = _pseudoInverseChunk[i].rows();
113 PRECICE_ASSERT(colsLSSystemBackThen == _WtilChunk[i].cols(), colsLSSystemBackThen, _WtilChunk[i].cols());
114 Eigen::VectorXd Zv = Eigen::VectorXd::Zero(colsLSSystemBackThen);
115 // multiply: Zv := Z^q * V(:,0) of size (m x 1)
116 _parMatrixOps->multiply(_pseudoInverseChunk[i], v, Zv, colsLSSystemBackThen, getLSSystemRows(), 1);
117 // multiply: Wtil^q * Zv dimensions: (n x m) * (m x 1), fully local
118 wtil += _WtilChunk[i] * Zv;
119 }
120
121 // store columns if restart mode = RS-LS
122 if (_imvjRestartType == RS_LS) {
125 _matrixCols_RSLS.front()++;
126 }
127
128 // imvj without restart is used, but efficient update, i.e. no Jacobian assembly in each iteration
129 // add column: Wtil(:,0) = W(:,0) - J_prev * V(:,0)
130 } else {
131 // compute J_prev * V(0) := wtil the new column in _Wtil of dimension: (n x n) * (n x 1) = (n x 1),
132 // parallel: (n_global x n_local) * (n_local x 1) = (n_local x 1)
133 _parMatrixOps->multiply(_oldInvJacobian, v, wtil, _dimOffsets, getLSSystemRows(), getLSSystemRows(), 1, false, false);
134 }
135 wtil *= -1;
136 wtil += w;
137
138 if (not columnLimitReached && overdetermined) {
140 } else {
142 }
143 }
144 }
145 }
146}
147
148// ==================================================================================
149void IQNIMVJAcceleration::computeQNUpdate(Eigen::VectorXd &xUpdate)
150{
160
169 computeNewtonUpdate(xUpdate);
170 } else {
172 }
173}
174
175// ==================================================================================
177 Eigen::MatrixXd &pseudoInverse)
178{
184 auto Q = _qrV.matrixQ();
185 auto R = _qrV.matrixR();
186
187 PRECICE_ASSERT(pseudoInverse.rows() == _qrV.cols(), pseudoInverse.rows(), _qrV.cols());
188 PRECICE_ASSERT(pseudoInverse.cols() == _qrV.rows(), pseudoInverse.cols(), _qrV.rows());
189
190 Eigen::VectorXd yVec(pseudoInverse.rows());
191
192 // assertions for the case of processors with no vertices
195 PRECICE_ASSERT(_qrV.rows() == 0, _qrV.rows());
196 PRECICE_ASSERT(Q.size() == 0, Q.size());
197 }
198
199 // backsubstitution
200 for (int i = 0; i < Q.rows(); i++) {
201 Eigen::VectorXd Qrow = Q.row(i);
202 yVec = R.triangularView<Eigen::Upper>().solve<Eigen::OnTheLeft>(Qrow);
203 pseudoInverse.col(i) = yVec;
204 } // ----------------
205
206 // scale pseudo inverse back Z := Z' * P,
207 // Z' is scaled pseudo inverse i.e, Z' = R^-1 * Q^T * P^-1
208 _preconditioner->apply(pseudoInverse, true);
209 // e.stop(true);
210}
211
212// ==================================================================================
214{
219
220 PRECICE_ASSERT(_matrixV.rows() == _qrV.rows(), _matrixV.rows(), _qrV.rows());
222
223 _Wtil = Eigen::MatrixXd::Zero(_residuals.rows(), _qrV.cols());
224
225 // imvj restart mode: re-compute Wtil: Wtil = W - sum_q [ Wtil^q * (Z^q*V) ]
226 // |--- J_prev ---|
227 // iterate over all stored Wtil and Z matrices in current chunk
228 if (_imvjRestart) {
229 for (int i = 0; i < static_cast<int>(_WtilChunk.size()); i++) {
230 int colsLSSystemBackThen = _pseudoInverseChunk[i].rows();
231 PRECICE_ASSERT(colsLSSystemBackThen == _WtilChunk[i].cols(), colsLSSystemBackThen, _WtilChunk[i].cols());
232 Eigen::MatrixXd ZV = Eigen::MatrixXd::Zero(colsLSSystemBackThen, _qrV.cols());
233 // multiply: ZV := Z^q * V of size (m x m) with m=#cols, stored on each proc.
234 _parMatrixOps->multiply(_pseudoInverseChunk[i], _matrixV, ZV, colsLSSystemBackThen, getLSSystemRows(), _qrV.cols());
235 // multiply: Wtil^q * ZV dimensions: (n x m) * (m x m), fully local and embarrassingly parallel
236 _Wtil += _WtilChunk[i] * ZV;
237 }
238
239 // imvj without restart is used, i.e., recompute Wtil: Wtil = W - J_prev * V
240 } else {
241 // multiply J_prev * V = W_til of dimension: (n x n) * (n x m) = (n x m),
242 // parallel: (n_global x n_local) * (n_local x m) = (n_local x m)
244 }
245
246 // W_til = (W-J_inv_n*V) = (W-V_tilde)
247 _Wtil *= -1.;
248 _Wtil = _Wtil + _matrixW;
249
250 _resetLS = false;
251 // e.stop(true);
252}
253
254// ==================================================================================
256{
264
268 Eigen::MatrixXd Z(_qrV.cols(), _qrV.rows());
269 pseudoInverse(Z);
270
274 PRECICE_ASSERT(_matrixV.rows() == _qrV.rows(), _matrixV.rows(), _qrV.rows());
276 if (_resetLS) {
277 buildWtil();
278 PRECICE_WARN(" ATTENTION, in buildJacobian call for buildWtill() - this should not be the case except the coupling did only one iteration");
279 }
280
287 // --------
288
289 // update Jacobian
291}
292
293// ==================================================================================
295{
297
314
318 Eigen::MatrixXd Z(_qrV.cols(), _qrV.rows());
319 pseudoInverse(Z);
320
324 PRECICE_ASSERT(_matrixV.rows() == _qrV.rows(), _matrixV.rows(), _qrV.rows());
326
327 // rebuild matrix Wtil if V changes substantially.
328 if (_resetLS) {
329 buildWtil();
330 }
331
343 Eigen::VectorXd negativeResiduals = -_primaryResiduals;
344 Eigen::VectorXd r_til = Eigen::VectorXd::Zero(getLSSystemCols());
345 _parMatrixOps->multiply(Z, negativeResiduals, r_til, getLSSystemCols(), getLSSystemRows(), 1); // --------
346
355 Eigen::VectorXd xUptmp(_residuals.size());
356 xUpdate = Eigen::VectorXd::Zero(_residuals.size());
357 xUptmp = _Wtil * r_til; // local product, result is naturally distributed.
358
365 if (_imvjRestart) {
366 for (int i = 0; i < static_cast<int>(_WtilChunk.size()); i++) {
367 int colsLSSystemBackThen = _pseudoInverseChunk[i].rows();
368 PRECICE_ASSERT(colsLSSystemBackThen == _WtilChunk[i].cols(), colsLSSystemBackThen, _WtilChunk[i].cols());
369 r_til = Eigen::VectorXd::Zero(colsLSSystemBackThen);
370 // multiply: r_til := Z^q * (-res) of size (m x 1) with m=#cols of LS at that time, result stored on each proc.
371 _parMatrixOps->multiply(_pseudoInverseChunk[i], negativeResiduals, r_til, colsLSSystemBackThen, getLSSystemRows(), 1);
372 // multiply: Wtil^q * r_til dimensions: (n x m) * (m x 1), fully local and embarrassingly parallel
373 xUpdate += _WtilChunk[i] * r_til;
374 }
375
376 // imvj without restart is used, i.e., compute directly J_prev * (-res)
377 } else {
378 _parMatrixOps->multiply(_oldInvJacobian, negativeResiduals, xUpdate, _dimOffsets, getLSSystemRows(), getLSSystemRows(), 1, false, false);
379 PRECICE_DEBUG("Mult J*V DONE");
380 }
381
382 xUpdate += xUptmp;
383
384 // pending deletion: delete Wtil
386 _Wtil.conservativeResize(0, 0);
387 _resetLS = true;
388 }
389}
390
391// ==================================================================================
392void IQNIMVJAcceleration::computeNewtonUpdate(Eigen::VectorXd &xUpdate)
393{
395
400
403 Eigen::MatrixXd Z(_qrV.cols(), _qrV.rows());
404 pseudoInverse(Z);
405
408 buildWtil();
409
416
417 // update Jacobian
419
422 Eigen::VectorXd negativeResiduals = -_primaryResiduals;
423
424 // multiply J_inv * (-res) = x_Update of dimension: (n x n) * (n x 1) = (n x 1),
425 // parallel: (n_global x n_local) * (n_local x 1) = (n_local x 1)
426 _parMatrixOps->multiply(_invJacobian, negativeResiduals, xUpdate, _dimOffsets, getLSSystemRows(), getPrimaryLSSystemRows(), 1, false, false); // --------
427}
428
429// ==================================================================================
431{
433
434 // int used_storage = 0;
435 // int theoreticalJ_storage = 2*getLSSystemRows()*_primaryResiduals.size() + 3*_primaryResiduals.size()*getLSSystemCols() + _primaryResiduals.size()*_primaryResiduals.size();
436 // ------------ RESTART SVD ------------
438
439 // we need to compute the updated SVD of the scaled Jacobian matrix
440 // |= APPLY PRECONDITIONING J_prev = Wtil^q, Z^q ===|
441 for (int i = 0; i < static_cast<int>(_WtilChunk.size()); i++) {
442 _preconditioner->apply(_WtilChunk[i]);
443 _preconditioner->revert(_pseudoInverseChunk[i], true);
444 }
445 // |=================== ===|
446
447 Rank rankBefore = _svdJ.isSVDinitialized() ? _svdJ.rank() : 0;
448
449 // if it is the first time window, there is no initial SVD, so take all Wtil, Z matrices
450 // otherwise, the first element of each container holds the decomposition of the current
451 // truncated SVD, i.e., Wtil^0 = \phi, Z^0 = S\psi^T, this should not be added to the SVD.
452 int q = _svdJ.isSVDinitialized() ? 1 : 0;
453
454 // perform M-1 rank-1 updates of the truncated SVD-dec of the Jacobian
455 for (; q < static_cast<int>(_WtilChunk.size()); q++) {
456 // update SVD, i.e., PSI * SIGMA * PHI^T <-- PSI * SIGMA * PHI^T + Wtil^q * Z^q
457 _svdJ.update(_WtilChunk[q], _pseudoInverseChunk[q].transpose());
458 // used_storage += 2*_WtilChunk.size();
459 }
460 // int m = _WtilChunk[q].cols(), n = _WtilChunk[q].rows();
461 // used_storage += 2*rankBefore*m + 4*m*n + 2*m*m + (rankBefore+m)*(rankBefore+m) + 2*n*(rankBefore+m);
462
463 // drop all stored Wtil^q, Z^q matrices
464 _WtilChunk.clear();
465 _pseudoInverseChunk.clear();
466
467 auto &psi = _svdJ.matrixPsi();
468 auto &sigma = _svdJ.singularValues();
469 auto &phi = _svdJ.matrixPhi();
470
471 // multiply sigma * phi^T, phi is distributed block-row wise, phi^T is distributed block-column wise
472 // sigma is stored local on each proc, thus, the multiplication is fully local, no communication.
473 // Z = sigma * phi^T
474 Eigen::MatrixXd Z(phi.cols(), phi.rows());
475 for (int i = 0; i < static_cast<int>(Z.rows()); i++)
476 for (int j = 0; j < static_cast<int>(Z.cols()); j++)
477 Z(i, j) = phi(j, i) * sigma[i];
478
479 Rank rankAfter = _svdJ.rank();
480 int waste = _svdJ.getWaste();
481 _avgRank += rankAfter;
482
483 // store factorized truncated SVD of J
484 _WtilChunk.push_back(psi);
485 _pseudoInverseChunk.push_back(Z);
486
487 // |= REVERT PRECONDITIONING J_prev = Wtil^0, Z^0 ==|
488 _preconditioner->revert(_WtilChunk.front());
489 _preconditioner->apply(_pseudoInverseChunk.front(), true);
490 // |=================== ==|
491
492 PRECICE_DEBUG("MVJ-RESTART, mode=SVD. Rank of truncated SVD of Jacobian {}, new modes: {}, truncated modes: {} avg rank: {}", rankAfter, rankAfter - rankBefore, waste, _avgRank / _nbRestarts);
493
494 // double percentage = 100.0*used_storage/(double)theoreticalJ_storage;
496 _infostringstream << " - MVJ-RESTART " << _nbRestarts << ", mode= SVD -\n new modes: " << rankAfter - rankBefore << "\n rank svd: " << rankAfter << "\n avg rank: " << _avgRank / _nbRestarts << "\n truncated modes: " << waste << "\n"
497 << '\n';
498 }
499
500 // ------------ RESTART LEAST SQUARES ------------
502 // drop all stored Wtil^q, Z^q matrices
503 _WtilChunk.clear();
504 _pseudoInverseChunk.clear();
505
506 if (_matrixV_RSLS.cols() > 0) {
507 // avoid that the system is getting too squared
508 while (_matrixV_RSLS.cols() * 2 >= getLSSystemRows()) {
510 }
511
512 // preconditioning
513 // V needs to be sclaed to compute the pseudo inverse
514 // W only needs to be scaled, as the design requires to store scaled
515 // matrices Wtil^0 and Z^0 as initial guess after the restart
518
521 // for QR2-filter, the QR-dec is computed in qr-applyFilter()
523 for (int i = 0; i < static_cast<int>(_matrixV_RSLS.cols()); i++) {
524 Eigen::VectorXd v = _matrixV_RSLS.col(i);
525 qr.pushBack(v); // same order as matrix V_RSLS
526 }
527 }
528
529 // apply filter
531 std::vector<int> delIndices(0);
533 // start with largest index (as V,W matrices are shrunk and shifted
534 for (int i = delIndices.size() - 1; i >= 0; i--) {
535 removeMatrixColumnRSLS(delIndices[i]);
536 }
537 PRECICE_ASSERT(_matrixV_RSLS.cols() == qr.cols(), _matrixV_RSLS.cols(), qr.cols());
538 }
539
544 auto Q = qr.matrixQ();
545 auto R = qr.matrixR();
546 Eigen::MatrixXd pseudoInverse(qr.cols(), qr.rows());
547 Eigen::VectorXd yVec(pseudoInverse.rows());
548
549 // backsubstitution
550 for (int i = 0; i < Q.rows(); i++) {
551 Eigen::VectorXd Qrow = Q.row(i);
552 yVec = R.triangularView<Eigen::Upper>().solve<Eigen::OnTheLeft>(Qrow);
553 pseudoInverse.col(i) = yVec;
554 }
555
556 // scale pseudo inverse back Z := Z' * P,
557 // Z' is scaled pseudo inverse i.e, Z' = R^-1 * Q^T * P^-1
558 //_preconditioner->apply(pseudoInverse, true, false);
559
560 // store factorization of least-squares initial guess for Jacobian
561 _WtilChunk.push_back(_matrixW_RSLS);
563
564 // |= REVERT PRECONDITIONING J_prev = Wtil^0, Z^0 ==|
565 _preconditioner->revert(_WtilChunk.front());
566 _preconditioner->apply(_pseudoInverseChunk.front(), true);
569 // |=================== ==|
570 }
571
572 PRECICE_DEBUG("MVJ-RESTART, mode=LS. Restart with {} columns from {} time windows.", _matrixV_RSLS.cols(), _RSLSreusedTimeWindows);
574 _infostringstream << " - MVJ-RESTART" << _nbRestarts << ", mode= LS -\n used cols: " << _matrixV_RSLS.cols() << "\n R_RS: " << _RSLSreusedTimeWindows << "\n"
575 << '\n';
576 }
577
578 // ------------ RESTART ZERO ------------
580 // drop all stored Wtil^q, Z^q matrices
581 _WtilChunk.clear();
582 _pseudoInverseChunk.clear();
583
584 PRECICE_DEBUG("MVJ-RESTART, mode=Zero");
585
587
588 // re-compute Wtil -- compensate for dropping of Wtil_0 and Z_0:
589 // Wtil_q <-- Wtil_q + Wtil^0 * (Z^0*V_q)
590 for (int i = static_cast<int>(_WtilChunk.size()) - 1; i >= 1; i--) {
591
592 int colsLSSystemBackThen = _pseudoInverseChunk.front().rows();
593 PRECICE_ASSERT(colsLSSystemBackThen == _WtilChunk.front().cols(), colsLSSystemBackThen, _WtilChunk.front().cols());
594 Eigen::MatrixXd ZV = Eigen::MatrixXd::Zero(colsLSSystemBackThen, _qrV.cols());
595 // multiply: ZV := Z^q * V of size (m x m) with m=#cols, stored on each proc.
596 _parMatrixOps->multiply(_pseudoInverseChunk.front(), _matrixV, ZV, colsLSSystemBackThen, getLSSystemRows(), _qrV.cols());
597 // multiply: Wtil^0 * (Z_0*V) dimensions: (n x m) * (m x m), fully local and embarrassingly parallel
598 Eigen::MatrixXd tmp = Eigen::MatrixXd::Zero(_residuals.rows(), _qrV.cols());
599 tmp = _WtilChunk.front() * ZV;
600 _WtilChunk[i] += tmp;
601
602 // drop oldest pair Wtil_0 and Z_0
603 PRECICE_ASSERT(not _WtilChunk.empty());
605 _WtilChunk.erase(_WtilChunk.begin());
607 }
608
610 PRECICE_ASSERT(false); // should not happen, in this case _imvjRestart=false
611 } else {
612 PRECICE_ASSERT(false);
613 }
614}
615
616// ==================================================================================
618 const DataMap &cplData)
619{
621
622 // truncate V_RSLS and W_RSLS matrices according to _RSLSreusedTimeWindows
623 if (_imvjRestartType == RS_LS) {
624 if (_matrixCols_RSLS.front() == 0) { // Did only one iteration
625 _matrixCols_RSLS.pop_front();
626 }
627 if (_RSLSreusedTimeWindows == 0) {
628 _matrixV_RSLS.resize(0, 0);
629 _matrixW_RSLS.resize(0, 0);
630 _matrixCols_RSLS.clear();
631 } else if (static_cast<int>(_matrixCols_RSLS.size()) > _RSLSreusedTimeWindows) {
632 int toRemove = _matrixCols_RSLS.back();
633 PRECICE_ASSERT(toRemove > 0, toRemove);
634 if (_matrixV_RSLS.size() > 0) {
635 PRECICE_ASSERT(_matrixV_RSLS.cols() > toRemove, _matrixV_RSLS.cols(), toRemove);
636 }
637
638 // remove columns
639 for (int i = 0; i < toRemove; i++) {
642 }
643 _matrixCols_RSLS.pop_back();
644 }
645 _matrixCols_RSLS.push_front(0);
646 }
647
648 //_info2<<'\n';
649
650 // if efficient update of imvj is enabled
652 // need to apply the preconditioner, as all data structures are reverted after
653 // call to computeQNUpdate. Need to call this before the preconditioner is updated.
654
655 // |= REBUILD QR-dec if needed ============|
656 // apply scaling to V, V' := P * V (only needed to reset the QR-dec of V)
658
659 if (_preconditioner->requireNewQR()) {
660 if (not(_filter == Acceleration::QR2FILTER)) { // for QR2 filter, there is no need to do this twice
661 auto failAddedCols = _qrV.reset(_matrixV, getPrimaryLSSystemRows());
662 for (int i : failAddedCols) {
664 }
665 PRECICE_ASSERT(_matrixV.cols() == _qrV.cols(), _matrixV.cols(), _qrV.cols());
666 }
667 _preconditioner->newQRfulfilled();
668 }
669 // apply the configured filter to the LS system
670 // as it changed in BaseQNAcceleration::iterationsConverged()
672 _preconditioner->revert(_matrixV);
673 // |=================== ============|
674
675 // ------- RESTART/ JACOBIAN ASSEMBLY -------
676 if (_imvjRestart) {
677
678 // add the matrices Wtil and Z of the converged configuration to the storage containers
679 Eigen::MatrixXd Z(_qrV.cols(), _qrV.rows());
680 // compute pseudo inverse using QR factorization and back-substitution
681 // also compensates for the scaling of V, i.e.,
682 // reverts Z' = R^-1 * Q^T * P^-1 as Z := Z' * P
683 pseudoInverse(Z);
684
685 // push back unscaled pseudo Inverse, Wtil is also unscaled.
686 // all objects in Wtil chunk and Z chunk are NOT PRECONDITIONED
687 _WtilChunk.push_back(_Wtil);
688 _pseudoInverseChunk.push_back(Z);
689
693 if (static_cast<int>(_WtilChunk.size()) >= _chunkSize + 1) {
694
695 // < RESTART >
696 _nbRestarts++;
697 profiling::Event restartUpdate("IMVJRestart");
698 restartIMVJ();
699 restartUpdate.stop();
700 }
701
702 // only in imvj normal mode with efficient update:
703 } else {
704
705 // compute explicit representation of Jacobian
707 }
708
714 //_Wtil.conservativeResize(0, 0);
715 _resetLS = true;
716 }
717 }
718
719 // only store Jacobian if imvj is in normal mode, i.e., the Jacobian is build
720 if (not _imvjRestart) {
721 // store inverse Jacobian from converged time window. NOT SCALED with preconditioner
723 }
724}
725
726// ==================================================================================
728 int columnIndex)
729{
730 PRECICE_TRACE(columnIndex, _matrixV.cols());
731 PRECICE_ASSERT(_matrixV.cols() > 1, _matrixV.cols());
732 PRECICE_ASSERT(_Wtil.cols() > 1);
733
734 // remove column from matrix _Wtil
735 if (not _resetLS && not _alwaysBuildJacobian)
737
739}
740
742{
743 int entries = _primaryResiduals.size();
744 int cplDataEntries = _residuals.size();
745 int global_n = 0;
746
748 global_n = cplDataEntries;
749 } else {
750 global_n = _dimOffsets.back();
751 }
752
753 // initialize parallel matrix-matrix operation module
754 _parMatrixOps = std::make_shared<impl::ParallelMatrixOperations>();
755 _parMatrixOps->initialize(not _imvjRestart);
756 _svdJ.initialize(_parMatrixOps, global_n, getLSSystemRows());
757
758 if (not _imvjRestart) {
759 // only need memory for Jacobain if not in restart mode
760 _invJacobian = Eigen::MatrixXd::Zero(global_n, entries);
761 _oldInvJacobian = Eigen::MatrixXd::Zero(global_n, entries);
762 }
763
764 // initialize parallel matrix-matrix operation module
765 _parMatrixOps = std::make_shared<impl::ParallelMatrixOperations>();
766 _parMatrixOps->initialize(not _imvjRestart);
767 _svdJ.reset();
768 _svdJ.initialize(_parMatrixOps, global_n, getLSSystemRows());
769
770 // initialize V, W matrices for the LS restart
771 if (_imvjRestartType == RS_LS) {
772 _matrixCols_RSLS.clear();
773 _matrixCols_RSLS.push_front(0);
774 _matrixV_RSLS = Eigen::MatrixXd::Zero(entries, 0);
775 _matrixW_RSLS = Eigen::MatrixXd::Zero(cplDataEntries, 0);
776 }
777 _Wtil = Eigen::MatrixXd::Zero(cplDataEntries, 0);
778 if (_imvjRestartType > 0) {
779 // drop all stored Wtil^q, Z^q matrices
780 _WtilChunk.clear();
781 _pseudoInverseChunk.clear();
782 }
783
785 _infostringstream << " IMVJ restart mode: " << _imvjRestart << "\n chunk size: " << _chunkSize << "\n trunc eps: " << _svdJ.getThreshold() << "\n R_RS: " << _RSLSreusedTimeWindows << "\n--------\n"
786 << '\n';
787 }
788}
789
790// ==================================================================================
792 int columnIndex)
793{
794 PRECICE_TRACE(columnIndex, _matrixV_RSLS.cols());
795 PRECICE_ASSERT(_matrixV_RSLS.cols() > 1);
796
799
800 // Reduce column count
801 std::deque<int>::iterator iter = _matrixCols_RSLS.begin();
802 int cols = 0;
803 while (iter != _matrixCols_RSLS.end()) {
804 cols += *iter;
805 if (cols > columnIndex) {
806 PRECICE_ASSERT(*iter > 0);
807 *iter -= 1;
808 if (*iter == 0) {
809 _matrixCols_RSLS.erase(iter);
810 }
811 break;
812 }
813 iter++;
814 }
815}
816
817} // namespace precice::acceleration
818
819#endif
#define PRECICE_WARN(...)
Definition LogMacros.hpp:12
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
OnBoundViolation
Options for handling bound violations.
std::map< int, cplscheme::PtrCouplingData > DataMap
Map from data ID to data values.
virtual void updateDifferenceMatrices(const DataMap &cplData)
Updates the V, W matrices (as well as the matrices for the secondary data)
int getLSSystemCols() const
: computes number of cols in least squares system, i.e, number of cols in _matrixV,...
virtual void applyFilter()
Applies the filter method for the least-squares system, defined in the configuration.
Eigen::VectorXd _primaryResiduals
Current iteration residuals of primary IQN data. Temporary.
std::ostringstream _infostringstream
write some debug/acceleration info to file
bool _resetLS
If true, the LS system has been modified (reset or recomputed) in such a way, that mere updating of m...
const int _filter
filter method that is used to maintain good conditioning of the least-squares system Either of two ty...
Eigen::MatrixXd _matrixV
Stores residual deltas.
std::vector< int > _dimOffsets
Stores the local dimensions, i.e., the offsets in _invJacobian for all processors.
const double _singularityLimit
Determines sensitivity when two matrix columns are considered equal.
const int _maxIterationsUsed
Maximum number of old data iterations kept.
Eigen::VectorXd _residuals
Current iteration residuals of IQN data. Temporary.
virtual void removeMatrixColumn(int columnIndex)
Removes one iteration from V,W matrices and adapts _matrixCols.
Eigen::MatrixXd _matrixW
Stores x tilde deltas, where x tilde are values computed by solvers.
const int _timeWindowsReused
Maximum number of old time windows (with data values) kept.
bool _firstIteration
Indicates the first iteration, where constant relaxation is used.
impl::PtrPreconditioner _preconditioner
Preconditioner for least-squares system if vectorial system is used.
impl::QRFactorization _qrV
Stores the current QR decomposition ov _matrixV, can be updated via deletion/insertion of columns.
BaseQNAcceleration(double initialRelaxation, bool forceInitialRelaxation, int maxIterationsUsed, int timeWindowsReused, int filter, double singularityLimit, std::vector< int > dataIDs, OnBoundViolation onBoundViolation, impl::PtrPreconditioner preconditioner, bool reducedTimeGrid)
const bool _imvjRestart
: If true, the imvj method is used with the restart chunk based approach that avoids to explicitly bu...
int _RSLSreusedTimeWindows
: Number of reused time windows at restart if restart-mode = RS-LS
const int _imvjRestartType
: Indicates the type of the imvj restart-mode:
void updateDifferenceMatrices(const DataMap &cplData) override
: updates the V, W matrices (as well as the matrices for the secondary data)
void pseudoInverse(Eigen::MatrixXd &pseudoInverse)
: computes the pseudo inverse of V multiplied with V^T, i.e., Z = (V^TV)^-1V^T via QR-dec
~IQNIMVJAcceleration() override
Destructor, empty.
Eigen::MatrixXd _matrixW_RSLS
stores columns from previous _RSLSreusedTimeWindows time windows if RS-LS restart-mode is active
int _nbRestarts
tracks the number of restarts of IMVJ
void computeQNUpdate(Eigen::VectorXd &xUpdate) override
: computes the IQNIMVJ update using QR decomposition of V, furthermore it updates the inverse of the ...
Eigen::MatrixXd _invJacobian
stores the approximation of the inverse Jacobian of the system at current time window.
void removeMatrixColumn(int columnIndex) override
: Removes one iteration from V,W matrices and adapts _matrixCols.
void specializedInitializeVectorsAndPreconditioner(const DataMap &cplData) final override
IQNIMVJAcceleration(double initialRelaxation, bool forceInitialRelaxation, int maxIterationsUsed, int pastTimeWindowsReused, int filter, double singularityLimit, std::vector< int > dataIDs, OnBoundViolation onBoundViolation, const impl::PtrPreconditioner &preconditioner, bool alwaysBuildJacobian, int imvjRestartType, int chunkSize, int RSLSreusedTimeWindows, double RSSVDtruncationEps, bool reducedTimeGrid)
Constructor.
impl::PtrParMatrixOps _parMatrixOps
encapsulates matrix-matrix and matrix-vector multiplications for serial and parallel execution
int _chunkSize
: Number of time windows between restarts for the imvj method in restart mode
void computeNewtonUpdate(Eigen::VectorXd &update)
: computes the quasi-Newton update vector based on the matrices V and W using a QR decomposition of V...
Eigen::MatrixXd _Wtil
stores the sub result (W-J_prev*V) for the current iteration
void removeMatrixColumnRSLS(int columnINdex)
: Removes one column form the V_RSLS and W_RSLS matrices and adapts _matrixCols_RSLS
void buildWtil()
: re-computes the matrix _Wtil = ( W - J_prev * V) instead of updating it according to V
void specializedIterationsConverged(const DataMap &cplData) override
Marks a iteration sequence as converged.
bool _alwaysBuildJacobian
If true, the less efficient method to compute the quasi-Newton update is used, that explicitly builds...
void restartIMVJ()
: restarts the imvj method, i.e., drops all stored matrices Wtil and Z and computes a initial guess o...
void buildJacobian()
: computes a explicit representation of the Jacobian, i.e., n x n matrix
void computeNewtonUpdateEfficient(Eigen::VectorXd &update)
: computes the quasi-Newton update vector based on the same numerics as above. However,...
std::vector< Eigen::MatrixXd > _pseudoInverseChunk
stores all pseudo inverses within the current chunk of the imvj restart mode, disabled if _imvjRestar...
Eigen::MatrixXd _oldInvJacobian
stores the approximation of the inverse Jacobian from the previous time window.
Eigen::MatrixXd _matrixV_RSLS
stores columns from previous _RSLSreusedTimeWindows time windows if RS-LS restart-mode is active
std::deque< int > _matrixCols_RSLS
number of cols per time window
std::vector< Eigen::MatrixXd > _WtilChunk
stores all Wtil matrices within the current chunk of the imvj restart mode, disabled if _imvjRestart ...
impl::SVDFactorization _svdJ
holds and maintains a truncated SVD decomposition of the Jacobian matrix
Class that provides functionality for a dynamic QR-decomposition, that can be updated in O(mn) flops ...
Eigen::MatrixXd & matrixR()
returns a matrix representation of the upper triangular matrix R
void applyFilter(double singularityLimit, std::vector< int > &delIndices, Eigen::MatrixXd &V)
filters the least squares system, i.e., the decomposition Q*R = V according to the defined filter tec...
void pushBack(const Eigen::VectorXd &v)
inserts a new column at position _cols-1, i.e., appends a column at the end and updates the QR factor...
Eigen::MatrixXd & matrixQ()
returns a matrix representation of the orthogonal matrix Q
void stop()
Stops a running event.
Definition Event.cpp:51
static bool isPrimary()
True if this process is running the primary rank.
Definition IntraComm.cpp:52
static bool isParallel()
True if this process is running in parallel.
Definition IntraComm.cpp:62
std::shared_ptr< Preconditioner > PtrPreconditioner
contains implementations of acceleration schemes.
std::shared_ptr< CouplingData > PtrCouplingData
void removeColumnFromMatrix(Eigen::MatrixXd &A, int col)
void appendFront(Eigen::MatrixXd &A, Eigen::VectorXd &v)
void shiftSetFirst(Eigen::MatrixXd &A, const Eigen::VectorXd &v)
int Rank
Definition Types.hpp:37
STL namespace.