Flow123d
time_governor.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2007 Technical University of Liberec. All rights reserved.
4  *
5  * Please make a following refer to Flow123d on your project site if you use the program for any purpose,
6  * especially for academic research:
7  * Flow123d, Research Centre: Advanced Remedial Technologies, Technical University of Liberec, Czech Republic
8  *
9  * This program is free software; you can redistribute it and/or modify it under the terms
10  * of the GNU General Public License version 3 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along with this program; if not,
17  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 021110-1307, USA.
18  *
19  *
20  * $Id: time_governor.cc 1877 2012-09-27 12:53:36Z jan.brezina $
21  * $Revision: 1877 $
22  * $LastChangedBy: jan.brezina $
23  * $LastChangedDate: 2012-09-27 14:53:36 +0200 (Čt, 27 zář 2012) $
24  *
25  * @file
26  * @ingroup application
27  * @brief Basic time management class.
28  */
29 
30 #include "system/system.hh"
31 #include "time_governor.hh"
32 #include "time_marks.hh"
33 #include "input/accessors.hh"
34 
35 #include <limits>
36 
37 //initialize constant pointer to TimeMarks object
39 
40 // fraction of subsequent time steps can not be less then the comparison_precision
41 const double TimeGovernor::comparison_precision = 0.0001;
43 const double TimeGovernor::inf_time = numeric_limits<double>::infinity();
44 const double TimeGovernor::round_n_steps_precision = 1e-14;
45 
46 
47 
48 using namespace Input::Type;
49 
50 
51 
52 
53 
54 Record TimeGovernor::input_type = Record("TimeGovernor",
55  "Setting of the simulation time. (can be specific to one equation)")
56  .allow_auto_conversion("init_dt")
57  .declare_key("start_time", Double(), Default("0.0"),
58  "Start time of the simulation.")
59  .declare_key("end_time", Double(), Default::optional(),
60  "End time of the simulation.")
61  .declare_key("init_dt", Double(0.0), Default("0.0"),
62  "Initial guess for the time step.\n"
63  "The time step is fixed if the hard time step limits are not set.\n"
64  "If set to 0.0, the time step is determined in fully autonomous way if the equation supports it.")
65  .declare_key("min_dt", Double(0.0), Default::read_time("Machine precision or 'init_dt' if specified"),
66  "Hard lower limit for the time step.")
67  .declare_key("max_dt", Double(0.0), Default::read_time("Whole time of the simulation or 'init_dt' if specified"),
68  "Hard upper limit for the time step.");
69 
70 
71 
72 
73 /*
74  * TODO:
75  * TimeGovernor should be constructed from JSON object.
76  */
77 //TimeGovernor::TimeGovernor(const Input::Record &input, TimeMarks &marks, const TimeMark::Type
79 {
80  // use INF end time as default
81  end_time_ = inf_time;
82  if (input.opt_val<double>("end_time", end_time_))
83 
84  // use new mark type as default
85  if (eq_mark_type == TimeMark::none_type) eq_mark_type = marks().new_mark_type();
86 
87  try {
88  init_common(input.val<double>("init_dt"),
89  input.val<double>("start_time"),
90  end_time_,
91  eq_mark_type);
92 
93  // possibly overwrite limits
94  set_permanent_constraint(
95  input.val<double>("min_dt", min_time_step_),
96  input.val<double>("max_dt", max_time_step_)
97  );
98  } catch(ExcTimeGovernorMessage &exc) {
99  exc << input.ei_address();
100  throw;
101  }
102 
103 }
104 
105 TimeGovernor::TimeGovernor(double init_time, double dt)
106 {
107  init_common(dt, init_time, inf_time, TimeMark::none_type);
108 }
109 
110 
111 // steady time governor constructor
112 TimeGovernor::TimeGovernor(double init_time, TimeMark::Type eq_mark_type)
113 {
114  // use new mark type as default
115  if (eq_mark_type == TimeMark::none_type) eq_mark_type = marks().new_mark_type();
116 
117  init_common(0.0, init_time, inf_time, eq_mark_type);
118  steady_ = true;
119 }
120 
121 
122 
123 // common part of constructors
124 void TimeGovernor::init_common(double dt, double init_time, double end_time, TimeMark::Type type)
125 {
126  time_level_=0;
127 
128  if (init_time < 0.0) {
129  THROW(ExcTimeGovernorMessage()
130  << EI_Message("Start time has to be greater or equal to 0.0\n")
131 
132  );
133  }
134 
135  init_time_ = time_ = init_time;
136  last_time_ = -inf_time;
137  last_time_step_ = inf_time;
138 
139 
140  if (end_time < init_time) {
141  THROW(ExcTimeGovernorMessage() << EI_Message("End time must be greater than start time.\n") );
142  }
143 
144  end_time_ = end_time;
145 
146  if (dt == 0.0) {
147  // variable time step
148  fixed_time_step_=0.0;
149  is_time_step_fixed_=false;
150  time_step_changed_=true;
151  end_of_fixed_dt_interval_ = time_;
152 
153  min_time_step_=lower_constraint_=time_step_lower_bound;
154  if (end_time_ == inf_time) {
155  max_time_step_=upper_constraint_=inf_time;
156  } else {
157  max_time_step_=upper_constraint_= end_time - time_;
158  }
159  // choose maximum possible time step
160  time_step_=max_time_step_;
161  } else {
162  // fixed time step
163  if (dt < time_step_lower_bound)
164  xprintf(UsrErr, "Fixed time step small then machine precision. \n");
165 
166  fixed_time_step_=dt;
167  is_time_step_fixed_=true;
168  time_step_changed_=true;
169  end_of_fixed_dt_interval_ = inf_time;
170 
171  upper_constraint_=max_time_step_=dt;
172  lower_constraint_=min_time_step_=dt;
173  time_step_=dt;
174 
175  }
176 
177  eq_mark_type_=type;
178  steady_=false;
179  time_marks_->add( TimeMark(time_, equation_fixed_mark_type()) );
180  if (end_time_ != inf_time)
181  time_marks_->add( TimeMark(end_time_, equation_fixed_mark_type()) );
182 }
183 
184 
185 
186 
187 
188 
189 void TimeGovernor::set_permanent_constraint( double min_dt, double max_dt)
190 {
191  if (min_dt < time_step_lower_bound) {
192  THROW(ExcTimeGovernorMessage() << EI_Message("'min_dt' smaller then machine precision.\n") );
193  }
194  if (max_dt < min_dt) {
195  THROW(ExcTimeGovernorMessage() << EI_Message("'max_dt' smaller then 'min_dt'.\n") );
196  }
197 
198  lower_constraint_ = min_time_step_ = max(min_dt, time_step_lower_bound);
199  upper_constraint_ = max_time_step_ = min(max_dt, end_time_-time_);
200 }
201 
202 
203 // int set_constrain - dle nastaveni constraint
204 // interval - constraint - jako v cmp u Qsortu
205 // -1 vetsi nez interval (min_dt,max_dt)
206 // +1 mensi
207 // 0 OK
208 
210 {
211  if (upper_constraint_ < upper)
212  {
213  //do not change upper_constraint_
214  return -1;
215  }
216 
217  if (lower_constraint_ <= upper)
218  {
219  //change upper_constraint_ to upper
220  upper_constraint_ = upper;
221  return 0;
222  }
223 
224  if (lower_constraint_ > upper)
225  {
226  //do not change upper_constraint_
227  return 1;
228  }
229 
230  return 0;
231 }
232 
234 {
235  if (upper_constraint_ < lower)
236  {
237  //do not change lower_constraint_
238  return -1;
239  }
240 
241  if (min_time_step_ <= lower)
242  {
243  //change lower_constraint_ to lower
244  lower_constraint_ = lower;
245  return 0;
246  }
247 
248  if (min_time_step_ > lower)
249  {
250  //do not change lower_constraint_
251  return 1;
252  }
253 
254  return 0;
255 }
256 
257 
258 
259 void TimeGovernor::add_time_marks_grid(double step, TimeMark::Type mark_type) const
260 {
261  if (end_time() == inf_time) {
262  THROW(ExcTimeGovernorMessage()
263  << EI_Message("Missing end time for make output grid required by key 'time_step' of the output stream.\n")
264  );
265  }
266 
267  marks().add_time_marks(init_time_, step, end_time(), mark_type | eq_mark_type_);
268  // always add start time and end time
269  marks().add(TimeMark(init_time_, mark_type | eq_mark_type_));
270  marks().add(TimeMark(end_time(), mark_type | eq_mark_type_));
271 }
272 
273 
274 
276  if (time_ == inf_time || is_end()) return 0.0;
277  if (time_marks_ == NULL) return inf_time;
278 
279  //two states of steady time governor returns different estimate
280  //if (steady && time_level==0) return inf_time; //at the beginning
281  //if (steady && time_level==1) return 0.0; //at the end
282 
283  if (this->lt(end_of_fixed_dt_interval_)) return fixed_time_step_;
284 
285  // jump to the first future fix time
286  TimeMarks::iterator fix_time_it = time_marks_->next(*this, equation_fixed_mark_type());
287  // compute step to next fix time and apply constraints
288  double full_step = fix_time_it->time() - time_;
289 
290  double step_estimate = min(full_step, upper_constraint_);
291  step_estimate = max(step_estimate, lower_constraint_); //these two must be in this order
292 
293  if (step_estimate == inf_time) return step_estimate;
294 
295  // round the time step to have integer number of steps till next fix time
296  // this always select shorter time step
297  int n_steps = ceil( full_step / step_estimate );
298  //xprintf(Msg, "float_n_step: %.16f ", full_step / step_estimate);
299 
300 
301  //checking rounding precision: e.g. 1.0000000000000009 -> 1 NOT 2
302  int n_floor_steps = floor(full_step / step_estimate);
303  if ( abs(full_step / step_estimate - n_floor_steps) < round_n_steps_precision) n_steps = n_floor_steps;
304 
305  step_estimate = full_step / n_steps;
306  //xprintf(Msg, "n_step: %d step_estimate: %f\n", n_steps, step_estimate);
307 
308  // if the step estimate gets by rounding lower than lower constraint program will not crash
309  // will just output a user warning.
310  if (step_estimate < lower_constraint_)
311  xprintf(Warn, "Time step estimate is below the lower constraint of time step. The difference is: %.16f.\n",
312  lower_constraint_ - step_estimate);
313 
314  /* OBSOLETE
315  // check permanent bounds with a bit of tolerance
316  if (step_estimate < min_time_step*0.99) {
317  // try longer step
318  double longer_step = full_step / (n_steps - 1);
319  if (longer_step <= max_time_step*1.01) {
320  step_estimate = longer_step;
321  }
322  }
323  */
324 
325  return step_estimate;
326 }
327 
328 
329 
331 {
332  ASSERT_LE(0.0, time_);
333  if (time_ == inf_time || is_end()) return;
334 
335  //in case the time governor is steady the time is set to end time which is infinity
336  /* if (steady) {
337  last_time_ = time;
338  last_time_step = end_time_;
339  time_level = 1;
340  time_step = 0.0;
341  time = end_time_;
342  dt_changed = false;
343  return;
344  }*/
345 
346  if (this->lt(end_of_fixed_dt_interval_)) {
347  // this is done for fixed step
348  // make tiny correction of time step in order to avoid big rounding errors
349  // tiny correction means that dt_changed 'is NOT changed'
350  if (end_of_fixed_dt_interval_ < inf_time) {
351  fixed_time_step_ = (end_of_fixed_dt_interval_-time_) / round( (end_of_fixed_dt_interval_-time_) / fixed_time_step_ );
352  }
353 
354  last_time_step_ = time_step_;
355  time_step_ = fixed_time_step_;
356 
357  //checking whether fixed time step has been changed (by fix_dt_until_mark() method) since last time
358  if (is_time_step_fixed_)
359  {
360  is_time_step_fixed_ = false;
361 
362  //is true unless new fixed_dt is not equal previous time_step
363  time_step_changed_ = (last_time_step_ != time_step_);
364  }
365  else
366  time_step_changed_ = false;
367  }
368  else
369  {
370  // this is done if end_of_fixed_dt_interval is not set (means it is equal to -infinity)
371  last_time_step_ = time_step_;
372  time_step_ = estimate_dt();
373  time_step_changed_= (last_time_step_ != time_step_);
374  }
375 
376  last_time_ = time_;
377  time_+=time_step_;
378  time_level_++;
379  // refreshing the upper_constraint_
380  upper_constraint_ = min(end_time_ - time_, max_time_step_);
381  lower_constraint_ = min_time_step_;
382 
383  //if (time_level_ == 7) time_ = end_time_;
384 }
385 
386 
387 
388 void TimeGovernor::view(const char *name) const
389 {
390  //xprintf(Msg, "\nTG[%s]: level: %d end_time: %f time: %f step: %f upper: %f lower: %f end_fixed_time: %f type: %x\n",
391  // name, time_level_, end_time_, time_, time_step_, upper_constraint_, lower_constraint_, end_of_fixed_dt_interval_, eq_mark_type_);
392 
393  xprintf(Msg, "\nTG[%s]:%06d t:%10.4f dt:%10.6f dt_int<%10.6f,%10.6f>",
394  name, time_level_, time_, time_step_, lower_constraint_, upper_constraint_ );
395 #ifdef DEBUG_MESSAGES
396  xprintf(Msg, " end_time: %f end_fixed_time: %f type: 0x%x\n" , end_time_, end_of_fixed_dt_interval_, eq_mark_type_);
397 #else
398  xprintf(Msg,"\n");
399 #endif
400 }