13#ifndef PECLET_CORE_SOLVER_ANDERSON_HPP
14#define PECLET_CORE_SOLVER_ANDERSON_HPP
58 std::vector<AndersonRole>
roles;
71namespace detail::anderson {
78 Kokkos::MDRangePolicy<ExecSpace, Kokkos::IndexType<Index>,
79 Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left>>;
90 for (
int i = 0;
i <
N; ++
i)
94 for (
int i = 0;
i <
N; ++
i)
105struct reduction_identity<
peclet::core::solver::detail::anderson::SumN<N>> {
113namespace detail::anderson {
123 const Index ex = e[0],
exy = e[0] * e[1];
124 Kokkos::parallel_reduce(
131 Kokkos::Sum<Index, MemSpace>(
out));
146 Kokkos::parallel_for(
147 "anderson::mix", Kokkos::RangePolicy<
ExecSpace, Kokkos::IndexType<Index>>(0,
buf.extent(0)),
149 double c = 0.0,
d = 0.0;
150 for (
int j = 0;
j <
cols.count; ++
j) {
163 Kokkos::parallel_for(
164 "anderson::diff", Kokkos::RangePolicy<
ExecSpace, Kokkos::IndexType<Index>>(0,
buf.extent(0)),
166 const double r =
buf(
i) - x(
i);
175 Kokkos::parallel_for(
176 "anderson::residual",
177 Kokkos::RangePolicy<
ExecSpace, Kokkos::IndexType<Index>>(0,
buf.extent(0)),
185 const Index ex = e[0],
exy = e[0] * e[1];
186 Kokkos::parallel_reduce(
190 acc.v[0] += x(
i) * x(
i);
201 const Index ex = e[0],
exy = e[0] * e[1];
202 Kokkos::parallel_reduce(
221 for (
int i = 0;
i < n; ++
i)
222 for (
int j = 0;
j < n; ++
j)
223 v[
i][
j] = (
i ==
j) ? 1.0 : 0.0;
225 double off = 0.0,
all = 0.0;
226 for (
int i = 0;
i < n; ++
i)
227 for (
int j = 0;
j < n; ++
j) {
230 off += a[
i][
j] * a[
i][
j];
232 if (std::sqrt(off) <= 1e-15 * std::sqrt(
all))
234 for (
int p = 0; p < n; ++p)
235 for (
int q = p + 1;
q < n; ++
q) {
238 const double theta = (a[
q][
q] - a[p][p]) / (2.0 * a[p][
q]);
240 (theta >= 0.0 ? 1.0 : -1.0) / (std::abs(theta) + std::sqrt(theta * theta + 1.0));
241 const double c = 1.0 / std::sqrt(
t *
t + 1.0), s =
t * c;
242 for (
int k = 0;
k < n; ++
k) {
243 const double akp = a[
k][p],
akq = a[
k][
q];
247 for (
int k = 0;
k < n; ++
k) {
248 const double apk = a[p][
k],
aqk = a[
q][
k];
252 for (
int k = 0;
k < n; ++
k) {
253 const double vkp = v[
k][p],
vkq = v[
k][
q];
259 for (
int i = 0;
i < n; ++
i)
277 for (
int j = 0;
j < n; ++
j) {
278 const double d = std::sqrt(
gram[
j][
j]);
279 r.
dinv[
j] =
d > 0.0 ? 1.0 /
d : 0.0;
282 for (
int i = 0;
i < n; ++
i)
283 for (
int j = 0;
j < n; ++
j)
287 for (
int i = 1;
i < n; ++
i) {
299 for (
int j = 0;
j < n; ++
j)
301 for (
int i = 0;
i < n; ++
i) {
305 for (
int l = 0;
l < n; ++
l)
307 const double w =
vb /
se.lam[
i];
308 for (
int k = 0;
k < n; ++
k)
309 y[
k] +=
se.v[
k][
i] * w;
311 for (
int k = 0;
k < n; ++
k)
312 x[
k] =
se.dinv[
k] * y[
k];
351 double failed[2] = {0.0, 0.0};
355 }
catch (
const std::invalid_argument& e) {
359 const std::size_t
ns = st_.
fields.size();
363 auto alloc = [&](std::vector<View<double>>&
set,
const char* label) {
365 for (std::size_t
f = 0;
f <
ns; ++
f)
367 Kokkos::view_alloc(std::string(
"anderson::") + label, Kokkos::WithoutInitializing),
371 alloc(rPrev_,
"Rprev");
372 alloc(gPrev_,
"Gprev");
373 for (
int s = 0; s < m_; ++s) {
377 allocReductionScratch();
378 }
catch (
const std::exception&) {
384 throw std::invalid_argument(
invalid.empty()
385 ? std::string(
"AndersonCore: the state descriptor is "
386 "inconsistent on another rank")
390 std::snprintf(
msg,
sizeof msg,
391 "AndersonCore: window %d needs %zu bytes ((2m+3)*n_s*8*n_pad, n_s = %zu, "
392 "n_pad = %lld per rank); window 2 needs %zu; run on more ranks or without "
396 throw std::runtime_error(
msg);
407 return static_cast<std::size_t
>(2 *
window + 3) *
numFields *
sizeof(
double) *
408 static_cast<std::size_t
>(
nPad);
419 throw std::logic_error(
"AndersonCore::prepare: called twice without complete()");
425 const std::size_t
ns = st_.
fields.size();
426 for (std::size_t
f = 0;
f <
ns; ++
f)
428 Kokkos::subview(countDev_,
f));
429 Kokkos::deep_copy(
ExecSpace(), countHost_, countDev_);
432 for (std::size_t
f = 0;
f <
ns; ++
f)
433 changed +=
static_cast<double>(countHost_(
f));
440 for (std::size_t
f = 0;
f < st_.
fields.size(); ++
f) {
443 for (
int k = 0;
k < mk_; ++
k) {
444 cols.dG[
k] = dG_[order_[
k]][
f];
445 cols.dR[
k] = dR_[order_[
k]][
f];
446 cols.gamma[
k] = gamma_[
k];
451 for (std::size_t
f = 0;
f < st_.
fields.size(); ++
f)
467 reasonCode_ = Reason::StepFailed;
468 reason_ =
"step failed at an accelerated iterate: " +
what;
470 if (st_.
comm.
rank == 0 && !noticed_) {
471 std::fprintf(
stderr,
"AndersonCore: %s; acceleration disabled\n", reason_.c_str());
482 throw std::logic_error(
"AndersonCore::complete: called without prepare()");
486 namespace da = detail::anderson;
487 const std::size_t
ns = st_.
fields.size();
489 const int g = st_.
ghost;
497 for (
int c = 0; c < m_ && s < 0; ++c)
498 if (std::find(order_, order_ + mk_, c) == order_ + mk_)
500 for (
int k = 0;
k < mk_; ++
k)
504 for (
int k = 1;
k < mk_; ++
k)
508 for (std::size_t
f = 0;
f <
ns; ++
f)
509 da::diff(st_.
fields[
f], x_[
f], rPrev_[
f], gPrev_[
f], dR_[s][
f], dG_[s][
f]);
511 for (std::size_t
f = 0;
f <
ns; ++
f)
519 for (std::size_t
f = 0, v = 0;
f <
ns; ++
f) {
522 da::selfSums(x_[
f], st_.
fields[
f], e, g, Kokkos::subview(selfDev_, v));
525 da::columnSums(dR_[s][
f], dR_[
j][
f], x_[
f], e, g,
530 Kokkos::deep_copy(
ExecSpace(), selfHost_, selfDev_);
532 Kokkos::deep_copy(
ExecSpace(), colHost_, colDev_);
534 std::vector<double>
packet(
static_cast<std::size_t
>(2 *
nCols + 2), 0.0);
535 for (std::size_t v = 0; v < selfHost_.extent(0); ++v) {
536 const da::SumN<2>&
ss = selfHost_(v);
541 for (
int q = 0;
q < 2; ++
q)
542 packet[
static_cast<std::size_t
>(2 + 2 *
k +
q)] +=
cs.v[
q];
549 const double rho = std::sqrt(
xx);
554 residual_ = std::numeric_limits<double>::infinity();
556 residual_ = std::sqrt(
xx /
uu);
560 reasonCode_ = Reason::NonFiniteMixed;
562 reasonCode_ = Reason::NonFinitePlain;
564 reason_ = reasonText(reasonCode_);
573 const double* v = &
packet[
static_cast<std::size_t
>(2 + 2 *
k)];
574 rr_[s][
j] = rr_[
j][s] = v[0];
593 reasonCode_ = Reason::TooManyRestarts;
594 reason_ = reasonText(reasonCode_);
597 decCount_ = (
rho < rhoPrev_) ? decCount_ + 1 : 0;
607 for (
int k = 0;
k < mk_;) {
608 if (rr_[order_[
k]][order_[
k]] > 0.0) {
614 while (mk_ > 1 && condScaled() <
kCondMin)
621 broadcastDecisions();
627 std::swap(rPrev_, x_);
628 for (std::size_t
f = 0;
f <
ns; ++
f)
640 pending_ = engaged_ =
false;
642 rhoMin_ = rhoPrev_ = std::numeric_limits<double>::infinity();
657 reasonCode_ = Reason::DisabledByCaller;
658 reason_ = reasonText(reasonCode_);
668 const std::string&
reason()
const {
return reason_; }
682 std::vector<double>
gamma()
const {
return std::vector<double>(gamma_, gamma_ + mk_); }
686 auto add = [&b](
const std::vector<View<double>>&
set) {
687 for (
const auto& v :
set)
688 b += v.span() *
sizeof(
double);
693 for (
int s = 0; s < m_; ++s) {
702 enum class Reason :
int {
713 struct DecisionPacket {
725 static std::string reasonText(Reason r) {
729 case Reason::StepFailed:
730 return "step failed at an accelerated iterate";
731 case Reason::NonFiniteMixed:
732 return "non-finite residual / failed pressure solve at an accelerated iterate";
733 case Reason::NonFinitePlain:
734 return "non-finite residual on a plain step";
735 case Reason::TooManyRestarts:
736 return "too many restarts";
737 case Reason::DisabledByCaller:
738 return "disabled by the caller";
744 const std::size_t
ns = st_.
fields.size();
746 throw std::invalid_argument(
"AndersonCore: the state has no fields");
748 throw std::invalid_argument(
"AndersonCore: one role per state field is required");
750 throw std::invalid_argument(
"AndersonCore: window must be in [1, 8]");
751 if (!(beta_ > 0.0 && beta_ <= 1.0))
752 throw std::invalid_argument(
"AndersonCore: mixing must be in (0, 1]");
755 throw std::invalid_argument(
"AndersonCore: the padded extent has no inner entries");
757 for (std::size_t
f = 0;
f <
ns; ++
f) {
759 throw std::invalid_argument(
"AndersonCore: a state field is not the full padded box");
761 throw std::invalid_argument(
"AndersonCore: a role must be Velocity or Carried");
767 void sumAll(
double* data,
int count)
const {
772 void restoreLastOutput() {
773 for (std::size_t
f = 0;
f < st_.
fields.size(); ++
f)
779 void allocReductionScratch() {
780 namespace da = detail::anderson;
784 countDev_ = Kokkos::View<Index*, MemSpace>(
"anderson::count", st_.
fields.size());
785 selfDev_ = Kokkos::View<da::SumN<2>*,
MemSpace>(
"anderson::self_sums", nv);
786 colDev_ = Kokkos::View<da::SumN<2>*,
MemSpace>(
"anderson::column_sums", nv *
kMaxWindow);
787 countHost_ = Kokkos::create_mirror_view(countDev_);
788 selfHost_ = Kokkos::create_mirror_view(selfDev_);
789 colHost_ = Kokkos::create_mirror_view(colDev_);
792 void dropColumn(
int k) {
793 for (
int q =
k;
q + 1 < mk_; ++
q)
794 order_[
q] = order_[
q + 1];
799 for (
int a = 0; a < mk_; ++a)
800 for (
int c = 0; c < mk_; ++c)
801 out[a][c] =
full[order_[a]][order_[c]];
805 double condScaled()
const {
809 return se.lamMin /
se.lamMax;
813 void solveTruncated() {
817 for (
int k = 0;
k < mk_; ++
k)
818 b[
k] = b_[order_[
k]];
823 void broadcastDecisions() {
827 p.status =
static_cast<int>(status_);
828 p.reason =
static_cast<int>(reasonCode_);
830 p.pending = pending_ ? 1 : 0;
831 p.restarted = restarted_ ? 1 : 0;
832 p.numRestarts = numRestarts_;
834 p.order[
k] =
k < mk_ ? order_[
k] : -1;
835 p.gamma[
k] =
k < mk_ ? gamma_[
k] : 0.0;
837 p.residual = residual_;
842 status_ =
static_cast<Status>(p.status);
843 reasonCode_ =
static_cast<Reason
>(p.reason);
845 pending_ = p.pending != 0;
846 restarted_ = p.restarted != 0;
847 numRestarts_ = p.numRestarts;
849 order_[
k] = p.order[
k] >= 0 ? p.order[
k] : 0;
850 gamma_[
k] = p.gamma[
k];
852 residual_ = p.residual;
854 reason_ = reasonText(reasonCode_);
862 std::vector<View<double>> x_, rPrev_, gPrev_;
865 Kokkos::View<Index*, MemSpace> countDev_;
866 Kokkos::View<detail::anderson::SumN<2>*,
MemSpace> selfDev_;
867 Kokkos::View<detail::anderson::SumN<2>*,
MemSpace> colDev_;
868 typename Kokkos::View<Index*, MemSpace>::host_mirror_type countHost_;
878 bool havePrev_ =
false, pending_ =
false, engaged_ =
false;
879 bool prepared_ =
false, mixed_ =
false, noticed_ =
false;
880 bool restarted_ =
false;
881 int decCount_ = 0, numRestarts_ = 0, numResets_ = 0;
882 double rhoPrev_ = std::numeric_limits<double>::infinity();
883 double rhoMin_ = std::numeric_limits<double>::infinity();
885 Reason reasonCode_ = Reason::None;
887 double residual_ = std::numeric_limits<double>::infinity();
bool stepFailed(const std::string &what)
§4.3 step 2, the caller's step threw.
static constexpr int kMaxWindow
void invalidate()
The caller's map changed (flow: its parameter signature) or the state was written: reset() and forget...
static std::size_t memoryBytesFor(int window, std::size_t numFields, Index nPad)
The §6.3 memory formula: (2m + 3)·n_s·8·n_pad bytes.
static constexpr double kCondMin
AndersonCore(const AndersonCore &)=delete
AndersonCore(AndersonCore &&)=default
std::size_t memoryBytes() const
Bytes held by the history, X, Rprev and Gprev: equals memoryBytesFor(window(), n_s,...
AndersonCore(AndersonState state, int window=5, double mixing=1.0)
Allocates the history (design §6.3: (2m+3)·n_s·8·n_pad bytes).
void complete(bool pressureSolveFailed=false)
§4.3 steps 3–7, after the caller's step.
const AndersonState & state() const
AndersonCore & operator=(const AndersonCore &)=delete
bool prepare(bool accelerate)
§4.3 steps 0–1.
double residual() const
Relative velocity residual of the last evaluation whose output the state holds (design §3....
const std::string & reason() const
void disable()
Stops acceleration for good: prepare/complete become no-ops around a plain step.
static constexpr double kRestartGrowth
const char * statusName() const
"active" | "disabled".
bool pending() const
A mix is pending for the next accelerated call.
int numColumns() const
Window columns in use (mk).
static constexpr double kNoiseFloor
static constexpr int kMaxRestarts
void reset()
Clears the history and the counters (design §4.2); keeps the previous evaluation, so the next call fo...
static constexpr int kEngageDecreases
AndersonCore & operator=(AndersonCore &&)=default
std::vector< double > gamma() const
The pending coefficients γ, oldest column first (numColumns() of them).
double[kMaxWindow][kMaxWindow] Mat
Kokkos::View< T, MemSpace > DeviceScalar
A reduction result held on the device: a reduction into it does not synchronise the host,...
void truncatedSolve(const ScaledEig &se, const double *b, double condMin, double *x)
x = D⁻¹ Σ_i v_i (v_iᵀ D⁻¹ b)/λ_i over the eigenvalues λ_i ≥ condMin·λ_max (and > 0): the truncated so...
ScaledEig scaledEigen(int n, const Mat &gram)
void columnSums(View< const double > dRs, View< const double > dRj, View< const double > x, const IVec< 3 > &e, int g, const DeviceScalar< SumN< 2 > > &out)
Pass 2, one window column j against the new column s, over the inner entries of one Velocity field: {...
Kokkos::MDRangePolicy< ExecSpace, Kokkos::IndexType< Index >, Kokkos::Rank< 3, Kokkos::Iterate::Left, Kokkos::Iterate::Left > > InnerPolicy
Inner-region policy, x (the first index) fastest in tiles and within a tile on every backend (suite C...
void selfSums(View< const double > x, View< const double > buf, const IVec< 3 > &e, int g, const DeviceScalar< SumN< 2 > > &out)
Pass 2, residual part: {Σ X², Σ buf²} over the inner entries of one Velocity field,...
void countChanged(View< const double > buf, View< const double > prev, const IVec< 3 > &e, int g, const DeviceScalar< Index > &out)
COUNT: inner entries where buf != prev, into out (no host synchronisation).
void jacobiEigen(int n, Mat &a, Mat &v, double(&lam)[kMaxWindow])
Cyclic Jacobi eigendecomposition of the symmetric n×n a (overwritten), fixed sweep order p < q row-ma...
void mix(View< double > buf, View< double > xOut, View< const double > rPrev, const MixColumns &cols, double beta)
MIX (design §4.3 step 1, §4.5): x = buf − ΔG γ − (1−β)(Rprev − ΔR γ); buf = X = x....
InnerPolicy innerPolicy(const IVec< 3 > &e, int g)
void residual(View< const double > buf, View< double > x)
R (step 3, first evaluation): X = buf − X. Padded.
void diff(View< const double > buf, View< double > x, View< const double > rPrev, View< const double > gPrev, View< double > dRs, View< double > dGs)
DIFF (step 3): r = buf − X; dR_s = r − Rprev; dG_s = buf − Gprev; X = r. Padded.
AndersonRole
What a state field is to the metric (design §3.1–3.2, rev 1: the metric is velocity only).
@ Carried
mixed, stored and differenced like the others, but not measured (e.g. P)
@ Velocity
measured at unit weight, every inner entry
ExecSpace::memory_space MemSpace
PECLET_HD Vec3< Real > add(Vec3< Real > a, Vec3< Real > b)
Kokkos::View< T *, MemSpace > View
1D device array.
Kokkos::DefaultExecutionSpace ExecSpace
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
static KOKKOS_FORCEINLINE_FUNCTION peclet::core::solver::detail::anderson::SumN< N > sum()
The collectives of a distributed run.
std::function< void(void *data, std::size_t bytes)> broadcast
int rank
this rank in the communicator; rank 0's decisions win (design §6.1)
std::function< void(double *data, int count)> sumAll
The state descriptor (design §5.1): exactly what the caller's step reads across steps,...
std::vector< View< double > > fields
The state buffers, each over the full padded box (extent(0) == extent[0]·extent[1]·extent[2]),...
AndersonComm comm
Collectives; empty = a single rank.
IVec< 3 > extent
Padded extents e (inner + 2·ghost per axis); inner entries are ghost ≤ i < e − ghost.
std::vector< AndersonRole > roles
One role per field.
The window columns the mix reads, oldest first.
View< const double > dG[kMaxWindow]
View< const double > dR[kMaxWindow]
The Jacobi-scaled eigendecomposition of a Gram block G: A = D⁻¹ G D⁻¹, D = diag(G)^{1/2} (design §4....
N independent double sums in one reduction.
SumN & operator+=(const SumN &o)