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