preCICE
Loading...
Searching...
No Matches
BaseQNAcceleration.cpp
Go to the documentation of this file.
2#include <Eigen/Core>
3#include <boost/range/adaptor/map.hpp>
4#include <cmath>
5#include <memory>
6#include <utility>
7
10#include "com/Communication.hpp"
11#include "com/SharedPointer.hpp"
13#include "io/TXTTableWriter.hpp"
14#include "logging/LogMacros.hpp"
15#include "mesh/Mesh.hpp"
17#include "profiling/Event.hpp"
18#include "time/TimeGrids.hpp"
20#include "utils/Helpers.hpp"
21#include "utils/IntraComm.hpp"
22#include "utils/assertion.hpp"
23
24namespace precice {
25namespace io {
26class TXTReader;
27class TXTWriter;
28} // namespace io
29namespace acceleration {
30
31/* ----------------------------------------------------------------------------
32 * Constructor
33 * ----------------------------------------------------------------------------
34 */
36 double initialRelaxation,
37 bool forceInitialRelaxation,
38 int maxIterationsUsed,
39 int timeWindowsReused,
40 int filter,
41 double singularityLimit,
42 std::vector<int> dataIDs,
43 OnBoundViolation onBoundViolation,
44 impl::PtrPreconditioner preconditioner,
45 bool reducedTimeGrid)
46 : _preconditioner(std::move(preconditioner)),
47 _initialRelaxation(initialRelaxation),
48 _maxIterationsUsed(maxIterationsUsed),
49 _timeWindowsReused(timeWindowsReused),
50 _primaryDataIDs(std::move(dataIDs)),
51 _onBoundViolation(onBoundViolation),
52 _forceInitialRelaxation(forceInitialRelaxation),
53 _reducedTimeGrid(reducedTimeGrid),
54 _qrV(filter),
55 _filter(filter),
56 _singularityLimit(singularityLimit),
57 _infostringstream(std::ostringstream::ate)
58{
60 "Initial relaxation factor for QN acceleration has to "
61 "be larger than zero and smaller or equal than one. "
62 "Current initial relaxation is {}",
65 "Maximum number of iterations used in the quasi-Newton acceleration "
66 "scheme has to be larger than zero. "
67 "Current maximum reused iterations is {}",
70 "Number of previous time windows to be reused for "
71 "quasi-Newton acceleration has to be larger than or equal to zero. "
72 "Current number of time windows reused is {}",
74}
75
83 const DataMap &cplData)
84{
85 PRECICE_TRACE(cplData.size());
86
87 for (const DataMap::value_type &pair : cplData) {
88 PRECICE_ASSERT(pair.second->getSize() == pair.second->getPreviousIterationSize(), "current and previousIteration have to be initialized and of identical size.",
89 pair.second->getSize(), pair.second->getPreviousIterationSize());
90 }
91
93 std::any_of(cplData.cbegin(), cplData.cend(), [](const auto &p) { return p.second->hasGradient(); }),
94 "Gradient data, which is required by at least one of the configured data mappings, is not yet compatible with quasi-Newton acceleration. This combination might lead to numerical issues. "
95 "Consider switching to a different acceleration scheme or a different data mapping scheme.");
96
97 checkDataIDs(cplData);
98 // store all data IDs in vector
99 _dataIDs.clear();
100 _idsWithBounds.clear();
101 for (const DataMap::value_type &pair : cplData) {
102 _dataIDs.push_back(pair.first);
103
104 auto lowerBound = pair.second->getLowerBound();
105 auto upperBound = pair.second->getUpperBound();
106 if (std::any_of(lowerBound.begin(), lowerBound.end(), [](const auto &v) { return v.has_value(); }) ||
107 std::any_of(upperBound.begin(), upperBound.end(), [](const auto &v) { return v.has_value(); })) {
108 _idsWithBounds.push_back(pair.first);
109 }
110 }
111 _matrixCols.clear();
112 _matrixCols.push_front(0);
113 _firstIteration = true;
114 _firstTimeWindow = true;
115}
116
117std::vector<DataID> BaseQNAcceleration::checkBoundViolation(Eigen::VectorXd &data, DataMap &cplData) const
118{
119 Eigen::Index offset = 0;
120 std::vector<DataID> violatingIDs = {};
121
122 // check for bound violations
123 for (auto id : _dataIDs) {
124 Eigen::Index size = cplData.at(id)->values().size();
125
126 if (std::find(_idsWithBounds.begin(), _idsWithBounds.end(), id) == _idsWithBounds.end()) {
127 offset += size;
128 continue;
129 }
130
131 auto lowerBound = cplData.at(id)->getLowerBound();
132 auto upperBound = cplData.at(id)->getUpperBound();
133 int dataDimension = cplData.at(id)->getDimensions();
134
135 Eigen::VectorXd dataPerEntry = data.segment(offset, size);
136 Eigen::Map<Eigen::MatrixXd> dataPerDimension(dataPerEntry.data(), dataDimension, dataPerEntry.size() / dataDimension);
137
138 for (int j = 0; j < dataDimension; j++) {
139 if ((lowerBound[j].has_value() && (dataPerDimension.row(j).array() < lowerBound[j].value()).any()) ||
140 (upperBound[j].has_value() && (dataPerDimension.row(j).array() > upperBound[j].value()).any())) {
141 violatingIDs.push_back(id);
142 break;
143 }
144 }
145 offset += size;
146 }
147
148 return violatingIDs;
149}
150
151void BaseQNAcceleration::onBoundViolationsClamp(Eigen::VectorXd &data, DataMap &cplData, const std::vector<DataID> &violatingIDs)
152{
153 Eigen::Index offset = 0;
154 for (auto id : _dataIDs) {
155 Eigen::Index size = cplData.at(id)->values().size();
156
157 if (std::find(violatingIDs.begin(), violatingIDs.end(), id) == violatingIDs.end()) {
158 offset += size;
159 continue;
160 }
161
162 int dataDimension = cplData.at(id)->getDimensions();
163 auto lowerBound = cplData.at(id)->getLowerBound();
164 auto upperBound = cplData.at(id)->getUpperBound();
165 auto dataPerEntry = data.segment(offset, size);
166
167 const Eigen::Index nEntries = dataPerEntry.size() / dataDimension;
168 Eigen::Map<Eigen::MatrixXd> dataPerDimension(dataPerEntry.data(), dataDimension, nEntries);
169
170 clampToBounds(dataPerDimension, lowerBound, upperBound);
171
172 offset += size;
173 }
174}
175
176void BaseQNAcceleration::onBoundViolationsScale(Eigen::VectorXd &data, DataMap &cplData, const std::vector<DataID> &violatingIDs, Eigen::VectorXd &xUpdate)
177{
178 Eigen::Index offset = 0;
179 double scaleStep = 0.0;
180
181 for (auto id : _dataIDs) {
182 Eigen::Index size = cplData.at(id)->values().size();
183
184 if (std::find(violatingIDs.begin(), violatingIDs.end(), id) == violatingIDs.end()) {
185 offset += size;
186 continue;
187 }
188
189 int dataDimension = cplData.at(id)->getDimensions();
190 auto lowerBound = cplData.at(id)->getLowerBound();
191 auto upperBound = cplData.at(id)->getUpperBound();
192 auto dataPerEntry = data.segment(offset, size);
193 auto updatePerEntry = xUpdate.segment(offset, size);
194
195 const Eigen::Index nEntries = dataPerEntry.size() / dataDimension;
196 Eigen::Map<Eigen::MatrixXd> dataMat(dataPerEntry.data(), dataDimension, nEntries);
197 Eigen::Map<Eigen::MatrixXd> updMat(updatePerEntry.data(), dataDimension, nEntries);
198
199 scaleStep = std::max(scaleStep, computeShorteningFactor(dataMat, updMat, lowerBound, upperBound));
200 offset += size;
201 }
202
203 double scaleStepGlobal = scaleStep;
206
208 comm->send(scaleStep, 0);
209 }
210
212 double recv = 0.0;
213
215 comm->receive(recv, rank);
216 scaleStepGlobal = std::max(scaleStepGlobal, recv);
217 }
218 }
219
220 utils::IntraComm::broadcast(scaleStepGlobal);
221 }
222 data -= xUpdate * scaleStepGlobal;
223}
224
225void BaseQNAcceleration::clampToBounds(Eigen::Map<Eigen::MatrixXd> &data, const std::vector<std::optional<double>> &lowerBound, const std::vector<std::optional<double>> &upperBound)
226{
227 for (int j = 0; j < data.rows(); j++) {
228 if (lowerBound[j].has_value()) {
229 data.row(j) = data.row(j).cwiseMax(*lowerBound[j]);
230 }
231 if (upperBound[j].has_value()) {
232 data.row(j) = data.row(j).cwiseMin(*upperBound[j]);
233 }
234 }
235}
236
237double BaseQNAcceleration::computeShorteningFactor(Eigen::Map<Eigen::MatrixXd> &data, Eigen::Map<Eigen::MatrixXd> &update, const std::vector<std::optional<double>> &lowerBound, const std::vector<std::optional<double>> &upperBound)
238{
239 double scaleStep = 0.0;
240
241 for (int j = 0; j < data.rows(); j++) {
242 if (lowerBound[j].has_value()) {
243 Eigen::ArrayXd numerator = (data.row(j).array() - *lowerBound[j]);
244 Eigen::ArrayXd denominator = -(update.row(j).array()).abs();
245
246 scaleStep = std::max(scaleStep, (denominator != 0.0).select(numerator / denominator, 0.0).maxCoeff());
247 }
248 if (upperBound[j].has_value()) {
249 Eigen::ArrayXd numerator = (data.row(j).array() - *upperBound[j]);
250 Eigen::ArrayXd denominator = (update.row(j).array()).abs();
251
252 scaleStep = std::max(scaleStep, (denominator != 0.0).select(numerator / denominator, 0.0).maxCoeff());
253 }
254 }
255 PRECICE_ASSERT(scaleStep >= 0.0);
256 PRECICE_ASSERT(scaleStep <= 1.0);
257 return scaleStep;
258}
259
267 const DataMap &cplData)
268{
270
271 // Compute current residual: vertex-data - oldData
276
278 "The coupling residual equals almost zero. There is maybe something wrong in your adapter. "
279 "Maybe you always write the same data or you call advance without "
280 "providing new data first or you do not use available read data. "
281 "Or you just converge much further than actually necessary.");
282
283 // if (_firstIteration && (_firstTimeWindow || (_matrixCols.size() < 2))) {
285 // do nothing: constant relaxation
286 } else {
287 PRECICE_DEBUG(" Update Difference Matrices");
288 if (not _firstIteration) {
289 // Update matrices V, W with newest information
290
291 PRECICE_ASSERT(_matrixV.cols() == _matrixW.cols(), _matrixV.cols(), _matrixW.cols());
293
296 "The number of columns in the least squares system exceeded half the number of primary unknowns at the interface. "
297 "The system will probably become bad or ill-conditioned and the quasi-Newton acceleration may not "
298 "converge. Maybe the number of allowed columns (\"max-used-iterations\") should be limited.");
299
300 Eigen::VectorXd deltaR = _primaryResiduals;
301 deltaR -= _oldPrimaryResiduals;
302
303 Eigen::VectorXd deltaXTilde = _values;
304 deltaXTilde -= _oldXTilde;
305
306 double residualMagnitude = utils::IntraComm::l2norm(deltaR);
307
309 residualMagnitude /= utils::IntraComm::l2norm(_primaryValues);
310 }
312 math::equals(residualMagnitude, 0.0),
313 "Adding a vector with a two-norm of {} to the quasi-Newton V matrix, which will lead to "
314 "ill-conditioning. A filter might delete the column again. Still, this could mean that you are "
315 "converging too tightly, that you reached steady-state, or that you are giving by mistake identical "
316 "data to preCICE in two consecutive iterations.",
317 residualMagnitude);
318
319 bool columnLimitReached = getLSSystemCols() == _maxIterationsUsed;
320 bool overdetermined = getLSSystemCols() <= getPrimaryLSSystemRows();
321 if (not columnLimitReached && overdetermined) {
322
324 utils::appendFront(_matrixW, deltaXTilde);
325
326 // insert column deltaR = _primaryResiduals - _oldPrimaryResiduals at pos. 0 (front) into the
327 // QR decomposition and update decomposition
328
329 // apply scaling here
330 _preconditioner->apply(deltaR);
331 _qrV.pushFront(deltaR);
332
333 _matrixCols.front()++;
334 } else {
336 utils::shiftSetFirst(_matrixW, deltaXTilde);
337
338 // inserts column deltaR at pos. 0 to the QR decomposition and deletes the last column
339 // the QR decomposition of V is updated
340 _preconditioner->apply(deltaR);
341 _qrV.pushFront(deltaR);
342 _qrV.popBack();
343
344 _matrixCols.front()++;
345 _matrixCols.back()--;
346 if (_matrixCols.back() == 0) {
347 _matrixCols.pop_back();
348 }
349 _nbDropCols++;
350 }
351 }
352 _oldPrimaryResiduals = _primaryResiduals; // Store residuals
353 _oldPrimaryXTilde = _primaryValues; // Store x_tilde
354 _oldXTilde = _values; // Store coupling x_tilde
355 }
356}
357
365 DataMap &cplData,
366 double windowStart,
367 double windowEnd)
368{
369 PRECICE_TRACE(_primaryDataIDs.size(), cplData.size());
370
371 profiling::Event e("cpl.computeQuasiNewtonUpdate", profiling::Synchronize);
372
373 // We can only initialize here since we need to know the time grid already.
375 initializeVectorsAndPreconditioner(cplData, windowStart);
376 }
377
382
383 // Needs to be called in the first iteration of each time window.
384 if (_firstIteration) {
385 _timeGrids.value().moveTimeGridToNewWindow(cplData);
386 _primaryTimeGrids.value().moveTimeGridToNewWindow(cplData);
387 }
388
391 concatenateCouplingData(_values, _oldValues, cplData, _dataIDs, _timeGrids.value(), windowStart);
393
400
402 PRECICE_DEBUG(" Performing underrelaxation");
403 _oldPrimaryXTilde = _primaryValues; // Store x tilde of primary data
404 _oldXTilde = _values; // Store x tilde of primary and secondary data
405 _oldPrimaryResiduals = _primaryResiduals; // Store current residual of primary data
406
407 applyRelaxation(_initialRelaxation, cplData, windowStart);
408 its++;
409 _firstIteration = false;
410 return;
411 }
412
413 PRECICE_DEBUG(" Performing quasi-Newton Step");
414
415 // If the previous time window converged within one single iteration, nothing was added
416 // to the LS system matrices and they need to be restored from the backup at time T-2
418 PRECICE_DEBUG(" Last time window converged after one iteration. Need to restore the matrices from backup.");
419
423
424 // re-computation of QR decomposition from _matrixV = _matrixVBackup
425 // this occurs very rarely, to be precise, it occurs only if the coupling terminates
426 // after the first iteration and the matrix data from time window t-2 has to be used
428
429 // the columns that fail to be inserted into the QR factorization while resetting need to be removed from V and W for consistency
430 auto failAddedCols = _qrV.reset(_matrixV, getPrimaryLSSystemRows());
431 for (int i : failAddedCols) {
433 }
434 PRECICE_ASSERT(_matrixV.cols() == _qrV.cols(), _matrixV.cols(), _qrV.cols());
435
436 _preconditioner->revert(_matrixV);
437 _resetLS = true; // need to recompute _Wtil, Q, R (only for IMVJ efficient update)
438 }
439
447
448 // apply scaling to V, V' := P * V (only needed to reset the QR-dec of V)
450
451 if (_preconditioner->requireNewQR()) {
452 if (not(_filter == Acceleration::QR2FILTER || _filter == Acceleration::QR3FILTER)) { // for QR2 and QR3 filter, there is no need to do this twice
453 auto failAddedCols = _qrV.reset(_matrixV, getPrimaryLSSystemRows());
454 // the columns that fail to be inserted into the QR factorization while resetting need to be removed from V and W for consistency
455 for (int i : failAddedCols) {
457 }
458 PRECICE_ASSERT(_matrixV.cols() == _qrV.cols(), _matrixV.cols(), _qrV.cols());
459 }
460 if (_filter == Acceleration::QR3FILTER) { // QR3 filter needs to recompute QR3 filter
461 _qrV.requireQR3Fallback();
462 }
463 _preconditioner->newQRfulfilled();
464 }
465
466 if (_firstIteration) {
467 _nbDelCols = 0;
468 _nbDropCols = 0;
469 }
470
471 // apply the configured filter to the LS system
472 profiling::Event applyingFilter("ApplyFilter");
473 applyFilter();
474 applyingFilter.stop();
475
476 // revert scaling of V, in computeQNUpdate all data objects are unscaled.
477 _preconditioner->revert(_matrixV);
478
484 Eigen::VectorXd xUpdate = Eigen::VectorXd::Zero(_values.size());
485 computeQNUpdate(xUpdate);
486
487 // Apply the quasi-Newton update
488 _values += xUpdate;
489
490 // Check for bound violations
492 profiling::Event e("CheckBoundViolationsInQN");
493 auto violatingIDs = checkBoundViolation(_values, cplData);
494
495 for (auto id : violatingIDs) {
496 PRECICE_WARN("The coupling data {} has violated its bound after the quasi-Newton acceleration.", cplData.at(id)->getDataName());
497 }
498
499 if (violatingIDs.size() > 0 && _onBoundViolation == OnBoundViolation::Clamp) {
500 onBoundViolationsClamp(_values, cplData, violatingIDs);
501 }
502
504 int violatingSizeSum = 0;
505 int localViolatingSize = violatingIDs.size();
506 utils::IntraComm::allreduceSum(localViolatingSize, violatingSizeSum);
507
508 if (violatingSizeSum > 0 && _onBoundViolation == OnBoundViolation::Discard) {
509 PRECICE_WARN("The violating quasi-Newton step will be discarded.");
510 _values -= xUpdate;
511 }
512 if (violatingSizeSum > 0 && _onBoundViolation == OnBoundViolation::ScaleToBound) {
513 onBoundViolationsScale(_values, cplData, violatingIDs, xUpdate);
514 }
515 }
516 }
517
518 // pending deletion: delete old V, W matrices if timeWindowsReused = 0
519 // those were only needed for the first iteration (instead of underrelax.)
521 // save current matrix data in case the coupling for the next time window will terminate
522 // after the first iteration (no new data, i.e., V = W = 0)
523 if (getLSSystemCols() > 0) {
527 }
528 // if no time windows reused, the matrix data needs to be cleared as it was only needed for the
529 // QN-step in the first iteration (idea: rather perform QN-step with information from last converged
530 // time window instead of doing a underrelaxation)
531 if (not _firstTimeWindow) {
532 _matrixV.resize(0, 0);
533 _matrixW.resize(0, 0);
534 _matrixCols.clear();
535 _matrixCols.push_front(0); // vital after clear()
536 _qrV.reset();
537 // set the number of global rows in the QRFactorization.
538 _qrV.setGlobalRows(getPrimaryLSSystemRows());
539 _resetLS = true; // need to recompute _Wtil, Q, R (only for IMVJ efficient update)
540 }
541 }
542
544 !std::isnan(utils::IntraComm::l2norm(xUpdate)),
545 "The quasi-Newton update contains NaN values. This means that the quasi-Newton acceleration failed to converge. "
546 "When writing your own adapter this could indicate that you give wrong information to preCICE, such as identical "
547 "data in succeeding iterations. Or you do not properly save and reload checkpoints. "
548 "If you give the correct data this could also mean that the coupled problem is too hard to solve. Try to use a QR "
549 "filter or increase its threshold (larger epsilon).");
550
551 // updates the waveform and values in coupling data by splitting the primary and secondary data back into the individual data objects
552 updateCouplingData(cplData, windowStart);
553
554 // number of iterations (usually equals number of columns in LS-system)
555 its++;
556 _firstIteration = false;
557}
558
560{
562
564 // do nothing
565 } else {
566 // do: filtering of least-squares system to maintain good conditioning
567 std::vector<int> delIndices(0);
568 _qrV.applyFilter(_singularityLimit, delIndices, _matrixV);
569 // start with largest index (as V,W matrices are shrunk and shifted
570
571 for (int i = delIndices.size() - 1; i >= 0; i--) {
572
573 removeMatrixColumn(delIndices[i]);
574
575 PRECICE_DEBUG(" Filter: removing column with index {} in iteration {} of time window: {}", delIndices[i], its, tWindows);
576 }
577 PRECICE_ASSERT(_matrixV.cols() == _qrV.cols(), _matrixV.cols(), _qrV.cols());
578 }
579}
580
582 const DataMap &cplData, double windowStart)
583{
584
586 // offset to keep track of the position in xUpdate
587 Eigen::Index offset = 0;
588
589 for (int id : _dataIDs) {
590
591 auto &couplingData = *cplData.at(id);
592 size_t dataSize = couplingData.getSize();
593
594 Eigen::VectorXd timeGrid = _timeGrids->getTimeGridAfter(id, windowStart);
595 couplingData.waveform().trimAfter(windowStart);
596 for (int i = 0; i < timeGrid.size(); i++) {
597
598 Eigen::VectorXd temp = Eigen::VectorXd::Zero(dataSize);
599 for (size_t j = 0; j < dataSize; j++) {
600 temp(j) = _values(offset + j);
601 }
602 offset += dataSize;
603
604 couplingData.setSampleAtTime(timeGrid(i), time::Sample(couplingData.getDimensions(), temp));
605 }
606 }
607}
608
618 const DataMap &cplData, double windowStart)
619{
621
623 _infostringstream << "# time window " << tWindows << " converged #\n iterations: " << its
624 << "\n used cols: " << getLSSystemCols() << "\n del cols: " << _nbDelCols << '\n';
625
626 its = 0;
627 tWindows++;
628
629 // the most recent differences for the V, W matrices have not been added so far
630 // this has to be done in iterations converged, as PP won't be called any more if
631 // convergence was achieved
632
633 // If, in the first time window, we converge already in the first iteration, we have not yet initialized. Then, we need to do it here.
635 initializeVectorsAndPreconditioner(cplData, windowStart);
636 }
637
638 // Needs to be called in the first iteration of each time window.
639 if (_firstIteration) {
640 _timeGrids->moveTimeGridToNewWindow(cplData);
641 _primaryTimeGrids->moveTimeGridToNewWindow(cplData);
642 }
645 concatenateCouplingData(_values, _oldValues, cplData, _dataIDs, _timeGrids.value(), windowStart);
648
649 if (not _matrixCols.empty() && _matrixCols.front() == 0) { // Did only one iteration
650 _matrixCols.pop_front();
651 }
652
653#ifndef NDEBUG
654 std::ostringstream stream;
655 stream << "Matrix column counters: ";
656 for (int cols : _matrixCols) {
657 stream << cols << ", ";
658 }
659 PRECICE_DEBUG(stream.str());
660#endif // Debug
661
662 // doing specialized stuff for the corresponding acceleration scheme after
663 // convergence of iteration i.e.:
664 // - analogously to the V,W matrices, remove columns from matrices for secondary data
665 // - save the old Jacobian matrix
667
668 // if we already have convergence in the first iteration of the first time window
669 // we need to do underrelaxation in the first iteration of the second time window
670 // so "_firstTimeWindow" is slightly misused, but still the best way to understand
671 // the concept
672 if (not _firstIteration)
673 _firstTimeWindow = false;
674
675 // update preconditioner depending on residuals or values (must be after specialized iterations converged --> IMVJ)
677
678 if (_timeWindowsReused == 0) {
680 _matrixV.resize(0, 0);
681 _matrixW.resize(0, 0);
682 _qrV.reset();
683 // set the number of global rows in the QRFactorization.
684 _qrV.setGlobalRows(getPrimaryLSSystemRows());
685 _matrixCols.clear(); // _matrixCols.push_front() at the end of the method.
686 } else {
692 }
693 } else if (static_cast<int>(_matrixCols.size()) > _timeWindowsReused) {
694 int toRemove = _matrixCols.back();
695 _nbDropCols += toRemove;
696 PRECICE_ASSERT(toRemove > 0, toRemove);
697 PRECICE_DEBUG("Removing {} cols from least-squares system with {} cols", toRemove, getLSSystemCols());
698 PRECICE_ASSERT(_matrixV.cols() == _matrixW.cols(), _matrixV.cols(), _matrixW.cols());
699 PRECICE_ASSERT(getLSSystemCols() > toRemove, getLSSystemCols(), toRemove);
700
701 // remove columns
702 for (int i = 0; i < toRemove; i++) {
705 // also remove the corresponding columns from the dynamic QR-descomposition of _matrixV
706 _qrV.popBack();
707 }
708 _matrixCols.pop_back();
709 }
710
711 _matrixCols.push_front(0);
712 _firstIteration = true;
713}
714
722 int columnIndex)
723{
724 PRECICE_TRACE(columnIndex, _matrixV.cols());
725
726 _nbDelCols++;
727
728 PRECICE_ASSERT(_matrixV.cols() > 1);
731
732 // Reduce column count
733 std::deque<int>::iterator iter = _matrixCols.begin();
734 int cols = 0;
735 while (iter != _matrixCols.end()) {
736 cols += *iter;
737 if (cols > columnIndex) {
738 PRECICE_ASSERT(*iter > 0);
739 *iter -= 1;
740 if (*iter == 0) {
741 _matrixCols.erase(iter);
742 }
743 break;
744 }
745 iter++;
746 }
747}
748
753
758
760{
761 return _nbDelCols;
762}
763
765{
766 return _nbDropCols;
767}
768
770{
771 int cols = 0;
772 for (int col : _matrixCols) {
773 cols += col;
774 }
776 PRECICE_ASSERT(cols == _matrixV.cols(), cols, _matrixV.cols(), _matrixCols, _qrV.cols());
777 PRECICE_ASSERT(cols == _matrixW.cols(), cols, _matrixW.cols());
778 }
779
780 return cols;
781}
782
787
792
794{
796 return _dimOffsets.back();
797 }
798 return _residuals.size();
799}
800
802{
804 return _dimOffsetsPrimary.back();
805 }
806 return _primaryResiduals.size();
807}
808
810 const std::string &s, bool allProcs)
811{
813 // serial acceleration mode
815
816 // parallel acceleration
817 } else {
818 if (not allProcs) {
821 } else {
823 }
824 }
825 _infostringstream << std::flush;
826}
827
828void BaseQNAcceleration::concatenateCouplingData(Eigen::VectorXd &data, Eigen::VectorXd &oldData, const DataMap &cplData, std::vector<int> dataIDs, precice::time::TimeGrids timeGrids, double windowStart) const
829{
830 Eigen::Index offset = 0;
831 for (int id : dataIDs) {
832 Eigen::Index dataSize = cplData.at(id)->getSize();
833 Eigen::VectorXd timeGrid = timeGrids.getTimeGridAfter(id, windowStart);
834
835 for (int i = 0; i < timeGrid.size(); i++) {
836
837 auto current = cplData.at(id)->waveform().sample(timeGrid(i));
838 auto old = cplData.at(id)->getPreviousValuesAtTime(timeGrid(i));
839
840 PRECICE_ASSERT(data.size() >= offset + dataSize, "the values were not initialized correctly");
841 PRECICE_ASSERT(oldData.size() >= offset + dataSize, "the values were not initialized correctly");
842
843 for (Eigen::Index i = 0; i < dataSize; i++) {
844 data(i + offset) = current(i);
845 oldData(i + offset) = old(i);
846 }
847 offset += dataSize;
848 }
849 }
850}
851
853{
854 // Saves the time grid of each waveform in the data field to be used in the QN method
855 _timeGrids.emplace(cplData, _dataIDs, false);
857
858 // Helper function
859 auto addTimeSliceSize = [&](size_t sum, int id, precice::time::TimeGrids timeGrids) { return sum + timeGrids.getTimeGridAfter(id, windowStart).size() * cplData.at(id)->getSize(); };
860
861 // Size of primary data
862 const size_t primaryDataSize = std::accumulate(_primaryDataIDs.begin(), _primaryDataIDs.end(), (size_t) 0, [&](size_t sum, int id) { return addTimeSliceSize(sum, id, _primaryTimeGrids.value()); });
863
864 // Size of values
865 const size_t dataSize = std::accumulate(_dataIDs.begin(), _dataIDs.end(), (size_t) 0, [&](size_t sum, int id) { return addTimeSliceSize(sum, id, _timeGrids.value()); });
866
867 _values = Eigen::VectorXd::Zero(dataSize);
868 _oldValues = Eigen::VectorXd::Zero(dataSize);
869 _oldXTilde = Eigen::VectorXd::Zero(dataSize);
870 _residuals = Eigen::VectorXd::Zero(dataSize);
871 _primaryValues = Eigen::VectorXd::Zero(primaryDataSize);
872 _oldPrimaryValues = Eigen::VectorXd::Zero(primaryDataSize);
873 _oldPrimaryXTilde = Eigen::VectorXd::Zero(primaryDataSize);
874 _primaryResiduals = Eigen::VectorXd::Zero(primaryDataSize);
875 _oldPrimaryResiduals = Eigen::VectorXd::Zero(primaryDataSize);
876
877 // Also clear the matrices, since the initialization phase will be called more than once when using remeshing
878 _matrixW.resize(0, 0);
879 _matrixV.resize(0, 0);
880
885 std::stringstream ss;
889
890 if (primaryDataSize <= 0) {
891 _hasNodesOnInterface = false;
892 }
893
901 _dimOffsets[0] = 0;
902 _dimOffsetsPrimary[0] = 0;
903 for (size_t i = 0; i < _dimOffsets.size() - 1; i++) {
904 int accumulatedNumberOfUnknowns = 0;
905 int accumulatedNumberOfPrimaryUnknowns = 0;
906
907 for (auto &elem : _dataIDs) {
908 const auto &offsets = cplData.at(elem)->getVertexOffsets();
909
910 accumulatedNumberOfUnknowns += offsets[i] * cplData.at(elem)->getDimensions() * _timeGrids.value().getTimeGridAfter(elem, windowStart).size();
911
913 accumulatedNumberOfPrimaryUnknowns += offsets[i] * cplData.at(elem)->getDimensions() * _primaryTimeGrids.value().getTimeGridAfter(elem, windowStart).size();
914 }
915 }
916 _dimOffsets[i + 1] = accumulatedNumberOfUnknowns;
917 _dimOffsetsPrimary[i + 1] = accumulatedNumberOfPrimaryUnknowns;
918 }
919
920 PRECICE_DEBUG("Number of unknowns at the interface (global): {}", _dimOffsets.back());
922 _infostringstream << fmt::format("\n--------\n DOFs (global): {}\n offsets: {}\n", _dimOffsets.back(), _dimOffsets);
923 }
924
925 // test that the computed number of unknown per proc equals the number of primaryDataSize actually present on that proc
927 PRECICE_ASSERT(dataSize == unknowns, dataSize, unknowns);
929 PRECICE_ASSERT(primaryDataSize == primaryUnknowns, primaryDataSize, primaryUnknowns);
930 } else {
931 _infostringstream << fmt::format("\n--------\n DOFs (global): {}\n", dataSize);
932 }
933
934 // set the number of global rows in the QRFactorization.
935 _qrV.reset();
936 _qrV.setGlobalRows(getPrimaryLSSystemRows());
937
938 std::vector<size_t> subVectorSizes; // needed for preconditioner
939 std::transform(_primaryDataIDs.cbegin(), _primaryDataIDs.cend(), std::back_inserter(subVectorSizes), [&cplData, windowStart, this](const auto &d) { return _primaryTimeGrids.value().getTimeGridAfter(d, windowStart).size() * cplData.at(d)->getSize(); });
940 std::vector<std::string> subVectorNames;
941 std::transform(_primaryDataIDs.cbegin(), _primaryDataIDs.cend(), std::back_inserter(subVectorNames), [&cplData](const auto &d) { return cplData.at(d)->getDataName(); });
942 _preconditioner->initialize(std::move(subVectorSizes), std::move(subVectorNames));
943
945}
946
948{
949 writer.addData("QNColumns", io::TXTTableWriter::INT);
950 writer.addData("DeletedQNColumns", io::TXTTableWriter::INT);
951 writer.addData("DroppedQNColumns", io::TXTTableWriter::INT);
952}
953
955{
956 writer.writeData("QNColumns", getLSSystemCols());
957 writer.writeData("DeletedQNColumns", getDeletedColumns());
958 writer.writeData("DroppedQNColumns", getDroppedColumns());
959}
960
961} // namespace acceleration
962} // namespace precice
#define PRECICE_WARN_IF(condition,...)
Definition LogMacros.hpp:18
#define PRECICE_WARN(...)
Definition LogMacros.hpp:12
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:32
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
OnBoundViolation
Options for handling bound violations.
static void applyRelaxation(double omega, DataMap &cplData, double windowStart)
performs a relaxation given a relaxation factor omega
void checkDataIDs(const DataMap &cplData) const
Checks if all dataIDs are contained in cplData.
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)
double computeShorteningFactor(Eigen::Map< Eigen::MatrixXd > &data, Eigen::Map< Eigen::MatrixXd > &update, const std::vector< std::optional< double > > &lowerBound, const std::vector< std::optional< double > > &upperBound)
calculate the shortening factor to fit the violating data to the bounds
void onBoundViolationsScale(Eigen::VectorXd &data, DataMap &cplData, const std::vector< DataID > &violatingIDs, Eigen::VectorXd &xUpdate)
Handles bound violations after QN update by scaling the QN step.
const double _initialRelaxation
Constant relaxation factor used for first iteration.
virtual void computeQNUpdate(Eigen::VectorXd &xUpdate)=0
Computes the quasi-Newton update using the specified pp scheme (IQNIMVJ, IQNILS)
std::vector< int > _dimOffsetsPrimary
Stores the local dimensions regarding primary data,.
Eigen::VectorXd _oldPrimaryValues
Concatenation of all old primary data involved in the QN system.
int getLSSystemCols() const
: computes number of cols in least squares system, i.e, number of cols in _matrixV,...
int _nbDelCols
Number of filtered out columns in this time window.
void concatenateCouplingData(Eigen::VectorXd &data, Eigen::VectorXd &oldData, const DataMap &cplData, std::vector< int > dataIDs, precice::time::TimeGrids timeGrids, double windowStart) const
Samples and concatenates the data and old data in cplData into a long vector.
void onBoundViolationsClamp(Eigen::VectorXd &data, DataMap &cplData, const std::vector< DataID > &violatingIDs)
Handles bound violations after QN update by clamping.
virtual void specializedIterationsConverged(const DataMap &cplData)=0
Marks a iteration sequence as converged.
virtual void applyFilter()
Applies the filter method for the least-squares system, defined in the configuration.
Eigen::VectorXd _oldValues
Concatenation of all old primary and secondary data involved in the QN system.
std::vector< DataID > _idsWithBounds
Data IDs of all coupling data that have bounds defined.
int getMaxUsedTimeWindows() const
Get the maximum number of reused time windows.
int getMaxUsedIterations() const
Get the maximum number of reused iterations.
Eigen::MatrixXd _matrixVBackup
backup of the V,W and matrixCols data structures. Needed for the skipping of initial relaxation,...
Eigen::VectorXd _oldXTilde
Solver output from last iteration.
Eigen::VectorXd _primaryResiduals
Current iteration residuals of primary IQN data. Temporary.
std::ostringstream _infostringstream
write some debug/acceleration info to file
virtual void updateCouplingData(const DataMap &cplData, double windowStart)
Splits up QN system vector back into the waveforms in coupling data.
bool _resetLS
If true, the LS system has been modified (reset or recomputed) in such a way, that mere updating of m...
void importState(io::TXTReader &reader) final override
Imports the last exported state of the acceleration from file.
Eigen::VectorXd _primaryValues
Concatenation of all primary data involved in the QN system.
int _nbDropCols
Number of dropped columns in this time window (old time window out of scope)
int getDroppedColumns() const
how many QN columns were dropped (went out of scope) in this time window
std::vector< DataID > checkBoundViolation(Eigen::VectorXd &data, DataMap &cplData) const
Check bound violations caused by performing QN update.
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.
void writeLogEntries(io::TXTTableWriter &writer) const override
Writes QN-specific values to the iteration log columns.
void exportState(io::TXTWriter &writer) final override
Exports the current state of the acceleration to a file.
Eigen::VectorXd _residuals
Current iteration residuals of IQN data. Temporary.
std::optional< time::TimeGrids > _primaryTimeGrids
Stores the time grids to which the primary data involved in the QN system will be interpolated to....
virtual void specializedInitializeVectorsAndPreconditioner(const DataMap &cplData)=0
handles the initialization of matrices and vectors in the sub-classes
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.
Eigen::VectorXd _values
Concatenation of all primary and secondary data involved in the QN system.
std::optional< time::TimeGrids > _timeGrids
Stores the time grids to which the primary and secondary data involved in the QN system will be inter...
void initialize(const DataMap &cplData) final override
Initializes the acceleration.
void addLogEntries(io::TXTTableWriter &writer) const override
Adds QN-specific columns to the iteration log file.
const int _timeWindowsReused
Maximum number of old time windows (with data values) kept.
const std::vector< int > _primaryDataIDs
Data IDs of primary data to be involved in the IQN coefficient computation.
void writeInfo(const std::string &s, bool allProcs=false)
Writes info to the _infostream (also in parallel)
std::deque< int > _matrixCols
Indices (of columns in W, V matrices) of 1st iterations of time windows.
bool _firstIteration
Indicates the first iteration, where constant relaxation is used.
int getDeletedColumns() const
how many QN columns were deleted in this time window
const bool _reducedTimeGrid
if _reducedTimeGrid = false uses the full QN-WI and if _reducedTimeGrid = true uses rQN-WI form the p...
impl::PtrPreconditioner _preconditioner
Preconditioner for least-squares system if vectorial system is used.
void clampToBounds(Eigen::Map< Eigen::MatrixXd > &data, const std::vector< std::optional< double > > &lowerBound, const std::vector< std::optional< double > > &upperBound)
Clamp data to their bounds.
impl::QRFactorization _qrV
Stores the current QR decomposition ov _matrixV, can be updated via deletion/insertion of columns.
std::vector< int > _dataIDs
Data IDs of all coupling data.
void iterationsConverged(const DataMap &cplData, double windowStart) final override
Marks a iteration sequence as converged.
void performAcceleration(DataMap &cplData, double windowStart, double windowEnd) final override
Performs one acceleration step.
void initializeVectorsAndPreconditioner(const DataMap &cplData, double windowStart)
Initializes the vectors, matrices and preconditioner This has to be done after the first iteration of...
Eigen::VectorXd _oldPrimaryResiduals
Difference between solver input and output from last time window regarding primary data.
BaseQNAcceleration(double initialRelaxation, bool forceInitialRelaxation, int maxIterationsUsed, int timeWindowsReused, int filter, double singularityLimit, std::vector< int > dataIDs, OnBoundViolation onBoundViolation, impl::PtrPreconditioner preconditioner, bool reducedTimeGrid)
Eigen::VectorXd _oldPrimaryXTilde
Solver output regarding primary data from last iteration.
File reader for matrix/vector in Matlab V7 ASCII format.
Definition TXTReader.hpp:15
File writer for table-data in text-format.
void addData(const std::string &name, DataType type)
Adds a data entry to the table.
void writeData(const std::string &name, int value)
Writes a integral scalar data value associated to the entry name.
File writer for matrix in Matlab V7 ASCII format.
Definition TXTWriter.hpp:13
void stop()
Stops a running event.
Definition Event.cpp:51
Interface for storing the time grids in the Quasi-Newton and Aitken methods. A time grid is a ordered...
Definition TimeGrids.hpp:21
Eigen::VectorXd getTimeGridAfter(int dataID, double time) const
Definition TimeGrids.cpp:23
static void allreduceSum(precice::span< const double > sendData, precice::span< double > rcvData)
static int getSize()
Number of ranks. This includes ranks from both participants, e.g. minimal size is 2.
Definition IntraComm.cpp:47
static double l2norm(const Eigen::VectorXd &vec)
The l2 norm of a vector is calculated on distributed data.
Definition IntraComm.cpp:67
static Rank getRank()
Current rank.
Definition IntraComm.cpp:42
static bool isPrimary()
True if this process is running the primary rank.
Definition IntraComm.cpp:52
static void broadcast(bool &value)
static auto allSecondaryRanks()
Returns an iterable range over salve ranks [1, _size)
Definition IntraComm.hpp:37
static bool isParallel()
True if this process is running in parallel.
Definition IntraComm.cpp:62
static bool isSecondary()
True if this process is running a secondary rank.
Definition IntraComm.cpp:57
static com::PtrCommunication & getCommunication()
Intra-participant communication.
Definition IntraComm.hpp:31
std::shared_ptr< Preconditioner > PtrPreconditioner
contains implementations of acceleration schemes.
provides Import and Export of the coupling mesh and data.
constexpr bool equals(const Eigen::MatrixBase< DerivedA > &A, const Eigen::MatrixBase< DerivedB > &B, double tolerance=NUMERICAL_ZERO_DIFFERENCE)
Compares two Eigen::MatrixBase for equality up to tolerance.
static constexpr SynchronizeTag Synchronize
Convenience instance of the SynchronizeTag.
Definition Event.hpp:28
void removeColumnFromMatrix(Eigen::MatrixXd &A, int col)
bool contained(const ELEMENT_T &element, const std::vector< ELEMENT_T > &vec)
Returns true, if given element is in vector, otherwise false.
Definition Helpers.hpp:38
void appendFront(Eigen::MatrixXd &A, Eigen::VectorXd &v)
void shiftSetFirst(Eigen::MatrixXd &A, const Eigen::VectorXd &v)
Main namespace of the precice library.
STL namespace.