Line data Source code
1 : //* This file is part of the MOOSE framework
2 : //* https://mooseframework.inl.gov
3 : //*
4 : //* All rights reserved, see COPYRIGHT for full restrictions
5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 : //*
7 : //* Licensed under LGPL 2.1, please see LICENSE for details
8 : //* https://www.gnu.org/licenses/lgpl-2.1.html
9 :
10 : #include "TimeSequenceStepperBase.h"
11 : #include "FEProblem.h"
12 : #include "Transient.h"
13 :
14 : #include <algorithm>
15 : #include <functional>
16 :
17 : InputParameters
18 16245 : TimeSequenceStepperBase::validParams()
19 : {
20 16245 : InputParameters params = TimeStepper::validParams();
21 48735 : params.addParam<bool>(
22 : "use_last_dt_after_last_t",
23 32490 : false,
24 : "If true, uses the final time step size for times after the last time in the sequence, "
25 : "instead of taking a single step directly to the simulation end time");
26 32490 : params.addParam<bool>(
27 32490 : "use_last_t_for_end_time", false, "Use last time in sequence as 'end_time' in Executioner.");
28 16245 : return params;
29 0 : }
30 :
31 394 : TimeSequenceStepperBase::TimeSequenceStepperBase(const InputParameters & parameters)
32 : : TimeStepper(parameters),
33 394 : _use_last_dt_after_last_t(getParam<bool>("use_last_dt_after_last_t")),
34 788 : _current_step(declareRestartableData<unsigned int>("current_step", 0)),
35 788 : _time_sequence(declareRestartableData<std::vector<Real>>("time_sequence")),
36 1182 : _set_end_time(getParam<bool>("use_last_t_for_end_time"))
37 : {
38 394 : }
39 :
40 : void
41 394 : TimeSequenceStepperBase::setupSequence(const std::vector<Real> & times)
42 : {
43 : // In case of half transient, transient's end time needs to be reset to
44 : // be able to imprint TimeSequenceStepperBase's end time
45 394 : if (_app.testCheckpointHalfTransient())
46 27 : _executioner.endTime() = _executioner.endTime() * 2.0 - _executioner.getStartTime();
47 :
48 : // When restarting or recovering, we reload _time_sequence as restartable data
49 394 : if (_time_sequence.empty() || (!_app.isRestarting() && !_app.isRecovering()))
50 331 : updateSequence(times);
51 63 : else if (_app.isRecovering())
52 : mooseAssert(_current_step < _time_sequence.size() &&
53 : _time_sequence[_current_step] - _time <= _timestep_tolerance &&
54 : (_current_step + 1 == _time_sequence.size() ||
55 : _time_sequence[_current_step + 1] - _time > _timestep_tolerance),
56 : "The recovered current step must identify the last reached sequence time");
57 : else
58 : {
59 36 : if (!MooseUtils::absoluteFuzzyEqual(_executioner.getStartTime(), _time_sequence[0]))
60 0 : mooseError("Timesequencestepper does not allow the start time to be modified.");
61 :
62 36 : auto current_input_sequence = buildSequence(times);
63 : // Count the leading sequence entries already reached at the current time. Entries no greater
64 : // than _time + _timestep_tolerance are complete, so this count is also the index of the first
65 : // future entry, or sequence.size() if every entry is complete.
66 72 : const auto completed_prefix_size = [this](const auto & sequence)
67 : {
68 72 : return std::distance(sequence.begin(),
69 : std::find_if(sequence.begin(),
70 : sequence.end(),
71 268 : [this](const auto sequence_time)
72 340 : { return sequence_time - _time > _timestep_tolerance; }));
73 36 : };
74 :
75 36 : const auto saved_prefix_size = completed_prefix_size(_time_sequence);
76 36 : const auto current_prefix_size = completed_prefix_size(current_input_sequence);
77 36 : if (current_prefix_size != saved_prefix_size)
78 6 : mooseError("The timesequence provided in the restart file must be identical to "
79 : "the one in the old file through the restart time, but it contains ",
80 : current_prefix_size,
81 : " completed value(s) instead of ",
82 : saved_prefix_size,
83 : ".");
84 :
85 110 : for (const auto j : make_range(saved_prefix_size))
86 80 : if (!MooseUtils::absoluteFuzzyEqual(current_input_sequence[j], _time_sequence[j]))
87 0 : mooseError("The timesequence provided in the restart file must be identical to "
88 : "the one in the old file through the restart time, but entry ",
89 0 : j + 1,
90 : " is ",
91 0 : current_input_sequence[j],
92 : " in the restart input and ",
93 0 : _time_sequence[j],
94 : " in the restarted input.");
95 :
96 30 : _time_sequence = std::move(current_input_sequence);
97 30 : synchronizeCurrentStep(_time, _timestep_tolerance);
98 30 : }
99 :
100 : // Set end time to last time in sequence if requested
101 382 : if (_set_end_time)
102 : {
103 24 : auto & end_time = _executioner.endTime();
104 24 : end_time = _time_sequence.back();
105 : }
106 :
107 382 : if (_app.testCheckpointHalfTransient())
108 : {
109 27 : unsigned int half = (_time_sequence.size() - 1) / 2;
110 27 : _executioner.endTime() = _time_sequence[half];
111 : }
112 382 : }
113 :
114 : std::vector<Real>
115 609 : TimeSequenceStepperBase::buildSequence(const std::vector<Real> & times) const
116 : {
117 609 : const Real start_time = _executioner.getStartTime();
118 609 : const Real end_time = _executioner.endTime();
119 :
120 : // make sure time sequence is in strictly ascending order
121 609 : if (!std::is_sorted(times.begin(), times.end(), std::less_equal<Real>()))
122 12 : paramError("time_sequence", "Time points must be in strictly ascending order.");
123 :
124 1206 : std::vector<Real> sequence{start_time};
125 3338 : for (const auto time : times)
126 2735 : if (time > start_time && time <= end_time)
127 2417 : sequence.push_back(time);
128 :
129 : // Always append end_time as a sentinel, even when it duplicates the last supplied time.
130 603 : if (!_set_end_time)
131 581 : sequence.push_back(end_time);
132 :
133 1206 : return sequence;
134 0 : }
135 :
136 : void
137 573 : TimeSequenceStepperBase::updateSequence(const std::vector<Real> & times)
138 : {
139 573 : _time_sequence = buildSequence(times);
140 567 : synchronizeCurrentStep(_time, _timestep_tolerance);
141 567 : }
142 :
143 : void
144 0 : TimeSequenceStepperBase::resetSequence()
145 : {
146 0 : _time_sequence.clear();
147 0 : }
148 :
149 : bool
150 1463 : TimeSequenceStepperBase::advanceToFutureTime(Real time, Real tolerance, Real & next_time)
151 : {
152 1463 : refreshSequence();
153 1463 : const auto first_future = findFirstFutureTime(time, tolerance);
154 1463 : if (first_future == _time_sequence.cend())
155 0 : return false;
156 :
157 1463 : next_time = *first_future;
158 1463 : return true;
159 : }
160 :
161 : std::vector<Real>::const_iterator
162 2060 : TimeSequenceStepperBase::findFirstFutureTime(Real time, Real tolerance) const
163 : {
164 2060 : return std::partition_point(_time_sequence.begin(),
165 2060 : _time_sequence.end(),
166 6099 : [time, tolerance](const auto sequence_time)
167 8159 : { return sequence_time - time <= tolerance; });
168 : }
169 :
170 : void
171 597 : TimeSequenceStepperBase::synchronizeCurrentStep(Real time, Real tolerance)
172 : {
173 597 : const auto first_future = findFirstFutureTime(time, tolerance);
174 597 : _current_step =
175 597 : first_future == _time_sequence.cbegin()
176 597 : ? 0
177 1194 : : static_cast<unsigned int>(std::distance(_time_sequence.cbegin(), first_future) - 1);
178 597 : }
179 :
180 : Real
181 0 : TimeSequenceStepperBase::getNextTimeInSequence()
182 : {
183 0 : refreshSequence();
184 : mooseAssert(_current_step + 1 < _time_sequence.size(),
185 : "The time sequence must contain a future time");
186 0 : return _time_sequence[_current_step + 1];
187 : }
188 :
189 : void
190 2155 : TimeSequenceStepperBase::acceptStep()
191 : {
192 2155 : TimeStepper::acceptStep();
193 2155 : refreshSequence();
194 6938 : while (_current_step + 1 < _time_sequence.size() &&
195 3339 : _time_sequence[_current_step + 1] - _time <= _timestep_tolerance)
196 1444 : increaseCurrentStep();
197 2155 : }
198 :
199 : Real
200 432 : TimeSequenceStepperBase::computeInitialDT()
201 : {
202 432 : return computeDT();
203 : }
204 :
205 : Real
206 1047 : TimeSequenceStepperBase::computeDT()
207 : {
208 1047 : refreshSequence();
209 : mooseAssert(_current_step + 1 < _time_sequence.size(),
210 : "The time sequence must contain a future time");
211 1047 : const auto next_time = _time_sequence[_current_step + 1];
212 :
213 1047 : if (_use_last_dt_after_last_t)
214 : {
215 : // last *provided* time value index; actual last index corresponds to end time
216 77 : const auto last_t_index = _time_sequence.size() - 2;
217 77 : if (_current_step + 1 > last_t_index)
218 22 : return _time_sequence[last_t_index] - _time_sequence[last_t_index - 1];
219 : }
220 :
221 1025 : return next_time - _time;
222 : }
|