preCICE v3.2.0
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 const impl::PtrPreconditioner &preconditioner,
40 bool alwaysBuildJacobian,
41 int imvjRestartType,
42 int chunkSize,
43 int RSLSreusedTimeWindows,
44 double RSSVDtruncationEps,
45 bool reducedTimeGrid)
46 : BaseQNAcceleration(initialRelaxation, forceInitialRelaxation, maxIterationsUsed, pastTimeWindowsReused,
47 filter, singularityLimit, std::move(dataIDs), preconditioner, reducedTimeGrid),
48 // _secondaryOldXTildes(),
51 _Wtil(),
52 _WtilChunk(),
57 _parMatrixOps(nullptr),
58 _svdJ(RSSVDtruncationEps, preconditioner),
59 _alwaysBuildJacobian(alwaysBuildJacobian),
60 _imvjRestartType(imvjRestartType),
61 _imvjRestart(imvjRestartType > 0),
62 _chunkSize(chunkSize),
63 _RSLSreusedTimeWindows(RSLSreusedTimeWindows),
64 _nbRestarts(0),
65 _avgRank(0)
66{
67}
68
69// ==================================================================================
71
72// ==================================================================================
74 const DataMap &cplData)
75{
80
82
83 // call the base method for common update of V, W matrices
84 // important that base method is called before updating _Wtil
86
87 // update _Wtil if the efficient computation of the quasi-Newton update is used
88 // or update current Wtil if the restart mode of imvj is used
91 // do nothing: constant relaxation
92 } else {
93 if (not _firstIteration) {
94 // Update matrix _Wtil = (W - J_prev*V) with newest information
95
96 Eigen::VectorXd v = _matrixV.col(0);
97 Eigen::VectorXd w = _matrixW.col(0);
98
99 // here, we check for _Wtil.cols() as the matrices V, W need to be updated before hand
100 // and thus getLSSystemCols() does not yield the correct result.
101 bool columnLimitReached = _Wtil.cols() == _maxIterationsUsed;
102 bool overdetermined = _Wtil.cols() <= getLSSystemRows();
103
104 Eigen::VectorXd wtil = Eigen::VectorXd::Zero(_matrixW.rows());
105
106 // add column: Wtil(:,0) = W(:,0) - sum_q [ Wtil^q * ( Z^q * V(:,0)) ]
107 // |--- J_prev ---|
108 // iterate over all stored Wtil and Z matrices in current chunk
109 if (_imvjRestart) {
110 for (int i = 0; i < static_cast<int>(_WtilChunk.size()); i++) {
111 int colsLSSystemBackThen = _pseudoInverseChunk[i].rows();
112 PRECICE_ASSERT(colsLSSystemBackThen == _WtilChunk[i].cols(), colsLSSystemBackThen, _WtilChunk[i].cols());
113 Eigen::VectorXd Zv = Eigen::VectorXd::Zero(colsLSSystemBackThen);
114 // multiply: Zv := Z^q * V(:,0) of size (m x 1)
115 _parMatrixOps->multiply(_pseudoInverseChunk[i], v, Zv, colsLSSystemBackThen, getLSSystemRows(), 1);
116 // multiply: Wtil^q * Zv dimensions: (n x m) * (m x 1), fully local
117 wtil += _WtilChunk[i] * Zv;
118 }
119
120 // store columns if restart mode = RS-LS
121 if (_imvjRestartType == RS_LS) {
124 _matrixCols_RSLS.front()++;
125 }
126
127 // imvj without restart is used, but efficient update, i.e. no Jacobian assembly in each iteration
128 // add column: Wtil(:,0) = W(:,0) - J_prev * V(:,0)
129 } else {
130 // compute J_prev * V(0) := wtil the new column in _Wtil of dimension: (n x n) * (n x 1) = (n x 1),
131 // parallel: (n_global x n_local) * (n_local x 1) = (n_local x 1)
132 _parMatrixOps->multiply(_oldInvJacobian, v, wtil, _dimOffsets, getLSSystemRows(), getLSSystemRows(), 1, false, false);
133 }
134 wtil *= -1;
135 wtil += w;
136
137 if (not columnLimitReached && overdetermined) {
139 } else {
141 }
142 }
143 }
144 }
145}
146
147// ==================================================================================
148void IQNIMVJAcceleration::computeQNUpdate(Eigen::VectorXd &xUpdate)
149{
159
168 computeNewtonUpdate(xUpdate);
169 } else {
171 }
172}
173
174// ==================================================================================
176 Eigen::MatrixXd &pseudoInverse)
177{
183 auto Q = _qrV.matrixQ();
184 auto R = _qrV.matrixR();
185
186 PRECICE_ASSERT(pseudoInverse.rows() == _qrV.cols(), pseudoInverse.rows(), _qrV.cols());
187 PRECICE_ASSERT(pseudoInverse.cols() == _qrV.rows(), pseudoInverse.cols(), _qrV.rows());
188
189 Eigen::VectorXd yVec(pseudoInverse.rows());
190
191 // assertions for the case of processors with no vertices
194 PRECICE_ASSERT(_qrV.rows() == 0, _qrV.rows());
195 PRECICE_ASSERT(Q.size() == 0, Q.size());
196 }
197
198 // backsubstitution
199 for (int i = 0; i < Q.rows(); i++) {
200 Eigen::VectorXd Qrow = Q.row(i);
201 yVec = R.triangularView<Eigen::Upper>().solve<Eigen::OnTheLeft>(Qrow);
202 pseudoInverse.col(i) = yVec;
203 } // ----------------
204
205 // scale pseudo inverse back Z := Z' * P,
206 // Z' is scaled pseudo inverse i.e, Z' = R^-1 * Q^T * P^-1
207 _preconditioner->apply(pseudoInverse, true);
208 // e.stop(true);
209}
210
211// ==================================================================================
213{
218
219 PRECICE_ASSERT(_matrixV.rows() == _qrV.rows(), _matrixV.rows(), _qrV.rows());
221
222 _Wtil = Eigen::MatrixXd::Zero(_residuals.rows(), _qrV.cols());
223
224 // imvj restart mode: re-compute Wtil: Wtil = W - sum_q [ Wtil^q * (Z^q*V) ]
225 // |--- J_prev ---|
226 // iterate over all stored Wtil and Z matrices in current chunk
227 if (_imvjRestart) {
228 for (int i = 0; i < static_cast<int>(_WtilChunk.size()); i++) {
229 int colsLSSystemBackThen = _pseudoInverseChunk[i].rows();
230 PRECICE_ASSERT(colsLSSystemBackThen == _WtilChunk[i].cols(), colsLSSystemBackThen, _WtilChunk[i].cols());
231 Eigen::MatrixXd ZV = Eigen::MatrixXd::Zero(colsLSSystemBackThen, _qrV.cols());
232 // multiply: ZV := Z^q * V of size (m x m) with m=#cols, stored on each proc.
233 _parMatrixOps->multiply(_pseudoInverseChunk[i], _matrixV, ZV, colsLSSystemBackThen, getLSSystemRows(), _qrV.cols());
234 // multiply: Wtil^q * ZV dimensions: (n x m) * (m x m), fully local and embarrassingly parallel
235 _Wtil += _WtilChunk[i] * ZV;
236 }
237
238 // imvj without restart is used, i.e., recompute Wtil: Wtil = W - J_prev * V
239 } else {
240 // multiply J_prev * V = W_til of dimension: (n x n) * (n x m) = (n x m),
241 // parallel: (n_global x n_local) * (n_local x m) = (n_local x m)
243 }
244
245 // W_til = (W-J_inv_n*V) = (W-V_tilde)
246 _Wtil *= -1.;
247 _Wtil = _Wtil + _matrixW;
248
249 _resetLS = false;
250 // e.stop(true);
251}
252
253// ==================================================================================
255{
263
267 Eigen::MatrixXd Z(_qrV.cols(), _qrV.rows());
268 pseudoInverse(Z);
269
273 PRECICE_ASSERT(_matrixV.rows() == _qrV.rows(), _matrixV.rows(), _qrV.rows());
275 if (_resetLS) {
276 buildWtil();
277 PRECICE_WARN(" ATTENTION, in buildJacobian call for buildWtill() - this should not be the case except the coupling did only one iteration");
278 }
279
286 // --------
287
288 // update Jacobian
290}
291
292// ==================================================================================
294{
296
313
317 Eigen::MatrixXd Z(_qrV.cols(), _qrV.rows());
318 pseudoInverse(Z);
319
323 PRECICE_ASSERT(_matrixV.rows() == _qrV.rows(), _matrixV.rows(), _qrV.rows());
325
326 // rebuild matrix Wtil if V changes substantially.
327 if (_resetLS) {
328 buildWtil();
329 }
330
342 Eigen::VectorXd negativeResiduals = -_primaryResiduals;
343 Eigen::VectorXd r_til = Eigen::VectorXd::Zero(getLSSystemCols());
344 _parMatrixOps->multiply(Z, negativeResiduals, r_til, getLSSystemCols(), getLSSystemRows(), 1); // --------
345
354 Eigen::VectorXd xUptmp(_residuals.size());
355 xUpdate = Eigen::VectorXd::Zero(_residuals.size());
356 xUptmp = _Wtil * r_til; // local product, result is naturally distributed.
357
364 if (_imvjRestart) {
365 for (int i = 0; i < static_cast<int>(_WtilChunk.size()); i++) {
366 int colsLSSystemBackThen = _pseudoInverseChunk[i].rows();
367 PRECICE_ASSERT(colsLSSystemBackThen == _WtilChunk[i].cols(), colsLSSystemBackThen, _WtilChunk[i].cols());
368 r_til = Eigen::VectorXd::Zero(colsLSSystemBackThen);
369 // multiply: r_til := Z^q * (-res) of size (m x 1) with m=#cols of LS at that time, result stored on each proc.
370 _parMatrixOps->multiply(_pseudoInverseChunk[i], negativeResiduals, r_til, colsLSSystemBackThen, getLSSystemRows(), 1);
371 // multiply: Wtil^q * r_til dimensions: (n x m) * (m x 1), fully local and embarrassingly parallel
372 xUpdate += _WtilChunk[i] * r_til;
373 }
374
375 // imvj without restart is used, i.e., compute directly J_prev * (-res)
376 } else {
377 _parMatrixOps->multiply(_oldInvJacobian, negativeResiduals, xUpdate, _dimOffsets, getLSSystemRows(), getLSSystemRows(), 1, false, false);
378 PRECICE_DEBUG("Mult J*V DONE");
379 }
380
381 xUpdate += xUptmp;
382
383 // pending deletion: delete Wtil
385 _Wtil.conservativeResize(0, 0);
386 _resetLS = true;
387 }
388}
389
390// ==================================================================================
391void IQNIMVJAcceleration::computeNewtonUpdate(Eigen::VectorXd &xUpdate)
392{
394
399
402 Eigen::MatrixXd Z(_qrV.cols(), _qrV.rows());
403 pseudoInverse(Z);
404
407 buildWtil();
408
415
416 // update Jacobian
418
421 Eigen::VectorXd negativeResiduals = -_primaryResiduals;
422
423 // multiply J_inv * (-res) = x_Update of dimension: (n x n) * (n x 1) = (n x 1),
424 // parallel: (n_global x n_local) * (n_local x 1) = (n_local x 1)
425 _parMatrixOps->multiply(_invJacobian, negativeResiduals, xUpdate, _dimOffsets, getLSSystemRows(), getPrimaryLSSystemRows(), 1, false, false); // --------
426}
427
428// ==================================================================================
430{
432
433 // int used_storage = 0;
434 // int theoreticalJ_storage = 2*getLSSystemRows()*_primaryResiduals.size() + 3*_primaryResiduals.size()*getLSSystemCols() + _primaryResiduals.size()*_primaryResiduals.size();
435 // ------------ RESTART SVD ------------
437
438 // we need to compute the updated SVD of the scaled Jacobian matrix
439 // |= APPLY PRECONDITIONING J_prev = Wtil^q, Z^q ===|
440 for (int i = 0; i < static_cast<int>(_WtilChunk.size()); i++) {
441 _preconditioner->apply(_WtilChunk[i]);
442 _preconditioner->revert(_pseudoInverseChunk[i], true);
443 }
444 // |=================== ===|
445
446 Rank rankBefore = _svdJ.isSVDinitialized() ? _svdJ.rank() : 0;
447
448 // if it is the first time window, there is no initial SVD, so take all Wtil, Z matrices
449 // otherwise, the first element of each container holds the decomposition of the current
450 // truncated SVD, i.e., Wtil^0 = \phi, Z^0 = S\psi^T, this should not be added to the SVD.
451 int q = _svdJ.isSVDinitialized() ? 1 : 0;
452
453 // perform M-1 rank-1 updates of the truncated SVD-dec of the Jacobian
454 for (; q < static_cast<int>(_WtilChunk.size()); q++) {
455 // update SVD, i.e., PSI * SIGMA * PHI^T <-- PSI * SIGMA * PHI^T + Wtil^q * Z^q
456 _svdJ.update(_WtilChunk[q], _pseudoInverseChunk[q].transpose());
457 // used_storage += 2*_WtilChunk.size();
458 }
459 // int m = _WtilChunk[q].cols(), n = _WtilChunk[q].rows();
460 // used_storage += 2*rankBefore*m + 4*m*n + 2*m*m + (rankBefore+m)*(rankBefore+m) + 2*n*(rankBefore+m);
461
462 // drop all stored Wtil^q, Z^q matrices
463 _WtilChunk.clear();
464 _pseudoInverseChunk.clear();
465
466 auto &psi = _svdJ.matrixPsi();
467 auto &sigma = _svdJ.singularValues();
468 auto &phi = _svdJ.matrixPhi();
469
470 // multiply sigma * phi^T, phi is distributed block-row wise, phi^T is distributed block-column wise
471 // sigma is stored local on each proc, thus, the multiplication is fully local, no communication.
472 // Z = sigma * phi^T
473 Eigen::MatrixXd Z(phi.cols(), phi.rows());
474 for (int i = 0; i < static_cast<int>(Z.rows()); i++)
475 for (int j = 0; j < static_cast<int>(Z.cols()); j++)
476 Z(i, j) = phi(j, i) * sigma[i];
477
478 Rank rankAfter = _svdJ.rank();
479 int waste = _svdJ.getWaste();
480 _avgRank += rankAfter;
481
482 // store factorized truncated SVD of J
483 _WtilChunk.push_back(psi);
484 _pseudoInverseChunk.push_back(Z);
485
486 // |= REVERT PRECONDITIONING J_prev = Wtil^0, Z^0 ==|
487 _preconditioner->revert(_WtilChunk.front());
488 _preconditioner->apply(_pseudoInverseChunk.front(), true);
489 // |=================== ==|
490
491 PRECICE_DEBUG("MVJ-RESTART, mode=SVD. Rank of truncated SVD of Jacobian {}, new modes: {}, truncated modes: {} avg rank: {}", rankAfter, rankAfter - rankBefore, waste, _avgRank / _nbRestarts);
492
493 // double percentage = 100.0*used_storage/(double)theoreticalJ_storage;
495 _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"
496 << '\n';
497 }
498
499 // ------------ RESTART LEAST SQUARES ------------
501 // drop all stored Wtil^q, Z^q matrices
502 _WtilChunk.clear();
503 _pseudoInverseChunk.clear();
504
505 if (_matrixV_RSLS.cols() > 0) {
506 // avoid that the system is getting too squared
507 while (_matrixV_RSLS.cols() * 2 >= getLSSystemRows()) {
509 }
510
511 // preconditioning
512 // V needs to be sclaed to compute the pseudo inverse
513 // W only needs to be scaled, as the design requires to store scaled
514 // matrices Wtil^0 and Z^0 as initial guess after the restart
517
520 // for QR2-filter, the QR-dec is computed in qr-applyFilter()
522 for (int i = 0; i < static_cast<int>(_matrixV_RSLS.cols()); i++) {
523 Eigen::VectorXd v = _matrixV_RSLS.col(i);
524 qr.pushBack(v); // same order as matrix V_RSLS
525 }
526 }
527
528 // apply filter
530 std::vector<int> delIndices(0);
532 // start with largest index (as V,W matrices are shrunk and shifted
533 for (int i = delIndices.size() - 1; i >= 0; i--) {
534 removeMatrixColumnRSLS(delIndices[i]);
535 }
536 PRECICE_ASSERT(_matrixV_RSLS.cols() == qr.cols(), _matrixV_RSLS.cols(), qr.cols());
537 }
538
543 auto Q = qr.matrixQ();
544 auto R = qr.matrixR();
545 Eigen::MatrixXd pseudoInverse(qr.cols(), qr.rows());
546 Eigen::VectorXd yVec(pseudoInverse.rows());
547
548 // backsubstitution
549 for (int i = 0; i < Q.rows(); i++) {
550 Eigen::VectorXd Qrow = Q.row(i);
551 yVec = R.triangularView<Eigen::Upper>().solve<Eigen::OnTheLeft>(Qrow);
552 pseudoInverse.col(i) = yVec;
553 }
554
555 // scale pseudo inverse back Z := Z' * P,
556 // Z' is scaled pseudo inverse i.e, Z' = R^-1 * Q^T * P^-1
557 //_preconditioner->apply(pseudoInverse, true, false);
558
559 // store factorization of least-squares initial guess for Jacobian
560 _WtilChunk.push_back(_matrixW_RSLS);
562
563 // |= REVERT PRECONDITIONING J_prev = Wtil^0, Z^0 ==|
564 _preconditioner->revert(_WtilChunk.front());
565 _preconditioner->apply(_pseudoInverseChunk.front(), true);
568 // |=================== ==|
569 }
570
571 PRECICE_DEBUG("MVJ-RESTART, mode=LS. Restart with {} columns from {} time windows.", _matrixV_RSLS.cols(), _RSLSreusedTimeWindows);
573 _infostringstream << " - MVJ-RESTART" << _nbRestarts << ", mode= LS -\n used cols: " << _matrixV_RSLS.cols() << "\n R_RS: " << _RSLSreusedTimeWindows << "\n"
574 << '\n';
575 }
576
577 // ------------ RESTART ZERO ------------
579 // drop all stored Wtil^q, Z^q matrices
580 _WtilChunk.clear();
581 _pseudoInverseChunk.clear();
582
583 PRECICE_DEBUG("MVJ-RESTART, mode=Zero");
584
586
587 // re-compute Wtil -- compensate for dropping of Wtil_0 and Z_0:
588 // Wtil_q <-- Wtil_q + Wtil^0 * (Z^0*V_q)
589 for (int i = static_cast<int>(_WtilChunk.size()) - 1; i >= 1; i--) {
590
591 int colsLSSystemBackThen = _pseudoInverseChunk.front().rows();
592 PRECICE_ASSERT(colsLSSystemBackThen == _WtilChunk.front().cols(), colsLSSystemBackThen, _WtilChunk.front().cols());
593 Eigen::MatrixXd ZV = Eigen::MatrixXd::Zero(colsLSSystemBackThen, _qrV.cols());
594 // multiply: ZV := Z^q * V of size (m x m) with m=#cols, stored on each proc.
595 _parMatrixOps->multiply(_pseudoInverseChunk.front(), _matrixV, ZV, colsLSSystemBackThen, getLSSystemRows(), _qrV.cols());
596 // multiply: Wtil^0 * (Z_0*V) dimensions: (n x m) * (m x m), fully local and embarrassingly parallel
597 Eigen::MatrixXd tmp = Eigen::MatrixXd::Zero(_residuals.rows(), _qrV.cols());
598 tmp = _WtilChunk.front() * ZV;
599 _WtilChunk[i] += tmp;
600
601 // drop oldest pair Wtil_0 and Z_0
602 PRECICE_ASSERT(not _WtilChunk.empty());
604 _WtilChunk.erase(_WtilChunk.begin());
606 }
607
609 PRECICE_ASSERT(false); // should not happen, in this case _imvjRestart=false
610 } else {
611 PRECICE_ASSERT(false);
612 }
613}
614
615// ==================================================================================
617 const DataMap &cplData)
618{
620
621 // truncate V_RSLS and W_RSLS matrices according to _RSLSreusedTimeWindows
622 if (_imvjRestartType == RS_LS) {
623 if (_matrixCols_RSLS.front() == 0) { // Did only one iteration
624 _matrixCols_RSLS.pop_front();
625 }
626 if (_RSLSreusedTimeWindows == 0) {
627 _matrixV_RSLS.resize(0, 0);
628 _matrixW_RSLS.resize(0, 0);
629 _matrixCols_RSLS.clear();
630 } else if (static_cast<int>(_matrixCols_RSLS.size()) > _RSLSreusedTimeWindows) {
631 int toRemove = _matrixCols_RSLS.back();
632 PRECICE_ASSERT(toRemove > 0, toRemove);
633 if (_matrixV_RSLS.size() > 0) {
634 PRECICE_ASSERT(_matrixV_RSLS.cols() > toRemove, _matrixV_RSLS.cols(), toRemove);
635 }
636
637 // remove columns
638 for (int i = 0; i < toRemove; i++) {
641 }
642 _matrixCols_RSLS.pop_back();
643 }
644 _matrixCols_RSLS.push_front(0);
645 }
646
647 //_info2<<'\n';
648
649 // if efficient update of imvj is enabled
651 // need to apply the preconditioner, as all data structures are reverted after
652 // call to computeQNUpdate. Need to call this before the preconditioner is updated.
653
654 // |= REBUILD QR-dec if needed ============|
655 // apply scaling to V, V' := P * V (only needed to reset the QR-dec of V)
657
658 if (_preconditioner->requireNewQR()) {
659 if (not(_filter == Acceleration::QR2FILTER)) { // for QR2 filter, there is no need to do this twice
660 _qrV.reset(_matrixV, getLSSystemRows());
661 }
662 _preconditioner->newQRfulfilled();
663 }
664 // apply the configured filter to the LS system
665 // as it changed in BaseQNAcceleration::iterationsConverged()
667 _preconditioner->revert(_matrixV);
668 // |=================== ============|
669
670 // ------- RESTART/ JACOBIAN ASSEMBLY -------
671 if (_imvjRestart) {
672
673 // add the matrices Wtil and Z of the converged configuration to the storage containers
674 Eigen::MatrixXd Z(_qrV.cols(), _qrV.rows());
675 // compute pseudo inverse using QR factorization and back-substitution
676 // also compensates for the scaling of V, i.e.,
677 // reverts Z' = R^-1 * Q^T * P^-1 as Z := Z' * P
678 pseudoInverse(Z);
679
680 // push back unscaled pseudo Inverse, Wtil is also unscaled.
681 // all objects in Wtil chunk and Z chunk are NOT PRECONDITIONED
682 _WtilChunk.push_back(_Wtil);
683 _pseudoInverseChunk.push_back(Z);
684
688 if (static_cast<int>(_WtilChunk.size()) >= _chunkSize + 1) {
689
690 // < RESTART >
691 _nbRestarts++;
692 profiling::Event restartUpdate("IMVJRestart");
693 restartIMVJ();
694 restartUpdate.stop();
695 }
696
697 // only in imvj normal mode with efficient update:
698 } else {
699
700 // compute explicit representation of Jacobian
702 }
703
709 //_Wtil.conservativeResize(0, 0);
710 _resetLS = true;
711 }
712 }
713
714 // only store Jacobian if imvj is in normal mode, i.e., the Jacobian is build
715 if (not _imvjRestart) {
716 // store inverse Jacobian from converged time window. NOT SCALED with preconditioner
718 }
719}
720
721// ==================================================================================
723 int columnIndex)
724{
725 PRECICE_TRACE(columnIndex, _matrixV.cols());
726 PRECICE_ASSERT(_matrixV.cols() > 1, _matrixV.cols());
727 PRECICE_ASSERT(_Wtil.cols() > 1);
728
729 // remove column from matrix _Wtil
730 if (not _resetLS && not _alwaysBuildJacobian)
732
734}
735
737{
738 int entries = _primaryResiduals.size();
739 int cplDataEntries = _residuals.size();
740 int global_n = 0;
741
743 global_n = cplDataEntries;
744 } else {
745 global_n = _dimOffsets.back();
746 }
747
748 // initialize parallel matrix-matrix operation module
750 _parMatrixOps->initialize(not _imvjRestart);
751 _svdJ.initialize(_parMatrixOps, global_n, getLSSystemRows());
752
753 if (not _imvjRestart) {
754 // only need memory for Jacobain if not in restart mode
755 _invJacobian = Eigen::MatrixXd::Zero(global_n, entries);
756 _oldInvJacobian = Eigen::MatrixXd::Zero(global_n, entries);
757 }
758
759 // initialize parallel matrix-matrix operation module
761 _parMatrixOps->initialize(not _imvjRestart);
762 _svdJ.reset();
763 _svdJ.initialize(_parMatrixOps, global_n, getLSSystemRows());
764
765 // initialize V, W matrices for the LS restart
766 if (_imvjRestartType == RS_LS) {
767 _matrixCols_RSLS.clear();
768 _matrixCols_RSLS.push_front(0);
769 _matrixV_RSLS = Eigen::MatrixXd::Zero(entries, 0);
770 _matrixW_RSLS = Eigen::MatrixXd::Zero(cplDataEntries, 0);
771 }
772 _Wtil = Eigen::MatrixXd::Zero(cplDataEntries, 0);
773 if (_imvjRestartType > 0) {
774 // drop all stored Wtil^q, Z^q matrices
775 _WtilChunk.clear();
776 _pseudoInverseChunk.clear();
777 }
778
780 _infostringstream << " IMVJ restart mode: " << _imvjRestart << "\n chunk size: " << _chunkSize << "\n trunc eps: " << _svdJ.getThreshold() << "\n R_RS: " << _RSLSreusedTimeWindows << "\n--------\n"
781 << '\n';
782 }
783}
784
785// ==================================================================================
787 int columnIndex)
788{
789 PRECICE_TRACE(columnIndex, _matrixV_RSLS.cols());
790 PRECICE_ASSERT(_matrixV_RSLS.cols() > 1);
791
794
795 // Reduce column count
796 std::deque<int>::iterator iter = _matrixCols_RSLS.begin();
797 int cols = 0;
798 while (iter != _matrixCols_RSLS.end()) {
799 cols += *iter;
800 if (cols > columnIndex) {
801 PRECICE_ASSERT(*iter > 0);
802 *iter -= 1;
803 if (*iter == 0) {
804 _matrixCols_RSLS.erase(iter);
805 }
806 break;
807 }
808 iter++;
809 }
810}
811
812} // namespace precice::acceleration
813
814#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
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.
BaseQNAcceleration(double initialRelaxation, bool forceInitialRelaxation, int maxIterationsUsed, int timeWindowsReused, int filter, double singularityLimit, std::vector< int > dataIDs, impl::PtrPreconditioner preconditioner, bool reducedTimeGrid)
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.
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
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.
IQNIMVJAcceleration(double initialRelaxation, bool forceInitialRelaxation, int maxIterationsUsed, int pastTimeWindowsReused, int filter, double singularityLimit, std::vector< int > dataIDs, const impl::PtrPreconditioner &preconditioner, bool alwaysBuildJacobian, int imvjRestartType, int chunkSize, int RSLSreusedTimeWindows, double RSSVDtruncationEps, bool reducedTimeGrid)
Constructor.
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
T make_shared(T... args)
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.
T size(T... args)