• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

sxs-collaboration / spectre / 4197471208

pending completion
4197471208

push

github

GitHub
Merge pull request #4698 from ffoucart/FiniteVolumeIntegralWeights

12 of 12 new or added lines in 2 files covered. (100.0%)

63692 of 66379 relevant lines covered (95.95%)

355590.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.37
/src/Time/TimeSteppers/AdamsBashforth.cpp
1
// Distributed under the MIT License.
2
// See LICENSE.txt for details.
3

4
#include "Time/TimeSteppers/AdamsBashforth.hpp"
5

6
#include <algorithm>
7
#include <boost/container/static_vector.hpp>
8
#include <boost/iterator/transform_iterator.hpp>
9
#include <iterator>
10
#include <limits>
11
#include <map>
12
#include <ostream>
13
#include <pup.h>
14
#include <tuple>
15

16
#include "NumericalAlgorithms/Interpolation/LagrangePolynomial.hpp"
17
#include "Time/BoundaryHistory.hpp"
18
#include "Time/EvolutionOrdering.hpp"
19
#include "Time/History.hpp"
20
#include "Time/SelfStart.hpp"
21
#include "Time/Time.hpp"
22
#include "Time/TimeStepId.hpp"
23
#include "Utilities/CachedFunction.hpp"
24
#include "Utilities/EqualWithinRoundoff.hpp"
25
#include "Utilities/ErrorHandling/Assert.hpp"
26
#include "Utilities/ErrorHandling/Error.hpp"
27
#include "Utilities/Gsl.hpp"
28
#include "Utilities/Math.hpp"
29
#include "Utilities/Overloader.hpp"
30

