Flow123d  release_1.8.2-1603-g0109a2b
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 
118 {
119  // use new mark type as default
120  if (eq_mark_type == TimeMark::none_type) eq_mark_type = marks().new_mark_type();
121 
122  try {
123  // set permanent limits
124  init_common(input.val<double>("start_time"),
125  input.val<double>("end_time", inf_time),
126  eq_mark_type);
127  set_permanent_constraint(
128  input.val<double>("min_dt", min_time_step_),
129  input.val<double>("max_dt", max_time_step_)
130  );
131 
132  double init_dt=input.val<double>("init_dt");
133  if (init_dt > 0.0) {
134  // set first time step suggested by user
135  //time_step_=min(init_dt, time_step_);
136  lower_constraint_=init_dt;
137  lower_constraint_message_ = "Initial time step set by user.";
138  upper_constraint_=init_dt;
139  upper_constraint_message_ = "Initial time step set by user.";
140  }
141 
142 
143  } catch(ExcTimeGovernorMessage &exc) {
144  exc << input.ei_address();
145  throw;
146  }
147 
148 }
149 
150 TimeGovernor::TimeGovernor(double init_time, double dt)
151 {
152  init_common( init_time, inf_time, TimeMark::none_type);
153  // fixed time step
154  if (dt < time_step_precision)
155  THROW(ExcTimeGovernorMessage() << EI_Message("Fixed time step smaller then machine precision. \n") );
156 
157  fixed_time_step_=dt;
158  is_time_step_fixed_=true;
159  time_step_changed_=true;
160  end_of_fixed_dt_interval_ = inf_time;
161 
162  lower_constraint_=min_time_step_=dt;
163  lower_constraint_message_ = "Initial time step set by user.";
164  upper_constraint_=max_time_step_=dt;
165  upper_constraint_message_ = "Initial time step set by user.";
166 
167  //time_step_=dt;
168 }
169 
170 
171 // steady time governor constructor
172 TimeGovernor::TimeGovernor(double init_time, TimeMark::Type eq_mark_type)
173 {
174  // use new mark type as default
175  if (eq_mark_type == TimeMark::none_type) eq_mark_type = marks().new_mark_type();
176 
177  init_common(init_time, inf_time, eq_mark_type);
178  steady_ = true;
179 }
180 
181 
182 
183 // common part of constructors
184 void TimeGovernor::init_common(double init_time, double end_time, TimeMark::Type type)
185 {
186 
187 
188  if (init_time < 0.0) {
189  THROW(ExcTimeGovernorMessage()
190  << EI_Message("Start time has to be greater or equal to 0.0\n")
191 
192  );
193  }
194 
195  recent_steps_.set_capacity(size_of_recent_steps_);
196  recent_steps_.push_front(TimeStep(init_time));
197  init_time_=init_time;
198 
199  if (end_time < init_time) {
200  THROW(ExcTimeGovernorMessage() << EI_Message("End time must be greater than start time.\n") );
201  } else {
202  end_time_ = end_time;
203  }
204 
205  //if (dt == 0.0) {
206  // variable time step
207  fixed_time_step_=0.0;
208  is_time_step_fixed_=false;
209  time_step_changed_=true;
210  end_of_fixed_dt_interval_ = init_time_;
211 
212  min_time_step_=lower_constraint_=time_step_precision;
213  lower_constraint_message_ = "Permanent minimal constraing, default, time_step_precision.";
214  if (end_time_ == inf_time) {
215  max_time_step_=upper_constraint_=inf_time;
216  } else {
217  max_time_step_=upper_constraint_= end_time - init_time_;
218  }
219  upper_constraint_message_ = "Permanent maximal constraint, default, total simulation time.";
220  // choose maximum possible time step
221  //time_step_=max_time_step_;
222  /*} else {
223  // fixed time step
224  if (dt < time_step_lower_bound)
225  THROW(ExcTimeGovernorMessage() << EI_Message("Fixed time step smaller then machine precision. \n") );
226 
227  fixed_time_step_=dt;
228  is_time_step_fixed_=true;
229  time_step_changed_=true;
230  end_of_fixed_dt_interval_ = inf_time;
231 
232  upper_constraint_=max_time_step_=dt;
233  lower_constraint_=min_time_step_=dt;
234  time_step_=dt;
235 
236  }*/
237 
238  eq_mark_type_=type;
239  steady_=false;
240  time_marks_.add( TimeMark(init_time_, equation_fixed_mark_type()) );
241  if (end_time_ != inf_time)
242  time_marks_.add( TimeMark(end_time_, equation_fixed_mark_type()) );
243 }
244 
245 
246 
247 
248 
249 
250 void TimeGovernor::set_permanent_constraint( double min_dt, double max_dt)
251 {
252  if (min_dt < time_step_precision) {
253  THROW(ExcTimeGovernorMessage() << EI_Message("'min_dt' smaller then machine precision.\n") );
254  }
255  if (max_dt < min_dt) {
256  THROW(ExcTimeGovernorMessage() << EI_Message("'max_dt' smaller then 'min_dt'.\n") );
257  }
258 
259  lower_constraint_ = min_time_step_ = max(min_dt, time_step_precision);
260  lower_constraint_message_ = "Permanent minimal constraint, custom.";
261  upper_constraint_ = max_time_step_ = min(max_dt, end_time_-t());
262  upper_constraint_message_ = "Permanent maximal constraint, custom.";
263 
264 }
265 
266 
267 // int set_constrain - dle nastaveni constraint
268 // interval - constraint - jako v cmp u Qsortu
269 // -1 vetsi nez interval (min_dt,max_dt)
270 // +1 mensi
271 // 0 OK
272 
273 int TimeGovernor::set_upper_constraint (double upper, std::string message)
274 {
275  if (upper_constraint_ < upper) {
276  //do not change upper_constraint_
277  return -1;
278  } else
279  if (lower_constraint_ > upper) {
280  // set upper constraint to the lower constraint
281  upper_constraint_ = lower_constraint_;
282  upper_constraint_message_ = "Forced lower constraint. " + message;
283  return 1;
284  } else {
285  //change upper_constraint_ to upper
286  upper_constraint_ = upper;
287  upper_constraint_message_ = message;
288  return 0;
289  }
290 }
291 
292 
293 
294 int TimeGovernor::set_lower_constraint (double lower, std::string message)
295 {
296  if (upper_constraint_ < lower) {
297  // set lower constraint to the upper constraint
298  lower_constraint_ = upper_constraint_;
299  return -1;
300  } else
301  if (lower_constraint_ > lower) {
302  //do not change lower_constraint_
303  return 1;
304  } else {
305  //change lower_constraint_ to lower
306  lower_constraint_ = lower;
307  lower_constraint_message_ = message;
308  return 0;
309  }
310 }
311 
312 
313 
314 
316  if (steady_) return 0.0;
317  end_of_fixed_dt_interval_=-inf_time; // release previous fixed interval
318  fixed_time_step_ = estimate_dt();
319  is_time_step_fixed_ = true; //flag means fixed step has been set since now
320  return end_of_fixed_dt_interval_ = time_marks_.next(*this, equation_fixed_mark_type())->time();
321 }
322 
323 
324 
325 
326 void TimeGovernor::add_time_marks_grid(double step, TimeMark::Type mark_type) const
327 {
328  if (end_time() == inf_time) {
329  THROW(ExcTimeGovernorMessage()
330  << EI_Message("Missing end time for making output grid required by key 'time_step' of the output stream.\n")
331  );
332  }
333 
334  marks().add_time_marks(init_time_, step, end_time(), mark_type | eq_mark_type_);
335  // always add start time and end time
336  marks().add(TimeMark(init_time_, mark_type | eq_mark_type_));
337  marks().add(TimeMark(end_time(), mark_type | eq_mark_type_));
338 }
339 
340 
341 
343  if (is_end()) return 0.0;
344 
345  if (this->step().lt(end_of_fixed_dt_interval_)) return fixed_time_step_;
346 
347  // jump to the first future fix time
348  TimeMarks::iterator fix_time_it = time_marks_.next(*this, equation_fixed_mark_type());
349  // compute step to next fix time and apply constraints
350  double full_step = fix_time_it->time() - t();
351 
352  double step_estimate = min(full_step, upper_constraint_);
353  step_estimate = max(step_estimate, lower_constraint_); //these two must be in this order
354 
355  if (step_estimate == inf_time) return step_estimate;
356 
357  // round the time step to have integer number of steps till next fix time
358  // this always selects shorter time step,
359  // but allows time step larger then constraint by a number close to machine epsilon
360  //
361  //DBGMSG("%g %g %g\n",full_step , step_estimate, full_step / step_estimate);
362  double n_steps = ceil( full_step / step_estimate - time_step_precision);
363  step_estimate = full_step / n_steps;
364 
365  // try to avoid time_step changes
366  if (n_steps > 1 && abs(step_estimate - step().length()) < time_step_precision) {
367  step_estimate=step().length();
368  }
369 
370  // if the step estimate gets by rounding lower than lower constraint program will not crash
371  // will just output a user warning.
372  if (step_estimate < lower_constraint_)
373  xprintf(Warn, "Time step estimate is below the lower constraint of time step. The difference is: %.16f.\n",
374  lower_constraint_ - step_estimate);
375 
376  return step_estimate;
377 }
378 
379 
380 
382 {
383  OLD_ASSERT_LE(0.0, t());
384  if (is_end()) return;
385 
386 
387  if (this->step().lt(end_of_fixed_dt_interval_)) {
388  // this is done for fixed step
389  // make tiny correction of time step in order to avoid big rounding errors
390  // tiny correction means that dt_changed 'is NOT changed'
391  if (end_of_fixed_dt_interval_ < inf_time) {
392  fixed_time_step_ = (end_of_fixed_dt_interval_-t()) / round( (end_of_fixed_dt_interval_-t()) / fixed_time_step_ );
393  }
394 
395  recent_steps_.push_front(recent_steps_.front().make_next(fixed_time_step_));
396 
397 
398  //checking whether fixed time step has been changed (by fix_dt_until_mark() method) since last time
399  if (is_time_step_fixed_)
400  {
401  is_time_step_fixed_ = false;
402 
403  //is true unless new fixed_dt is not equal previous time_step
404  time_step_changed_ = (step(-2).length() != step().length());
405  }
406  else
407  time_step_changed_ = false;
408  }
409  else
410  {
411  // this is done if end_of_fixed_dt_interval is not set (means it is equal to -infinity)
412  double dt=estimate_dt();
413  TimeStep step_ = recent_steps_.front().make_next(dt);
414  //DBGMSG("step: %f, end: %f\n", step_.length(), step_.end());
415  recent_steps_.push_front(step_);
416  //DBGMSG("last:%f, new: %f\n",step(-1).length(),step().length());
417  time_step_changed_= (step(-2).length() != step().length());
418  }
419 
420  last_lower_constraint_ = lower_constraint_;
421  last_upper_constraint_ = upper_constraint_;
422  // refreshing the upper_constraint_
423  upper_constraint_ = min(end_time_ - t(), max_time_step_);
424  lower_constraint_ = min_time_step_;
425  lower_constraint_message_ = "Permanent minimal constraint, in next time.";
426  upper_constraint_message_ = "Permanent maximal constraint, in next time.";
427 }
428 
429 
430 double TimeGovernor::reduce_timestep(double factor) {
431  double prior_dt = dt();
432  double new_upper_constraint = factor * dt();
433 
434  // Revert time.
435 // DBGMSG("tg idx: %d\n", recent_steps_.front().index());
436  recent_steps_.pop_front();
437 // DBGMSG("tg idx: %d\n", recent_steps_.back().index());
438  upper_constraint_ = last_upper_constraint_;
439  lower_constraint_ = last_lower_constraint_;
440 
441  // Set constraint.
442  int current_minus_new = set_upper_constraint(new_upper_constraint, "Reduce time step.");
443  if (current_minus_new < 0)
444  // current constraint < reduced dt, should not happen
445  THROW(ExcMessage() << EI_Message("Internal error."));
446 
447  next_time();
448 
449  // Return false if we hit lower time step constraint.
450  return dt() / prior_dt;
451 }
452 
453 
454 
455 const TimeStep &TimeGovernor::step(int index) const {
456  unsigned int back_idx;
457  if (index < 0) {
458  back_idx = static_cast<unsigned int>(-index-1);
459  } else {
460  back_idx = static_cast<unsigned int>(recent_steps_[0].index() - index);
461  }
462  if ( back_idx >= recent_steps_.size())
463  THROW(ExcMissingTimeStep() << EI_Index(index) << EI_BackIndex(back_idx) << EI_HistorySize(recent_steps_.size()));
464 
465  return recent_steps_[back_idx];
466 }
467 
468 
469 
470 
471 void TimeGovernor::view(const char *name) const
472 {
473  xprintf(Msg, "\nTG[%s]:%06d t:%10.4f dt:%10.6f dt_int<%10.6f,%10.6f>",
474  name, tlevel(), t(), dt(), lower_constraint_, upper_constraint_ );
475 #ifdef FLOW123D_DEBUG_MESSAGES
476  xprintf(Msg, " end_time: %f end_fixed_time: %f type: 0x%x\n" , end_time_, end_of_fixed_dt_interval_, eq_mark_type_);
477 #else
478  xprintf(Msg,"\n");
479 #endif
480  xprintf(Msg, "Lower time step constraint [%f]: %s \nUpper time step constraint [%f]: %s \n",
481  lower_constraint_, lower_constraint_message_.c_str(),
482  upper_constraint_, upper_constraint_message_.c_str() );
483 }
484 
485 
486 
487 
488 ostream& operator<<(ostream& out, const TimeGovernor& tg)
489 {
490  static char buffer[1024];
491  sprintf(buffer, "\n%06d t:%10.4f dt:%10.6f dt_int<%10.6f,%10.6f>\n",
492  tg.tlevel(), tg.t(), tg.dt(), tg.lower_constraint(), tg.upper_constraint());
493  return (out << buffer);
494 }
495 
496 
unsigned long int Type
Definition: time_marks.hh:51
Iterator over TimeMark objects in TimeMarks object (database of TimeMark objects).
Definition: time_marks.hh:281
double reduce_timestep(double factor)
Force timestep reduction in particular in the case of failure of the non-linear solver.
Definition: system.hh:59
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.
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.
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
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:193
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:130
Class for declaration of the input data that are floating point numbers.
Definition: type_base.hh:513
Definition: system.hh:59
void add_time_marks_grid(double step, TimeMark::Type mark_type=TimeMark::none_type) const
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
#define xprintf(...)
Definition: system.hh:87
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:459
double lower_constraint() const
const Record & close() const
Close the Record for further declarations of keys.
Definition: type_record.cc:271
unsigned int index_
Index of the step is index if the end time. Zero time step is artificial.
ostream & operator<<(ostream &out, const TimeGovernor &tg)
Stream output operator for TimeGovernor.
This class is a collection of time marks to manage various events occurring during simulation time...
Definition: time_marks.hh:157
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()
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:132
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:44
Representation of one time step..
double end_
End time point of the time step.