SyDEVS  v0.7
Simulation-based analysis of complex systems involving people, devices, physical elements, and dynamic environments.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
simulation.h
Go to the documentation of this file.
1 #pragma once
2 #ifndef SYDEVS_SYSTEMS_SIMULATION_H_
3 #define SYDEVS_SYSTEMS_SIMULATION_H_
4 
8 
9 namespace sydevs {
10 namespace systems {
11 
12 
72 template<typename Node>
74 {
75 public:
91  simulation(const time_point& start_t, const time_point& end_t, bool can_end_early, int64 seed, std::ostream& stream);
92 
105  simulation(duration total_dt, int64 seed, std::ostream& stream);
106 
107  simulation(const simulation&) = delete;
108  simulation& operator=(const simulation&) = delete;
109  simulation(simulation&&) = delete;
111  virtual ~simulation() = default;
112 
113  const time_point& start_time() const;
114  const time_point& end_time() const;
115  bool can_end_early() const;
116 
117  bool started() const;
118  bool finishing() const;
119  bool finished() const;
120 
121  const discrete_event_time& time() const;
122  duration imminent_duration() const;
123 
124  void process_next_event();
128 
129  const timer& event_timer() const;
130 
131 private:
132  node_interface& top_IO();
133  discrete_event_time& event_time();
134  void validate();
135 
136  void process_initialization_event();
137  void process_planned_event();
138  void process_finalization_event();
139  void advance_time();
140 
141  const time_point start_t_; // (must be declared before member variable external_context_)
142  const time_point end_t_;
143  bool can_end_early_;
144  node_context external_context_; // (must be declared before member variable top)
145  bool started_;
146  bool finishing_;
147  bool finished_;
148  time_queue t_queue_;
149  time_cache t_cache_;
150  timer event_timer_;
151 
152 public:
153  Node top;
154 };
155 
156 
157 template<typename Node>
158 inline simulation<Node>::simulation(const time_point& start_t, const time_point& end_t, bool can_end_early, int64 seed, std::ostream& stream)
159  : start_t_(start_t)
160  , end_t_(end_t)
161  , can_end_early_(can_end_early)
162  , external_context_(start_t, seed, stream)
163  , started_(false)
164  , finishing_(false)
165  , finished_(false)
166  , t_queue_(start_t)
167  , t_cache_(start_t)
168  , event_timer_()
169  , top("top", external_context_)
170 {
171  validate();
172 }
173 
174 
175 template<typename Node>
176 inline simulation<Node>::simulation(duration total_dt, int64 seed, std::ostream& stream)
177  : start_t_()
178  , end_t_(total_dt.finite() ? time_point() + total_dt :
179  time_point() + duration(1, scale(std::numeric_limits<scale::level_type>::max() - 6)))
180  , can_end_early_(!total_dt.finite())
181  , external_context_(start_t_, seed, stream)
182  , started_(false)
183  , finishing_(false)
184  , finished_(false)
185  , t_queue_()
186  , t_cache_()
187  , event_timer_()
188  , top("top", external_context_)
189 {
190  validate();
191 }
192 
193 
194 template<typename Node>
196 {
197  return start_t_;
198 }
199 
200 
201 template<typename Node>
203 {
204  return end_t_;
205 }
206 
207 
208 template<typename Node>
210 {
211  return can_end_early_;
212 }
213 
214 
215 template<typename Node>
216 inline bool simulation<Node>::started() const
217 {
218  return started_;
219 }
220 
221 
222 template<typename Node>
223 inline bool simulation<Node>::finishing() const
224 {
225  return finishing_;
226 }
227 
228 
229 template<typename Node>
230 inline bool simulation<Node>::finished() const
231 {
232  return finished_;
233 }
234 
235 
236 template<typename Node>
238 {
239  return const_cast<node_context&>(external_context_).event_time();
240 }
241 
242 
243 template<typename Node>
245 {
246  return t_queue_.imminent_duration();
247 }
248 
249 
250 template<typename Node>
252 {
253  if (!finished_) {
254  if (!finishing_) {
255  if (!started_) {
256  process_initialization_event();
257  }
258  else {
259  process_planned_event();
260  }
261  advance_time();
262  }
263  else {
264  process_finalization_event();
265  }
266  }
267 }
268 
269 
270 template<typename Node>
272 {
273  int64 event_count = 0;
274  auto t = event_time().t();
275  while (!finished_ && event_time().t() == t) {
276  process_next_event();
277  ++event_count;
278  }
279  return event_count;
280 }
281 
282 
283 template<typename Node>
285 {
286  int64 event_count = 0;
287  while (!finished_ && event_time().t() < t) {
288  process_next_event();
289  ++event_count;
290  }
291  return event_count;
292 }
293 
294 
295 template<typename Node>
297 {
298  int64 event_count = 0;
299  while (!finished_) {
300  process_next_event();
301  ++event_count;
302  }
303  return event_count;
304 }
305 
306 
307 template<typename Node>
309 {
310  return event_timer_;
311 }
312 
313 
314 template<typename Node>
316 {
317  return const_cast<node_interface&>(top.external_interface());
318 }
319 
320 
321 template<typename Node>
322 inline discrete_event_time& simulation<Node>::event_time()
323 {
324  return external_context_.event_time();
325 }
326 
327 
328 template<typename Node>
329 inline void simulation<Node>::validate()
330 {
331  static_assert(std::is_base_of<system_node, Node>::value, "Node must inherit from system_node");
332 
333  if (top_IO().flow_input_port_count() != 0 ||
334  top_IO().message_input_port_count() != 0 ||
335  top_IO().message_output_port_count() != 0 ||
336  top_IO().flow_output_port_count() != 0) {
337  throw std::invalid_argument("Node to be simulated must have no ports");
338  }
339 }
340 
341 
342 template<typename Node>
343 inline void simulation<Node>::process_initialization_event()
344 {
345  started_ = true;
346  top_IO().print_event("initialization");
347  top_IO().activate(flow, input);
348  event_timer_.start();
349  auto planned_dt = top.process_initialization_event();
350  event_timer_.stop();
351  top_IO().deactivate();
352  if (planned_dt.finite()) {
353  t_queue_.plan_event(0, planned_dt);
354  }
355  if (top.time_precision() != no_scale) {
356  t_cache_.retain_event(0, top.time_precision());
357  }
358 }
359 
360 
361 template<typename Node>
362 inline void simulation<Node>::process_planned_event()
363 {
364  top_IO().print_event("planned");
365  auto elapsed_dt = duration();
366  if (top.time_precision() != no_scale) {
367  elapsed_dt = t_cache_.duration_since(0).fixed_at(top.time_precision());
368  }
369  top_IO().activate(message, output);
370  event_timer_.start();
371  auto planned_dt = top.process_planned_event(elapsed_dt);
372  event_timer_.stop();
373  top_IO().deactivate();
374  if (planned_dt.finite()) {
375  t_queue_.plan_event(0, planned_dt);
376  }
377  else {
378  t_queue_.pop_imminent_event(0);
379  }
380  if (top.time_precision() != no_scale) {
381  t_cache_.retain_event(0, top.time_precision());
382  }
383 }
384 
385 
386 template<typename Node>
387 inline void simulation<Node>::process_finalization_event()
388 {
389  top_IO().print_event("finalization");
390  auto elapsed_dt = duration();
391  if (top.time_precision() != no_scale) {
392  elapsed_dt = t_cache_.duration_since(0).fixed_at(top.time_precision());
393  }
394  top_IO().activate(flow, output);
395  event_timer_.start();
396  top.process_finalization_event(elapsed_dt);
397  event_timer_.stop();
398  top_IO().deactivate();
399  finished_ = true;
400 }
401 
402 
403 template<typename Node>
404 inline void simulation<Node>::advance_time()
405 {
406  if (!finishing_) {
407  auto planned_dt = t_queue_.imminent_duration();
408  if (planned_dt.finite() || !can_end_early_) {
409  // The event time must advance.
410  event_time().advance(planned_dt, end_t_);
411  if (planned_dt > 0_s) {
412  // The simulated time just advanced.
413  external_context_.time_printed() = false;
414  t_queue_.advance_time(event_time().t());
415  t_cache_.advance_time(event_time().t());
416  }
417  if (event_time().t() >= end_t_) {
418  // The end time has been reached.
419  finishing_ = true;
420  }
421  }
422  else {
423  // The simulation must finish at the current event time.
424  finishing_ = true;
425  }
426  }
427 }
428 
429 
430 } // namespace
431 } // namespace
432 
433 #endif
A data type which represents the general concept of scale as a dimensionless power of 1000.
Definition: scale.h:71
A data structure which represents progress through a simulation, encapsulating both simulated time an...
Definition: discrete_event_time.h:37
Definition: node_context.h:17
Definition: node_interface.h:17
A class template for running simulations.
Definition: simulation.h:74
virtual ~simulation()=default
Destructor
const time_point & end_time() const
Returns the end time of the simulation.
Definition: simulation.h:202
bool finishing() const
Returns true if all events are finished except finalization.
Definition: simulation.h:223
void process_next_event()
Runs the next event of the topmost system node.
Definition: simulation.h:251
const timer & event_timer() const
Returns the object that accumulated wallclock event durations.
Definition: simulation.h:308
duration imminent_duration() const
Returns the duration until the imminent event.
Definition: simulation.h:244
const discrete_event_time & time() const
Returns the current point in discrete event time.
Definition: simulation.h:237
const time_point & start_time() const
Returns the start time of the simulation.
Definition: simulation.h:195
int64 process_events_until(const time_point &t)
Runs all events until simulated time advances at least to t; returns the number of processed events.
Definition: simulation.h:284
bool started() const
Returns true if the simulation has started.
Definition: simulation.h:216
simulation & operator=(simulation &&)=delete
No move assignment.
simulation(simulation &&)=delete
No move constructor.
simulation & operator=(const simulation &)=delete
No copy assignment.
int64 process_next_events()
Runs all events until simulated time advances; returns the number of processed events.
Definition: simulation.h:271
Node top
The topmost system node.
Definition: simulation.h:153
simulation(const simulation &)=delete
No copy constructor.
int64 process_remaining_events()
Runs simulation until completion; returns the number of processed events.
Definition: simulation.h:296
bool can_end_early() const
Returns true if the simulation can end before the specified end time.
Definition: simulation.h:209
simulation(const time_point &start_t, const time_point &end_t, bool can_end_early, int64 seed, std::ostream &stream)
Constructs a simulation with the full set of configuration options.
Definition: simulation.h:158
bool finished() const
Returns true if the simulation has finished.
Definition: simulation.h:230
A data structure which provides durations elapsed since past events.
Definition: time_cache.h:28
A data structure which represents a point in time as an arbitrary-precision multiple of its shortest ...
Definition: time_point.h:85
A data structure which supports the scheduling of future events.
Definition: time_queue.h:35
A class for measuring and accumulating intervals of wallclock time.
Definition: timer.h:25
Definition: arraynd.h:1211
const auto message
Equivalent to data_mode::message.
Definition: data_mode.h:23
const auto output
Equivalent to data_goal::output.
Definition: data_goal.h:22
const auto input
Equivalent to data_goal::input.
Definition: data_goal.h:21
const auto flow
Equivalent to data_mode::flow.
Definition: data_mode.h:22
Definition: arraynd.h:8
constexpr auto _s
Definition: units.h:128
quantity< seconds > duration
Definition: quantity.h:1006
int64_t int64
Definition: number_types.h:15
constexpr scale no_scale
Definition: scale.h:153