Flow123d  release_2.2.1-10-gb9fad4d
time_governor.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file time_governor.cc
15  * @ingroup application
16  * @brief Basic time management class.
17  */
18 
19 #include <limits>
20 #include "system/system.hh"
21 #include "input/accessors.hh"
22 #include "time_governor.hh"
23 #include "time_marks.hh"
24 
25 //initialize constant pointer to TimeMarks object
27 
28 //const double TimeGovernor::time_step_lower_bound = numeric_limits<double>::epsilon();
29 
30 #define MAX_END_TIME 5.0e+17
31 #define MAX_END_TIME_STR "5.0e+17"
32 
33 const double TimeGovernor::inf_time = numeric_limits<double>::infinity();
34 const double TimeGovernor::max_end_time = MAX_END_TIME; // more then age of universe in seconds.
36 
37 
38 using namespace Input::Type;
39 
40 
42  return Record("TimeGovernor",
43  "Time axis settings of the simulation.\n"
44  "The settings is specific to a particular equation.")
45  .allow_auto_conversion("max_dt")
46  .declare_key("start_time", Double(), Default("0.0"),
47  "Start time of the simulation.")
48  .declare_key("end_time", Double(), Default(MAX_END_TIME_STR),
49  "End time of the simulation.\n"
50  "The default value is higher than the age of the Universe (given in seconds).")
51  .declare_key("init_dt", Double(0.0), Default("0.0"),
52  "Initial guess for the time step.\n"
53  "It applies to equations that use an adaptive time stepping."
54  "If set to 0.0, the time step is determined in fully autonomous"
55  " way, assuming the equation supports it.")
56  .declare_key("min_dt", Double(0.0),
57  Default::read_time("Machine precision."),
58  "Soft lower limit for the time step.\n"
59  "Equation using an adaptive time stepping cannot suggest smaller time step."
60  "The actual time step can only decrease below the limit in order to match "
61  "the prescribed input or output times.")
62  .declare_key("max_dt", Double(0.0),
63  Default::read_time("Whole time of the simulation if specified, infinity else."),
64  "Hard upper limit for the time step.\n"
65  "The actual time step can only increase above the limit in order to match "
66  "the prescribed input or output times.")
67  .close();
68 }
69 
70 
71 
72 TimeStep::TimeStep(double init_time) :
73 index_(0),
74 length_(1.0),
75 end_(init_time)
76 {}
77 
78 
79 
81 index_(0),
82 length_(TimeGovernor::inf_time),
83 end_(-TimeGovernor::inf_time)
84 {}
85 
86 
87 
88 
90 index_(other.index_),
91 length_(other.length_),
92 end_(other.end_)
93 {}
94 
95 
96 
97 TimeStep TimeStep::make_next(double new_length) const
98 {
99  return make_next(new_length, this->end_+new_length);
100 }
101 
102 
103 
104 TimeStep TimeStep::make_next(double new_lenght, double end_time) const
105 {
106  TimeStep ts;
107  ts.index_=this->index_ +1;
108  ts.length_=new_lenght;
109  ts.end_=end_time;
110  return ts;
111 }
112 
113 
114 
115 bool TimeStep::safe_compare(double t1, double t0) const
116 {
117  return t1 >= t0
118  - 16*numeric_limits<double>::epsilon()*(1.0+max(abs(t1),abs(t0)));
119 }
120 
121 
122 
123 ostream& operator<<(ostream& out, const TimeStep& t_step) {
124  out << "time: " << t_step.end() << "step: " << t_step.length() << endl;
125 }
126 
127 
128 
130 {
131  // use new mark type as default
132  if (eq_mark_type == TimeMark::none_type) eq_mark_type = marks().new_mark_type();
133 
134  try {
135 
136  // Get rid of rounding errors.
137  double end_time = input.val<double>("end_time");
138  if (end_time> 0.99*max_end_time) end_time = max_end_time;
139 
140  // set permanent limits
141  init_common(input.val<double>("start_time"),
142  end_time,
143  eq_mark_type);
144  set_permanent_constraint(
145  input.val<double>("min_dt", min_time_step_),
146  input.val<double>("max_dt", max_time_step_)
147  );
148 
149  double init_dt=input.val<double>("init_dt");
150  if (init_dt > 0.0) {
151  // set first time step suggested by user
152  //time_step_=min(init_dt, time_step_);
153  lower_constraint_=init_dt;
154  lower_constraint_message_ = "Initial time step set by user.";
155  upper_constraint_=init_dt;
156  upper_constraint_message_ = "Initial time step set by user.";
157  }
158 
159 
160  } catch(ExcTimeGovernorMessage &exc) {
161  exc << input.ei_address();
162  throw;
163  }
164 
165 }
166 
167 TimeGovernor::TimeGovernor(double init_time, double dt)
168 {
169  init_common( init_time, inf_time, TimeMark::every_type);
170  // fixed time step
171  if (dt < time_step_precision)
172  THROW(ExcTimeGovernorMessage() << EI_Message("Fixed time step smaller then machine precision. \n") );
173 
174  fixed_time_step_=dt;
175  is_time_step_fixed_=true;
176  time_step_changed_=true;
177  end_of_fixed_dt_interval_ = inf_time;
178 
179  lower_constraint_=min_time_step_=dt;
180  lower_constraint_message_ = "Initial time step set by user.";
181  upper_constraint_=max_time_step_=dt;
182  upper_constraint_message_ = "Initial time step set by user.";
183 
184  //time_step_=dt;
185 }
186 
187 
188 // steady time governor constructor
189 TimeGovernor::TimeGovernor(double init_time, TimeMark::Type eq_mark_type)
190 {
191  // use new mark type as default
192  if (eq_mark_type == TimeMark::none_type) eq_mark_type = marks().new_mark_type();
193 
194  init_common(init_time, inf_time, eq_mark_type);
195  steady_ = true;
196 }
197 
198 
199 // common part of constructors
200 void TimeGovernor::init_common(double init_time, double end_time, TimeMark::Type type)
201 {
202 
203  if (init_time < 0.0) {
204  THROW(ExcTimeGovernorMessage()
205  << EI_Message("Start time has to be greater or equal to 0.0\n")
206 
207  );
208  }
209 
210  recent_steps_.set_capacity(size_of_recent_steps_);
211  recent_steps_.push_front(TimeStep(init_time));
212  init_time_=init_time;
213 
214  if (end_time < init_time) {
215  THROW(ExcTimeGovernorMessage() << EI_Message("End time must be greater than start time.\n") );
216  } else {
217  end_time_ = end_time;
218  }
219 
220  //if (dt == 0.0) {
221  // variable time step
222  fixed_time_step_=0.0;
223  is_time_step_fixed_=false;
224  time_step_changed_=true;
225  end_of_fixed_dt_interval_ = init_time_;
226 
227  min_time_step_=lower_constraint_=time_step_precision;
228  lower_constraint_message_ = "Permanent minimal constraing, default, time_step_precision.";
229  max_time_step_=upper_constraint_= end_time - init_time_;
230  upper_constraint_message_ = "Permanent maximal constraint, default, total simulation time.";
231  // choose maximum possible time step
232  //time_step_=max_time_step_;
233  /*} else {
234  // fixed time step
235  if (dt < time_step_lower_bound)
236  THROW(ExcTimeGovernorMessage() << EI_Message("Fixed time step smaller then machine precision. \n") );
237 
238  fixed_time_step_=dt;
239  is_time_step_fixed_=true;
240  time_step_changed_=true;
241  end_of_fixed_dt_interval_ = inf_time;
242 
243  upper_constraint_=max_time_step_=dt;
244  lower_constraint_=min_time_step_=dt;
245  time_step_=dt;
246 
247  }*/
248 
249  eq_mark_type_=type;
250  steady_=false;
251  time_marks_.add( TimeMark(init_time_, equation_fixed_mark_type()) );
252  //if (end_time_ != inf_time)
253  time_marks_.add( TimeMark(end_time_, equation_fixed_mark_type()) );
254 }
255 
256 
257 
258 
259 
260 
261 void TimeGovernor::set_permanent_constraint( double min_dt, double max_dt)
262 {
263  if (min_dt < time_step_precision) {
264  THROW(ExcTimeGovernorMessage() << EI_Message("'min_dt' smaller then machine precision.\n") );
265  }
266  if (max_dt < min_dt) {
267  THROW(ExcTimeGovernorMessage() << EI_Message("'max_dt' smaller then 'min_dt'.\n") );
268  }
269 
270  lower_constraint_ = min_time_step_ = max(min_dt, time_step_precision);
271  lower_constraint_message_ = "Permanent minimal constraint, custom.";
272  upper_constraint_ = max_time_step_ = min(max_dt, end_time_-t());
273  upper_constraint_message_ = "Permanent maximal constraint, custom.";
274 
275 }
276 
277 
278 // int set_constrain - dle nastaveni constraint
279 // interval - constraint - jako v cmp u Qsortu
280 // -1 vetsi nez interval (min_dt,max_dt)
281 // +1 mensi
282 // 0 OK
283 
284 int TimeGovernor::set_upper_constraint (double upper, std::string message)
285 {
286  if (upper_constraint_ < upper) {
287  //do not change upper_constraint_
288  return -1;
289  } else
290  if (lower_constraint_ > upper) {
291  // set upper constraint to the lower constraint
292  upper_constraint_ = lower_constraint_;
293  upper_constraint_message_ = "Forced lower constraint. " + message;
294  return 1;
295  } else {
296  //change upper_constraint_ to upper
297  upper_constraint_ = upper;
298  upper_constraint_message_ = message;
299  return 0;
300  }
301 }
302 
303 
304 
305 int TimeGovernor::set_lower_constraint (double lower, std::string message)
306 {
307  if (upper_constraint_ < lower) {
308  // set lower constraint to the upper constraint
309  lower_constraint_ = upper_constraint_;
310  return -1;
311  } else
312  if (lower_constraint_ > lower) {
313  //do not change lower_constraint_
314  return 1;
315  } else {
316  //change lower_constraint_ to lower
317  lower_constraint_ = lower;
318  lower_constraint_message_ = message;
319  return 0;
320  }
321 }
322 
323 
324 
325 
327  if (steady_) return 0.0;
328  end_of_fixed_dt_interval_=-inf_time; // release previous fixed interval
329  fixed_time_step_ = estimate_dt();
330  is_time_step_fixed_ = true; //flag means fixed step has been set since now
331  return end_of_fixed_dt_interval_ = time_marks_.next(*this, equation_fixed_mark_type())->time();
332 }
333 
334 
335 
336 
337 void TimeGovernor::add_time_marks_grid(double step, TimeMark::Type mark_type) const
338 {
339  if (end_time() == inf_time) {
340  THROW(ExcTimeGovernorMessage()
341  << EI_Message("Missing end time for making output grid required by key 'time_step' of the output stream.\n")
342  );
343  }
344 
345  marks().add_time_marks(init_time_, step, end_time(), mark_type | eq_mark_type_);
346  // always add start time and end time
347  marks().add(TimeMark(init_time_, mark_type | eq_mark_type_));
348  marks().add(TimeMark(end_time(), mark_type | eq_mark_type_));
349 }
350 
351 
353 {
354  TimeMark::Type type = equation_mark_type() | mask;
355  return time_marks_.current(step(), type) != time_marks_.end(type);
356 }
357 
358 
359 
361  if (is_end()) return 0.0;
362 
363  if (this->step().lt(end_of_fixed_dt_interval_)) return fixed_time_step_;
364 
365  // jump to the first future fix time
366  TimeMarks::iterator fix_time_it = time_marks_.next(*this, equation_fixed_mark_type());
367  // compute step to next fix time and apply constraints
368  double full_step = fix_time_it->time() - t();
369 
370  double step_estimate = min(full_step, upper_constraint_);
371  step_estimate = max(step_estimate, lower_constraint_); //these two must be in this order
372 
373  if (step_estimate == inf_time) return step_estimate;
374 
375  // round the time step to have integer number of steps till next fix time
376  // this always selects shorter time step,
377  // but allows time step larger then constraint by a number close to machine epsilon
378  //
379  double n_steps = ceil( full_step / step_estimate - time_step_precision);
380  step_estimate = full_step / n_steps;
381 
382  // try to avoid time_step changes
383  if (n_steps > 1 && abs(step_estimate - step().length()) < time_step_precision) {
384  step_estimate=step().length();
385  }
386 
387  // if the step estimate gets by rounding lower than lower constraint program will not crash
388  // will just output a user warning.
389  if (step_estimate < lower_constraint_) {
390  DebugOut().fmt("Time step estimate is below the lower constraint of time step. The difference is: {:.16f}.\n",
391  lower_constraint_ - step_estimate);
392  }
393 
394  return step_estimate;
395 }
396 
397 
398 
400 {
401  OLD_ASSERT_LE(0.0, t());
402  if (is_end()) return;
403 
404 
405  if (this->step().lt(end_of_fixed_dt_interval_)) {
406  // this is done for fixed step
407  // make tiny correction of time step in order to avoid big rounding errors
408  // tiny correction means that dt_changed 'is NOT changed'
409  if (end_of_fixed_dt_interval_ < inf_time) {
410  fixed_time_step_ = (end_of_fixed_dt_interval_-t()) / round( (end_of_fixed_dt_interval_-t()) / fixed_time_step_ );
411  }
412 
413  recent_steps_.push_front(recent_steps_.front().make_next(fixed_time_step_));
414 
415 
416  //checking whether fixed time step has been changed (by fix_dt_until_mark() method) since last time
417  if (is_time_step_fixed_)
418  {
419  is_time_step_fixed_ = false;
420 
421  //is true unless new fixed_dt is not equal previous time_step
422  time_step_changed_ = (step(-2).length() != step().length());
423  }
424  else
425  time_step_changed_ = false;
426  }
427  else
428  {
429  // this is done if end_of_fixed_dt_interval is not set (means it is equal to -infinity)
430  double dt=estimate_dt();
431  TimeStep step_ = recent_steps_.front().make_next(dt);
432  //DebugOut().fmt("step: {}, end: {}\n", step_.length(), step_.end());
433  recent_steps_.push_front(step_);
434  //DebugOut().fmt("last: {}, new: {}\n",step(-1).length(),step().length());
435  time_step_changed_= (step(-2).length() != step().length());
436  }
437 
438  last_lower_constraint_ = lower_constraint_;
439  last_upper_constraint_ = upper_constraint_;
440  // refreshing the upper_constraint_
441  upper_constraint_ = min(end_time_ - t(), max_time_step_);
442  lower_constraint_ = min_time_step_;
443  lower_constraint_message_ = "Permanent minimal constraint, in next time.";
444  upper_constraint_message_ = "Permanent maximal constraint, in next time.";
445 }
446 
447 
448 double TimeGovernor::reduce_timestep(double factor) {
449  double prior_dt = dt();
450  double new_upper_constraint = factor * dt();
451 
452  // Revert time.
453 // DebugOut().fmt("tg idx: {}\n", recent_steps_.front().index());
454  recent_steps_.pop_front();
455 // DebugOut().fmt("tg idx: {}\n", recent_steps_.back().index());
456  upper_constraint_ = last_upper_constraint_;
457  lower_constraint_ = last_lower_constraint_;
458 
459  // Set constraint.
460  int current_minus_new = set_upper_constraint(new_upper_constraint, "Reduce time step.");
461  if (current_minus_new < 0)
462  // current constraint < reduced dt, should not happen
463  THROW(ExcMessage() << EI_Message("Internal error."));
464 
465  next_time();
466 
467  // Return false if we hit lower time step constraint.
468  return dt() / prior_dt;
469 }
470 
471 
472 
473 const TimeStep &TimeGovernor::step(int index) const {
474  unsigned int back_idx;
475  if (index < 0) {
476  back_idx = static_cast<unsigned int>(-index-1);
477  } else {
478  back_idx = static_cast<unsigned int>(recent_steps_[0].index() - index);
479  }
480  if ( back_idx >= recent_steps_.size())
481  THROW(ExcMissingTimeStep() << EI_Index(index) << EI_BackIndex(back_idx) << EI_HistorySize(recent_steps_.size()));
482 
483  return recent_steps_[back_idx];
484 }
485 
486 
487 
488 
489 void TimeGovernor::view(const char *name) const
490 {
491  static char buffer[1024];
492 #ifdef FLOW123D_DEBUG_MESSAGES
493  MessageOut().fmt(
494  "TG[{}]:{:06d} t:{:10.4f} dt:{:10.6f} dt_int<{:10.6f},{:10.6f}> "
495  "end_time: {} end_fixed_time: {} type: {:#x}\n",
496  name, tlevel(), t(), dt(), lower_constraint_, upper_constraint_,
497  end_time_, end_of_fixed_dt_interval_, eq_mark_type_.bitmap_);
498 
499  MessageOut().fmt("Lower time step constraint [{}]: {} \nUpper time step constraint [{}]: {} \n",
500  lower_constraint_, lower_constraint_message_.c_str(),
501  upper_constraint_, upper_constraint_message_.c_str() );
502 #else
503  MessageOut().fmt(
504  "TG[{}]:{:06d} t:{:10.4f} dt:{:10.6f} dt_int<{:10.6f},{:10.6f}>\n",
505  name, tlevel(), t(), dt(), lower_constraint_, upper_constraint_);
506 
507  //sprintf(buffer, "TG[%s]:%06d t:%10.4f dt:%10.6f dt_int<%10.6f,%10.6f>\n",
508  // name, tlevel(), t(), dt(), lower_constraint_, upper_constraint_ );
509 #endif
510 }
511 
512 
513 
514 
515 ostream& operator<<(ostream& out, const TimeGovernor& tg)
516 {
517  static char buffer[1024];
518  sprintf(buffer, "\n%06d t:%10.4f dt:%10.6f dt_int<%10.6f,%10.6f>\n",
519  tg.tlevel(), tg.t(), tg.dt(), tg.lower_constraint(), tg.upper_constraint());
520  return (out << buffer);
521 }
522 
523 
Iterator over TimeMark objects in TimeMarks object (database of TimeMark objects).
Definition: time_marks.hh:342
double reduce_timestep(double factor)
Force timestep reduction in particular in the case of failure of the non-linear solver.
int tlevel() const
static TimeMarks time_marks_
bool lt(double other_time) const
double upper_constraint() const
Class Input::Type::Default specifies default value of keys of a Input::Type::Record.
Definition: type_record.hh:61
TimeGovernor(const Input::Record &input, TimeMark::Type fixed_time_mask=TimeMark::none_type)
Constructor for unsteady solvers.
#define MessageOut()
Macro defining &#39;message&#39; record of log.
Definition: logger.hh:233
double fix_dt_until_mark()
Fixing time step until fixed time mark.
int set_lower_constraint(double lower, std::string message)
Sets lower constraint for the next time step estimating.
void set_permanent_constraint(double min_dt, double max_dt)
Sets permanent constraints for time step.
void next_time()
Proceed to the next time according to current estimated time step.
bool is_current(const TimeMark::Type &mask) const
const TimeStep & step(int index=-1) const
#define MAX_END_TIME_STR
static const double max_end_time
double t() const
Basic time management functionality for unsteady (and steady) solvers (class Equation).
static const Type none_type
Mark Type with all bits unset.
Definition: time_marks.hh:86
Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:303
Basic time management class.
static const Type every_type
Mark Type with all bits set.
Definition: time_marks.hh:84
double estimate_dt() const
Estimate choice of next time step according to actual setting of constraints.
void view(const char *name="") const
EI_Address ei_address() const
Definition: accessors.cc:178
virtual Record & allow_auto_conversion(const string &from_key)
Allows shorter input of the Record providing only value of the from_key given as the parameter...
Definition: type_record.cc:132
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:540
#define MAX_END_TIME
void add_time_marks_grid(double step, TimeMark::Type mark_type=TimeMark::none_type) const
std::string sprintf(CStringRef format, ArgList args)
Definition: printf.h:457
static const double time_step_precision
Accessor to the data with type Type::Record.
Definition: accessors.hh:292
const Ret val(const string &key) const
double length() const
Record & declare_key(const string &key, std::shared_ptr< TypeBase > type, const Default &default_value, const string &description, TypeBase::attribute_map key_attributes=TypeBase::attribute_map())
Declares a new key of the Record.
Definition: type_record.cc:490
double lower_constraint() const
double end() const
unsigned int index_
Index of the step is index if the end time. Zero time step is artificial.
This class is a collection of time marks to manage various events occurring during simulation time...
Definition: time_marks.hh:197
TimeStep make_next(double new_length) const
static Default read_time(const std::string &description)
The factory function to make an default value that will be specified at the time when a key will be r...
Definition: type_record.hh:97
const double epsilon
Definition: mathfce.h:23
int set_upper_constraint(double upper, std::string message)
Sets upper constraint for the next time step estimating.
void init_common(double init_time, double end_time, TimeMark::Type type)
Common part of the constructors. Set most important parameters, check they are valid and set default ...
bool safe_compare(double t1, double t0) const
double dt() const
double time() const
Getter for the time of the TimeMark.
Definition: time_marks.hh:106
static const Input::Type::Record & get_input_type()
unsigned int index() const
Class used for marking specified times at which some events occur.
Definition: time_marks.hh:36
Record type proxy class.
Definition: type_record.hh:182
double length_
#define OLD_ASSERT_LE(a, b)
Definition: global_defs.h:135
ostream & operator<<(ostream &out, const TimeStep &t_step)
#define DebugOut()
Macro defining &#39;debug&#39; record of log.
Definition: logger.hh:242
static const double inf_time
Infinity time used for steady case.
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
Representation of one time step..
double end_
End time point of the time step.