31
namespace TimeSteppers {
32

33
namespace {
34
template <typename Iter>
35
struct TimeFromRecord {
36
  Time operator()(typename std::iterator_traits<Iter>::reference record) const {
2,212,238✔
37
    return record.time_step_id.step_time();
2,212,238✔
38
  }
39
};
40

41
// This must be templated on the iterator type rather than the math
42
// wrapper type because of quirks in the template deduction rules.
43
template <typename Iter>
44
auto history_time_iterator(const Iter& it) {
548,322✔
45
  return boost::transform_iterator(it, TimeFromRecord<Iter>{});
548,322✔
46
}
47

48
// TimeDelta-like interface to a double used for dense output
49
struct ApproximateTimeDelta {
50
  double delta = std::numeric_limits<double>::signaling_NaN();
51
  double value() const { return delta; }
21,327✔
52
  bool is_positive() const { return delta > 0.; }
24✔
53

54
  // Only the operators that are actually used are defined.
55
  friend bool operator<(const ApproximateTimeDelta& a,
1,512✔
56
                        const ApproximateTimeDelta& b) {
57
    return a.value() < b.value();
1,512✔
58
  }
59
};
60

61
// Time-like interface to a double used for dense output
62
struct ApproximateTime {
63
  double time = std::numeric_limits<double>::signaling_NaN();
64
  double value() const { return time; }
450✔
65

66
  // Only the operators that are actually used are defined.
67
  friend ApproximateTimeDelta operator-(const ApproximateTime& a,
426✔
68
                                        const Time& b) {
69
    return {a.value() - b.value()};
426✔
70
  }
71

72
  friend bool operator<(const Time& a, const ApproximateTime& b) {
24✔
73
    return a.value() < b.value();
24✔
74
  }
75

76
  friend bool operator<(const ApproximateTime& a, const Time& b) {
×
77
    return a.value() < b.value();
×
78
  }
79

80
  friend std::ostream& operator<<(std::ostream& s, const ApproximateTime& t) {
×
81
    return s << t.value();
×
82
  }
83
};
84

85
// A vector holding one entry per order of integration.
86
template <typename T>
87
using OrderVector =
88
    boost::container::static_vector<T, AdamsBashforth::maximum_order>;
89

90
OrderVector<double> constant_coefficients(const size_t order) {
256,273✔
91
  switch (order) {
256,273✔
92
    case 0: return {};
10,816✔
93
    case 1: return {1.};
114,086✔
94
    case 2: return {1.5, -0.5};
88,592✔
95
    case 3: return {23.0 / 12.0, -4.0 / 3.0, 5.0 / 12.0};
76,948✔
96
    case 4: return {55.0 / 24.0, -59.0 / 24.0, 37.0 / 24.0, -3.0 / 8.0};
66,032✔
97
    case 5: return {1901.0 / 720.0, -1387.0 / 360.0, 109.0 / 30.0,
27,439✔
98
          -637.0 / 360.0, 251.0 / 720.0};
54,878✔
99
    case 6: return {4277.0 / 1440.0, -2641.0 / 480.0, 4991.0 / 720.0,
22,069✔
100
          -3649.0 / 720.0, 959.0 / 480.0, -95.0 / 288.0};
44,138✔
101
    case 7: return {198721.0 / 60480.0, -18637.0 / 2520.0, 235183.0 / 20160.0,
16,832✔
102
          -10754.0 / 945.0, 135713.0 / 20160.0, -5603.0 / 2520.0,
103
          19087.0 / 60480.0};
33,664✔
104
    case 8: return {16083.0 / 4480.0, -1152169.0 / 120960.0, 242653.0 / 13440.0,
11,696✔
105
          -296053.0 / 13440.0, 2102243.0 / 120960.0, -115747.0 / 13440.0,
106
          32863.0 / 13440.0, -5257.0 / 17280.0};
23,392✔
107
    default:
×
108
      ERROR("Bad order: " << order);
×
109
  }
110
}
111

112
// Only T=double is used, but this can be used with T=Rational to
113
// generate coefficient tables.
114
template <typename T>
115
OrderVector<T> variable_coefficients(const OrderVector<T>& control_times) {
25,426✔
116
  // The argument vector contains the control times for a step from
117
  // the last time in the list to t=0.
118

119
  // The coefficients are, for each j,
120
  // 1/step \int_0^{step} dt ell_j(t; -control_times),
121
  // where the step size is step=-control_times.back().
122

123
  const size_t order = control_times.size();
50,852✔
124
  OrderVector<T> result;
125
  for (size_t j = 0; j < order; ++j) {
127,259✔
126
    // Calculate coefficients of the Lagrange interpolating polynomials,
127
    // in the standard a_0 + a_1 t + a_2 t^2 + ... form.
128
    OrderVector<T> poly(order, 0);
101,833✔
129

130
    poly[0] = 1;
101,833✔
131

132
    for (size_t m = 0; m < order; ++m) {
585,906✔
133
      if (m == j) {
484,073✔
134
        continue;
101,833✔
135
      }
136
      const T denom =
382,240✔
137
          1 / (control_times[order - m - 1] - control_times[order - j - 1]);
382,240✔
138
      for (size_t i = m < j ? m + 1 : m; i > 0; --i) {
1,453,090✔
139
        poly[i] =
1,070,850✔
140
            (poly[i - 1] + poly[i] * control_times[order - m - 1]) * denom;
1,070,850✔
141
      }
142
      poly[0] *= control_times[order - m - 1] * denom;
382,240✔
143
    }
144

145
    // Integrate p(t), term by term.  We choose the constant of
146
    // integration so the indefinite integral is zero at t=0.  We do
147
    // not adjust the indexing, so the t^n term in the integral is in
148
    // the (n-1)th entry of the vector (as opposed to the nth entry
149
    // before integrating).  This is convenient because we want to
150
    // divide by the step size in the end, which is equivalent to this
151
    // shift.
152
    for (size_t m = 0; m < order; ++m) {
585,906✔
153
      poly[m] /= m + 1;
484,073✔
154
    }
155
    result.push_back(evaluate_polynomial(poly, -control_times.back()));
203,666✔
156
  }
157
  return result;
25,426✔
158
}
159

160
// Get coefficients for a time step.  Arguments are an iterator
161
// pair to past times, oldest to newest, and the time step to take.
162
template <typename Iterator, typename Delta>
163
OrderVector<double> get_coefficients(const Iterator& times_begin,
281,655✔
164
                                     const Iterator& times_end,
165
                                     const Delta& step) {
166
  bool constant_step_size = true;
281,655✔
167
  OrderVector<double> control_times;
281,655✔
168
  for (auto t = times_begin; t != times_end; ++t) {
1,257,461✔
169
    // Ideally we would also include the slab size in the scale of the
170
    // roundoff comparison, but there's no good way to get it here,
171
    // and it should only matter for slabs near time zero.
172
    if (constant_step_size and not control_times.empty() and
2,549,517✔
173
        not equal_within_roundoff(
1,946,189✔
174
            t->value() - control_times.back(), step.value(),
674,565✔
175
            100.0 * std::numeric_limits<double>::epsilon(), abs(t->value()))) {
2,229,815✔
176
      constant_step_size = false;
25,426✔
177
    }
178
    control_times.push_back(t->value());
1,951,618✔
179
  }
180
  if (constant_step_size) {
281,655✔
181
    return constant_coefficients(control_times.size());
256,229✔
182
  }
183

184
  const double goal_time = control_times.back() + step.value();
25,426✔
185
  for (auto& t : control_times) {
178,111✔
186
    t -= goal_time;
101,833✔
187
  }
188

189
  return variable_coefficients(control_times);
25,426✔
190
}
191

192
template <typename T>
193
void clean_history(const MutableUntypedHistory<T>& history) {
245,941✔
194
  ASSERT(history.size() >= history.integration_order(),
245,941✔
195
         "Insufficient data to take an order-" << history.integration_order()
196
         << " step.  Have " << history.size() << " times, need "
197
         << history.integration_order());
198
  while (history.size() > history.integration_order()) {
495,044✔
199
    history.pop_front();
249,103✔
200
  }
201
  history.discard_value(history.back().time_step_id);
245,941✔
202
}
245,941✔
203
}  // namespace
204

205
AdamsBashforth::AdamsBashforth(const size_t order) : order_(order) {
3,331✔
206
  if (order_ < 1 or order_ > maximum_order) {
3,331✔
207
    ERROR("The order for Adams-Bashforth Nth order must be 1 <= order <= "
×
208
          << maximum_order);
209
  }
210
}
3,331✔
211

212
size_t AdamsBashforth::order() const { return order_; }
4,258✔
213

214
size_t AdamsBashforth::error_estimate_order() const { return order_ - 1; }
96✔
215

216
size_t AdamsBashforth::number_of_past_steps() const { return order_ - 1; }
3,940✔
217

218
double AdamsBashforth::stable_step() const {
86✔
219
  if (order_ == 1) {
86✔
220
    return 1.;
42✔
221
  }
222

223
  // This is the condition that the characteristic polynomial of the
224
  // recurrence relation defined by the method has the correct sign at
225
  // -1.  It is not clear whether this is actually sufficient.
226
  const auto& coefficients = constant_coefficients(order_);
44✔
227
  double invstep = 0.;
44✔
228
  double sign = 1.;
44✔
229
  for (const auto coef : coefficients) {
422✔
230
    invstep += sign * coef;
145✔
231
    sign = -sign;
145✔
232
  }
233
  return 1. / invstep;
44✔
234
}
235

236
TimeStepId AdamsBashforth::next_time_id(const TimeStepId& current_id,
245,767✔
237
                                        const TimeDelta& time_step) const {
238
  ASSERT(current_id.substep() == 0, "Adams-Bashforth should not have substeps");
245,767✔
239
  return current_id.next_step(time_step);
245,767✔
240
}
241

242
void AdamsBashforth::pup(PUP::er& p) {
7,005✔
243
  LtsTimeStepper::pup(p);
7,005✔
244
  p | order_;
7,005✔
245
}
7,005✔
246

247
template <typename T>
248
void AdamsBashforth::update_u_impl(
221,602✔
249
    const gsl::not_null<T*> u, const MutableUntypedHistory<T>& history,
250
    const TimeDelta& time_step) const {
251
  clean_history(history);
221,602✔
252
  update_u_common(u, history, time_step, history.integration_order());
221,602✔
253
}
221,602✔
254

255
template <typename T>
256
bool AdamsBashforth::update_u_impl(
24,339✔
257
    const gsl::not_null<T*> u, const gsl::not_null<T*> u_error,
258
    const MutableUntypedHistory<T>& history, const TimeDelta& time_step) const {
259
  clean_history(history);
24,339✔
260
  *u_error = *u;
24,339✔
261
  update_u_common(u, history, time_step, history.integration_order());
24,339✔
262
  // the error estimate is only useful once the history has enough elements to
263
  // do more than one order of step
264
  update_u_common(u_error, history, time_step, history.integration_order() - 1);
24,339✔
265
  *u_error = *u - *u_error;
24,339✔
266
  return true;
24,339✔
267
}
268

269
template <typename T>
270
bool AdamsBashforth::dense_update_u_impl(const gsl::not_null<T*> u,
3,632✔
271
                                         const ConstUntypedHistory<T>& history,
272
                                         const double time) const {
273
  const ApproximateTimeDelta time_step{
3,632✔
274
      time - history.back().time_step_id.step_time().value()};
3,632✔
275
  update_u_common(u, history, time_step, history.integration_order());
3,632✔
276
  return true;
3,632✔
277
}
278

279
template <typename T, typename Delta>
280
void AdamsBashforth::update_u_common(const gsl::not_null<T*> u,
273,912✔
281
                                     const ConstUntypedHistory<T>& history,
282
                                     const Delta& time_step,
283
                                     const size_t order) const {
284
  ASSERT(
273,912✔
285
      history.size() > 0,
286
      "Cannot meaningfully update the evolved variables with an empty history");
287
  ASSERT(order <= order_,
273,912✔
288
         "Requested integration order higher than integrator order");
289

290
  const auto history_start =
291
      history.end() -
273,912✔
292
      static_cast<typename ConstUntypedHistory<T>::difference_type>(order);
273,912✔
293
  const auto coefficients =
547,824✔
294
      get_coefficients(history_time_iterator(history_start),
273,912✔
295
                       history_time_iterator(history.end()), time_step);
547,824✔
296

297
  auto coefficient = coefficients.rbegin();
298
  for (auto history_entry = history_start;
1,223,883✔
299
       history_entry != history.end();
1,223,883✔
300
       ++history_entry, ++coefficient) {
949,975✔
301
    *u += time_step.value() * *coefficient * history_entry->derivative;
949,975✔
302
  }
303
}
273,912✔
304

305
template <typename T>
306
bool AdamsBashforth::can_change_step_size_impl(
264✔
307
    const TimeStepId& time_id, const ConstUntypedHistory<T>& history) const {
308
  // We need to forbid local time-stepping before initialization is
309
  // complete.  The self-start procedure itself should never consider
310
  // changing the step size, but we need to wait during the main
311
  // evolution until the self-start history has been replaced with
312
  // "real" values.
313
  const evolution_less<Time> less{time_id.time_runs_forward()};
264✔
314
  return not ::SelfStart::is_self_starting(time_id) and
528✔
315
         (history.size() == 0 or
264✔
316
          (less(history.back().time_step_id.step_time(),
255✔
317
                time_id.step_time()) and
249✔
318
           std::is_sorted(history_time_iterator(history.begin()),
249✔
319
                          history_time_iterator(history.end()), less)));
513✔
320
}
321

322
template <typename T>
323
void AdamsBashforth::add_boundary_delta_impl(
7,500✔
324
    const gsl::not_null<T*> result,
325
    const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,
326
    const TimeSteppers::BoundaryHistoryCleaner& cleaner,
327
    const TimeDelta& time_step) const {
328
  const auto signed_order =
7,500✔
329
      static_cast<typename decltype(cleaner.local_end())::difference_type>(
330
          cleaner.integration_order());
7,500✔
331

332
  ASSERT(cleaner.local_size() >= cleaner.integration_order(),
7,500✔
333
         "Insufficient data to take an order-" << cleaner.integration_order()
334
         << " step.  Have " << cleaner.local_size() << " times, need "
335
         << cleaner.integration_order());
336
  cleaner.local_mark_unneeded(cleaner.local_end() - signed_order);
7,500✔
337

338
  if (std::equal(cleaner.local_begin(), cleaner.local_end(),
7,500✔
339
                 cleaner.remote_end() - signed_order)) {
7,500✔
340
    // GTS
341
    ASSERT(cleaner.remote_size() >= cleaner.integration_order(),
7,257✔
342
           "Insufficient data to take an order-" << cleaner.integration_order()
343
           << " step.  Have " << cleaner.remote_size() << " times, need "
344
           << cleaner.integration_order());
345
    cleaner.remote_mark_unneeded(cleaner.remote_end() - signed_order);
7,257✔
346
  } else {
347
    const auto remote_step_for_step_start =
243✔
348
        std::upper_bound(cleaner.remote_begin(), cleaner.remote_end(),
243✔
349
                         *(cleaner.local_end() - 1),
243✔
350
                         evolution_less<Time>{time_step.is_positive()});
243✔
351
    ASSERT(remote_step_for_step_start - cleaner.remote_begin() >= signed_order,
243✔
352
           "Insufficient data to take an order-" << cleaner.integration_order()
353
           << " step.  Have "
354
           << remote_step_for_step_start - cleaner.remote_begin()
355
           << " times before the step, need " << cleaner.integration_order());
356
    cleaner.remote_mark_unneeded(remote_step_for_step_start - signed_order);
243✔
357
  }
358

359
  boundary_impl(result, coupling, *(cleaner.local_end() - 1) + time_step);
7,500✔
360
}
7,500✔
361

362
template <typename T>
363
void AdamsBashforth::boundary_dense_output_impl(
24✔
364
    const gsl::not_null<T*> result,
365
    const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,
366
    const double time) const {
367
  return boundary_impl(result, coupling, ApproximateTime{time});
24✔
368
}
369

370
template <typename T, typename TimeType>
371
void AdamsBashforth::boundary_impl(const gsl::not_null<T*> result,
7,524✔
372
                                   const BoundaryHistoryEvaluator<T>& coupling,
373
                                   const TimeType& end_time) const {
374
  // Might be different from order_ during self-start.
375
  const auto current_order = coupling.integration_order();
7,524✔
376

377
  ASSERT(current_order <= order_,
7,524✔
378
         "Local history is too long for target order (" << current_order
379
         << " should not exceed " << order_ << ")");
380
  ASSERT(coupling.remote_size() >= current_order,
7,524✔
381
         "Remote history is too short (" << coupling.remote_size()
382
         << " should be at least " << current_order << ")");
383

384
  // Avoid billions of casts
385
  const auto order_s = static_cast<
7,524✔
386
      typename BoundaryHistoryEvaluator<T>::iterator::difference_type>(
387
      current_order);
388

389
  // Start and end of the step we are trying to take
390
  const Time start_time = *(coupling.local_end() - 1);
7,524✔
391
  const auto time_step = end_time - start_time;
7,524✔
392

393
  // We define the local_begin and remote_begin variables as the start
394
  // of the part of the history relevant to this calculation.
395
  // Boundary history cleanup happens immediately before the step, but
396
  // boundary dense output happens before that, so there may be data
397
  // left over that was needed for the previous step and has not been
398
  // cleaned out yet.
399
  const auto local_begin = coupling.local_end() - order_s;
7,524✔
400

401
  if (std::equal(local_begin, coupling.local_end(),
7,524✔
402
                 coupling.remote_end() - order_s)) {
7,524✔
403
    // No local time-stepping going on.
404
    const auto coefficients =
7,257✔
405
        get_coefficients(local_begin, coupling.local_end(), time_step);
7,257✔
406

407
    auto local_it = local_begin;
7,257✔
408
    auto remote_it = coupling.remote_end() - order_s;
7,257✔
409
    for (auto coefficients_it = coefficients.rbegin();
24,131✔
410
         coefficients_it != coefficients.rend();
31,388✔
411
         ++coefficients_it, ++local_it, ++remote_it) {
24,131✔
412
      *result +=
48,166✔
413
          time_step.value() * *coefficients_it * *coupling(local_it, remote_it);
24,227✔
414
    }
415
    return;
7,257✔
416
  }
417

418
  ASSERT(current_order == order_,
267✔
419
         "Cannot perform local time-stepping while self-starting.");
420

421
  const evolution_less<> less{time_step.is_positive()};
267✔
422
  const auto remote_begin =
267✔
423
      std::upper_bound(coupling.remote_begin(), coupling.remote_end(),
267✔
424
                       start_time, less) -
425
      order_s;
426

427
  ASSERT(std::is_sorted(local_begin, coupling.local_end(), less),
267✔
428
         "Local history not in order");
429
  ASSERT(std::is_sorted(remote_begin, coupling.remote_end(), less),
267✔
430
         "Remote history not in order");
431
  ASSERT(not less(start_time, *(remote_begin + (order_s - 1))),
267✔
432
         "Remote history does not extend far enough back");
433
  ASSERT(less(*(coupling.remote_end() - 1), end_time),
267✔
434
         "Please supply only older data: " << *(coupling.remote_end() - 1)
435
         << " is not before " << end_time);
436

437
  // Union of times of all step boundaries on any side.
438
  const auto union_times = [&coupling, &local_begin, &remote_begin, &less]() {
2,670✔
439
    std::vector<Time> ret;
534✔
440
    ret.reserve(coupling.local_size() + coupling.remote_size());
267✔
441
    std::set_union(local_begin, coupling.local_end(), remote_begin,
267✔
442
                   coupling.remote_end(), std::back_inserter(ret), less);
443
    return ret;
534✔
444
  }();
445

446
  using UnionIter = typename decltype(union_times)::const_iterator;
447

448
  // Find the union times iterator for a given time.
449
  const auto union_step = [&union_times, &less](const Time& t) {
12,023✔
450
    return std::lower_bound(union_times.cbegin(), union_times.cend(), t, less);
5,878✔
451
  };
452

453
  // The union time index for the step start.
454
  const auto union_step_start = union_step(start_time);
267✔
455

456
  // min(union_times.end(), it + order_s) except being careful not
457
  // to create out-of-range iterators.
458
  const auto advance_within_step = [order_s,
18,354✔
459
                                    &union_times](const UnionIter& it) {
460
    return union_times.end() - it >
12,058✔
461
                   static_cast<typename decltype(union_times)::difference_type>(
462
                       order_s)
463
               ? it + static_cast<typename decltype(
6,029✔
464
                          union_times)::difference_type>(order_s)
465
               : union_times.end();
6,029✔
466
  };
467

468
  // Calculating the Adams-Bashforth coefficients is somewhat
469
  // expensive, so we cache them.  ab_coefs(it, step) returns the
470
  // coefficients used to step from *it to *it + step.
471
  auto ab_coefs = Overloader{
534✔
472
      make_cached_function<std::tuple<UnionIter, TimeDelta>, std::map>(
473
          [order_s](const std::tuple<UnionIter, TimeDelta>& args) {
924✔
474
            return get_coefficients(
475
                std::get<0>(args) -
462✔
476
                    static_cast<typename UnionIter::difference_type>(order_s -
462✔
477
                                                                     1),
478
                std::get<0>(args) + 1, std::get<1>(args));
1,386✔
479
          }),
480
      make_cached_function<std::tuple<UnionIter, ApproximateTimeDelta>,
481
                           std::map>(
482
          [order_s](const std::tuple<UnionIter, ApproximateTimeDelta>& args) {
48✔
483
            return get_coefficients(
484
                std::get<0>(args) -
24✔
485
                    static_cast<typename UnionIter::difference_type>(order_s -
24✔
486
                                                                     1),
487
                std::get<0>(args) + 1, std::get<1>(args));
72✔
488
          })};
489

490
  // The value of the coefficient of `evaluation_step` when doing
491
  // a standard Adams-Bashforth integration over the union times
492
  // from `step` to `step + 1`.
493
  const auto base_summand = [&ab_coefs, &end_time, &union_times](
13,269✔
494
                                const UnionIter& step,
495
                                const UnionIter& evaluation_step) {
496
    if (step + 1 != union_times.end()) {
5,004✔
497
      const TimeDelta step_size = *(step + 1) - *step;
2,010✔
498
      return step_size.value() *
2,010✔
499
             ab_coefs(std::make_tuple(
2,010✔
500
                 step, step_size))[static_cast<size_t>(step - evaluation_step)];
2,010✔
501
    } else {
502
      const auto step_size = end_time - *step;
2,994✔
503
      return step_size.value() *
2,994✔
504
             ab_coefs(std::make_tuple(
2,994✔
505
                 step, step_size))[static_cast<size_t>(step - evaluation_step)];
2,994✔
506
    }
507
  };
508

509
  for (auto local_evaluation_step = local_begin;
1,215✔
510
       local_evaluation_step != coupling.local_end();
1,215✔
511
       ++local_evaluation_step) {
948✔
512
    const auto union_local_evaluation_step = union_step(*local_evaluation_step);
948✔
513
    for (auto remote_evaluation_step = remote_begin;
5,568✔
514
         remote_evaluation_step != coupling.remote_end();
5,568✔
515
         ++remote_evaluation_step) {
4,620✔
516
      double deriv_coef = 0.;
4,620✔
517

518
      if (*local_evaluation_step == *remote_evaluation_step) {
4,620✔
519
        // The two elements stepped at the same time.  This gives a
520
        // standard Adams-Bashforth contribution to each segment
521
        // making up the current step.
522
        const auto union_step_upper_bound =
523
            advance_within_step(union_local_evaluation_step);
365✔
524
        for (auto step = union_step_start;
943✔
525
             step < union_step_upper_bound;
943✔
526
             ++step) {
578✔
527
          deriv_coef += base_summand(step, union_local_evaluation_step);
578✔
528
        }
529
      } else {
530
        // In this block we consider a coupling evaluation that is not
531
        // performed at equal times on the two sides of the mortar.
532

533
        // Makes an iterator with a map to give time as a double.
534
        const auto make_lagrange_iterator = [](const auto& it) {
13,508✔
535
          return boost::make_transform_iterator(
536
              it, [](const Time& t) { return t.value(); });
33,770✔
537
        };
538

539
        const auto union_remote_evaluation_step =
540
            union_step(*remote_evaluation_step);
4,255✔
541
        const auto union_step_lower_bound =
4,255✔
542
            std::max(union_step_start, union_remote_evaluation_step);
4,255✔
543

544
        // Compute the contribution to an interpolation over the local
545
        // times to `remote_evaluation_step->value()`, which we will
546
        // use as the coupling value for that time.  If there is an
547
        // actual evaluation at that time then skip this because the
548
        // Lagrange polynomial will be zero.
549
        if (not std::binary_search(local_begin, coupling.local_end(),
4,255✔
550
                                   *remote_evaluation_step, less)) {
4,255✔
551
          const auto union_step_upper_bound =
552
              advance_within_step(union_remote_evaluation_step);
3,006✔
553
          for (auto step = union_step_lower_bound;
5,600✔
554
               step < union_step_upper_bound;
5,600✔
555
               ++step) {
2,594✔
556
            deriv_coef += base_summand(step, union_remote_evaluation_step);
2,594✔
557
          }
558
          deriv_coef *=
3,006✔
559
              lagrange_polynomial(make_lagrange_iterator(local_evaluation_step),
3,006✔
560
                                  remote_evaluation_step->value(),
561
                                  make_lagrange_iterator(local_begin),
562
                                  make_lagrange_iterator(coupling.local_end()));
3,006✔
563
        }
564

565
        // Same qualitative calculation as the previous block, but
566
        // interpolating over the remote times.  This case is somewhat
567
        // more complicated because the latest remote time that can be
568
        // used varies for the different segments making up the step.
569
        if (not std::binary_search(remote_begin, coupling.remote_end(),
4,255✔
570
                                   *local_evaluation_step, less)) {
4,255✔
571
          auto union_step_upper_bound =
572
              advance_within_step(union_local_evaluation_step);
2,658✔
573
          if (coupling.remote_end() - remote_evaluation_step > order_s) {
2,658✔
574
            union_step_upper_bound = std::min(
408✔
575
                union_step_upper_bound,
576
                union_step(*(remote_evaluation_step + order_s)));
816✔
577
          }
578

579
          auto control_points = make_lagrange_iterator(
2,658✔
580
              remote_evaluation_step - remote_begin >= order_s
2,658✔
581
                  ? remote_evaluation_step - (order_s - 1)
2,658✔
582
                  : remote_begin);
583
          for (auto step = union_step_lower_bound;
4,490✔
584
               step < union_step_upper_bound;
4,490✔
585
               ++step, ++control_points) {
1,832✔
586
            deriv_coef +=
1,832✔
587
                base_summand(step, union_local_evaluation_step) *
1,832✔
588
                lagrange_polynomial(
1,832✔
589
                    make_lagrange_iterator(remote_evaluation_step),
590
                    local_evaluation_step->value(), control_points,
591
                    control_points +
592
                        static_cast<typename decltype(
593
                            control_points)::difference_type>(order_s));
594
          }
595
        }
596
      }
597

598
      if (deriv_coef != 0.) {
4,620✔
599
        // Skip the (potentially expensive) coupling calculation if
600
        // the coefficient is zero.
601
        *result += deriv_coef *
3,269✔
602
                   *coupling(local_evaluation_step, remote_evaluation_step);
6,538✔
603
      }
604
    }  // for remote_evaluation_step
605
  }  // for local_evaluation_step
606
}
607

608
bool operator==(const AdamsBashforth& lhs, const AdamsBashforth& rhs) {
4✔
609
  return lhs.order_ == rhs.order_;
4✔
610
}
611

612
bool operator!=(const AdamsBashforth& lhs, const AdamsBashforth& rhs) {
1✔
613
  return not(lhs == rhs);
1✔
614
}
615

616
TIME_STEPPER_DEFINE_OVERLOADS(AdamsBashforth)
249,837✔
617
LTS_TIME_STEPPER_DEFINE_OVERLOADS(AdamsBashforth)
7,524✔
618
}  // namespace TimeSteppers
619

620
PUP::able::PUP_ID TimeSteppers::AdamsBashforth::my_PUP_ID = 0;  // NOLINT
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc