Skip to content

Commit 12def62

Browse files
authored
Merge pull request #6565 from wthrowe/variable_order_coeffs
Add coefficient calculations for variable-order Adams LTS
2 parents 7ef827d + 17d0389 commit 12def62

7 files changed

Lines changed: 801 additions & 293 deletions

File tree

src/Time/TimeSteppers/AdamsBashforth.cpp

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -235,31 +235,41 @@ void AdamsBashforth::add_boundary_delta_impl(
235235
const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,
236236
const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,
237237
const TimeDelta& time_step) const {
238-
const auto step_start = local_times.back().step_time();
239-
const size_t integration_order =
240-
local_times.integration_order(local_times.size() - 1);
238+
for (size_t i = 0; i < local_times.size(); ++i) {
239+
ASSERT(local_times.integration_order(i) == order_ or
240+
::SelfStart::is_self_starting(local_times[i]),
241+
"Incorrect local order " << local_times.integration_order(i)
242+
<< " at time " << local_times[i]);
243+
}
244+
for (size_t i = 0; i < remote_times.size(); ++i) {
245+
ASSERT(remote_times.integration_order(i) == order_ or
246+
::SelfStart::is_self_starting(remote_times[i]),
247+
"Incorrect remote order " << remote_times.integration_order(i)
248+
<< " at time " << remote_times[i]);
249+
}
241250

242-
const adams_lts::AdamsScheme scheme{adams_lts::SchemeType::Explicit,
243-
integration_order};
251+
const auto step_start = local_times.back().step_time();
244252
const auto lts_coefficients = adams_lts::lts_coefficients(
245-
local_times, remote_times, step_start, step_start + time_step, scheme,
246-
scheme, scheme);
253+
local_times, remote_times, step_start, step_start + time_step,
254+
adams_lts::SchemeType::Explicit, adams_lts::SchemeType::Explicit, 0, 0);
247255
adams_lts::apply_coefficients(result, lts_coefficients, coupling);
248256
}
249257

250258
void AdamsBashforth::clean_boundary_history_impl(
251259
const TimeSteppers::MutableBoundaryHistoryTimes& local_times,
252260
const TimeSteppers::MutableBoundaryHistoryTimes& remote_times) const {
253-
const size_t integration_order =
261+
const size_t local_order =
254262
local_times.integration_order(local_times.size() - 1);
255-
256-
while (local_times.size() >= integration_order) {
263+
while (local_times.size() >= local_order) {
257264
local_times.pop_front();
258265
}
266+
267+
const size_t remote_order =
268+
remote_times.integration_order(remote_times.size() - 1);
259269
// We're guaranteed to have a new local value inserted before the
260270
// next use, but not a new remote value, so we need to keep one more
261271
// of these.
262-
while (remote_times.size() > integration_order) {
272+
while (remote_times.size() > remote_order) {
263273
remote_times.pop_front();
264274
}
265275
}
@@ -271,24 +281,35 @@ void AdamsBashforth::boundary_dense_output_impl(
271281
const TimeSteppers::ConstBoundaryHistoryTimes& remote_times,
272282
const TimeSteppers::BoundaryHistoryEvaluator<T>& coupling,
273283
const double time) const {
284+
for (size_t i = 0; i < local_times.size(); ++i) {
285+
ASSERT(local_times.integration_order(i) == order_ or
286+
::SelfStart::is_self_starting(local_times[i]),
287+
"Incorrect local order " << local_times.integration_order(i)
288+
<< " at time " << local_times[i]);
289+
}
290+
for (size_t i = 0; i < remote_times.size(); ++i) {
291+
ASSERT(remote_times.integration_order(i) == order_ or
292+
::SelfStart::is_self_starting(remote_times[i]),
293+
"Incorrect remote order " << remote_times.integration_order(i)
294+
<< " at time " << remote_times[i]);
295+
}
296+
274297
if (local_times.back().step_time().value() == time) {
275298
// Nothing to do. The requested time is the start of the step,
276299
// which is the input value of `result`.
277300
return;
278301
}
279-
const auto current_order =
280-
local_times.integration_order(local_times.size() - 1);
281-
const adams_lts::AdamsScheme scheme{adams_lts::SchemeType::Explicit,
282-
current_order};
283302
const auto small_step_start =
284303
std::max(local_times.back(), remote_times.back()).step_time();
285304
const auto lts_coefficients =
286-
adams_lts::lts_coefficients(local_times, remote_times,
287-
local_times.back().step_time(),
288-
small_step_start, scheme, scheme, scheme) +
305+
adams_lts::lts_coefficients(
306+
local_times, remote_times, local_times.back().step_time(),
307+
small_step_start, adams_lts::SchemeType::Explicit,
308+
adams_lts::SchemeType::Explicit, 0, 0) +
289309
adams_lts::lts_coefficients(local_times, remote_times, small_step_start,
290-
ApproximateTime{time}, scheme, scheme,
291-
scheme);
310+
ApproximateTime{time},
311+
adams_lts::SchemeType::Explicit,
312+
adams_lts::SchemeType::Explicit, 0, 0);
292313
adams_lts::apply_coefficients(result, lts_coefficients, coupling);
293314
}
294315

src/Time/TimeSteppers/AdamsLts.cpp

Lines changed: 68 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,6 @@ void apply_coefficients(const gsl::not_null<T*> result,
155155
}
156156
}
157157

