SyDEVS  v0.6.7
Multiscale Simulation and Systems Modeling Library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
collection_node.h
Go to the documentation of this file.
1 #pragma once
2 #ifndef SYDEVS_SYSTEMS_COLLECTION_NODE_H_
3 #define SYDEVS_SYSTEMS_COLLECTION_NODE_H_
4 
8 
9 namespace sydevs {
10 namespace systems {
11 
12 
13 // collection node declaration
14 
54 template<typename AgentID, typename Node>
56 {
57 public:
58  template<typename T>
60 
61  template<typename T>
63 
64  class const_iterator;
65 
79  collection_node(const std::string& node_name, const node_context& external_context);
80 
81  virtual ~collection_node() = default;
82 
83  void print_on_elapsed_duration(bool flag = true) const;
84  void print_on_planned_duration(bool flag = true) const;
85 
86  const Node prototype;
87 
91  void handle_finalization_event(duration elapsed_dt);
92 
93 protected:
94  static const int64 macro_event_id = std::numeric_limits<int64>::max();
95 
96  template<typename T>
97  bool transmitted(const port<message, output, T>& prototype_port);
98 
99  template<typename T>
100  flow_port_proxy<T> access(const port<flow, input, T>& prototype_port);
101 
102  template<typename T>
103  message_port_proxy<T> access(const port<message, input, T>& prototype_port);
104 
105  template<typename T>
106  const T& access(const port<message, output, T>& prototype_port);
107 
108  template<typename T>
109  const T& access(const port<flow, output, T>& prototype_port);
110 
126  template<typename AgentNode>
127  void create_agent(const AgentID& agent_id);
128 
141  void create_agent(const AgentID& agent_id);
142 
155  void affect_agent(const AgentID& agent_id);
156 
168  void remove_agent(const AgentID& agent_id);
169 
182  void invoke_agent(const AgentID& agent_id);
183 
184  bool agent_exists(const AgentID& agent_id);
185  int64 agent_count();
186  const_iterator agent_begin();
187  const_iterator agent_end();
188 
189 private:
190  node_structure& internal_structure();
191  node_interface& prototype_IO() const;
192  timer& prototype_ET() const;
193  node_interface& agent_IO(const system_node& agent) const;
194  discrete_event_time& event_time();
195 
196  template<data_mode dmode, data_goal dgoal, typename T>
197  void validate_prototype_port(const port<dmode, dgoal, T>& prototype_port);
198 
199  Node& handle_agent_planned_event(int64 agent_index);
200  void erase_removed_agents();
201 
202  template<typename T>
203  std::string agent_name_from_id(qualified_type<T> agent_id_type, const AgentID& agent_id);
204 
205  std::string agent_name_from_id(qualified_type<std::string> agent_id_type, const AgentID& agent_id);
206 
207  void adopt_component_print_flags(const system_node& node) const;
208 
209  virtual duration macro_initialization_event() = 0;
210  virtual duration macro_unplanned_event(duration elapsed_dt) = 0;
211  virtual duration micro_planned_event(const AgentID& agent_id, duration elapsed_dt) = 0;
212  virtual duration macro_planned_event(duration elapsed_dt) = 0;
213  virtual void macro_finalization_event(duration elapsed_dt) = 0;
214 
215  node_context internal_context_;
216  std::map<AgentID, int64> agent_indices_; // Includes indices for all agents except those marked as removed
217  std::map<int64, AgentID> agent_ids_; // Includes IDs for all agents including those marked as removed
218  std::map<int64, std::unique_ptr<Node>> agents_; // Includes all agents including those marked as removed
219  std::set<int64> removed_indices_; // Includes indices only for agents marked as removed
220  time_queue t_queue_;
221  time_cache t_cache_;
222  bool initialized_;
223  bool finalized_;
224 };
225 
226 
227 // flow port proxy declaration
228 
229 template<typename AgentID, typename Node>
230 template<typename T>
231 class collection_node<AgentID, Node>::flow_port_proxy
232 {
233 friend class collection_node<AgentID, Node>;
234 public:
235  flow_port_proxy(const flow_port_proxy&) = delete;
236  flow_port_proxy(flow_port_proxy&&) = default;
238  virtual ~flow_port_proxy() = default;
239 
240  flow_port_proxy& operator=(const T& rhs);
242 
243 private:
244  flow_port_proxy(const port<flow, input, T>& input_port);
245 
246  node_interface& external_interface_;
247  const int64 port_index_;
248 };
249 
250 
251 // message port proxy declaration
252 
253 template<typename AgentID, typename Node>
254 template<typename T>
255 class collection_node<AgentID, Node>::message_port_proxy
256 {
257 friend class collection_node<AgentID, Node>;
258 public:
259  message_port_proxy(const message_port_proxy&) = delete;
262  virtual ~message_port_proxy() = default;
263 
264  message_port_proxy& operator=(const T& rhs);
266 
267 private:
268  message_port_proxy(const port<message, input, T>& input_port);
269 
270  node_interface& external_interface_;
271  const int64 port_index_;
272 };
273 
274 
275 // const iterator declaration
276 
277 template<typename AgentID, typename Node>
278 class collection_node<AgentID, Node>::const_iterator : public std::iterator<std::bidirectional_iterator_tag, AgentID>
279 {
280 friend class collection_node<AgentID, Node>;
281 public:
282  const AgentID& operator*() const;
283  const AgentID* operator->() const;
284 
285  const_iterator& operator++();
286  const_iterator operator++(int);
287  const_iterator& operator--();
288  const_iterator operator--(int);
289 
290  bool operator==(const const_iterator& rhs) const;
291  bool operator!=(const const_iterator& rhs) const;
292 
293 private:
294  const_iterator(const typename std::map<AgentID, int64>::const_iterator& iter);
295 
296  typename std::map<AgentID, int64>::const_iterator iter_;
297 };
298 
299 
300 // collection node members
301 
302 template<typename AgentID, typename Node>
303 inline collection_node<AgentID, Node>::collection_node(const std::string& node_name, const node_context& external_context)
304  : collection_node_base(node_name, external_context)
305  , prototype("prototype", prototype_context())
306  , internal_context_(&const_cast<node_interface&>(external_interface()),
307  const_cast<node_context&>(external_context))
308  , agent_indices_()
309  , agent_ids_()
310  , agents_()
311  , removed_indices_()
312  , t_queue_()
313  , t_cache_()
314  , initialized_(false)
315  , finalized_(false)
316 {
317  static_assert(qualified_type<AgentID>::valid_and_sortable, "typename AgentID must be a sortable qualified type");
318 }
319 
320 
321 template<typename AgentID, typename Node>
323 {
324  external_IO().print_on_elapsed_duration(flag);
325 }
326 
327 
328 template<typename AgentID, typename Node>
330 {
331  external_IO().print_on_planned_duration(flag);
332 }
333 
334 
335 template<typename AgentID, typename Node>
337 {
338  if (initialized_) throw std::logic_error("Attempt to initialize collection node (" + full_name() + ") more than once");
339  auto& current_t = event_time().t();
340  t_queue_ = time_queue(current_t);
341  t_cache_ = time_cache(current_t);
342  external_IO().print_event("macro-initialization");
343  ET().start();
344  auto dt = macro_initialization_event();
345  ET().stop();
346  prototype_IO().clear_flow_inputs();
347  prototype_IO().clear_message_input();
348  prototype_IO().clear_flow_outputs();
349  auto macro_planned_dt = scale_planned_dt(dt);
350  if (macro_planned_dt.finite()) {
351  t_queue_.plan_event(macro_event_id, macro_planned_dt);
352  }
353  if (time_precision() != no_scale) {
354  t_cache_.retain_event(macro_event_id, time_precision());
355  }
356  auto planned_dt = t_queue_.imminent_duration();
357  external_IO().print_planned_duration(planned_dt);
358  initialized_ = true;
359  erase_removed_agents();
360  return planned_dt;
361 }
362 
363 
364 template<typename AgentID, typename Node>
366 {
367  auto& current_t = event_time().t();
368  t_queue_.advance_time(current_t);
369  t_cache_.advance_time(current_t);
370  external_IO().print_event("macro-unplanned");
371  external_IO().print_elapsed_duration(elapsed_dt);
372  ET().start();
373  auto dt = macro_unplanned_event(elapsed_dt);
374  ET().stop();
375  prototype_IO().clear_flow_inputs();
376  prototype_IO().clear_message_input();
377  prototype_IO().clear_flow_outputs();
378  auto macro_planned_dt = scale_planned_dt(dt);
379  if (macro_planned_dt.finite()) {
380  t_queue_.plan_event(macro_event_id, macro_planned_dt);
381  }
382  else {
383  t_queue_.cancel_event(macro_event_id);
384  }
385  if (time_precision() != no_scale) {
386  t_cache_.retain_event(macro_event_id, time_precision());
387  }
388  auto planned_dt = t_queue_.imminent_duration();
389  external_IO().print_planned_duration(planned_dt);
390  erase_removed_agents();
391  return planned_dt;
392 }
393 
394 
395 template<typename AgentID, typename Node>
397 {
398  auto& current_t = event_time().t();
399  t_queue_.advance_time(current_t);
400  t_cache_.advance_time(current_t);
401  if (t_queue_.imminent_duration() != 0_s) throw std::logic_error("Unexpected error while advancing time to that of next planned event");
402  int64 agent_index = *std::begin(t_queue_.imminent_event_ids());
403  if (agent_index < macro_event_id) {
404  // The planned event is triggered by an agent.
405  // Execute an agent planned event followed by a micro planned event.
406  auto& agent = handle_agent_planned_event(agent_index);
407  int64 list_size = agent_IO(agent).message_output_list_size();
408  const auto& agent_id = agent_ids_[agent_index];
409  auto micro_elapsed_dt = elapsed_dt;
410  for (int64 list_index = 0; list_index < list_size; ++list_index) {
411  event_time().advance();
412  int64 port_index = agent_IO(agent).message_output_index(list_index);
413  const auto& val = agent_IO(agent).message_output_value(list_index);
414  external_IO().print_event("micro-planned");
415  external_IO().print_elapsed_duration(micro_elapsed_dt);
416  prototype_IO().append_message_output(port_index, val);
417  ET().start();
418  auto dt = micro_planned_event(agent_id, micro_elapsed_dt);
419  ET().stop();
420  prototype_IO().clear_flow_inputs();
421  prototype_IO().clear_message_input();
422  prototype_IO().clear_message_outputs();
423  prototype_IO().clear_flow_outputs();
424  auto macro_planned_dt = scale_planned_dt(dt);
425  external_IO().print_planned_duration(macro_planned_dt);
426  micro_elapsed_dt -= micro_elapsed_dt;
427  if (macro_planned_dt.finite()) {
428  t_queue_.plan_event(macro_event_id, macro_planned_dt);
429  }
430  else {
431  t_queue_.cancel_event(macro_event_id);
432  }
433  if (time_precision() != no_scale) {
434  t_cache_.retain_event(macro_event_id, time_precision());
435  }
436  }
437  agent_IO(agent).clear_message_outputs();
438  }
439  else {
440  // The planned event is triggered by the collection.
441  // Execute a macro planned event.
442  external_IO().print_event("macro-planned");
443  external_IO().print_elapsed_duration(elapsed_dt);
444  ET().start();
445  auto dt = macro_planned_event(elapsed_dt);
446  ET().stop();
447  prototype_IO().clear_flow_inputs();
448  prototype_IO().clear_message_input();
449  prototype_IO().clear_flow_outputs();
450  auto macro_planned_dt = scale_planned_dt(dt);
451  external_IO().print_planned_duration(macro_planned_dt);
452  if (macro_planned_dt.finite()) {
453  t_queue_.plan_event(macro_event_id, macro_planned_dt);
454  }
455  else {
456  t_queue_.pop_imminent_event(macro_event_id);
457  }
458  if (time_precision() != no_scale) {
459  t_cache_.retain_event(macro_event_id, time_precision());
460  }
461  }
462  auto planned_dt = t_queue_.imminent_duration();
463  erase_removed_agents();
464  return planned_dt;
465 }
466 
467 
468 template<typename AgentID, typename Node>
470 {
471  if (finalized_) throw std::logic_error("Attempt to finalize collection node (" + full_name() + ") more than once");
472  auto& current_t = event_time().t();
473  t_queue_.advance_time(current_t);
474  t_cache_.advance_time(current_t);
475  external_IO().print_event("macro-finalization");
476  external_IO().print_elapsed_duration(elapsed_dt);
477  ET().start();
478  macro_finalization_event(elapsed_dt);
479  while (agent_count() > 0) {
480  auto agent_id = *agent_begin();
481  remove_agent(agent_id);
482  }
483  ET().stop();
484  erase_removed_agents();
485  finalized_ = true;
486 }
487 
488 
489 template<typename AgentID, typename Node>
490 template<typename T>
492 {
493  validate_prototype_port(prototype_port);
494  return prototype_port.port_index() == prototype_IO().message_output_index(0);
495 }
496 
497 
498 template<typename AgentID, typename Node>
499 template<typename T>
500 #if _WIN32
502 #else
504 #endif
505 {
506  validate_prototype_port(prototype_port);
507  return flow_port_proxy<T>(const_cast<port<flow, input, T>&>(prototype_port));
508 }
509 
510 
511 template<typename AgentID, typename Node>
512 template<typename T>
513 #if _WIN32
515 #else
517 #endif
518 {
519  validate_prototype_port(prototype_port);
520  if (prototype_IO().message_input_port_index() != -1) {
521  throw std::logic_error("Attempt to access message input port (" + prototype_port.port_name() + "), " +
522  "but another message input port of prototype agent (" + prototype.full_name() + ") " +
523  "has already been accessed");
524  }
525  else {
526  prototype_IO().set_message_input(prototype_port.port_index(), pointer());
527  }
528  return message_port_proxy<T>(const_cast<port<message, input, T>&>(prototype_port));
529 }
530 
531 
532 template<typename AgentID, typename Node>
533 template<typename T>
535 {
536  validate_prototype_port(prototype_port);
537  if (prototype_port.port_index() != prototype_IO().message_output_index(0)) {
538  throw std::logic_error("Attempt to access message output port (" + prototype_port.port_name() + "), " +
539  "which is not the port of prototype agent (" + prototype.full_name() + ") " +
540  "on which the current message is being transmitted");
541  }
542  auto& val = prototype_IO().message_output_value(0);
543  return const_cast<const T&>(val.template dereference<T>());
544 }
545 
546 
547 template<typename AgentID, typename Node>
548 template<typename T>
550 {
551  validate_prototype_port(prototype_port);
552  auto& val = prototype_IO().flow_output_port_value(prototype_port.port_index());
553  return const_cast<const T&>(val.template dereference<T>());
554 }
555 
556 
557 template<typename AgentID, typename Node>
558 inline void collection_node<AgentID, Node>::create_agent(const AgentID& agent_id)
559 {
560  create_agent<Node>(agent_id);
561 }
562 
563 
564 template<typename AgentID, typename Node>
565 template<typename AgentNode>
566 inline void collection_node<AgentID, Node>::create_agent(const AgentID& agent_id)
567 {
568  static_assert(std::is_base_of<Node, AgentNode>::value, "AgentNode must inherit from Node");
569  if (prototype.node_dmode() == flow) {
570  throw std::logic_error("Attempt to use \"create_agent\" to create a flow node agent of a collection node (" + full_name() + "); use \"invoke_agent\" instead");
571  }
572  event_time().advance();
573  auto agent_name = agent_name_from_id(qualified_type<AgentID>(), agent_id);
574  std::unique_ptr<Node> agent_ptr(new AgentNode(agent_name, internal_context_));
575  auto& agent = *agent_ptr;
576  agent.adopt_print_flags(prototype);
577  if (agent_exists(agent_id)) {
578  throw std::logic_error("Created agent (" + agent.full_name() + ") already exists.");
579  }
580  int64 agent_index = agent.node_index();
581  agent_indices_[agent_id] = agent_index;
582  agent_ids_[agent_index] = agent_id;
583  agents_[agent_index] = std::move(agent_ptr);
584  agent_IO(agent).print_event("initialization");
585  int64 missing_input = prototype_IO().missing_flow_input();
586  if (missing_input != -1) {
587  throw std::logic_error("Flow input port (" + agent_IO(agent).flow_input_port_name(missing_input) + ") " +
588  "of created agent (" + agent.full_name() + ") has no value");
589  }
590  auto input_port_count = prototype_IO().flow_input_port_count();
591  for (int64 port_index = 0; port_index < input_port_count; ++port_index) {
592  const auto& flow_input_val = prototype_IO().flow_input_port_value(port_index);
593  agent_IO(agent).assign_flow_input(port_index, flow_input_val);
594  agent_IO(agent).print_flow_input(port_index);
595  }
596  agent_IO(agent).activate(flow, input);
597  auto planned_dt = duration();
598  try {
599  planned_dt = agent.process_initialization_event();
600  }
601  catch (const std::exception& e) {
602  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
603  throw e;
604  }
605  agent_IO(agent).deactivate();
606  if (planned_dt.finite()) {
607  t_queue_.plan_event(agent_index, planned_dt);
608  }
609  if (agent.time_precision() != no_scale) {
610  t_cache_.retain_event(agent_index, agent.time_precision());
611  }
612 }
613 
614 
615 template<typename AgentID, typename Node>
616 inline void collection_node<AgentID, Node>::affect_agent(const AgentID& agent_id)
617 {
618  if (prototype.node_dmode() == flow) {
619  throw std::logic_error("Attempt to use \"affect_agent\" to affect a flow node agent of a collection node (" + full_name() + "); use \"invoke_agent\" instead");
620  }
621  event_time().advance();
622  auto agent_iter = agent_indices_.find(agent_id);
623  if (agent_iter == std::end(agent_indices_)) {
624  throw std::logic_error("Attempt to affect agent (" + full_name() + "." + tostring(agent_id) + ") that does not exist");
625  }
626  int64 agent_index = agent_iter->second;
627  auto& agent = *dynamic_cast<Node*>(&internal_structure().node(agent_index));
628  int64 port_index = prototype_IO().message_input_port_index();
629  if (port_index == -1) {
630  throw std::logic_error("Attempt to affect agent (" + agent.full_name() + "), " +
631  "but none of the prototype's message input ports have been accessed");
632  }
633  auto val = prototype_IO().message_input_port_value(port_index);
634  if (!val) {
635  throw std::logic_error("Attempt to affect agent (" + agent.full_name() + "), " +
636  "but none of the prototype's message input ports have been assigned a value");
637  }
638  agent_IO(agent).print_event("unplanned");
639  agent_IO(agent).set_message_input(port_index, val);
640  agent_IO(agent).print_message_input(port_index);
641  auto elapsed_dt = duration();
642  if (agent.time_precision() != no_scale) {
643  elapsed_dt = t_cache_.duration_since(agent_index).fixed_at(agent.time_precision());
644  }
645  agent_IO(agent).activate(message, input);
646  auto planned_dt = duration();
647  try {
648  planned_dt = agent.process_unplanned_event(elapsed_dt);
649  }
650  catch (const std::exception& e) {
651  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
652  throw e;
653  }
654  agent_IO(agent).deactivate();
655  if (planned_dt.finite()) {
656  t_queue_.plan_event(agent_index, planned_dt);
657  }
658  else {
659  t_queue_.cancel_event(agent_index);
660  }
661  if (agent.time_precision() != no_scale) {
662  t_cache_.retain_event(agent_index, agent.time_precision());
663  }
664  agent_IO(agent).clear_message_input();
665  prototype_IO().clear_message_input();
666 }
667 
668 
669 template<typename AgentID, typename Node>
670 inline void collection_node<AgentID, Node>::remove_agent(const AgentID& agent_id)
671 {
672  if (prototype.node_dmode() == flow) {
673  throw std::logic_error("Attempt to use \"remove_agent\" to remove a flow node agent of a collection node (" + full_name() + "); use \"invoke_agent\" instead");
674  }
675  event_time().advance();
676  auto agent_iter = agent_indices_.find(agent_id);
677  if (agent_iter == std::end(agent_indices_)) {
678  throw std::logic_error("Attempt to remove agent (" + full_name() + "." + tostring(agent_id) + ") that does not exist");
679  }
680  int64 agent_index = agent_iter->second;
681  auto& agent = *dynamic_cast<Node*>(&internal_structure().node(agent_index));
682  agent_IO(agent).print_event("finalization");
683  auto elapsed_dt = duration();
684  if (agent.time_precision() != no_scale) {
685  elapsed_dt = t_cache_.duration_since(agent_index).fixed_at(agent.time_precision());
686  }
687  agent_IO(agent).activate(flow, output);
688  try {
689  agent.process_finalization_event(elapsed_dt);
690  }
691  catch (const std::exception& e) {
692  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
693  throw e;
694  }
695  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
696  agent_IO(agent).deactivate();
697  int64 missing_output = agent_IO(agent).missing_flow_output();
698  if (missing_output != -1) {
699  throw std::logic_error("Flow output port (" + agent_IO(agent).flow_output_port_name(missing_output) + ") " +
700  "of removed agent (" + agent.full_name() + ") not assigned");
701  }
702  int64 output_port_count = agent_IO(agent).flow_output_port_count();
703  for (int64 port_index = 0; port_index < output_port_count; ++port_index) {
704  const auto& flow_output_val = agent_IO(agent).flow_output_port_value(port_index);
705  agent_IO(agent).print_flow_output(port_index);
706  prototype_IO().assign_flow_output(port_index, flow_output_val);
707  }
708  agent_indices_.erase(agent_iter);
709  removed_indices_.insert(agent_index);
710  t_queue_.cancel_event(agent_index);
711  t_cache_.release_event(agent_index);
712 }
713 
714 
715 template<typename AgentID, typename Node>
716 inline void collection_node<AgentID, Node>::invoke_agent(const AgentID& agent_id)
717 {
718  if (prototype.node_dmode() == message) {
719  throw std::logic_error(std::string("Attempt to use \"invoke_agent\" to invoke a message node agent of a collection node (" + full_name() + "); ") +
720  "use \"create_agent\", \"affect_agent\", and \"remove_agent\" instead");
721  }
722  event_time().advance();
723  std::string agent_name = agent_name_from_id(qualified_type<AgentID>(), agent_id);
724  auto agent_ptr = std::make_unique<Node>(agent_name, internal_context_);
725  auto& agent = *agent_ptr;
726  int64 agent_index = agent.node_index();
727  agent.adopt_print_flags(prototype);
728  agent_IO(agent).print_event("flow");
729  int64 missing_input = prototype_IO().missing_flow_input();
730  if (missing_input != -1) {
731  throw std::logic_error("Flow input port (" + agent_IO(agent).flow_input_port_name(missing_input) + ") " +
732  "of invoked agent (" + agent.full_name() + ") has no value");
733  }
734  auto input_port_count = prototype_IO().flow_input_port_count();
735  for (int64 port_index = 0; port_index < input_port_count; ++port_index) {
736  const auto& flow_input_val = prototype_IO().flow_input_port_value(port_index);
737  if (!flow_input_val) throw std::logic_error("Flow input ports of created agent (" + agent.full_name() + ") not assigned");
738  agent_IO(agent).assign_flow_input(port_index, flow_input_val);
739  agent_IO(agent).print_flow_input(port_index);
740  }
741  agent_IO(agent).activate(flow, input);
742  try {
743  agent.process_initialization_event();
744  }
745  catch (const std::exception& e) {
746  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
747  throw e;
748  }
749  agent_IO(agent).deactivate();
750  int64 missing_output = agent_IO(agent).missing_flow_output();
751  if (missing_output != -1) {
752  throw std::logic_error("Flow output port (" + agent_IO(agent).flow_output_port_name(missing_output) + ") " +
753  "of invoked agent (" + agent.full_name() + ") not assigned");
754  }
755  int64 output_port_count = agent_IO(agent).flow_output_port_count();
756  for (int64 port_index = 0; port_index < output_port_count; ++port_index) {
757  agent_IO(agent).print_flow_output(port_index);
758  const auto& flow_output_val = agent_IO(agent).flow_output_port_value(port_index);
759  prototype_IO().assign_flow_output(port_index, flow_output_val);
760  }
761  internal_structure().erase_node(agent_index);
762 }
763 
764 
765 template<typename AgentID, typename Node>
766 inline bool collection_node<AgentID, Node>::agent_exists(const AgentID& agent_id)
767 {
768  return agent_indices_.find(agent_id) != std::end(agent_indices_);
769 }
770 
771 
772 template<typename AgentID, typename Node>
774 {
775  return int64(agent_indices_.size());
776 }
777 
778 
779 template<typename AgentID, typename Node>
781 {
782  return const_iterator(std::begin(agent_indices_));
783 }
784 
785 
786 template<typename AgentID, typename Node>
788 {
789  return const_iterator(std::end(agent_indices_));
790 }
791 
792 
793 template<typename AgentID, typename Node>
795 {
796  return internal_context_.internal_structure();
797 }
798 
799 
800 template<typename AgentID, typename Node>
801 inline node_interface& collection_node<AgentID, Node>::prototype_IO() const
802 {
803  return const_cast<node_interface&>(const_cast<Node&>(prototype).external_interface());
804 }
805 
806 
807 template<typename AgentID, typename Node>
808 inline timer& collection_node<AgentID, Node>::prototype_ET() const
809 {
810  return const_cast<timer&>(prototype.event_timer());
811 }
812 
813 
814 template<typename AgentID, typename Node>
815 inline node_interface& collection_node<AgentID, Node>::agent_IO(const system_node& agent) const
816 {
817  return const_cast<node_interface&>(const_cast<system_node&>(agent).external_interface());
818 }
819 
820 
821 template<typename AgentID, typename Node>
822 inline discrete_event_time& collection_node<AgentID, Node>::event_time()
823 {
824  return external_IO().external_context().event_time();
825 }
826 
827 
828 template<typename AgentID, typename Node>
829 template<data_mode dmode, data_goal dgoal, typename T>
830 inline void collection_node<AgentID, Node>::validate_prototype_port(const port<dmode, dgoal, T>& prototype_port)
831 {
832  if (&prototype_port.external_interface() != &prototype_IO()) {
833  throw std::logic_error("Attempt to access " + string_from_data_goal(dgoal) + " " + string_from_data_mode(dmode) + " " +
834  "port (" + prototype_port.port_name() + "), " +
835  "which is not a port of the prototype agent (" + prototype.full_name() + ")");
836  }
837  if (!external_interface().active()) {
838  throw std::logic_error("Attempt to access " + string_from_data_goal(dgoal) + " " + string_from_data_mode(dmode) + " " +
839  "port (" + prototype_port.port_name() + "), " +
840  "which is a prototype port of inactive node (" + full_name() + ")");
841  }
842 }
843 
844 
845 template<typename AgentID, typename Node>
846 inline Node& collection_node<AgentID, Node>::handle_agent_planned_event(int64 agent_index)
847 {
848  event_time().advance();
849  auto& agent = *dynamic_cast<Node*>(&internal_structure().node(agent_index));
850  agent_IO(agent).print_event("planned");
851  auto elapsed_dt = duration();
852  if (agent.time_precision() != no_scale) {
853  elapsed_dt = t_cache_.duration_since(agent_index).fixed_at(agent.time_precision());
854  }
855  agent_IO(agent).activate(message, output);
856  auto planned_dt = duration();
857  try {
858  ET().start();
859  planned_dt = agent.process_planned_event(elapsed_dt);
860  ET().stop();
861  }
862  catch (const std::exception& e) {
863  if (ET().timing()) {
864  ET().stop();
865  }
866  prototype_ET() = timer(prototype_ET().cumulative_duration() + agent.event_timer().cumulative_duration());
867  throw e;
868  }
869  agent_IO(agent).deactivate();
870  if (planned_dt.finite()) {
871  t_queue_.plan_event(agent_index, planned_dt);
872  }
873  else {
874  t_queue_.pop_imminent_event(agent_index);
875  }
876  if (agent.time_precision() != no_scale) {
877  t_cache_.retain_event(agent_index, agent.time_precision());
878  }
879  int64 list_size = agent_IO(agent).message_output_list_size();
880  for (int64 list_index = 0; list_index < list_size; ++list_index) {
881  int64 port_index = agent_IO(agent).message_output_index(list_index);
882  agent_IO(agent).print_message_output(list_index, port_index);
883  }
884  return agent;
885 }
886 
887 
888 template<typename AgentID, typename Node>
889 inline void collection_node<AgentID, Node>::erase_removed_agents()
890 {
891  for (int64 agent_index : removed_indices_) {
892  internal_structure().erase_node(agent_index);
893  agent_ids_.erase(agent_index);
894  agents_.erase(agent_index);
895  }
896  removed_indices_.clear();
897 }
898 
899 
900 template<typename AgentID, typename Node>
901 template<typename T>
902 inline std::string collection_node<AgentID, Node>::agent_name_from_id(qualified_type<T> agent_id_type, const AgentID& agent_id)
903 {
904  return tostring(agent_id);
905 }
906 
907 
908 template<typename AgentID, typename Node>
909 inline std::string collection_node<AgentID, Node>::agent_name_from_id(qualified_type<std::string> agent_id_type, const AgentID& agent_id)
910 {
911  return agent_id;
912 }
913 
914 
915 template<typename AgentID, typename Node>
916 inline void collection_node<AgentID, Node>::adopt_component_print_flags(const system_node& node) const
917 {
918  const auto node_ptr = dynamic_cast<const collection_node*>(&node);
919  if (!node_ptr) {
920  throw std::logic_error("Attempt to transfer print flags from node (" + node.full_name() + ")" +
921  "to node (" + full_name() + "), but the nodes are of different types");
922  }
923  auto& other_node = const_cast<collection_node&>(*node_ptr);
924  prototype.adopt_print_flags(other_node.prototype);
925 }
926 
927 
928 // flow port proxy members
929 
930 template<typename AgentID, typename Node>
931 template<typename T>
932 #if _WIN32
933  inline typename collection_node<AgentID, Node>::flow_port_proxy<T>& collection_node<AgentID, Node>::flow_port_proxy<T>::operator=(const T& rhs)
934 #else
936 #endif
937 {
938  external_interface_.assign_flow_input(port_index_, qualified_type<T>::copy(rhs));
939  return *this;
940 }
941 
942 
943 template<typename AgentID, typename Node>
944 template<typename T>
945 #if _WIN32
947 #else
949 #endif
950 {
951  const pointer& val = rhs.external_interface_.flow_input_port_value(rhs.port_index_);
952  external_interface_.assign_flow_input(port_index_, val);
953  return *this;
954 }
955 
956 
957 template<typename AgentID, typename Node>
958 template<typename T>
960  : external_interface_(const_cast<node_interface&>(input_port.external_interface()))
961  , port_index_(input_port.port_index())
962 {
963 }
964 
965 
966 // message port proxy members
967 
968 template<typename AgentID, typename Node>
969 template<typename T>
970 #if _WIN32
972 #else
974 #endif
975 {
976  external_interface_.set_message_input(port_index_, qualified_type<T>::copy(rhs));
977  return *this;
978 }
979 
980 
981 template<typename AgentID, typename Node>
982 template<typename T>
983 #if _WIN32
985 #else
987 #endif
988 {
989  const pointer& val = rhs.external_interface_.message_input_port_value(rhs.port_index_);
990  external_interface_.set_message_input(port_index_, val);
991  return *this;
992 }
993 
994 
995 template<typename AgentID, typename Node>
996 template<typename T>
998  : external_interface_(const_cast<node_interface&>(input_port.external_interface()))
999  , port_index_(input_port.port_index())
1000 {
1001 }
1002 
1003 
1004 // const iterator members
1005 
1006 template<typename AgentID, typename Node>
1008 {
1009  return iter_->first;
1010 }
1011 
1012 
1013 template<typename AgentID, typename Node>
1015 {
1016  return &iter_->first;
1017 }
1018 
1019 
1020 template<typename AgentID, typename Node>
1022 {
1023  ++iter_;
1024  return *this;
1025 }
1026 
1027 
1028 template<typename AgentID, typename Node>
1030 {
1031  auto old = *this;
1032  ++(*this);
1033  return old;
1034 }
1035 
1036 
1037 template<typename AgentID, typename Node>
1039 {
1040  --iter_;
1041  return *this;
1042 }
1043 
1044 
1045 template<typename AgentID, typename Node>
1047 {
1048  auto old = *this;
1049  --(*this);
1050  return old;
1051 }
1052 
1053 
1054 template<typename AgentID, typename Node>
1056 {
1057  return iter_ == rhs.iter_;
1058 }
1059 
1060 
1061 template<typename AgentID, typename Node>
1063 {
1064  return iter_ != rhs.iter_;
1065 }
1066 
1067 
1068 template<typename AgentID, typename Node>
1069 inline collection_node<AgentID, Node>::const_iterator::const_iterator(const typename std::map<AgentID, int64>::const_iterator& iter)
1070  : iter_(iter)
1071 {
1072 }
1073 
1074 
1075 } // namespace
1076 } // namespace
1077 
1078 #endif
duration handle_planned_event(duration elapsed_dt)
Invoked when the planned duration elapses for either an agent or the overall collection; calls micro_...
Definition: collection_node.h:396
arraynd< bool, ndims > operator==(const arraynd< T, ndims > &lhs, const arraynd< T, ndims > &rhs)
Definition: arraynd.h:931
const auto output
Equivalent to data_goal::output.
Definition: data_goal.h:22
std::string tostring(const T &val)
Definition: qualified_type.h:35
const std::string & node_name() const
Returns the name of the node.
Definition: system_node.h:135
bool operator!=(const const_iterator &rhs) const
Definition: collection_node.h:1062
const auto input
Equivalent to data_goal::input.
Definition: data_goal.h:21
const_iterator & operator--()
Definition: collection_node.h:1038
collection_node(const std::string &node_name, const node_context &external_context)
Constructs a collection_node.
Definition: collection_node.h:303
A data structure which represents progress through a simulation, encapsulating both simulated time an...
Definition: discrete_event_time.h:36
A generic port class template declaration.
Definition: port.h:53
duration handle_unplanned_event(duration elapsed_dt)
Invoked whenever a message is received; calls macro_unplanned_event.
Definition: collection_node.h:365
A base class for all nodes from which systems models are constructed.
Definition: system_node.h:41
void invoke_agent(const AgentID &agent_id)
Creates, invokes, and removes an agent.
Definition: collection_node.h:716
constexpr scale no_scale
Definition: scale.h:153
Definition: qualified_type.h:24
const_iterator & operator++()
Definition: collection_node.h:1021
const_iterator agent_begin()
Returns an iterator pointing to the oldest agent.
Definition: collection_node.h:780
Definition: qualified_type.h:76
A class for measuring and accumulating intervals of wallclock time.
Definition: timer.h:24
A base class for the collection node class template.
Definition: collection_node_base.h:21
void remove_agent(const AgentID &agent_id)
Finalizes and removes an agent.
Definition: collection_node.h:670
const_iterator agent_end()
Returns an iterator pointing beyond the newest agent.
Definition: collection_node.h:787
const std::string & port_name() const
Returns the name of the port.
Definition: port.h:212
constexpr auto _s
Definition: units.h:128
A data structure which provides durations elapsed since past events.
Definition: time_cache.h:27
const Node prototype
A node that serves as a proxy for all agent nodes.
Definition: collection_node.h:86
system_node & operator=(const system_node &)=delete
No copy assignment.
const std::string & string_from_data_goal(data_goal dgoal)
Returns the std::string representation of the dgoal value.
Definition: data_goal.cpp:21
flow_port_proxy< T > access(const port< flow, input, T > &prototype_port)
Returns a proxy of the flow input port prototype_port, allowing its value to be modified.
A data structure which supports the scheduling of future events.
Definition: time_queue.h:34
bool operator==(const const_iterator &rhs) const
Definition: collection_node.h:1055
Definition: collection_node.h:278
void print_on_planned_duration(bool flag=true) const
If flag is true, all planned durations are printed for this node.
Definition: collection_node.h:329
Definition: node_context.h:16
const AgentID * operator->() const
Definition: collection_node.h:1014
duration handle_initialization_event()
Invoked at the beginning of a simulation; calls macro_initialization_event.
Definition: collection_node.h:336
Definition: node_interface.h:16
A base class template for all collection nodes.
Definition: collection_node.h:55
const auto flow
Equivalent to data_mode::flow.
Definition: data_mode.h:22
void create_agent(const AgentID &agent_id)
Creates a new agent of type AgentNode.
Definition: collection_node.h:566
bool agent_exists(const AgentID &agent_id)
Returns true if an agent with ID agent_id currently exists.
Definition: collection_node.h:766
quantity< seconds > duration
Definition: quantity.h:1006
static const int64 macro_event_id
The index used to schedule macro events; it must not conflict with any of the agent indices...
Definition: collection_node.h:94
A class template for message input ports.
Definition: port.h:106
int64 agent_count()
Returns the current number of agents.
Definition: collection_node.h:773
const auto message
Equivalent to data_mode::message.
Definition: data_mode.h:23
A class template for message output ports.
Definition: port.h:145
Definition: node_structure.h:15
void handle_finalization_event(duration elapsed_dt)
Invoked at the end of a simulation; calls macro_finalization_event.
Definition: collection_node.h:469
arraynd< bool, ndims > operator!=(const arraynd< T, ndims > &lhs, const arraynd< T, ndims > &rhs)
Definition: arraynd.h:941
const pointer & flow_input_port_value(int64 port_index)
Definition: node_interface.cpp:62
const std::string & string_from_data_mode(data_mode dmode)
Returns the std::string representation of the dmode value.
Definition: data_mode.cpp:21
bool transmitted(const port< message, output, T > &prototype_port)
Returns true if a message was transmitted by an agent on the port corresponding to prototype_port...
Definition: collection_node.h:491
arraynd< T, ndims > operator*(const arraynd< T, ndims > &lhs, const arraynd< T, ndims > &rhs)
Definition: arraynd.h:796
int64_t int64
Definition: number_types.h:14
void affect_agent(const AgentID &agent_id)
Sends a message to an agent.
Definition: collection_node.h:616
A class template for flow output ports.
Definition: port.h:183
const pointer & message_input_port_value(int64 port_index)
Definition: node_interface.cpp:113
int64 port_index() const
Returns the index of the port within the encompassing node_interface object.
Definition: port.h:219
const AgentID & operator*() const
Definition: collection_node.h:1007
A class template for flow input ports.
Definition: port.h:68
void print_on_elapsed_duration(bool flag=true) const
If flag is true, all elapsed durations are printed for this node.
Definition: collection_node.h:322
const node_interface & external_interface() const
Returns the object responsible for exchanging information between the node and its surrounding contex...
Definition: system_node.h:153
virtual ~collection_node()=default
Destructor.
A data type which represents a pointer to anything.
Definition: pointer.h:27