Flow123d  release_2.2.0-914-gf1a3a4f
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  "Setting of the simulation time. (can be specific to one equation)")
44  .allow_auto_conversion("max_dt")
45  .declare_key("start_time", Double(), Default("0.0"),
46  "Start time of the simulation.")
47  .declare_key("end_time", Double(), Default(MAX_END_TIME_STR),
48  "End time of the simulation. Default value is more then age of universe in seconds.")
49  .declare_key("init_dt", Double(0.0), Default("0.0"),
50  "Initial guess for the time step.\n"
51  "Only useful for equations that use adaptive time stepping."
52  "If set to 0.0, the time step is determined in fully autonomous"
53  " way if the equation supports it.")
54  .declare_key("min_dt", Double(0.0),
55  Default::read_time("Machine precision."),
56  "Soft lower limit for the time step. Equation using adaptive time stepping can not"
57  "suggest smaller time step, but actual time step could be smaller in order to match "
58  "prescribed input or output times.")
59  .declare_key("max_dt", Double(0.0),
60  Default::read_time("Whole time of the simulation if specified, infinity else."),
61  "Hard upper limit for the time step. Actual length of the time step is also limited"
62  "by input and output times.")
63  .close();
64 }
65 
66 
67 
68 TimeStep::TimeStep(double init_time) :
69 index_(0),
70 length_(1.0),
71 end_(init_time)
72 {}
73 
74 
75 
77 index_(0),
78 length_(TimeGovernor::inf_time),
79 end_(-TimeGovernor::inf_time)
80 {}
81 
82 
83 
84 
86 index_(other.index_),
87 length_(other.length_),
88 end_(other.end_)
89 {}
90 
91 
92 
93 TimeStep TimeStep::make_next(double new_length) const
94 {
95  return make_next(new_length, this->end_+new_length);
96 }
97 
98 
99 
100 TimeStep TimeStep::make_next(double new_lenght, double end_time) const
101 {
102  TimeStep ts;
103  ts.index_=this->index_ +1;
104  ts.length_=new_lenght;
105  ts.end_=end_time;
106  return ts;
107 }
108 
109 
110 
111 bool TimeStep::safe_compare(double t1, double t0) const
112 {
113  return t1 >= t0
114  - 16*numeric_limits<double>::epsilon()*(1.0+max(abs(t1),abs(t0)));
115 }
116 
117 
118 
119 ostream& operator<<(ostream& out, const TimeStep& t_step) {
120  out << "time: " << t_step.end() << "step: " << t_step.length() << endl;
121 }
122 
123 
124 
126 {
127  // use new mark type as default
128  if (eq_mark_type == TimeMark::none_type) eq_mark_type = marks().new_mark_type();
129 
130  try {
131 
132  // Get rid of rounding errors.
133  double end_time = input.val<double>("end_time");
134  if (end_time> 0.99*max_end_time) end_time = max_end_time;
135 
136  // set permanent limits
137  init_common(input.val<double>("start_time"),
138  end_time,
139  eq_mark_type);
140  set_permanent_constraint(
141  input.val<double>("min_dt", min_time_step_),
142  input.val<double>("max_dt", max_time_step_)
143  );
144 
145  double init_dt=input.val<double>("init_dt");
146  if (init_dt > 0.0) {
147  // set first time step suggested by user
148  //time_step_=min(init_dt, time_step_);
149  lower_constraint_=init_dt;
150  lower_constraint_message_ = "Initial time step set by user.";
151  upper_constraint_=init_dt;
152  upper_constraint_message_ = "Initial time step set by user.";
153  }
154 
155 
156  } catch(ExcTimeGovernorMessage &exc) {
157  exc << input.ei_address();
158  throw;
159  }
160 
161 }
162 
163 TimeGovernor::TimeGovernor(double init_time, double dt)
164 {
165  init_common( init_time, inf_time, TimeMark::every_type);
166  // fixed time step
167  if (dt < time_step_precision)
168  THROW(ExcTimeGovernorMessage() << EI_Message("Fixed time step smaller then machine precision. \n") );
169 
170  fixed_time_step_=dt;
171  is_time_step_fixed_=true;
172  time_step_changed_=true;
173  end_of_fixed_dt_interval_ = inf_time;
174 
175  lower_constraint_=min_time_step_=dt;
176  lower_constraint_message_ = "Initial time step set by user.";
177  upper_constraint_=max_time_step_=dt;
178  upper_constraint_message_ = "Initial time step set by user.";
179 
180  //time_step_=dt;
181 }
182 
183 
184 // steady time governor constructor
185 TimeGovernor::TimeGovernor(double init_time, TimeMark::Type eq_mark_type)
186 {
187  // use new mark type as default
188  if (eq_mark_type == TimeMark::none_type) eq_mark_type = marks().new_mark_type();
189 
190  init_common(init_time, inf_time, eq_mark_type);
191  steady_ = true;
192 }
193 
194 
195 // common part of constructors
196 void TimeGovernor::init_common(double init_time, double end_time, TimeMark::Type type)
197 {
198 
199  if (init_time < 0.0) {
200  THROW(ExcTimeGovernorMessage()
201  << EI_Message("Start time has to be greater or equal to 0.0\n")
202 
203  );
204  }
205 
206  recent_steps_.set_capacity(size_of_recent_steps_);
207  recent_steps_.push_front(TimeStep(init_time));
208  init_time_=init_time;
209 
210  if (end_time < init_time) {
211  THROW(ExcTimeGovernorMessage() << EI_Message("End time must be greater than start time.\n") );
212  } else {
213  end_time_ = end_time;
214  }
215 
216  //if (dt == 0.0) {
217  // variable time step
218  fixed_time_step_=0.0;
219  is_time_step_fixed_=false;
220  time_step_changed_=true;
221  end_of_fixed_dt_interval_ = init_time_;
222 
223  min_time_step_=lower_constraint_=time_step_precision;
224  lower_constraint_message_ = "Permanent minimal constraing, default, time_step_precision.";
225  max_time_step_=upper_constraint_= end_time - init_time_;
226  upper_constraint_message_ = "Permanent maximal constraint, default, total simulation time.";
227  // choose maximum possible time step
228  //time_step_=max_time_step_;
229  /*} else {
230  // fixed time step
231  if (dt < time_step_lower_bound)
232  THROW(ExcTimeGovernorMessage() << EI_Message("Fixed time step smaller then machine precision. \n") );
233 
234  fixed_time_step_=dt;
235  is_time_step_fixed_=true;
236  time_step_changed_=true;
237  end_of_fixed_dt_interval_ = inf_time;
238 
239  upper_constraint_=max_time_step_=dt;
240  lower_constraint_=min_time_step_=dt;
241  time_step_=dt;
242 
243  }*/
244 
245  eq_mark_type_=type;
246  steady_=false;
247  time_marks_.add( TimeMark(init_time_, equation_fixed_mark_type()) );
248  //if (end_time_ != inf_time)
249  time_marks_.add( TimeMark(end_time_, equation_fixed_mark_type()) );
250 }
251 
252 
253 
254 
255 
256 
257 void TimeGovernor::set_permanent_constraint( double min_dt, double max_dt)
258 {
259  if (min_dt < time_step_precision) {
260  THROW(ExcTimeGovernorMessage() << EI_Message("'min_dt' smaller then machine precision.\n") );
261  }
262  if (max_dt < min_dt) {
263  THROW(ExcTimeGovernorMessage() << EI_Message("'max_dt' smaller then 'min_dt'.\n") );
264  }
265 
266  lower_constraint_ = min_time_step_ = max(min_dt, time_step_precision);
267  lower_constraint_message_ = "Permanent minimal constraint, custom.";
268  upper_constraint_ = max_time_step_ = min(max_dt, end_time_-t());
269  upper_constraint_message_ = "Permanent maximal constraint, custom.";
270 
271 }
272 
273 
274 // int set_constrain - dle nastaveni constraint
275 // interval - constraint - jako v cmp u Qsortu
276 // -1 vetsi nez interval (min_dt,max_dt)
277 // +1 mensi
278 // 0 OK
279 
280 int TimeGovernor::set_upper_constraint (double upper, std::string message)
281 {
282  if (upper_constraint_ < upper) {
283  //do not change upper_constraint_
284  return -1;
285  } else
286  if (lower_constraint_ > upper) {
287  // set upper constraint to the lower constraint
288  upper_constraint_ = lower_constraint_;
289  upper_constraint_message_ = "Forced lower constraint. " + message;
290  return 1;
291  } else {
292  //change upper_constraint_ to upper
293  upper_constraint_ = upper;
294  upper_constraint_message_ = message;
295  return 0;
296  }
297 }
298 
299 
300 
301 int TimeGovernor::set_lower_constraint (double lower, std::string message)
302 {
303  if (upper_constraint_ < lower) {
304  // set lower constraint to the upper constraint
305  lower_constraint_ = upper_constraint_;
306  return -1;
307  } else
308  if (lower_constraint_ > lower) {
309  //do not change lower_constraint_
310  return 1;
311  } else {
312  //change lower_constraint_ to lower
313  lower_constraint_ = lower;
314  lower_constraint_message_ = message;
315  return 0;
316  }
317 }
318 
319 
320 
321 
323  if (steady_) return 0.0;
324  end_of_fixed_dt_interval_=-inf_time; // release previous fixed interval
325  fixed_time_step_ = estimate_dt();
326  is_time_step_fixed_ = true; //flag means fixed step has been set since now
327  return end_of_fixed_dt_interval_ = time_marks_.next(*this, equation_fixed_mark_type())->time();
328 }
329 
330 
331 
332 
333 void TimeGovernor::add_time_marks_grid(double step, TimeMark::Type mark_type) const
334 {
335  if (end_time() == inf_time) {
336  THROW(ExcTimeGovernorMessage()
337  << EI_Message("Missing end time for making output grid required by key 'time_step' of the output stream.\n")
338  );
339  }
340 
341  marks().add_time_marks(init_time_, step, end_time(), mark_type | eq_mark_type_);
342  // always add start time and end time
343  marks().add(TimeMark(init_time_, mark_type | eq_mark_type_));
344  marks().add(TimeMark(end_time(), mark_type | eq_mark_type_));
345 }
346 
347 
349 {
350  TimeMark::Type type = equation_mark_type() | mask;
351  return time_marks_.current(step(), type) != time_marks_.end(type);
352 }
353 
354 
355 
357  if (is_end()) return 0.0;
358 
359  if (this->step().lt(end_of_fixed_dt_interval_)) return fixed_time_step_;
360 
361  // jump to the first future fix time
362  TimeMarks::iterator fix_time_it = time_marks_.next(*this, equation_fixed_mark_type());
363  // compute step to next fix time and apply constraints
364  double full_step = fix_time_it->time() - t();
365 
366  double step_estimate = min(full_step, upper_constraint_);
367  step_estimate = max(step_estimate, lower_constraint_); //these two must be in this order
368 
369  if (step_estimate == inf_time) return step_estimate;
370 
371  // round the time step to have integer number of steps till next fix time
372  // this always selects shorter time step,
373  // but allows time step larger then constraint by a number close to machine epsilon
374  //
375  double n_steps = ceil( full_step / step_estimate - time_step_precision);
376  step_estimate = full_step / n_steps;
377 
378  // try to avoid time_step changes
379  if (n_steps > 1 && abs(step_estimate - step().length()) < time_step_precision) {
380  step_estimate=step().length();
381  }
382 
383  // if the step estimate gets by rounding lower than lower constraint program will not crash
384  // will just output a user warning.
385  if (step_estimate < lower_constraint_) {
386  DebugOut().fmt("Time step estimate is below the lower constraint of time step. The difference is: {:.16f}.\n",
387  lower_constraint_ - step_estimate);
388  }
389 
390  return step_estimate;
391 }
392 
393 
394 
396 {
397  OLD_ASSERT_LE(0.0, t());
398  if (is_end()) return;
399 
400 
401  if (this->step().lt(end_of_fixed_dt_interval_)) {
402  // this is done for fixed step
403  // make tiny correction of time step in order to avoid big rounding errors
404  // tiny correction means that dt_changed 'is NOT changed'
405  if (end_of_fixed_dt_interval_ < inf_time) {
406  fixed_time_step_ = (end_of_fixed_dt_interval_-t()) / round( (end_of_fixed_dt_interval_-t()) / fixed_time_step_ );
407  }
408 
409  recent_steps_.push_front(recent_steps_.front().make_next(fixed_time_step_));
410 
411 
412  //checking whether fixed time step has been changed (by fix_dt_until_mark() method) since last time
413  if (is_time_step_fixed_)
414  {
415  is_time_step_fixed_ = false;
416 
417  //is true unless new fixed_dt is not equal previous time_step
418  time_step_changed_ = (step(-2).length() != step().length());
419  }
420  else
421  time_step_changed_ = false;
422  }
423  else
424  {
425  // this is done if end_of_fixed_dt_interval is not set (means it is equal to -infinity)
426  double dt=estimate_dt();
427  TimeStep step_ = recent_steps_.front().make_next(dt);
428  //DebugOut().fmt("step: {}, end: {}\n", step_.length(), step_.end());
429  recent_steps_.push_front(step_);
430  //DebugOut().fmt("last: {}, new: {}\n",step(-1).length(),step().length());
431  time_step_changed_= (step(-2).length() != step().length());
432  }
433 
434  last_lower_constraint_ = lower_constraint_;
435  last_upper_constraint_ = upper_constraint_;
436  // refreshing the upper_constraint_
437  upper_constraint_ = min(end_time_ - t(), max_time_step_);
438  lower_constraint_ = min_time_step_;
439  lower_constraint_message_ = "Permanent minimal constraint, in next time.";
440  upper_constraint_message_ = "Permanent maximal constraint, in next time.";
441 }
442 
443 
444 double TimeGovernor::reduce_timestep(double factor) {
445  double prior_dt = dt();
446  double new_upper_constraint = factor * dt();
447 
448  // Revert time.
449 // DebugOut().fmt("tg idx: {}\n", recent_steps_.front().index());
450  recent_steps_.pop_front();
451 // DebugOut().fmt("tg idx: {}\n", recent_steps_.back().index());
452  upper_constraint_ = last_upper_constraint_;
453  lower_constraint_ = last_lower_constraint_;
454 
455  // Set constraint.
456  int current_minus_new = set_upper_constraint(new_upper_constraint, "Reduce time step.");
457  if (current_minus_new < 0)
458  // current constraint < reduced dt, should not happen
459  THROW(ExcMessage() << EI_Message("Internal error."));
460 
461  next_time();
462 
463  // Return false if we hit lower time step constraint.
464  return dt() / prior_dt;
465 }
466 
467 
468 
469 const TimeStep &TimeGovernor::step(int index) const {
470  unsigned int back_idx;
471  if (index < 0) {
472  back_idx = static_cast<unsigned int>(-index-1);
473  } else {
474  back_idx = static_cast<unsigned int>(recent_steps_[0].index() - index);
475  }
476  if ( back_idx >= recent_steps_.size())
477  THROW(ExcMissingTimeStep() << EI_Index(index) << EI_BackIndex(back_idx) << EI_HistorySize(recent_steps_.size()));
478 
479  return recent_steps_[back_idx];
480 }
481 
482 
483 
484 
485 void TimeGovernor::view(const char *name) const
486 {
487  static char buffer[1024];
488 #ifdef FLOW123D_DEBUG_MESSAGES
489  MessageOut().fmt(
490  "TG[{}]:{:06d} t:{:10.4f} dt:{:10.6f} dt_int<{:10.6f},{:10.6f}> "
491  "end_time: {} end_fixed_time: {} type: {:#x}\n",
492  name, tlevel(), t(), dt(), lower_constraint_, upper_constraint_,
493  end_time_, end_of_fixed_dt_interval_, eq_mark_type_.bitmap_);
494 
495  MessageOut().fmt("Lower time step constraint [{}]: {} \nUpper time step constraint [{}]: {} \n",
496  lower_constraint_, lower_constraint_message_.c_str(),
497  upper_constraint_, upper_constraint_message_.c_str() );
498 #else
499  MessageOut().fmt(
500  "TG[{}]:{:06d} t:{:10.4f} dt:{:10.6f} dt_int<{:10.6f},{:10.6f}>\n",
501  name, tlevel(), t(), dt(), lower_constraint_, upper_constraint_);
502 
503  //sprintf(buffer, "TG[%s]:%06d t:%10.4f dt:%10.6f dt_int<%10.6f,%10.6f>\n",
504  // name, tlevel(), t(), dt(), lower_constraint_, upper_constraint_ );
505 #endif
506 }
507 
508 
509 
510 
511 ostream& operator<<(ostream& out, const TimeGovernor& tg)
512 {
513  static char buffer[1024];
514  sprintf(buffer, "\n%06d t:%10.4f dt:%10.6f dt_int<%10.6f,%10.6f>\n",
515  tg.tlevel(), tg.t(), tg.dt(), tg.lower_constraint(), tg.upper_constraint());
516  return (out << buffer);
517 }
518 
519 
Iterator over TimeMark objects in TimeMarks object (database of TimeMark objects).
Definition: time_marks.hh:351
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:243
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:95
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:93
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:206
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:115
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:45
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:252
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.