158-
bool operator==(const AdamsScheme& a, const AdamsScheme& b) {
159-
return a.type == b.type and a.order == b.order;
160-
}
161-
bool operator!=(const AdamsScheme& a, const AdamsScheme& b) {
162-
return not(a == b);
163-
}
164-
165158
namespace {
166159
// Collect the ids used for interpolating during a step to `end_time`
167160
// from `times`.
@@ -171,29 +164,33 @@ namespace {
171164
template <typename TimeType>
172165
OrderVector<TimeStepId> find_relevant_ids(
173166
const ConstBoundaryHistoryTimes& times, const TimeType& end_time,
174-
const AdamsScheme& scheme) {
167+
const SchemeType scheme_type, const int order_offset) {
175168
OrderVector<TimeStepId> ids{};
176169
using difference_type = std::iterator_traits<
177170
ConstBoundaryHistoryTimes::const_iterator>::difference_type;
178171
const evolution_less<> less{times.front().time_runs_forward()};
179172
// Can't do a binary search because times are not sorted during self-start.
180173
auto used_range_end = times.end();
181-
while (used_range_end != times.begin()) {
174+
for (;;) {
175+
ASSERT(used_range_end != times.begin(),
176+
"All times in history were after " << end_time);
182177
if (less((used_range_end - 1)->step_time(), end_time)) {
183178
break;
184179
}
185180
--used_range_end;
186181
}
182+
const auto last_step =
183+
static_cast<size_t>(used_range_end - times.begin() - 1);
184+
const auto order =
185+
static_cast<difference_type>(times.integration_order(last_step)) +
186+
order_offset;
187187
const auto number_of_past_steps =
188-
static_cast<difference_type>(scheme.order) -
189-
(scheme.type == SchemeType::Implicit ? 1 : 0);
188+
order - (scheme_type == SchemeType::Implicit ? 1 : 0);
190189
ASSERT(used_range_end - times.begin() >= number_of_past_steps,
191190
"Insufficient past data.");
192191
std::copy(used_range_end - number_of_past_steps, used_range_end,
193192
std::back_inserter(ids));
194-
if (scheme.type == SchemeType::Implicit) {
195-
const auto last_step =
196-
static_cast<size_t>(used_range_end - times.begin() - 1);
193+
if (scheme_type == SchemeType::Implicit) {
197194
ASSERT(times.number_of_substeps(last_step) == 2,
198195
"Must have substep data for implicit stepping.");
199196
ids.push_back(times[{last_step, 1}]);
@@ -202,52 +199,42 @@ OrderVector<TimeStepId> find_relevant_ids(
202199
}
203200

204201
// Choose the relevant times from `local` and `remote` for defining
205-
// small steps for `small_step_scheme`, using the specified schemes
206-
// for interpolation on the local and remote sides.
202+
// the small steps, using the specified schemes for interpolation on
203+
// the local and remote sides.
207204
//
208-
// This is the most recent values from the union of the `local` and
209-
// `remote` times with any values that should only be used for
210-
// interpolation removed.
205+
// This is the `num_steps` most recent values from the union of the
206+
// `local` and `remote` times, excluding any values that should only
207+
// be used for interpolation.
211208
OrderVector<Time> merge_to_small_steps(const OrderVector<Time>& local,
212209
const OrderVector<Time>& remote,
213210
const evolution_less<Time>& less,
214-
const AdamsScheme& local_scheme,
215-
const AdamsScheme& remote_scheme,
216-
const AdamsScheme& small_step_scheme) {
217-
OrderVector<Time> small_steps(small_step_scheme.order);
211+
const SchemeType local_scheme,
212+
const SchemeType remote_scheme,
213+
const size_t num_steps) {
214+
OrderVector<Time> small_steps(num_steps);
218215
auto local_it = local.rbegin();
219216
auto remote_it = remote.rbegin();
220217

221-
ASSERT(not(local_scheme.type == SchemeType::Implicit and
222-
remote_scheme.type == SchemeType::Explicit and
218+
ASSERT(not(local_scheme == SchemeType::Implicit and
219+
remote_scheme == SchemeType::Explicit and
223220
less(*local_it, *remote_it)),
224221
"Explicit time " << *remote_it << " after implicit " << *local_it);
225-
ASSERT(not(remote_scheme.type == SchemeType::Implicit and
226-
local_scheme.type == SchemeType::Explicit and
222+
ASSERT(not(remote_scheme == SchemeType::Implicit and
223+
local_scheme == SchemeType::Explicit and
227224
less(*remote_it, *local_it)),
228225
"Explicit time " << *local_it << " after implicit " << *remote_it);
229226

230-
if (small_step_scheme.type == SchemeType::Explicit) {
231-
// Don't use implicit interpolation points for an explicit step
232-
if (local_scheme.type == SchemeType::Implicit) {
233-
++local_it;
234-
}
235-
if (remote_scheme.type == SchemeType::Implicit) {
227+
if (local_scheme == SchemeType::Implicit and
228+
remote_scheme == SchemeType::Implicit) {
229+
// If both the interpolation schemes are implicit, we will get
230+
// two times after the small step we are working on, one from
231+
// each. One of them (if they are different) belongs to a later
232+
// small step, and we should ignore it. If they are the same,
233+
// ignoring one of them is harmless.
234+
if (less(*local_it, *remote_it)) {
236235
++remote_it;
237-
}
238-
} else {
239-
if (local_scheme.type == SchemeType::Implicit and
240-
remote_scheme.type == SchemeType::Implicit) {
241-
// If both the interpolation schemes are implicit, we will get
242-
// two times after the small step we are working on, one from
243-
// each. One of them (if they are different) belongs to a later
244-
// small step, and we should ignore it. If they are the same,
245-
// ignoring one of them is harmless.
246-
if (less(*local_it, *remote_it)) {
247-
++remote_it;
248-
} else {
249-
++local_it;
250-
}
236+
} else {
237+
++local_it;
251238
}
252239
}
253240

@@ -327,44 +314,61 @@ LtsCoefficients lts_coefficients(const ConstBoundaryHistoryTimes& local_times,
327314
const ConstBoundaryHistoryTimes& remote_times,
328315
const Time& start_time,
329316
const TimeType& end_time,
330-
const AdamsScheme& local_scheme,
331-
const AdamsScheme& remote_scheme,
332-
const AdamsScheme& small_step_scheme) {
317+
const SchemeType local_scheme,
318+
const SchemeType remote_scheme,
319+
const int local_order_offset,
320+
const int remote_order_offset) {
321+
ASSERT(local_order_offset == 0 or local_order_offset == -1,
322+
"Must be 0 or -1, not " << local_order_offset);
323+
ASSERT(remote_order_offset == 0 or remote_order_offset == -1,
324+
"Must be 0 or -1, not " << remote_order_offset);
333325
if (start_time == end_time) {
334326
return {};
335327
}
328+
const int integration_order_offset =
329+
std::max(local_order_offset, remote_order_offset);
336330
const evolution_less<Time> time_less{local_times.front().time_runs_forward()};
337331

338332
LtsCoefficients step_coefficients{};
339333

340334
TimeType small_step_end = end_time;
341335
for (;;) {
342-
const OrderVector<TimeStepId> local_ids =
343-
find_relevant_ids(local_times, small_step_end, local_scheme);
344-
const OrderVector<TimeStepId> remote_ids =
345-
find_relevant_ids(remote_times, small_step_end, remote_scheme);
336+
const OrderVector<TimeStepId> local_ids = find_relevant_ids(
337+
local_times, small_step_end, local_scheme, local_order_offset);
338+
const OrderVector<TimeStepId> remote_ids = find_relevant_ids(
339+
remote_times, small_step_end, remote_scheme, remote_order_offset);
346340

347341
// Check is the there is actually local time-stepping happening at
348342
// this boundary. Only check for the latest small step, before we
349343
// have generated any coefficients.
350-
if (step_coefficients.empty() and small_step_scheme == local_scheme and
351-
small_step_scheme == remote_scheme and local_ids == remote_ids) {
344+
if (step_coefficients.empty() and local_scheme == remote_scheme and
345+
local_ids == remote_ids) {
352346
return lts_coefficients_for_gts(local_ids, start_time, end_time);
353347
}
354348

355-
OrderVector<Time> local_control_times(local_scheme.order);
349+
OrderVector<Time> local_control_times(local_ids.size());
356350
alg::transform(local_ids, local_control_times.begin(), exact_substep_time);
357-
OrderVector<Time> remote_control_times(remote_scheme.order);
351+
OrderVector<Time> remote_control_times(remote_ids.size());
358352
alg::transform(remote_ids, remote_control_times.begin(),
359353
exact_substep_time);
360354

355+
const size_t local_order =
356+
local_ids.size() - static_cast<size_t>(local_order_offset);
357+
const size_t remote_order =
358+
remote_ids.size() - static_cast<size_t>(remote_order_offset);
359+
const size_t integration_order =
360+
std::max(local_order, remote_order) +
361+
static_cast<size_t>(integration_order_offset);
362+
361363
const OrderVector<Time> small_step_times = merge_to_small_steps(
362364
local_control_times, remote_control_times, time_less, local_scheme,
363-
remote_scheme, small_step_scheme);
365+
remote_scheme, integration_order);
364366
const Time current_small_step =
365367
small_step_times[small_step_times.size() -
366-
(small_step_scheme.type == SchemeType::Implicit ? 2
367-
: 1)];
368+
(local_scheme == SchemeType::Implicit or
369+
remote_scheme == SchemeType::Implicit
370+
? 2
371+
: 1)];
368372
ASSERT(not time_less(current_small_step, start_time),
369373
"Reached time " << current_small_step
370374
<< " without hitting start time " << start_time
@@ -459,8 +463,9 @@ GENERATE_INSTANTIATIONS(INSTANTIATE, (MATH_WRAPPER_TYPES))
459463
template LtsCoefficients lts_coefficients( \
460464
const ConstBoundaryHistoryTimes& local_times, \
461465
const ConstBoundaryHistoryTimes& remote_times, const Time& start_time, \
462-
const TIME_TYPE(data) & end_time, const AdamsScheme& local_scheme, \
463-
const AdamsScheme& remote_scheme, const AdamsScheme& small_step_scheme);
466+
const TIME_TYPE(data) & end_time, SchemeType local_scheme, \
467+
SchemeType remote_scheme, int local_order_offset, \
468+
int remote_order_offset);
464469

465470
GENERATE_INSTANTIATIONS(INSTANTIATE, (Time, ApproximateTime))
466471
#undef INSTANTIATE

src/Time/TimeSteppers/AdamsLts.hpp

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,22 @@ void apply_coefficients(gsl::not_null<T*> result,
6969
const LtsCoefficients& coefficients,
7070
const BoundaryHistoryEvaluator<T>& coupling);
7171

72-
/// Type of coefficients used for an AdamsScheme
72+
/// Type of coefficients used for an Adams scheme
7373
enum class SchemeType { Explicit, Implicit };
7474

75-
struct AdamsScheme {
76-
SchemeType type;
77-
size_t order;
78-
};
79-
80-
bool operator==(const AdamsScheme& a, const AdamsScheme& b);
81-
bool operator!=(const AdamsScheme& a, const AdamsScheme& b);
82-
8375
/*!
8476
* Calculate the nonzero terms in an Adams LTS boundary contribution.
8577
*
8678
* The coefficients are generated for a step from \p start_time to \p
87-
* end_time, integrating using the \p small_step_scheme.
88-
* Interpolation from the elements is performed using polynomials
89-
* appropriate for the given \p local_scheme and \p remote_scheme.
79+
* end_time. Interpolation from the elements is performed using
80+
* polynomials appropriate for the given \p local_scheme and \p
81+
* remote_scheme. The orders for the interpolations are taken from
82+
* the \p local_times and \p remote_times, adjusted by \p
83+
* local_order_offset and \p remote_order_offset, which can be either
84+
* 0 or -1. The small-step integration is performed with an implicit
85+
* method if either \p local_scheme or \p remote_scheme is implicit,
86+
* and the order is the larger of the integration orders on the two
87+
* sides, reduced by one if both sides have offset -1.
9088
*
9189
* This function returns the sum of the coefficients for the small
9290
* steps, i.e., the steps between all the times either side updates
@@ -104,10 +102,11 @@ bool operator!=(const AdamsScheme& a, const AdamsScheme& b);
104102
* $n$, $y^L_{n,1}$ and $y^R_{n,1}$ are the predictor values for those
105103
* steps, and other index values count backwards in the history of
106104
* that side. The range of $i$ and choice of the control times
107-
* $t^L_\cdots$ are determined by \p local_scheme, $j$ and
108-
* $t^R_\cdots$ are determined by \p remote_scheme, and the Adams
109-
* coefficients $\tilde{\alpha}_{nq}$ correspond to \p
110-
* small_step_scheme.
105+
* $t^L_\cdots$ are determined by \p local_scheme and the local order,
106+
* $j$ and $t^R_\cdots$ are determined by \p remote_scheme and the
107+
* remote order, and the Adams coefficients $\tilde{\alpha}_{nq}$
108+
* correspond to the larger of the two orders, and are implicit if
109+
* either set of interpolation coefficients is.
111110
*
112111
* When called for dense output, the arguments must represent a
113112
* single small step, i.e., the step cannot cross any of the control
@@ -118,11 +117,9 @@ bool operator!=(const AdamsScheme& a, const AdamsScheme& b);
118117
* control times or `ApproximateTime` for dense output.
119118
*/
120119
template <typename TimeType>
121-
LtsCoefficients lts_coefficients(const ConstBoundaryHistoryTimes& local_times,
122-
const ConstBoundaryHistoryTimes& remote_times,
123-
const Time& start_time,
124-
const TimeType& end_time,
125-
const AdamsScheme& local_scheme,
126-
const AdamsScheme& remote_scheme,
127-
const AdamsScheme& small_step_scheme);
120+
LtsCoefficients lts_coefficients(
121+
const ConstBoundaryHistoryTimes& local_times,
122+
const ConstBoundaryHistoryTimes& remote_times, const Time& start_time,
123+
const TimeType& end_time, SchemeType local_scheme, SchemeType remote_scheme,
124+
int local_order_offset, int remote_order_offset);
128125
} // namespace TimeSteppers::adams_lts

0 commit comments

Comments
 (0)