From b8ffcfb352caecca18c89749665ee1317470917e Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 17 Aug 2021 12:29:28 +0200 Subject: [PATCH 01/12] villasnode submodule update - adapt to new C++ functions of VILLASnode - improve error handling for interface functions (throw exceptions upon error and add return value to behavior init function) - do not save redundant config infos - init node in constructor - testing required! --- include/agents/agent.h | 4 +- include/behavior/agent_behavior.h | 6 +- .../dpsim_cosim/dpsim_cosim_slack_behavior.h | 2 +- include/behavior/ensure/ensure_behavior.h | 2 +- .../mqtt_highload/mqtt_highload_behavior.h | 2 +- .../mqtt_pingpong/mqtt_pingpong_behavior.h | 2 +- .../mqtt_reference/mqtt_ref_behavior.h | 2 +- .../behavior/ref_intcomm/refic_df_behavior.h | 2 +- .../ref_intcomm/refic_prosumer_behavior.h | 2 +- .../ref_intcomm/refic_slack_behavior.h | 2 +- .../ref_intcomm/refic_substation_behavior.h | 2 +- include/behavior/reference/ref_df_behavior.h | 2 +- .../reference/ref_prosumer_behavior.h | 2 +- .../behavior/reference/ref_slack_behavior.h | 2 +- .../reference/ref_substation_behavior.h | 2 +- .../behavior/swarmgrid/swarm_df_behavior.h | 2 +- .../swarm_dpsim_cosim_slack_behavior.h | 2 +- .../swarmgrid/swarm_prosumer_behavior.h | 2 +- .../behavior/swarmgrid/swarm_slack_behavior.h | 2 +- .../swarmgrid/swarm_substation_behavior.h | 2 +- include/villas_interface/villas_interface.h | 32 +- libs/villasnode | 2 +- src/agents/agent.cpp | 12 +- src/behavior/agent_behavior.cpp | 26 +- .../dpsim_cosim_slack_behavior.cpp | 9 +- src/behavior/ensure/ensure_behavior.cpp | 5 +- .../mqtt_highload/mqtt_highload_behavior.cpp | 5 +- .../mqtt_pingpong/mqtt_pingpong_behavior.cpp | 5 +- .../mqtt_reference/mqtt_ref_behavior.cpp | 5 +- .../ref_intcomm/refic_df_behavior.cpp | 4 +- .../ref_intcomm/refic_prosumer_behavior.cpp | 4 +- .../ref_intcomm/refic_slack_behavior.cpp | 4 +- .../ref_intcomm/refic_substation_behavior.cpp | 4 +- src/behavior/reference/ref_df_behavior.cpp | 4 +- .../reference/ref_prosumer_behavior.cpp | 4 +- src/behavior/reference/ref_slack_behavior.cpp | 4 +- .../reference/ref_substation_behavior.cpp | 4 +- src/behavior/swarmgrid/swarm_df_behavior.cpp | 4 +- .../swarm_dpsim_cosim_slack_behavior.cpp | 13 +- .../swarmgrid/swarm_prosumer_behavior.cpp | 4 +- .../swarmgrid/swarm_slack_behavior.cpp | 3 +- .../swarmgrid/swarm_substation_behavior.cpp | 3 +- src/model/model.cpp | 5 +- src/model/model_config.cpp | 5 - src/model/model_init_agents.cpp | 7 +- src/villas_interface/villas_interface.cpp | 458 ++++++++---------- 46 files changed, 332 insertions(+), 349 deletions(-) diff --git a/include/agents/agent.h b/include/agents/agent.h index 00b1b82..6fb7997 100644 --- a/include/agents/agent.h +++ b/include/agents/agent.h @@ -118,7 +118,7 @@ public: /*Methods used during initialization*/ virtual void save_adjacent_agents(repast::SharedNetwork, Edge_content, Edge_content_manager >* agent_network_elec); - void init_behavior(std::vector>> * components_at_nodes, + int init_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -144,7 +144,7 @@ public: std::list get_outgoing_queue(); /*Method to disconnect villas node*/ - void villas_interface_disconnect(); + int villas_interface_disconnect(); /* Method to advance a simulation step */ virtual void step(); // virtual function, to be re-implemented in all derived agent classes diff --git a/include/behavior/agent_behavior.h b/include/behavior/agent_behavior.h index 1ad95b9..8f6442b 100644 --- a/include/behavior/agent_behavior.h +++ b/include/behavior/agent_behavior.h @@ -49,7 +49,7 @@ public: Agent_behavior(int _id, int _type, std::string _subtype, double& step_size, struct data_props _d_props); virtual ~Agent_behavior(); - virtual void initialize_agent_behavior(std::vector>> * components_at_nodes, + virtual int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -68,8 +68,8 @@ public: unsigned int get_msg_sent(); /*Methods related to villas interface*/ - void init_villas_interface(villas_node_config *_villas_config, std::vector &meta); - void destroy_villas_interface(); + int init_villas_interface(villas_node_config *_villas_config, std::vector &meta); + int destroy_villas_interface(); protected: /* message processing */ diff --git a/include/behavior/dpsim_cosim/dpsim_cosim_slack_behavior.h b/include/behavior/dpsim_cosim/dpsim_cosim_slack_behavior.h index 8cd0a27..9b9473c 100644 --- a/include/behavior/dpsim_cosim/dpsim_cosim_slack_behavior.h +++ b/include/behavior/dpsim_cosim/dpsim_cosim_slack_behavior.h @@ -33,7 +33,7 @@ public: villas_node_config * _villas_config, bool _realtime, bool _sync); ~Dpsim_cosim_slack_behavior()= default; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/ensure/ensure_behavior.h b/include/behavior/ensure/ensure_behavior.h index 2432a17..d3cf90d 100644 --- a/include/behavior/ensure/ensure_behavior.h +++ b/include/behavior/ensure/ensure_behavior.h @@ -38,7 +38,7 @@ public: virtual ~Ensure_behavior() override; protected: - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/mqtt_highload/mqtt_highload_behavior.h b/include/behavior/mqtt_highload/mqtt_highload_behavior.h index 4fd2205..bd5ed39 100644 --- a/include/behavior/mqtt_highload/mqtt_highload_behavior.h +++ b/include/behavior/mqtt_highload/mqtt_highload_behavior.h @@ -37,7 +37,7 @@ public: villas_node_config * _villas_config); virtual ~Mqtt_highload_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/mqtt_pingpong/mqtt_pingpong_behavior.h b/include/behavior/mqtt_pingpong/mqtt_pingpong_behavior.h index ee6f2a5..470f922 100644 --- a/include/behavior/mqtt_pingpong/mqtt_pingpong_behavior.h +++ b/include/behavior/mqtt_pingpong/mqtt_pingpong_behavior.h @@ -38,7 +38,7 @@ public: virtual ~Mqtt_pingpong_behavior() override; protected: - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/mqtt_reference/mqtt_ref_behavior.h b/include/behavior/mqtt_reference/mqtt_ref_behavior.h index 13ce399..550265f 100644 --- a/include/behavior/mqtt_reference/mqtt_ref_behavior.h +++ b/include/behavior/mqtt_reference/mqtt_ref_behavior.h @@ -37,7 +37,7 @@ public: villas_node_config * _villas_config); virtual ~Mqtt_ref_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/ref_intcomm/refic_df_behavior.h b/include/behavior/ref_intcomm/refic_df_behavior.h index 1cf15c3..299eefc 100644 --- a/include/behavior/ref_intcomm/refic_df_behavior.h +++ b/include/behavior/ref_intcomm/refic_df_behavior.h @@ -35,7 +35,7 @@ public: int rank); ~Refic_df_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/ref_intcomm/refic_prosumer_behavior.h b/include/behavior/ref_intcomm/refic_prosumer_behavior.h index 6e7b3ae..9f09419 100644 --- a/include/behavior/ref_intcomm/refic_prosumer_behavior.h +++ b/include/behavior/ref_intcomm/refic_prosumer_behavior.h @@ -35,7 +35,7 @@ public: Prosumer_data * _prosumer_data); ~Refic_prosumer_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/ref_intcomm/refic_slack_behavior.h b/include/behavior/ref_intcomm/refic_slack_behavior.h index 19aa889..69d1748 100644 --- a/include/behavior/ref_intcomm/refic_slack_behavior.h +++ b/include/behavior/ref_intcomm/refic_slack_behavior.h @@ -34,7 +34,7 @@ public: Refic_slack_behavior(int _id, int _type, std::string _subtype, double& step_size, struct data_props _d_props); ~Refic_slack_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/ref_intcomm/refic_substation_behavior.h b/include/behavior/ref_intcomm/refic_substation_behavior.h index 9f5b71f..329823e 100644 --- a/include/behavior/ref_intcomm/refic_substation_behavior.h +++ b/include/behavior/ref_intcomm/refic_substation_behavior.h @@ -36,7 +36,7 @@ public: Substation_data * _substation_data); ~Refic_substation_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/reference/ref_df_behavior.h b/include/behavior/reference/ref_df_behavior.h index 1e93bc4..23c5d5e 100644 --- a/include/behavior/reference/ref_df_behavior.h +++ b/include/behavior/reference/ref_df_behavior.h @@ -35,7 +35,7 @@ public: int rank); ~Ref_df_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/reference/ref_prosumer_behavior.h b/include/behavior/reference/ref_prosumer_behavior.h index 6bb63c6..f432033 100644 --- a/include/behavior/reference/ref_prosumer_behavior.h +++ b/include/behavior/reference/ref_prosumer_behavior.h @@ -36,7 +36,7 @@ public: Prosumer_data * _prosumer_data); ~Ref_prosumer_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/reference/ref_slack_behavior.h b/include/behavior/reference/ref_slack_behavior.h index 3bc97fa..0718b0b 100644 --- a/include/behavior/reference/ref_slack_behavior.h +++ b/include/behavior/reference/ref_slack_behavior.h @@ -34,7 +34,7 @@ public: Ref_slack_behavior(int _id, int _type, std::string _subtype, double& step_size, struct data_props _d_props); ~Ref_slack_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/reference/ref_substation_behavior.h b/include/behavior/reference/ref_substation_behavior.h index 2ffbcff..c2f7da7 100644 --- a/include/behavior/reference/ref_substation_behavior.h +++ b/include/behavior/reference/ref_substation_behavior.h @@ -36,7 +36,7 @@ public: Substation_data * _substation_data); ~Ref_substation_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/swarmgrid/swarm_df_behavior.h b/include/behavior/swarmgrid/swarm_df_behavior.h index a79445f..e76b30b 100644 --- a/include/behavior/swarmgrid/swarm_df_behavior.h +++ b/include/behavior/swarmgrid/swarm_df_behavior.h @@ -37,7 +37,7 @@ public: DF_data * _df_data, int _rank); ~Swarm_df_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/swarmgrid/swarm_dpsim_cosim_slack_behavior.h b/include/behavior/swarmgrid/swarm_dpsim_cosim_slack_behavior.h index b134ed6..ad15fdb 100644 --- a/include/behavior/swarmgrid/swarm_dpsim_cosim_slack_behavior.h +++ b/include/behavior/swarmgrid/swarm_dpsim_cosim_slack_behavior.h @@ -42,7 +42,7 @@ public: ~Swarm_dpsim_cosim_slack_behavior(); - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/swarmgrid/swarm_prosumer_behavior.h b/include/behavior/swarmgrid/swarm_prosumer_behavior.h index 7c72626..a9a02a2 100644 --- a/include/behavior/swarmgrid/swarm_prosumer_behavior.h +++ b/include/behavior/swarmgrid/swarm_prosumer_behavior.h @@ -39,7 +39,7 @@ public: Prosumer_data * _prosumer_data); virtual ~Swarm_prosumer_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/swarmgrid/swarm_slack_behavior.h b/include/behavior/swarmgrid/swarm_slack_behavior.h index 0f08028..0302ca5 100644 --- a/include/behavior/swarmgrid/swarm_slack_behavior.h +++ b/include/behavior/swarmgrid/swarm_slack_behavior.h @@ -37,7 +37,7 @@ public: Slack_data * _slack_data); virtual ~Swarm_slack_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/behavior/swarmgrid/swarm_substation_behavior.h b/include/behavior/swarmgrid/swarm_substation_behavior.h index 6f5d872..3f149d9 100644 --- a/include/behavior/swarmgrid/swarm_substation_behavior.h +++ b/include/behavior/swarmgrid/swarm_substation_behavior.h @@ -41,7 +41,7 @@ public: Substation_data * _substation_data); virtual ~Swarm_substation_behavior() override; - void initialize_agent_behavior(std::vector>> * components_at_nodes, + int initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, diff --git a/include/villas_interface/villas_interface.h b/include/villas_interface/villas_interface.h index adcf8a6..8683671 100644 --- a/include/villas_interface/villas_interface.h +++ b/include/villas_interface/villas_interface.h @@ -27,11 +27,9 @@ #include #include "villas_interface/villas_message.h" - - //forward declarations -struct node; -struct node_type; +struct vnode; +struct vnode_type; struct pool; class IO_object; @@ -62,23 +60,18 @@ struct mqtt_data{ std::string publish; std::string subscribe; int qos; - bool enabled; }; /*! \brief Struct that holds general info about the villas node type nanomsg */ struct nanomsg_data{ - int in_socket; std::list in_endpoints; - int out_socket; std::list out_endpoints; - bool enabled; }; /*! \brief Struct that holds general info about the villas node */ struct villas_node_config{ std::string type_name; std::string format_name; - std::string n_name; std::string loglevel; bool with_node; double stop_at; // used to synchronize end of simulation in cosimulation @@ -92,7 +85,7 @@ struct villas_node_config{ class Villas_interface{ public: - Villas_interface(villas_node_config *_config, IO_object *_IO, std::string _name, std::vector &_meta); + Villas_interface(villas_node_config *_config, IO_object *IO_obj, std::string _name, std::vector &_meta); ~Villas_interface(); //methods to send/receive data via villas node @@ -102,7 +95,6 @@ public: int start(); int stop(); //methods to init/destroy the villas node - int init(); int destroy(); //methods to control villas node type (used by model ONLY) @@ -112,32 +104,26 @@ public: private: /*VillasNode struct*/ - struct node *n; - struct node_type *type; + struct vnode *n; + struct vnode_type *type; //Memory pool struct pool *p; - struct villas_node_config vn_config; + // meta infos std::vector meta; //counting number of sent messages for sequence numbers unsigned int number_of_sent_messages; + bool with_node; //for logging into agent log file IO_object *IO; - //MQTT specific config - void configure_node_mqtt(); - - //Nanomsg specific config - void configure_node_nanomsg(); - - void allocate_and_receive(unsigned long number_of_messages, std::list &messages); - + void allocate_and_receive(int number_of_messages, std::list &messages); //Behavior specific methods void set_signals(); }; -#endif //DISTAIX_VILLAS_INTERFACE_H +#endif //VILLAS_INTERFACE_H diff --git a/libs/villasnode b/libs/villasnode index f2f4ca8..aa0e05e 160000 --- a/libs/villasnode +++ b/libs/villasnode @@ -1 +1 @@ -Subproject commit f2f4ca8efd10bd9524c4ba718a17b30334b4308e +Subproject commit aa0e05e8a1a5095659c4e61f5fa4041ea3b07de0 diff --git a/src/agents/agent.cpp b/src/agents/agent.cpp index 70b71d1..7a7df3c 100644 --- a/src/agents/agent.cpp +++ b/src/agents/agent.cpp @@ -345,24 +345,26 @@ std::list Agent::get_outgoing_queue() { * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Agent::init_behavior(std::vector>> *components_at_nodes, +int Agent::init_behavior(std::vector>> *components_at_nodes, std::vector>> *connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - behavior->initialize_agent_behavior(components_at_nodes, connected_nodes, components_file, + int ret = behavior->initialize_agent_behavior(components_at_nodes, connected_nodes, components_file, subtypes_file, el_grid_file); + return ret; } -void Agent::villas_interface_disconnect() { +int Agent::villas_interface_disconnect() { if (behavior != nullptr) { //destroy villas interface if this agent has a behavior - behavior->destroy_villas_interface(); + int ret = behavior->destroy_villas_interface(); + return ret; } - + return 0; } /*! \brief Set pointers of time measurements (called by model class for each agent) diff --git a/src/behavior/agent_behavior.cpp b/src/behavior/agent_behavior.cpp index 55bde42..43be87e 100644 --- a/src/behavior/agent_behavior.cpp +++ b/src/behavior/agent_behavior.cpp @@ -145,21 +145,33 @@ unsigned int Agent_behavior::get_msg_received() { } -void Agent_behavior::init_villas_interface(villas_node_config *_villas_config, std::vector &meta) { +int Agent_behavior::init_villas_interface(villas_node_config *_villas_config, std::vector &meta) { //init and start villas node interface IO->log_info("Setting up villas interface"); - villas_interface = new Villas_interface(_villas_config, IO, "agent_" + std::to_string(id), meta); - villas_interface->init(); - villas_interface->start(); + try{ + villas_interface = new Villas_interface(_villas_config, IO, "agent_" + std::to_string(id), meta); + //villas_interface->init(); + villas_interface->start(); + } catch (std::runtime_error &ex) { + IO->log_info("Caught exception: " + std::string(ex.what())); + return -1; + } + return 0; } -void Agent_behavior::destroy_villas_interface() { +int Agent_behavior::destroy_villas_interface() { if(villas_interface !=nullptr) { //init and start villas node interface IO->log_info("Destroying villas interface"); - villas_interface->stop(); - villas_interface->destroy(); + try { + villas_interface->stop(); + villas_interface->destroy(); + } catch (std::runtime_error &ex) { + IO->log_info("Caught exception: " + std::string(ex.what())); + return -1; + } delete villas_interface; } + return 0; } \ No newline at end of file diff --git a/src/behavior/dpsim_cosim/dpsim_cosim_slack_behavior.cpp b/src/behavior/dpsim_cosim/dpsim_cosim_slack_behavior.cpp index 35baa14..341071f 100644 --- a/src/behavior/dpsim_cosim/dpsim_cosim_slack_behavior.cpp +++ b/src/behavior/dpsim_cosim/dpsim_cosim_slack_behavior.cpp @@ -119,7 +119,7 @@ void Dpsim_cosim_slack_behavior::synchronize_with_remote(){ * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Dpsim_cosim_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Dpsim_cosim_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -141,12 +141,17 @@ void Dpsim_cosim_slack_behavior::initialize_agent_behavior(std::vectorlog_info("Init VILLAS interface..."); - init_villas_interface(vnconfig, Dpsim_cosim_msg::dpsim_cosim_message_meta); + int ret = init_villas_interface(vnconfig, Dpsim_cosim_msg::dpsim_cosim_message_meta); + if (ret) { + return ret; + } if(sync){ synchronize_with_remote(); } + return 0; + } diff --git a/src/behavior/ensure/ensure_behavior.cpp b/src/behavior/ensure/ensure_behavior.cpp index 21446d1..77106df 100644 --- a/src/behavior/ensure/ensure_behavior.cpp +++ b/src/behavior/ensure/ensure_behavior.cpp @@ -82,7 +82,7 @@ void Ensure_behavior::process_incoming_messages() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Ensure_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Ensure_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -97,7 +97,8 @@ void Ensure_behavior::initialize_agent_behavior(std::vectortype_config.mqtt_conf->publish = "agent_"+std::to_string(id)+"_sen"; //Initialize villas interface (important: after init of message meta info!) - init_villas_interface(vnconfig, Ensure_msg::ensure_msg_meta); + int ret = init_villas_interface(vnconfig, Ensure_msg::ensure_msg_meta); + return ret; } diff --git a/src/behavior/mqtt_highload/mqtt_highload_behavior.cpp b/src/behavior/mqtt_highload/mqtt_highload_behavior.cpp index 4454ab9..deb3dbd 100644 --- a/src/behavior/mqtt_highload/mqtt_highload_behavior.cpp +++ b/src/behavior/mqtt_highload/mqtt_highload_behavior.cpp @@ -59,7 +59,7 @@ Mqtt_highload_behavior::~Mqtt_highload_behavior(){ * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Mqtt_highload_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Mqtt_highload_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -71,7 +71,8 @@ void Mqtt_highload_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Mqtt_pingpong_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -99,7 +99,8 @@ void Mqtt_pingpong_behavior::initialize_agent_behavior(std::vectortype_config.mqtt_conf->keepalive = 1000; //set to high value to avoid keepalive messages //Initialize villas interface (important: after init of message meta info!) - init_villas_interface(vnconfig, Mqtt_pingpong_msg::mqtt_pingpong_msg_meta); + int ret = init_villas_interface(vnconfig, Mqtt_pingpong_msg::mqtt_pingpong_msg_meta); + return ret; } diff --git a/src/behavior/mqtt_reference/mqtt_ref_behavior.cpp b/src/behavior/mqtt_reference/mqtt_ref_behavior.cpp index fadc5c3..94eb7d1 100644 --- a/src/behavior/mqtt_reference/mqtt_ref_behavior.cpp +++ b/src/behavior/mqtt_reference/mqtt_ref_behavior.cpp @@ -59,7 +59,7 @@ Mqtt_ref_behavior::~Mqtt_ref_behavior(){ * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Mqtt_ref_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Mqtt_ref_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -71,7 +71,8 @@ void Mqtt_ref_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Refic_df_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - //nothing to do + return 0; } /*! \brief DF has nothing to do in reference behavior diff --git a/src/behavior/ref_intcomm/refic_prosumer_behavior.cpp b/src/behavior/ref_intcomm/refic_prosumer_behavior.cpp index 3ea611b..87fbe23 100644 --- a/src/behavior/ref_intcomm/refic_prosumer_behavior.cpp +++ b/src/behavior/ref_intcomm/refic_prosumer_behavior.cpp @@ -54,7 +54,7 @@ Refic_prosumer_behavior::~Refic_prosumer_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Refic_prosumer_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Refic_prosumer_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -78,6 +78,8 @@ void Refic_prosumer_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Refic_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - //nothing to do + return 0; } /*! \brief Slack agent has nothing to do in reference behavior diff --git a/src/behavior/ref_intcomm/refic_substation_behavior.cpp b/src/behavior/ref_intcomm/refic_substation_behavior.cpp index fc6ed08..744b4fd 100644 --- a/src/behavior/ref_intcomm/refic_substation_behavior.cpp +++ b/src/behavior/ref_intcomm/refic_substation_behavior.cpp @@ -53,12 +53,12 @@ Refic_substation_behavior::~Refic_substation_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Refic_substation_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Refic_substation_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - //nothing to do + return 0; } /*! \brief Substation reference behavior execution diff --git a/src/behavior/reference/ref_df_behavior.cpp b/src/behavior/reference/ref_df_behavior.cpp index 51c874f..c5bb1a7 100644 --- a/src/behavior/reference/ref_df_behavior.cpp +++ b/src/behavior/reference/ref_df_behavior.cpp @@ -53,12 +53,12 @@ Ref_df_behavior::~Ref_df_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Ref_df_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Ref_df_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - //nothing to do + return 0; } /*! \brief DF has nothing to do in reference behavior diff --git a/src/behavior/reference/ref_prosumer_behavior.cpp b/src/behavior/reference/ref_prosumer_behavior.cpp index 50c75d2..c48afba 100644 --- a/src/behavior/reference/ref_prosumer_behavior.cpp +++ b/src/behavior/reference/ref_prosumer_behavior.cpp @@ -53,12 +53,12 @@ Ref_prosumer_behavior::~Ref_prosumer_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Ref_prosumer_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Ref_prosumer_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - //nothing to do + return 0; } /*! \brief adjusts control values according to simple rules not requiring communication diff --git a/src/behavior/reference/ref_slack_behavior.cpp b/src/behavior/reference/ref_slack_behavior.cpp index 8742580..2eb9244 100644 --- a/src/behavior/reference/ref_slack_behavior.cpp +++ b/src/behavior/reference/ref_slack_behavior.cpp @@ -52,12 +52,12 @@ Ref_slack_behavior::~Ref_slack_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Ref_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Ref_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - //nothing to do + return 0; } /*! \brief Slack agent has nothing to do in reference behavior diff --git a/src/behavior/reference/ref_substation_behavior.cpp b/src/behavior/reference/ref_substation_behavior.cpp index 77d407f..be8726e 100644 --- a/src/behavior/reference/ref_substation_behavior.cpp +++ b/src/behavior/reference/ref_substation_behavior.cpp @@ -53,12 +53,12 @@ Ref_substation_behavior::~Ref_substation_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Ref_substation_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Ref_substation_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - //nothing to do + return 0; } /*! \brief Substation reference behavior execution diff --git a/src/behavior/swarmgrid/swarm_df_behavior.cpp b/src/behavior/swarmgrid/swarm_df_behavior.cpp index 541fafc..17465c6 100644 --- a/src/behavior/swarmgrid/swarm_df_behavior.cpp +++ b/src/behavior/swarmgrid/swarm_df_behavior.cpp @@ -57,12 +57,12 @@ Swarm_df_behavior::~Swarm_df_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Swarm_df_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Swarm_df_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { - //nothing to do + return 0; } /*! \brief processes messages diff --git a/src/behavior/swarmgrid/swarm_dpsim_cosim_slack_behavior.cpp b/src/behavior/swarmgrid/swarm_dpsim_cosim_slack_behavior.cpp index 27235bf..b0ec2a3 100644 --- a/src/behavior/swarmgrid/swarm_dpsim_cosim_slack_behavior.cpp +++ b/src/behavior/swarmgrid/swarm_dpsim_cosim_slack_behavior.cpp @@ -133,14 +133,17 @@ void Swarm_dpsim_cosim_slack_behavior::synchronize_with_remote(){ } } -void Swarm_dpsim_cosim_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Swarm_dpsim_cosim_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, boost::multi_array *el_grid_file) { // Initialize swarm behavior - Swarm_slack_behavior::initialize_agent_behavior(components_at_nodes, connected_nodes, components_file, subtypes_file, el_grid_file); + int ret = Swarm_slack_behavior::initialize_agent_behavior(components_at_nodes, connected_nodes, components_file, subtypes_file, el_grid_file); + if (ret){ + return ret; + } // Initialize cosim behavior // Initialize meta information for all message types used in this behavior @@ -160,11 +163,15 @@ void Swarm_dpsim_cosim_slack_behavior::initialize_agent_behavior(std::vectorlog_info("Init VILLAS interface..."); - init_villas_interface(vnconfig, Dpsim_cosim_msg::dpsim_cosim_message_meta); + ret = init_villas_interface(vnconfig, Dpsim_cosim_msg::dpsim_cosim_message_meta); + if (ret){ + return ret; + } if(sync){ synchronize_with_remote(); } + return 0; } void Swarm_dpsim_cosim_slack_behavior::report_to_superior_grid(Dpsim_cosim_msg message, std::list *incoming_villas_messages){ diff --git a/src/behavior/swarmgrid/swarm_prosumer_behavior.cpp b/src/behavior/swarmgrid/swarm_prosumer_behavior.cpp index 032a17d..22099c3 100644 --- a/src/behavior/swarmgrid/swarm_prosumer_behavior.cpp +++ b/src/behavior/swarmgrid/swarm_prosumer_behavior.cpp @@ -54,7 +54,7 @@ Swarm_prosumer_behavior::~Swarm_prosumer_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Swarm_prosumer_behavior::initialize_agent_behavior( +int Swarm_prosumer_behavior::initialize_agent_behavior( std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, @@ -145,6 +145,8 @@ void Swarm_prosumer_behavior::initialize_agent_behavior( IO->log_info("### FINISHED Determining initial communication partners "); + return 0; + } /*! \brief processes messages, executes behavior rules, adjusts control values diff --git a/src/behavior/swarmgrid/swarm_slack_behavior.cpp b/src/behavior/swarmgrid/swarm_slack_behavior.cpp index dba693f..c157dfb 100644 --- a/src/behavior/swarmgrid/swarm_slack_behavior.cpp +++ b/src/behavior/swarmgrid/swarm_slack_behavior.cpp @@ -54,7 +54,7 @@ Swarm_slack_behavior::~Swarm_slack_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Swarm_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, +int Swarm_slack_behavior::initialize_agent_behavior(std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, boost::multi_array *subtypes_file, @@ -88,6 +88,7 @@ void Swarm_slack_behavior::initialize_agent_behavior(std::vectorlog_info("### FINISHED Determining initial communication partners "); + return 0; } /*! \brief processes messages, executes behavior rules, adjusts control values diff --git a/src/behavior/swarmgrid/swarm_substation_behavior.cpp b/src/behavior/swarmgrid/swarm_substation_behavior.cpp index c9b9e3a..254149d 100644 --- a/src/behavior/swarmgrid/swarm_substation_behavior.cpp +++ b/src/behavior/swarmgrid/swarm_substation_behavior.cpp @@ -55,7 +55,7 @@ Swarm_substation_behavior::~Swarm_substation_behavior() * \param subtypes_file [in] subtypes contained in scenario components input file (strings) * \param el_grid_file [in] electrical connections contained in el. grid input file (integers) * */ -void Swarm_substation_behavior::initialize_agent_behavior( +int Swarm_substation_behavior::initialize_agent_behavior( std::vector>> * components_at_nodes, std::vector>> * connected_nodes, boost::multi_array *components_file, @@ -225,6 +225,7 @@ void Swarm_substation_behavior::initialize_agent_behavior( knowledge_comm.add_swarmmember(TYPE_DF_INT, TYPE_DF_INT, 1); IO->log_info("### FINISHED Determining initial communication partners "); + return 0; } /*! \brief processes messages, executes behavior rules, adjusts control values diff --git a/src/model/model.cpp b/src/model/model.cpp index e8f94fd..22275c7 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -112,7 +112,10 @@ Model::~Model(){ //disconnect villas_interface of all all agents for(auto a : local_agents){ IO->log_info("Disconnecting agent " + IO->id2str(a->getId())); - a->villas_interface_disconnect(); + int ret = a->villas_interface_disconnect(); + if (ret) { + IO->log_info("Error upon disconnecting agent " + IO->id2str(a->getId()) + ": " + std::to_string(ret)); + } } struct timespec t; t.tv_sec = 0; diff --git a/src/model/model_config.cpp b/src/model/model_config.cpp index b09871b..a9bd283 100644 --- a/src/model/model_config.cpp +++ b/src/model/model_config.cpp @@ -127,7 +127,6 @@ void Model::read_villas_config() { //create villas config with disabled villas node (used in all agents that never contain a villas node) villas_config_node_disabled = new villas_node_config(); villas_config_node_disabled->with_node = false; - villas_config_node_disabled->n_name = ""; villas_config_node_disabled->type_name = ""; villas_config_node_disabled->format_name = ""; villas_config_node_disabled->loglevel = ""; @@ -148,7 +147,6 @@ void Model::read_villas_config() { villas_config_model->format_name = props->getProperty("villas.format"); villas_config_model->loglevel = props->getProperty("villas.loglevel"); villas_config_model->with_node = false; - villas_config_model->n_name = ""; //no node contained here --> no name provided! villas_config_model->stop_at = stop_at; // villas node configs for agents (independent of node type) @@ -157,7 +155,6 @@ void Model::read_villas_config() { villas_config_agents->format_name = villas_config_model->format_name; villas_config_agents->loglevel = villas_config_model->loglevel; villas_config_agents->with_node = true; - villas_config_agents->n_name = "PLACEHOLDER_AGENT_NAME"; villas_config_agents->stop_at = villas_config_model->stop_at; //parse the properties of the node type configuration for the agents @@ -165,7 +162,6 @@ void Model::read_villas_config() { IO->log_info("Reading VILLAS config for MQTT"); villas_config_agents->type_config.mqtt_conf = new mqtt_data(); - villas_config_agents->type_config.mqtt_conf->enabled = true; villas_config_agents->type_config.mqtt_conf->broker = props->getProperty( "villas.mqtt.broker"); villas_config_agents->type_config.mqtt_conf->port = repast::strToInt( @@ -188,7 +184,6 @@ void Model::read_villas_config() { IO->log_info("Reading VILLAS config for nanomsg"); villas_config_agents->type_config.nanomsg_conf = new nanomsg_data(); - villas_config_agents->type_config.nanomsg_conf->enabled = true; villas_config_agents->type_config.nanomsg_conf->in_endpoints.push_back(props->getProperty( "villas.nanomsg.in_endpoints")); villas_config_agents->type_config.nanomsg_conf->out_endpoints.push_back(props->getProperty( diff --git a/src/model/model_init_agents.cpp b/src/model/model_init_agents.cpp index 0270d69..9121e3e 100644 --- a/src/model/model_init_agents.cpp +++ b/src/model/model_init_agents.cpp @@ -200,9 +200,14 @@ void Model::init_agent_behaviors() { IO->log_info("Init behavior of agent: " + IO->id2str(i->getId())); IO->log_info(" Agent Type: " + std::to_string(i->getId().agentType())); IO->log_info(" Behavior Type: " + std::to_string(i->behavior_type)); - i->init_behavior(&components_at_nodes, &connected_nodes, + int ret = i->init_behavior(&components_at_nodes, &connected_nodes, &scenario_file_components_content, &scenario_file_components_subtypes, &scenario_file_el_grid_content); + + if (ret){ + // something went wrong during behavior init + do_exit(55); + } } else if(i->getId().agentType() == TYPE_MR_INT){ //initialize count matrix in MR Agent (memory allocated only if use_commdata = true) diff --git a/src/villas_interface/villas_interface.cpp b/src/villas_interface/villas_interface.cpp index 26419af..8cafbd9 100644 --- a/src/villas_interface/villas_interface.cpp +++ b/src/villas_interface/villas_interface.cpp @@ -23,7 +23,6 @@ #include "villas_interface/villas_interface.h" -#include #include #include #include "model/io_object.h" @@ -33,7 +32,7 @@ #include "villas/nodes/mqtt.hpp" #include "villas/nodes/nanomsg.hpp" #include "villas/node.h" - #include "villas/format_type.h" + #include "villas/format.hpp" #include "villas/signal.h" #include "villas/log.hpp" #endif @@ -43,26 +42,20 @@ /*! \brief Constructor of Villas_interface class * * */ -Villas_interface::Villas_interface(villas_node_config *_config, IO_object *_IO, std::string _name, std::vector &_meta) { +Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_obj, std::string _name, std::vector &_meta) { - IO = _IO; //use IO object of holing instance + IO = IO_obj; //use IO object of holing instance IO->log_info("Villas_interface: constructor for node " + _name); #ifdef WITH_VILLAS meta = _meta; number_of_sent_messages = 0; - vn_config.n_name = _name; //_config->n_name; - vn_config.format_name = _config->format_name; - vn_config.type_name = _config->type_name; - vn_config.with_node = _config->with_node; - vn_config.loglevel = _config->loglevel; - - + with_node = _config->with_node; //ranks set logging properties for VILLASnode lib - if(vn_config.n_name.find("rank_") != std::string::npos){ - json_t *json_logging = nullptr; + if(_name.find("rank_") != std::string::npos){ + json_t *json_logging; json_error_t * j_err = nullptr; // available log levels of VILLASnode lib: @@ -75,7 +68,7 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *_IO, // off : no logging at all //json_logging = json_loads("{\"level\" : \"warning\", \"file\" : \"villas.log\"}", 0, j_err); - std::string params = "{\"level\" : \"" + vn_config.loglevel + "\"}"; + std::string params = R"({"level" : ")" + _config->loglevel + "\"}"; json_logging = json_loads(params.c_str(), 0, j_err); villas::logging.parse(json_logging); @@ -83,22 +76,22 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *_IO, } //lookup node type - IO->log_info("Villas_interface: Node type lookup for node " + _name + " " + vn_config.type_name.c_str() + "."); - type = node_type_lookup(vn_config.type_name.c_str()); + IO->log_info("Villas_interface: Node type lookup for node " + _name + " Type: " + _config->type_name + "."); + type = node_type_lookup(_config->type_name); if(type== nullptr){ IO->log_info("\"Villas_interface: ERROR: something went wrong in node type lookup"); } - if(vn_config.with_node) { + if(with_node) { + int ret; //for return values of villas functions + //set several states of node internal objects to STATE_DESTROYED - n = (struct node*) malloc(sizeof(struct node)); - n->state = State::DESTROYED; - n->in.state = State::DESTROYED; - n->out.state = State::DESTROYED; - n->in.signals.state = State::DESTROYED; - n->out.signals.state = State::DESTROYED; - n->in.hooks.state = State::DESTROYED; - n->out.hooks.state = State::DESTROYED; + //n = (struct vnode*) malloc(sizeof(struct vnode)); + //n->state = State::DESTROYED; + //n->in.state = State::DESTROYED; + //n->out.state = State::DESTROYED; + //n->in.signals.state = State::DESTROYED; + //n->out.signals.state = State::DESTROYED; //Init memory pool for this node (used to get memory for sample in send_message) p = (struct pool*) malloc(sizeof(struct pool)); @@ -109,104 +102,160 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *_IO, //1 MiB = 1048576 size_t blocksize=SAMPLE_LENGTH(meta.size()); //sizeof(struct sample) + meta.size()* sizeof(double); IO->log_info("Villas_interface: Memory pool allocation for " + std::to_string(meta.size())+ " signals and blocksize=" + std::to_string(blocksize)); - pool_init(p, 1024, blocksize, &(memory_heap)); + ret = pool_init(p, 1024, blocksize, &(memory_heap)); + if (ret) { + //IO->log_info("Villas_interface: pool_init failed for node " + _name + + //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + throw std::runtime_error("Villas_interface: pool_init failed for node " + _name + + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + } + //parse configuration of node IO->log_info("Villas_interface: node configuration"); - if (_config->type_name == "mqtt") { - vn_config.type_config.mqtt_conf = new mqtt_data(); - vn_config.type_config.mqtt_conf->enabled = _config->type_config.mqtt_conf->enabled; - vn_config.type_config.mqtt_conf->ssl_enabled = _config->type_config.mqtt_conf->ssl_enabled; - vn_config.type_config.mqtt_conf->subscribe = _config->type_config.mqtt_conf->subscribe; - vn_config.type_config.mqtt_conf->publish = _config->type_config.mqtt_conf->publish; - vn_config.type_config.mqtt_conf->keepalive = _config->type_config.mqtt_conf->keepalive; - vn_config.type_config.mqtt_conf->retain = _config->type_config.mqtt_conf->retain; - vn_config.type_config.mqtt_conf->broker = _config->type_config.mqtt_conf->broker; - vn_config.type_config.mqtt_conf->port = _config->type_config.mqtt_conf->port; - vn_config.type_config.mqtt_conf->qos = _config->type_config.mqtt_conf->qos; - - } else if (_config->type_name == "nanomsg") { - vn_config.type_config.nanomsg_conf = new nanomsg_data(); - vn_config.type_config.nanomsg_conf->in_endpoints = _config->type_config.nanomsg_conf->in_endpoints; - vn_config.type_config.nanomsg_conf->out_endpoints = _config->type_config.nanomsg_conf->out_endpoints; - } else { - IO->log_info( - "Villas_interface: Error: node_type_configuration does not contain " - "config for supported node type" + vn_config.type_name); + ret = node_init(n, type); + if (ret) { + //IO->log_info("Villas_interface: node_init failed for node " + _name + + //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + throw std::runtime_error("Villas_interface: node_init failed for node " + _name + + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); } - } -#else - //Warn the user - IO->log_info("ERROR: Your agent behavior is trying to instantiate a Villas_interface, but DistAIX is compiled without VILLASnode support.\n" - "This will very likely cause a simulation failure!\n" - "Pass option -villas to build_and_install.sh script to compile DistAIX with VILLASnode support"); - std::cerr << "ERROR: Your agent behavior is trying to instantiate a Villas_interface, but DistAIX is compiled without VILLASnode support." << std::endl; - std::cerr << "This will very likely cause a simulation failure!" << std::endl; - std::cerr << "Pass option -villas to build_and_install.sh script to compile DistAIX with VILLASnode support" << std::endl; -#endif -} + n->name = strdup(_name.c_str()); + n->in.enabled = 1; + n->out.enabled = 1; + n->out.vectorize = 1; //only one sample at a time + n->in.vectorize = 1; + if(std::string(type->name) == "mqtt") { + //configure the node for mqtt -/*! \brief Destructor of Villas_interface class - * - * */ -Villas_interface::~Villas_interface() { -#ifdef WITH_VILLAS - if(vn_config.with_node) { - pool_destroy(p); - } -#endif -} + //set mqtt parameters + auto *m = (mqtt *) n->_vd; -/*! \brief Initialize a Villas_interface - * - * */ -int Villas_interface::init() { -#ifdef WITH_VILLAS - int ret = 0; //for return values of villas functions - IO->log_info("Villas_interface::init: initializing villas node " + vn_config.n_name); - if(vn_config.with_node) { - ret = node_init(n, type); - if (ret) { - IO->log_info("Villas_interface::init: node_init failed for node " + vn_config.n_name + - " and node type " + vn_config.type_name); - return ret; - } - if(vn_config.type_name == "mqtt") { - configure_node_mqtt(); //configure the node for mqtt + //set broker + m->host = strdup(_config->type_config.mqtt_conf->broker.c_str()) ; + + //set publish topic (if any) + if(!_config->type_config.mqtt_conf->publish.empty()){ + m->publish = strdup(_config->type_config.mqtt_conf->publish.c_str()); + } else{ + m->publish = nullptr; + } + //set subscribe topic (if any) + if(!_config->type_config.mqtt_conf->subscribe.empty()){ + m->subscribe = strdup(_config->type_config.mqtt_conf->subscribe.c_str()); + } else{ + m->subscribe = nullptr; + } + + m->username = nullptr; + m->password = nullptr; + m->port = _config->type_config.mqtt_conf->port; + m->retain = _config->type_config.mqtt_conf->retain; + m->keepalive = _config->type_config.mqtt_conf->keepalive; + m->qos = _config->type_config.mqtt_conf->qos; + m->ssl.enabled = _config->type_config.mqtt_conf->ssl_enabled; + m->queue.queue.state = State::DESTROYED; + m->pool.state = State::DESTROYED; + + //lookup the format type for this mqtt node + m->formatter = villas::node::FormatFactory::make(_config->format_name); } - else if (vn_config.type_name == "nanomsg") { - configure_node_nanomsg(); //configure the node for nanomsg + else if (std::string(type->name) == "nanomsg") { + //configure_node_nanomsg(); //configure the node for nanomsg + + // set nanomsg parameters + auto *m = (nanomsg *) n->_vd; + + ret = vlist_init(&m->out.endpoints); + if (ret) { + //IO->log_info("Villas_interface: vlist_init of out.endpoints failed for node " + _name + + // " with return value " + std::to_string(ret)); + throw std::runtime_error("Villas_interface: vlist_init of out.endpoints failed for node " + _name + + " with return value " + std::to_string(ret)); + } + ret = vlist_init(&m->in.endpoints); + if (ret) { + //IO->log_info("Villas_interface: vlist_init of in.endpoints failed for node " + _name + + //" with return value " + std::to_string(ret)); + throw std::runtime_error("Villas_interface: vlist_init of in.endpoints failed for node " + _name + + " with return value " + std::to_string(ret)); + } + + if(!_config->type_config.nanomsg_conf->in_endpoints.empty()) { + for(const auto& endpoint : _config->type_config.nanomsg_conf->in_endpoints){ + IO->log_info("PUSH IN ENDPOINT: " + endpoint); + vlist_push(&m->in.endpoints, &strdup(endpoint.c_str())[0]); + } + } + if(!_config->type_config.nanomsg_conf->out_endpoints.empty()) { + for(const auto& endpoint : _config->type_config.nanomsg_conf->out_endpoints){ + IO->log_info("PUSH OUT ENDPOINT: " + endpoint); + vlist_push(&m->out.endpoints, &strdup(endpoint.c_str())[0]); + } + } + m->formatter = villas::node::FormatFactory::make(_config->format_name); + } else { + //IO->log_info("Villas_interface: Error: node type " + std::string(type->name) + " not supported by DistAIX."); + throw std::runtime_error("Villas_interface: Error in node " + _name + ": node type " + + std::string(type->name) + " not supported."); } - //set in and out node directions to PARSED + + // set state to parsed to be able to continue with this node + n->state = State::PARSED; + // set in and out node directions to PARSED n->in.state = State::PARSED; n->out.state = State::PARSED; //check node ret = node_check(n); if (ret) { - IO->log_info("Villas_interface::init: node_check failed for node " + vn_config.n_name + - " and node type " + vn_config.type_name); + //IO->log_info("Villas_interface: node_check failed for node " + _name + + //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + throw std::runtime_error("Villas_interface: node_check failed for node " + _name + + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); } //prepare node ret = node_prepare(n); if (ret) { - IO->log_info("Villas_interface::init: node_prepare failed for node " + vn_config.n_name + - " and node type " + vn_config.type_name); + //IO->log_info("Villas_interface: node_prepare failed for node " + _name + + //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + throw std::runtime_error("Villas_interface: node_prepare failed for node " + _name + + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); } - } - else{ - IO->log_info("Villas_interface::init: Cannot init() villas node because this instance" - " of Villas_interface is used without node"); + } else{ + IO->log_info("This instance of Villas_interface is used without node"); } - return ret; #else - return -1; + //Warn the user + IO->log_info("ERROR: Your agent behavior is trying to instantiate a Villas_interface, but DistAIX is compiled without VILLASnode support.\n" + "This will very likely cause a simulation failure!\n" + "Pass option -villas to build_and_install.sh script to compile DistAIX with VILLASnode support"); + std::cerr << "ERROR: Your agent behavior is trying to instantiate a Villas_interface, but DistAIX is compiled without VILLASnode support." << std::endl; + std::cerr << "This will very likely cause a simulation failure!" << std::endl; + std::cerr << "Pass option -villas to build_and_install.sh script to compile DistAIX with VILLASnode support" << std::endl; +#endif +} + + +/*! \brief Destructor of Villas_interface class + * + * */ +Villas_interface::~Villas_interface() { +#ifdef WITH_VILLAS + if(with_node) { + int ret; + ret = pool_destroy(p); + if (ret){ + IO->log_info("Villas_interface::Destructor pool_destroy failed for node " + std::string(n->name) + + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + } + } #endif } @@ -217,16 +266,16 @@ int Villas_interface::init() { int Villas_interface::start() { #ifdef WITH_VILLAS int ret =0; //for return values of villas functions - IO->log_info("Villas_interface::start: starting villas node " + vn_config.n_name); - if(vn_config.with_node) { + IO->log_info("Villas_interface::start: starting villas node " + std::string(n->name)); + if(with_node) { //configure behavior specific input and output signals set_signals(); ret = node_start(n); if (ret) { - IO->log_info("Villas_interface::start: node_start failed for node " + vn_config.n_name + - " and node type " + vn_config.type_name + " ; ret="+std::to_string(ret)); + IO->log_info("Villas_interface::start: node_start failed for node " + std::string(n->name) + + " and node type " + std::string(type->name) + " ; ret="+std::to_string(ret)); } } @@ -248,12 +297,12 @@ int Villas_interface::start() { int Villas_interface::stop() { #ifdef WITH_VILLAS int ret =0; //for return values of villas functions - IO->log_info("Villas_interface::stop: stopping villas node " + vn_config.n_name); - if(vn_config.with_node) { + IO->log_info("Villas_interface::stop: stopping villas node " + std::string(n->name)); + if(with_node) { ret = node_stop(n); if (ret) { - IO->log_info("Villas_interface::stop: node_stop failed for node " + vn_config.n_name + - " and node type " + vn_config.type_name); + IO->log_info("Villas_interface::stop: node_stop failed for node " + std::string(n->name) + + " and node type " + std::string(type->name)); } } else{ @@ -273,12 +322,12 @@ int Villas_interface::stop() { int Villas_interface::destroy() { #ifdef WITH_VILLAS int ret =0; //for return values of villas functions - IO->log_info("Villas_interface::destroy: destroying villas node " + vn_config.n_name); - if(vn_config.with_node) { + IO->log_info("Villas_interface::destroy: destroying villas node " + std::string(n->name)); + if(with_node) { ret = node_destroy(n); if (ret) { - IO->log_info("Villas_interface::destroy: node_destroy failed for node " + vn_config.n_name + - " and node type " + vn_config.type_name); + IO->log_info("Villas_interface::destroy: node_destroy failed for node " + std::string(n->name) + + " and node type " + std::string(type->name)); } } else{ @@ -291,143 +340,52 @@ int Villas_interface::destroy() { #endif } -/*! \brief Configure the villasnode for mqtt - * - * */ -void Villas_interface::configure_node_mqtt() { -#ifdef WITH_VILLAS - - n->name = strdup(vn_config.n_name.c_str()); - n->in.enabled = 1; - n->out.enabled = 1; - n->out.vectorize = 1; //only one sample at a time - n->in.vectorize = 1; - //n->samplelen = (int) meta.size(); //length of meta info vector is equal length of sample - - //set mqtt parameters - struct mqtt *m = (mqtt *) n->_vd; - - //set broker - m->host = strdup(vn_config.type_config.mqtt_conf->broker.c_str()) ; - //set publish topic (if any) - if(!vn_config.type_config.mqtt_conf->publish.empty()){ - m->publish = strdup(vn_config.type_config.mqtt_conf->publish.c_str()); - } else{ - m->publish = nullptr; - } - //set subscribe topic (if any) - if(!vn_config.type_config.mqtt_conf->subscribe.empty()){ - m->subscribe = strdup(vn_config.type_config.mqtt_conf->subscribe.c_str()); - } else{ - m->subscribe = nullptr; - } - - m->username = nullptr; - m->password = nullptr; - m->port = vn_config.type_config.mqtt_conf->port; - m->retain = vn_config.type_config.mqtt_conf->retain; - m->keepalive = vn_config.type_config.mqtt_conf->keepalive; - m->qos = vn_config.type_config.mqtt_conf->qos; - m->ssl.enabled = vn_config.type_config.mqtt_conf->ssl_enabled; - m->queue.queue.state = State::DESTROYED; - m->pool.state = State::DESTROYED; - - //lookup the format type for this mqtt node - m->format = format_type_lookup(vn_config.format_name.c_str()); - - //set state to parsed to be able to continue with this node - n->state = State::PARSED; -#endif - -} - -/*! \brief configure the villasnode for nanomsg - * - * */ -void Villas_interface::configure_node_nanomsg() { -#ifdef WITH_VILLAS - - n->name = strdup(vn_config.n_name.c_str()); - n->in.enabled = 1; - n->out.enabled = 1; - n->out.vectorize = 1; - n->in.vectorize = 1; - - // set nanomsg parameters - struct nanomsg *m = (nanomsg *) n->_vd; - - vlist_init(&m->out.endpoints); - vlist_init(&m->in.endpoints); - - if(!vn_config.type_config.nanomsg_conf->in_endpoints.empty()) { - for(auto endpoint : vn_config.type_config.nanomsg_conf->in_endpoints){ - IO->log_info("PUSH IN ENDPOINT: " + endpoint); - vlist_push(&m->in.endpoints, &strdup(endpoint.c_str())[0]); - } - } - if(!vn_config.type_config.nanomsg_conf->out_endpoints.empty()) { - for(auto endpoint : vn_config.type_config.nanomsg_conf->out_endpoints){ - IO->log_info("PUSH OUT ENDPOINT: " + endpoint); - vlist_push(&m->out.endpoints, &strdup(endpoint.c_str())[0]); - } - } - - m->format = format_type_lookup(vn_config.format_name.c_str()); - // set state to parsed to be able to continue with this node - n->state = State::PARSED; -#endif - -} - void Villas_interface::set_signals() { #ifdef WITH_VILLAS + //generate list of signals for villas node based on meta information of message type - for(unsigned int i = 0; i< meta.size(); i++){ - struct signal * sig_in = (struct signal*) malloc(sizeof(struct signal)); - struct signal * sig_out = (struct signal*) malloc(sizeof(struct signal)); - sig_in->enabled = 1; - sig_out->enabled = 1; - - if(meta[i].type == VILLAS_DATA_TYPE_INT64){ - sig_in->type = SignalType::INTEGER; - sig_out->type = SignalType::INTEGER; + for(auto & i : meta){ + SignalType sig_type; + + // map data type to VILLAS signal data types + if(i.type == VILLAS_DATA_TYPE_INT64){ + sig_type = SignalType::INTEGER; } - else if(meta[i].type == VILLAS_DATA_TYPE_DOUBLE){ - sig_in->type = SignalType::FLOAT; - sig_out->type = SignalType::FLOAT; + else if(i.type == VILLAS_DATA_TYPE_DOUBLE){ + sig_type = SignalType::FLOAT; } - else if(meta[i].type == VILLAS_DATA_TYPE_BOOLEAN){ - sig_in->type = SignalType::BOOLEAN; - sig_out->type = SignalType::BOOLEAN; + else if(i.type == VILLAS_DATA_TYPE_BOOLEAN){ + sig_type = SignalType::BOOLEAN; } - else if(meta[i].type == VILLAS_DATA_TYPE_COMPLEX){ - sig_in->type = SignalType::COMPLEX; - sig_out->type = SignalType::COMPLEX; + else if(i.type == VILLAS_DATA_TYPE_COMPLEX){ + sig_type = SignalType::COMPLEX; + } else { + sig_type = SignalType::INVALID; } - sig_in->name = strdup(meta[i].name.c_str()); - sig_out->name = strdup(meta[i].name.c_str()); - sig_in->unit = strdup(meta[i].unit.c_str()); - sig_out->unit = strdup(meta[i].unit.c_str()); + // create signals + struct signal * sig_in = signal_create(i.name.c_str(), i.unit.c_str(), sig_type); + struct signal * sig_out = signal_create(i.name.c_str(), i.unit.c_str(), sig_type); + + // save signals in Villas node signal lists vlist_push(&(n->in.signals), sig_in); vlist_push(&(n->out.signals), sig_out); } #endif } - /*! \brief Send a message via a Villas_interface * * */ void Villas_interface::send_message(Villas_message &msg) { #ifdef WITH_VILLAS - if(vn_config.with_node) { + if(with_node) { //int ret = 0; - int allocated = 0; + int allocated; struct sample * new_sample[n->out.vectorize]; - allocated = sample_alloc_many(p, new_sample, n->out.vectorize); + allocated = sample_alloc_many(p, new_sample, (int) n->out.vectorize); //fill sample IO->log_info("Villas_interface::send_message: sending message of length: " + std::to_string(msg.length)); @@ -467,15 +425,15 @@ void Villas_interface::send_message(Villas_message &msg) { new_sample[0]->flags |= (unsigned int) SampleFlags::HAS_DATA; // send sample - unsigned int release=allocated; - int sent = node_write(n, new_sample, n->out.vectorize, &release); + int release=allocated; + int sent = node_write(n, new_sample, n->out.vectorize); IO->log_info("Villas_interface::send_message: node_write(...) completed, sent " + std::to_string(sent) + " samples"); if (sent < 0) { IO->log_info("Villas_interface::send_message: Failed to sent sample, reason=" + std::to_string(sent)); } number_of_sent_messages++; sample_decref_many(new_sample, release); - sample_free_many(new_sample, n->out.vectorize); + sample_free_many(new_sample, (int) n->out.vectorize); } @@ -489,29 +447,29 @@ void Villas_interface::send_message(Villas_message &msg) { } -void Villas_interface::allocate_and_receive(unsigned long number_of_messages, std::list &messages) { +void Villas_interface::allocate_and_receive(int number_of_messages, std::list &messages) { #ifdef WITH_VILLAS if(number_of_messages > 0) { - int allocated = 0; - unsigned long release_number_of_messages = number_of_messages; + int allocated; + int release_number_of_messages = number_of_messages; struct sample *new_sample[number_of_messages]; allocated = sample_alloc_many(p, new_sample, number_of_messages); - unsigned int release = allocated; + int release = allocated; //IO->log_info("Allocated " + std::to_string(number_of_messages) + " samples"); // As nanomsg has no built-in queue, it is emulated by reading the socket for multiple times and // seting the number_of_messages based on node_read()'s actual return values. - if (vn_config.type_name == "nanomsg"){ + if (std::string(type->name) == "nanomsg"){ struct sample *temp_sample[1]; // Temporary sample - unsigned int allocated_temp = sample_alloc_many(p, temp_sample, 1); + int allocated_temp = sample_alloc_many(p, temp_sample, 1); - unsigned int received = 0; + int received = 0; unsigned int index = 0; - for (unsigned int i = 0; i < number_of_messages; ++i){ + for (int i = 0; i < number_of_messages; ++i){ - int recv = node_read(n, temp_sample, 1, &allocated_temp); + int recv = node_read(n, temp_sample, 1); if (recv > 0){ sample_copy(new_sample[index], temp_sample[0]); received++; @@ -535,7 +493,7 @@ void Villas_interface::allocate_and_receive(unsigned long number_of_messages, st } else { //receive sample - int recv = node_read(n, new_sample, number_of_messages, &release); + int recv = node_read(n, new_sample, number_of_messages); IO->log_info("Villas_interface::receive_messages: node_read(...) completed, received " + std::to_string(recv) + " samples"); @@ -544,9 +502,9 @@ void Villas_interface::allocate_and_receive(unsigned long number_of_messages, st " reason=" + std::to_string(recv)); } } - for (unsigned long x = 0; x < number_of_messages; x++) { + for (int x = 0; x < number_of_messages; x++) { //print received samples - std::string output = ""; + std::string output; output += "Villas_interface::receive_messages: Received the following sample of length " + std::to_string(new_sample[x]->length) + ":\t"; @@ -597,20 +555,20 @@ void Villas_interface::allocate_and_receive(unsigned long number_of_messages, st * */ void Villas_interface::receive_messages(std::list &messages) { #ifdef WITH_VILLAS - if(vn_config.with_node) { + if(with_node) { //receive all messages which are in incoming queue of villas node messages.clear(); - unsigned long number_of_messages; + int number_of_messages; // Check which villas node type is used and create corresponding struct - if(vn_config.type_name == "mqtt") { - struct mqtt * m = (struct mqtt*) n->_vd; + if(std::string(type->name) == "mqtt") { + auto * m = (struct mqtt*) n->_vd; //determine number of messages in the queue unsigned long queue_tail = m->queue.queue.tail; unsigned long queue_head = m->queue.queue.head; - number_of_messages = queue_tail - queue_head; + number_of_messages = (int) (queue_tail - queue_head); //IO->log_info("Villas_interface::receive_messages: " + std::to_string(number_of_messages) // + " samples are in the queue"); @@ -619,7 +577,7 @@ void Villas_interface::receive_messages(std::list &messages) { allocate_and_receive(number_of_messages, messages); } } - else if(vn_config.type_name == "nanomsg") { + else if(std::string(type->name) == "nanomsg") { allocate_and_receive(1000, messages); // 128kB Memory / 8 Byte per message = 16000 messages } } @@ -639,19 +597,19 @@ void Villas_interface::receive_messages(std::list &messages) { * */ int Villas_interface::start_node_type() { #ifdef WITH_VILLAS - int ret= 0; + int ret; //init memory subsystem in each process ret = memory_init(0); if(ret){ - IO->log_info("Villas_interface::start_node_type: memory_init failed for type " + vn_config.type_name); + IO->log_info("Villas_interface::start_node_type: memory_init failed for type " + std::string(type->name)); } //start node type - IO->log_info("Villas_interface::start_node_type: Starting villas node type " + vn_config.type_name); + IO->log_info("Villas_interface::start_node_type: Starting villas node type " + std::string(type->name)); ret = node_type_start(type, nullptr); if(ret){ - IO->log_info("Villas_interface::start_node_type: node_type_start failed for type " + vn_config.type_name); + IO->log_info("Villas_interface::start_node_type: node_type_start failed for type " + std::string(type->name)); } return ret; @@ -666,12 +624,12 @@ int Villas_interface::start_node_type() { * */ int Villas_interface::stop_node_type() { #ifdef WITH_VILLAS - int ret= 0; + int ret; //stop node type - IO->log_info("Villas_interface::stop_node_type: Stopping villas node type " + vn_config.type_name); + IO->log_info("Villas_interface::stop_node_type: Stopping villas node type " + std::string(type->name)); ret = node_type_stop(type); if(ret){ - IO->log_info("Villas_interface::stop_node_type: node_type_stop failed for type " + vn_config.type_name); + IO->log_info("Villas_interface::stop_node_type: node_type_stop failed for type " + std::string(type->name)); } return ret; -- GitLab From 7c65921a925379918122171490d966ab2b0d4278 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 17 Aug 2021 14:32:22 +0200 Subject: [PATCH 02/12] change to c++17 standard for compilation --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4342380..11a22f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,8 +43,8 @@ set(OLD_CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER}) #set compiler and compiler flags set(CMAKE_CXX_COMPILER ${MPI_CXX_COMPILER}) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -std=c++11 -Wall") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 -std=c++11 -Wall") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -std=c++17 -Wall") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -O0 -std=c++17 -Wall") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin") #add feature infos -- GitLab From cea70169a556b7460e5b47861422efa38ce8a35c Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 17 Aug 2021 14:32:44 +0200 Subject: [PATCH 03/12] update version of villasnode lib --- libs/villasnode | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/villasnode b/libs/villasnode index aa0e05e..840da0d 160000 --- a/libs/villasnode +++ b/libs/villasnode @@ -1 +1 @@ -Subproject commit aa0e05e8a1a5095659c4e61f5fa4041ea3b07de0 +Subproject commit 840da0d7a20dcde83c59156a5b2f1668abada68f -- GitLab From 243a6143aeac87fa817da563eca39cee1bc654ca Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 17 Aug 2021 15:23:39 +0200 Subject: [PATCH 04/12] CI: enable kaniko cache (2 weeks) --- .gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 566faad..78c1b38 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,7 +11,7 @@ stages: - docker - test -# prepare stage +# prepare stage (use kaniko and cache images for 2 weeks) docker:ubuntu: stage: docker image: @@ -25,6 +25,8 @@ docker:ubuntu: --dockerfile ${CI_PROJECT_DIR}/Dockerfile --destination ${DOCKER_IMAGE}:${DOCKER_TAG} --snapshotMode=redo + --cache=true + --cache-ttl=336h # test stage test:compile: -- GitLab From 2b72570bd36284ba959014b059672785cdf8604a Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 17 Aug 2021 15:25:07 +0200 Subject: [PATCH 05/12] move setting of signals to constructor of VILLASinterface to be compliant with VILLASnode state machine, update submodule of VILLASnode lib --- include/villas_interface/villas_interface.h | 4 +- libs/villasnode | 2 +- src/villas_interface/villas_interface.cpp | 69 +++++++++------------ 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/include/villas_interface/villas_interface.h b/include/villas_interface/villas_interface.h index 8683671..ca91c7c 100644 --- a/include/villas_interface/villas_interface.h +++ b/include/villas_interface/villas_interface.h @@ -106,6 +106,7 @@ private: /*VillasNode struct*/ struct vnode *n; struct vnode_type *type; + //Memory pool struct pool *p; @@ -121,9 +122,6 @@ private: void allocate_and_receive(int number_of_messages, std::list &messages); - //Behavior specific methods - void set_signals(); - }; #endif //VILLAS_INTERFACE_H diff --git a/libs/villasnode b/libs/villasnode index 840da0d..51e1952 160000 --- a/libs/villasnode +++ b/libs/villasnode @@ -1 +1 @@ -Subproject commit 840da0d7a20dcde83c59156a5b2f1668abada68f +Subproject commit 51e1952a0aaa2ee2bc44f7739ba1be0beb172c44 diff --git a/src/villas_interface/villas_interface.cpp b/src/villas_interface/villas_interface.cpp index 8cafbd9..7863448 100644 --- a/src/villas_interface/villas_interface.cpp +++ b/src/villas_interface/villas_interface.cpp @@ -203,6 +203,37 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob + std::string(type->name) + " not supported."); } + // generate list of signals for villas node based on meta information of message type + // configures behavior-specific input and output signals + // this does the job of "node_direction_parse(...)" + for(auto & i : meta){ + SignalType sig_type; + + // map data type to VILLAS signal data types + if(i.type == VILLAS_DATA_TYPE_INT64){ + sig_type = SignalType::INTEGER; + } + else if(i.type == VILLAS_DATA_TYPE_DOUBLE){ + sig_type = SignalType::FLOAT; + } + else if(i.type == VILLAS_DATA_TYPE_BOOLEAN){ + sig_type = SignalType::BOOLEAN; + } + else if(i.type == VILLAS_DATA_TYPE_COMPLEX){ + sig_type = SignalType::COMPLEX; + } else { + sig_type = SignalType::INVALID; + } + + // create signals + struct signal * sig_in = signal_create(i.name.c_str(), i.unit.c_str(), sig_type); + struct signal * sig_out = signal_create(i.name.c_str(), i.unit.c_str(), sig_type); + + // save signals in Villas node signal lists + vlist_push(&(n->in.signals), sig_in); + vlist_push(&(n->out.signals), sig_out); + } + // set state to parsed to be able to continue with this node n->state = State::PARSED; @@ -268,10 +299,6 @@ int Villas_interface::start() { int ret =0; //for return values of villas functions IO->log_info("Villas_interface::start: starting villas node " + std::string(n->name)); if(with_node) { - - //configure behavior specific input and output signals - set_signals(); - ret = node_start(n); if (ret) { IO->log_info("Villas_interface::start: node_start failed for node " + std::string(n->name) + @@ -340,40 +367,6 @@ int Villas_interface::destroy() { #endif } -void Villas_interface::set_signals() { -#ifdef WITH_VILLAS - - //generate list of signals for villas node based on meta information of message type - for(auto & i : meta){ - SignalType sig_type; - - // map data type to VILLAS signal data types - if(i.type == VILLAS_DATA_TYPE_INT64){ - sig_type = SignalType::INTEGER; - } - else if(i.type == VILLAS_DATA_TYPE_DOUBLE){ - sig_type = SignalType::FLOAT; - } - else if(i.type == VILLAS_DATA_TYPE_BOOLEAN){ - sig_type = SignalType::BOOLEAN; - } - else if(i.type == VILLAS_DATA_TYPE_COMPLEX){ - sig_type = SignalType::COMPLEX; - } else { - sig_type = SignalType::INVALID; - } - - // create signals - struct signal * sig_in = signal_create(i.name.c_str(), i.unit.c_str(), sig_type); - struct signal * sig_out = signal_create(i.name.c_str(), i.unit.c_str(), sig_type); - - // save signals in Villas node signal lists - vlist_push(&(n->in.signals), sig_in); - vlist_push(&(n->out.signals), sig_out); - } -#endif -} - /*! \brief Send a message via a Villas_interface * * */ -- GitLab From d3eb9f4b69ba06ef9a6d6dab4b56136dad8a5a46 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 19 Aug 2021 10:13:51 +0200 Subject: [PATCH 06/12] require at least cmake version 3.0, fix cmake boost variables in repast cmake file, do not use boost-cmake to find boost --- CMakeLists.txt | 5 +++-- libs/CMakeLists.txt | 4 ++-- libs/dbconnector | 2 +- libs/fbs-components | 2 +- libs/repasthpc_CMakeLists.txt | 7 ++++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11a22f0..1925d97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ ############################################################################# -cmake_minimum_required(VERSION 2.8.1) +cmake_minimum_required(VERSION 3.0) project(distaix) option(WITH_VILLAS "Build with VILLASnode support in agents" ON ) @@ -35,8 +35,9 @@ file(GLOB_RECURSE SRC_LIST ./src/*.cpp) find_package(MPI REQUIRED) #find boost libraries +set(Boost_NO_BOOST_CMAKE ON) find_package(Boost REQUIRED) -add_definitions(-DBOOST_ALLOW_DEPRECATED_HEADERS) +#add_definitions(-DBOOST_ALLOW_DEPRECATED_HEADERS) #save previous compiler to be used by library in libs folder set(OLD_CMAKE_CXX_COMPILER ${CMAKE_CXX_COMPILER}) diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 84a50b6..055ff5b 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -21,7 +21,7 @@ # along with this program. If not, see . ############################################################################# -cmake_minimum_required(VERSION 2.8.1) +cmake_minimum_required(VERSION 3.0) #return to old compiler found by cmake in order NOT to use the MPI compiler here set(CMAKE_CXX_COMPILER ${OLD_CMAKE_CXX_COMPILER}) @@ -43,4 +43,4 @@ endif() if(WITH_DB) #DBconnector library add_subdirectory(dbconnector) -endif() \ No newline at end of file +endif() diff --git a/libs/dbconnector b/libs/dbconnector index efe2b0c..65ae340 160000 --- a/libs/dbconnector +++ b/libs/dbconnector @@ -1 +1 @@ -Subproject commit efe2b0c2c15cc3508b878a3931852201b21d29d2 +Subproject commit 65ae340429bb44177d1d2102690501f452065693 diff --git a/libs/fbs-components b/libs/fbs-components index 7fefb80..be15b3b 160000 --- a/libs/fbs-components +++ b/libs/fbs-components @@ -1 +1 @@ -Subproject commit 7fefb80a81a96d8d3d221443828304e64f9befd3 +Subproject commit be15b3b7a35ce7164912068e1c2cbe47e36bfc74 diff --git a/libs/repasthpc_CMakeLists.txt b/libs/repasthpc_CMakeLists.txt index 58746c1..af5f3a0 100644 --- a/libs/repasthpc_CMakeLists.txt +++ b/libs/repasthpc_CMakeLists.txt @@ -33,8 +33,8 @@ file(GLOB_RECURSE rhpc_src ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) set (rhpc_lib_name repast_hpc) find_package(Boost 1.54.0 REQUIRED COMPONENTS system filesystem mpi serialization) -include_directories(${Boost_INCLUDE_DIR}) -link_directories(${Boost_LIB_DIR}) +include_directories(${Boost_INCLUDE_DIRS}) +link_directories(${Boost_LIBRARY_DIRS}) set (NETCDF_CXX "YES") find_package(NetCDF REQUIRED) @@ -49,4 +49,5 @@ include_directories(${MPI_CXX_INCLUDE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}) add_library(${rhpc_lib_name} SHARED ${rhpc_src}) set_target_properties(${rhpc_lib_name} PROPERTIES OUTPUT_NAME ${rhpc_lib_name}) -target_link_libraries(${rhpc_lib_name} ${Boost_LIBRARIES} ${NETCDF_LIBRARIES} ${CURL_LIBRARIES} ${MPI_LIBRARIES}) \ No newline at end of file +target_link_libraries(${rhpc_lib_name} ${Boost_LIBRARIES} ${NETCDF_LIBRARIES} ${CURL_LIBRARIES} ${MPI_LIBRARIES}) + -- GitLab From d83e08fb4cd02b57aa63c98c2dce5b81b93fc96b Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Thu, 19 Aug 2021 14:41:32 +0200 Subject: [PATCH 07/12] include repast code directly in the repo, remove submodule for repasthpc --- .gitmodules | 4 - CMakeLists.txt | 2 +- libs/CMakeLists.txt | 7 +- libs/repast_hpc/AgentId.cpp | 80 + libs/repast_hpc/AgentId.h | 280 +++ libs/repast_hpc/AgentImporterExporter.cpp | 1424 +++++++++++++ libs/repast_hpc/AgentImporterExporter.h | 1090 ++++++++++ libs/repast_hpc/AgentRequest.cpp | 169 ++ libs/repast_hpc/AgentRequest.h | 328 +++ libs/repast_hpc/AgentStatus.cpp | 61 + libs/repast_hpc/AgentStatus.h | 151 ++ libs/repast_hpc/BaseGrid.h | 518 +++++ .../CMakeLists.txt} | 2 +- libs/repast_hpc/CartesianTopology.cpp | 147 ++ libs/repast_hpc/CartesianTopology.h | 112 + libs/repast_hpc/Context.h | 1115 ++++++++++ libs/repast_hpc/DataSet.h | 73 + libs/repast_hpc/DiffusionLayerND.h | 266 +++ libs/repast_hpc/DirectedVertex.h | 166 ++ libs/repast_hpc/Edge.h | 272 +++ libs/repast_hpc/Graph.cpp | 42 + libs/repast_hpc/Graph.h | 753 +++++++ libs/repast_hpc/Grid.h | 268 +++ libs/repast_hpc/Grid2DQuery.h | 98 + libs/repast_hpc/GridComponents.cpp | 194 ++ libs/repast_hpc/GridComponents.h | 182 ++ libs/repast_hpc/GridDimensions.cpp | 121 ++ libs/repast_hpc/GridDimensions.h | 115 ++ libs/repast_hpc/Moore2DGridQuery.h | 127 ++ libs/repast_hpc/MultipleOccupancy.h | 195 ++ libs/repast_hpc/NCDataSet.cpp | 132 ++ libs/repast_hpc/NCDataSet.h | 137 ++ libs/repast_hpc/NCDataSetBuilder.cpp | 79 + libs/repast_hpc/NCDataSetBuilder.h | 101 + libs/repast_hpc/NCDataSource.h | 94 + libs/repast_hpc/NCReducibleDataSource.h | 125 ++ libs/repast_hpc/NetworkBuilder.cpp | 51 + libs/repast_hpc/NetworkBuilder.h | 149 ++ libs/repast_hpc/Point.h | 368 ++++ libs/repast_hpc/Projection.h | 322 +++ libs/repast_hpc/Properties.cpp | 240 +++ libs/repast_hpc/Properties.h | 319 +++ libs/repast_hpc/README.md | 39 + libs/repast_hpc/Random.cpp | 153 ++ libs/repast_hpc/Random.h | 702 +++++++ libs/repast_hpc/ReducibleDataSource.h | 114 ++ libs/repast_hpc/RelativeLocation.cpp | 259 +++ libs/repast_hpc/RelativeLocation.h | 284 +++ libs/repast_hpc/RelativeLocationMap.h | 99 + libs/repast_hpc/RepastErrors.cpp | 73 + libs/repast_hpc/RepastErrors.h | 896 ++++++++ libs/repast_hpc/RepastProcess.cpp | 328 +++ libs/repast_hpc/RepastProcess.h | 1156 +++++++++++ libs/repast_hpc/SRManager.cpp | 101 + libs/repast_hpc/SRManager.h | 147 ++ libs/repast_hpc/SVDataSet.cpp | 154 ++ libs/repast_hpc/SVDataSet.h | 108 + libs/repast_hpc/SVDataSetBuilder.cpp | 64 + libs/repast_hpc/SVDataSetBuilder.h | 142 ++ libs/repast_hpc/SVDataSource.h | 102 + libs/repast_hpc/Schedule.cpp | 198 ++ libs/repast_hpc/Schedule.h | 321 +++ libs/repast_hpc/SharedBaseGrid.cpp | 138 ++ libs/repast_hpc/SharedBaseGrid.h | 488 +++++ libs/repast_hpc/SharedContext.cpp | 50 + libs/repast_hpc/SharedContext.h | 1104 ++++++++++ libs/repast_hpc/SharedContinuousSpace.h | 101 + libs/repast_hpc/SharedDiscreteSpace.h | 319 +++ libs/repast_hpc/SharedNetwork.h | 242 +++ libs/repast_hpc/SharedSpaces.h | 86 + libs/repast_hpc/SingleOccupancy.h | 139 ++ libs/repast_hpc/Spaces.h | 73 + libs/repast_hpc/TDataSource.h | 69 + libs/repast_hpc/UndirectedVertex.h | 158 ++ libs/repast_hpc/Utilities.cpp | 113 + libs/repast_hpc/Utilities.h | 128 ++ libs/repast_hpc/VN2DGridQuery.h | 122 ++ libs/repast_hpc/ValueLayer.cpp | 52 + libs/repast_hpc/ValueLayer.h | 427 ++++ libs/repast_hpc/ValueLayerND.cpp | 78 + libs/repast_hpc/ValueLayerND.h | 1809 +++++++++++++++++ libs/repast_hpc/Variable.cpp | 69 + libs/repast_hpc/Variable.h | 130 ++ libs/repast_hpc/Vertex.h | 238 +++ libs/repast_hpc/WeightedRandomSelector.h | 162 ++ .../repast_hpc/cmake/Modules/FindNetCDF.cmake | 71 + libs/repast_hpc/initialize_random.cpp | 231 +++ libs/repast_hpc/initialize_random.h | 129 ++ libs/repast_hpc/io.cpp | 109 + libs/repast_hpc/io.h | 66 + libs/repast_hpc/logger.cpp | 834 ++++++++ libs/repast_hpc/logger.h | 162 ++ libs/repast_hpc/matrix.h | 318 +++ libs/repast_hpc/module.mk | 37 + libs/repast_hpc/mpi_constants.h | 64 + libs/repast_hpc/spatial_math.cpp | 86 + libs/repast_hpc/spatial_math.h | 108 + libs/repasthpc | 1 - 98 files changed, 24419 insertions(+), 11 deletions(-) create mode 100644 libs/repast_hpc/AgentId.cpp create mode 100644 libs/repast_hpc/AgentId.h create mode 100644 libs/repast_hpc/AgentImporterExporter.cpp create mode 100644 libs/repast_hpc/AgentImporterExporter.h create mode 100644 libs/repast_hpc/AgentRequest.cpp create mode 100644 libs/repast_hpc/AgentRequest.h create mode 100644 libs/repast_hpc/AgentStatus.cpp create mode 100644 libs/repast_hpc/AgentStatus.h create mode 100644 libs/repast_hpc/BaseGrid.h rename libs/{repasthpc_CMakeLists.txt => repast_hpc/CMakeLists.txt} (98%) create mode 100644 libs/repast_hpc/CartesianTopology.cpp create mode 100644 libs/repast_hpc/CartesianTopology.h create mode 100644 libs/repast_hpc/Context.h create mode 100644 libs/repast_hpc/DataSet.h create mode 100644 libs/repast_hpc/DiffusionLayerND.h create mode 100644 libs/repast_hpc/DirectedVertex.h create mode 100644 libs/repast_hpc/Edge.h create mode 100644 libs/repast_hpc/Graph.cpp create mode 100644 libs/repast_hpc/Graph.h create mode 100644 libs/repast_hpc/Grid.h create mode 100644 libs/repast_hpc/Grid2DQuery.h create mode 100644 libs/repast_hpc/GridComponents.cpp create mode 100644 libs/repast_hpc/GridComponents.h create mode 100644 libs/repast_hpc/GridDimensions.cpp create mode 100644 libs/repast_hpc/GridDimensions.h create mode 100644 libs/repast_hpc/Moore2DGridQuery.h create mode 100644 libs/repast_hpc/MultipleOccupancy.h create mode 100644 libs/repast_hpc/NCDataSet.cpp create mode 100644 libs/repast_hpc/NCDataSet.h create mode 100644 libs/repast_hpc/NCDataSetBuilder.cpp create mode 100644 libs/repast_hpc/NCDataSetBuilder.h create mode 100644 libs/repast_hpc/NCDataSource.h create mode 100644 libs/repast_hpc/NCReducibleDataSource.h create mode 100644 libs/repast_hpc/NetworkBuilder.cpp create mode 100644 libs/repast_hpc/NetworkBuilder.h create mode 100644 libs/repast_hpc/Point.h create mode 100644 libs/repast_hpc/Projection.h create mode 100644 libs/repast_hpc/Properties.cpp create mode 100644 libs/repast_hpc/Properties.h create mode 100644 libs/repast_hpc/README.md create mode 100644 libs/repast_hpc/Random.cpp create mode 100644 libs/repast_hpc/Random.h create mode 100644 libs/repast_hpc/ReducibleDataSource.h create mode 100644 libs/repast_hpc/RelativeLocation.cpp create mode 100644 libs/repast_hpc/RelativeLocation.h create mode 100644 libs/repast_hpc/RelativeLocationMap.h create mode 100644 libs/repast_hpc/RepastErrors.cpp create mode 100644 libs/repast_hpc/RepastErrors.h create mode 100644 libs/repast_hpc/RepastProcess.cpp create mode 100644 libs/repast_hpc/RepastProcess.h create mode 100644 libs/repast_hpc/SRManager.cpp create mode 100644 libs/repast_hpc/SRManager.h create mode 100644 libs/repast_hpc/SVDataSet.cpp create mode 100644 libs/repast_hpc/SVDataSet.h create mode 100644 libs/repast_hpc/SVDataSetBuilder.cpp create mode 100644 libs/repast_hpc/SVDataSetBuilder.h create mode 100644 libs/repast_hpc/SVDataSource.h create mode 100644 libs/repast_hpc/Schedule.cpp create mode 100644 libs/repast_hpc/Schedule.h create mode 100644 libs/repast_hpc/SharedBaseGrid.cpp create mode 100644 libs/repast_hpc/SharedBaseGrid.h create mode 100644 libs/repast_hpc/SharedContext.cpp create mode 100644 libs/repast_hpc/SharedContext.h create mode 100644 libs/repast_hpc/SharedContinuousSpace.h create mode 100644 libs/repast_hpc/SharedDiscreteSpace.h create mode 100644 libs/repast_hpc/SharedNetwork.h create mode 100644 libs/repast_hpc/SharedSpaces.h create mode 100644 libs/repast_hpc/SingleOccupancy.h create mode 100644 libs/repast_hpc/Spaces.h create mode 100644 libs/repast_hpc/TDataSource.h create mode 100644 libs/repast_hpc/UndirectedVertex.h create mode 100644 libs/repast_hpc/Utilities.cpp create mode 100644 libs/repast_hpc/Utilities.h create mode 100644 libs/repast_hpc/VN2DGridQuery.h create mode 100644 libs/repast_hpc/ValueLayer.cpp create mode 100644 libs/repast_hpc/ValueLayer.h create mode 100644 libs/repast_hpc/ValueLayerND.cpp create mode 100644 libs/repast_hpc/ValueLayerND.h create mode 100644 libs/repast_hpc/Variable.cpp create mode 100644 libs/repast_hpc/Variable.h create mode 100644 libs/repast_hpc/Vertex.h create mode 100644 libs/repast_hpc/WeightedRandomSelector.h create mode 100644 libs/repast_hpc/cmake/Modules/FindNetCDF.cmake create mode 100644 libs/repast_hpc/initialize_random.cpp create mode 100644 libs/repast_hpc/initialize_random.h create mode 100644 libs/repast_hpc/io.cpp create mode 100644 libs/repast_hpc/io.h create mode 100644 libs/repast_hpc/logger.cpp create mode 100644 libs/repast_hpc/logger.h create mode 100644 libs/repast_hpc/matrix.h create mode 100644 libs/repast_hpc/module.mk create mode 100644 libs/repast_hpc/mpi_constants.h create mode 100644 libs/repast_hpc/spatial_math.cpp create mode 100644 libs/repast_hpc/spatial_math.h delete mode 160000 libs/repasthpc diff --git a/.gitmodules b/.gitmodules index 9b78a65..310425b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,10 +4,6 @@ [submodule "libs/villasnode"] path = libs/villasnode url = https://git.rwth-aachen.de/acs/public/villas/VILLASnode.git -[submodule "libs/repasthpc"] - path = libs/repasthpc - url = https://github.com/Repast/repast.hpc.git - ignore=dirty [submodule "libs/dbconnector"] path = libs/dbconnector url = https://git.rwth-aachen.de/acs/public/simulation/DistAIXFramework/dbconnector.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 1925d97..7bcc720 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ set(INCLUDE_DIRS_DISTAIX ./include ${MPI_CXX_INCLUDE_PATH} ${Boost_INCLUDE_DIRS} - libs/repasthpc/src/ + libs libs/fbs-components/include ) diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index 055ff5b..a7ed616 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -26,10 +26,9 @@ cmake_minimum_required(VERSION 3.0) #return to old compiler found by cmake in order NOT to use the MPI compiler here set(CMAKE_CXX_COMPILER ${OLD_CMAKE_CXX_COMPILER}) -#repasthpc library (as submodule) -#overwrite repasthpc CmakeLists file with custom version -execute_process(COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/repasthpc_CMakeLists.txt ${CMAKE_CURRENT_SOURCE_DIR}/repasthpc/src/repast_hpc/CMakeLists.txt) -add_subdirectory(repasthpc/src/repast_hpc) +#repasthpc library +add_subdirectory(repast_hpc) + #fbs-components library (as submodule) add_subdirectory(fbs-components) if(WITH_VILLAS) diff --git a/libs/repast_hpc/AgentId.cpp b/libs/repast_hpc/AgentId.cpp new file mode 100644 index 0000000..c9027fe --- /dev/null +++ b/libs/repast_hpc/AgentId.cpp @@ -0,0 +1,80 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * AgentId.cpp + * + * Created on: Dec 23, 2008 + * Author: nick + */ + +#include "AgentId.h" + +#include + +using namespace std; + +namespace repast { + +AgentId::AgentId(int id, int startProc, int agentType, int currentProc) : id_(id), startProc_(startProc), + agentType_(agentType), currentProc_( (currentProc == -1 ? startProc : currentProc) ) { + hash = 17; + hash = 31 * hash + boost::hash_value(id_); + hash = 31 * hash + boost::hash_value(startProc_); + hash = 31 * hash + boost::hash_value(agentType_); +} + +bool operator==(const AgentId &one, const AgentId &two) { + return one.id_ == two.id_ && one.startProc_ == two.startProc_ && one.agentType_ == two.agentType_; +} + +bool operator!=(const AgentId &one, const AgentId &two) { + return !(one == two); +} + +bool operator<(const AgentId &one, const AgentId &two) { + return ((one.agentType_ < two.agentType_) || + ((one.agentType_ == two.agentType_) && (one.startProc_ < two.startProc_)) || + ((one.agentType_ == two.agentType_) && (one.startProc_ == two.startProc_) && (one.id_ < two.id_))); +} + + +AgentId::~AgentId() {} + +ostream& operator<<(ostream& os, const AgentId& id) { + os << "AgentId(" << id.id_ << ", " << id.startProc_ << ", " << id.agentType_ + << ", " << id.currentProc_ << ")"; + return os; +} + +} diff --git a/libs/repast_hpc/AgentId.h b/libs/repast_hpc/AgentId.h new file mode 100644 index 0000000..e55d18d --- /dev/null +++ b/libs/repast_hpc/AgentId.h @@ -0,0 +1,280 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * AgentId.h + * + * Created on: Dec 23, 2008 + * Author: nick + */ + +#ifndef AGENTID_H_ +#define AGENTID_H_ + +#include +#include +#include + +namespace repast { + +/** + * Agent identity information. An Agent ID consists of four values: + * 1) a numerical identifier; 2) the process on which the agent + * was created; 3) a numerical identifier that indicates the + * agent's type (in simulation semantic terms, not a software object + * type); and 4) the process on which the agent is a local agent. + * Each agent should be uniquely identified by an AgentId using the + * first three of the four values, which should be immutable. The + * fourth value can change throughout the simulation. + */ +class AgentId { + + friend std::ostream& operator<<(std::ostream& os, const AgentId& id); + friend bool operator==(const AgentId &one, const AgentId &two); + friend bool operator<(const AgentId &one, const AgentId &two); + friend class boost::serialization::access; + +private: + int id_, startProc_, agentType_, currentProc_; + std::size_t hash; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & id_; + ar & startProc_; + ar & agentType_; + ar & currentProc_; + ar & hash; + } + +public: + + /** + * No-arg constructor necessary for serialization. + */ + AgentId() { + } + + /** + * Creates an AgentId. The combination of the first + * three parameters should uniquely identify the agent. + * + * @param id the agent's id + * @param startProc the rank of the agent's starting process + * @param agentType the agent's type (user defined) + * @param currentProc the rank where the agent is a local agent + */ + AgentId(int id, int startProc, int agentType, int currentProc = -1); + + virtual ~AgentId(); + + /** + * Gets the id component of this AgentId. + * + * @return the id component of this AgentId. + */ + int id() const { + return id_; + } + + /** + * Gets the starting rank component of this AgentId. + * + * @return the starting rank component of this AgentId. + */ + int startingRank() const { + return startProc_; + } + + /** + * Gets the agent type component of this AgentId. + * + * @return the agent type component of this AgentId. + */ + int agentType() const { + return agentType_; + } + + /** + * Gets the current process rank of this AgentId. The current rank + * identifies which process the agent with this AgentId is + * currently on. + * + * @return the current process rank of this AgentId. + */ + int currentRank() const { + return currentProc_; + } + + /** + * Sets the current process rank of this AgentId. The current rank + * identifies which process the agent with this AgentId is + * currently on. + * + * @param val the current process rank + */ + void currentRank(int val) { + currentProc_ = val; + } + + /** + * Gets the hashcode for this AgentId. + * + * @return the hashcode for this AgentId. + */ + std::size_t hashcode() const { + return hash; + } +}; + +/** + * Writes the agent id to the ostream. + */ +std::ostream& operator<<(std::ostream& os, const AgentId& id); + +/** + * Equality operator + */ +bool operator==(const AgentId &one, const AgentId &two); + +/** + * Inequality operator + */ +bool operator!=(const AgentId &one, const AgentId &two); + + +/** + * A comparison operator for use with std::set + */ +bool operator<(const AgentId &one, const AgentId &two); + +/** + * operator() implementation that returns the hashcode of + * an AgentId. + */ +struct HashId { + std::size_t operator()(const AgentId& id) const { + return id.hashcode(); + } +}; + +/** + * operator() implementation that returns the hashcode of + * an agent via its AgentId. + */ +template +struct AgentHashId { + std::size_t operator()(const AgentType* agent) const { + return agent->getId().hashcode(); + } +}; + +/** + * Interface for agent classes. + * + */ +class Agent { + +public: + virtual ~Agent() { + } + + /** + * Gets the AgentId for this Agent. + * + * @return the AgentId for this Agent. + */ + virtual AgentId& getId() = 0; + + /** + * Gets the AgentId for this Agent. + * + * @return the AgentId for this Agent. + */ + virtual const AgentId& getId() const = 0; +}; + + +/** + * Struct that allows filtering by Agent Type + */ +template +struct IsAgentType { + int _typeId; + + IsAgentType(){ + _typeId = 0; + } + + IsAgentType(int typeId) : + _typeId(typeId) { + } + + bool operator()(const boost::shared_ptr& ptr) { + return ptr->getId().agentType() == _typeId; + } + + bool operator()(const T* agent) { + return agent->getId().agentType() == _typeId; + } +}; + + +/** + * Struct that allows filtering by !(Agent Type) + */ +template +struct IsNotType { + int _typeId; + + IsNotType(){ + _typeId = 0; + } + + IsNotType(int typeId) : + _typeId(typeId) { + } + + bool operator()(const boost::shared_ptr& ptr) { + return ptr->getId().agentType() != _typeId; + } + + bool operator()(const T* agent) { + return agent->getId().agentType() != _typeId; + } + +}; + +} + +#endif /* AGENTID_H_ */ diff --git a/libs/repast_hpc/AgentImporterExporter.cpp b/libs/repast_hpc/AgentImporterExporter.cpp new file mode 100644 index 0000000..a6dec95 --- /dev/null +++ b/libs/repast_hpc/AgentImporterExporter.cpp @@ -0,0 +1,1424 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * AgentImporterExporter.cpp + * + * Created on: Jun 14, 2011 + * Author: John T. Murphy, nick + */ + +#include "AgentImporterExporter.h" + +#include + +#include "boost/serialization/set.hpp" + +#include "boost/mpi.hpp" // debug only + +using namespace repast; + +/* AgentExporterData */ + +#ifndef SHARE_AGENTS_BY_SET +AgentExporterData::AgentExporterData(){} +#else +AgentExporterData::AgentExporterData(): selectedSet(DEFAULT_AGENT_REQUEST_SET){} +#endif + + +AgentExporterData::~AgentExporterData(){ + clear(); +} + +void AgentExporterData::addData(const AgentId& id, const int destProc, const int sourceProc, const int numberOfCopies){ + AgentRequest* req; + std::map::iterator infoIter = data.find(destProc); + +#ifndef SHARE_AGENTS_BY_SET + if(infoIter == data.end()){ + req = new AgentRequest(sourceProc, destProc); + std::map* sourceMap = new std::map; + sourceMap->insert(std::pair(sourceProc, req)); + data.insert(std::pair*>(destProc, sourceMap)); + } + else{ + std::map* sourceMap = infoIter->second; + std::map::iterator reqIter = sourceMap->find(sourceProc); + if(reqIter == sourceMap->end()){ + req = new AgentRequest(sourceProc, destProc); + sourceMap->insert(std::pair(sourceProc, req)); + } + else{ + req = reqIter->second; + } + } +#else + if(infoIter == data.end()){ + req = new AgentRequest(sourceProc, destProc); + std::map* sourceMap = new std::map; + sourceMap->insert(std::pair(sourceProc, req)); + std::map* >* stringMap = new std::map* >; + stringMap->insert(std::pair* >(selectedSet, sourceMap)); + data.insert(std::pair*>*>(destProc, stringMap)); + } + else{ + std::map*>* stringMap = infoIter->second; + std::map*>::iterator stringIter = stringMap->find(selectedSet); + if(stringIter == stringMap->end()){ + req = new AgentRequest(sourceProc, destProc); + std::map* sourceMap = new std::map; + sourceMap->insert(std::pair(sourceProc, req)); + stringMap->insert(std::pair* >(selectedSet, sourceMap)); + } + else{ + std::map* sourceMap = stringIter->second; + std::map::iterator sourceIter = sourceMap->find(sourceProc); + if(sourceIter == sourceMap->end()){ + req = new AgentRequest(sourceProc, destProc); + sourceMap->insert(std::pair(sourceProc, req)); + } + else{ + req = sourceIter->second; + } + } + } +#endif + for(int i = 0; i < numberOfCopies; i++) req->addRequest(id); +} + +AgentExporterInfo* AgentExporterData::dataForProc(int destProc){ + std::map::iterator iter = data.find(destProc); + return (iter == data.end() ? &empty : iter->second); +} + + +void AgentExporterData::clear(){ + std::map::iterator iter = data.begin(); + while(iter != data.end()){ +#ifndef SHARE_AGENTS_BY_SET + std::map& sourceMap = *(iter->second); + std::map::iterator sourceIter = sourceMap.begin(); + while(sourceIter != sourceMap.end()){ + delete sourceIter->second; + sourceIter++; + } +#else + std::map*> stringMap = *(iter->second); + std::map*>::iterator stringIter = stringMap.begin(); + while(stringIter != stringMap.end()){ + std::map& sourceMap = *(stringIter->second); + std::map::iterator sourceIter = sourceMap.begin(); + while(sourceIter != sourceMap.end()){ + delete sourceIter->second; + sourceIter++; + } + delete stringIter->second; + stringIter++; + } +#endif + delete iter->second; + iter++; + } + data.clear(); +} + +void AgentExporterData::removeAllDataForAgent(AgentId& id){ + std::map::iterator iter = data.begin(); + while(iter != data.end()){ +#ifndef SHARE_AGENTS_BY_SET + std::map& sourceMap = *(iter->second); + std::map::iterator sourceIter = sourceMap.begin(); + while(sourceIter != sourceMap.end()){ + sourceIter->second->removeRequest(id, true); + sourceIter++; + } +#else + std::map*> stringMap = *(iter->second); + std::map*>::iterator stringIter = stringMap.begin(); + while(stringIter != stringMap.end()){ + std::map& sourceMap = *(stringIter->second); + std::map::iterator sourceIter = sourceMap.begin(); + while(sourceIter != sourceMap.end()){ + sourceIter->second->removeRequest(id, true); + sourceIter++; + } + stringIter++; + } +#endif + iter++; + } +} + +#ifdef SHARE_AGENTS_BY_SET +void AgentExporterData::selectSet(std::string setName){ + selectedSet = setName; +} +#endif + + + +/* AbstractImporter */ +AbstractImporter::AbstractImporter(){} + +AbstractImporter::~AbstractImporter(){} + +/* Importer_COUNT */ + +#if !defined OMIT_IMPORTER_EXPORTER_COUNT_LIST || \ + !defined OMIT_IMPORTER_EXPORTER_COUNT_SET + +Importer_COUNT::Importer_COUNT(){} + +Importer_COUNT::~Importer_COUNT(){} + +void Importer_COUNT::incrementCount(int sourceProcess){ + std::map::iterator iter = sources.find(sourceProcess); + if (iter == sources.end()){ + sources[sourceProcess] = 1; + exportingProcesses.insert(sourceProcess); + } + else{ + sources[sourceProcess] = iter->second + 1; + } +} + +void Importer_COUNT::decrementCount(int sourceProcess){ + std::map::iterator iter = sources.find(sourceProcess); + if (iter == sources.end()) throw std::domain_error("Cannot decrement the agent count for a non-exporting rank"); + else { + int newVal = iter->second - 1; + if (newVal == 0){ + sources.erase(iter); + exportingProcesses.erase(sourceProcess); + } + else{ + sources[sourceProcess] = newVal; + } + } +} + +void Importer_COUNT::registerOutgoingRequests(AgentRequest &req){ + std::vector::const_iterator iter; + for (iter = req.requestedAgents().begin(); iter != req.requestedAgents().end(); ++iter) incrementCount(iter->currentRank()); + for (iter = req.cancellations().begin(); iter != req.cancellations().end(); ++iter) decrementCount(iter->currentRank()); +} + +void Importer_COUNT::importedAgentIsRemoved(const AgentId& id){ + decrementCount(id.currentRank()); +} + +void Importer_COUNT::importedAgentIsMoved(const AgentId& id, int newProcess){ + decrementCount(id.currentRank()); + incrementCount(newProcess); +} + +std::string Importer_COUNT::getReport(){ + std::stringstream ss; + ss << "Importer_COUNT: Map Size = " << sources.size() << ": \n"; + if(sources.size() > 0){ + ss << " "; + std::map::iterator it = sources.begin(); + const std::map::iterator itEnd = sources.end(); + while(it != itEnd){ + ss << "[ Source Proc: " << it->first << " | " << it->second << "] "; + it++; + } + ss << std::endl; + } + return ss.str(); +} + +void Importer_COUNT::getSetOfAgentsBeingImported(std::set& set){ + // Not implemented for 'count' +} + +#endif + + +/* Importer_LIST */ + +#ifndef OMIT_IMPORTER_EXPORTER_LIST + +Importer_LIST::Importer_LIST(){} + +Importer_LIST::~Importer_LIST(){ + std::map* >::iterator itr = sources.begin(); + while(itr != sources.end()){ + delete itr->second; + itr++; + } +} + +std::list* Importer_LIST::getRecord(int rank){ + std::map* >::iterator iter = sources.find(rank); + if(iter != sources.end()) return iter->second; + else{ + exportingProcesses.insert(rank); + return (sources[rank] = new std::list); // Create, assign, return + } +} + +int Importer_LIST::removeAll(const AgentId& id, std::list* record, int rank){ + int initialSize = record->size(); + record->remove(id); + int finalSize = record->size(); + checkRecord(record, rank); + return initialSize - finalSize; +} + +void Importer_LIST::removeID(AgentId& id, std::list* record, int rank){ + std::list::iterator idPosition = find(record->begin(), record->end(), id); + if(idPosition != record->end()) removeID(idPosition, record, rank); +} + +void Importer_LIST::removeID(std::list::iterator idPosition, std::list* record, int rank){ + record->erase(idPosition); + checkRecord(record, rank); +} + +void Importer_LIST::checkRecord(std::list* record, int rank){ + if(record->size() == 0){ + sources.erase(rank); + exportingProcesses.erase(rank); + delete record; + } +} + + +void Importer_LIST::registerOutgoingRequests(AgentRequest &req){ + // First, loop through the requested Agent IDs + std::vector::iterator reqAgentIdIterator = req.requestedAgents_.begin(); + while(reqAgentIdIterator != req.requestedAgents_.end()){ + const AgentId &id = *reqAgentIdIterator; + + std::list* record = getRecord(id.currentRank()); + + // If the record already contains the ID, delete the ID from the request + if(find(record->begin(), record->end(), id) != record->end()) reqAgentIdIterator = req.requestedAgents_.erase(reqAgentIdIterator); + else reqAgentIdIterator++; + + // And add the ID to the record + record->push_back(id); + } + + // Then loop through the cancellations + std::vector::iterator cancelledIdIterator = req.cancellations_.begin(); + + while(cancelledIdIterator != req.cancellations_.end()){ + AgentId &id = *cancelledIdIterator; + +#ifdef ALLOW_FULL_AGENT_REQUEST_CANCELLATION + bool isFullCancel = checkForFullCancellation(id); +#endif + int idRank = id.currentRank(); + + // Retrieve the current list for this source rank + std::map* >::iterator sourceListIter = sources.find(idRank); + + // If there is not even a list for this rank, just delete the cancellation + if(sourceListIter == sources.end()) cancelledIdIterator = req.cancellations_.erase(cancelledIdIterator); + else{ + std::list* record = sourceListIter->second; + std::list::iterator firstInstance = find(record->begin(), record->end(), id); + + // If there is no entry for this ID, just delete the cancellation + if(firstInstance == record->end()) cancelledIdIterator = req.cancellations_.erase(cancelledIdIterator); + else{ + +#ifdef ALLOW_FULL_AGENT_REQUEST_CANCELLATION + if(!isFullCancel){ +#endif + + // See if there is a second entry in the record + std::list::iterator secondFind = firstInstance; + secondFind++; + secondFind = find(secondFind, record->end(), id); + + // If so, need to delete one but should not pass on the cancellation + if(secondFind != record->end()){ + record->erase(secondFind); // We know there are at least two, so no possibility that size->0 + cancelledIdIterator = req.cancellations_.erase(cancelledIdIterator); + } + else{ + // If there is only one entry in the record, delete it AND pass along + // the cancellation + removeID(firstInstance, record, idRank); + cancelledIdIterator++; + } + +#ifdef ALLOW_FULL_AGENT_REQUEST_CANCELLATION + } + else{ + removeAll(id, record, idRank); // Remove all instances of the ID + cancelledIdIterator++; + } +#endif + } + } + } +} + +void Importer_LIST::importedAgentIsRemoved(const AgentId& id){ + std::map*>::iterator iter = sources.find(id.currentRank()); + if(iter != sources.end()) removeAll(id, iter->second, id.currentRank()); +} + +void Importer_LIST::importedAgentIsMoved(const AgentId& id, int newProcess){ + std::map*>::iterator iter = sources.find(id.currentRank()); + if(iter != sources.end()){ + int c = removeAll(id, iter->second, id.currentRank()); + AgentId newId(id); + newId.currentRank(newProcess); + std::list* record = getRecord(newProcess); + for(int i = 0; i < c; i++) record->push_back(newId); + } +} + + +std::string Importer_LIST::getReport(){ + std::stringstream ss; + ss << "Importer_LIST: Map Size = " << sources.size() << ": \n"; + if(sources.size() > 0){ + ss << " "; + std::map* >::iterator it = sources.begin(); + const std::map* >::iterator itEnd = sources.end(); + while(it != itEnd){ + ss << "[ Source Proc: " << it->first << " | "; + std::list::iterator listIt = it->second->begin(); + const std::list::iterator listItEnd = it->second->end(); + while(listIt != listItEnd){ + ss << *listIt << " "; + listIt++; + } + ss << "]" << std::endl; + it++; + } + } + return ss.str(); +} + +void Importer_LIST::getSetOfAgentsBeingImported(std::set& set){ + if(sources.size() > 0){ + std::map* >::iterator it = sources.begin(); + const std::map* >::iterator itEnd = sources.end(); + while(it != itEnd){ + set.insert(it->second->begin(), it->second->end()); + it++; + } + } +} + +#endif + + +/* Importer_SET */ + +#ifndef OMIT_IMPORTER_EXPORTER_SET + +Importer_SET::Importer_SET(){ +} + +Importer_SET::~Importer_SET(){ + std::map* >::iterator iter = sources.begin(); + while(iter != sources.end()){ + delete iter->second; + iter++; + } +} + +std::set* Importer_SET::getRecord(int rank){ + std::map* >::iterator iter = sources.find(rank); + if(iter != sources.end()) return iter->second; + else{ + exportingProcesses.insert(rank); + return (sources[rank] = new std::set); // Create, assign, return + } +} + +int Importer_SET::removeID(const AgentId& id, std::set* record, int rank){ + int initialSize = record->size(); + record->erase(id); + int finalSize = record->size(); + checkRecord(record, rank); + return initialSize - finalSize; +} + +void Importer_SET::checkRecord(std::set* record, int rank){ + if(record->size() == 0){ + sources.erase(rank); + exportingProcesses.erase(rank); + delete record; + } +} + + + +void Importer_SET::registerOutgoingRequests(AgentRequest& req){ + + // First, loop through the requested Agent IDs + std::vector::iterator reqAgentIdIterator = req.requestedAgents_.begin(); + while(reqAgentIdIterator != req.requestedAgents_.end()){ + // Try to insert the id; if it is inserted, it wasn't in the set before, so pass along the request + if(getRecord(reqAgentIdIterator->currentRank())->insert(*reqAgentIdIterator).second) + reqAgentIdIterator++; + else // Otherwise, already in the set, so delete from the request + reqAgentIdIterator = req.requestedAgents_.erase(reqAgentIdIterator); + } + + // Then loop through the cancellations + std::vector::iterator cancelledIdIterator = req.cancellations_.begin(); + while(cancelledIdIterator != req.cancellations_.end()){ + std::map* >::iterator iter = sources.find(cancelledIdIterator->currentRank()); + if(iter == sources.end()) req.cancellations_.erase(cancelledIdIterator); // No other action required + else{ + if(removeID(*cancelledIdIterator, iter->second, cancelledIdIterator->currentRank()) > 0 ) cancelledIdIterator++; + else req.cancellations_.erase(cancelledIdIterator); + } + } +} + +void Importer_SET::importedAgentIsRemoved(const AgentId& id){ + std::map*>::iterator iter = sources.find(id.currentRank()); + if(iter != sources.end()) removeID(id, iter->second, id.currentRank()); +} + +void Importer_SET::importedAgentIsMoved(const AgentId& id, int newProcess){ + std::map*>::iterator iter = sources.find(id.currentRank()); + if(iter != sources.end()){ + int c = removeID(id, iter->second, id.currentRank()); + if(c > 0){ + AgentId newId(id); + newId.currentRank(newProcess); + std::set* record = getRecord(newProcess); + record->insert(newId); + } + } +} + +std::string Importer_SET::getReport(){ + std::stringstream ss; + ss << "Importer_SET: Map Size = " << sources.size() << ": \n"; + if(sources.size() > 0){ + ss << " "; + std::map* >::iterator it = sources.begin(); + const std::map* >::iterator itEnd = sources.end(); + while(it != itEnd){ + ss << "[ Source Proc: " << it->first << " | "; + std::set::iterator setIt = it->second->begin(); + const std::set::iterator setItEnd = it->second->end(); + while(setIt != setItEnd){ + ss << *setIt << " "; + setIt++; + } + ss << "]" << std::endl; + it++; + } + } + return ss.str(); +} + +void Importer_SET::getSetOfAgentsBeingImported(std::set& set){ + if(sources.size() > 0){ + std::map* >::iterator it = sources.begin(); + const std::map* >::iterator itEnd = sources.end(); + while(it != itEnd){ + set.insert(it->second->begin(), it->second->end()); + it++; + } + } +} + +#endif + + +/* Importer_MAP_int */ + +#ifndef OMIT_IMPORTER_EXPORTER_MAP_int + +Importer_MAP_int::Importer_MAP_int(){} + +Importer_MAP_int::~Importer_MAP_int(){ + std::map* >::iterator iter = sources.begin(); + while(iter != sources.end()){ + delete iter->second; + iter++; + } +} + +std::map* Importer_MAP_int::getRecord(int rank){ + std::map* >::iterator iter = sources.find(rank); + if(iter != sources.end()) return iter->second; + else{ + exportingProcesses.insert(rank); + return (sources[rank] = new std::map); // Create, assign, return + } +} + +int Importer_MAP_int::removeAll(const AgentId& id, std::map* record, int rank){ + int ret = 0; + std::map::iterator idPos= record->find(id); + if(idPos == record->end()) return ret; + else{ + ret = idPos->second; + record->erase(idPos); + } + checkRecord(record, rank); + return ret; +} + + +int Importer_MAP_int::removeID(const AgentId& id, std::map* record, int rank){ + std::map::iterator idPos= record->find(id); + if(idPos == record->end()) return 0; + else{ + idPos->second = idPos->second - 1; + if(idPos->second == 0) record->erase(idPos); + } + checkRecord(record, rank); + return 1; +} + +void Importer_MAP_int::removeID(std::map::iterator idPosition, std::map* record, int rank){ + record->erase(idPosition); + checkRecord(record, rank); +} + +void Importer_MAP_int::checkRecord(std::map* record, int rank){ + if(record->size() == 0){ + sources.erase(rank); + exportingProcesses.erase(rank); + delete record; + } +} + + +void Importer_MAP_int::registerOutgoingRequests(AgentRequest &req){ + // First, loop through the requested Agent IDs + std::vector::iterator reqAgentIdIterator = req.requestedAgents_.begin(); + while(reqAgentIdIterator != req.requestedAgents_.end()){ + const AgentId &id = *reqAgentIdIterator; + + // Retrieve the current list for this source rank + std::map* record = getRecord(id.currentRank()); + + std::map::iterator mapEntry = record->find(id); + if(mapEntry != record->end()){ + mapEntry->second = mapEntry->second + 1; + reqAgentIdIterator = req.requestedAgents_.erase(reqAgentIdIterator); + } + else{ + (*record)[id] = 1; + reqAgentIdIterator++; + } + } + + // Then loop through the cancellations + std::vector::iterator cancelledIdIterator = req.cancellations_.begin(); + while(cancelledIdIterator != req.cancellations_.end()){ + AgentId &id = *cancelledIdIterator; + +#ifdef ALLOW_FULL_AGENT_REQUEST_CANCELLATION + bool isFullCancel = checkForFullCancellation(id); +#endif + + int rank = id.currentRank(); + + // Retrieve the current list for this source rank + std::map* >::iterator sourceMapIter = sources.find(rank); + + if(sourceMapIter == sources.end()) req.cancellations_.erase(cancelledIdIterator); + else{ + std::map* record = sourceMapIter->second; + std::map::iterator instance = record->find(id); + + if(instance == record->end()) + cancelledIdIterator = req.cancellations_.erase(cancelledIdIterator); + + else{ +#ifdef ALLOW_FULL_AGENT_REQUEST_CANCELLATION + if(!isFullCancel){ +#endif + + if(instance->second == 1){ + removeID(instance, record, rank); + cancelledIdIterator++; + } + else{ + // n.b.: Removing an element here, too... but we know there are at least two + instance->second = instance->second - 1; + cancelledIdIterator = req.cancellations_.erase(cancelledIdIterator); + } +#ifdef ALLOW_FULL_AGENT_REQUEST_CANCELLATION + } + else{ + removeAll(id, record, rank); + cancelledIdIterator++; + } +#endif + } + } + } +} + +void Importer_MAP_int::importedAgentIsRemoved(const AgentId& id){ + std::map* >::iterator sourceMapIter = sources.find(id.currentRank()); + if(sourceMapIter != sources.end()) removeAll(id, sourceMapIter->second, id.currentRank()); +} + +void Importer_MAP_int::importedAgentIsMoved(const AgentId& id, int newProcess){ + std::map* >::iterator sourceMapIter = sources.find(id.currentRank()); + if(sourceMapIter != sources.end()){ + int c = removeAll(id, sourceMapIter->second, id.currentRank()); + AgentId newId(id); + newId.currentRank(newProcess); + std::map* record = getRecord(newProcess); + record->insert(std::pair(newId, c)); + } +} + +std::string Importer_MAP_int::getReport(){ + std::stringstream ss; + ss << "Importer_MAP_int: Map Size = " << sources.size() << ": \n"; + if(sources.size() > 0){ + ss << " "; + std::map* >::iterator it = sources.begin(); + const std::map* >::iterator itEnd = sources.end(); + while(it != itEnd){ + ss << "[ Source Proc: " << it->first << " | "; + std::map::iterator mapIt = it->second->begin(); + const std::map::iterator mapItEnd = it->second->end(); + while(mapIt != mapItEnd){ + ss << mapIt->first << " = " << mapIt->second << ", "; + mapIt++; + } + ss << " ]" << std::endl; + it++; + } + } + return ss.str(); +} + +void Importer_MAP_int::getSetOfAgentsBeingImported(std::set& set){ + if(sources.size() > 0){ + std::map* >::iterator it = sources.begin(); + const std::map* >::iterator itEnd = sources.end(); + while(it != itEnd){ + std::map::iterator mapIt = it->second->begin(); + const std::map::iterator mapItEnd = it->second->end(); + while(mapIt != mapItEnd){ + set.insert(mapIt->first); + mapIt++; + } + it++; + } + } +} + +#endif + + + + + + + + + +/* AbstractExporter */ +AbstractExporter::AbstractExporter(){ + outgoingStatusChangesDeletePtr = new StatusMap; + outgoingStatusChanges = outgoingStatusChangesDeletePtr; + outgoingAgentExporterInformationDeletePtr = new AgentExporterData; + outgoingAgentExporterInformation = outgoingAgentExporterInformationDeletePtr; +} + +#ifdef SHARE_AGENTS_BY_SET +AbstractExporter::AbstractExporter(StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo){ + outgoingStatusChangesDeletePtr = 0; + outgoingStatusChanges = outgoingStatusMap; + outgoingAgentExporterInformationDeletePtr = 0; + outgoingAgentExporterInformation = outgoingAgentExporterInfo; +} +#endif + +AbstractExporter::~AbstractExporter(){ + delete outgoingStatusChangesDeletePtr; + delete outgoingAgentExporterInformationDeletePtr; +} + + +const std::set& AbstractExporter::getProcessesExportedTo(){ + return processesExportedTo; +} + + +void AbstractExporter::agentRemoved(const AgentId& id){ + AgentStatus status(id); // Indicates removal + + // Loop through the exported map + std::map::iterator requestIter = exportedMap.begin(); + while(requestIter != exportedMap.end()){ + AgentRequest* req = &requestIter->second; + int initialSize = req->requestCountRequested(); + req->remove(id, true); + int numberRemoved = initialSize - req->requestCountRequested(); + if(numberRemoved > 0){ + (*outgoingStatusChanges)[requestIter->first].insert(status); + if(req->requestCountRequested() == 0){ + processesExportedTo.erase(requestIter->first); + std::map::iterator temp = requestIter; + requestIter++; + exportedMap.erase(temp); + } + else requestIter++; + } + else requestIter++; + } +} + +void AbstractExporter::agentMoved(const AgentId& id, int process){ + AgentId newId(id); + newId.currentRank(process); + AgentStatus status(id, newId); // Indicates move + + // Loop through the exported map + std::map::iterator requestIter = exportedMap.begin(); + while(requestIter != exportedMap.end()){ + AgentRequest* req = &requestIter->second; + int initialSize = req->requestCountRequested(); + req->remove(id, true); + int numberRemoved = initialSize - req->requestCountRequested(); + if(numberRemoved > 0){ + outgoingAgentExporterInformation->addData(id, process, requestIter->first, numberRemoved); + (*outgoingStatusChanges)[requestIter->first].insert(status); + if(req->requestCountRequested() == 0){ + processesExportedTo.erase(requestIter->first); + std::map::iterator temp = requestIter; + requestIter++; + exportedMap.erase(temp); + } + else requestIter++; + } + else requestIter++; + } +} + +void AbstractExporter::incorporateAgentExporterInfo(std::map info){ + std::map::iterator infoIter = info.begin(); + while(infoIter != info.end()){ + processesExportedTo.insert(infoIter->first); + exportedMap[infoIter->first].addAll(*(infoIter->second)); + infoIter++; + } +} + + +AgentExporterInfo* AbstractExporter::getAgentExportInfo(int destProc){ + return outgoingAgentExporterInformation->dataForProc(destProc); +} + +const AbstractExporter::StatusMap* AbstractExporter::getOutgoingStatusChanges(){ + return outgoingStatusChanges; +} + +void AbstractExporter::clearAgentExportInfo(){ + outgoingAgentExporterInformation->clear(); +} + +void AbstractExporter::clearStatusMap(){ + outgoingStatusChanges->clear(); +} + +const std::map& AbstractExporter::getAgentsToExport(){ + return exportedMap; +} + + + +/* Exporter_LIST */ + +#if !defined OMIT_IMPORTER_EXPORTER_COUNT_LIST || \ + !defined OMIT_IMPORTER_EXPORTER_LIST || \ + !defined OMIT_IMPORTER_EXPORTER_SET || \ + !defined OMIT_IMPORTER_EXPORTER_MAP_int +Exporter_LIST::Exporter_LIST(){} + +#ifdef SHARE_AGENTS_BY_SET + Exporter_LIST::Exporter_LIST(StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo): + AbstractExporter(outgoingStatusMap, outgoingAgentExporterInfo){} +#endif + +Exporter_LIST::~Exporter_LIST(){} + +void Exporter_LIST::registerIncomingRequests(std::vector& requests){ + std::vector::iterator reqIter = requests.begin(); + while(reqIter != requests.end()){ + AgentRequest& request = *reqIter; + int requestingProc = request.sourceProcess(); + std::map::iterator iter = exportedMap.find(requestingProc); + + if (iter == exportedMap.end()) { + AgentRequest request(requestingProc, -1); + exportedMap[requestingProc] = request; + processesExportedTo.insert(requestingProc); + iter = exportedMap.find(requestingProc); + } + + iter->second.addAllRequests(request); // Adds all + + // Now deal with cancellations + std::vector cancellations = request.cancellations(); + for(std::vector::iterator agentIdIter = cancellations.begin(); agentIdIter != cancellations.end(); agentIdIter++){ + iter->second.removeRequest(*agentIdIter, false); // Remove only the first instance, if any + } + + // The mapped request may now be empty + if(iter->second.requestCountRequested() == 0){ + exportedMap.erase(requestingProc); + processesExportedTo.erase(requestingProc); + } + + reqIter++; + } +} + +std::string Exporter_LIST::getReport(){ + std::stringstream ss; + ss << "Exporter_LIST: Sending to " << exportedMap.size() << " (" << processesExportedTo.size() << ") procs:" << std::endl; + std::map::iterator it = exportedMap.begin(); + const std::map::iterator itEnd = exportedMap.end(); + while(it != itEnd){ + ss << " " << it->first << " -> " << it->second << std::endl; + it++; + } + return ss.str(); +} + +#endif + + +/* Exporter_SET */ + +#ifndef OMIT_IMPORTER_EXPORTER_COUNT_SET + +Exporter_SET::Exporter_SET(): AbstractExporter(){} + +#ifdef SHARE_AGENTS_BY_SET + Exporter_SET::Exporter_SET(StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo): + AbstractExporter(outgoingStatusMap, outgoingAgentExporterInfo){} +#endif + +Exporter_SET::~Exporter_SET(){ } + +void Exporter_SET::registerIncomingRequests(std::vector& requests){ + std::vector::iterator reqIter = requests.begin(); + while(reqIter != requests.end()){ + AgentRequest& request = *reqIter; + int requestingProc = request.sourceProcess(); + std::map::iterator iter = exportedMap.find(requestingProc); + + if (iter == exportedMap.end()) { + AgentRequest request(requestingProc, -1); + exportedMap[requestingProc] = request; + processesExportedTo.insert(requestingProc); + iter = exportedMap.find(requestingProc); + } + + std::vector requestedIDs = request.requestedAgents(); + std::vector::iterator requestedID = requestedIDs.begin(); + while(requestedID != requestedIDs.end()){ + if(!iter->second.containsInRequests(*requestedID)) iter->second.addRequest(*requestedID); + requestedID++; + } + + // Now deal with cancellations + std::vector cancellations = request.cancellations(); + for(std::vector::iterator agentIdIter = cancellations.begin(); agentIdIter != cancellations.end(); agentIdIter++){ + iter->second.removeRequest(*agentIdIter, true); // Remove ALL instances, though there should never be more than one + } + + // The mapped request may now be empty + if(iter->second.requestCountRequested() == 0){ + exportedMap.erase(requestingProc); + processesExportedTo.erase(requestingProc); + } + + reqIter++; + } +} + +std::string Exporter_SET::getReport(){ + std::stringstream ss; + ss << "Exporter_SET: Sending to " << exportedMap.size() << " procs:" << std::endl; + std::map::iterator it = exportedMap.begin(); + const std::map::iterator itEnd = exportedMap.end(); + while(it != itEnd){ + ss << " " << it->first << " -> " << it->second << std::endl; + it++; + } + return ss.str(); +} + +#endif + + +/* AbstractImporterExporter */ + +AbstractImporterExporter::AbstractImporterExporter(AbstractImporter* i, AbstractExporter* e):importer(i), exporter(e){} + +AbstractImporterExporter::~AbstractImporterExporter(){ + delete importer; + delete exporter; +} + +const AbstractExporter::StatusMap* AbstractImporterExporter::getOutgoingStatusChanges(){ + return exporter->getOutgoingStatusChanges(); +} + +void AbstractImporterExporter::exchangeAgentStatusUpdates(boost::mpi::communicator comm, std::vector* >& statusUpdates){ + std::vector requests; + + // Create the appropriate receives... + const std::set& toReceiveFrom = getExportingProcesses(); + + for (std::set::const_iterator iter = toReceiveFrom.begin(); iter != toReceiveFrom.end(); ++iter) { + int exportingProcess = *iter; + std::vector* vec = new std::vector (); + statusUpdates.push_back(vec); + requests.push_back(comm.irecv(exportingProcess, AGENT_SYNC_STATUS, *vec)); + } + + // ... and sends + const std::set& exportedSet = getProcessesExportedTo(); + const AbstractExporter::StatusMap* outgoingStatusChanges = getOutgoingStatusChanges(); + + // Using two iterators, both sorted on proc ID (!) + std::set::iterator exportedSetIter = exportedSet.begin(); + AbstractExporter::StatusMap::const_iterator statusMapIter = outgoingStatusChanges->begin(); + + while((exportedSetIter != exportedSet.end()) || (statusMapIter != outgoingStatusChanges->end())){ + int exportedSetVal = (exportedSetIter != exportedSet.end() ? *exportedSetIter : INT_MAX); + int statusMapVal = (statusMapIter != outgoingStatusChanges->end() ? statusMapIter->first : INT_MAX); + if(statusMapVal == exportedSetVal){ + requests.push_back(comm.isend(statusMapVal, AGENT_SYNC_STATUS, statusMapIter->second)); + exportedSetIter++; + statusMapIter++; + } + else if(statusMapVal < exportedSetVal){ + requests.push_back(comm.isend(statusMapVal, AGENT_SYNC_STATUS, statusMapIter->second)); + statusMapIter++; + } + else if(statusMapVal > exportedSetVal){ + requests.push_back(comm.isend(exportedSetVal, AGENT_SYNC_STATUS, emptyStatus)); + exportedSetIter++; + } + } + + // Exchange data via mpi + boost::mpi::wait_all(requests.begin(), requests.end()); + + // Clear the data from the status map once the sends are complete + clearStatusMap(); + +} + + + +#ifndef OMIT_IMPORTER_EXPORTER_COUNT_LIST +/* Importer_Exporter_COUNT_LIST */ +ImporterExporter_COUNT_LIST::ImporterExporter_COUNT_LIST():AbstractImporterExporter(new Importer_COUNT(), new Exporter_LIST()){} + +#ifdef SHARE_AGENTS_BY_SET +ImporterExporter_COUNT_LIST::ImporterExporter_COUNT_LIST(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo): + AbstractImporterExporter(new Importer_COUNT(), new Exporter_LIST(outgoingStatusMap, outgoingAgentExporterInfo)){} +#endif + +ImporterExporter_COUNT_LIST::~ImporterExporter_COUNT_LIST(){} + +std::string ImporterExporter_COUNT_LIST::version(){ return "COUNT_LIST"; } +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_COUNT_SET +/* Importer_Exporter_COUNT_SET */ +ImporterExporter_COUNT_SET::ImporterExporter_COUNT_SET():AbstractImporterExporter(new Importer_COUNT(), new Exporter_SET()){} + +#ifdef SHARE_AGENTS_BY_SET +ImporterExporter_COUNT_SET::ImporterExporter_COUNT_SET(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo): + AbstractImporterExporter(new Importer_COUNT(), new Exporter_SET(outgoingStatusMap, outgoingAgentExporterInfo)){} +#endif + +ImporterExporter_COUNT_SET::~ImporterExporter_COUNT_SET(){} + +std::string ImporterExporter_COUNT_SET::version(){ return "COUNT_SET"; } +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_LIST +/* ImporterExporter_LIST */ +ImporterExporter_LIST::ImporterExporter_LIST():AbstractImporterExporter(new Importer_LIST(), new Exporter_LIST()){} + +#ifdef SHARE_AGENTS_BY_SET +ImporterExporter_LIST::ImporterExporter_LIST(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo): + AbstractImporterExporter(new Importer_LIST(), new Exporter_LIST(outgoingStatusMap, outgoingAgentExporterInfo)){} +#endif + +ImporterExporter_LIST::~ImporterExporter_LIST(){} + +std::string ImporterExporter_LIST::version(){ return "LIST"; } +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_SET +/* ImporterExporter_SET */ +ImporterExporter_SET::ImporterExporter_SET():AbstractImporterExporter(new Importer_SET(), new Exporter_LIST()){} + +#ifdef SHARE_AGENTS_BY_SET +ImporterExporter_SET::ImporterExporter_SET(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo): + AbstractImporterExporter(new Importer_SET(), new Exporter_LIST(outgoingStatusMap, outgoingAgentExporterInfo)){} +#endif + +ImporterExporter_SET::~ImporterExporter_SET(){} + +std::string ImporterExporter_SET::version(){ return "SET"; } +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_MAP_int +/* ImporterExporter_MAP_int */ +ImporterExporter_MAP_int::ImporterExporter_MAP_int():AbstractImporterExporter(new Importer_MAP_int(), new Exporter_LIST()){} + +#ifdef SHARE_AGENTS_BY_SET +ImporterExporter_MAP_int::ImporterExporter_MAP_int(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo): + AbstractImporterExporter(new Importer_MAP_int(), new Exporter_LIST(outgoingStatusMap, outgoingAgentExporterInfo)){} +#endif + +ImporterExporter_MAP_int::~ImporterExporter_MAP_int(){} + +std::string ImporterExporter_MAP_int::version(){ return "MAP(int)"; } +#endif + + + + + + + +/* ImporterExporter_BY_SET */ + +#ifdef SHARE_AGENTS_BY_SET + +ImporterExporter_BY_SET::ImporterExporter_BY_SET():AbstractImporterExporter(NULL,NULL), + exportingProcessesIsDirty(true), processesExportedToIsDirty(true), exportedMapIsDirty(true){ + outgoingStatusChanges = new AbstractExporter::StatusMap; + outgoingAgentExporterInformation = new AgentExporterData; +} + +ImporterExporter_BY_SET::~ImporterExporter_BY_SET(){ + std::map::iterator iter = importersExportersMap.begin(); + while(iter != importersExportersMap.end()){ + delete iter->second; + iter++; + } + outgoingStatusChanges->clear(); + delete outgoingStatusChanges; + outgoingAgentExporterInformation->clear(); + delete outgoingAgentExporterInformation; +} + +const AbstractExporter::StatusMap* ImporterExporter_BY_SET::getOutgoingStatusChanges(){ + return outgoingStatusChanges; +} + + +AbstractImporterExporter* ImporterExporter_BY_SET::getSet(std::string setName, + AGENT_IMPORTER_EXPORTER_TYPE setType){ + std::map::iterator match = importersExportersMap.find(setName); + if(match != importersExportersMap.end()) return match->second; + + // Otherwise, create a new one, put the ref to it in the map, and return + switch(setType){ +#ifndef OMIT_IMPORTER_EXPORTER_COUNT_LIST + case COUNT_LIST: + return ( importersExportersMap[setName] = new ImporterExporter_COUNT_LIST(outgoingStatusChanges, outgoingAgentExporterInformation) ); +#endif +#ifndef OMIT_IMPORTER_EXPORTER_COUNT_SET + case COUNT_SET: + return ( importersExportersMap[setName] = new ImporterExporter_COUNT_SET(outgoingStatusChanges, outgoingAgentExporterInformation) ); +#endif +#ifndef OMIT_IMPORTER_EXPORTER_LIST + case LIST: + return ( importersExportersMap[setName] = new ImporterExporter_LIST(outgoingStatusChanges, outgoingAgentExporterInformation) ); +#endif +#ifndef OMIT_IMPORTER_EXPORTER_SET + case SET: + return ( importersExportersMap[setName] = new ImporterExporter_SET(outgoingStatusChanges, outgoingAgentExporterInformation) ); +#endif +#ifndef OMIT_IMPORTER_EXPORTER_MAP_int + case MAP_int: + return ( importersExportersMap[setName] = new ImporterExporter_MAP_int(outgoingStatusChanges, outgoingAgentExporterInformation) ); +#endif + } + return NULL; +} + + +void ImporterExporter_BY_SET::rebuildExportingProcesses(bool forceRebuild){ + if(exportingProcessesIsDirty || forceRebuild){ + exportingProcesses.clear(); + std::map::iterator ieIter = importersExportersMap.begin(); + while(ieIter != importersExportersMap.end()){ + AbstractImporterExporter* ie = ieIter->second; + const std::set& toAdd = ie->getExportingProcesses(); + exportingProcesses.insert(toAdd.begin(), toAdd.end()); + ieIter++; + } + exportingProcessesIsDirty = false; + } +} + +void ImporterExporter_BY_SET::rebuildProcessesExportedTo(bool forceRebuild){ + if(processesExportedToIsDirty || forceRebuild){ + processesExportedTo.clear(); + std::map::iterator ieIter = importersExportersMap.begin(); + while(ieIter != importersExportersMap.end()){ + AbstractImporterExporter* ie = ieIter->second; + const std::set& toAdd = ie->getProcessesExportedTo(); + processesExportedTo.insert(toAdd.begin(), toAdd.end()); + ieIter++; + } + processesExportedToIsDirty = false; + } +} + +void ImporterExporter_BY_SET::rebuildExportedMap(bool forceRebuild){ + if(exportedMapIsDirty || forceRebuild){ + exportedMap.clear(); + std::map > idsByProc; + std::map::iterator ie = importersExportersMap.begin(); + // Collect the agents to be exported in a set first; this will eliminate duplicates + while(ie != importersExportersMap.end()){ + std::map reqs = ie->second->getAgentsToExport(); + std::map::iterator procIter = reqs.begin(); + while(procIter != reqs.end()){ + std::set& set = idsByProc[procIter->first]; + AgentRequest& r = procIter->second; + set.insert(r.requestedAgents().begin(), r.requestedAgents().end()); + procIter++; + } + ie++; + } + std::map >::iterator setIter = idsByProc.begin(); + while(setIter != idsByProc.end()){ + AgentRequest& req = exportedMap[setIter->first]; + std::set::iterator idListIter = setIter->second.begin(); + while(idListIter != setIter->second.end()){ + req.addRequest(*idListIter); + idListIter++; + } + setIter++; + } + exportedMapIsDirty = false; + } +} + + +const std::set& ImporterExporter_BY_SET::getExportingProcesses(){ + return getExportingProcesses(REQUEST_AGENTS_ALL); +} + +const std::set& ImporterExporter_BY_SET::getExportingProcesses(std::string setName){ + if(setName != REQUEST_AGENTS_ALL) return getSet(setName)->getExportingProcesses(); + else{ + rebuildExportingProcesses(); + return exportingProcesses; + } +} + + +void ImporterExporter_BY_SET::registerOutgoingRequests(AgentRequest& request){ + registerOutgoingRequests(request, DEFAULT_AGENT_REQUEST_SET, DEFAULT_ENUM_SYMBOL); +} + +void ImporterExporter_BY_SET::registerOutgoingRequests(AgentRequest& request, + std::string setName, AGENT_IMPORTER_EXPORTER_TYPE setType){ + getSet(setName, setType)->registerOutgoingRequests(request); + processesExportedToIsDirty = true; // May not need all of these + exportingProcessesIsDirty = true; + exportedMapIsDirty = true; +} + + +void ImporterExporter_BY_SET::importedAgentIsRemoved(const AgentId& id){ + std::map::iterator mapIter = importersExportersMap.begin(); + while(mapIter != importersExportersMap.end()){ + mapIter->second->importedAgentIsRemoved(id); + mapIter++; + } + processesExportedToIsDirty = true; // May not need all of these + exportingProcessesIsDirty = true; + exportedMapIsDirty = true; +} + +void ImporterExporter_BY_SET::importedAgentIsMoved(const AgentId& id, int newProcess){ + std::map::iterator mapIter = importersExportersMap.begin(); + while(mapIter != importersExportersMap.end()){ + mapIter->second->importedAgentIsMoved(id, newProcess); + mapIter++; + } + processesExportedToIsDirty = true; // May not need all of these + exportingProcessesIsDirty = true; + exportedMapIsDirty = true; +} + +void ImporterExporter_BY_SET::importedAgentIsNowLocal(const AgentId& id){ + importedAgentIsRemoved(id); +} + + + +const std::set& ImporterExporter_BY_SET::getProcessesExportedTo(){ + return getProcessesExportedTo(REQUEST_AGENTS_ALL); +} + +const std::set& ImporterExporter_BY_SET::getProcessesExportedTo(std::string setName){ + if(setName != REQUEST_AGENTS_ALL) return getSet(setName)->getProcessesExportedTo(); + else{ + rebuildProcessesExportedTo(); + return processesExportedTo; + } +} + + +void ImporterExporter_BY_SET::registerIncomingRequests(std::vector& requests){ + registerIncomingRequests(requests, DEFAULT_AGENT_REQUEST_SET); +} + +void ImporterExporter_BY_SET::registerIncomingRequests(std::vector& requests, std::string setName){ + getSet(setName)->registerIncomingRequests(requests); + processesExportedToIsDirty = true; // May not need all of these + exportingProcessesIsDirty = true; + exportedMapIsDirty = true; +} + +void ImporterExporter_BY_SET::agentRemoved(const AgentId& id){ + std::map::iterator mapIter = importersExportersMap.begin(); + while(mapIter != importersExportersMap.end()){ + outgoingAgentExporterInformation->selectSet(mapIter->first); + mapIter->second->agentRemoved(id); + mapIter++; + } + processesExportedToIsDirty = true; // May not need all of these + exportingProcessesIsDirty = true; + exportedMapIsDirty = true; +} + +void ImporterExporter_BY_SET::agentMoved(const AgentId& id, int process){ + std::map::iterator mapIter = importersExportersMap.begin(); + while(mapIter != importersExportersMap.end()){ + outgoingAgentExporterInformation->selectSet(mapIter->first); + mapIter->second->agentMoved(id, process); + mapIter++; + } + processesExportedToIsDirty = true; // May not need all of these + exportingProcessesIsDirty = true; + exportedMapIsDirty = true; +} + +void ImporterExporter_BY_SET::incorporateAgentExporterInfo(std::map info){ + getSet(DEFAULT_AGENT_REQUEST_SET)->incorporateAgentExporterInfo(info); +} + +void ImporterExporter_BY_SET::incorporateAgentExporterInfo(std::map* > info){ + std::map* >::iterator setIter = info.begin(); + while(setIter != info.end()){ + getSet(setIter->first)->incorporateAgentExporterInfo(*(setIter->second)); // In theory this could create a new set, but really only extant sets should be passed + setIter++; + } + processesExportedToIsDirty = true; // May not need all of these + exportingProcessesIsDirty = true; + exportedMapIsDirty = true; +} + +void ImporterExporter_BY_SET::clearStatusMap(){ + outgoingStatusChanges->clear(); +} + +AgentExporterInfo* ImporterExporter_BY_SET::getAgentExportInfo(int destProc){ + return outgoingAgentExporterInformation->dataForProc(destProc); +} + +void ImporterExporter_BY_SET::clearAgentExportInfo(){ + outgoingAgentExporterInformation->clear(); +} + +const std::map& ImporterExporter_BY_SET::getAgentsToExport(){ + return getAgentsToExport(REQUEST_AGENTS_ALL); +} + +const std::map& ImporterExporter_BY_SET::getAgentsToExport(std::string setName){ + if(setName != REQUEST_AGENTS_ALL) return getSet(setName)->getAgentsToExport(); + else{ + rebuildExportedMap(); + return exportedMap; + } +} + + +void ImporterExporter_BY_SET::getSetOfAgentsBeingImported(std::set& set){ + std::map::iterator mapIter = importersExportersMap.begin(); + while(mapIter != importersExportersMap.end()){ + mapIter->second->getSetOfAgentsBeingImported(set); + mapIter++; + } +} + +void ImporterExporter_BY_SET::getSetOfAgentsBeingImported(std::set& set, std::string excludeSet){ + std::map::iterator mapIter = importersExportersMap.begin(); + while(mapIter != importersExportersMap.end()){ + if(mapIter->first.compare(excludeSet) != 0) mapIter->second->getSetOfAgentsBeingImported(set); + mapIter++; + } +} + +std::string ImporterExporter_BY_SET::version(){ return "BY SET: " +#if DEFAULT_IMPORTER_EXPORTER == 1 + "COUNT_LIST" +#elif DEFAULT_IMPORTER_EXPORTER == 2 + "COUNT_SET" +#elif DEFAULT_IMPORTER_EXPORTER == 3 + "LIST" +#elif DEFAULT_IMPORTER_EXPORTER == 4 + "SET" +#elif DEFAULT_IMPORTER_EXPORTER == 5 + "MAP(int)" +#endif + ; } + +#endif diff --git a/libs/repast_hpc/AgentImporterExporter.h b/libs/repast_hpc/AgentImporterExporter.h new file mode 100644 index 0000000..d7d266a --- /dev/null +++ b/libs/repast_hpc/AgentImporterExporter.h @@ -0,0 +1,1090 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Created on: Jun 14, 2011 + * Author: John T. Murphy + */ + +#ifndef AGENTIMPORTEREXPORTER_H_ +#define AGENTIMPORTEREXPORTER_H_ + +#include +#include +#include + +#include "AgentRequest.h" +#include "AgentId.h" +#include "AgentStatus.h" +#include "mpi_constants.h" + +/* + * If you wish to select the default + * kind of Importer/Exporter, use one + * of the valid values here (default + * if not specified is 1): + * + * 1 = COUNT_LIST + * 2 = COUNT_SET + * 3 = LIST + * 4 = SET + * 5 = MAP(int) + */ +//#define DEFAULT_IMPORTER_EXPORTER 5 + +/* + * If defined, allow multiple importer/exporter + * instances managing distinct sets of agents + */ +#define SHARE_AGENTS_BY_SET + +/* + * If you wish to allow a single cancellation to be used + * to cancel all of an agent's requests (for semantics + * where multiple cancellations would ordinarily be + * required), define this flag + */ +//#define ALLOW_FULL_AGENT_REQUEST_CANCELLATION + + +/* Preprocessor configuration */ + +#ifndef DEFAULT_IMPORTER_EXPORTER + #define DEFAULT_IMPORTER_EXPORTER 4 +#endif + +#if DEFAULT_IMPORTER_EXPORTER < 1 || DEFAULT_IMPORTER_EXPORTER > 5 + #error "Invalid value used for default Importer_Exporter" +#endif + +// If you are not permitting agent request by set, +// then only one of the importer_exporters should be compiled +// (The one chosen as the default) +#ifndef SHARE_AGENTS_BY_SET + + #if DEFAULT_IMPORTER_EXPORTER != 1 && !defined OMIT_IMPORTER_EXPORTER_COUNT_LIST + #define OMIT_IMPORTER_EXPORTER_COUNT_LIST + #endif + + #if DEFAULT_IMPORTER_EXPORTER != 2 && !defined OMIT_IMPORTER_EXPORTER_COUNT_SET + #define OMIT_IMPORTER_EXPORTER_COUNT_SET + #endif + + #if DEFAULT_IMPORTER_EXPORTER != 3 && !defined OMIT_IMPORTER_EXPORTER_LIST + #define OMIT_IMPORTER_EXPORTER_LIST + #endif + + #if DEFAULT_IMPORTER_EXPORTER != 4 && !defined OMIT_IMPORTER_EXPORTER_SET + #define OMIT_IMPORTER_EXPORTER_SET + #endif + + #if DEFAULT_IMPORTER_EXPORTER != 5 && !defined OMIT_IMPORTER_EXPORTER_MAP_int + #define OMIT_IMPORTER_EXPORTER_MAP_int + #endif + +#endif + + + +// Cannot set both 'default' and 'omit'; setting one +// as default overrides the omit +#if DEFAULT_IMPORTER_EXPORTER == 1 + #define DEFAULT_IMPORTER_EXPORTER_CLASS ImporterExporter_COUNT_LIST + #define DEFAULT_ENUM_SYMBOL COUNT_LIST + #undef OMIT_IMPORTER_EXPORTER_COUNT_LIST +#endif + +#if DEFAULT_IMPORTER_EXPORTER == 2 + #define DEFAULT_IMPORTER_EXPORTER_CLASS ImporterExporter_COUNT_SET + #define DEFAULT_ENUM_SYMBOL COUNT_SET + #undef OMIT_IMPORTER_EXPORTER_COUNT_SET +#endif + +#if DEFAULT_IMPORTER_EXPORTER == 3 + #define DEFAULT_IMPORTER_EXPORTER_CLASS ImporterExporter_LIST + #define DEFAULT_ENUM_SYMBOL LIST + #undef OMIT_IMPORTER_EXPORTER_LIST +#endif + +#if DEFAULT_IMPORTER_EXPORTER == 4 + #define DEFAULT_IMPORTER_EXPORTER_CLASS ImporterExporter_SET + #define DEFAULT_ENUM_SYMBOL SET + #undef OMIT_IMPORTER_EXPORTER_SET +#endif + +#if DEFAULT_IMPORTER_EXPORTER == 5 + #define DEFAULT_IMPORTER_EXPORTER_CLASS ImporterExporter_MAP_int + #define DEFAULT_ENUM_SYMBOL MAP_int + #undef OMIT_IMPORTER_EXPORTER_MAP_int +#endif + + + +#ifdef SHARE_AGENTS_BY_SET +// This enum is useful when requests by set are allowed +// Result of this construction is enum AGENT_IMPORTER_EXPORTER_TYPE{ Default[, non-default1][, non-default2] }, with non-compiled classes omitted +enum AGENT_IMPORTER_EXPORTER_TYPE{ DEFAULT_ENUM_SYMBOL +#if !defined OMIT_IMPORTER_EXPORTER_COUNT_LIST && DEFAULT_IMPORTER_EXPORTER != 1 +, COUNT_LIST +#endif +#if !defined OMIT_IMPORTER_EXPORTER_COUNT_SET && DEFAULT_IMPORTER_EXPORTER != 2 +, COUNT_SET +#endif +#if !defined OMIT_IMPORTER_EXPORTER_LIST && DEFAULT_IMPORTER_EXPORTER != 3 +, LIST +#endif +#if !defined OMIT_IMPORTER_EXPORTER_SET && DEFAULT_IMPORTER_EXPORTER != 4 +, SET +#endif +#if !defined OMIT_IMPORTER_EXPORTER_MAP_int && DEFAULT_IMPORTER_EXPORTER != 5 +, MAP_int +#endif +}; + +#define DEFAULT_AGENT_REQUEST_SET "__Default_Agent_Request_Set__" + +#endif + +/* END PREPROCESSOR */ + + +#define REQUEST_AGENTS_ALL "AGENTS_FROM_ALL_IMPORTER_EXPORTERS" + + +namespace repast{ + + +#ifndef SHARE_AGENTS_BY_SET +typedef std::map AgentExporterInfo; +#else +typedef std::map* > AgentExporterInfo; +#endif + +/** + * Data structure for exporter data that is to be sent + * to other processes when the agents being exported are + * moved. Note that the internal data structure is protected; + * classes can use this data without knowing its actual internal + * structure. + */ +class AgentExporterData{ +private: + AgentExporterInfo empty; // Used as dummy for sending empty data + std::map data; + +#ifdef SHARE_AGENTS_BY_SET + std::string selectedSet; +#endif + +public: + AgentExporterData(); + ~AgentExporterData(); + + /** + * Adds an agent ID to this list of data that is being exported + * to a specific processor (destProc), so that the agent's + * information will be exported to another processor (sourceProc). + */ + void addData(const AgentId& id, const int destProc, const int sourceProc, + const int numberOfCopies = 1); + + /** + * Gets the packaged set of information to be sent to a specific + * processor + */ + AgentExporterInfo* dataForProc(int destProc); + + /** + * Clears this data structure + */ + void clear(); + + /** + * Remove all the data for a specific agent; useful + * when the agent is removed + */ + void removeAllDataForAgent(AgentId& id); + + +#ifdef SHARE_AGENTS_BY_SET + /** + * Specifies that add and retrieve actions are to be + * performed on the subset of data identified by the given set + * name. (Does not affect 'clear' or 'removeAllDataForAgent') + */ + void selectSet(std::string setName); +#endif + +}; + + + + +/* Importers */ + +#ifdef ALLOW_FULL_AGENT_REQUEST_CANCELLATION +/** + * Marks an ID as a 'full cancellation,' meaning + * all previous requests for this agent will be + * cancelled. + */ +void markIdAsFullCancellation(AgentId& id){ + id.currentRank(((-1) * id.currentRank()) - 1); +} + +/** + * Checks an AgentId to see if it is marked as a 'full cancellation'; + * if it is, MODIFIES IT back to its original, unmarked + * state and returns true + * + * @param id the AgentId to be checked; modified back to + * a normal AgentId if found to be a full cancellation + * @return true if the AgentId was in a state that marked it + * as a full cancellation, false otherwise + */ +bool checkForFullCancellation(AgentId& id){ + if(id.currentRank() < 0){ + id.currentRank((id.currentRank() + 1) * (-1)); + return true; + } + else return false; +} +#endif + + + +/* IMPORTER */ + +/** + * This class manages importing agent information; primarily + * this means constructing the appropriate mpi receives when + * agent information is to be exchanged. However, this class + * can also define specific semantics that can apply to + * agent requests- what to do in the case that an agent + * is requested twice, for example. + */ +class AbstractImporter{ +protected: + // Each child implementation is required to maintain this list + std::set exportingProcesses; + +public: + + AbstractImporter(); + virtual ~AbstractImporter(); + + /** + * Gets a const reference to the set of ints representing the processes that are + * sending this process agent information + */ + virtual const std::set& getExportingProcesses(){ return exportingProcesses; } + + /** + * Given an agent request (including requests for agents on multiple other + * processes), makes a record of the agents that are being requested by this + * process and will therefore be received from other processes. The record must + * at a minimum indicate which other processes are sending agent information, + * but may include other information, such as how many times a particular + * agent has been requested. + */ + virtual void registerOutgoingRequests(AgentRequest &req) = 0; + + /** + * Notifies this importer that the agent that it (presumably) has been importing + * has been removed from the simulation on its home process, and the information + * for that agent will no longer be sent + */ + virtual void importedAgentIsRemoved(const AgentId& id) = 0; + + /** + * Notifies this importer that the agent that it (presumably) has been importing + * from another process has been moved; its information will now be received + * from its new home process (unless the agent was moved to this process) + */ + virtual void importedAgentIsMoved(const AgentId& id, int newProcess) = 0; + + /** + * Some semantic sugar; operationally this is the same as 'importedAgentIsRemoved' + */ + inline void importedAgentIsNowLocal(const AgentId& id){ importedAgentIsRemoved(id); } + + /** + * Get a printable indication of the data in this object + */ + virtual std::string getReport() = 0; + + virtual void getSetOfAgentsBeingImported(std::set& set) = 0; + + virtual void clear(){ + exportingProcesses.clear(); + } +}; + + +#if !defined OMIT_IMPORTER_EXPORTER_COUNT_LIST || \ + !defined OMIT_IMPORTER_EXPORTER_COUNT_SET + +/** + * Importer that maintains a simple count of the agents being sent + * from each sending process. When the count for a given process is + * zero no mpi 'receive' is created for that process; when the count is + * nonzero, a 'receive' is created. Note that no record + * of which agents are requested is kept, only the count of + * agents requested is kept. + */ +class Importer_COUNT: public AbstractImporter{ + +private: + std::map sources; + void incrementCount(int sourceProcess); + void decrementCount(int sourceProcess); + +public: + Importer_COUNT(); + virtual ~Importer_COUNT(); + + virtual void registerOutgoingRequests(AgentRequest &req); + virtual void importedAgentIsRemoved(const AgentId& id); + virtual void importedAgentIsMoved(const AgentId& id, int newProcess); + + virtual std::string getReport(); + virtual void getSetOfAgentsBeingImported(std::set& set); +}; +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_LIST + +/** + * Importer that maintains a list of the agents being sent from + * each sending process. If there are no agents being sent the + * size of the list will be zero and no mpi 'receive' will be + * created; for non-zero-length lists a single mpi 'receiver' + * is created. An agent that is requested twice is + * placed in the list twice. Semantically this means that an + * agent cancellation will remove the agent from the list exactly + * once, but also that removing an agent from the list who is + * not found in the list will not reduce the list size (c.f. the + * 'count' version, where every cancellation reduces the count + * whether the specific agent being canceled was ever requested + * at all). + */ +class Importer_LIST: public AbstractImporter{ + +private: + std::map* > sources; + + // Inline helper functions to manage the internal recordkeeping + + // Gets the record associated with the specified other process; + // if none exists, one is created. 'exportingProcesses' is updated + inline std::list* getRecord(int rank); + + // Removes all the entries for a given agent from the specified record; + // Returns the number of entries removed + // Also: Invokes 'checkRecord' + inline int removeAll(const AgentId& id, std::list* record, int rank); + + // Removes one entry for a given ID (passes through to removeId by position) + inline void removeID(AgentId& id, std::list* record, int rank); + + // Removes the entry at the specified position in the record list. + // Also: Invokes 'checkRecord' + inline void removeID(std::list::iterator idPosition, std::list* record, int rank); + + // Checks the length of this record; if the record is now empty, + // it is removed from the map (and exportingProcesses is updated) + inline void checkRecord(std::list* record, int rank); + +public: + Importer_LIST(); + ~Importer_LIST(); + + virtual void registerOutgoingRequests(AgentRequest& req); + virtual void importedAgentIsRemoved(const AgentId& id); + virtual void importedAgentIsMoved(const AgentId& id, int newProcess); + + virtual std::string getReport(); + virtual void getSetOfAgentsBeingImported(std::set& set); + + virtual void clear(){ + AbstractImporter::clear(); + sources.clear(); + } +}; +#endif + + +#ifndef OMIT_IMPORTER_EXPORTER_SET + +/** + * Importer that maintains a set of agents being sent from + * each sending process. Note that because this is a set, + * an agent may appear in it only once, no matter how many + * times it is requested. Canceling the agent will remove + * it from the set, even if the agent was requested multiple + * duplicate times. If the set for a given process is size + * zero, no mpi 'receive' will be created; if the set has any + * elements, an mpi 'receive' will be created. + */ +class Importer_SET: public AbstractImporter{ +private: + std::map* > sources; + + // Helper functions to manage internal bookkeeping + + // Gets the record associated with the specified other + // rank. If none exists, one is created (and exportingProcesses + // is updated). + inline std::set* getRecord(int rank); + + // Removes an ID from the specified set. + // If this results in an actual removal, will return 1; if not, + // will return zero. AgentID will not be in map after operation. + // Also: Invokes checkRecord + inline int removeID(const AgentId& id, std::set* record, int rank); + + // Checks the size of the record passed; if zero, deletes it from + // the map and updates exportingProcesses + inline void checkRecord(std::set* record, int rank); + +public: + Importer_SET(); + ~Importer_SET(); + + virtual void registerOutgoingRequests(AgentRequest& req); + virtual void importedAgentIsRemoved(const AgentId& id); + virtual void importedAgentIsMoved(const AgentId& id, int newProcess); + + virtual std::string getReport(); + virtual void getSetOfAgentsBeingImported(std::set& set); + virtual void clear(){ + AbstractImporter::clear(); + sources.clear(); + } +}; +#endif + + +#ifndef OMIT_IMPORTER_EXPORTER_MAP_int +/** + * Importer that maintains a map of agents being sent from + * each sending process and a count of the number of times + * that agent was requested. This is semantically equivalent + * to a 'LIST' type, but may be more or less appropriate + * in specific contexts based on performance. Duplicate + * requests for an agent increment the count associated with + * that agent; canceling a request reduces that count. + * If a given sending process has no agents being shared, no + * mpi 'receive' will be created, but if there are agents + * being shared, a 'receive' will be created. + */ +class Importer_MAP_int: public AbstractImporter{ +private: + std::map* > sources; + + // Helper functions to manage internal bookkeeping + + // Gets the record associated with the specified rank + // If one does not exist, it is created, and exportingProcesses + // is updated + std::map* getRecord(int rank); + + // Removes the entry for the specified agent. + // Returns the number associated with that agent (essentially the number of 'entries' removed) + // Also: invokes checkRecord + inline int removeAll(const AgentId& id, std::map* record, int rank); + + // Removes a single entry for the specified ID (decrements its count) + // If the entry does not exist returns 0, otherwise + // returns one (representing one entry removed) + // Also: invokes checkRecord + inline int removeID(const AgentId& id, std::map* record, int rank); + + // Removes the entire entry for a given agent, by position. Does not return + // any indication of success of operation + // Invokes checkRecord + inline void removeID(std::map::iterator idPosition, std::map* record, int rank); + + // Checks if the record size is zero; if so, deletes it and notifies exportingProcesses + inline void checkRecord(std::map* record, int rank); + + +public: + Importer_MAP_int(); + ~Importer_MAP_int(); + + virtual void registerOutgoingRequests(AgentRequest& req); + virtual void importedAgentIsRemoved(const AgentId& id); + virtual void importedAgentIsMoved(const AgentId& id, int newProcess); + + virtual std::string getReport(); + virtual void getSetOfAgentsBeingImported(std::set& set); + + virtual void clear(){ + AbstractImporter::clear(); + sources.clear(); + } +}; +#endif + + + + + + + + + + +/* Exporters */ + +static std::vector emptyStatus; + +/** + * Responsible for keeping a list of the agents that have + * been requested by other processes for which data is to be sent + * when agents' states are synchronized, and for packaging and sending + * that data during synchronization. It is also responsible + * for exchanging this 'export' information when any of the + * agents that it is exporting are being moved to other + * processes; when an agent moves, its new home process must + * be able to assume the same export duties that its original + * process was performing. + */ +class AbstractExporter{ +public: + typedef std::map > StatusMap; + +private: + StatusMap* outgoingStatusChangesDeletePtr; + AgentExporterData* outgoingAgentExporterInformationDeletePtr; + +protected: + StatusMap* outgoingStatusChanges; + AgentExporterData* outgoingAgentExporterInformation; + + std::set processesExportedTo; + std::map exportedMap; + +public: + + AbstractExporter(); + virtual ~AbstractExporter(); + + /** + * Makes a record of the data receives (in the form of a vector of AgentRequests) + * so that the agents' data can be sent to the requesting processes. + */ + virtual void registerIncomingRequests(std::vector& requests) = 0; + + /** + * The set of information received here comprises the information that some + * other process was using to export information about agents that are + * now being moved to this process. This method takes that information + * and incorporates it into this exporter, so that this exporter can now + * export the agents' information to the processes that have requested it. + */ + virtual void incorporateAgentExporterInfo(std::map info); + + + /** + * 1) Removes the agent export information from this process + * 2) Updates the outgoing status change buffer to include + * the status change for this agent to all procs + * to which this agent was being exported (except + * if one of these was the proc to which the agent + * is now moving; this is omitted) + */ + virtual void agentRemoved(const AgentId& id); + + /** + * 1) Removes the agent export information from this process + * 2) Places a copy of the agent export information into the + * outgoing buffer + * 3) Updates the outgoing status change buffer to include + * the status change for this agent to all procs + * to which this agent was being exported (except + * if one of these was the proc to which the agent + * is now moving; this is omitted) + */ + virtual void agentMoved(const AgentId& id, int process); + + /** + * Gets the list of processes this exporter is sending information + * to. + */ + virtual const std::set& getProcessesExportedTo(); + + /** + * Gets the export information that has been placed into the 'outgoing agent + * export information' buffer because agents that were being exported are being + * sent to a new process, for the specified process. + */ + AgentExporterInfo* getAgentExportInfo(int destProc); + + /** + * Gets the set of status changes for the exported agents. + */ + const StatusMap* getOutgoingStatusChanges(); + + /** + * Clears the outgoing agent export information buffer; should be + * called after the information is sent. + */ + void clearAgentExportInfo(); + + /** + * Clears the outgoing status information buffer; should + * be called after the information is sent + */ + void clearStatusMap(); + + /** + * Gets the list of agents being exported by this exported, + * as a map by ints representing the processes to which + * information will be sent. + */ + virtual const std::map& getAgentsToExport(); + +#ifdef SHARE_AGENTS_BY_SET +public: + AbstractExporter(StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo); +#endif + + /** + * Gets a printable report of the state of this object + */ + virtual std::string getReport() = 0; + + virtual void clear(){ + outgoingStatusChanges->clear(); + outgoingAgentExporterInformation->clear(); + + processesExportedTo.clear(); + exportedMap.clear(); + } + + virtual void clearExportToSpecificProc(int rank){ + processesExportedTo.erase(rank); + exportedMap.erase(rank); + } + +}; + + +#if !defined OMIT_IMPORTER_EXPORTER_COUNT_LIST || \ + !defined OMIT_IMPORTER_EXPORTER_LIST || \ + !defined OMIT_IMPORTER_EXPORTER_SET || \ + !defined OMIT_IMPORTER_EXPORTER_MAP_int + +/** + * Maintains a list of agents being exported for each receiving + * process. An agent that is requested more than once will + * appear on this list more than once; canceling an agent just + * once removes only one of its appearances on this list. + * A 'send' will be created for a receiving process only if that + * process has entries in the list. + */ +class Exporter_LIST: public AbstractExporter{ +private: + +public: + Exporter_LIST(); + virtual ~Exporter_LIST(); + + virtual void registerIncomingRequests(std::vector& requests); + +#ifdef SHARE_AGENTS_BY_SET +public: + Exporter_LIST(StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo); +#endif + + virtual std::string getReport(); +}; +#endif + + +#ifndef OMIT_IMPORTER_EXPORTER_COUNT_SET + +/** + * Maintains a set of agents being exported for each receiving + * process. An agent that is requested more than once will + * appear in this set only once; canceling an agent just + * once removes it from this set. A 'send' will be created + * for a receiving process only if that process has entries in + * the list. + */ +class Exporter_SET: public AbstractExporter{ + +public: + Exporter_SET(); + virtual ~Exporter_SET(); + + virtual void registerIncomingRequests(std::vector& requests); + +#ifdef SHARE_AGENTS_BY_SET +public: + Exporter_SET(StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo); +#endif + + virtual std::string getReport(); +}; +#endif + + +/* Importer and Exporter */ + +/** + * Wraps and Importer and an Exporter so that both use + * commensurate semantics and all imports and exports + * are balanced. Most methods are pass-through to the underlying + * importer or exporter. + */ +class AbstractImporterExporter{ + +protected: + AbstractImporter* importer; + AbstractExporter* exporter; + +public: + AbstractImporterExporter(AbstractImporter* i, AbstractExporter* e); + virtual ~AbstractImporterExporter(); + + // Pass-through methods for all importer and exporter public methods + virtual const std::set& getExportingProcesses(){ return importer->getExportingProcesses(); } + virtual void registerOutgoingRequests(AgentRequest &req){ importer->registerOutgoingRequests(req); } + virtual void importedAgentIsRemoved(const AgentId& id){ importer->importedAgentIsRemoved(id); } + virtual void importedAgentIsMoved(const AgentId& id, int newProcess){ importer->importedAgentIsMoved(id, newProcess); } + + virtual void importedAgentIsNowLocal(const AgentId& id){ importer->importedAgentIsNowLocal(id); } + + virtual void getSetOfAgentsBeingImported(std::set& set){ importer->getSetOfAgentsBeingImported(set); } + + virtual const AbstractExporter::StatusMap* getOutgoingStatusChanges(); + + virtual const std::set& getProcessesExportedTo(){ return exporter->getProcessesExportedTo(); } + virtual void registerIncomingRequests(std::vector& requests){ exporter->registerIncomingRequests(requests); } + virtual void agentRemoved(const AgentId& id){ exporter->agentRemoved(id); } + virtual void agentMoved(const AgentId& id, int process){ exporter->agentMoved(id, process); } + virtual void incorporateAgentExporterInfo(std::map info){ exporter->incorporateAgentExporterInfo(info); } + virtual void clearStatusMap(){ exporter->clearStatusMap(); } + virtual AgentExporterInfo* getAgentExportInfo(int destProc){ return exporter->getAgentExportInfo(destProc); } + virtual void clearAgentExportInfo(){ exporter->clearAgentExportInfo(); } + virtual const std::map& getAgentsToExport(){ return exporter->getAgentsToExport(); } + + /** + * Exchanges the contents of the 'statusMap' with the destination processes, updating + * the status (moved or removed) for all agents being exported. Returns this information + * in the statusUpdates vector. + */ + virtual void exchangeAgentStatusUpdates(boost::mpi::communicator comm, std::vector* >& statusUpdates); + + /** + * Returns the version of this AbstractImporterExporter. The version is a string + * that indicates the semantic version of the importer and the exporter (e.g. + * "COUNT_LIST") + */ + virtual std::string version() = 0; + + /** + * Gets a printable report of the state of this object + */ + virtual std::string getReport(){ return importer->getReport() + exporter->getReport(); } + + virtual void clear(){ + importer->clear(); + exporter->clear(); + } + + virtual void clearExporter(){ + exporter->clear(); + } + + virtual void clearExportToSpecificProc(int rank){ + exporter->clearExportToSpecificProc(rank); + } + +}; + +/* Normal variants, with semantics defined by which importer/exporter combination is used */ + +#ifndef OMIT_IMPORTER_EXPORTER_COUNT_LIST + +/** + * An implementation of AbstractImporterExporter that uses + * an importer of type 'Importer_COUNT' and an exporter of + * type 'Exporter_LIST'. + */ +class ImporterExporter_COUNT_LIST: public AbstractImporterExporter{ +public: + ImporterExporter_COUNT_LIST(); + +#ifdef SHARE_AGENTS_BY_SET + ImporterExporter_COUNT_LIST(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo); +#endif + + virtual ~ImporterExporter_COUNT_LIST(); + + virtual std::string version(); + +}; +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_COUNT_SET +/** + * An implementation of AbstractImporterExporter that uses + * an importer of type 'Importer_COUNT' and an exporter of + * type 'Exporter_SET'. + */ +class ImporterExporter_COUNT_SET: public AbstractImporterExporter{ +public: + ImporterExporter_COUNT_SET(); + +#ifdef SHARE_AGENTS_BY_SET + ImporterExporter_COUNT_SET(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo); +#endif + + virtual ~ImporterExporter_COUNT_SET(); + + virtual std::string version(); +}; +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_LIST + +/** + * An implementation of AbstractImporterExporter that uses + * an importer of type 'Importer_LIST' and an exporter of + * type 'Exporter_LIST'. + */ +class ImporterExporter_LIST: public AbstractImporterExporter{ +public: + ImporterExporter_LIST(); + +#ifdef SHARE_AGENTS_BY_SET + ImporterExporter_LIST(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo); +#endif + + virtual ~ImporterExporter_LIST(); + + virtual std::string version(); +}; +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_SET +/** + * An implementation of AbstractImporterExporter that uses + * an importer of type 'Importer_SET' and an exporter of + * type 'Exporter_LIST'. + */ +class ImporterExporter_SET: public AbstractImporterExporter{ +public: + ImporterExporter_SET(); + +#ifdef SHARE_AGENTS_BY_SET + ImporterExporter_SET(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo); +#endif + + virtual ~ImporterExporter_SET(); + + virtual std::string version(); +}; +#endif + +#ifndef OMIT_IMPORTER_EXPORTER_MAP_int +/** + * An implementation of AbstractImporterExporter that uses + * an importer of type 'Importer_MAP_int' and an exporter of + * type 'Exporter_LIST'. + */ +class ImporterExporter_MAP_int: public AbstractImporterExporter{ +public: + ImporterExporter_MAP_int(); + +#ifdef SHARE_AGENTS_BY_SET + ImporterExporter_MAP_int(AbstractExporter::StatusMap* outgoingStatusMap, AgentExporterData* outgoingAgentExporterInfo); +#endif + + virtual ~ImporterExporter_MAP_int(); + + virtual std::string version(); +}; +#endif + + +/* "BY SET" variant; allows multiple sets of shared agents to be managed independently */ + +#ifdef SHARE_AGENTS_BY_SET + +/** + * Implementation of the AbstractImporterExporter class that wraps a + * collection of AbstractImporterExporter objects that can be referenced + * by name. Each object can contain a different collection of agents, + * which were requested with an associated name for the importer/exporter + * to be used. They can then be updated separately, allowing for improved + * performance under certain circumstances. + */ +class ImporterExporter_BY_SET: public AbstractImporterExporter{ + +private: + + AbstractExporter::StatusMap* outgoingStatusChanges; + AgentExporterData* outgoingAgentExporterInformation; + std::map importersExportersMap; + std::set exportingProcesses; + bool exportingProcessesIsDirty; + std::set processesExportedTo; + bool processesExportedToIsDirty; + std::map exportedMap; + bool exportedMapIsDirty; + + AbstractImporterExporter* getSet(std::string setname, + AGENT_IMPORTER_EXPORTER_TYPE setType = DEFAULT_ENUM_SYMBOL); + + + inline void rebuildExportingProcesses(bool forceRebuild = false); + + inline void rebuildProcessesExportedTo(bool forceRebuild = false); + + inline void rebuildExportedMap(bool forceRebuild = false); + +public: + ImporterExporter_BY_SET(); + + virtual ~ImporterExporter_BY_SET(); + + + /* Importer-related functions */ + + virtual const std::set& getExportingProcesses(); + const std::set& getExportingProcesses(std::string setName); + + + virtual void registerOutgoingRequests(AgentRequest& request); + void registerOutgoingRequests(AgentRequest& request, std::string setName, + AGENT_IMPORTER_EXPORTER_TYPE setType = DEFAULT_ENUM_SYMBOL); + + virtual void importedAgentIsRemoved(const AgentId& id); + + virtual void importedAgentIsMoved(const AgentId& id, int newProcess); + + virtual void importedAgentIsNowLocal(const AgentId& id); + + + /* Exporter-related functions */ + + virtual const AbstractExporter::StatusMap* getOutgoingStatusChanges(); + + virtual const std::set& getProcessesExportedTo(); + const std::set& getProcessesExportedTo(std::string setName); + + virtual void registerIncomingRequests(std::vector& requests); + void registerIncomingRequests(std::vector& requests, std::string setName); + + virtual void agentRemoved(const AgentId& id); + virtual void agentMoved(const AgentId& id, int newProcess); + + // This should NEVER be called; it is inappropriate to use this when 'by set' is being used + virtual void incorporateAgentExporterInfo(std::map info); + void incorporateAgentExporterInfo(std::map*> info); + + virtual void clearStatusMap(); + + virtual AgentExporterInfo* getAgentExportInfo(int destProc); + + virtual void clearAgentExportInfo(); + + virtual const std::map& getAgentsToExport(); + const std::map& getAgentsToExport(std::string setName); + + virtual std::string version(); + + void dropSet(std::string setName){ + importersExportersMap.erase(setName); // Fails silently if the specified set is not present + } + + virtual std::string getReport(){ + std::stringstream ss; + std::map::iterator it = importersExportersMap.begin(); + std::map::iterator itEnd = importersExportersMap.end(); + while(it != itEnd){ + ss << it->first << "\n" << it->second->getReport(); + it++; + } + return ss.str(); + } + + virtual void getSetOfAgentsBeingImported(std::set& set); + void getSetOfAgentsBeingImported(std::set& set, std::string excludeSet); + + virtual void clear(){ + std::map::iterator it = importersExportersMap.begin(); + std::map::iterator itEnd = importersExportersMap.end(); + while(it != itEnd){ + it->second->clear(); + it++; + } + } + + void clear(std::string setName){ + std::map::iterator it = importersExportersMap.find(setName); + if(it != importersExportersMap.end()) it->second->clear(); + } + + virtual void clearExporter(){ + std::map::iterator it = importersExportersMap.begin(); + std::map::iterator itEnd = importersExportersMap.end(); + while(it != itEnd){ + it->second->clearExporter(); + it++; + } + } + + void clearExporter(std::string setName){ + std::map::iterator it = importersExportersMap.find(setName); + if(it != importersExportersMap.end()) it->second->clearExporter(); + } + + void clearExportToSpecificProc(int rank){ + std::map::iterator it = importersExportersMap.begin(); + std::map::iterator itEnd = importersExportersMap.end(); + while(it != itEnd){ + it->second->clearExportToSpecificProc(rank); + it++; + } + } + +}; +#endif + +} +#endif /* AGENTIMPORTEREXPORTER_H_ */ diff --git a/libs/repast_hpc/AgentRequest.cpp b/libs/repast_hpc/AgentRequest.cpp new file mode 100644 index 0000000..0e77ad6 --- /dev/null +++ b/libs/repast_hpc/AgentRequest.cpp @@ -0,0 +1,169 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * AgentRequest.cpp + * + * Created on: Aug 19, 2010 + * Author: nick + */ + +#include + +#include "AgentRequest.h" + +using namespace std; + +namespace repast { + +AgentRequest::AgentRequest(int sourceProcess) : + source(sourceProcess), target(-1) { +} + +AgentRequest::AgentRequest(int sourceProcess, int targetProcess) : + source(sourceProcess), target(targetProcess) { +} + +void AgentRequest::addRequest(const AgentId& id) { + requestedAgents_.push_back(id); +} + +void AgentRequest::addCancellation(const AgentId& id) { + cancellations_.push_back(id); +} + +bool AgentRequest::contains(const AgentId& id) { + return containsInRequests(id) || containsInCancellations(id); // Note: may short-circuit, but this is OK +} + +bool AgentRequest::containsInRequests(const AgentId& id){ + return find(requestedAgents_.begin(), requestedAgents_.end(), id) != requestedAgents_.end(); +} + +bool AgentRequest::containsInCancellations(const AgentId& id){ + return find(cancellations_.begin(), cancellations_.end(), id) != cancellations_.end(); +} + +bool AgentRequest::remove(const AgentId& id, bool removeAllInstances) { + // Note: (removeRequest(id) || removeCancellation(id)) will fail because of short-circuit evaluation + bool foundInRequests = removeRequest(id); + bool foundInCancellations = removeCancellation(id); + return foundInRequests || foundInCancellations; +} + +bool AgentRequest::removeRequest(const AgentId& id, bool removeAllInstances) { + bool retVal = false; + for (vector::iterator iter = requestedAgents_.begin(); iter != requestedAgents_.end(); ) { + if ((*iter) == id) { + iter = requestedAgents_.erase(iter); + retVal = true; + if(!removeAllInstances) return retVal; + } else { + ++iter; + } + } + return retVal; +} + + +bool AgentRequest::removeCancellation(const AgentId& id, bool removeAllInstances) { + bool retVal = false; + for (vector::iterator iter = cancellations_.begin(); iter != cancellations_.end(); ) { + if ((*iter) == id) { + iter = cancellations_.erase(iter); + retVal = true; + if(!removeAllInstances) return retVal; + } else { + ++iter; + } + } + return retVal; +} + +void AgentRequest::addAll(const AgentRequest& req) { + addAllRequests(req); + addAllCancellations(req); +} + +void AgentRequest::addAllRequests(const AgentRequest& req) { + requestedAgents_.insert(requestedAgents_.end(), req.requestedAgents_.begin(), req.requestedAgents_.end()); +} + +void AgentRequest::addAllCancellations(const AgentRequest& req) { + cancellations_.insert(cancellations_.end(), req.cancellations_.begin(), req.cancellations_.end()); +} + +void AgentRequest::targets(set& targets) { + targetsOfRequests(targets); + targetsOfCancellations(targets); +} + +void AgentRequest::targetsOfRequests(std::set& targets) { + for (int i = 0, n = requestedAgents_.size(); i < n; i++) { + AgentId& id = requestedAgents_[i]; + int target = id.currentRank(); + targets.insert(target); + } +} + +void AgentRequest::targetsOfCancellations(std::set& targets) { + for (int i = 0, n = cancellations_.size(); i < n; i++) { + AgentId& id = cancellations_[i]; + int target = id.currentRank(); + targets.insert(target); + } +} + + +std::ostream& operator<<(std::ostream& os, const AgentRequest& request) { + os << "AgentRequest(" << request.source << ", " << request.target << ", ["; + + bool comma = false; + for (vector::const_iterator it = request.requestedAgents_.begin(); it != request.requestedAgents_.end(); it++) { + if (comma) + os << ", "; + os << *it; + comma = true; + } + os << "] CANCELLATIONS:[ "; + comma = false; + for (vector::const_iterator it = request.cancellations_.begin(); it != request.cancellations_.end(); it++) { + if (comma) + os << ", "; + os << *it; + comma = true; + } + os << "])"; + return os; +} + +} diff --git a/libs/repast_hpc/AgentRequest.h b/libs/repast_hpc/AgentRequest.h new file mode 100644 index 0000000..ebca1b3 --- /dev/null +++ b/libs/repast_hpc/AgentRequest.h @@ -0,0 +1,328 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * AgentRequest.h + * + * Created on: Aug 19, 2010 + * Author: nick + */ + +#ifndef AGENTREQUEST_H_ +#define AGENTREQUEST_H_ + +#include +#include +#include +#include + +#include "AgentId.h" + + +namespace repast { + +/** + * Encapsulates a request made by one process for agents in another. + * + * Includes a list of requests and a list that represents cancellations + * of previous requests. + */ +class AgentRequest { + + friend std::ostream& operator<<(std::ostream& os, const AgentRequest& request); + friend class boost::serialization::access; + + // To allow AgentImporters to edit agent requests before sending them out + friend class Importer_LIST; + friend class Importer_SET; + friend class Importer_MAP_int; + +private: + int source, target; + std::vector requestedAgents_; + std::vector cancellations_; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & source; + ar & target; + ar & requestedAgents_; + ar & cancellations_; + } + +public: + // No-arg constructor for serialization + AgentRequest() : + source(-1), target(-1) { + } + + /** + * Creates an AgentRequest that comes from the specified process. + * + * @param sourceProcess the rank of the process making the request + */ + AgentRequest(int sourceProcess); + + /** + * Creates an AgentRequest made from the source process to the target process. + * This can be used when all the requested agents reside on the same process + * (i.e. the target process). + * + * @param sourceProcess the rank of the source process + * @param targetProcess the rank of the target process + */ + AgentRequest(int sourceProcess, int targetProcess); + + /** + * Adds all the agent ids (both requests and cancellations) + * in req to this AgentRequest. + * + * @param req the AgentRequest to add all the agent ids from + */ + void addAll(const AgentRequest& req); + + /** + * Adds all the agent ids in req to this request, + * including only the ids that are requests and + * not those that are cancellations. + * + * @param req the AgentRequest to add all the agent ids from + */ + void addAllRequests(const AgentRequest& req); + + /** + * Adds all the agent ids in req to this request, + * including only the ids that are cancellations and + * not those that are requests. + * + * @param req the AgentRequest to add all the agent ids from + */ + void addAllCancellations(const AgentRequest& req); + + /** + * Gets a reference to the vector of requested agents. + * + * @return a reference to the vector of requested agents. + */ + const std::vector& requestedAgents() const { + return requestedAgents_; + } + + /** + * Gets a reference to the vector of cancellations. + * + * @return a reference to the vector of AgentIds representing + * cancellations. + */ + const std::vector& cancellations() const { + return cancellations_; + } + + /** + * Removes the specified id from the lists of requested agents, + * including both requests and cancellations. + * + * @param id the AgentId to be removed + * @param removeAllInstances if true (the default), all instances of the + * AgentId are removed; if false, only the first instance found + * is removed + * + * @return true if the id was found (in either list) and removed, + * otherwise false. + */ + bool remove(const AgentId& id, bool removeAllInstances = true); + + /** + * Removes the specified id from the list of agent requests; + * does not affect the list of cancellations. + * + * @param id the AgentId to be removed + * @param removeAllInstances if true (the default), all instances of the + * AgentId are removed; if false, only the first instance found + * is removed + * + * @return true if the id was found in the list of requests + * and removed, otherwise false + */ + bool removeRequest(const AgentId& id, bool removeAllInstances = true); + + /** + * Removes the specified id from the list of agent request + * cancellations; does not affect the list of requests. + * + * @param id the AgentId to be removed + * @param removeAllInstances if true (the default), all instances of the + * AgentId are removed; if false, only the first instance found + * is removed + * + * @return true if the id was found in the list of cancellations + * and removed, otherwise false + */ + bool removeCancellation(const AgentId& id, bool removeAllInstances = true); + + /** + * Puts the targets of all the requests into the set. + * Includes both the requests and the cancellations. + * + * @param targets set into which targets will be placed + */ + void targets(std::set& targets); + + /** + * Puts the targets of all the requests into the set, + * including only the requests and not the cancellations. + * + * @param targets the set into which the targets will be placed + */ + void targetsOfRequests(std::set& targets); + + /** + * Puts the targets of all the requests into the set, + * including only the requests and not the cancellations. + * + * @param targets the set into which the targets will be placed + */ + void targetsOfCancellations(std::set& targets); + + /** + * Adds the specified agent to the collection agents being + * requested. + * + * @param id the requested agent + */ + void addRequest(const AgentId& id); + + /** + * Adds the specified agent to the collection of agents + * for which a previous request is being cancelled. + * + * @param id the AgentId of the agent for which the + * request is being cancelled + */ + void addCancellation(const AgentId& id); + + /** + * Gets the number agents requested. + * + * Includes both requests and cancellations; + * exactly equivalent to + * + * requestCountRequested() + requestCountCancellations() + * + * @return the number agents requested. + */ + int requestCount() const { + return requestedAgents_.size() + cancellations_.size(); + } + + /** + * Gets the number of agents requested, counting + * only the requests and not the cancellations. + * + * @return the number of agents requested (requests only) + */ + int requestCountRequested() const { + return requestedAgents_.size(); + } + + /** + * Gets the number of agents requested, counting + * only the cancellations and not the requests. + * + * @return the number of agents requested (cancellations only) + */ + int requestCountCancellations() const { + return cancellations_.size(); + } + + /** + * Returns true if this AgentRequest contains a request for + * the specified id (either a request or a cancellation), + * otherwise false. + * + * @param id the id sought in the lists of requests and cancellations + * + * @return true if either the list of requests or the + * list of cancellations contains the specified id + */ + bool contains(const AgentId& id); + + /** + * Returns true if the list of requests contains the specified + * id (the list of cancellations is ignored) + * + * @param id the AgentId sought + * + * @return true if the specified AgentId is in the list of requests + */ + bool containsInRequests(const AgentId& id); + + /** + * Returns true if the list of cancellations contains the specified + * id (the list of requests is ignored) + * + * @param id the AgentId sought + * + * @return true if the specified AgentId is in the list of cancellations + */ + bool containsInCancellations(const AgentId& id); + + /** + * Gets the source process of these requests, that is, + * the process making the request. + * + * @return the process making the request + */ + int sourceProcess() const { + return source; + } + + /** + * If the requested agent ids are all on the same process + * then target process will identify that process. Otherwise + * this will return -1. + */ + int targetProcess() const { + return target; + } + +}; + +/** + * Prints the specified AgentRequest to the specified ostream. + */ +std::ostream& operator<<(std::ostream& os, const AgentRequest& request); + + +} + +#endif /* AGENTREQUEST_H_ */ diff --git a/libs/repast_hpc/AgentStatus.cpp b/libs/repast_hpc/AgentStatus.cpp new file mode 100644 index 0000000..d035cda --- /dev/null +++ b/libs/repast_hpc/AgentStatus.cpp @@ -0,0 +1,61 @@ +/* +*Repast for High Performance Computing (Repast HPC) +* +* Copyright (c) 2010 Argonne National Laboratory +* All rights reserved. +* +* Redistribution and use in source and binary forms, with +* or without modification, are permitted provided that the following +* conditions are met: +* +* Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* Neither the name of the Argonne National Laboratory nor the names of its +* contributors may be used to endorse or promote products derived from +* this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * AgentStatus.cpp + * + * Created on: Aug 19, 2010 + * Author: nick + */ + +#include "AgentStatus.h" + +namespace repast { + +// Single id, means removed +AgentStatus::AgentStatus(AgentId id) : + _oldId(id), _newId(id), _status(AgentStatus::REMOVED) { +} + +AgentStatus::AgentStatus(AgentId old, AgentId newId) : + _oldId(old), _newId(newId), _status(AgentStatus::MOVED) { + +} + +bool operator<(const AgentStatus &one, const AgentStatus &two) { + return ((one._oldId < two._oldId) || + ((one._oldId == two._oldId) && (one._newId < two._newId))); +} + +} diff --git a/libs/repast_hpc/AgentStatus.h b/libs/repast_hpc/AgentStatus.h new file mode 100644 index 0000000..c8af6cb --- /dev/null +++ b/libs/repast_hpc/AgentStatus.h @@ -0,0 +1,151 @@ +/* + *Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * AgentStatus.h + * + * Created on: Aug 19, 2010 + * Author: nick + */ + +#ifndef AGENTSTATUS_H_ +#define AGENTSTATUS_H_ + +#include +#include "AgentId.h" + +namespace repast { + +/** + * Encapsulates the status (moved or removed) of agent in order to synchronize that + * status across processes. + */ +class AgentStatus { + + friend bool operator<(const AgentStatus &one, const AgentStatus &two); + +public: + + /** + * Enum indicating the status of th agent. + */ + enum Status { + REMOVED, MOVED + }; + + /** + * No-arg constructor for serialization. + */ + AgentStatus() { + } + + /** + * Creates an AgentStatus indicating the status for the specified agent. + * + * @param id the id of the agent whose status this represents + */ + AgentStatus(AgentId id); + + /** + * Creates an AgentStatus indicating the status for the specified agent + * and the new id of that agent as result from the change in status. When + * an agent moves between processes its current rank may change and thus + * the current rank part of its id will change. + * + * @param old the id of the agent whose status this represents + * @param newId the new id of the agent that results from its status change + */ + AgentStatus(AgentId old, AgentId newId); + + /** + * Gets the status. + * + * @return the status + */ + Status getStatus() const { + return _status; + } + + /** + * Gets the id of the agent that this is the status for. + * + * @return the id of the agent that this is the status for. + */ + const AgentId& getId() const { + return _oldId; + } + + /** + * Gets the old id of the agent that this is the status for, if + * this contains an old and updated AgentId. + * + * @return Gets the old id of the agent that this is the status for, if + * this contains an old and updated AgentId. + */ + const AgentId& getOldId() const { + return _oldId; + } + + /** + * Gets the new updated id of the agent that this is the status for, if + * this contains an old and updated AgentId. + * + * @return Gets the new id of the agent that this is the status for, if + * this contains an old and updated AgentId. + */ + const AgentId& getNewId() const { + return _newId; + } + +private: + friend class boost::serialization::access; + + AgentId _oldId, _newId; + Status _status; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & _oldId; + ar & _newId; + ar & _status; + } +}; + +/** + * Comparison operator that can be used in sorts, etc. + */ +bool operator<(const AgentStatus &one, const AgentStatus &two); + + +} + +#endif /* AGENTSTATUS_H_ */ diff --git a/libs/repast_hpc/BaseGrid.h b/libs/repast_hpc/BaseGrid.h new file mode 100644 index 0000000..e3b7c51 --- /dev/null +++ b/libs/repast_hpc/BaseGrid.h @@ -0,0 +1,518 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Grid.h + * + * Created on: Jun 19, 2009 + * Author: nick + */ + +#ifndef BASE_GRID_H_ +#define BASE_GRID_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Grid.h" +#include "spatial_math.h" +#include "RepastErrors.h" + +namespace repast { + +/** + * Encapsulates a grid point and what is held in it. + */ +template +struct GridPointHolder { + + bool inGrid; + Point point; + boost::shared_ptr ptr; + + GridPointHolder() : + inGrid(false), point(0) { + } +}; + +/** + * Unary function used in the transform_iterator that allows context iterators + * to return the agent maps values. + */ +template +struct AgentFromGridPoint: public std::unary_function*>::value_type, boost::shared_ptr > { + boost::shared_ptr operator()( + const typename boost::unordered_map*>::value_type& value) const { + GridPointHolder *gp = value.second; + return gp->ptr; + } + +}; + +/** + * Base grid implementation, implementing elements common to both Grids and ContinuousSpaces. + * Standard grid and space types that provide defaults for the various template parameters + * can be found in Space in Space.h + * + * @tparam T the type of objects contained by this BaseGrid (generally the type of agents) + * @tparam CellAccessor implements the actual storage for the grid. + * @tparam GPTransformer transforms cell points according to the topology (e.g. periodic) + * of the BaseGrid. + * @tparam Adder determines how objects are added to the grid from its associated context. + * @tparam GPType the coordinate type of the grid point locations; this must + * be an int or a double. + */ +template +class BaseGrid: public Grid { + +private: + + // we use a GridPointHolder so we can swap out the GridPoint for an + // agent with a single map access, rather than have to put the new + // GridPoint back in the map. + typedef typename boost::unordered_map*, HashId> AgentLocationMap; + + AgentLocationMap agentToLocation; + GridDimensions dimensions_; + + CellAccessor cellAccessor; + + bool doMove(std::vector& point, GridPointHolder* gpHolder); + + size_t size_; + +protected: + + typedef typename AgentLocationMap::iterator LocationMapIter; + typedef typename AgentLocationMap::const_iterator LocationMapConstIter; + + GPTransformer gpTransformer; + Adder adder; + + virtual bool addAgent(boost::shared_ptr agent); + virtual void removeAgent(T* agent); + + LocationMapConstIter locationsBegin() const { + return agentToLocation.begin(); + } + LocationMapConstIter locationsEnd() const { + return agentToLocation.end(); + } + + T* get(const AgentId& id); + +public: + + /** + * A const iterator over shared_ptr. + */ + typedef typename boost::transform_iterator , LocationMapConstIter> const_iterator; + + /** + * Creates a BaseGrid with the specified name and dimensions. + * + * @param name the name of the BaseGrid + * @param dimensions the dimensions of the BaseGrid + */ + BaseGrid(std::string name, GridDimensions dimensions); + virtual ~BaseGrid(); + + // doc inherited from Grid + virtual bool contains(const AgentId& id); + + // doc inherited from Grid + virtual bool getLocation(const T* agent, std::vector& pt) const; + + // doc inherited from Grid + virtual bool getLocation(const AgentId& id, std::vector& out) const; + + // doc inherited from Grid + virtual T* getObjectAt(const Point& pt) const; + + // doc inherited from Grid + virtual void getObjectsAt(const Point& pt, std::vector& out) const; + + /** + * Moves the specified agent to the specified location. Returns + * true if the move was successful otherwise false. The agent + * must be already added to the context associated with this space, otherwise + * this throws an out_of_range exception if the new location out of bounds. + * + * @param agent the agent to move + * @param newLocation the location to move to + * + * @return true if the move was successful, otherwise false + */ + virtual bool moveTo(const T* agent, const std::vector& newLocation); + + /** + * Moves the specified agent to the specified location. Returns + * true if the move was successful otherwise false. The agent + * must be already added to the context associated with this space, otherwise + * this throws an out_of_range exception if the new location out of bounds. + * + * @param agent the agent to move + * @param newLocation the location to move to + * + * @return true if the move was successful, otherwise false + */ + virtual bool moveTo(const T* agent, const Point& newLocation); + + /** + * Moves the specified agent to the specified location. Returns + * true if the move was successful otherwise false. The agent + * must be already added to the context associated with this space, otherwise + * this throws an out_of_range exception if the new location out of bounds. + * + * @param id the id of the agent to move + * @param newLocation the location to move to + * + * @return true if the move was successful, otherwise false + */ + virtual bool moveTo(const AgentId& id, const std::vector& newLocation); + + // doc inherited from Grid + virtual bool moveTo(const AgentId& id, const Point& pt); + + // doc inherited from Grid + virtual std::pair > moveByDisplacement(const T* agent, const std::vector& displacement); + + /// doc inherited from Grid + virtual std::pair > moveByVector(const T* agent, double distance, + const std::vector& anglesInRadians); + + /** + * Gets an iterator over the agents in this BaseGrid starting with the + * first agent. The iterator derefrences into shared_ptr. The actual + * agent can be accessed by derefrenceing the iter: (*iter)->getId() for example. + * + * @return an iterator over the agents in this BaseGrid starting with the + * first agent. + */ + virtual const_iterator begin() const { + return const_iterator(agentToLocation.begin()); + } + + /** + * Gets the end of an iterator over the agents in this BaseGrid. + * + * @return the end of an iterator over the agents in this BaseGrid. + */ + virtual const_iterator end() const { + return const_iterator(agentToLocation.end()); + } + + /** + * Gets the number of agents in this BaseGrid. + * + * @return the number of agents in this BaseGrid. + */ + virtual size_t size() const { + return size_; + } + + // doc inherited from Grid + virtual double getDistance(const Point& pt1, const Point& pt2) const; + + // doc inherited from Grid + virtual double getDistanceSq(const Point& pt1, const Point& pt2) const; + + // doc inherited from Grid + virtual void getDisplacement(const Point& pt1, const Point& pt2, std::vector& out) const; + + // doc inherited from Grid + virtual const GridDimensions dimensions() const { + return dimensions_; + } + + // doc inherited from Grid + virtual void translate(const Point& location, const Point& displacement, std::vector& out) const { + gpTransformer.translate(location.coords(), out, displacement.coords()); + } + + // doc inherited from Grid + virtual void transform(const std::vector& location, std::vector& out) const { + gpTransformer.transform(location, out); + } + + // doc inherited from Grid + virtual bool isPeriodic() const { + return gpTransformer.isPeriodic(); + } + + virtual ProjectionInfoPacket* getProjectionInfo(AgentId id, bool secondaryInfo = false, std::set* secondaryIds = 0, int destProc = -1 ); + + virtual void updateProjectionInfo(ProjectionInfoPacket* pip, Context* context); + + virtual void getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush){ } + virtual void getInfoExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom) {} + virtual void getAgentStatusExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom) {} +}; + +template +BaseGrid::BaseGrid(std::string name, GridDimensions dimensions) : + Grid (name), gpTransformer(dimensions), dimensions_(dimensions), size_(0) { +// gpTransformer.init(dimensions); + adder.init(dimensions, this); +} + +template +BaseGrid::~BaseGrid() { + for (LocationMapIter iter = agentToLocation.begin(); iter != agentToLocation.end(); ++iter) { + delete iter->second; + } +} + +template +T* BaseGrid::get(const AgentId& id) { + LocationMapConstIter iter = agentToLocation.find(id); + if (iter == agentToLocation.end()) + return 0; + + return iter->second->ptr.get(); +} + +template +bool BaseGrid::getLocation(const AgentId& id, std::vector& out) const { + LocationMapConstIter iter = agentToLocation.find(id); + if (iter == agentToLocation.end()) + return false; + + GridPointHolder* holder = iter->second; + if (!holder->inGrid) + return false; + + if (out.size() != dimensions_.dimensionCount()) + out.resize(dimensions_.dimensionCount(), 0); + holder->point.copy(out); + return true; + +} + +template +bool BaseGrid::getLocation(const T* agent, std::vector& out) const { + return getLocation(agent->getId(), out); +} + +template +bool BaseGrid::contains(const AgentId& id) { + return agentToLocation.find(id) != agentToLocation.end(); +} + +template +bool BaseGrid::moveTo(const T* agent, const Point& newLocation) { + return moveTo(agent->getId(), newLocation.coords()); +} + +template +bool BaseGrid::moveTo(const T* agent, + const std::vector& newLocation) { + return moveTo(agent->getId(), newLocation); +} + +template +bool BaseGrid::moveTo(const AgentId& id, + const Point& newLocation) { + return moveTo(id, newLocation.coords()); +} + +template +bool BaseGrid::moveTo(const AgentId& id, + const std::vector& newLocation) { + LocationMapIter iter = agentToLocation.find(id); + + if (iter == agentToLocation.end()) + throw Repast_Error_2(id, Projection::name()); // Agent has not yet been introduced to this space/is not present + + if (newLocation.size() < dimensions_.dimensionCount()) + throw Repast_Error_3(newLocation.size(), dimensions_.dimensionCount()); // Destination not fully specified + + std::vector transformedCoords(newLocation.size(), 0); + gpTransformer.transform(newLocation, transformedCoords); + + if (iter->second->point.coords() == transformedCoords) return true; + return doMove(transformedCoords, iter->second); +} + +template +bool BaseGrid::doMove(std::vector& location, GridPointHolder* gpHolder) { + Point pt(location); + if (cellAccessor.put(gpHolder->ptr, pt)) { + if (gpHolder->inGrid) { + cellAccessor.remove(gpHolder->ptr, gpHolder->point); + } else { + size_++; + gpHolder->inGrid = true; + } + gpHolder->point = pt; + return true; + } + return false; +} + +template +std::pair > BaseGrid::moveByVector(const T* agent, + double distance, const std::vector& anglesInRadians) { + if (anglesInRadians.size() != dimensions_.dimensionCount()) + throw Repast_Error_4(anglesInRadians.size(), dimensions_.dimensionCount()); // Number of angles must equal dimensions + + std::vector pt = calculateDisplacement (dimensions_.dimensionCount(), 0, distance, anglesInRadians); + return moveByDisplacement(agent, pt); +} + +template +std::pair > BaseGrid::moveByDisplacement( + const T* agent, const std::vector& displacement) { + if (displacement.size() != dimensions_.dimensionCount()) + throw Repast_Error_5(displacement.size(), dimensions_.dimensionCount()); // displacement vector must equal number of grid/space dimensions + + LocationMapIter iter = agentToLocation.find(agent->getId()); + if (iter == agentToLocation.end()) + throw Repast_Error_6(agent->getId(), Projection::name()); // Agent has not in this grid / space + + GridPointHolder* gpHolder = iter->second; + std::vector newPos(displacement.size(), 0); + gpTransformer.translate(gpHolder->point.coords(), newPos, displacement); + std::vector transformedCoords(newPos.size(), 0); + gpTransformer.transform(newPos, transformedCoords); + if (iter->second->point.coords() == transformedCoords) + return std::make_pair(true, Point (transformedCoords)); + return std::make_pair(moveTo(agent->getId(), transformedCoords), Point (transformedCoords)); +} + +template +T* BaseGrid::getObjectAt(const Point& pt) const { + return cellAccessor.get(pt); +} + +template +void BaseGrid::getObjectsAt(const Point& pt, + std::vector& out) const { + return cellAccessor.getAll(pt, out); +} + +template +bool BaseGrid::addAgent(boost::shared_ptr agent) { + if(!Projection::agentCanBeAdded(agent)) return false; + GridPointHolder* gp = new GridPointHolder (); + gp->ptr = agent; + agentToLocation[agent->getId()] = gp; + return adder.add(agent); +} + +template +void BaseGrid::removeAgent(T* agent) { + LocationMapIter iter = agentToLocation.find(agent->getId()); + if (iter != agentToLocation.end()) { + GridPointHolder* gp = iter->second; + cellAccessor.remove(gp->ptr, gp->point); + delete gp; + agentToLocation.erase(iter); + } +} + +template +double BaseGrid::getDistance(const Point& pt1, const Point< + GPType>& pt2) const { + return sqrt(getDistanceSq(pt1, pt2)); +} + +template +double BaseGrid::getDistanceSq(const Point& pt1, const Point< + GPType>& pt2) const { + if (pt1.dimensionCount() != pt2.dimensionCount()) + throw Repast_Error_7(pt1.dimensionCount(), pt2.dimensionCount()); // Points do not have same number of dimensions + + double sum = 0; + for (int i = 0, n = pt1.dimensionCount(); i < n; i++) { + double diff = pt1.getCoordinate(i) - pt2.getCoordinate(i); + if (gpTransformer.isPeriodic()) { + double dim = dimensions_.extents(i); + double absDiff = ((diff < 0.00) ? (-1.0 * diff) : diff); //abs(diff); + if (absDiff > dim / 2.0) diff = dim - absDiff; + } + sum += diff * diff; + } + + return sum; +} + +template +void BaseGrid::getDisplacement(const Point& pt1, const Point< + GPType>& pt2, std::vector& out) const { + if (pt1.dimensionCount() != pt2.dimensionCount()) + throw Repast_Error_8(pt1.dimensionCount(), pt2.dimensionCount()); // Points do not have same number of dimensions + + for (int i = 0, n = pt1.dimensionCount(); i < n; i++) { + GPType diff = pt2.getCoordinate(i) - pt1.getCoordinate(i); + if (gpTransformer.isPeriodic()) { + double dim = dimensions_.extents(i); + GPType absDiff = ((diff < 0.00) ? (-1.0 * diff) : diff); //abs(diff); + if (absDiff > dim / ((GPType) 2)) diff = dim - absDiff; + } + out[i] = diff; + } +} + +template +ProjectionInfoPacket* BaseGrid::getProjectionInfo(AgentId id, bool secondaryInfo, + std::set* secondaryIds, int destProc){ + typename AgentLocationMap::const_iterator agentIter = agentToLocation.find(id); + if(agentIter == agentToLocation.end()){ + return 0; + } + else{ + return new SpecializedProjectionInfoPacket(id, agentIter->second->point.coords()); + } +} + + +template +void BaseGrid::updateProjectionInfo(ProjectionInfoPacket* pip, Context* context){ + SpecializedProjectionInfoPacket* spip = static_cast*>(pip); + moveTo(spip->id, spip->data); +// std::cout << " UPDATING PROJECTION INFO: " << spip->id << " " << spip->data[0] << "," << spip->data[1] << " " << context->getAgent(spip->id)->location() << std::endl; +} + +} + +#endif /* GRID_H_ */ diff --git a/libs/repasthpc_CMakeLists.txt b/libs/repast_hpc/CMakeLists.txt similarity index 98% rename from libs/repasthpc_CMakeLists.txt rename to libs/repast_hpc/CMakeLists.txt index af5f3a0..dcb51ec 100644 --- a/libs/repasthpc_CMakeLists.txt +++ b/libs/repast_hpc/CMakeLists.txt @@ -25,7 +25,7 @@ project(repast_hpc_project) cmake_minimum_required(VERSION 3.0) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/../../cmake/Modules/") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") file(GLOB_RECURSE rhpc_src ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) diff --git a/libs/repast_hpc/CartesianTopology.cpp b/libs/repast_hpc/CartesianTopology.cpp new file mode 100644 index 0000000..a3d4306 --- /dev/null +++ b/libs/repast_hpc/CartesianTopology.cpp @@ -0,0 +1,147 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * CartesianTopology.cpp + * + * Created on: July 25, 2008 + * Author: jtm + */ + +#include "CartesianTopology.h" + + +using namespace std; + +namespace repast { + + +CartesianTopology::CartesianTopology(vector processesPerDim, bool spaceIsPeriodic, boost::mpi::communicator* comm) : + periodic(spaceIsPeriodic), procsPerDim(processesPerDim) { + int numDims = procsPerDim.size(); + int* periods = new int[numDims]; + int periodicFlag = periodic ? 1 : 0; + for (int i = 0; i < numDims; i++) periods[i] = periodicFlag; + + MPI_Cart_create(*comm, numDims, &processesPerDim[0], periods, 0, &topologyComm); + delete[] periods; +} + +CartesianTopology::~CartesianTopology(){} + +int CartesianTopology::getRank(vector& loc, std::vector& relLoc) { + int numDims = relLoc.size(); + int* coord = new int[numDims]; + for(int i = 0; i < numDims; i++){ + coord[i] = loc[i] + relLoc[i]; + if(!periodic){ + if((coord[i] < 0) || (coord[i] > (procsPerDim[i] - 1))){ + delete[] coord; + return MPI_PROC_NULL; + } + } + } + int rank; + MPI_Cart_rank(topologyComm, coord, &rank); + delete[] coord; + return rank; +} + + +void CartesianTopology::getCoordinates(int rank, std::vector& coords) { + int numDims = procsPerDim.size(); + coords.resize(numDims); + MPI_Cart_coords(topologyComm, rank, numDims, &coords[0]); +} + +GridDimensions CartesianTopology::getDimensions(int rank, GridDimensions globalBoundaries) { + vector coords; + getCoordinates(rank, coords); + return getDimensions(coords, globalBoundaries); +} + +GridDimensions CartesianTopology::getDimensions(vector& pCoordinates, GridDimensions globalBoundaries) { + vector origins, extents; + for (size_t i = 0; i < pCoordinates.size(); i++) { + double lower = globalBoundaries.origin(i) + ( (double)pCoordinates[i] / (double)procsPerDim[i]) * globalBoundaries.extents(i); + double upper = globalBoundaries.origin(i) + (((double)pCoordinates[i] + 1 )/ (double)procsPerDim[i]) * globalBoundaries.extents(i); + origins.push_back(lower); + extents.push_back(upper - lower); + } + + return GridDimensions(Point (origins), Point (extents)); +} + +RelativeLocation CartesianTopology::trim(int rank, RelativeLocation volume){ + if( periodic || + volume.getCountOfDimensions() != procsPerDim.size()) return RelativeLocation(volume); + vector loc; + loc.assign(procsPerDim.size(), 0); + getCoordinates(rank, loc); + RelativeLocation test(volume); // Note: sets to minima + vector* min = 0; + vector* max = 0; + do{ + vector relLoc(test.getCurrentValue()); + int rankFound = getRank(loc, relLoc); + if(rankFound != MPI_PROC_NULL){ + if(min == 0){ + min = new vector(); + for(size_t i = 0; i < procsPerDim.size(); i++) min->push_back(test[i]); + } + if(max == 0){ + max = new vector(); + for(size_t i = 0; i < procsPerDim.size(); i++) max->push_back(test[i]); + } + for(size_t i = 0; i < procsPerDim.size(); i++){ + if(min->at(i) > test[i]) (*min)[i] = test[i]; // In practice this should never happen + if(max->at(i) < test[i]) (*max)[i] = test[i]; // But this will + } + } + }while(test.increment()); + RelativeLocation ret(*min, *max); + delete min; + delete max; + return ret; +} + +bool CartesianTopology::matches(std::vector processesPerDim, bool spaceIsPeriodic){ + if(spaceIsPeriodic != periodic) return false; + if(processesPerDim.size() != procsPerDim.size()) return false; + for(size_t i = 0; i < procsPerDim.size(); i++){ + if(processesPerDim[i] != procsPerDim[i]) return false; + } + return true; + } + +} diff --git a/libs/repast_hpc/CartesianTopology.h b/libs/repast_hpc/CartesianTopology.h new file mode 100644 index 0000000..71265ca --- /dev/null +++ b/libs/repast_hpc/CartesianTopology.h @@ -0,0 +1,112 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * CartesianTopology.h + * + * Created on: July 28, 2015 + * Author: jtm + */ + +#ifndef CARTESIANTOPOLOGY_H_ +#define CARTESIANTOPOLOGY_H_ + +#include +#include "mpi.h" +#include + +#include "GridDimensions.h" +#include "RelativeLocation.h" + +using namespace std; + +namespace repast { + +class CartesianTopology { + +private: + bool periodic; + std::vector procsPerDim; + +public: + MPI_Comm topologyComm; + + CartesianTopology(std::vector processesPerDim, bool spaceIsPeriodic, boost::mpi::communicator* world); + virtual ~CartesianTopology(); + + /** + * Gets the rank of the process at the specified offset + * from the specified location + */ + int getRank(std::vector& loc, std::vector& relLoc); + + /** + * Gets the coordinates in the MPI Cartesian Communicator + * for the specified rank + */ + void getCoordinates(int rank, std::vector& coords); + + /** + * Gets the GridDimensions boundaries for the specified + * rank + */ + GridDimensions getDimensions(int rank, GridDimensions globalBoundaries); + + /** + * Gets the GridDimensions boundaries for the specified + * MPI coordinates + */ + GridDimensions getDimensions(std::vector& pCoordinates, GridDimensions globalBoundaries); + + /** + * Trims the relative location volume to only valid values. + * If the CartesianTopology is periodic, this will have no effect, + * but if part of the relative location falls outside the + * boundaries of a (nonperiodic) Cartesian Topology, the + * RelativeLocation returned will include only the locations + * that fall within the boundaries and will be initialized + * to the minima values + */ + RelativeLocation trim(int rank, RelativeLocation volume); + + /** + * Returns true only if the periodicity specified matches + * the periodicity of this CartesianTopology, the size + * of the vector of processes per dimension matches, and + * the value for each dimension matches. + */ + bool matches(std::vector processesPerDim, bool spaceIsPeriodic); +}; +} + +#endif /* DIFFUSIONLAYERND_H_ */ diff --git a/libs/repast_hpc/Context.h b/libs/repast_hpc/Context.h new file mode 100644 index 0000000..8bf4328 --- /dev/null +++ b/libs/repast_hpc/Context.h @@ -0,0 +1,1115 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Context.h + * + * Created on: May 22, 2009 + * Author: nick + */ + +#ifndef CONTEXT_H_ +#define CONTEXT_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include "AgentId.h" +#include "AgentRequest.h" +#include "Random.h" +#include "ValueLayer.h" +#include "Projection.h" +#include "RepastErrors.h" + +namespace repast { + +/** + * Unary function used in the transform_iterator that allows context iterators + * to return the agent maps values. + */ +template +struct SecondElement: public std::unary_function >::value_type, boost::shared_ptr > { + boost::shared_ptr operator()(const typename boost::unordered_map >::value_type& value) const { + const boost::shared_ptr& ptr = value.second; + return ptr; + } +}; + + +/** + * Collection of agents of type T with set semantics. Object identity and equality + * is determined by their AgentId. + * + * @tparam the type objects contained by the Context. The T must extends repast::Agent. + */ +template +class Context { + +private: + + typedef typename std::vector*>::iterator ProjPtrIter; + typedef typename boost::unordered_map, HashId> AgentMap; + + typedef typename AgentMap::iterator AgentMapIterator; + typedef typename AgentMap::const_iterator AgentMapConstIterator; + + AgentMap agents; + std::map valueLayers; + +protected: + std::vector *> projections; + +public: + + typedef typename boost::transform_iterator , typename AgentMap::const_iterator> const_iterator; + typedef typename boost::filter_iterator , typename Context::const_iterator> const_bytype_iterator; + + Context(); + + /** + * Destroys this context and the projections it contains. + */ + virtual ~Context(); + + /** + * Adds the agent to the context. Performs a check to ensure + * that no agent with the same ID (presumably the 'same' agent) + * has previously been added. If a matching ID is found, the + * new agent is not added, and the address of the pre-existing + * agent is returned. If no match is found, the agent is added + * and the return value is the same as the value passed + * + * @param agent the agent to add + * + * @return the address of the agent in the context; will be the + * same as the address passed if the agent was successfully added, + * but if there was already an agent with the same ID the address + * returned will be that of the pre-existing agent, which is + * not replaced. + */ + T* addAgent(T* agent); + + /** + * Adds the specified projection to this context. All the agents in this + * context will be added to the Projection. Any agents subsequently added + * to this context will also be added to the Projection. + * + * @param projection the projection to add + */ + virtual void addProjection(Projection* projection); + + /** + * Get the named Projection. + * + * @param the name of the projection to get + * + * @return the named Projection or 0 if no such Projection is found. + */ + Projection* getProjection(const std::string& name); + + /** + * Removes the specified agent from this context. + * + * @param id the id of the agent to remove + */ + void removeAgent(const AgentId id); + + /** + * Removes the specified agent from this context. + */ + void removeAgent(T* agent); + + /** + * Gets the specified agent. + * + * @param the id of the agent to get. + */ + T* getAgent(const AgentId& id); + + /** + * Gets at random the specified count of agents and returns them + * in the agents vector. + * + * @param count the number of agents to get + * @param [out] agents a vector where the agents will be returned + */ + void getRandomAgents(const int count, std::vector& agents); + + /** + * Gets the start of iterator over the agents in this context. + * The iterator derefrences into shared_ptr. The actual + * agent can be accessed by derefrenceing the iter: (*iter)->getId() for example. + * + * @return the start of iterator over the agents in this context. + */ + const_iterator begin() const { + return const_iterator(agents.begin()); + } + + /** + * Gets the end of an iterator over the agents in this context. The iterator derefrences + * into shared_ptr. The actual agent can be accessed by derefrenceing the iter: + * (*iter)->getId() for example. + * + * @return the end of an iterator over the agents in this context + */ + const_iterator end() const { + return const_iterator(agents.end()); + } + + /** + * Gets the start of an iterator over agents in this context of the specified type. The type + * corresponds to the type component of an agent's AgentId. + * + * @param typeId the type of the agent. Only Agents whose agentId.agentType() is equal to + * this typeId will be included in the iterator + * + * @return the start of an iterator over agents in this context of the specified type. + */ + const_bytype_iterator byTypeBegin(int typeId) const { + return const_bytype_iterator(IsAgentType (typeId), Context::begin(), Context::end()); + } + + /** + * Gets the end of an iterator over agents in this context of the specified type. The type + * corresponds to the type component of an agent's AgentId. + * + * @param typeId the type of the agent. Only Agents whose agentId.agentType() is equal to + * this typeId will be included in the iterator + * + * @return the end of an iterator over agents in this context of the specified type. + */ + const_bytype_iterator byTypeEnd(int typeId) const { + return const_bytype_iterator(IsAgentType (typeId), Context::end(), Context::end()); + } + + /** + * Returns true if the specified agent is in this context, otherwise false. + */ + bool contains(const AgentId& id); + + /** + * Gets the size (number of agents) in this context. + */ + int size() const { + return agents.size(); + } + + /** + * Adds a value layer to this context. + * + * @param valueLayer the value layer to add + */ + void addValueLayer(BaseValueLayer* valueLayer); + + /** + * Gets the named discrete value layer from this Context. The value layer must have been + * previously added. + * + * @param valueLayerName the name of the value layer to get + * + * @tparam ValueType the numeric type contained by the value layer + * @tparam Borders the Border type of the value layer + * + * @return the named discrete value layer from this Context. + */ + template + DiscreteValueLayer* getDiscreteValueLayer(const std::string& valueLayerName); + + /** + * Gets the named continuous value layer from this Context. The value layer must have been + * previously added. + * + * @param valueLayerName the name of the value layer to get + * + * @tparam ValueType the numeric type contained by the value layer + * @tparam Borders the Border type of the value layer + * + * @return the named continuous value layer from this Context. + */ + template + ContinuousValueLayer* getContinuousValueLayer(const std::string& valueLayerName); + + /** + * Creates a filtered iterator over the set of agents + * in this context and returns it with a value equal + * to the beginning of the list. + * + * The struct can be any user-defined structure that implements + * a unary operator (see IsAgentType) that can be passed and + * which will become a filter to sort across the agent + * list, e.g.: + * + * struct filter { + * bool operator()(const boost::shared_ptr& ptr){ return (ptr->getAgentValue() == targetValue;) } + * } + * + * This should allow filtering of agents by any attribute. + * + * @param fStruct an instance of the struct to be used as the filter + * + * @tparam filterStruct the type of the filter to be applied to the agents + * + * @return an iterator positioned at the beginning of the list of + * agents meeting the filter's criteria + */ + template + boost::filter_iterator::const_iterator> filteredBegin(const filterStruct& fStruct); + + /** + * Creates a filtered iterator over the set of agents + * in this context and returns it with a value equal + * to one step past end of the list. + * + * The struct can be any user-defined structure that implements + * a unary operator (see IsAgentType) that can be passed and + * which will become a filter to sort across the agent + * list, e.g.: + * + * struct filter { + * bool operator()(const boost::shared_ptr& ptr){ return (ptr->getAgentValue() == targetValue;) } + * } + * + * This should allow filtering of agents by any attribute. + * + * @param fStruct an instance of the struct to be used as the filter + * + * @tparam filterStruct the type of the filter to be applied to the agents + * + * @return an iterator positioned at one past the end of the list of + * agents meeting the filter's criteria + */ + template + boost::filter_iterator::const_iterator> filteredEnd(const filterStruct& fStruct); + + + /** + * Creates a filtered iterator over the set of agents + * in this context of the specified type (per their AgentId + * values), and returns it with a value equal + * to the beginning of the list. + * + * The struct can be any user-defined structure that implements + * a unary operator (see IsAgentType) that can be passed and + * which will become a filter to sort across the agent + * list, e.g.: + * + * struct filter { + * bool operator()(const boost::shared_ptr& ptr){ return (ptr->getAgentValue() == targetValue;) } + * } + * + * This should allow filtering of agents by type and on any attribute. + * + * @param fStruct an instance of the struct to be used as the filter + * @param type the numeric type of agents to be included in the list + * + * @tparam filterStruct the type of the filter to be applied to the agents + * + * @return an iterator positioned at the beginning of the list of + * agents meeting the filter's criteria + */ + template + boost::filter_iterator::const_bytype_iterator> byTypeFilteredBegin(const int type, const filterStruct& fStruct); + + /** + * Creates a filtered iterator over the set of agents + * in this context of the specified type (per their AgentId + * values), and returns it with a value equal + * to one past the end of the list. + * + * The struct can be any user-defined structure that implements + * a unary operator (see IsAgentType) that can be passed and + * which will become a filter to sort across the agent + * list, e.g.: + * + * struct filter { + * bool operator()(const boost::shared_ptr& ptr){ return (ptr->getAgentValue() == targetValue;) } + * } + * + * This should allow filtering of agents by type and on any attribute. + * + * @param fStruct an instance of the struct to be used as the filter + * @param type the numeric type of agents to be included in the list + * + * @tparam filterStruct the type of the filter to be applied to the agents + * + * @return an iterator positioned at one past the end of the list of + * agents meeting the filter's criteria + */ + template + boost::filter_iterator::const_bytype_iterator> byTypeFilteredEnd(const int type, const filterStruct& fStruct); + + /** + * Gets a set of pointers to all agents in this context. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + */ + void selectAgents(std::set& selectedAgents, bool remove = false); + + /** + * Gets a randomly ordered vector of pointers to all agents in this context. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + */ + void selectAgents(std::vector& selectedAgents, bool remove = false); + + /** + * Gets a set of pointers to a specified number of randomly selected agents. + * + * If the set passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + */ + void selectAgents(int count, std::set& selectedAgents, bool remove = false); + + /** + * Gets a randomly ordered vector of pointers to a specified number of randomly selected agents. + * + * If the vector passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + */ + void selectAgents(int count, std::vector& selectedAgents, bool remove = false); + + /** + * Gets a set of pointers to all agents in this context + * of a specified type (per their AgentId values). + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(std::set& selectedAgents, int type, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to all agents in this context + * of a specified type (per their AgentId values). + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(std::vector& selectedAgents, int type, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to a specified number of randomly selected agents + * of a specified type (per their AgentId values). + * + * If the set passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(int count, std::set& selectedAgents, int type, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to a specified number of randomly selected agents + * of a specified type (per their AgentId values). + * + * If the vector passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(int count, std::vector& selectedAgents, int type, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to all agents in this context matching a user-defined filter. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(std::set& selectedAgents, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to all agents in this context + * matching a user-defined filter. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(std::vector& selectedAgents, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to a specified number of randomly selected agents + * matching a user-defined filter. + * + * If the set passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(int count, std::set& selectedAgents, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to a specified number of randomly selected agents + * matching a user-defined filter. + * + * If the vector passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(int count, std::vector& selectedAgents, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to all agents in this context + * of a specified type (per their AgentId values) and matching a user-defined filter. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(std::set& selectedAgents, int type, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to all agents in this context + * of a specified type (per their AgentId values) and matching a user-defined filter. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(std::vector& selectedAgents, int type, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to a specified number of randomly selected agents + * of a specified type (per their AgentId values) and matching a user-defined filter. + * + * If the set passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(int count, std::set& selectedAgents, int type, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to a specified number of randomly selected agents + * of a specified type (per their AgentId values) and matching a user-defined filter. + * + * If the vector passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(int count, std::vector& selectedAgents, int type, filterStruct& filter, bool remove = false, int popSize = -1); + + + + // BETA + /** + * Gets the projection information for all projections in this context, for all agents whose IDs are listed + * in the AgentRequest. + * + * The general sense of this method can be easily understood: given a list of agents, get the projection + * information for all of those agents. But there are some subtleties that should be kept in mind. + * + * "The projection information for an agent" is misleading. In fact, the projection information that + * is needed can vary depending on the context and on the kind of projection. + * + * Generally speaking, spaces return only one kind projection information: coordinate locations for + * the agent specified. This is the simplest case. + * + * The more complicated case is given by graphs. A graph projection can return different sets of information + * depending on how that information will have to be used. The basic issue is that a graph projection + * returns sets of edges, and edges must be connected to other agents; this means that a mechanism + * must be in place for ensuring that the projection info that arrives can be used, which means that + * for a given 'ego' agent, all 'alter' agents that are connected to it by edges must also be on the + * receiving process. (Note: Repast HPC 1.0 versions sent all of the alter agents' content along with + * the edge send; this version does not do this, partly to minimize the amount of information being + * packaged and sent but also because the alternative method used is integrated with the normal + * bookkeeping for sharing agent information across processes (AgentRequests).) In different + * circumstances, different assumptions can be made about what information will be available on the + * receiving process. Note that the coordinate information is generally referred to as 'Primary' + * information, while edge information is 'secondary'; in a third category ('secondary IDs') are the IDs of the + * alter agents, which can be packaged separately. + * + * The impact of this is that this function is generally called in the following ways: + * + * 1) When requesting agents: in this case, a copy of the agent will be sent from one + * process to another. No secondary information will be sent at all. This is because it is + * assumed that if an agent participated in a graph on the receiving process, it would already + * be present on that process and would not be being requested. + * + * 2) When synchronizing Projection Information: in this case, some secondary information + * (edges) is needed: the edges that connect the specified ego agent with edges on the receiving + * process. No secondary IDs are needed, because the only edges being sent are those that connect + * to agents on the receiving process, which will be assumed to already be available on that + * process. + * + * 3) When synchronizing Agent Status (moving agents from process to process): in this case, + * the full collection of projection information is needed, including all of the edges in which + * the specified agent participates and all of the secondary IDs. (The secondary + * IDs of agents that are already on the receiving process can be omitted, at least theoretically.) + * This allows the full reconstruction of Projection Information on the receiving process. + * + * @param req List of IDs for agents whose information is requested + * @param map A map into which the projection information will be placed. Key values represent the names + * of the projections in this context. + * @param secondaryInfo true if the 'secondary' projection info must also be returned + * @param secondaryIds A set of IDs for agents who are referred to by the projection informaton + * being returned (may be null) + * @param destProc The Process that will be receiving this information (the information sent may + * be customized depending on the destination process). If not specified a larger set of information + * will be sent. + */ + void getProjectionInfo(AgentRequest req, std::map >& map, + bool secondaryInfo = false, std::set* secondaryIds = 0, int destProc = -1); + + /** + * Sets the projection information as specified. + * + * @param projInfo map where keys represent projections in this context and the values represent collections + * of projection information content that will be used to specify the relationships among the agents. + */ + void setProjectionInfo(std::map >& projInfo); + + void cleanProjectionInfo(std::set& agentsToKeep); + +}; + + + +template +Context::Context() { +} + +template +Context::~Context() { + agents.erase(agents.begin(), agents.end()); + for (ProjPtrIter iter = projections.begin(); iter != projections.end(); ++iter) { + Projection* proj = *iter; + delete proj; + } + projections.clear(); + + for (std::map::iterator iter = valueLayers.begin(); iter != valueLayers.end(); ++iter) { + BaseValueLayer* layer = iter->second; + delete layer; + } + valueLayers.clear(); +} + +template +void Context::addProjection(Projection* projection) { + std::string name = projection->name(); + for (ProjPtrIter iter = projections.begin(); iter != projections.end(); ++iter){ + if(name.compare((*iter)->name()) == 0) throw Repast_Error_9(name); // Projection with specified name already in context + } + for (const_iterator iter = begin(); iter != end(); ++iter) { + projection->addAgent(*iter); + } + projections.push_back(projection); +} + +template +Projection* Context::getProjection(const std::string& name) { + for (ProjPtrIter iter = projections.begin(); iter != projections.end(); ++iter) { + Projection* proj = *iter; + if (proj->name() == name) + return proj; + } + return NULL; +} + +template +T* Context::getAgent(const AgentId& id) { + AgentMapIterator iter = agents.find(id); + if (iter == agents.end()) return 0; + return iter->second.get(); +} + +template +void Context::getRandomAgents(const int count, std::vector& agents) { + for (int i = 0; i < count; i++) { + IntUniformGenerator rnd = Random::instance()->createUniIntGenerator(0, size() - 1); + bool found = false; + while (!found) { + const_iterator iter = begin(); + for (int j = 0, n = rnd.next(); j < n; iter++, j++) + ; + T* agent = iter->get(); + if (find(agents.begin(), agents.end(), agent) == agents.end()) { + agents.push_back(agent); + found = true; + } + } + } +} + +template +T* Context::addAgent(T* agent) { + const AgentId& id = agent->getId(); + typename AgentMap::iterator findIter = agents.find(id); + if (findIter != agents.end()) return &*(findIter->second); + + boost::shared_ptr ptr(agent); + agents[id] = ptr; + + for (ProjPtrIter iter = projections.begin(); iter != projections.end(); ++iter) { + Projection* proj = *iter; + proj->addAgent(ptr); + } + + return agent; +} + +template +void Context::removeAgent(T* agent) { + removeAgent(agent->getId()); +} + +template +void Context::removeAgent(const AgentId id) { + const AgentMapIterator iter = agents.find(id); + if (iter != agents.end()) { + boost::shared_ptr& ptr = iter->second; + for (ProjPtrIter pIter = projections.begin(); pIter != projections.end(); ++pIter) { + Projection* proj = *pIter; + proj->removeAgent(ptr.get()); + } + agents.erase(iter); + } +} + +template +bool Context::contains(const AgentId& id) { + return agents.find(id) != agents.end(); +} + +template +void Context::addValueLayer(BaseValueLayer* layer) { + valueLayers[layer->name()] = layer; +} + +template +template +DiscreteValueLayer* Context::getDiscreteValueLayer(const std::string& valueLayerName) { + + std::map::iterator iter = valueLayers.find(valueLayerName); + if (iter == valueLayers.end()) + return 0; + return static_cast*> (iter->second); +} + +template +template +ContinuousValueLayer* Context::getContinuousValueLayer(const std::string& valueLayerName) { + + std::map::iterator iter = valueLayers.find(valueLayerName); + if (iter == valueLayers.end()) + return 0; + return static_cast*> (iter->second); +} + + + + +template +template +boost::filter_iterator::const_iterator> Context::filteredBegin(const filterStruct& fStruct){ + return boost::filter_iterator::const_iterator> (fStruct, Context::begin(), Context::end()); +} + +template +template +boost::filter_iterator::const_iterator> Context::filteredEnd(const filterStruct& fStruct){ + return boost::filter_iterator::const_iterator> (fStruct, Context::end(), Context::end()); +} + + +template +template +boost::filter_iterator::const_bytype_iterator> Context::byTypeFilteredBegin(const int type, const filterStruct& fStruct){ + return boost::filter_iterator::const_bytype_iterator> (fStruct, Context::byTypeBegin(type), Context::byTypeEnd(type)); +} + +template +template +boost::filter_iterator::const_bytype_iterator> Context::byTypeFilteredEnd(const int type, const filterStruct& fStruct){ + return boost::filter_iterator::const_bytype_iterator> (fStruct, Context::byTypeEnd(type), Context::byTypeEnd(type)); +} + + + + + + + /* ****************************** */ + +// Selection + +template +void Context::selectAgents(std::set& selectedAgents, bool remove){ + selectNElementsAtRandom(begin(), size(), size(), selectedAgents, remove); +} + +template +void Context::selectAgents(std::vector& selectedAgents, bool remove){ + selectNElementsInRandomOrder(begin(), size(), size(), selectedAgents, remove); +} + +template +void Context::selectAgents(int count, std::set& selectedAgents, bool remove){ + selectNElementsAtRandom(begin(), size(), count, selectedAgents, remove); +} + +template +void Context::selectAgents(int count, std::vector& selectedAgents, bool remove){ + selectNElementsInRandomOrder(begin(), end(), count, selectedAgents, remove); +} + +template +void Context::selectAgents(std::set& selectedAgents, int type, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(byTypeBegin(type), byTypeEnd(type), size(), selectedAgents, remove); + else selectNElementsAtRandom(byTypeBegin(type), popSize, size(), selectedAgents, remove); +} + +template +void Context::selectAgents(std::vector& selectedAgents, int type, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(byTypeBegin(type), byTypeEnd(type), size(), selectedAgents, remove); + else selectNElementsInRandomOrder(byTypeBegin(type), popSize, size(), selectedAgents, remove); +} + +template +void Context::selectAgents(int count, std::set& selectedAgents, int type, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(byTypeBegin(type), byTypeEnd(type), count, selectedAgents, remove); + else selectNElementsAtRandom(byTypeBegin(type), popSize, count, selectedAgents, remove); +} + +template +void Context::selectAgents(int count, std::vector& selectedAgents, int type, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(byTypeBegin(type), byTypeEnd(type), count, selectedAgents, remove); + else selectNElementsInRandomOrder(byTypeBegin(type), popSize, count, selectedAgents, remove); +} + +template +template +void Context::selectAgents(std::set& selectedAgents, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(filteredBegin(filter), filteredEnd(filter), size(), selectedAgents, remove); + else selectNElementsAtRandom(filteredBegin(filter), popSize, size(), selectedAgents, remove); +} + +template +template +void Context::selectAgents(std::vector& selectedAgents, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(filteredBegin(filter), filteredEnd(filter), size(), selectedAgents, remove); + else selectNElementsInRandomOrder(filteredBegin(filter), popSize, size(), selectedAgents, remove); +} + +template +template +void Context::selectAgents(int count, std::set& selectedAgents, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(filteredBegin(filter), filteredEnd(filter), count, selectedAgents, remove); + else selectNElementsAtRandom(filteredBegin(filter), popSize, count, selectedAgents, remove); +} + +template +template +void Context::selectAgents(int count, std::vector& selectedAgents, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(filteredBegin(filter), filteredEnd(filter), count, selectedAgents, remove); + else selectNElementsInRandomOrder(filteredBegin(filter), popSize, count, selectedAgents, remove); +} + +template +template +void Context::selectAgents(std::set& selectedAgents, int type, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(byTypeFilteredBegin(type, filter), byTypeFilteredEnd(type, filter), size(), selectedAgents, remove); + else selectNElementsAtRandom(byTypeFilteredBegin(type, filter), popSize, size(), selectedAgents, remove); +} + +template +template +void Context::selectAgents(std::vector& selectedAgents, int type, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(byTypeFilteredBegin(type, filter), byTypeFilteredEnd(type, filter), size(), selectedAgents, remove); + else selectNElementsInRandomOrder(byTypeFilteredBegin(type, filter), popSize, size(), selectedAgents, remove); +} + +template +template +void Context::selectAgents(int count, std::set& selectedAgents, int type, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(byTypeFilteredBegin(type, filter), byTypeFilteredEnd(type, filter), count, selectedAgents, remove); + else selectNElementsAtRandom(byTypeFilteredBegin(type, filter), popSize, count, selectedAgents, remove); +} + +template +template +void Context::selectAgents(int count, std::vector& selectedAgents, int type, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(byTypeFilteredBegin(type, filter), byTypeFilteredEnd(type, filter), count, selectedAgents, remove); + else selectNElementsInRandomOrder(byTypeFilteredBegin(type, filter), popSize, count, selectedAgents, remove); +} + + +// Beta + +template +void Context::getProjectionInfo(AgentRequest req, std::map >& map, + bool secondaryInfo, std::set* secondaryIds, int destProc){ + std::vector ids = req.requestedAgents(); + for(typename std::vector *>::iterator iter = projections.begin(), iterEnd = projections.end(); iter != iterEnd; iter++){ + std::string projName = (*iter)->name(); + map[projName] = std::vector(); // New, then inserted into collection + (*iter)->getProjectionInfo(ids, map[projName], secondaryInfo, secondaryIds, destProc); + } +} + +template +void Context::setProjectionInfo(std::map >& projInfo){ + for(std::map >::iterator iter = projInfo.begin(), iterEnd = projInfo.end(); iter != iterEnd; iter++) + getProjection(iter->first)->updateProjectionInfo(iter->second, this); +} + +template +void Context::cleanProjectionInfo(std::set& agentsToKeep){ + for(typename std::vector *>::iterator iter = projections.begin(), iterEnd = projections.end(); iter != iterEnd; iter++){ + (*iter)->cleanProjectionInfo(agentsToKeep); + } +} + +} +#endif /* CONTEXT_H_ */ diff --git a/libs/repast_hpc/DataSet.h b/libs/repast_hpc/DataSet.h new file mode 100644 index 0000000..ff66b8e --- /dev/null +++ b/libs/repast_hpc/DataSet.h @@ -0,0 +1,73 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DataSet.h + * + * Created on: Oct 13, 2010 + * Author: nick + */ + +#ifndef DATASET_H_ +#define DATASET_H_ + +namespace repast { + +/** + * Interface for recording and writing data. + */ +class DataSet { + +public: + virtual ~DataSet() {} + + /** + * Records the data. + */ + virtual void record() = 0; + + /** + * Writes the data. + */ + virtual void write() = 0; + + /** + * Closes the dataset, after which it must be recreated to be used. + */ + virtual void close() = 0; +}; + +} + + +#endif /* DATASET_H_ */ diff --git a/libs/repast_hpc/DiffusionLayerND.h b/libs/repast_hpc/DiffusionLayerND.h new file mode 100644 index 0000000..7c7ad84 --- /dev/null +++ b/libs/repast_hpc/DiffusionLayerND.h @@ -0,0 +1,266 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DiffusionLayerND.h + * + * Created on: July 25, 2015 + * Author: jtm + */ + +#ifndef DIFFUSIONLAYERND_H_ +#define DIFFUSIONLAYERND_H_ + +#include +#include +#include + +#include "mpi.h" + +#include "RelativeLocation.h" +#include "CartesianTopology.h" +#include "RepastProcess.h" +#include "ValueLayerND.h" + +using namespace std; + +namespace repast { + +/** + * A Diffusor is a custom class that performs diffusion. + * + */ +template +class Diffusor{ + +public: + + Diffusor(); + virtual ~Diffusor(); + + /** + * Implementing classes must return the value that is the + * number of concentric layers that are used in diffusion + * calculations. Typically this will be 1, meaning that + * only immediately adjacent grid cells are considered. + * + * @return the radius of the (rectilinear) volume that encompasses + * all cells that will diffuse into the central cell + */ + virtual int getRadius(); + + /** + * Given a list of values that represent the values in + * adjacent cells, return the value that should be placed + * in the central cell. The list of values should be in + * the order defined by a RelativeLocation object of the + * specified radius with the central cell being at + * (0, 0, 0, ...) + * + * @param values An array of values found in the adjacent + * cells from which the diffusion into this cell can be calculated + * + * @return the value that should be placed in the central cell + * based on diffusion from adjacent cells + */ + virtual T getNewValue(T* values) = 0; +}; + + +/** + * Empty constructor + */ +template +Diffusor::Diffusor(){} + +/** + * No-Op Destructor + */ +template +Diffusor::~Diffusor(){} + +/** + * Tefault radius is one + */ +template +int Diffusor::getRadius(){ + return 1; +} + +/** + * The DiffusionLayerND class is an N-dimensional layer of + * double values that can be used to diffuse through an N-D + * space. Diffusion is a synchronous updating of cells + * based on adjacent cells' values. + * + * The synchronous update is achieved through bank switching: + * two separate grids are maintained, and at any time one of + * them is 'active' and the other is obsolete and ready to + * accept the next round of values. + * + * The diffusion routine uses an MPI_Datatype to collect + * the cells within the defined radius and assemble them + * into a single buffer. For convenience and performance + * it uses an MPI send to self- that is, the process + * performs the send to itself. MPI is used to collect + * the information from its nested, looped memory and + * put it into a contiguous and linear buffer. It is + * up to the diffusor to parse this buffer. Note that + * if the space is at the edge of a strict boundary, + * the values in the buffer will be NaN values. + * + * The radius of diffusion must be less than or equal to + * the size of the buffer zone. + * + */ +template +class DiffusionLayerND: public ValueLayerNDSU{ + +private: + +public: + + DiffusionLayerND(vector processesPerDim, GridDimensions globalBoundaries, int bufferSize, bool periodic, T initialValue = 0, T initialBufferZoneValue = 0); + virtual ~DiffusionLayerND(); + + /** + * Performs the diffusion operation on the entire + * grid (only within local boundaries) + * If omit synchronize is set to 'true' will not perform + * a synchronization after diffusion- this is mainly + * useful for performance testing, as a synchronization + * is required to complete diffusion + * + * @param diffusor A pointer to an instance of a diffusor class + * that will contain the simulation-specific diffusion code + * @param omitSynchronize If true, diffusion will be done but + * not synchronized across processes; this is mainly useful + * for debugging. By default synchronization is performed + * after diffusion. + */ + void diffuse(Diffusor* diffusor, bool omitSynchronize = false); + +private: + + /** + * Diffuse across one of the dimensions. Note that this is called + * recursively. + */ + void diffuseDimension(T* currentDataSpacePointer, T* otherDataSpacePointer, T* vals, Diffusor* diffusor, int dimIndex); + + /** + * Gets the data found in the relevant dimension + */ + void grabDimensionData(T*& destinationPointer, T* startPointer, int radius, int dimIndex); +}; + + +template +DiffusionLayerND::DiffusionLayerND(vector processesPerDim, GridDimensions globalBoundaries, int bufferSize, bool periodic, + T initialValue, T initialBufferZoneValue): ValueLayerNDSU(processesPerDim, globalBoundaries, bufferSize, periodic, + initialValue, initialBufferZoneValue){ + +} + +template +DiffusionLayerND::~DiffusionLayerND(){ +} + +template +void DiffusionLayerND::diffuse(Diffusor* diffusor, bool omitSynchronize){ + int countOfVals = (int)(pow(diffusor->getRadius() * 2 + 1, AbstractValueLayerND::numDims)); + T* vals = new T[countOfVals]; + + diffuseDimension(ValueLayerNDSU::currentDataSpace, ValueLayerNDSU::otherDataSpace, vals, diffusor, AbstractValueLayerND::numDims - 1); + + this->switchValueLayer(); + + if(!omitSynchronize) this->synchronize(); + + delete[] vals; +} + +template +void DiffusionLayerND::diffuseDimension(T* currentDataSpacePointer, T* otherDataSpacePointer, T* vals, Diffusor* diffusor, int dimIndex){ + int bufferEdge = AbstractValueLayerND::dimensionData[dimIndex].leftBufferSize; + int localEdge = bufferEdge + AbstractValueLayerND::dimensionData[dimIndex].localWidth; + + int pointerIncrement = AbstractValueLayerND::places[dimIndex]; + + int i = 0; + for(; i < bufferEdge; i++){ + // Increment the pointers + currentDataSpacePointer += pointerIncrement; + otherDataSpacePointer += pointerIncrement; + } + for(; i < localEdge; i++){ + if(dimIndex == 0){ + // Populate the vals array + double* destLocation = vals; // Note: This gets passed as a handle and changed + grabDimensionData(destLocation, currentDataSpacePointer, diffusor->getRadius(), AbstractValueLayerND::numDims - 1); + *otherDataSpacePointer = diffusor->getNewValue(vals); + } + else{ + diffuseDimension(currentDataSpacePointer, otherDataSpacePointer, vals, diffusor, dimIndex - 1); + } + // Increment the pointers + currentDataSpacePointer += pointerIncrement; + otherDataSpacePointer += pointerIncrement; + } +} + +template +void DiffusionLayerND::grabDimensionData(T*& destinationPointer, T* startPointer, int radius, int dimIndex){ + int pointerIncrement = AbstractValueLayerND::places[dimIndex]; + startPointer -= pointerIncrement * radius; // Go back + int size = 2 * radius + 1; + for(int i = 0; i < size; i++){ + if(dimIndex == 0){ + *destinationPointer = 1; + double myVal = *startPointer; + *destinationPointer = myVal; + destinationPointer++; // Handle; all recursive instances share + } + else{ + grabDimensionData(destinationPointer, startPointer, radius, dimIndex - 1); + } + startPointer += pointerIncrement; + } + +} + + + +} + +#endif /* DIFFUSIONLAYERND_H_ */ diff --git a/libs/repast_hpc/DirectedVertex.h b/libs/repast_hpc/DirectedVertex.h new file mode 100644 index 0000000..e7e3070 --- /dev/null +++ b/libs/repast_hpc/DirectedVertex.h @@ -0,0 +1,166 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DirectedVertex.h + * + * Created on: Oct 13, 2010 + * Author: nick + */ + +#ifndef DIRECTEDVERTEX_H_ +#define DIRECTEDVERTEX_H_ + +#include "Vertex.h" + +namespace repast { + +/** + * Used internally by repast graphs / networks to encapsulate the vertices + * of a directed graph. + * + * @tparam V the type of object stored by in a Vertex. + * @tparam E the EdgeType of the network. + */ +template +class DirectedVertex : public Vertex { + +private: + typedef typename Vertex::AdjListMap AdjListMap; + typedef typename Vertex::AdjListMap::iterator AdjListMapIterator; + typedef typename Vertex::EdgeType EdgeType; + + AdjListMap* incoming, *outgoing; + +public: + /** + * Creates a DirectedVertex that will contain the specified item. + */ + DirectedVertex(boost::shared_ptr item); + virtual ~DirectedVertex(); + + // doc inherited from Vertex + virtual boost::shared_ptr removeEdge(Vertex* other, EdgeType type); + + // doc inherited from Vertex + virtual boost::shared_ptr findEdge(Vertex* other, EdgeType type); + + // doc inherited from Vertex + virtual void addEdge(Vertex* other, boost::shared_ptr edge, EdgeType type); + + // doc inherited from Vertex + virtual void successors(std::vector& out); + + // doc inherited from Vertex + virtual void predecessors(std::vector& out); + + // doc inherited from Vertex + virtual void adjacent(std::vector& out); + + // doc inherited from Vertex + virtual void edges(EdgeType type , std::vector >& out); + + // doc inherited from Vertex + int inDegree(); + + // doc inherited from Vertex + int outDegree(); +}; + +template +DirectedVertex::DirectedVertex(boost::shared_ptr item) : Vertex(item) { + incoming = new AdjListMap(); + outgoing = new AdjListMap(); +} + +template +DirectedVertex::~DirectedVertex(){ + delete incoming; + delete outgoing; +} + +template +boost::shared_ptr DirectedVertex::removeEdge(Vertex* other, EdgeType type) { + return Vertex::removeEdge(other, (type == Vertex::INCOMING ? incoming : outgoing)); +} + +template +boost::shared_ptr DirectedVertex::findEdge(Vertex* other, EdgeType type) { + boost::shared_ptr ret; + AdjListMap* adjMap = (type == Vertex::INCOMING ? incoming : outgoing); + AdjListMapIterator iter = adjMap->find(other); + return (iter != adjMap->end() ? iter->second : ret); +} + +template +void DirectedVertex::addEdge(Vertex* other, boost::shared_ptr edge, EdgeType type) { + if (type == Vertex::INCOMING) (*incoming)[other] = edge; + else (*outgoing)[other] = edge; +} + +template +void DirectedVertex::successors(std::vector& out) { + this->getItems(outgoing, out); +} + +template +void DirectedVertex::predecessors(std::vector& out) { + this->getItems(incoming, out); +} + +template +void DirectedVertex::adjacent(std::vector& out) { + this->getItems(incoming, out); + this->getItems(outgoing, out); +} + +template +int DirectedVertex::inDegree() { + return incoming->size(); +} + +template +int DirectedVertex::outDegree() { + return outgoing->size(); +} + +template +void DirectedVertex::edges(EdgeType type, std::vector >& out) { + Vertex::edges((type == Vertex::INCOMING ? incoming : outgoing), out); +} + +} + + + +#endif /* DIRECTEDVERTEX_H_ */ diff --git a/libs/repast_hpc/Edge.h b/libs/repast_hpc/Edge.h new file mode 100644 index 0000000..ca8c5d9 --- /dev/null +++ b/libs/repast_hpc/Edge.h @@ -0,0 +1,272 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Edge.h + * + * Created on: Jun 4, 2009 + * Author: nick + */ + +#ifndef EDGE_H_ +#define EDGE_H_ + +#include "AgentId.h" +#include "Context.h" +#include "RepastProcess.h" + +#include + +namespace repast { + +/** + * Default graph / network edge implementation. + * + * @tparam V agent type that is the source and target of the edge + */ +template +class RepastEdge { + +public: + enum MASTER_NODE{ DEFAULT, SOURCE, TARGET }; + +private: + double _weight; + V* _source, *_target; + bool _useTargetAsMaster; + bool _conflicted; + + bool defaultTarget(int sourceRank, int targetRank, MASTER_NODE useTargetAsMaster){ + int rank = repast::RepastProcess::instance()->rank(); + return (useTargetAsMaster == SOURCE ? false : + useTargetAsMaster == TARGET ? true : + ((_source->getId().currentRank() != rank) && + (_target->getId().currentRank() == rank))); + } + +public: + + // no arg constructor for serialization + RepastEdge() : _weight(1), _source(0), _target(0), _useTargetAsMaster(false), _conflicted(false){ } + ~RepastEdge(){ } + + /** + * Creates a RepastEdge with the specified source and target and a default + * weight of 1. + * + * @param source the edge source + * @param target the edge target + */ + RepastEdge(V* source, V* target, MASTER_NODE useTargetAsMaster = DEFAULT); + + /** + * Creates a RepastEdge with the specified source, target, + * and weight + * + * @param source the edge source + * @param target the edge target + * @param weight the edge weight + */ + RepastEdge(V* source, V* target, double weight, MASTER_NODE useTargetAsMaster = DEFAULT); + + /** + * Creates a RepastEdge with the specified source and target and a default + * weight of 1. + * + * @param source the edge source + * @param target the edge target + */ + RepastEdge(boost::shared_ptr source, boost::shared_ptr target, MASTER_NODE useTargetAsMaster = DEFAULT); + + /** + * Creates a RepastEdge with the specified source, target, + * and weight + * + * @param source the edge source + * @param target the edge target + * @param weight the edge weight + */ + RepastEdge(boost::shared_ptr source, boost::shared_ptr target, double weight, MASTER_NODE useTargetAsMaster = DEFAULT); + + /** + * Copy constructor that creates a RepastEdge from another RepastEdge. + */ + RepastEdge(const RepastEdge& edge); + + /** + * Gets the source of this RepastEdge. + * + * @return the source of this RepastEdge. + */ + V* source() const { + return _source; + } + + /** + * Gets the target of this RepastEdge. + * + * @return the target of this RepastEdge. + */ + V* target() const { + return _target; + } + + // sets the target. NON USER API + void target(V* target) { + _target = target; + } + + // sets the source. NON USER API + void source(V* source) { + _source = source; + } + + /** + * Gets the weight of this RepastEdge. + * + * @return the weight of this RepastEdge. + */ + double weight() const { + return _weight; + } + + void weight(double wt){ + _weight = wt; + } + + bool usesTargetAsMaster(){ return _useTargetAsMaster; } + + void markConflicted(){ _conflicted = true; } + void clearConflicted(){ _conflicted = false; } + bool isConflicted(){ return _conflicted; } + +}; + +template +RepastEdge::RepastEdge(boost::shared_ptr source, boost::shared_ptr target, MASTER_NODE useTargetAsMaster) : + _source(source.get()), _target(target.get()), _weight(1), _conflicted(false) { + _useTargetAsMaster = defaultTarget(_source->getId().currentRank(), _target->getId().currentRank(), useTargetAsMaster); +} + +template +RepastEdge::RepastEdge(V* source, V* target, MASTER_NODE useTargetAsMaster) : + _source(source), _target(target), _weight(1), _conflicted(false){ + _useTargetAsMaster = defaultTarget(_source->getId().currentRank(), _target->getId().currentRank(), useTargetAsMaster); +} + +template +RepastEdge::RepastEdge(V* source, V* target, double weight, MASTER_NODE useTargetAsMaster) : + _source(source), _target(target), _weight(weight), _conflicted(false){ + _useTargetAsMaster = defaultTarget(_source->getId().currentRank(), _target->getId().currentRank(), useTargetAsMaster); +} + +template +RepastEdge::RepastEdge(boost::shared_ptr source, boost::shared_ptr target, double weight, MASTER_NODE useTargetAsMaster) : + _source(source.get()), _target(target.get()), _weight(weight), _conflicted(false){ + _useTargetAsMaster = defaultTarget(_source->getId().currentRank(), _target->getId().currentRank(), useTargetAsMaster); +} + +template +RepastEdge::RepastEdge(const RepastEdge& edge) : + _source(edge._source), _target(edge._target), _weight(edge._weight), + _useTargetAsMaster(edge._useTargetAsMaster), _conflicted(edge._conflicted) { } + +template +std::ostream& operator<<(std::ostream& os, const RepastEdge& edge) { + os << (*edge.source()) << (edge._useTargetAsMaster ? "" : "(M)") << " -- " << (*edge.target() << (edge._useTargetAsMaster ? "(M)" : "")); + return os; +} + + + +/** + * Serializable; also, does not include agent content, + * only agent IDs + * + * @tparam V type for vertices; must provide AgentID + */ +template +struct RepastEdgeContent { + + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & usesTargetAsMaster; + ar & weight; + ar & source; + ar & target; + } + + AgentId source; + AgentId target; + double weight; + bool usesTargetAsMaster; + + RepastEdgeContent(){} // For serialization + + RepastEdgeContent(RepastEdge* edge): + source(edge->source()->getId()), + target(edge->target()->getId()), + weight(edge->weight()), + usesTargetAsMaster(edge->usesTargetAsMaster()){} +}; + + +/** + * Class for creating RepastEdges from RepastEdgeContent, + * and vice versa + * + * @tparam V type for vertices; must provide AgentID + */ +template +class RepastEdgeContentManager { + +public: + RepastEdgeContentManager(){} + virtual ~RepastEdgeContentManager(){} + + RepastEdge* createEdge(RepastEdgeContent& content, Context* context){ + return new RepastEdge(context->getAgent(content.source), context->getAgent(content.target), content.weight, + (content.usesTargetAsMaster ? repast::RepastEdge::TARGET : repast::RepastEdge::SOURCE) ); + } + + RepastEdgeContent* provideEdgeContent(RepastEdge* edge){ + return new RepastEdgeContent(edge); + } + +}; + +} + +#endif /* EDGE_H_ */ diff --git a/libs/repast_hpc/Graph.cpp b/libs/repast_hpc/Graph.cpp new file mode 100644 index 0000000..9a95e84 --- /dev/null +++ b/libs/repast_hpc/Graph.cpp @@ -0,0 +1,42 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Graph.cpp + * + * Created on: Dec 23, 2008 + * Author: nick + */ + +#include "Graph.h" + diff --git a/libs/repast_hpc/Graph.h b/libs/repast_hpc/Graph.h new file mode 100644 index 0000000..3797b07 --- /dev/null +++ b/libs/repast_hpc/Graph.h @@ -0,0 +1,753 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Graph.h + * + * Created on: Dec 23, 2008 + * Author: nick + */ + +#ifndef GRAPH_H_ +#define GRAPH_H_ + +#include "AgentId.h" +#include "Projection.h" +#include "Edge.h" +#include "DirectedVertex.h" +#include "UndirectedVertex.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace repast { + +/** + * Graph / Network implementation where agents are vertices in the graph. + * + * @tparam V the type agents in the graph. This type should extend repast::Agent + * @tparam E the edge type of the graph. This type should extend + * repast::RepastEdge. + * @tparam Ec class of serializable Edge Content + * @tparam EcM Class that is capable of transforming an Edge into Edge Content and + * vice versa + */ +template +class Graph: public Projection { + +protected: + typedef boost::unordered_map*, HashId> VertexMap; + typedef typename VertexMap::iterator VertexMapIterator; + + typedef typename Projection::RADIUS RADIUS; + + int edgeCount_; + bool isDirected; + VertexMap vertices; + + EcM* edgeContentManager; + + void cleanUp(); + void init(const Graph& graph); + + virtual bool addAgent(boost::shared_ptr agent); + virtual void removeAgent(V* agent); + + virtual void doAddEdge(boost::shared_ptr edge, bool allowOverwrite = true); + +public: + + + /** + * An iterator over the agents that are the vertices in this Graph. + */ + typedef typename boost::transform_iterator , typename VertexMap::const_iterator> vertex_iterator; + + std::set ranksToSendProjInfoTo; // Set these if the ranks for exchanging projection info are known + std::set ranksToReceiveProjInfoFrom; + + std::set ranksToSendAgentStatusInfoTo; // Set these if the ranks for exchanging agent status info are known + std::set ranksToReceiveAgentStatusInfoFrom; + + bool keepsAgents; // Set this to false if you are CERTAIN that this projection will never 'keep' an agent during a proj info sync + bool sendsSecondaryAgents; // Set this to false if you are CERTAIN that this projection will never send secondary agents during an agent status sync (VERY RARE) + + /** + * Creates a Graph with the specified name. + * + * @param name the name of the graph + * @param directed whether or not the created Graph is directed + */ + Graph(std::string name, bool directed, EcM* edgeContentMgr) : + Projection (name), edgeCount_(0), isDirected(directed), edgeContentManager(edgeContentMgr), keepsAgents(true), sendsSecondaryAgents(true) { + } + + /** + * Copy constructor for the graph. + */ + Graph(const Graph& graph); + virtual ~Graph(); + + // assignment + Graph& operator=(const Graph& graph); + + /** + * Adds an edge between source and target to this Graph. + * + * @param source the source of the edge + * @param target the target of the edge + * + * @return the added edge. + */ + virtual boost::shared_ptr addEdge(V* source, V* target); + + /** + * Adds an edge with the specified weight between source and target to this Graph. + * + * @param source the source of the edge + * @param target the target of the edge + * @param weight the weight of the edge + * + * @return the added edge. + */ + virtual boost::shared_ptr addEdge(V* source, V* target, double weight); + + /** + * Gets the edge between the source and target or 0 + * if no such edge is found. + * + * @param source the source of the edge to find + * @param target the target of the edge to find + * + * @return the found edge or 0. + */ + virtual boost::shared_ptr findEdge(V* source, V* target); + + /** + * Gets the sucessors of the specified vertex and puts them in + * out. + * + * @param vertex the vertex whose successors we want to get + * @param [out] where the successors will be returned + */ + virtual void successors(V* vertex, std::vector& out); + + /** + * Gets the predecessors of the specified vertex and puts them in + * out. + * + * @param vertex the vertex whose predecessors we want to get + * @param [out] where the predecessors will be returned + */ + virtual void predecessors(V* vertex, std::vector& out); + + /** + * Gets all the agent adjacent to the specified vertex. + * + * @param vertex the vertex whose adjacent agents we want to get + * @param [out] the vector where the results will be put + */ + virtual void adjacent(V* vertex, std::vector& out); + + /** + * Removes the edge between source and target from this Graph. + * + * @param source the source of the edge + * @param target the target of the edge + */ + virtual void removeEdge(V* source, V* target); + + /** + * Removes the edge between source and target from this Graph. + * + * @param source the id of the vertex that is the source of the edge + * @param target the id of the vertex that is the target of the edge + */ + virtual void removeEdge(const AgentId& source, const AgentId& target); + + /** + * Gets the in-degree of the specified vertex. + * + * @return the in-degree of the specified vertex. + */ + virtual int inDegree(V* vertex); + + /** + * Gets the out-degree of the specified vertex. + * + * @return the out-degree of the specified vertex. + */ + virtual int outDegree(V* vertex); + + /** + * Gets the number of edges in this Graph. + * + * @return the number of edges in this Graph. + */ + int edgeCount() const { + return edgeCount_; + } + + /** + * Gets the number of vertices in this Graph. + * + * @return the number of vertices in this Graph. + */ + int vertexCount() const { + return vertices.size(); + } + + /** + * Gets the start of an iterator over all the vertices in this graph. + * The iterator dereferences to a pointer to agents of type V. + * + * @return the start of an iterator over all the vertices in this graph. + */ + vertex_iterator verticesBegin() { + return vertex_iterator(vertices.begin()); + } + + /** + * Gets the end of an iterator over all the vertices in this graph. + * The iterator dereferences to a pointer to agents of type V. + * + * @return the end of an iterator over all the vertices in this graph. + */ + vertex_iterator verticesEnd() { + return vertex_iterator(vertices.end()); + } + + void showEdges(); + + + // Beta + virtual bool isMaster(E* e) = 0; + + virtual bool keepsAgentsOnSyncProj(){ return keepsAgents; } + + virtual bool sendsSecondaryAgentsOnStatusExchange(){ return sendsSecondaryAgents; } + + virtual void getInfoExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom); + + virtual void getAgentStatusExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom); + + virtual void getProjectionInfo(std::vector& agents, std::vector& packets, + bool secondaryInfo = false, std::set* secondaryIds = 0, int destProc = -1); + + virtual ProjectionInfoPacket* getProjectionInfo(AgentId id, bool secondaryInfo = false, std::set* secondaryIds = 0, int destProc = -1 ); + + virtual void updateProjectionInfo(ProjectionInfoPacket* pip, Context* context); + + virtual void getRequiredAgents(std::set& agentsToTest, std::set& agentsRequired, RADIUS radius =Projection::PRIMARY); + + virtual void getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush); + + virtual void cleanProjectionInfo(std::set& agentsToKeep); + + void clearConflictedEdges(); + + void getConflictedEdges(std::set >& conflictedEdges); + +}; + +template +Graph::~Graph() { + cleanUp(); +} + +template +void Graph::cleanUp() { + for (VertexMapIterator iter = vertices.begin(); iter != vertices.end(); ++iter) { + delete iter->second; + } + vertices.clear(); +} + +template +Graph::Graph(const Graph& graph) { + init(graph); +} + +template +void Graph::init(const Graph& graph) { + edgeCount_ = graph.edgeCount_; + isDirected = graph.isDirected; + edgeContentManager = graph.edgeContentManager; + + // create new vertices from the old ones + for (VertexMapIterator iter = graph.vertices.begin(); iter != graph.vertices.end(); ++iter) { + Vertex* vertex = iter->second; + if (isDirected) { + vertices[iter->first] = new DirectedVertex (vertex->item()); + } else { + vertices[iter->first] = new UndirectedVertex (vertex->item()); + } + } + + // fill adj list maps using the new vertex info. + // create new vertices from the old ones + for (VertexMapIterator iter = graph.vertices.begin(); iter != graph.vertices.end(); ++iter) { + Vertex* vertex = iter->second; + Vertex* newVert = vertices[iter->first]; + std::vector > edges; + + vertex->edges(Vertex::OUTGOING, edges); + for (typename std::vector >::iterator iter = edges.begin(); iter != edges.end(); ++iter) { + // create new edge and add it + boost::shared_ptr newEdge(new E(**iter)); + doAddEdge(newEdge); + } + } +} + +template +Graph& Graph::operator=(const Graph& graph) { + if (this != &graph) { + cleanUp(); + init(graph); + } + + return *this; +} + +template +boost::shared_ptr Graph::addEdge(V* source, V* target) { + boost::shared_ptr ret; + + const VertexMapIterator notFound = Graph::vertices.end(); + + VertexMapIterator srcIter = vertices.find(source->getId()); + if (srcIter == notFound) return ret; + + VertexMapIterator targetIter = vertices.find(target->getId()); + if (targetIter == notFound) return ret; + + boost::shared_ptr edge(new E(srcIter->second->item(), targetIter->second->item())); + doAddEdge(edge); + return edge; +} + +template +boost::shared_ptr Graph::addEdge(V* source, V* target, double weight) { + boost::shared_ptr ret; + + const VertexMapIterator notFound = Graph::vertices.end(); + + VertexMapIterator srcIter = vertices.find(source->getId()); + if (srcIter == notFound) return ret; + + VertexMapIterator targetIter = vertices.find(target->getId()); + if (targetIter == notFound) return ret; + + boost::shared_ptr edge(new E(srcIter->second->item(), targetIter->second->item(), weight)); + doAddEdge(edge); + return edge; +} + +template +void Graph::successors(V* vertex, std::vector& out) { + VertexMapIterator iter = Graph::vertices.find(vertex->getId()); + if (iter != Graph::vertices.end()) iter->second->successors(out); +} + +template +void Graph::predecessors(V* vertex, std::vector& out) { + VertexMapIterator iter = Graph::vertices.find(vertex->getId()); + if (iter != vertices.end()) iter->second->predecessors(out); +} + +template +void Graph::adjacent(V* vertex, std::vector& out) { + VertexMapIterator iter = Graph::vertices.find(vertex->getId()); + if (iter != vertices.end()) iter->second->adjacent(out); +} + +template +int Graph::inDegree(V* vertex) { + VertexMapIterator iter = Graph::vertices.find(vertex->getId()); + return (iter != vertices.end() ? iter->second->inDegree() : 0); +} + +template +int Graph::outDegree(V* vertex) { + VertexMapIterator iter = Graph::vertices.find(vertex->getId()); + return (iter != vertices.end() ? iter->second->outDegree() : 0); +} + +template +void Graph::removeEdge(const AgentId& sourceId, const AgentId& targetId) { + const VertexMapIterator vertexNotFound = Graph::vertices.end(); + + VertexMapIterator iter = Graph::vertices.find(sourceId); + if (iter == vertexNotFound) return; + Vertex* sVert = iter->second; + + iter = Graph::vertices.find(targetId); + if (iter == vertexNotFound) return; + Vertex* tVert = iter->second; + + boost::shared_ptr edgeNotFound; + if(sVert->removeEdge(tVert, Vertex::OUTGOING) != edgeNotFound) edgeCount_--; + tVert->removeEdge(sVert, Vertex::INCOMING); + +} + +template +void Graph::removeEdge(V* source, V* target) { + removeEdge(source->getId(), target->getId()); +} + +template +void Graph::removeAgent(V* vertex) { + VertexMapIterator iter = Graph::vertices.find(vertex->getId()); + if (iter != vertices.end()) { + Vertex* iVert = iter->second; + std::vector adjacentVertices; + iVert->adjacent(adjacentVertices); + for(typename std::vector::iterator adjIter = adjacentVertices.begin(), adjIterEnd = adjacentVertices.end(); adjIter != adjIterEnd; ++adjIter){ + removeEdge(vertex->getId(), (*adjIter)->getId()); + removeEdge((*adjIter)->getId(), vertex->getId()); + } + + delete iVert; + vertices.erase(iter); + } +} + +template +bool Graph::addAgent(boost::shared_ptr agent) { + if(!Projection::agentCanBeAdded(agent)) return false; + if (vertices.find(agent->getId()) != vertices.end()) return false; + + if(isDirected) vertices[agent->getId()] = new DirectedVertex (agent); + else vertices[agent->getId()] = new UndirectedVertex (agent); + + return true; +} + +template +boost::shared_ptr Graph::findEdge(V* source, V* target) { + boost::shared_ptr ret; + + const VertexMapIterator notFound = Graph::vertices.end(); + + VertexMapIterator sIter = vertices.find(source->getId()); + if (sIter == notFound) return ret; + + VertexMapIterator tIter = vertices.find(target->getId()); + if (tIter == notFound) return ret; + + return sIter->second->findEdge(tIter->second, Vertex::OUTGOING); +} + +template +void Graph::doAddEdge(boost::shared_ptr edge, bool allowOverwrite) { + V* source = edge->source(); + V* target = edge->target(); + + Vertex* vSource = vertices[source->getId()]; + Vertex* vTarget = vertices[target->getId()]; + + boost::shared_ptr notFound; + boost::shared_ptr extant = vSource->findEdge(vTarget, Vertex::OUTGOING); + if(extant == notFound){ + vSource->addEdge(vTarget, edge, Vertex::OUTGOING); + vTarget->addEdge(vSource, edge, Vertex::INCOMING); + edgeCount_++; + } + else{ + if(allowOverwrite){ + vSource->removeEdge(vTarget, Vertex::OUTGOING); + vTarget->removeEdge(vSource, Vertex::INCOMING); + + vSource->addEdge(vTarget, edge, Vertex::OUTGOING); + vTarget->addEdge(vSource, edge, Vertex::INCOMING); + } + else extant->markConflicted(); + } +} + +template +void Graph::showEdges(){ + std::set > edgeSet; + for(typename VertexMap::iterator iter = vertices.begin(), iterEnd = vertices.end(); iter != iterEnd; ++iter){ + std::vector > edges; + (*iter).second->edges(repast::Vertex::INCOMING, edges); + (*iter).second->edges(repast::Vertex::OUTGOING, edges); + for(typename std::vector >::iterator EI = edges.begin(), EIEnd = edges.end(); EI != EIEnd; ++EI) edgeSet.insert(*EI); + } + for(typename std::set >::iterator ei = edgeSet.begin(), eiEnd = edgeSet.end(); ei != eiEnd; ++ei){ + std::cout << "SOURCE: " << (*ei)->source()->getId() << " TARGET: " << (*ei)->target()->getId() << " " << (isMaster(&**ei) ? "MASTER" : "NONLOCAL") << " Weight = " << (*ei)->weight() << std::endl; + } +} + + + +// Beta + +template +void Graph::getInfoExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom){ + psToSendTo.insert(ranksToSendProjInfoTo.begin(), ranksToSendProjInfoTo.end()); + psToReceiveFrom.insert(ranksToReceiveProjInfoFrom.begin(), ranksToReceiveProjInfoFrom.end()); +} + +template +void Graph::getAgentStatusExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom){ + psToSendTo.insert(ranksToSendAgentStatusInfoTo.begin(), ranksToSendAgentStatusInfoTo.end()); + psToReceiveFrom.insert(ranksToReceiveAgentStatusInfoFrom.begin(), ranksToReceiveAgentStatusInfoFrom.end()); +} + + +template +void Graph::getProjectionInfo(std::vector& agents, std::vector& packets, + bool secondaryInfo, std::set* secondaryIds, int destProc){ + if(secondaryInfo == false) return; // Can be skipped entirely for graphs + // If not, call the superclass's implementation + Projection::getProjectionInfo(agents, packets, secondaryInfo, secondaryIds, destProc); +} + + +template +ProjectionInfoPacket* Graph::getProjectionInfo(AgentId id, bool secondaryInfo, std::set* secondaryIds, int destProc ){ + if(secondaryInfo == false) return 0; // All graph projection info is secondary; if not returning it, done. + + VertexMapIterator agent = vertices.find(id); + if(agent == vertices.end()) return 0; // The requested agent is not in this graph + + std::vector edgeContent; + + std::vector > edges; + agent->second->edges(Vertex::INCOMING, edges); + agent->second->edges(Vertex::OUTGOING, edges); + // Sometimes the incoming and outgoing edges are the same; purge duplicates + std::set > edgeSet; + edgeSet.insert(edges.begin(), edges.end()); + // Make all four instances of the loop to optimize for each case + AgentId sourceId; + AgentId targetId; + AgentId otherId; + + if(secondaryIds == 0){ + if(destProc > -1){ + for(typename std::set >::iterator iter = edgeSet.begin(), iterEnd = edgeSet.end(); iter != iterEnd; ++iter){ + sourceId = (*iter)->source()->getId(); + targetId = (*iter)->target()->getId(); + otherId = (sourceId != id ? sourceId : targetId); + if(otherId.currentRank() == destProc) edgeContent.push_back(*(edgeContentManager->provideEdgeContent(iter->get()))); + } + } + else{ + for(typename std::set >::iterator iter = edgeSet.begin(), iterEnd = edgeSet.end(); iter != iterEnd; ++iter){ + sourceId = (*iter)->source()->getId(); + targetId = (*iter)->target()->getId(); + otherId = (sourceId != id ? sourceId : targetId); + edgeContent.push_back(*(edgeContentManager->provideEdgeContent(iter->get()))); + } + } + } + else{ + if(destProc > -1){ + for(typename std::set >::iterator iter = edgeSet.begin(), iterEnd = edgeSet.end(); iter != iterEnd; ++iter){ + sourceId = (*iter)->source()->getId(); + targetId = (*iter)->target()->getId(); + otherId = (sourceId != id ? sourceId : targetId); + if(otherId.currentRank() == destProc){ + secondaryIds->insert(otherId); + edgeContent.push_back(*(edgeContentManager->provideEdgeContent(iter->get()))); + } + } + } + else{ + for(typename std::set >::iterator iter = edgeSet.begin(), iterEnd = edgeSet.end(); iter != iterEnd; ++iter){ + sourceId = (*iter)->source()->getId(); + targetId = (*iter)->target()->getId(); + otherId = (sourceId != id ? sourceId : targetId); + secondaryIds->insert(otherId); + edgeContent.push_back(*(edgeContentManager->provideEdgeContent(iter->get()))); + } + } + } + + return new SpecializedProjectionInfoPacket(id, edgeContent); +} + +template +void Graph::updateProjectionInfo(ProjectionInfoPacket* pip, Context* context){ + SpecializedProjectionInfoPacket* spip = static_cast*>(pip); + std::vector &edges = spip->data; + for(int i = 0; i < edges.size(); i++){ + boost::shared_ptr newEdge(edgeContentManager->createEdge(edges[i], context)); + doAddEdge(newEdge, false); + } +} + + + +template +void Graph::getRequiredAgents(std::set& agentsToTest, std::set& agentsRequired, RADIUS radius){ + switch(radius){ + case Projection::PRIMARY: {// Keep only the nonlocal ends of MASTER edges + std::set::iterator iter = agentsToTest.begin(); + while(iter != agentsToTest.end()){ + VertexMapIterator vertex = Graph::vertices.find(*iter); + if(vertex != vertices.end()){ + std::vector > edges; + vertex->second->edges(Vertex::INCOMING, edges); + vertex->second->edges(Vertex::OUTGOING, edges); + std::set > edgeSet; + edgeSet.insert(edges.begin(), edges.end()); + edges.clear(); + edges.assign(edgeSet.begin(), edgeSet.end()); + bool keep = false; + for(typename std::vector >::iterator edgeIter = edges.begin(), edgeIterEnd = edges.end(); edgeIter != edgeIterEnd; edgeIter++){ + if(isMaster(&**edgeIter)){ + agentsRequired.insert(*iter); + keep = true; + break; + } + } + if(keep){ + std::set::iterator iterTEMP = iter; + iter++; + agentsToTest.erase(*iterTEMP); + } + else iter++; + } + else iter++; + } + break; + } + case Projection::SECONDARY: {// Keep any nonlocal agent that is in any edge + std::set::iterator iter = agentsToTest.begin(); + while(iter != agentsToTest.end()){ + VertexMapIterator vertex = Graph::vertices.find(*iter); + if(vertex != vertices.end()){ + std::vector > edges; + vertex->second->edges(Vertex::INCOMING, edges); + vertex->second->edges(Vertex::OUTGOING, edges); + if(edges.size() > 0){ + std::set::iterator iterTEMP = iter; + iter++; + agentsToTest.erase(*iterTEMP); + } + else iter++; + } + else iter++; + } + break; + } + } +} + +template +void Graph::getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush){ + if(agentsToTest.size() == 0) return; + // The local agent ends of master edges must be pushed to the process of the non-local end + std::set::iterator iter = agentsToTest.begin(); + while(iter != agentsToTest.end()){ + VertexMapIterator vertexMapEntry = Graph::vertices.find(*iter); + if(vertexMapEntry != vertices.end()){ + int localRank = vertexMapEntry->second->item()->getId().currentRank(); + std::vector > edges; + vertexMapEntry->second->edges(Vertex::INCOMING, edges); + vertexMapEntry->second->edges(Vertex::OUTGOING, edges); + for(typename std::vector >::iterator edgeIter = edges.begin(), edgeIterEnd = edges.end(); edgeIter != edgeIterEnd; ++edgeIter){ + boost::shared_ptr e = *edgeIter; + if(isMaster(&**edgeIter)){ + AgentId sourceId = (*edgeIter)->source()->getId(); + AgentId targetId = (*edgeIter)->target()->getId(); + AgentId otherAgentId = (sourceId != *iter ? sourceId : targetId); + int destRank = otherAgentId.currentRank(); + if(destRank != localRank){ + agentsToPush[destRank].insert(*iter); + } + } + } + } + iter++; + } +} + +template +void Graph::cleanProjectionInfo(std::set& agentsToKeep){ + for(std::set::iterator iter = agentsToKeep.begin(), iterEnd = agentsToKeep.end(); iter != iterEnd; ++iter){ + VertexMapIterator vertexMapEntry = Graph::vertices.find(*iter); + if(vertexMapEntry != vertices.end()){ + std::vector > edges; + vertexMapEntry->second->edges(Vertex::INCOMING, edges); + for(typename std::vector >::iterator edgeIter = edges.begin(), edgeIterEnd = edges.end(); edgeIter != edgeIterEnd; ++edgeIter){ + if(!isMaster(&**edgeIter)) removeEdge((*edgeIter)->source(), (*edgeIter)->target()); + } + edges.clear(); + vertexMapEntry->second->edges(Vertex::OUTGOING, edges); + for(typename std::vector >::iterator edgeIter = edges.begin(), edgeIterEnd = edges.end(); edgeIter != edgeIterEnd; ++edgeIter){ + if(!isMaster(&**edgeIter)) removeEdge((*edgeIter)->source(), (*edgeIter)->target()); + } + } + } +} + +template +void Graph::clearConflictedEdges(){ + for(typename VertexMap::iterator iter = vertices.begin(), iterEnd = vertices.end(); iter != iterEnd; ++iter){ + std::vector > edges; + (*iter).second->edges(repast::Vertex::INCOMING, edges); + (*iter).second->edges(repast::Vertex::OUTGOING, edges); + for(typename std::vector >::iterator EI = edges.begin(), EIEnd = edges.end(); EI != EIEnd; ++EI){ + (*EI)->clearConflicted(); + } + } +} + +template +void Graph::getConflictedEdges(std::set >& conflictedEdges){ + for(typename VertexMap::iterator iter = vertices.begin(), iterEnd = vertices.end(); iter != iterEnd; ++iter){ + std::vector > edges; + (*iter).second->edges(repast::Vertex::INCOMING, edges); + (*iter).second->edges(repast::Vertex::OUTGOING, edges); + for(typename std::vector >::iterator EI = edges.begin(), EIEnd = edges.end(); EI != EIEnd; ++EI){ + if((*EI)->isConflicted()) conflictedEdges.insert(*EI); + } + } +} + + +} + +#endif /* GRAPH_H_ */ diff --git a/libs/repast_hpc/Grid.h b/libs/repast_hpc/Grid.h new file mode 100644 index 0000000..a0843dc --- /dev/null +++ b/libs/repast_hpc/Grid.h @@ -0,0 +1,268 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Grid.h + * + * Created on: Aug 10, 2010 + * Author: nick + */ + +#ifndef GRID_H_ +#define GRID_H_ + +#include +#include + +#include "Projection.h" +#include "Point.h" +#include "GridDimensions.h" +#include "AgentId.h" + +namespace repast { + +/** + * Abstract interface for Grids and ContinuousSpaces. + * + * @tparam T the type of objects this Grid contains + * @tparam GPType the coordinate type of the grid point locations. This must + * be an int or a double. + */ +template +class Grid: public Projection { + typedef typename Projection::RADIUS RADIUS; + +public: + + /** + * Creates a Grid with the specified name. + * + * @param name the name of the Grid. This should be unique + * among Projections. + */ + Grid(std::string name) : + Projection (name) { + } + virtual ~Grid() { + } + + /** + * Gets whether or not this grid contains the agent with the specified id. + * + * @param id the id of the agent to check + * + * @return true if the grid contains the agent, otherwise false. + */ + virtual bool contains(const AgentId& id) = 0; + + /** + * Moves the specified agent to the specified point. + * + * @param id the id of the agent to move + * @param pt where to move the agent to + * + * @return true if the move was successful, otherwise false + */ + virtual bool moveTo(const AgentId& id, const Point& pt) = 0; + + /** + * Moves the specifed object the specified distance from its current + * position along the specified angle. For example, moveByVector(object, 1, Grid.NORTH) + * will move the object 1 unit "north" up the y-axis, assuming a 2D grid. Similarly, + * grid.moveByVector(object, 2, 0, Math.toRadians(90), 0) will rotate 90 + * degrees around the y-axis, thus moving the object 2 units along the z-axis. + *

+ * Note that the radians / degrees are incremented in a anti-clockwise fashion, such that + * 0 degrees is "east", 90 degrees is "north", 180 is "west" and 270 is "south." + * + * @param agent the object to move + * @param distance the distance to move + * @param anglesInRadians the angle to move along in radians. + * @return a pair containing a bool that indicates whether the move was a success or not, and the + * point where the agent was moved to. + */ + virtual std::pair > moveByVector(const T* agent, double distance, + const std::vector& anglesInRadians) = 0; + + /** + * Moves the specified object from its current location by the specified + * amount. For example moveByDisplacement(object, 3, -2, 1) + * will move the object by 3 along the x-axis, -2 along the y and 1 along + * the z. The displacement argument can be less than the number of + * dimensions in the space in which case the remaining argument will be set + * to 0. For example, moveByDisplacement(object, 3) will move + * the object 3 along the x-axis and 0 along the y and z axes, assuming a 3D + * grid. + * + * @param agent + * the object to move + * @param displacement + * the amount to move the object + * @return a pair containing a bool that indicates whether the move was a success or not, and the + * point where the agent was moved to. + */ + virtual std::pair > + moveByDisplacement(const T* agent, const std::vector& displacement) = 0; + + /** + * Gets the dimensions of this Grid. + * + * @return the dimensions of this Grid. + */ + virtual const GridDimensions dimensions() const = 0; + + virtual const GridDimensions bounds() const = 0; + + + /** + * Gets the first object found at the specified point, or NULL if there is no + * such object. + * + * @return the first object found at the specified point, or NULL if there is no + * such object. + */ + virtual T* getObjectAt(const Point& pt) const = 0; + + /** + * Gets all the objects found at the specified point. The found objects + * will be put into the out parameter. + * + * @param pt the point to get all the objects at + * @param [out] out the vector into which the found objects will be put + */ + virtual void getObjectsAt(const Point& pt, std::vector& out) const = 0; + + /** + * Gets the location of this agent and puts it in the + * specified vector. The x coordinate will be the first value, + * the y the second and so on. + * + * @param agent the agent whose location we want to get + * @param [out] the vector where the agents location will be put + * + * @return true if the location was successfully found, otherwise false. + */ + virtual bool getLocation(const T* agent, std::vector& out) const = 0; + + /** + * Gets the location of this agent and puts it in the + * specified vectors. The x coordinate will be the first value, + * the y the second and so on. + * + * @param id the id of the agent whose location we want to get + * @param [out] out the agent's location will be put into this vector + * + * @return true if the location was successfully found, otherwise false. + */ + virtual bool getLocation(const AgentId& id, std::vector& out) const = 0; + + /** + * Gets vector difference between point 1 and point 2, putting the result + * in out. + * + * @param p1 the first point + * @param p2 the second point + * @param [out] the vector where the difference will be put + * + */ + virtual void getDisplacement(const Point& pt1, const Point& pt2, std::vector& out) const = 0; + + /** + * Gets the distance between the two grid points. + * + * @param p1 the first point + * @param p2 the second point + * + * @return the distance between pt1 and pt2. + */ + virtual double getDistance(const Point& pt1, const Point& pt2) const = 0; + + /** + * Gets the square of the distance between the two grid points. + * + * @param p1 the first point + * @param p2 the second point + * + * @return the square of the distance between pt1 and pt2. + */ + virtual double getDistanceSq(const Point& pt1, const Point& pt2) const = 0; + + /** + * Translates the specified location by the specified displacement put the result in out. + * + * @param location the initial location + * @param displacement the amount to translate the location by + * @param [out] out the vector where the result of the translation is put + */ + virtual void + translate(const Point& location, const Point& displacement, std::vector& out) const = 0; + + /** + * Transforms the specified location using the properties (e.g. toroidal) of this space. + * + * @param location the location to transform + * @param [out] out the vector where the result of the transform will be put + */ + virtual void + transform(const std::vector& location, std::vector& out) const = 0; + + /** + * Gets whether or not this grid is periodic (i.e. toroidal). + * + * @return true if this Grid is periodic, otherwise false. + */ + virtual bool isPeriodic() const = 0; + + virtual ProjectionInfoPacket* getProjectionInfo(AgentId id, bool secondaryInfo = false, std::set* secondaryIds = 0, int destProc = -1 ) = 0; + + virtual void updateProjectionInfo(ProjectionInfoPacket* pip, Context* context) = 0; + + virtual void getRequiredAgents(std::set& agentsToTest, std::set& agentsRequired, RADIUS radius = Projection::PRIMARY){} // Grids allow all agents to be dropped b/c agent info not dependent on other agents + + virtual void getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush) = 0; + + virtual bool keepsAgentsOnSyncProj(){ return false; } + + virtual bool sendsSecondaryAgentsOnStatusExchange(){ return false; } + + virtual void getInfoExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom) = 0; + + virtual void getAgentStatusExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom) = 0; + + virtual void cleanProjectionInfo(std::set& agentsToKeep){}; // Grids don't do this + +}; + +} + +#endif /* GRID_H_ */ diff --git a/libs/repast_hpc/Grid2DQuery.h b/libs/repast_hpc/Grid2DQuery.h new file mode 100644 index 0000000..e04c0d0 --- /dev/null +++ b/libs/repast_hpc/Grid2DQuery.h @@ -0,0 +1,98 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Grid2DQuery.h + * + * Created on: Aug 12, 2010 + * Author: nick + */ + +#ifndef GRID2DQUERY_H_ +#define GRID2DQUERY_H_ + +#include "RepastErrors.h" + +namespace repast { + +/** + * Base class for neighborhood queries on discrete Grids. + * + * @tparam T the type of object in the Grid. + */ +template +class Grid2DQuery { + +protected: + const Grid* _grid; + int minMax[2][2]; + +public: + + /** + * Creates Grid2DQuery that will query the specified Grid. + */ + Grid2DQuery(const Grid* grid); + virtual ~Grid2DQuery() {} + + /** + * Queries the Grid for the neighbors surrounding the center point within a specified range. What + * constitutes the neighborhood is determines by subclass implementors. + * + * @param center the center of the neighborhood + * @param range the range of the neighborhood out from the center + * @param includeCenter whether or not to include any agents at the center + * @param [out] the neighboring agents will be returned in this vector + */ + virtual void query(const Point& center, int range, bool includeCenter, std::vector& out) const = 0; +}; + +template +Grid2DQuery::Grid2DQuery(const Grid* grid) : + _grid(grid) { + if (grid->bounds().dimensionCount() != 2) + throw Repast_Error_10(grid->bounds().dimensionCount()); // Grid2DQuery only accepts 2D grids + + GridDimensions bounds = grid->bounds(); + + for (size_t i = 0; i < 2; i++) { + int origin = bounds.origin(i); + minMax[i][0] = origin; + // max is EXCLUSIVE + minMax[i][1] = (bounds.extents(i) + bounds.origin(i)); + } +} + +} + +#endif /* GRID2DQUERY_H_ */ diff --git a/libs/repast_hpc/GridComponents.cpp b/libs/repast_hpc/GridComponents.cpp new file mode 100644 index 0000000..881c4f8 --- /dev/null +++ b/libs/repast_hpc/GridComponents.cpp @@ -0,0 +1,194 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * GridComponents.cpp + * + * Created on: Jun 23, 2009 + * Author: nick + */ + +#include "GridComponents.h" +#include "RepastErrors.h" + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace repast { + +// Borders (parent class for border implementations with fixed boundaries +Borders::Borders(GridDimensions d): _dimensions(d){ } + +void Borders::boundsCheck(const vector& pt) const { + if (!_dimensions.contains(pt)) throw Repast_Error_12(pt, _dimensions); // Point is out of dimension range +} + +void Borders::boundsCheck(const vector& pt) const { + if (!_dimensions.contains(pt)) throw std::out_of_range("Point is out of dimension range"); // ! +} + +void Borders::transform(const std::vector& in, std::vector& out) const { + boundsCheck(in); + if (out.size() != in.size()) out.insert(out.begin(), in.size(), 0); + std::copy(in.begin(), in.end(), out.begin()); +} + +void Borders::transform(const std::vector& in, std::vector& out) const { + boundsCheck(in); + if (out.size() != in.size()) out.insert(out.begin(), in.size(), 0); + std::copy(in.begin(), in.end(), out.begin()); +} + + +// Strict Borders (translations outside the borders throw error +StrictBorders::StrictBorders(GridDimensions d): Borders(d) { } + +void StrictBorders::translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement) const { + if (displacement.size() != oldPos.size() || displacement.size() != newPos.size()) + throw Repast_Error_13(oldPos, newPos, displacement); // Position and displacement vectors must be of the same size + + for (int i = 0, n = displacement.size(); i < n; ++i) newPos[i] = oldPos[i] + displacement[i]; + + boundsCheck(newPos); +} + +void StrictBorders::translate(const std::vector& oldPos, std::vector& newPos, + const std::vector& displacement) const { + if (displacement.size() != oldPos.size() || displacement.size() != newPos.size()) + throw Repast_Error_14(oldPos, newPos, displacement); // Position and displacement vectors must be of the same size + + for (int i = 0, n = displacement.size(); i < n; ++i) newPos[i] = oldPos[i] + displacement[i]; + + boundsCheck(newPos); +} + + + +// Sticky Borders: Translations outside the border are fixed to the border + +StickyBorders::StickyBorders(GridDimensions d): Borders(d) { + for (size_t i = 0, n = _dimensions.dimensionCount(); i < n; ++i) { + mins.push_back(_dimensions.origin(i)); + maxs.push_back(_dimensions.origin(i) + _dimensions.extents(i)); // Originally - 1 + } +} + + +void StickyBorders::translate(const std::vector& oldPos, std::vector& newPos, + const std::vector& displacement) const { + if (displacement.size() != oldPos.size() || displacement.size() != newPos.size()) + throw Repast_Error_17(oldPos, newPos, displacement); // Position and displacement vectors must be of the same size + + for (size_t i = 0, n = displacement.size(); i < n; ++i) newPos[i] = calcCoord(oldPos[i] + displacement[i], i); + +} + +void StickyBorders::translate(const std::vector& oldPos, std::vector& newPos, + const std::vector& displacement) const { + if (displacement.size() != oldPos.size() || displacement.size() != newPos.size()) + throw Repast_Error_18(oldPos, newPos, displacement); // Position and displacement vectors must be of the same size + + for (size_t i = 0, n = displacement.size(); i < n; ++i) newPos[i] = calcCoord(oldPos[i] + displacement[i], i); + +} + + +// Wrap-around Borders + +WrapAroundBorders::WrapAroundBorders(GridDimensions dimensions): _dimensions(dimensions) { + for (size_t i = 0; i < dimensions.dimensionCount(); ++i) { + mins.push_back(dimensions.origin(i)); + maxs.push_back(dimensions.origin(i) + dimensions.extents(i)); // Originally - 1 + } +} + +void WrapAroundBorders::transform(const std::vector& in, std::vector& out) const { + if (out.size() < in.size()) out.insert(out.begin(), in.size(), 0); + + for (size_t i = 0, n = in.size(); i < n; ++i){ + int coord = in[i]; + if(coord >= mins[i] && coord < maxs[i]) + out[i] = coord; + else + out[i] = fmod((double)(coord-_dimensions.origin(i)), (double)_dimensions.extents(i)) + + (coord < _dimensions.origin(i) ? _dimensions.extents(i) : 0) + + _dimensions.origin(i); + } +} + +void WrapAroundBorders::transform(const std::vector& in, std::vector& out) const { + if (out.size() < in.size()) out.insert(out.begin(), in.size(), 0); + + for (size_t i = 0, n = in.size(); i < n; ++i){ + double coord = in[i]; + if(coord >= mins[i] && coord < maxs[i]) + out[i] = coord; + else{ + out[i] = fmod((coord-_dimensions.origin(i)), _dimensions.extents(i)) + + (coord < _dimensions.origin(i) ? _dimensions.extents(i) : 0) + + _dimensions.origin(i); + if(out[i] >= maxs[i]) out[i] = nextafter(maxs[i], -DBL_MAX); + else if(out[i] < mins[i]) out[i] = nextafter(mins[i], DBL_MAX); + } + } + +} + +void WrapAroundBorders::translate(const std::vector& oldPos, std::vector& newPos, const std::vector< + double>& displacement) const { + + if (displacement.size() != oldPos.size() || displacement.size() != newPos.size()) + throw Repast_Error_15(oldPos, newPos, displacement); // Position and displacement vectors must be of the same size + + for (int i = 0, n = displacement.size(); i < n; ++i) newPos[i] = oldPos[i] + displacement[i]; + transform(newPos, newPos); +} + +void WrapAroundBorders::translate(const std::vector& oldPos, std::vector& newPos, + const std::vector& displacement) const { + + if (displacement.size() != oldPos.size() || displacement.size() != newPos.size()) + throw Repast_Error_16(oldPos, newPos, displacement); // Position and displacement vectors must be of the same size + + for (int i = 0, n = displacement.size(); i < n; ++i) newPos[i] = oldPos[i] + displacement[i]; + transform(newPos, newPos); +} + +} + diff --git a/libs/repast_hpc/GridComponents.h b/libs/repast_hpc/GridComponents.h new file mode 100644 index 0000000..deef655 --- /dev/null +++ b/libs/repast_hpc/GridComponents.h @@ -0,0 +1,182 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * GridComponents.h + * + * Created on: Jun 23, 2009 + * Author: nick + */ + +#ifndef GRIDCOMPONENTS_H_ +#define GRIDCOMPONENTS_H_ + +#include +#include + +#include "GridDimensions.h" +#include "Grid.h" +#include "Random.h" + +namespace repast { + +/** + * Base class for representations of border semantics + * (e.g. Strict, Sticky, etc.) + */ +class Borders { + +protected: + const GridDimensions _dimensions; + + void boundsCheck(const std::vector& pt) const; + void boundsCheck(const std::vector& pt) const; + +public: + Borders(GridDimensions d); + + void transform(const std::vector& in, std::vector& out) const; + void transform(const std::vector& in, std::vector& out) const; + + bool isPeriodic() const { + return false; + } +}; + +/** + * Implements strict grid border semantics: anything + * outside the dimensions is out of bounds. + */ +class StrictBorders : public Borders { + +public: + StrictBorders(GridDimensions d); + + void translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement) const; + void translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement) const; + +}; + +/** + * Implements sticky border semantics: translates out + * side of the border are clamped to the border coordinates. + * Tranforms outside the border throw an exception. + */ +class StickyBorders : public Borders { + +private: + std::vector mins, maxs; + + template + T calcCoord(T coord, int dimension) const; + +public: + + StickyBorders(GridDimensions d); + void translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement) const; + void translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement) const; +}; + +template +T StickyBorders::calcCoord(T coord, int dimension) const { + if (coord < mins[dimension]) return mins[dimension]; + else if (coord > maxs[dimension]) return maxs[dimension]; + else return coord; +} + +/** + * Implements periodic wrap around style border semantics. + * Points that are outside the borders are wrapped until + * the point is inside the borders. + * + */ +class WrapAroundBorders { + +private: + GridDimensions _dimensions; + std::vector mins, maxs; + +public: + + WrapAroundBorders(GridDimensions dimensions); + + void transform(const std::vector& in, std::vector& out) const; + void transform(const std::vector& in, std::vector& out) const; + void translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement) const; + void translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement) const; + + void init(const GridDimensions& dimensions); + + bool isPeriodic() const { + return true; + } + +}; + +/** + * Basic class for adding elements to grids. NOTE: This does NOT + * actually add the element to the grid; this simply returns 'true' + * and leaves the actual addition to the grid up to the user. + * Other classes may do other things (e.g. add to a random location) + * but this one does NOT. + */ +template +class SimpleAdder { +public: + + template + void init(GridDimensions dimensions, GridType* grid) {} + bool add(boost::shared_ptr agent) { + return true; + } +}; + +/* +template +class RandomAdder { +private: + std::vector gens; + +public: + + void init(GridDimensions dimensions); + bool add(boost::shared_ptr agent); +}; +*/ + + + + +} + +#endif /* GRIDCOMPONENTS_H_ */ diff --git a/libs/repast_hpc/GridDimensions.cpp b/libs/repast_hpc/GridDimensions.cpp new file mode 100644 index 0000000..0e8b427 --- /dev/null +++ b/libs/repast_hpc/GridDimensions.cpp @@ -0,0 +1,121 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * GridDimensions.cpp + * + * Created on: 20 June 2012 + * Author: nick + */ + +#include "Point.h" +#include "GridDimensions.h" +#include "RepastErrors.h" + +#include +#include +#include + +using namespace std; + +namespace repast { + +GridDimensions::GridDimensions() : + _extents(0), _origin(0) { +} + +GridDimensions::GridDimensions(Point extent) : + _extents(extent), _origin(Point(vector(extent.dimensionCount(), 0))) { +} + +GridDimensions::GridDimensions(Point origin, Point dimensions) : + _extents(dimensions), _origin(origin) { + if (_extents.dimensionCount() != _origin.dimensionCount()) { + throw Repast_Error_36 >(origin, dimensions, _origin.dimensionCount(), _extents.dimensionCount()); // Origin dimension count != dimensions dimension count + } +} + +bool GridDimensions::contains(const std::vector& pt) const { + if (pt.size() != _origin.dimensionCount()) + throw Repast_Error_37(pt, _origin.dimensionCount()); // Point dimension count != dimensions' dimension count + + + for (size_t i = 0; i < pt.size(); i++) { + double start = _origin.getCoordinate(i); + double end = start + _extents.getCoordinate(i); + int pVal = pt[i]; + if (pVal < start || pVal >= end) + return false; + } + return true; +} + +bool GridDimensions::contains(const Point& pt) const { + return contains(pt.coords()); +} + +bool GridDimensions::contains(const Point& pt) const { + return contains(pt.coords()); +} + +bool GridDimensions::contains(const std::vector& pt) const { + if (pt.size() != _origin.dimensionCount()) { + throw Repast_Error_38(pt, _origin.dimensionCount()); // Point dimension count != dimensions' dimension count + } + + for (size_t i = 0; i < pt.size(); i++) { + double start = _origin.getCoordinate(i); + double end = start + _extents.getCoordinate(i); + double pVal = pt[i]; + if (pVal < start || pVal >= end) + return false; + } + + return true; +} + + +bool operator==(const GridDimensions &one, const GridDimensions &two) { + return one._extents == two._extents && one._origin == two._origin; +} + +bool operator!=(const GridDimensions &one, const GridDimensions &two) { + return !(one == two); +} + +ostream& operator<<(ostream& os, const GridDimensions& dimensions) { + os << "GridDimensions(Origin:[" << dimensions._origin << "], Extent: [" << dimensions._extents << "])"; + return os; +} + +} diff --git a/libs/repast_hpc/GridDimensions.h b/libs/repast_hpc/GridDimensions.h new file mode 100644 index 0000000..ca3263b --- /dev/null +++ b/libs/repast_hpc/GridDimensions.h @@ -0,0 +1,115 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * GridDimensions.h + * + * Created on: 20 June 2012 + * Author: nick + */ + +#ifndef GRIDDIMENSIONS_H_ +#define GRIDDIMENSIONS_H_ + +#include + +#include "RepastErrors.h" +#include "Point.h" + +namespace repast { + +/** + * Basic structure for specifying grid dimenions. Structure is + * to specify (using instances of Point) the origin and the + * extent, so that an origin of (-100, -100) and an extent + * of (200, 200) represents a rectangle with corners at + * (-100, -100), (-100, 100), (100, 100), and (100, -100). + */ +class GridDimensions { + +private: + friend bool operator==(const GridDimensions &one, const GridDimensions &two); + friend std::ostream& operator<<(std::ostream& os, const GridDimensions& dimensions); + Point _extents, _origin; + +public: + GridDimensions(); + explicit GridDimensions(Point extent); + + /** + * Creates a GridDimensions with the specified origin and extent. + */ + GridDimensions(Point origin, Point extent); + + bool contains(const Point& pt) const; + bool contains(const std::vector& pt) const; + + bool contains(const Point& pt) const; + bool contains(const std::vector& pt) const; + + /** + * Gets the origin. + */ + const Point& origin() const { + return _origin; + } + + /** + * Gets the extents along each dimension. + */ + const Point& extents() const { + return _extents; + } + + const double& origin(int index) const { + return _origin[index]; + } + + const double& extents(int index) const { + return _extents[index]; + } + + size_t dimensionCount() const { + return _extents.dimensionCount(); + } + +}; + +bool operator==(const GridDimensions &one, const GridDimensions &two); +bool operator!=(const GridDimensions &one, const GridDimensions &two); +std::ostream& operator<<(std::ostream& os, const GridDimensions& dimensions); + +} + +#endif /* GRIDDIMENSIONS_H_ */ + diff --git a/libs/repast_hpc/Moore2DGridQuery.h b/libs/repast_hpc/Moore2DGridQuery.h new file mode 100644 index 0000000..283b50a --- /dev/null +++ b/libs/repast_hpc/Moore2DGridQuery.h @@ -0,0 +1,127 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Moore2DGridQuery.h + * + * Created on: Aug 12, 2010 + * Author: nick + */ + +#ifndef MOORE2DGRIDQUERY_H_ +#define MOORE2DGRIDQUERY_H_ + +#include "Grid2DQuery.h" +#include "Point.h" +#include "Grid.h" + +namespace repast { + +/** + * Neighborhood query that gathers neighbors in a Moore (N, S, E, W, NE, etc.) + * neighborhood. + * + * @tparam T the type of agents in the Grid + * + */ +template +class Moore2DGridQuery: public Grid2DQuery { + +public: + + /** + * Creates Moore2DGridQuery that will query the specified Grid. + */ + Moore2DGridQuery(const Grid* grid); + virtual ~Moore2DGridQuery() { + } + + /** + * Queries the Grid for the Moore neighbors surrounding the center point within a specified range. + * + * @param center the center of the neighborhood + * @param range the range of the neighborhood out from the center + * @param includeCenter whether or not to include any agents at the center + * @param [out] the neighboring agents will be returned in this vector + */ + virtual void query(const Point& center, int range, bool includeCenter, std::vector& out) const; +}; + +template +Moore2DGridQuery::Moore2DGridQuery(const Grid* grid) : + Grid2DQuery (grid) { +} + +template +void Moore2DGridQuery::query(const Point& center, int range, bool includeCenter, std::vector& out) const { + + int xMin = center[0] - range; + int yMin = center[1] - range; + int xMax = center[0] + range + 1; + int yMax = center[1] + range + 1; + + if (!Grid2DQuery::_grid->isPeriodic()) { + if (xMin < Grid2DQuery::minMax[0][0]) + xMin = Grid2DQuery::minMax[0][0]; + + if (yMin < Grid2DQuery::minMax[1][0]) + yMin = Grid2DQuery::minMax[1][0]; + + if (xMax >= Grid2DQuery::minMax[0][1]) + xMax = Grid2DQuery::minMax[0][1]; + + if (yMax >= Grid2DQuery::minMax[1][1]) + yMax = Grid2DQuery::minMax[1][1]; + } + + if (includeCenter) { + for (int x = xMin; x < xMax; x++) { + for (int y = yMin; y < yMax; y++) { + Grid2DQuery::_grid->getObjectsAt(Point (x, y), out); + } + } + } else { + for (int x = xMin; x < xMax; x++) { + for (int y = yMin; y < yMax; y++) { + if (!(x == center[0] && y == center[1])) { + Grid2DQuery::_grid->getObjectsAt(Point (x, y), out); + } + } + } + } + +} + +} + +#endif /* MOORE2DGRIDQUERY_H_ */ diff --git a/libs/repast_hpc/MultipleOccupancy.h b/libs/repast_hpc/MultipleOccupancy.h new file mode 100644 index 0000000..ea52a27 --- /dev/null +++ b/libs/repast_hpc/MultipleOccupancy.h @@ -0,0 +1,195 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * MultipleOccupancy.h + * + * Created on: Aug 11, 2010 + * Author: nick + */ + +#ifndef MULTIPLEOCCUPANCY_H_ +#define MULTIPLEOCCUPANCY_H_ + +#include +#include + +#include "Point.h" + +namespace repast { + + +/** + * Unary function that allows retrieving the occupants of locations. + */ +template +struct ExtractPtrs: public std::unary_function< + typename boost::unordered_map >::value_type, T*> { + T* operator()(typename boost::unordered_map >::value_type& val) { + return val.second.get(); + } +}; + +/** + * Multiple Occupancy cell accessor for accessing the occupants of locations + * in a Grid. Each locations can have multiple occupants. + * + * @param T the type of object in the Grid + * @param GPType the coordinate type of the grid point locations. This must + * be an int or a double. + */ +template +class MultipleOccupancy { + +private: + typedef typename boost::unordered_map, HashId> ValueType; + typedef typename ValueType::iterator ValueTypeIter; + typedef typename boost::unordered_map , ValueType*, HashGridPoint > LocationMap; + + typedef typename LocationMap::iterator LocationMapIter; + typedef typename LocationMap::const_iterator LocationMapConstIter; + + LocationMap locations; + + ValueType* doGet(const Point& location) const; + +public: + + virtual ~MultipleOccupancy(); + + /** + * Gets the first object found at the specified location. + * + * @param location the location to get the object at + * @return the first object found at the specified location or 0 if there + * are no objects at the specified location. + */ + T* get(const Point& location) const; + + /** + * Gets all the items found at the specified location. + * + * @param location the location to get the items at + * @param [out] the found items will be returned in this vector + */ + void getAll(const Point& location, std::vector& out) const; + + /** + * Puts the specified item at the specified location. + * + * @param agent the item to put + * @param location the location to put the item at + */ + bool put(boost::shared_ptr& agent, const Point& location); + + /** + * Removes the specified item from the specified location. + * + * @param agent the item to remove + * @param location the location to remove the item from + */ + void remove(boost::shared_ptr& agent, const Point& location); + +}; + +template +MultipleOccupancy::~MultipleOccupancy() { + for (LocationMapIter iter = locations.begin(); iter != locations.end(); ++iter) { + delete iter->second; + } +} + +template +typename MultipleOccupancy::ValueType* MultipleOccupancy::doGet(const Point& location) const { + LocationMapConstIter iter = locations.find(location); + if (iter == locations.end()) + return NULL; + return iter->second; +} + +template +T* MultipleOccupancy::get(const Point& location) const { + ValueType* ptrs = doGet(location); + if (ptrs == NULL) + return NULL; + return ptrs->begin()->second.get(); +} + +template +void MultipleOccupancy::getAll(const Point& location, std::vector& out) const { + ValueType* ptrs = doGet(location); + if (ptrs != NULL) { + int index = out.size(); + out.resize(out.size() + ptrs->size(), 0); + ExtractPtrs func; + std::transform(ptrs->begin(), ptrs->end(), out.begin() + index, func); + } +} + +template +bool MultipleOccupancy::put(boost::shared_ptr& agent, const Point& location) { + LocationMapIter iter = locations.find(location); + ValueType* vec; + if (iter == locations.end()) { + vec = new ValueType(); + locations[location] = vec; + } else { + vec = iter->second; + } + vec->insert(std::make_pair(agent->getId(), agent)); + + return true; +} + +template +void MultipleOccupancy::remove(boost::shared_ptr& agent, const Point& location) { + LocationMapIter iter = locations.find(location); + if (iter != locations.end()) { + ValueType* vec = iter->second; + ValueTypeIter agentIter = vec->find(agent->getId()); + + if (agentIter != vec->end()) { + vec->erase(agentIter); + if (vec->size() == 0) { + delete vec; + locations.erase(iter); + } + + } + + } +} + +} + +#endif /* MULTIPLEOCCUPANCY_H_ */ diff --git a/libs/repast_hpc/NCDataSet.cpp b/libs/repast_hpc/NCDataSet.cpp new file mode 100644 index 0000000..82b4441 --- /dev/null +++ b/libs/repast_hpc/NCDataSet.cpp @@ -0,0 +1,132 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DataSet.cpp + * + * Created on: Jun 8, 2009 + * Author: nick + */ + +#include + +#include "NCDataSet.h" +#include "Utilities.h" +#include "io.h" + +#include +#include + +using namespace std; + +namespace fs = boost::filesystem; + +namespace repast { + +NCDataSet::NCDataSet(std::string file, const Schedule& schedule) : + schedule_(&schedule), start(0), open(true) { + std::string filename = file; + rank = RepastProcess::instance()->rank(); + if (rank == 0) { + fs::path filepath(file); + if (!fs::exists(filepath.parent_path())) fs::create_directories(filepath.parent_path()); + int i = 1; + std::string stem = filepath.stem().string(); + while(fs::exists(filepath)){ // This will increment i until it hits a unique name + i++; + std::stringstream ss; + ss << stem << "_" << i << filepath.extension().string(); + fs::path newName(filepath.parent_path() / ss.str()); + filepath = newName; + } + filename = filepath.string(); + } + file_ = filename; +} + +NCDataSet::~NCDataSet() { + close(); +} + +void NCDataSet::close() { + if (open) { + for (size_t i = 0, n = dataSources.size(); i < n; i++) { + NCDataSource* ds = dataSources[i]; + delete ds; + } + if (rank == 0) { + ncfile->close(); + delete ncfile; + } + open = false; + } +} + +// DataSet implementation +void NCDataSet::record() { + //Timer timer; + //timer.start(); + if (rank == 0) { + ticks.push_back(schedule_->getCurrentTick()); + } + for (size_t i = 0; i < dataSources.size(); i++) { + dataSources[i]->record(); + } + //Log4CL::instance()->get_logger("root").log(INFO, "dataset record, time: " + boost::lexical_cast(timer.stop())); +} + +void NCDataSet::write() { + //Timer timer; + //timer.start(); + if (rank == 0) { + NcVar* tickVar = ncfile->get_var("tick"); + tickVar->set_cur(start); + tickVar->put(&ticks[0], ticks.size()); + start += ticks.size(); + + ticks.clear(); + } + + for (size_t i = 0; i < dataSources.size(); i++) { + NCDataSource * ds = dataSources[i]; + NcVar* var = 0; + if (rank == 0) + var = ncfile->get_var(ds->name().c_str()); + ds->write(var); + } + + //Log4CL::instance()->get_logger("root").log(INFO, "dataset write, time: " + boost::lexical_cast(timer.stop())); +} + +} + diff --git a/libs/repast_hpc/NCDataSet.h b/libs/repast_hpc/NCDataSet.h new file mode 100644 index 0000000..b6babd9 --- /dev/null +++ b/libs/repast_hpc/NCDataSet.h @@ -0,0 +1,137 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DataSet.h + * + * Created on: Jun 8, 2009 + * Author: nick + */ + +#ifndef NCDATASET_H +#define NCDATASET_H + +#include +#include +#include + +#include "Schedule.h" +#include "RepastProcess.h" +#include "TDataSource.h" +#include "NCReducibleDataSource.h" +#include "DataSet.h" + +namespace repast { + +class NCDataSetBuilder; + +/** + * Provides data recording and writing into a single file in NetCDF + * format. A NCDataSet uses rank 0 to + * write to a single file from multiple pan-process data sources. A NCDataSet + * should be built using a NCDataSetBuilder. + */ +class NCDataSet: public DataSet { + + friend class NCDataSetBuilder; + +private: + std::vector dataSources; + std::vector ticks; + std::string file_; + const Schedule* schedule_; + int rank, start; + bool open; + + NcFile* ncfile; + + // private so can only be created using an NCDataSetBuilder + NCDataSet(std::string file, const Schedule& schedule); + +public: + + virtual ~NCDataSet(); + + // doc inherited from DataSet + void record(); + + // doc inherited from DataSet + void write(); + + // doc inherited from DataSet + void close(); +}; + +/** + * Creates an NCDataSource with the specified name that will retreive int data from the + * specified TDataSource, and perform the specified reduction Op on it. This function + * is used to add data sources to an NCDataSetBuilder prior to creating an NCDataSet + * from it. + * + * @param name the name of the data source. This will be the name of the variable for which + * data is collected from the TDataSource. + * @param intDataSource the actual source of the data that will be recorded. + * @param op the reduction operation to perform on the recorded data when combining + * the data across processes. + * + * @tparam Op an associative binary function or function object that work with ints. For example, + * std::plus, or mpi::minimum and so on. + */ +template +NCDataSource* createNCDataSource(std::string name, TDataSource* intDataSource, Op op) { + return new NCReducibleDataSource (name, intDataSource, op); +} + +/** + * Creates an NCDataSource with the specified name that will retreive double data from the + * specified TDataSource, and perform the specified reduction Op on it. This function + * is used to add data sources to an NCDataSetBuilder prior to creating an NCDataSet + * from it. + * + * @param name the name of the data source. This will be the name of the variable for which + * data is collected from the TDataSource. + * @param doubleDataSource the actual source of the data that will be recorded. + * @param op the reduction operation to perform on the recorded data when combining + * the data across processes. + * + * @tparam Op an associative binary function or function object that work with doubles. For example, + * std::plus, or mpi::minimum and so on. + */ +template +NCDataSource* createNCDataSource(std::string name, TDataSource* doubleDataSource, Op op) { + return new NCReducibleDataSource (name, doubleDataSource, op); +} + +} + +#endif /* DATASET_H_ */ diff --git a/libs/repast_hpc/NCDataSetBuilder.cpp b/libs/repast_hpc/NCDataSetBuilder.cpp new file mode 100644 index 0000000..7e89942 --- /dev/null +++ b/libs/repast_hpc/NCDataSetBuilder.cpp @@ -0,0 +1,79 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * NCDataSetBuilder.cpp + * + * Created on: Oct 14, 2010 + * Author: nick + */ + +#include "NCDataSetBuilder.h" + +namespace repast { + +NCDataSetBuilder::NCDataSetBuilder(std::string file, const Schedule& schedule) { + dataSet = new NCDataSet(file, schedule); +} + +NCDataSetBuilder::~NCDataSetBuilder() { + if (!returned) + delete dataSet; +} + +NCDataSetBuilder& NCDataSetBuilder::addDataSource(NCDataSource* source) { + dataSet->dataSources.push_back(source); + return *this; +} + +NCDataSet* NCDataSetBuilder::createDataSet() { + returned = true; + if (RepastProcess::instance()->rank() == 0) { + + NcFile* ncfile = new NcFile(dataSet->file_.c_str(), NcFile::Replace, NULL, 0, NcFile::Offset64Bits); + NcDim* runDim = ncfile->add_dim("run", 1); + NcDim* tickDim = ncfile->add_dim("tick"); + + ncfile->add_var("tick", ncDouble, tickDim); + + for (size_t i = 0; i < dataSet->dataSources.size(); i++) { + NCDataSource* ds = dataSet->dataSources[i]; + ncfile->add_var(ds->name().c_str(), ds->ncType(), tickDim, runDim); + } + dataSet->ncfile = ncfile; + } + + return dataSet; +} + +} diff --git a/libs/repast_hpc/NCDataSetBuilder.h b/libs/repast_hpc/NCDataSetBuilder.h new file mode 100644 index 0000000..b4101b9 --- /dev/null +++ b/libs/repast_hpc/NCDataSetBuilder.h @@ -0,0 +1,101 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * NCDataSetBuilder.h + * + * Created on: Oct 14, 2010 + * Author: nick + */ + +#ifndef NCDATASETBUILDER_H_ +#define NCDATASETBUILDER_H_ + +#include "NCDataSet.h" +#include "Schedule.h" + +namespace repast { + +/** + * Used to build NCDataSets to record data in NetCDF format. Steps for use + * are: + *

    + *
  1. Create a NCDataSetBuilder. + *
  2. Add NCDataSources to the builder using the createNCDataSource functions. Each + * DataSource defines a variable and where the data for that variable will be retrieved. + * Recording data on the NCDataSet produced by the builder will record this data for + * each variable. + *
  3. Call createDataSet to create the NCDataSet. + *
  4. Schedule calls to record and write on the NCDataSet. + *
+ */ +class NCDataSetBuilder { + +private: + NCDataSet* dataSet; + bool returned; + +public: + /** + * Creates an NCDataSetBuilder that will write to the specified file and + * get its tick counts from the specified schedule. + * + * @param file the name of the file to write to. Only rank 0 will + * actually write to this file. + * @param schedule the schedule to get tick counts from + */ + NCDataSetBuilder(std::string file, const Schedule& schedule); + ~NCDataSetBuilder(); + + /** + * Adds a NCDataSource to this NCDataSetBuilder. The added NCDataSource defines + * a variable and where the data for that variable will be retrieved. + * Recording data on the NCDataSet produced by this builder will record this data for + * each variable. + */ + NCDataSetBuilder& addDataSource(NCDataSource* source); + + /** + * Creates the NCDataSet defined by this NCDataSetBuilder. + * The caller is responsible for properly deleting the + * returned pointer. + * + * @return the created NCDataSet. + */ + NCDataSet* createDataSet(); + +}; + +} + +#endif /* NCDATASETBUILDER_H_ */ diff --git a/libs/repast_hpc/NCDataSource.h b/libs/repast_hpc/NCDataSource.h new file mode 100644 index 0000000..32a0026 --- /dev/null +++ b/libs/repast_hpc/NCDataSource.h @@ -0,0 +1,94 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * NCDataSource.h + * + * Created on: Oct 14, 2010 + * Author: nick + */ + +#ifndef NCDATASOURCE_H_ +#define NCDATASOURCE_H_ + +namespace repast { + +/** + * Data source used internally by NCDataSets. + */ +class NCDataSource { + +protected: + std::string _name; + +public: + NCDataSource(std::string name) : _name(name) {} + virtual ~NCDataSource() {}; + virtual void record() = 0; + virtual void write(NcVar* var) = 0; + + virtual NcType ncType() = 0; + + const std::string name() const { + return _name; + } +}; + +/** + * Base class for specialized int and double NcType classes + */ +template +struct NcTypeTrait; + +/** + * Used for converting to NetCDF Data, double type + */ +template<> +struct NcTypeTrait { + const static NcType type = ncDouble; +}; + +/** + * Used for converting to NetCDF Data, int type + */ +template<> +struct NcTypeTrait { + const static NcType type = ncInt; +}; + + + +} + + +#endif /* NCDATASOURCE_H_ */ diff --git a/libs/repast_hpc/NCReducibleDataSource.h b/libs/repast_hpc/NCReducibleDataSource.h new file mode 100644 index 0000000..aed174d --- /dev/null +++ b/libs/repast_hpc/NCReducibleDataSource.h @@ -0,0 +1,125 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * NCReducibleDataSource.h + * + * Created on: Oct 14, 2010 + * Author: nick + */ + +#ifndef NCREDUCIBLEDATASOURCE_H_ +#define NCREDUCIBLEDATASOURCE_H_ + +#include "NCDataSource.h" +#include "RepastProcess.h" +#include "TDataSource.h" + +#include +#include +#include + +namespace repast { + +/** + * Source of data and a reduction operation. Used internally by a NCDataSet to + * store the data sources. their associated ops etc. + */ +template +class NCReducibleDataSource : public NCDataSource { + +protected: + Op op_; + std::vector data; + TDataSource* dataSource_; + int rank, start; + +public: + NCReducibleDataSource(std::string name, TDataSource* dataSource, Op op); + ~NCReducibleDataSource(); + + virtual NcType ncType(); + + virtual void record(); + virtual void write(NcVar* var); + +}; + +template +NCReducibleDataSource::NCReducibleDataSource(std::string name, TDataSource* dataSource, Op op) : NCDataSource(name), op_(op), +dataSource_(dataSource), start(0) { + rank = RepastProcess::instance()->rank(); +}; + +template +NCReducibleDataSource::~NCReducibleDataSource() { + delete dataSource_; +} +template +NcType NCReducibleDataSource::ncType() { + return NcTypeTrait::type; +} + +template +void NCReducibleDataSource::record() { + data.push_back(dataSource_->getData()); +} + +template +void NCReducibleDataSource::write(NcVar* var) { + boost::mpi::communicator* comm = RepastProcess::instance()->getCommunicator(); + if (rank == 0) { + size_t size = data.size(); + T* results = new T[size]; + reduce(*comm, &data[0], size, results, op_, 0); + + var->set_cur(start, 0); + // writing results along the tick dimension + // and run dimension -- each result is indexed by the + // the tick values of the tick dimension and the single run dimension + var->put(results, size, 1); + start += size; + + delete[] results; + } else { + reduce(*comm, &data[0], data.size(), op_, 0); + } + data.clear(); +} + + + +} + + +#endif /* NCREDUCIBLEDATASOURCE_H_ */ diff --git a/libs/repast_hpc/NetworkBuilder.cpp b/libs/repast_hpc/NetworkBuilder.cpp new file mode 100644 index 0000000..024d31c --- /dev/null +++ b/libs/repast_hpc/NetworkBuilder.cpp @@ -0,0 +1,51 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * NetworkBuilder.cpp + * + * Created on: Nov 30, 2009 + * Author: nick + */ + +#include "NetworkBuilder.h" + +namespace repast { + +ProbItem::ProbItem(int i, double lb, double ub) : _index(i), lowerBound(lb), upperBound(ub) {} + +bool ProbItem::contains(double val) { + return lowerBound <= val && val < upperBound; +} + +} diff --git a/libs/repast_hpc/NetworkBuilder.h b/libs/repast_hpc/NetworkBuilder.h new file mode 100644 index 0000000..3f8a6a9 --- /dev/null +++ b/libs/repast_hpc/NetworkBuilder.h @@ -0,0 +1,149 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * NetworkBuilder.h + * + * Created on: Oct 30, 2009 + * Author: nick + */ + +#ifndef NETWORKBUILDER_H_ +#define NETWORKBUILDER_H_ + +#include "Graph.h" +#include "Properties.h" +#include "Utilities.h" +#include "Random.h" + +#include + +namespace repast { + +/** + * Helper class for calculating outcomes based on + * a set of probabilities that sum to 1. + */ +class ProbItem { +private: + int _index; + double lowerBound, upperBound; + +public: + ProbItem(int i, double lb, double ub); + bool contains(double val); + + int index() const { + return _index; + } +}; + +/** + * Buils KE type networks. See Klemm and Eguiluz, + * "Growing scale-free network with small world behavior" in + * Phys. Rev. E 65. + */ +template +class KEBuilder { + +private: + static const std::string M; + +public: + /** + * Builds the network. The graph should contains the vertices the build the + * network with and props should contain the M values. + * + * @param props a Properties containing a property "ke.model.m" that specifies + * the M value. + * @param graph the graph to build the network + */ + void build(repast::Properties& props, repast::Graph* graph); +}; + +template +const std::string KEBuilder::M = "ke.model.m"; + +template +void KEBuilder::build(repast::Properties& props, repast::Graph* graph) { + int m = strToInt(props.getProperty(M)); + typename repast::Graph::vertex_iterator iter; + int k = 0; + // advance iter m - 1 number of elements. + for (iter = graph->verticesBegin(); k < m; ++k, ++iter); + std::vector activeNodes(graph->verticesBegin(), iter); + // fully connect all the active nodes + for (int i = 0; i < m; i++) { + V* source = activeNodes[i]; + for (int j = 0; j < m; j++) { + if (i != j) { + graph->addEdge(source, (activeNodes[j])); + } + } + } + // add the remaining nodes -- iter through verticesEnd() + while (iter != graph->verticesEnd()) { + V* source = *iter; + double sum = 0; + // make an edge between iter and all the active nodes + for (int i = 0, n = activeNodes.size(); i < n; i++) { + V* target = activeNodes[i]; + graph->addEdge(source, target); + sum += 1.0 / graph->inDegree(target); + } + + std::vector probItems; + double lowerBound = 0; + for (int i = 0, n = activeNodes.size(); i < n; i++) { + V* node = activeNodes[i]; + double upperBound = lowerBound + (1.0 / graph->inDegree(node) / sum); + probItems.push_back(ProbItem(i, lowerBound, upperBound)); + lowerBound = upperBound; + } + + double p = repast::Random::instance()->nextDouble(); + for (int i = 0, n = probItems.size(); i < n; i++) { + ProbItem& item = probItems[i]; + if (item.contains(p)) { + activeNodes.erase(activeNodes.begin() + item.index()); + break; + } + } + activeNodes.push_back(source); + ++iter; + } +} + +} + +#endif /* NETWORKBUILDER_H_ */ diff --git a/libs/repast_hpc/Point.h b/libs/repast_hpc/Point.h new file mode 100644 index 0000000..0421d7c --- /dev/null +++ b/libs/repast_hpc/Point.h @@ -0,0 +1,368 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Point.h + * + * Created on: Apr 1, 2010 + * Author: nick + */ + +#ifndef POINT_H_ +#define POINT_H_ + +#include +#include +#include +#include +#include + +#include +#include + +#include "RepastErrors.h" + +namespace repast { + +/** + * A N-dimensional Point representation. + */ +template +class Point; + +/** + * Class that allows retrieval of hash value for Point objects. + */ +template +struct HashGridPoint { + std::size_t operator()(const Point& pt) const { + return pt.hash; + } +}; + +template +bool operator==(const Point &one, const Point &two); + +template +std::ostream& operator<<(std::ostream& os, const Point& pt); + +/** + * N dimensional point for addressing matrix locations. + * + * @param T a numeric type. In repast and relogo these are limited to int + * and double. + */ +template +class Point { + +private: + friend bool operator==<> (const Point &one, const Point &two); + friend std::ostream& operator<<<> (std::ostream& os, const Point& pt); + friend struct HashGridPoint ; + + void calcHash(); + + std::vector point; + size_t hash; + + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { + ar & point; + ar & hash; + } + +public: + + typedef typename std::vector::const_iterator const_iterator; + + /** + * Creates a one dimensional point with the specified value. + * + * @param x the x coordinate of the point + */ + explicit Point(T x); + + /** + * Creates a two dimensional point with the specified values. + * + * @param x the x coordinate of the point + * @param y the y coordinate of the point + */ + Point(T x, T y); + + /** + * Creates a three dimensional point with the specified values. + * + * @param x the x coordinate of the point + * @param y the y coordinate of the point + * @param z the z coordinate of the point + */ + Point(T x, T y, T z); + + /** + * Creates a multi-dimensional point with the specified values. + * + * @param coordinates the coordinate values of the point. The first + * element will be x, the second y and so on. + */ + Point(std::vector coordinates); + + /** + * Gets the x coordinate of the point. + * + * @return the x coordinate of the point. + */ + T getX() const; + + /** + * Gets the y coordinate of the point. + * + * @return the y coordinate of the point. + * + * @throws an out_of_range exception if this GridPoint has less than 2 + * dimensions. + */ + T getY() const; + + /** + * Gets the z coordinate of the point. + * + * @return the z coordinate of the point. + * + * @throws an out_of_range exception if this GridPoint has less than 3 + * dimensions. + */ + T getZ() const; + + /** + * Gets the coodinate of the point in the specified dimension. + * + * @param coordIndex the dimension of the point to get the coordinate for. + * X is the first, y is the second and so on. + * + * @return the coordinate of the point in the specified dimension. + * + * @throws an out_of_range exception if this GridPoint has doesn't + * have the specified dimension. + */ + T getCoordinate(int coordIndex) const; + + /** + * Adds the specified GridPoint to this GridPoint. This GridPoint + * contains the result. + * + * @throws invalid_argument exception if the pt doesn't have the same + * number of dimensions as this GridPoint. + */ + void add(const Point &pt); + + /** + * Gets the number of dimensions of this point. + * + * @return the number of dimensions of this point. + */ + size_t dimensionCount() const { + return point.size(); + } + + /** + * Gets the coordinate value at the specified index. + * + * @param index the dimension of the point to get the coordinate for. + * X is the first, y is the second and so on. + * + * @return the coordinate of the point in the specified dimension. + */ + const T& operator[](size_t index) const { + return point[index]; + } + + /** + * Gets the coordinate value at the specified index. + * + * @param index the dimension of the point to get the coordinate for. + * X is the first, y is the second and so on. + * + * @return the coordinate of the point in the specified dimension. + */ + T& operator[](size_t index) { + return point[index]; + } + + /** + * Gets the coordinates of this point as a vector. + * + * @return a vector containing the coordinates of this point. + */ + const std::vector& coords() const { + return point; + } + + /** + * Gets the start of an iterator over the coordinates of this point. + * + * @return the start of an iterator over the coordinates of this point. + */ + const_iterator begin() const { + return point.begin(); + } + + /** + * Gets the end of an iterator over the coordinates of this point. + * + * @return the end of an iterator over the coordinates of this point. + */ + const_iterator end() const { + return point.end(); + } + + /** + * Copies the point into the specified vector. Assumes the + * array is the same length as this GridPoint. + * + * @param [out] the vector to copy the point coordinates into + */ + void copy(std::vector& out) const; +}; + +template +bool operator==(const Point &one, const Point &two); +template +bool operator!=(const Point &one, const Point &two); +template +std::ostream& operator<<(std::ostream& os, const Point& pt); + +template +Point::Point(T x) { + point.push_back(x); + calcHash(); +} + +template +Point::Point(T x, T y) { + point.push_back(x); + point.push_back(y); + calcHash(); +} + +template +Point::Point(T x, T y, T z) { + point.push_back(x); + point.push_back(y); + point.push_back(z); + calcHash(); +} + +template +Point::Point(std::vector coordinates) : + point(coordinates.size(), 0) { + std::copy(coordinates.begin(), coordinates.end(), point.begin()); + calcHash(); +} + +template +void Point::calcHash() { + hash = 17; + boost::hash hasher; + for (size_t i = 0; i < point.size(); i++) { + hash = 37 * hash + hasher(point[i]); + } +} + +template +T Point::getX() const { + return point.at(0); +} + +template +T Point::getY() const { + return point.at(1); +} + +template +T Point::getZ() const { + return point.at(2); +} + +template +T Point::getCoordinate(int coordIndex) const { + return point.at(coordIndex); +} + +template +void Point::copy(std::vector& out) const { + std::copy(point.begin(), point.end(), out.begin()); +} + +template +bool operator==(const Point &one, const Point &two) { + return one.point == two.point; +} + +template +bool operator!=(const Point &one, const Point &two) { + return !(one == two); +} + +template +void Point::add(const Point &pt) { + if (pt.dimensionCount() != dimensionCount()) throw Repast_Error_35 >(*this, pt); // Points do not have same number of dimensions + + for (size_t i = 0; i < point.size(); i++) { + point[i] += pt.getCoordinate(i); + } +} + +template +std::ostream& operator<<(std::ostream& os, const Point& pt) { + os << "Point["; + for (size_t i = 0; i < pt.point.size(); i++) { + if (i > 0) + os << ", "; + os << pt.point[i]; + } + os << "]"; + return os; +} + +template +bool operator<(const Point& one, const Point& two) { + return std::lexicographical_compare(one.begin(), one.end(), two.begin(), two.end()); +} + +} + +#endif /* POINT_H_ */ + diff --git a/libs/repast_hpc/Projection.h b/libs/repast_hpc/Projection.h new file mode 100644 index 0000000..2fe1364 --- /dev/null +++ b/libs/repast_hpc/Projection.h @@ -0,0 +1,322 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Projection.h + * + * Created on: Aug 5, 2010 + * Author: nick + */ + +#ifndef PROJECTION_H_ +#define PROJECTION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AgentId.h" + +namespace repast { + + +/** + * Serializable packet that can contain projection + * information regardless of the type of projection + * (network or spatial). + */ +class ProjectionInfoPacket{ + friend class boost::serialization::access; + +public: + ProjectionInfoPacket(){} // For serialization + ProjectionInfoPacket(AgentId agentId): id(agentId){} + virtual ~ProjectionInfoPacket(){} + + template + void serialize(Archive& ar, const unsigned int version) { + ar & id; + } + + AgentId id; + + virtual bool isEmpty(){ return false; } + +}; + +BOOST_SERIALIZATION_ASSUME_ABSTRACT(ProjectionInfoPacket); + +/** + * Serializable packet that can contain projection information + * of a specific kind using the template parameter. + */ +template +class SpecializedProjectionInfoPacket: public ProjectionInfoPacket{ + friend class boost::serialization::access; + +public: + + SpecializedProjectionInfoPacket(){} // For serialization + SpecializedProjectionInfoPacket(AgentId agentId): ProjectionInfoPacket(agentId){} + SpecializedProjectionInfoPacket(AgentId agentId, std::vector projectionData): ProjectionInfoPacket(agentId){ + data.assign(projectionData.begin(), projectionData.end()); + } + SpecializedProjectionInfoPacket(AgentId agentId, std::set projectionData): ProjectionInfoPacket(agentId){ + data.assign(projectionData.begin(), projectionData.end()); + } + + ~SpecializedProjectionInfoPacket(){} + + template + void serialize(Archive& ar, const unsigned int version) { + ar & boost::serialization::base_object(*this); + ar & data; + } + + std::vector data; + + virtual bool isEmpty(){ return (data.size() == 0); } + +}; + +template +class Context; + +/** + * Abstract base class for all Projections. + */ +template +class Projection : public boost::noncopyable { + + friend class Context ; + +protected: + + std::string name_; + virtual bool addAgent(boost::shared_ptr agent) = 0; + virtual void removeAgent(T* agent) = 0; + + + // Beta (Protected) + std::set filter; + + + virtual ProjectionInfoPacket* getProjectionInfo(AgentId id, bool secondaryInfo = false, std::set* secondaryIds = 0, int destProc = -1 ) = 0; + + virtual void updateProjectionInfo(ProjectionInfoPacket* pip, Context* context) = 0; + +public: + + enum RADIUS{ PRIMARY, SECONDARY }; + + /** + * Creates a projection with specified name. + * + * @param name the name of the projection. This must be unique + * across projections + */ + Projection(std::string name) : + name_(name) { + } + + virtual ~Projection() { + } + + /** + * Gets the name of this projection. + */ + const std::string name() const { + return name_; + } + + // Beta (public) + + /** + * Adds an entry to the list of agent types that can be added to this projection. + * + * Note: no indication if type is already listed + * + * @param type type to be added + */ + void addFilterVal(int type){ + filter.insert(type); + } + + /** + * Removes an entry from the list of agent types that can be added to this projection. + * + * Note: no indication if type is not listed + * + * @param type entry to be removed + */ + void removeFilterVal(int type){ + filter.erase(type); + } + + /** + * Clears the list of agent types that can be added to this projection; the result + * is that the filter is empty, and any agent can be added. + */ + void clearFilter(){ + filter.clear(); + } + + /** + * Returns true if the agent can be added to the projection, which will + * be the case if the filter list is empty or if the agent's type is in the + * filter list. + * + * @param agent pointer to the agent to be tested + */ + bool agentCanBeAdded(boost::shared_ptr agent){ + return ( (filter.size() == 0) || + (filter.find(agent->getId().agentType()) != filter.end())); + } + + /** + * Should return true if the Projection implemented can 'keep' some (non-local) + * agents during a projection information synchronization operation. Generally + * spaces will allow all non-local agents to be deleted, but graphs keep the + * non-local agents that participate in Master edges. + * + * It is possible to override these. A graph projection can be created that does + * not permit non-local agents to be 'kept'. This would be an extremely unusual + * use case, but it is possible. + * + * Note that these are used for optimization. If no projection in a given context + * keeps any agents, several steps in the synchronization algorithm can be omitted. + * Of course, omitting these steps when a projection actually retains agents can + * caused undefined problems. + * + * @return true if this projection will keep non-local agents during a projection + * information synchronziation event, false if it will not. + */ + virtual bool keepsAgentsOnSyncProj() = 0; + + /** + * Should return true if the Projection implemented will send secondary agents during + * a status exchange. Generally spaces do not and graphs do. + * + * If no secondary agents will be sent, portions of the algorithm can be omitted for + * optimization. + * + * @return true if the Projection returns secondary agents, false if not + */ + virtual bool sendsSecondaryAgentsOnStatusExchange() = 0; + + /** + * Gets the set of processes with which this Projection exchanges projection info. + * In the most general case this will be all other processors; this is the case + * for graphs, where agent connections can be arbitrary. However, spaces usually + * exchange information only with a small subset of 'neighbor' processes, which + * is knowable in advance and constant. To accommodate the general case, the + * algorithm for exchanging information must poll all other processes to see which + * are sending to this one; if this is known in advance, this additional (expensive) + * step can be skipped. + */ + virtual void getInfoExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom) = 0; + + /** + * Gets the set of processes with which this Projection exchanges agent status info- + * that is, the set of processes from which agents can move to this one or to which + * they can move when moving from this one. In the most general case this will + * be all other processors. However, simulations where agents move in spaces will + * usually exchange agents only with a small subset of 'neighbor' processes, which + * is knowable in advance and constant. To accommodate the general case, the + * algorithm for exchanging information must poll all other processes to see which + * are sending to this one; if this is known in advance, this additional (expensive) + * step can be skipped. + */ + virtual void getAgentStatusExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom) = 0; + + /** + * Given a set of agents to test, gets the subset that must be kept in order to fulfill the projection's + * 'contract' to the specified radius. Generally spaces do not require any agents, but graphs + * do- generally the non-local ends to master copies of edges. + */ + virtual void getRequiredAgents(std::set& agentsToTest, std::set& agentsRequired, RADIUS radius = PRIMARY) = 0; + + /** + * Given a set of agents, gets the agents that this projection implementation must 'push' to + * other processes. Generally spaces must push agents that are in 'buffer zones' and graphs + * must push local agents that are vertices to master edges where the other vertex is non- + * local. The results are returned per-process in the agentsToPush map. + */ + virtual void getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush) = 0; + + // Note: Virtual because some child classes may be able to short-circuit this (like Graphs) + /** + * Convenience wrapper that gets all of the projection information for the agents specified + * (calls implementation in child class that gets only the information for one agent). + */ + virtual void getProjectionInfo(std::vector& agents, std::vector& packets, + bool secondaryInfo = false, std::set* secondaryIds = 0, int destProc = -1); + + /** + * Updates the projection information for the agents in this projection according to + * the information contained in the vector of information packets passed. + */ + void updateProjectionInfo(std::vector& pips, Context* context); + + virtual void cleanProjectionInfo(std::set& agentsToKeep) = 0; + + virtual void balance(){}; + +}; + + +template +void Projection::getProjectionInfo(std::vector& agents, std::vector& packets, + bool secondaryInfo, std::set* secondaryIds, int destProc){ + for(std::vector::const_iterator iter = agents.begin(), iterEnd = agents.end(); iter != iterEnd; iter++){ + ProjectionInfoPacket* packet = getProjectionInfo((*iter), secondaryInfo, secondaryIds, destProc); + if((packet != 0) && (!packet->isEmpty())) packets.push_back(packet); + } +} + +template +void Projection::updateProjectionInfo(std::vector& pips, Context* context){ + for(std::vector::const_iterator pipIter = pips.begin(), pipIterEnd = pips.end(); pipIter != pipIterEnd; pipIter++){ + updateProjectionInfo(*pipIter, context); + } +} + +} + +#endif /* PROJECTION_H_ */ diff --git a/libs/repast_hpc/Properties.cpp b/libs/repast_hpc/Properties.cpp new file mode 100644 index 0000000..a82250b --- /dev/null +++ b/libs/repast_hpc/Properties.cpp @@ -0,0 +1,240 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Properties.cpp + * + * Created on: Sep 25, 2009 + * Author: nick + */ + +#include +#include +#include +#include + +#include "Properties.h" +#include "io.h" +#include "RepastErrors.h" + +#include "boost/serialization/map.hpp" +#include "boost/mpi/collectives.hpp" + + +using namespace std; + +namespace repast { + +string KeyGetter::operator()(const map::value_type& value) const { + return value.first; +} + +Properties::Properties() { +} + +Properties::Properties(const string& file, boost::mpi::communicator* comm, int maxPropFileSize) { + readFile(file, comm, maxPropFileSize); +} + +Properties::Properties(const string& file, int argc, char** argv, boost::mpi::communicator* comm, int maxPropFileSize) { + readFile(file, comm, maxPropFileSize); + processCommandLineArguments(argc, argv); +} + +Properties::Properties(int argc, char** argv) { + this->processCommandLineArguments(argc, argv); +} + +void Properties::putProperty(const string& key, string value) { + map[key] = value; +} + +void Properties::putProperty(const string& key, long double value){ + map[key] = std::to_string(value); +} + +bool Properties::contains(const string& key) const { + return map.find(key) != map.end(); +} + +string Properties::getProperty(const string& key) const { + std::map::const_iterator iter = map.find(key); + if (iter == map.end()) + return ""; + else + return iter->second; +} + +void Properties::readFile(const std::string& file, boost::mpi::communicator* comm, int maxPropFileSize){ + + char* PROPFILEBUFFER = new char[maxPropFileSize]; // All procs allocate memory for the properties file + + if(comm == 0 || comm->rank() == 0){ // If no communicator is passed, all ranks read props file + ifstream fileInStream(file.c_str(), ios_base::in); + if (fileInStream.is_open()){ + fileInStream.read(PROPFILEBUFFER, maxPropFileSize); + // Check if fail: + if(fileInStream.gcount() >= (maxPropFileSize - 2)){ + throw Repast_Error_52(maxPropFileSize, fileInStream.gcount(), file); // Properties file exceeds maximum allowed size + } + PROPFILEBUFFER[fileInStream.gcount()] = '\0'; // Add a null terminator + fileInStream.close(); + } else { + throw Repast_Error_53(file); // Properties file not found + } + } + if(comm != 0){ // If a communicator was passed, proc 0 broadcasts to all other procs + MPI_Bcast(PROPFILEBUFFER, maxPropFileSize, MPI_CHAR, 0, *comm); + } + + std::string P(PROPFILEBUFFER); + delete[] PROPFILEBUFFER; + + istringstream in(P); + string line; + while (!in.eof()) { + getline(in, line); + str_trim(line); + if (line.length() > 0 && line[0] != '#') { + size_t pos = line.find_first_of("="); + if (pos == string::npos) + throw Repast_Error_54(line, file); // Missing '=' in properties file + string key = line.substr(0, pos); + repast::str_trim(key); + if (key.length() == 0) + throw Repast_Error_55(line, file); // Missing key value in properties file + string value = ""; + if (line.length() > pos) { + // this makes sure we only try to get value if it exists + value = line.substr(pos + 1, line.length()); + } + repast::str_trim(value); + if (value.length() == 0) + throw Repast_Error_56(line, file); // Missing key value in properties file + map[key] = value; + } + } +} + +void Properties::processCommandLineArguments(int argc, char **argv){ + for(int i = 0; i < argc; i++){ + std::string pEntry(argv[i]); + size_t indexOfEqualsSign = pEntry.find('='); + if(indexOfEqualsSign != std::string::npos) + putProperty(pEntry.substr(0, indexOfEqualsSign), pEntry.substr(indexOfEqualsSign + 1)); + } +} + +void Properties::writeToSVFile(std::string fileName, std::string separator){ + std::vector keysToWrite; + std::map::iterator iter = map.begin(); + std::map::iterator iterEnd = map.end(); + while(iter != iterEnd){ + keysToWrite.push_back(iter->first); + iter++; + } + writeToSVFile(fileName, keysToWrite, separator); +} + +void Properties::writeToSVFile(std::string fileName, std::vector &keysToWrite, std::string separator){ + bool writeHeader = !boost::filesystem::exists(fileName); + std::ofstream outFile; + + outFile.open(fileName.c_str(), std::ios::app); + + if(writeHeader){ + std::vector::iterator keys = keysToWrite.begin(); + std::vector::iterator keysEnd = keysToWrite.end(); + int i = 1; + while(keys != keysEnd){ + outFile << *keys << (i != keysToWrite.size() ? separator : ""); + keys++; + i++; + } + outFile << std::endl; + } + std::vector::iterator keys = keysToWrite.begin(); + std::vector::iterator keysEnd = keysToWrite.end(); + int i = 1; + while(keys != keysEnd){ + outFile << std::fixed << getProperty(*keys) << (i != keysToWrite.size() ? separator : ""); + keys++; + i++; + } + outFile << std::endl; + + outFile.close(); + +} + +bool Properties::writeToPropsFile(std::string fileName, std::string header){ + std::vector keysToWrite; + std::map::iterator iter = map.begin(); + std::map::iterator iterEnd = map.end(); + while(iter != iterEnd){ + keysToWrite.push_back(iter->first); + iter++; + } + return writeToPropsFile(fileName, keysToWrite, header); +} + +bool Properties::writeToPropsFile(std::string fileName, std::vector &keysToWrite, std::string header){ + if(boost::filesystem::exists(fileName)) return false; + std::ofstream outfile; + outfile.open(fileName.c_str(), std::ios::app); + outfile << propsFileString(keysToWrite, header); + outfile.close(); + return true; +} + +std::string Properties::propsFileString(std::vector &keysToWrite, std::string header){ + stringstream outstring; + outstring << header; + std::vector::iterator keys = keysToWrite.begin(); + int maxKeyWidth = 0; + while(keys != keysToWrite.end()){ + maxKeyWidth = (*keys).length() > maxKeyWidth ? (*keys).length() : (maxKeyWidth + 1); // Note: maxWidth gets padded by an additional space + keys++; + } + std::string pad = " "; + while(pad.length() < maxKeyWidth) pad = pad + pad; + keys = keysToWrite.begin(); + while(keys != keysToWrite.end()){ + outstring << (*keys + pad).substr(0, maxKeyWidth) << " = " << getProperty(*keys) << std::endl; + keys++; + } + return outstring.str(); +} + + +} diff --git a/libs/repast_hpc/Properties.h b/libs/repast_hpc/Properties.h new file mode 100644 index 0000000..0dea704 --- /dev/null +++ b/libs/repast_hpc/Properties.h @@ -0,0 +1,319 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Properties.h + * + * Created on: Sep 25, 2009 + * Author: nick + */ + +#ifndef PROPERTIES_H_ +#define PROPERTIES_H_ + +#include + + +#include +#include +#include + +#include +#include +#include + +#include "logger.h" + +#define MAX_PROP_FILE_SIZE 16384 + +namespace repast { + +/** + * Unary function used in a transform_iterator that allows the map + * iterator to return the keys + */ +struct KeyGetter: public std::unary_function::value_type, std::string> { + std::string operator()(const std::map::value_type& value) const; +}; + +/** + * Map type object that contains key, value(string) properties. A Properties + * instance can be constructed from a file. Each line is a property with the + * key and value separated by =. For example, + * + * some.property = 3
+ * another.property = hello + */ +class Properties { + +private: + std::map map; + +public: + + typedef boost::transform_iterator::const_iterator> key_iterator; + + /** + * Creates an empty Properties. + */ + Properties(); + + /** + * Creates a new Properties using the properties defined in the specified file. + * Each line is a property with the key and value separated by =. For example, + * + * some.property = 3
+ * another.property = hello + * + * @param file the properties file path + * @param comm pointer to a communicator; if null (the default), all + * processes read the properties file separately. If a communicator + * is provided, rank 0 reads the file and broadcasts it to all + * other ranks. + * @param maxPropFileSize optional parameter; if the properties + * file is larger than the default MAX_PROP_FILE_SIZE, the + * new size can be passed here. + */ + Properties(const std::string& file, boost::mpi::communicator* comm = 0, int maxPropFileSize = MAX_PROP_FILE_SIZE); + + /** + * Creates a new Properties using the properties defined in the specified + * file and any properties specified in Key=Val format in the argument + * array. Properties in the argument array will supersede any in the properties + * file. + * + * Each line in the properties file is a property with the key and value + * separated by =. For example, + * + * some.property = 3
+ * another.property = hello + * + * + * @param file the properties file path + * @param argc count of the elements in the argv array + * @param array of char* that may include Key=Value pairs. Elements with + * no '=' are ignored. + * @param comm pointer to a communicator; if null (the default), all + * processes read the properties file separately. If a communicator + * is provided, rank 0 reads the file and broadcasts it to all + * other ranks. + * @param maxPropFileSize optional parameter; if the properties + * file is larger than the default MAX_PROP_FILE_SIZE, the + * new size can be passed here. + */ + Properties(const std::string& file, int argc, char** argv, boost::mpi::communicator* comm = 0, int maxPropFileSize = MAX_PROP_FILE_SIZE); + + /** + * Creates a new Properties using the properties specified in Key=Val + * format in the argument + * + * @param argc count of the elements in the argv array + * @param array of char* that may include Key=Value pairs. Elements with + * no '=' are ignored. + */ + Properties(int argc, char** argv); + + virtual ~Properties() { + } + + /** + * Puts a property into this Properties with + * the specified key and value. + * + * @param key the property key + * @param value the property value + */ + void putProperty(const std::string& key, std::string value); + + /** + * Puts a property into this Properties with + * the specified key and value. Note that + * even though the second argument can be passed + * as a numeric value, it is stored as a string + * + * @param key the property key + * @param value the property value + */ + void putProperty(const std::string& key, long double value); + + + /** + * Gets the property with the specified key. + * + * @param key the property key + * + * @return the value for that key, or an empty string + * if the property is not found. + */ + std::string getProperty(const std::string& key) const; + + /** + * Gets whether or not this Properties contains the specified key. + * + * @param key the property key + */ + bool contains(const std::string& key) const; + + /** + * Gets the start of an iterator over this Properties' keys. + * + * @return the start of an iterator over this Properties' keys. + */ + key_iterator keys_begin() const { + return key_iterator(map.begin()); + } + + /** + * Gets the end of an iterator over this Properties' keys. + * + * @return the end of an iterator over this Properties' keys. + */ + key_iterator keys_end() const { + return key_iterator(map.end()); + } + + /** + * Adds any properties defined in the specified file. + * Each line is a property with the key and value separated by =. For example, + * + * some.property = 3
+ * another.property = hello + * + * @param file the properties file path + * @param comm pointer to a communicator; if null (the default), all + * processes read the properties file separately. If a communicator + * is provided, rank 0 reads the file and broadcasts it to all + * other ranks. + */ + void readFile(const std::string& file, boost::mpi::communicator* comm = 0, int maxPropFileSize = MAX_PROP_FILE_SIZE); + + /** + * Processes a char** array of the given size; + * any component that has an equals sign is entered + * as a property value, overriding any previous + * entry read from the properies file + * + * @param argc the number of entries in the array + * @param argv the array of char values to be mapped + */ + void processCommandLineArguments(int argc, char **argv); + + /** + * Gets the number of properties in this Properties. + * + * @return the number of properties in this Properties. + */ + int size() const { + return map.size(); + } + + /** + * Writes the contents of the properties file to the specified + * repast log (at 'INFO' log level) + * + * @param logName name of the log to use + * @param keysToWrite optional; if included, writes only the + * keys included in the vector and their values, in the + * order they appear in the vector. Will write + * blank values for any key name in the vector that is not + * in the properties file. If not included, all properties + * and their values are written, in map order. + */ + void log(std::string logName, std::vector *keysToWrite = 0){ + if(keysToWrite != 0){ + std::vector::iterator iter = keysToWrite->begin(); + std::vector::iterator iterEnd = keysToWrite->end(); + while(iter != iterEnd){ + Log4CL::instance()->get_logger(logName).log(INFO, (*iter) + " = " + (getProperty(*iter))); + iter++; + } + } + else{ + std::map::iterator iter = map.begin(); + std::map::iterator iterEnd = map.end(); + while(iter != iterEnd){ + Log4CL::instance()->get_logger(logName).log(INFO, iter->first + " = " + iter->second); + iter++; + } + } + } + + + /** + * Writes the contents of the properties file to the specified separated-value + * file. If the file does not exist it is created and a header line is written + * with the key values. + * + * @param fileName name + */ + void writeToSVFile(std::string fileName, std::string separator = ","); + + /** + * Writes the contents of the properties file to the specified separated-value + * file. If the file does not exist it is created and a header line is written + * with the key values. + * + * @param fileName name + */ + void writeToSVFile(std::string fileName, std::vector &keysToWrite, std::string separator = ","); + + /** + * Writes the contents of this properties object to a file + * that could be read as another properties file. If a file + * with the specified name already exists, the existing file + * will not be changed and 'false' will be returned. + */ + bool writeToPropsFile(std::string filename, std::string header = ""); + + /** + * Writes the contents of this properties object + * to a file that could be read as another properties file, + * writing only the keys specified, in the order specified. + * If a file with the specified name already exists, the existing file + * will not be changed and 'false' will be returned. + */ + bool writeToPropsFile(std::string filename, std::vector &keysToWrite, std::string header=""); + + /** + * Gets the string that will be written to a properties + * file using the writeToPropsFile methods + */ + + std::string propsFileString(std::vector &keysToWrite, std::string header=""); + + +}; + +} + +#endif /* PROPERTIES_H_ */ diff --git a/libs/repast_hpc/README.md b/libs/repast_hpc/README.md new file mode 100644 index 0000000..a751909 --- /dev/null +++ b/libs/repast_hpc/README.md @@ -0,0 +1,39 @@ +Original source files available at: [https://github.com/Repast/repast.hpc](https://github.com/Repast/repast.hpc) + +## License: + +>Repast for High Performance Computing (Repast HPC) +> +> Copyright (c) 2021 Argonne National Laboratory +> All rights reserved. +> +> Redistribution and use in source and binary forms, with +> or without modification, are permitted provided that the following +> conditions are met: +> +> Redistributions of source code must retain the above copyright notice, +> this list of conditions and the following disclaimer. +> +> Redistributions in binary form must reproduce the above copyright notice, +> this list of conditions and the following disclaimer in the documentation +> and/or other materials provided with the distribution. +> +> Neither the name of the Argonne National Laboratory nor the names of its +> contributors may be used to endorse or promote products derived from +> this software without specific prior written permission. +> +> THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +> ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +> LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +> PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR +> CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +> EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +> PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +> PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +> LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +> NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +> EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +## Changed or added files +- CMakeFiles.txt +- GridComponents.cpp (added "cfloat" include) diff --git a/libs/repast_hpc/Random.cpp b/libs/repast_hpc/Random.cpp new file mode 100644 index 0000000..eb86c4e --- /dev/null +++ b/libs/repast_hpc/Random.cpp @@ -0,0 +1,153 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Random.cpp + * + * Created on: Jun 9, 2009 + * Author: nick + */ + +#include "Random.h" + +#include + +namespace repast { + +using namespace std; +using namespace boost; + +Random* Random::instance_ = 0; + +Random::Random(uint32_t seed) : _seed(seed), rng(seed), uni10(0, 1), uniGen(_RealUniformGenerator(rng, boost::uniform_real<>(0, 1))) { + +} + +Random::~Random() { + for (map::iterator iter = generators.begin(); iter != generators.end(); iter++) { + delete iter->second; + } +} + +void Random::initialize(uint32_t seed) { + instance_ = new Random(seed); +} + +Random* Random::instance() { + if (instance_ == 0) { + Random::initialize(std::time(0)); + } + return instance_; +} + +/** + * inclusive of from, exclusive of to. + */ +DoubleUniformGenerator Random::createUniDoubleGenerator(double from, double to) { + _RealUniformGenerator gen(rng, boost::uniform_real<>(from, to)); + return DefaultNumberGenerator<_RealUniformGenerator> (gen); + //return DoubleUniformGenerator(from, to); +} + +double Random::nextDouble() { + return uniGen(); +} + +IntUniformGenerator Random::createUniIntGenerator(int from, int to) { + _IntUniformGenerator gen(rng, boost::uniform_int<>(from, to)); + return DefaultNumberGenerator<_IntUniformGenerator> (gen); + //return IntUniformGenerator(from, to); +} + +TriangleGenerator Random::createTriangleGenerator(double lowerBound, double mostLikely, double upperBound) { + boost::triangle_distribution<> dist(lowerBound, mostLikely, upperBound); + _TriangleGenerator gen(rng, dist); + return DefaultNumberGenerator<_TriangleGenerator> (gen); +} + +CauchyGenerator Random::createCauchyGenerator(double median, double sigma) { + boost::cauchy_distribution<> dist(median, sigma); + _CauchyGenerator gen(rng, dist); + return DefaultNumberGenerator<_CauchyGenerator> (gen); +} + +ExponentialGenerator Random::createExponentialGenerator(double lambda) { + boost::exponential_distribution<> dist(lambda); + _ExponentialGenerator gen(rng, dist); + return DefaultNumberGenerator<_ExponentialGenerator> (gen); +} + +/* + GeometricGenerator Random::createGeometricGenerator(double p) { + boost::geometric_distribution > dist(p); + _GeometricGenerator gen(rng, dist); + return GeometricGenerator(gen); + } + */ + +NormalGenerator Random::createNormalGenerator(double mean, double sigma) { + boost::normal_distribution<> dist(mean, sigma); + _NormalGenerator gen(rng, dist); + return DefaultNumberGenerator<_NormalGenerator> (gen); +} + +LogNormalGenerator Random::createLogNormalGenerator(double mean, double sigma) { + boost::lognormal_distribution<> dist(mean, sigma); + _LogNormalGenerator gen(rng, dist); + return DefaultNumberGenerator<_LogNormalGenerator> (gen); +} + +/* + GeometricGenerator::GeometricGenerator(_GeometricGenerator generator) : gen(generator) {} + + double GeometricGenerator::next() { + return gen()(gen.engine()); + } + */ + +void Random::putGenerator(const string& id, NumberGenerator* generator) { + generators.insert( make_pair(id, generator)); +} + +NumberGenerator* Random::getGenerator(const string& id) { + map::iterator iter = generators.find(id); + if (iter == generators.end()) return 0; + return iter->second; +} + +ptrdiff_t uni_random(ptrdiff_t i) { + IntUniformGenerator gen = Random::instance()->createUniIntGenerator(0, i - 1); + return (ptrdiff_t)gen.next(); +} + +} diff --git a/libs/repast_hpc/Random.h b/libs/repast_hpc/Random.h new file mode 100644 index 0000000..866ab7b --- /dev/null +++ b/libs/repast_hpc/Random.h @@ -0,0 +1,702 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Random.h + * + * Created on: Jun 9, 2009 + * Author: nick + */ + +#ifndef RANDOM_H_ +#define RANDOM_H_ + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace repast { + +typedef boost::variate_generator > _RealUniformGenerator; +typedef boost::variate_generator > _IntUniformGenerator; + +typedef boost::variate_generator > _TriangleGenerator; +typedef boost::variate_generator > _CauchyGenerator; +typedef boost::variate_generator > _ExponentialGenerator; +typedef boost::variate_generator > > + _GeometricGenerator; +typedef boost::variate_generator > _NormalGenerator; +typedef boost::variate_generator > _LogNormalGenerator; + +/** + * Number generator interface. + */ +class NumberGenerator { + +public: + virtual ~NumberGenerator() { + } + + /** + * Gets the "next" number from this + * Number Generator. + */ + virtual double next() = 0; +}; + +/** + * Adapts the templated boost::variate_generator to the + * NumberGenerator interface. + */ +template +class DefaultNumberGenerator: public NumberGenerator { + +private: + T gen; + +public: + DefaultNumberGenerator(T generator); + double next(); +}; + +template +DefaultNumberGenerator::DefaultNumberGenerator(T generator) : + gen(generator) { +} + +template +double DefaultNumberGenerator::next() { + return gen(); +} + +typedef DefaultNumberGenerator<_IntUniformGenerator> IntUniformGenerator; +typedef DefaultNumberGenerator<_RealUniformGenerator> DoubleUniformGenerator; +typedef DefaultNumberGenerator<_TriangleGenerator> TriangleGenerator; +typedef DefaultNumberGenerator<_CauchyGenerator> CauchyGenerator; +typedef DefaultNumberGenerator<_ExponentialGenerator> ExponentialGenerator; +typedef DefaultNumberGenerator<_NormalGenerator> NormalGenerator; +typedef DefaultNumberGenerator<_LogNormalGenerator> LogNormalGenerator; + +/** + * Methods for working with random distributions, draws etc. + */ +class Random { + +private: + static Random* instance_; + + boost::uint32_t _seed; + boost::mt19937 rng; + boost::uniform_real<> uni10; + _RealUniformGenerator uniGen; + + std::map generators; + +protected: + Random(boost::uint32_t seed); + +public: + + /** + * Initialize the Random singleton with the specified seed. + * + * @param the seed to initialize the random number generator with. + */ + static void initialize(boost::uint32_t seed); + + /** + * Gets the singleton instance of this Random. + */ + static Random* instance(); + virtual ~Random(); + + /** + * Puts the named generator into this Random. Added + * generators will be deleted by Random when it is destroyed. + * + * @param the id of the generator + * @param generator the generator to add + */ + void putGenerator(const std::string& id, NumberGenerator* generator); + + /** + * Gets the named generator or 0 if the name + * is not found. + * + * @param id the name of the generator to get + */ + NumberGenerator* getGenerator(const std::string& id); + + /** + * Gets the random number engine from which the distributions are created. + * + * @return he random number engine from which the distributions are created. + */ + boost::mt19937& engine() { + return rng; + } + + /** + * Gets the current seed. + * + * @return the current seed. + */ + boost::uint32_t seed() { + return _seed; + } + + /** + * Gets the next double in the range [0, 1). + * + * @return the next double in the range [0, 1). + */ + double nextDouble(); + + /** + * Creates a generator that produces doubles in the range [from, to). + * + * @param from the range start (inclusive) + * @param to the range end (exclusive) + * + * @return a generator that produces doubles in the range [from, to). + */ + DoubleUniformGenerator createUniDoubleGenerator(double from, double to); + + /** + * Creates a generator that produces ints in the range [from, to]. + * + * @param from the range start (inclusive) + * @param to the range end (inclusive) + * + * @return a generator that produces ints in the range [from, to]. + */ + IntUniformGenerator createUniIntGenerator(int from, int to); + + /** + * Creates a triangle generator with the specified properties. A TriangleGenerator produces a + * floating point value x where lowerbound <= x <= upperBound and mostLikely is the most + * probable value for x. + * + * @param lowerBound the lower bound of the values produced by the generator + * @param mostLikely the most likely value produced by the generator + * @param upperBound the upper bound of the values produced by the generator + * + * @return a triangle generator. + */ + TriangleGenerator createTriangleGenerator(double lowerBound, double mostLikely, double upperBound); + + /** + * pdf: p(x) = sigma/(pi*(sigma**2 + (x-median)**2)) + * + * @param median + * @param sigma + * + * @return a Cauchy generator. + */ + CauchyGenerator createCauchyGenerator(double median, double sigma); + + /** + * pdf: p(x) = lambda * exp(-lambda * x) + * + * @param lambda must be > 0 + * + * @return an exponential generator. + */ + ExponentialGenerator createExponentialGenerator(double lambda); + + /** + * Creates a normal generator. + * + * pdf: p(x) = 1/sqrt(2*pi*sigma) * exp(- (x-mean)2 / (2*sigma2) ) + * + * + */ + NormalGenerator createNormalGenerator(double mean, double sigma); + + /** + * Produces random numbers with p(x) = 1/(x * normal_sigma * sqrt(2*pi)) * exp( -(log(x)-normal_mean)2 / (2*normal_sigma2) ) + * for x > 0, where normal_mean = log(mean2/sqrt(sigma2 + mean2)) and normal_sigma = sqrt(log(1 + sigma2/mean2)) + */ + LogNormalGenerator createLogNormalGenerator(double mean, double sigma); +}; + +/** + * Random generator function that can be used in STL algorithms that + * need a random generator. + * + * @return a value from 0 up to, but not including i. + */ + +ptrdiff_t uni_random(ptrdiff_t i); + +/** + * Given an iterator's start and end, returns the count of the number + * of elements. + * + * @param iteratorStart + * @param iteratorEnd + * + * @return number of steps from start to end; count of elements + * in iterator + */ +template +int countOf(I iteratorStart, I iteratorEnd){ + I iterator = iteratorStart; + int c = 0; + while(iterator != iteratorEnd){ iterator++; c++; } + return c; +} + +/** + * Shuffles the order of a vector of elements. + * Note that there can be alternative algorithms + * for this that trade memory and performance + * in different ways. This proceeds through the + * entire list, and at each position randomly + * chooses one of the other elements and swaps them. + * + * The result should be mathematically random- + * that is, every possible combination should have an equal + * chance of being the final result. + * + * + * @param elementList the list to be shuffled + */ +template +void shuffleList(std::vector& elementList){ + if(elementList.size() <= 1) return; + IntUniformGenerator rnd = Random::instance()->createUniIntGenerator(0, elementList.size() - 1); + T* swap; + for(size_t i = 0, sz = elementList.size(); i < sz; i++){ + int other = rnd.next(); + swap = elementList[i]; + elementList[i] = elementList[other]; + elementList[other] = swap; + } + + // OR + // see: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle#The_modern_algorithm + + // DoubleUniformGenerator rnd = Random::instance()->createUniDoubleGenerator(0, 1); + // T* swap; + // for(int pos = elementList.size() - 1; pos > 0; pos--){ + // int range = pos + 1; + // int other = (int)(rnd.next() * (range)); + // swap = elementList[pos]; + // elementList[pos] = elementList[other]; + // elementList[other] = swap; + // } +} + +/** + * Given a set, returns the elements in that set in shuffled + * order. Acts by assigning all elements in the given set + * to the vector, then calling shuffleList on that vector. + * + * @param elementSet collection of elements to be shuffled + * @param [out] elementList the list, shuffled + */ +template +void shuffleSet(std::set& elementSet, std::vector& elementList){ + elementList.assign(elementSet.begin(), elementSet.end()); + shuffleList(elementList); + + // // Or- probably faster... ?? + // + // elementList.reserve(elementSet.size()); + // typename std::set::iterator setIterator = elementSet.begin(); + // DoubleUniformGenerator rnd = Random::instance()->createUniDoubleGenerator(0,1); + // for(int i = 0; i < elementSet.size(); i++){ + // int j = (int)(rnd.next() * (i + 1)); + // elementList.push_back(elementList[j]); + // elementList[j] = *setIterator; + // setIterator++; + // } +} + +/** + * Given an iterator and a number of elements, + * creates a data structure that allows efficient access + * to those elements. Is only valid as long as the iterator + * is valid. + * + * The basic implementation creates a vector of ordered + * pairs linking an integer and an iterator pointing + * to an element in the original iteration set. To find + * the nth element, the algorithm searches backwards through the + * list of 'landmarks', finds the highest landmark lower than + * n, chooses the iterator associated with that landmark, + * and steps forward until n is reached, adding new landmarks + * if appropriate. So given landmarks: + * + * 0 - pointer to element 0 + * 100 - pointer to element 100 + * 200 - pointer to element 200 + * + * if the request for element 438 is given, the algorithm will + * search backward and find landmark 200; it will + * then step forward, adding landmarks for 300 and 400, until element 438 + * is reached and returned. + * + * Assuming that requests are evenly distributed, optimum interval for + * landmarks is the square root of the size of the list, and performance + * for the algorithm will be in log(size) time. + * + * Note that other implementations are possible- for example, checking + * if enough memory would allow a completely indexed list. A long-term + * possibility is allowing the user to specify (for example, specify that + * the algorithm with lowest memory cost be used even though memory + * is initially available, perhaps because other routines will be filling + * that memory while this object is in use). + */ +template +class RandomAccess{ +private: + I it; + I begin; + + int interval; + int maxLandmark; + std::vector > landmarks; + +public: + + /** + * Constructs a RandomAccess instance for this iterator + * + * @param beginning + * @param size + */ + RandomAccess(I beginning, int size) : + interval((int)(sqrt((double)size))), maxLandmark(0), it(beginning), begin(beginning) { + landmarks.push_back(std::pair(0, beginning)); + } + + /** + * Gets the element at the specified index + * + * @param index + */ + I get(int index){ + bool place = (index > (maxLandmark + interval)); + typename std::vector >::iterator lm = landmarks.end(); + while((--lm)->first > index); + int c = lm->first; + it = lm->second; + if(place){ + while(c != index){ + c++; + it++; + if(c % interval == 0){ + landmarks.push_back(std::pair(c, it)); + maxLandmark = c; + } + } + } + else{ + while(c != index){ + c++; + it++; + } + } + return it; + } + +}; + + +/** + * Gets N elements selected randomly from the elements that are indexed by the iterator passed. + * Selection is 'without replacement'; no element will appear in the result set twice. + * The elements are placed in a set; order is not randomized. + * + * If the 'selectedElements' set is not empty initially, the elements chosen will be added + * to it with duplication prevented- in other words, the elements initially in the + * set will not be selected from the population and added to the set by this routine. + * + * If it is not possible to select all of the agents requested + * because the number requested exceeds the number of unique agents that can be added + * to the result set, then all of the agents from the iterator will be added to the set. + * + * If the optional 'remove' parameter is set to true, the elements in the + * original 'selectedElements' set will be removed before the set is + * returned. + * + * @param iteratorStart the start of the iterated list of elements from which + * selection will be made + * @param size the size of the source population- i.e., the number of elements + * in the list pointed to by iteratorStart + * @param count number of elements to be selected + * @param [out] selectedElements collection into which selected elements + * are placed. If not empty, elements in initial set are excluded from + * selection. + * @param remove if true, the elements in the original set will be removed from + * the set returned. + * + * @tparam T the type of the elements to be selected (i.e. the agent type) + * @tparam I the type of the iterator to be used to pass through the population + * to be sampled + */ +template +void selectNElementsAtRandom(I iterator, int size, unsigned int count, std::set&selectedElements, bool remove = false){ + // When 'selectedElements' is large in comparison to size, or 'count' is + // large in comparison to size, or both, a potential problem arises; this is both + // a performance issue and a potential error (infinite loop). The number + // of valid selections remaining in the original population will be + // the size of the population minus the number of members of the original + // population already in the set of 'selectedElements'. There is a performance + // problem that can arise if the number of valid selections approaches + // zero, and an infinite loop arises if that number reaches zero. However, a + // complication is that we do not assume that all members in + // 'selectedElements' are drawn from the same population that 'iterator' describes. + + // At a general level, the solution to this is that when the number of valid + // selections is expected to become small, the algorithm will switch to + // selecting the elements that will _not_ be added to the final set, rather + // than those that will. + + // However, the more immediate issue is determining whether the problem + // will arise at all, and a key question is how many members of 'selectedElements' + // are drawn from the population. This, however, is costly to check, so + // it should only be checked if it seems possible to lead to a problem. + + // A ceiling; the actual value may be lower + int maxAlreadySelected = selectedElements.size(); + + // We are not certain _any_ elements in the selectedElements set are from this population + int knownSelectedElementsFromPop = 0; + + // This is a floor; there are at least this many available, but possibly more + int minAvailable = size - maxAlreadySelected; + + // If you are requesting more than might be available, you must determine + // the actual number available. + if(count > minAvailable){ + if(maxAlreadySelected > 0){ + I tempIt = iterator; + for(int i = 0; i < size; i++){ + T* ptr = (&**tempIt); + typename std::set::iterator found = selectedElements.find(ptr); + if(found != selectedElements.end()) knownSelectedElementsFromPop++; + tempIt++; + } + maxAlreadySelected = knownSelectedElementsFromPop; + minAvailable = size - maxAlreadySelected; + } + } + // If removing the original elements, will need a copy of them + typename std::vector tempToRemove; + if(remove){ + tempToRemove.assign(selectedElements.begin(), selectedElements.end()); + } + + // There is no way to satisfy the request; copy all and return + if(count > minAvailable){ + I it = iterator; + for(int i = 0; i < size; i++){ + selectedElements.insert(&**it); + it++; + } + } + else{ + RandomAccess ra(iterator, size); + IntUniformGenerator rnd = Random::instance()->createUniIntGenerator(0, size - 1); + T* ptr; + + // If the count of elements is very high in proportion to the population, faster to + // choose agents that will _not_ be in the final set and then switch... + // First the normal case, in which the number of agents is low + if(count <= ((double)minAvailable)*0.6){ // Tests suggest that 2/3 is probably too high, so adjusted to .6; TODO optimize or analytically solve + for (unsigned int i = 0; i < count; i++) + do { ptr = &**ra.get(rnd.next()); } while(!(selectedElements.insert(ptr).second)); + } + else{ // Now the other case + std::set elementsThatWillNotBeAdded; + + if(selectedElements.size() > 0){ + if(selectedElements.size() == knownSelectedElementsFromPop){ // Maybe we already checked and they ALL belong + elementsThatWillNotBeAdded.insert(selectedElements.begin(), selectedElements.end()); + } + else{ + I tempIt = iterator; + for(int i = 0; i < size; i++){ + ptr = (&**tempIt); + if(selectedElements.find(ptr) != selectedElements.end()) elementsThatWillNotBeAdded.insert(ptr); + tempIt++; + } + } + } + // Note: duplicate element will not be inserted; failure will leave size of elementsThatWillNotBeAdded unchanged. + while((size - elementsThatWillNotBeAdded.size()) > count) elementsThatWillNotBeAdded.insert(&**ra.get(rnd.next())); + + // Once the set of elements that will not be added is complete, cycle through the iterator and add + // all of those elements that are not in elementsThatWillNotBeAdded + I toAdd = iterator; + typename std::set::iterator notFound = elementsThatWillNotBeAdded.end(); + for(int i = 0; i < size; i++){ + ptr = &**toAdd; + if(elementsThatWillNotBeAdded.find(ptr) == notFound) selectedElements.insert(ptr); + toAdd++; + } + } + } + // Before returning, remove the elements from the set, if the user requested + if(remove){ + typename std::vector::iterator toRemove = tempToRemove.begin(); + while(toRemove != tempToRemove.end()){ + selectedElements.erase(*toRemove); + toRemove++; + } + } +} + + +/** + * Gets N elements selected randomly from the elements that are indexed by the iterator passed. + * + * Acts by calculating the size of the population covered by the iterator given using + * countOf(iteratorStart, iteratorEnd) and invoking + * selectNElementsAtRandom(I iterator, int size, int count, std::set&selectedElements, bool remove). + * + * @param iteratorStart the start of the iterated list of elements from which + * selection will be made + * @param iteratorEnd the end of the iterated list of elements from which + * selection will be made + * @param count number of elements to be selected + * @param [out] selectedElements collection into which selected elements + * are placed. If not empty, elements in initial set are excluded from + * selection. + * @param remove if true, the elements in the original set will be removed from + * the set returned. + * + * @tparam T the type of the elements to be selected (i.e. the agent type) + * @tparam I the type of the iterator to be used to pass through the population + * to be sampled + */ +template +void selectNElementsAtRandom(I iteratorStart, I iteratorEnd, int count, std::set&selectedElements, bool remove = false){ + selectNElementsAtRandom(iteratorStart, countOf(iteratorStart, iteratorEnd), count, selectedElements, remove); +} + +/** + * Randomly selects elements from the iterator (equivalent + * to a call to selectNElementsAtRandom), but before returning them + * places them into the specified vector and + * randomly shuffles them. + * + * @param iteratorStart the start of the list of elements from which + * selection will be made + * @param size the count of the source population from which selection + * will be made (i.e. the end of the iterated list) + * @param count the number of elements to be selected. If this + * exceeds the number of valid, unique selections available in the + * source population then all valid, unique elements in the source population + * will be returned + * @param [out] selectedElements collection into which selected elements + * are placed. If not empty, elements in initial set are excluded from + * selection. + * @param remove Optional; if included, specifies whether the elements + * given in the 'selectedElements' set should appear in the final, sorted set. + * If true, the elements in this set are not selected + * from the population and are removed from the results before the final + * set is returned; if false, the elements in the set are + * not selected from the population but are included in the result set before + * it is shuffled and returned. + * + * @tparam T the type of the elements to be selected (i.e. the agent type) + * @tparam I the type of the iterator to be used to pass through the population + * to be sampled + */ +template +void selectNElementsInRandomOrder(I iterator, int size, int count, std::vector& selectedElements, bool remove = false){ + // Transfer all elements from the vector to a set + std::set selectedElementSet; + selectedElementSet.insert(selectedElements.begin(), selectedElements.end()); + selectedElements.clear(); + selectNElementsAtRandom(iterator, size, count, selectedElementSet, remove); + shuffleSet(selectedElementSet, selectedElements); +} + +/** + * Randomly selects elements from the iterator (equivalent + * to a call to selectNElementsAtRandom), but before returning them + * places them into the specified vector and + * randomly shuffles them. + * + * @param iteratorStart the start of the list of elements from which + * selection will be made + * @param iteratorEnd the end of the list of elements from which selection + * will be made + * @param count the number of elements to be selected. If this + * exceeds the number of valid, unique selections available in the + * source population then all valid, unique elements in the source population + * will be returned + * @param [out] selectedElements collection into which selected elements + * are placed. If not empty, elements in initial set are excluded from + * selection. + * @param remove Optional; if included, specifies whether the elements + * given in the 'selectedElements' set should appear in the final, sorted set. + * If true, the elements in this set are not selected + * from the population and are removed from the results before the final + * set is returned; if false, the elements in the set are + * not selected from the population but are included in the result set before + * it is shuffled and returned. + * + * @tparam T the type of the elements to be selected (i.e. the agent type) + * @tparam I the type of the iterator to be used to pass through the population + * to be sampled + */ +template +void selectNElementsInRandomOrder(I iteratorStart, I iteratorEnd, int count, std::vector& selectedElements, bool remove = false){ + selectNElementsInRandomOrder(iteratorStart, countOf(iteratorStart, iteratorEnd), count, selectedElements, remove); +} + + +} + +#endif /* RANDOM_H_ */ diff --git a/libs/repast_hpc/ReducibleDataSource.h b/libs/repast_hpc/ReducibleDataSource.h new file mode 100644 index 0000000..bfb2887 --- /dev/null +++ b/libs/repast_hpc/ReducibleDataSource.h @@ -0,0 +1,114 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * ReducibleDataSource.h + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#ifndef REDUCEABLEDATASOURCE_H_ +#define REDUCEABLEDATASOURCE_H_ + +#include +#include + +#include "TDataSource.h" +#include "SVDataSource.h" +#include "Variable.h" +#include "RepastProcess.h" + +namespace repast { + +/** + * Source of data and a reduction operation. Used internally by a SVDataSet to + * store the data sources. their associated ops etc. + */ +template +class ReducibleDataSource : public SVDataSource { + +private: + bool dirty; + +protected: + Op _op; + std::vector data; + TDataSource* _dataSource; + int rank; + +public: + ReducibleDataSource(std::string name, TDataSource* dataSource, Op op); + ~ReducibleDataSource(); + + virtual void record(); + virtual void write(Variable* var); + virtual SVDataSource::DataType type() const { + return data_type_traits::data_type(); + } +}; + +template +ReducibleDataSource::ReducibleDataSource(std::string name, TDataSource* dataSource, Op op) : SVDataSource(name), dirty(false), _op(op), +_dataSource(dataSource) { + rank = RepastProcess::instance()->rank(); +}; + +template +ReducibleDataSource::~ReducibleDataSource() { + delete _dataSource; +} + +template +void ReducibleDataSource::record() { + data.push_back(_dataSource->getData()); +} + +template +void ReducibleDataSource::write(Variable* var) { + boost::mpi::communicator* comm = RepastProcess::instance()->getCommunicator(); + if (rank == 0) { + size_t size = data.size(); + T* results = new T[size]; + reduce(*comm, &data[0], size, results, _op, 0); + var->insert(results, size); + delete[] results; + } else { + reduce(*comm, &data[0], data.size(), _op, 0); + } + data.clear(); +} + +} + +#endif /* REDUCEABLEDATASOURCE_H_ */ diff --git a/libs/repast_hpc/RelativeLocation.cpp b/libs/repast_hpc/RelativeLocation.cpp new file mode 100644 index 0000000..da8da55 --- /dev/null +++ b/libs/repast_hpc/RelativeLocation.cpp @@ -0,0 +1,259 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * RelativeLocation.cpp + * + * Created on: July 28, 2008 + * Author: jtm + */ +#include +#include + +#include "RelativeLocation.h" +#include "RepastProcess.h" // DEBUGGING ONLY + +using namespace std; + +namespace repast { + + +int RelativeLocation::getDirectionIndex(vector dirVec){ + RelativeLocation baseline(dirVec.size()); + for(int i = 0; i < dirVec.size(); i++){ + int original = dirVec[i]; + dirVec[i] = original < 0 ? -1 : original == 0 ? 0 : 1; + } + return baseline.getIndex(dirVec); +} + +int RelativeLocation::getReverseDirectionIndex(vector dirVec){ + RelativeLocation baseline(dirVec.size()); + for(int i = 0; i < dirVec.size(); i++){ + int original = dirVec[i]; + dirVec[i] = original > 0 ? -1 : original == 0 ? 0 : 1; + } + return baseline.getIndex(dirVec); +} + +void RelativeLocation::setPlaces(){ + int v = 1; + for(size_t i = 0; i < countOfDimensions; i++){ + places.push_back(v); + v *= (maxima[i] - minima[i] + 1); + } +} + +RelativeLocation::RelativeLocation(int dimensions): countOfDimensions(dimensions), maxIndex(-1), indexOfCenter(-1){ + for(int i = 0; i < dimensions; i++){ + currentValue.push_back(-1); + minima.push_back(-1); + maxima.push_back(1); + } + setPlaces(); +} + +RelativeLocation::RelativeLocation(vector minimaVals, vector maximaVals): maxIndex(-1), indexOfCenter(-1){ + countOfDimensions = minimaVals.size() < maximaVals.size() ? minimaVals.size() : maximaVals.size(); + for(int i = 0; i < countOfDimensions; i++){ + currentValue.push_back(minimaVals[i]); + minima.push_back(minimaVals[i]); + maxima.push_back(maximaVals[i] >= minimaVals[i] ? maximaVals[i]: minimaVals[i]); + } + setPlaces(); +} + +RelativeLocation::RelativeLocation(const RelativeLocation& original): + countOfDimensions(original.countOfDimensions), + currentValue(original.currentValue), + minima(original.minima), + maxima(original.maxima), + places(original.places), + maxIndex(original.maxIndex), + indexOfCenter(original.indexOfCenter){} + +void RelativeLocation::translate(vector displacement){ + int otherSize = displacement.size(); + int dims = countOfDimensions < otherSize ? countOfDimensions : otherSize; + for(int i = 0; i < dims; i++){ + minima[i] += displacement[i]; + maxima[i] += displacement[i]; + currentValue[i] += displacement[i]; + } +} + + +RelativeLocation::~RelativeLocation(){} + +bool RelativeLocation::increment(){ + int i = 0; + bool addNext = true; + while((addNext) && (i < countOfDimensions)){ + currentValue[i] = currentValue[i] + 1; + if(currentValue[i] <= maxima[i]) addNext = false; + else currentValue[i] = minima[i]; + i++; + } + if(addNext == true){ + currentValue.clear(); + currentValue.insert(currentValue.begin(), minima.begin(), minima.end()); + return false; + } + else return true; +} + +bool RelativeLocation::increment(bool skipZero){ + if(!skipZero) return increment(); // Silly to call this with 'false' + int i = 0; + bool addNext = true; + while((addNext) && (i < countOfDimensions)){ + currentValue[i] = currentValue[i] + 1; + if(currentValue[i] <= maxima[i]) addNext = false; + else currentValue[i] = minima[i]; + i++; + } + if(addNext == true){ // Rolled over + currentValue.clear(); + currentValue.insert(currentValue.begin(), minima.begin(), minima.end()); + return false; + } + else{ + return (validNonCenter() ? true : increment()); // If it's zero, increment again before returning + } +} + + + +bool RelativeLocation::set(vector newValues){ + if(newValues.size() != countOfDimensions) return false; + for(size_t i = 0; i < countOfDimensions; i++){ + if(newValues[i] < minima[i] || newValues[i] > maxima[i]) return false; + } + currentValue.clear(); + currentValue.insert(currentValue.begin(), newValues.begin(), newValues.end()); + return true; +} + +bool RelativeLocation::equals(RelativeLocation other){ + size_t n = currentValue.size() <= other.currentValue.size() ? currentValue.size() : other.currentValue.size(); + for(size_t i = 0; i < n; i++) if(currentValue[i] != other.currentValue[i]) return false; + return true; +} + +vector RelativeLocation::getCurrentValue(){ + return vector(currentValue); +} + +int RelativeLocation::operator[](int index){ + return (index > countOfDimensions || index < 0) ? 0 : currentValue[index]; +} + +int RelativeLocation::getCountOfDimensions(){ + return countOfDimensions; +} + +int RelativeLocation::getMaxIndex(){ + if(maxIndex > -1) return maxIndex; + int index = 1; + for(size_t i = 0; i < minima.size(); i++) index *= (maxima[i] - minima[i] + 1); + maxIndex = index - 1; + return maxIndex; +} + +int RelativeLocation::getTotalValues(){ + return getMaxIndex() + 1; +} + +int RelativeLocation::getIndex(vector value){ + if(value.size() != countOfDimensions) return -1; + int index = 0; + for(int i = countOfDimensions - 1; i >= 0; i--){ + if(value[i] < minima[i] || value[i] > maxima[i]) return -1; // Error + index += (value[i] - minima[i]) * places[i]; + } + return index; +} + +int RelativeLocation::getIndex(){ + return getIndex(currentValue); +} + +int RelativeLocation::getIndexOfCenter(){ + if(indexOfCenter != -1) return indexOfCenter; + vector center; + center.assign(countOfDimensions, 0); + indexOfCenter = getIndex(center); + return indexOfCenter; +} + +bool RelativeLocation::validNonCenter(){ + for(size_t i = 0; i < countOfDimensions; i++) if(currentValue[i] != 0) return true; + return false; +} + +int RelativeLocation::getMinimumAt(int index){ + return minima[index]; +} + +int RelativeLocation::getMaximumAt(int index){ + return maxima[index]; +} + +RelativeLocation RelativeLocation::trim(RelativeLocation toBeTrimmed){ + bool isValid = true; + int dims = toBeTrimmed.countOfDimensions < countOfDimensions ? toBeTrimmed.countOfDimensions : countOfDimensions; + vector newMinima; + vector newMaxima; + for(int i = 0; i < dims; i++){ + int originalMin = toBeTrimmed.getMinimumAt(i); + int originalMax = toBeTrimmed.getMaximumAt(i); + isValid = isValid && originalMax >= minima[i] && originalMin <= maxima[i]; + newMinima.push_back(originalMin >= minima[i] ? originalMin : minima[i]); + newMaxima.push_back(originalMax <= maxima[i] ? originalMax : maxima[i]); + } + if(!isValid){ + vector dummy; + dummy.assign(toBeTrimmed.countOfDimensions, 0); + RelativeLocation ret(dummy, dummy); + return ret; + } + RelativeLocation ret(newMinima, newMaxima); + return ret; +} + +std::string RelativeLocation::report(){ + std::stringstream ret; + for(int i = 0; i < countOfDimensions; i++) ret << currentValue[i] << (i < (countOfDimensions - 1) ? " " : ""); + return ret.str(); +} +} diff --git a/libs/repast_hpc/RelativeLocation.h b/libs/repast_hpc/RelativeLocation.h new file mode 100644 index 0000000..a836840 --- /dev/null +++ b/libs/repast_hpc/RelativeLocation.h @@ -0,0 +1,284 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * RelativeLocation.h + * + * Created on: July 28, 2015 + * Author: jtm + */ + +#ifndef RELATIVELOCATION_H_ +#define RELATIVELOCATION_H_ + +#include + +using namespace std; + +namespace repast { + +/** + * A RelativeLocation is a vector of integers that + * express a relative location in a coordinate system; this + * is generally used in an MPI Cartesian Topology. A vector + * might look like: + * + * [ -1, -1, 1 ] + * + * which would indicate one unit to the 'left' in the x dimension, + * one to the 'left' in the y dimension, and one to the 'right' + * in the z dimension. + * + * This class can be 'incremented' so that it advances + * through a series of relative locations in a fixed and + * predictable order. The boundaries can be customized + * so that the range of possible values in any dimension + * is settable; by default it is [-1, 1] (inclusive) in + * all dimensions, meaning, roughly, 'left, center, right'. + * However, different values can be used, e.g.: + * + * minima: [-2, -1, -2] + * maxima: [1, 2, 3] + * + * This would describe a volume of space ranging from -2 to 1 + * in the x dimension, -1 to 2 in the y dimension, and -2 to 3 + * in the z dimension. A typical use when incrementing would + * be to start at the lowest value [-2, -1, -2] and loop + * through all possible variants: + * + * [ -2, -1, -2 ] + * [ -1, -1, -2 ] + * [ 0, -1, -2 ] + * [ 1, -1, -2 ] + * [ -2, 0, -2 ] + * [ -1, 0, -2 ] + * [ 0, 0, -2 ] + * [ 1, 0, -2 ] + * [ -2, 1, -2 ] + * [ -1, 1, -2 ] + * [ 0, 1, -2 ] + * [ 1, 1, -2 ] + * [ -2, 2, -2 ] + * [ -1, 2, -2 ] + * [ 0, 2, -2 ] + * [ 1, 2, -2 ] + * [ -2, -1, -1 ] + * [ -1, -1, -1 ] + * [ 0, -1, -1 ] + * [ 1, -1, -1 ] + * ... + * [ 0, 2, 3 ] + * [ 1, 2, 3 ] + */ +class RelativeLocation{ + +public: + /** + * Assumes that the vector given can be reduced to a 'unit' vector + * (in which all values are either -1, 0, or 1); returns the index + * value of this vector assuming a RelativeLocation with + * minima of -1, -,1 -1, ... and maxima of 1, 1, 1. + */ + static int getDirectionIndex(vector dirVec); + + /** + * Assumes that the vector given can be reduced to a 'unit' vector + * (in which all values are either -1, 0, or 1); returns the index + * value of the negative of this vector (all values multiplied + * by -1), assuming a RelativeLocation with + * minima of -1, -,1 -1, ... and maxima of 1, 1, 1. + */ + static int getReverseDirectionIndex(vector dirVec); + +private: + vector currentValue; + vector minima; + vector maxima; + int countOfDimensions; + vector places; + int maxIndex; // Memoize + int indexOfCenter; // Memoize + + void setPlaces(); + +public: + + /** + * Default constructor; min values are all -1, + * max values are all 1, for the specified number + * of dimensions + */ + RelativeLocation(int dimensions); + + /** + * Constructor that takes specific minima and maxima. + * Current Value is set to the minima + * Note that if any maximum value is less than the + * corresponding minimum value, it will be reset + * to match the minimum value. If the minima and maxima + * vectors are of different sizes, the shorter vector + * size is used and the extra values in the longer + * one are ignored. + */ + RelativeLocation(vector minima, vector maxima); + + /** + * Copy Constructor + */ + RelativeLocation(const RelativeLocation& original); + + void translate(vector displacement); + + virtual ~RelativeLocation(); + + /* + * Increments this RelativeLocation instance. + * Returns false if the instance is already at its + * maximum (but will also roll over to minimum values!) + */ + bool increment(); + + bool increment(bool skipZero); + + + /** + * Set the current value. Will return false + * if the number of dimensions does not match the existing + * value, or if any of the new values are out of the + * ranges defined by the minima and maxima vectors. + */ + bool set(vector newValues); + + /** + * Returns true if the current value for the other + * instance is equal to this one; does NOT + * compare minima or maxima. Note: if the two + * instances are different sizes, only the + * first N values are compared, where N is + * the size of the shorter instance. + */ + bool equals(RelativeLocation other); + + /** + * Gets the array of current values + */ + vector getCurrentValue(); + + /** + * Gets the current value at the specified index + */ + int operator[](int index); + + /** + * Returns the count of dimensions + */ + int getCountOfDimensions(); + + /** + * Gets the maximum index value; this will be one + * less than the total number of values + */ + int getMaxIndex(); + + /** + * Gets the total number of relative location values + * (including the center spot) + */ + int getTotalValues(); + + /** + * Gets the index value associated with the specified + * position + */ + int getIndex(vector value); + + /** + * Gets the index value associated with the current + * position + */ + int getIndex(); + + /** + * Gets the index value of the 0,0,0... position + */ + int getIndexOfCenter(); + + + /** + * Returns false when the values + * are all zeroes, true otherwise + */ + bool validNonCenter(); + + /** + * Gets the lower bound in the + * specified dimension + */ + int getMinimumAt(int index); + + /** Gets the upper bound in the + * specified dimension + * + */ + int getMaximumAt(int index); + + /** + * Returns a new RelativeLocation object + * based on the one passed, but trimmed + * so that it fits within the boundaries + * of this one. + * + * If the result would be invalid (because the + * one to be trimmed falls outside of this + * one) the return value will be a RelativeLocation + * with the original number of dimensions, but + * all zeroes in the values. + * + * Trimming is done only on the first N dimensions, + * where N is the smaller of the number of dimensions + * in this or the passed RelativeLocation; extra + * dimensions are ignored + * + */ + RelativeLocation trim(RelativeLocation toBeTrimmed); + + /** + * Gets a string describing this object for human-readable + * output + */ + std::string report(); +}; + +} + +#endif /* DIFFUSIONLAYERND_H_ */ diff --git a/libs/repast_hpc/RelativeLocationMap.h b/libs/repast_hpc/RelativeLocationMap.h new file mode 100644 index 0000000..fb86e35 --- /dev/null +++ b/libs/repast_hpc/RelativeLocationMap.h @@ -0,0 +1,99 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * RelativeLocationMap.h + * + * Created on: July 28, 2015 + * Author: jtm + */ + +#ifndef RELATIVELOCATIONMAP_H_ +#define RELATIVELOCATION_H_ + +#include "RelativeLocation.h" +using namespace std; + +namespace repast { + +template +class RelativeLocationMap{ +private: + vector* data; + int size; + +public: + RelativeLocationMap(RelativeLocation location); + virtual ~RelativeLocationMap(); + + T& get(int key); + T& get(RelativeLocation location); + T& operator[](RelativeLocation location); + int getSize(); +}; + +template +RelativeLocationMap::RelativeLocationMap(RelativeLocation location){ + size = location.getTotalValues(); + data = new vector(size); +} + +template +RelativeLocationMap::~RelativeLocationMap(){ + delete data; +} + +template +T& RelativeLocationMap::get(int key){ + if(key < 0 || key > size) return 0; + return data[key]; +} + +template +T& RelativeLocationMap::RelativeLocationMap::get(RelativeLocation location){ + return get(location.getIndex()); +} + +template +T& RelativeLocationMap::operator[](RelativeLocation location){ + return data[location.getIndex()]; +} + +template +int RelativeLocationMap::getSize(){ + return size; +} + +} + +#endif /* DIFFUSIONLAYERND_H_ */ diff --git a/libs/repast_hpc/RepastErrors.cpp b/libs/repast_hpc/RepastErrors.cpp new file mode 100644 index 0000000..c533dad --- /dev/null +++ b/libs/repast_hpc/RepastErrors.cpp @@ -0,0 +1,73 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * RepastErrors.cpp + * + * Created on: Feb 21, 2012 + * Author: JTM + */ + +#include "RepastProcess.h" +#include "RepastErrors.h" + +namespace repast { + +std::string err_msg(int idNum, std::string thrown_by, std::string reason, std::string explanation, std::string cause, std::string resolution){ + std::stringstream ss; + ss << "REPAST_ERROR_" << idNum << "\n" + << " Thrown By: " << thrown_by << "\n" + << " On Rank: " << repast::RepastProcess::instance()->rank() << "\n" + << " Reason: " << reason << "\n" + << " Explanation: " << explanation << "\n" + << " Known Causes: " << cause << "\n" + << " Resolution: " << resolution << "\n"; + return ss.str(); +} + +std::string err_msg_omit_rank(int idNum, std::string thrown_by, std::string reason, std::string explanation, std::string cause, std::string resolution){ + std::stringstream ss; + ss << "REPAST_ERROR_" << idNum << "\n" + << " Thrown By: " << thrown_by << "\n" + << " Reason: " << reason << "\n" + << " Explanation: " << explanation << "\n" + << " Known Causes: " << cause << "\n" + << " Resolution: " << resolution << "\n"; + return ss.str(); +} + + + + + +} diff --git a/libs/repast_hpc/RepastErrors.h b/libs/repast_hpc/RepastErrors.h new file mode 100644 index 0000000..bbd792b --- /dev/null +++ b/libs/repast_hpc/RepastErrors.h @@ -0,0 +1,896 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * RepastErrors.h + * + * Created on: Feb 13, 2012 + * Author: JTM + */ + +#ifndef REPAST_ERRORS_H +#define REPAST_ERRORS_H + +#include +#include +#include + +#include + + +namespace repast{ + +std::string err_msg(int idNum, std::string thrown_by, std::string reason, std::string explanation, + std::string cause, std::string resolution); + +std::string err_msg_omit_rank(int idNum, std::string thrown_by, std::string reason, std::string explanation, + std::string cause, std::string resolution); + +template +std::string make_vec_str(const std::vector& v){ + std::stringstream ss; + ss << "[ "; + for(size_t i = 0; i < v.size(); i++) ss << v[i] << ", "; + ss << "]"; + return ss.str(); +} + +template +std::string make_str(const T t){ + std::stringstream ss; + ss << t; + return ss.str(); +} + + +// Some syntactic sugar to make the error definitions clean +#define INVALID_ARG(_VAL) std::invalid_argument(err_msg(_VAL +#define INVALID_ARG_OMIT_RANK(_VAL) std::invalid_argument(err_msg_omit_rank(_VAL +#define DOMAIN_ERR(_VAL) std::domain_error(err_msg(_VAL +#define DOMAIN_ERR_OMIT_RANK(_VAL) std::domain_error(err_msg_omit_rank(_VAL +#define OUT_OF_RANGE(_VAL) std::out_of_range(err_msg(_VAL +#define ERROR_NUMBER +#define THROWN_BY , std::string("") + +#define REASON , std::string("") + +#define EXPLANATION , std::string("") + +#define CAUSE , std::string("") + +#define RESOLUTION , std::string("") + +#define END_ERR )){}}; + +#define VAL(a) (boost::lexical_cast(a)) + + +/* Canonical List of Repast HPC Errors */ + +/* ERROR 1 */ +class Repast_Error_1: public std::domain_error{ +public: + Repast_Error_1(int val): DOMAIN_ERR(ERROR_NUMBER 1) + THROWN_BY "AgentImporter::decrementImportedAgentCount(int exportingRank)" + REASON "'exportingRank' is invalid; value = '" + VAL(val) + "'" + EXPLANATION "The index listing ranks that are exporting their agents to this process does not contain an entry for the specified rank." + CAUSE "An agent request cancellation may have been issued for an agent that was not being exported." + RESOLUTION "Ensure that only agents that have been requested are canceled." +END_ERR + +/* ERROR 2 */ +template +class Repast_Error_2: public std::invalid_argument{ +public: + Repast_Error_2(T agId, std::string projName): INVALID_ARG(ERROR_NUMBER 2) + THROWN_BY "BaseGrid::moveTo(AgentId&, vector newLocation)" + REASON "Agent '" + make_str(agId) + "' is not in space '" + projName + "" + EXPLANATION "The spatial projection that is attempting to move an agent does not contain the specified agent." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + +/* ERROR 3 */ +class Repast_Error_3: public std::invalid_argument{ +public: + Repast_Error_3(int size, int dims): INVALID_ARG(ERROR_NUMBER 3) + THROWN_BY "BaseGrid::moveTo(AgentId&, vector newLocation)" + REASON "'newLocation' is invalid: has " + VAL(size) + " dimensions, while space has " + VAL(dims) + EXPLANATION "The spatial projection is attempting to move an agent to a destination, but the destination is not fully specified;" + + "it has fewer dimensions than the space does." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + +/* ERROR 4 */ +class Repast_Error_4: public std::invalid_argument{ +public: + Repast_Error_4(int numAngles, int dims): INVALID_ARG(ERROR_NUMBER 4) + THROWN_BY "BaseGrid::moveByVector(const T* agent, double distance, const std::vector& anglesInRadians)" + REASON "'anglesInRadians' is invalid: has " + VAL(numAngles) + " angles, while space has " + VAL(dims) + " dimensions" + EXPLANATION "To move by a vector, the number of angles in the vector passed must equal the number of dimensions in the space." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + +/* ERROR 5 */ +class Repast_Error_5: public std::invalid_argument{ +public: + Repast_Error_5(int size, int dims): INVALID_ARG(ERROR_NUMBER 5) + THROWN_BY "BaseGrid::moveByDisplacement(const T* agent, const std::vector& displacement)" + REASON "Size of displacement vector = " + VAL(size) + " but space has " + VAL(dims) + " dimensions." + EXPLANATION "The vector specifying displacement must have the same number of dimensions as the space." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + +/* ERROR 6 */ +template +class Repast_Error_6: public std::invalid_argument{ +public: + Repast_Error_6(T id, std::string spaceName): INVALID_ARG(ERROR_NUMBER 6) + THROWN_BY "BaseGrid::moveByDisplacement(const T* agent, const std::vector& displacement)" + REASON "Agent " + make_str(id) + " is not present in this space " + spaceName + EXPLANATION "The specified agent is not present in the specified space" + CAUSE "The agent has not yet been introduced into the space, or it has been removed." + RESOLUTION "Confirm that the agent being moved should be in the specified space." +END_ERR + +/* ERROR 7 */ +class Repast_Error_7: public std::invalid_argument{ +public: + Repast_Error_7(int P1Dim, int P2Dim): INVALID_ARG(ERROR_NUMBER 7) + THROWN_BY "double BaseGrid::getDistanceSq(const Point& pt1, const Point& pt2)" + REASON "Point P1 has " + VAL(P1Dim) + " dimensions while P2 has " + VAL(P2Dim) + " dimensions" + EXPLANATION "Points for which distance is being calculated must have the same number of dimensions" + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + +/* ERROR 8 */ +class Repast_Error_8: public std::invalid_argument{ +public: + Repast_Error_8(int P1Dim, int P2Dim): INVALID_ARG(ERROR_NUMBER 8) + THROWN_BY "double BaseGrid::getDisplacement(const Point& pt1, const Point& pt2, " + + "std::vector& out))" + REASON "Point P1 has " + VAL(P1Dim) + " dimensions while P2 has " + VAL(P2Dim) + " dimensions" + EXPLANATION "Points for which displacement is being calculated must have the same number of dimensions" + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + +/* ERROR 9 */ +class Repast_Error_9: public std::invalid_argument{ +public: + Repast_Error_9(std::string projName): INVALID_ARG(ERROR_NUMBER 9) + THROWN_BY "Context::addProjection(Projection* projection)" + REASON "The context already contains a projection with the name '" + projName + "'" + EXPLANATION "Names of projections in contexts must be unique; two projections with the " + + "same name cannot be added, nor can the same projection be added twice." + CAUSE "Unknown" + RESOLUTION "Ensure that projections are uniquely named and only added to the context once." +END_ERR + +/* ERROR 10 */ +class Repast_Error_10: public std::invalid_argument{ +public: + Repast_Error_10(int dims): INVALID_ARG(ERROR_NUMBER 10) + THROWN_BY "Grid2DQuery::Grid2DQuery(const Grid* grid)" + REASON "Grid2DQuery can only be used with 2 dimensions; grid has " + VAL(dims) + " dimensions" + EXPLANATION "The query constructor was passed a grid that has the wrong number of dimensions." + CAUSE "Unknown" + RESOLUTION "Ensure that the grids being used with the Grid2DQuery are 2-D grids." +END_ERR + +/* ERROR 11 */ +template +class Repast_Error_11: public std::out_of_range{ +public: + Repast_Error_11(std::vector pt, T dimensions): OUT_OF_RANGE(ERROR_NUMBER 11) + THROWN_BY "void Borders::boundsCheck(const vector& pt) const" + REASON "The point passed (at " + make_vec_str(pt) + " is outside the dimension range being checked (" + make_str(dimensions) + ")." + EXPLANATION "The point passed to the boundary check is outside the permitted range; it probably " + + "falls outside the area of the simulation being managed by this process." + CAUSE "Unknown" + RESOLUTION "Ensure that no agent is moving outside the boundaries of a process and its buffer zone." +END_ERR + +/* ERROR 12 */ +template +class Repast_Error_12: public std::out_of_range{ +public: + Repast_Error_12(std::vector pt, T dimensions): OUT_OF_RANGE(ERROR_NUMBER 12) + THROWN_BY "void Borders::boundsCheck(const vector& pt) const" + REASON "The point passed (at " + make_vec_str(pt) + ") is outside the dimension range being checked (" + make_str(dimensions) + ")." + EXPLANATION "The point passed to the boundary check is outside the permitted range; it probably " + + "falls outside the area of the simulation being managed by this process." + CAUSE "Unknown" + RESOLUTION "Ensure that no agent is moving outside the boundaries of a process and its buffer zone." +END_ERR + + +/* ERROR 13 */ +class Repast_Error_13: public std::invalid_argument{ +public: + Repast_Error_13(std::vector oldPos, std::vector newPos, std::vector displacement): INVALID_ARG(ERROR_NUMBER 13) + THROWN_BY "StrictBorders::translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement)" + REASON "The old position (" + make_vec_str(oldPos) + "), new position (" + make_vec_str(newPos) + ") and " + + "displacement (" + make_vec_str(displacement) + ") must all have the same number of dimensions." + EXPLANATION "Sizes of old position, new position, and displacement vectors do not match " + + "( " + VAL(oldPos.size()) + " vs. " + VAL(newPos.size()) + " vs. " + VAL(displacement.size()) + " )." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + + +/* ERROR 14 */ +class Repast_Error_14: public std::invalid_argument{ +public: + Repast_Error_14(std::vector oldPos, std::vector newPos, std::vector displacement): INVALID_ARG(ERROR_NUMBER 14) + THROWN_BY "StrictBorders::translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement)" + REASON "The old position (" + make_vec_str(oldPos) + "), new position (" + make_vec_str(newPos) + ") and " + + "displacement (" + make_vec_str(displacement) + ") must all have the same number of dimensions." + EXPLANATION "Sizes of old position, new position, and displacement vectors do not match " + + "( " + VAL(oldPos.size()) + " vs. " + VAL(newPos.size()) + " vs. " + VAL(displacement.size()) + " )." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + + +/* ERROR 15 */ +class Repast_Error_15: public std::invalid_argument{ +public: + Repast_Error_15(std::vector oldPos, std::vector newPos, std::vector displacement): INVALID_ARG(ERROR_NUMBER 15) + THROWN_BY "WrapAroundBorders::translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement)" + REASON "The old position (" + make_vec_str(oldPos) + "), new position (" + make_vec_str(newPos) + ") and " + + "displacement (" + make_vec_str(displacement) + ") must all have the same number of dimensions." + EXPLANATION "Sizes of old position, new position, and displacement vectors do not match " + + "( " + VAL(oldPos.size()) + " vs. " + VAL(newPos.size()) + " vs. " + VAL(displacement.size()) + " )." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + + +/* ERROR 16 */ +class Repast_Error_16: public std::invalid_argument{ +public: + Repast_Error_16(std::vector oldPos, std::vector newPos, std::vector displacement): INVALID_ARG(ERROR_NUMBER 16) + THROWN_BY "WrapAroundBorders::translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement)" + REASON "The old position (" + make_vec_str(oldPos) + "), new position (" + make_vec_str(newPos) + ") and " + + "displacement (" + make_vec_str(displacement) + ") must all have the same number of dimensions." + EXPLANATION "Sizes of old position, new position, and displacement vectors do not match " + + "( " + VAL(oldPos.size()) + " vs. " + VAL(newPos.size()) + " vs. " + VAL(displacement.size()) + " )." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + + +/* ERROR 17 */ +class Repast_Error_17: public std::invalid_argument{ +public: + Repast_Error_17(std::vector oldPos, std::vector newPos, std::vector displacement): INVALID_ARG(ERROR_NUMBER 17) + THROWN_BY "StickyBorders::translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement)" + REASON "The old position (" + make_vec_str(oldPos) + "), new position (" + make_vec_str(newPos) + ") and " + + "displacement (" + make_vec_str(displacement) + ") must all have the same number of dimensions." + EXPLANATION "Sizes of old position, new position, and displacement vectors do not match " + + "( " + VAL(oldPos.size()) + " vs. " + VAL(newPos.size()) + " vs. " + VAL(displacement.size()) + " )." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + + +/* ERROR 18 */ +class Repast_Error_18: public std::invalid_argument{ +public: + Repast_Error_18(std::vector oldPos, std::vector newPos, std::vector displacement): INVALID_ARG(ERROR_NUMBER 18) + THROWN_BY "StickyBorders::translate(const std::vector& oldPos, std::vector& newPos, const std::vector& displacement)" + REASON "The old position (" + make_vec_str(oldPos) + "), new position (" + make_vec_str(newPos) + ") and " + + "displacement (" + make_vec_str(displacement) + ") must all have the same number of dimensions." + EXPLANATION "Sizes of old position, new position, and displacement vectors do not match " + + "( " + VAL(oldPos.size()) + " vs. " + VAL(newPos.size()) + " vs. " + VAL(displacement.size()) + " )." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + + +/* ERROR 19 */ +class Repast_Error_19: public std::invalid_argument{ +public: + Repast_Error_19(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 19) + THROWN_BY "createDblUni(string& name, vector& params)" + REASON "Creating double_uniform distribution '" + name + "' requires the type of distributon " + + "plus two additional parameters (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of a double_uniform distribution; " + + "this should have been in the form\n\n: distribution." + name + " = double_uniform,FROM,TO\n\n" + + "where 'FROM' is the lower boundary for the distribution and 'TO' is the upper boundary. The error occurs "+ + "because " + VAL(params.size()) + " is too few or too many parameters." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + +/* ERROR 20 */ +class Repast_Error_20: public std::invalid_argument{ +public: + Repast_Error_20(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 20) + THROWN_BY "createIntUni(string& name, vector& params)" + REASON "Creating int_uniform distribution '" + name + "' requires the type of distributon " + + "plus two additional parameters (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of an int_uniform distribution; " + + "this should have been in the form\n\n: distribution." + name + " = int_uniform,FROM,TO\n\n" + + "where 'FROM' is the lower boundary for the distribution and 'TO' is the upper boundary. The error occurs "+ + "because " + VAL(params.size()) + " is too few or too many parameters." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + +/* ERROR 21 */ +class Repast_Error_21: public std::invalid_argument{ +public: + Repast_Error_21(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 21) + THROWN_BY "createTriangle(string& name, vector& params)" + REASON "Creating triangle distribution '" + name + "' requires the type of distributon " + + "plus three additional parameters (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of a triangle distribution; " + + "this should have been in the form\n\n: distribution." + name + " = int_uniform,LOWER,MOST_LIKELY,UPPER\n\n" + + "where 'LOWER' is the lower boundary for the distribution, 'MOST_LIKELY' is the most likely value, " + + "and 'UPPER' is the upper boundary. The error occurs "+ + "because " + VAL(params.size()) + " is too few or too many parameters." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + +/* ERROR 22 */ +class Repast_Error_22: public std::invalid_argument{ +public: + Repast_Error_22(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 22) + THROWN_BY "createCauchy(string& name, vector& params)" + REASON "Creating Cauchy distribution '" + name + "' requires the type of distributon " + + "plus two additional parameters (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of a Cauchy distribution; " + + "this should have been in the form\n\n: distribution." + name + " = cauchy,MEDIAN,SIGMA\n\n" + + "where 'MEDIAN' is the median value in the distribution and 'SIGMA' is the sigma" + + "parameter. The error occurs because " + VAL(params.size()) + " is too few or too many parameters." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + +/* ERROR 23 */ +class Repast_Error_23: public std::invalid_argument{ +public: + Repast_Error_23(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 23) + THROWN_BY "createExponential(string& name, vector& params)" + REASON "Creating Exponential distribution '" + name + "' requires the type of distributon " + + "plus one additional parameter (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of an exponential distribution; " + + "this should have been in the form\n\n: distribution." + name + " = exponential,LAMBDA\n\n" + + "where 'LAMBDA' is the lambda parameter. The error occurs because " + + VAL(params.size()) + " is too few or too many parameters." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + +/* ERROR 24 */ +class Repast_Error_24: public std::invalid_argument{ +public: + Repast_Error_24(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 24) + THROWN_BY "createNormal(string& name, vector& params)" + REASON "Creating Normal distribution '" + name + "' requires the type of distributon " + + "plus two additional parameters (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of a normal distribution; " + + "this should have been in the form\n\n: distribution." + name + " = normal,MEAN,SIGMA\n\n" + + "where 'MEAN' is the mean value and 'SIGMA' is the sigma parameter. The error occurs because " + + VAL(params.size()) + " is too few or too many parameters." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + +/* ERROR 25 */ +class Repast_Error_25: public std::invalid_argument{ +public: + Repast_Error_25(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 25) + THROWN_BY "createLogNormal(string& name, vector& params)" + REASON "Creating Log Normal distribution '" + name + "' requires the type of distributon " + + "plus two additional parameters (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of a log normal distribution; " + + "this should have been in the form\n\n: distribution." + name + " = lognormal,MEAN,SIGMA\n\n" + + "where 'MEAN' is the mean value and 'SIGMA' is the sigma parameter. The error occurs because " + + VAL(params.size()) + " is too few or too many parameters." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + +/* ERROR 26 */ +class Repast_Error_26: public std::invalid_argument{ +public: + Repast_Error_26(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 26) + THROWN_BY "void initializeRandom(const Properties& props, boost::mpi::communicator* comm)" + REASON "Creating a distribution requires at least a distribution name (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of random distribution; " + + "this should have been in the form\n\n: distribution." + name + " = TYPE,param1,param2\n\n" + + "where 'TYPE' is the type of distribution and param1, param2, etc. are the parameters needed " + + "to generate that distribution (if any). The error occurs because too few parameters are specified." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + +/* ERROR 27 */ +class Repast_Error_27: public std::invalid_argument{ +public: + Repast_Error_27(std::string name, std::vector params): INVALID_ARG(ERROR_NUMBER 27) + THROWN_BY "void initializeRandom(const Properties& props, boost::mpi::communicator* comm)" + REASON "Creating a distribution requires the name of a known distribution (given " + make_vec_str(params) + ")" + EXPLANATION "A value in the properties specifications requested the creation of random distribution; " + + "this should have been in the form\n\n: distribution." + name + " = TYPE,param1,param2\n\n" + + "where 'TYPE' is the type of distribution and param1, param2, etc. are the parameters needed " + + "to generate that distribution (if any). The error occurs because the TYPE value is " + + "not a kind of distribution that is recognized." + CAUSE "Generally this is a problem in the properties file or in the command line arguments." + RESOLUTION "Alter the properties specification to provide the correct number of arguments." +END_ERR + + + +/* ERROR 28 */ +class Repast_Error_28: public std::domain_error{ +public: + Repast_Error_28(): DOMAIN_ERR(ERROR_NUMBER 28) + THROWN_BY "SVDataSet::record()" + REASON "Attempted to record to a data set that is not open" + EXPLANATION "Closed data sets cannot record" + CAUSE "Usually this is an error in the scheduling of data collection." + RESOLUTION "Ensure that no scheduled event calling 'record' occurs after the data set is closed." +END_ERR + + +/* ERROR 29 */ +class Repast_Error_29: public std::domain_error{ +public: + Repast_Error_29(): DOMAIN_ERR(ERROR_NUMBER 29) + THROWN_BY "SVDataSet::write()" + REASON "Attempted to write a data set that is not open" + EXPLANATION "Closed data sets cannot write" + CAUSE "Usually this is an error in the scheduling of data collection." + RESOLUTION "Ensure that no scheduled event calling 'write' occurs after the data set is closed." +END_ERR + + +/* ERROR 30 */ +class Repast_Error_30: public std::invalid_argument{ +public: + Repast_Error_30(int rank): INVALID_ARG(ERROR_NUMBER 30) + THROWN_BY "SharedNetwork::removeSender(int rank) " + REASON "Sender being removed (" + VAL(rank) + ") is not in the list of senders." + EXPLANATION "Repast HPC is attempting to decrement the count of agents being exported " + + "from each rank by the SharedNetwork, but the rank specified does not have" + + "any value associated with it (equivalent to the rank 'sending' zero agents" + + "already." + CAUSE "Unknown" + RESOLUTION "Confirm that all network functions (including creation of links and moving agents) " + + "are being called only when agents are actually being added or removed from the " + + "local network." +END_ERR + + +/* ERROR 31 */ +template +class Repast_Error_31: public std::invalid_argument{ +public: + Repast_Error_31(T id): INVALID_ARG(ERROR_NUMBER 31) + THROWN_BY "SharedContext::decrementProjRefCount(const AgentId& id)" + REASON "Id '" + make_str(id) + "' is not contained in the map of projections and references" + EXPLANATION "An attempt was made to remove a non-local agent from a projection, but the agent " + + "was not present in the map of non-local agents and their projection reference counts." + CAUSE "Possibly this reflects a borrowed agent being moved or cancelled more times than" + + "it was requested." + RESOLUTION "Ensure that each non-local agent is removed/cancelled only once." +END_ERR + + +/* ERROR 32 */ +template +class Repast_Error_32: public std::domain_error{ +public: + Repast_Error_32(T id): DOMAIN_ERR(ERROR_NUMBER 32) + THROWN_BY "RepastProcess::syncAgentStatus(SharedContext& context, Provider& provider, AgentCreator& creator)" + REASON "The process has received information about an agent (" + make_str(id) + ") that does not exist on this process." + EXPLANATION "A process has sent information to this process that could be used to update that agent's " + + "status, but the agent does not exist on this process." + CAUSE "A problem with the balance of agent requests has occurred, or a non-local agent has been removed from " + + "this process without notifying the other processes that its information need no longer be sent." + RESOLUTION "Ensure that all changes to borrowed agents are appropriate and reconciled with their home " + "processes." +END_ERR + + +/* ERROR 33 */ +class Repast_Error_33: public std::domain_error{ +public: + Repast_Error_33(): DOMAIN_ERR(ERROR_NUMBER 33) + THROWN_BY "SVDataSetBuilder& SVDataSetBuilder::addDataSource(SVDataSource* source)" + REASON "Cannot call 'addDataSource' after Data Set Builder has been used to construct a dataset" + EXPLANATION "The SVDataSetBuilder should be used to construct exactly on SVDataSet. The instance " + + "of SVDataSetBuilder should be used in a sequence: first the instance is created, then" + + "data sources are added (using addDataSource), then the data set is created (using " + + "'createDataSet'), containing all of the data sources added. This error is thrown when " + + "'addDataSource' is called after 'createDataSet' has already returned the Data Set being " + + "built." + CAUSE "Generally this is a problem in model initialization or scheduling of initialization events." + RESOLUTION "Ensure that the proper sequence of instructions is used to create the SVDataSet." +END_ERR + + +/* ERROR 34 */ +class Repast_Error_34: public std::domain_error{ +public: + Repast_Error_34(): DOMAIN_ERR(ERROR_NUMBER 34) + THROWN_BY "SVDataSetBuilder::createDataSet()" + REASON "This function is being called for a second time, but can only be called once." + EXPLANATION "An instance of the SVDataSetBuilder class should be used exactly once, to create a " + + "single SVDataSet instance. This error occurs when the 'createDataSet' method is called " + + "for a second time on the same SVDataSetBuilder instance." + CAUSE "Generally this is a problem in model initialization or scheduling of initialization events." + RESOLUTION "Ensure that the proper sequence of instructions is used to create the SVDataSet." +END_ERR + + +/* ERROR 35 */ +template +class Repast_Error_35: public std::invalid_argument{ +public: + Repast_Error_35(T pt1, T pt2): INVALID_ARG(ERROR_NUMBER 35) + THROWN_BY "Point::add(const Point &pt)" + REASON "The two points being added do not have the same number of dimensions " + + "(" + make_str(pt1) + " vs. " + make_str(pt2) + ")" + EXPLANATION "Point addition can only be done if the two points have the same number of dimensions." + CAUSE "Unknown" + RESOLUTION "Ensure that all points use the same number of dimensions." +END_ERR + + +/* Error 36 */ +template +class Repast_Error_36: public std::invalid_argument{ +public: + Repast_Error_36(T origin, T dimensions, int originDimCount, int dimensionsDimCount): INVALID_ARG(ERROR_NUMBER 36) + THROWN_BY "GridDimensions::GridDimensions(Point origin, Point dimensions)" + REASON "Number of dimensions in origin (" + make_str(origin) + " = " + VAL(originDimCount) + ") " + + "does not equal number of dimensions in dimension specification (" + make_str(dimensions) + " = " + + VAL(dimensionsDimCount) + ")" + EXPLANATION "When creating a grid the number of dimensions used to specify the origin and the number of dimensions" + + "used to specify the extents must be commensurate." + CAUSE "Improper model initialization" + RESOLUTION "Reconcile the number of dimensions used." +END_ERR + +/* Error 37 */ +class Repast_Error_37: public std::invalid_argument{ +public: + Repast_Error_37(const std::vector point, int dimensionsDimCount): INVALID_ARG(ERROR_NUMBER 37) + THROWN_BY "GridDimensions::contains(const std::vector& pt)" + REASON "Number of dimensions in point (" + make_vec_str(point) + " = " + VAL(point.size()) + ") " + + "does not equal number of dimensions in the grid (" + VAL(dimensionsDimCount) + ")" + EXPLANATION "The number of dimensions in the point to be found must be the same as the number " + + "of dimensions in the grid." + CAUSE "Unknown" + RESOLUTION "Reconcile the number of dimensions used." +END_ERR + + +/* Error 38 */ +class Repast_Error_38: public std::invalid_argument{ +public: + Repast_Error_38(const std::vector point, int dimensionsDimCount): INVALID_ARG(ERROR_NUMBER 38) + THROWN_BY "GridDimensions::contains(const std::vector& pt)" + REASON "Number of dimensions in point (" + make_vec_str(point) + " = " + VAL(point.size()) + ") " + + "does not equal number of dimensions in the grid (" + VAL(dimensionsDimCount) + ")" + EXPLANATION "The number of dimensions in the point to be found must be the same as the number " + + "of dimensions in the grid." + CAUSE "Unknown" + RESOLUTION "Reconcile the number of dimensions used." +END_ERR + + +/* Error 39 */ +class Repast_Error_39: public std::domain_error{ +public: + Repast_Error_39(): DOMAIN_ERR_OMIT_RANK(ERROR_NUMBER 39) + THROWN_BY "RepastProcess* RepastProcess::instance()" + REASON "RepastProcess must be initialized before calling instance" + EXPLANATION "The instance of RepastProcess is not created until RepastProcess::init() is called" + CAUSE "Improper model initialization" + RESOLUTION "Call RepastProcess::init() before attempting to access the instance of RepastProcess" +END_ERR + + +/* Error 40 */ +template +class Repast_Error_40: public std::domain_error{ +public: + Repast_Error_40(T id, int rank, int dest1, int dest2): DOMAIN_ERR(ERROR_NUMBER 40) + THROWN_BY "RepastProcess::moveAgent(const AgentId& id, int process)" + REASON "An attempt was made to move agent " + make_str(id) + " to two different processes " + + "during a single iteration (from rank " + VAL(rank) + " to " + VAL(dest2) + " but already " + + "moved to " + VAL(dest1) + EXPLANATION "An agent can be moved from one process to another, but not from one process to two other " + + "processes; this error occurs when such an attempt is made." + CAUSE "Improper algorithm for agent movement." + RESOLUTION "Ensure than an agent moves once, then cannot be moved again." +END_ERR + + +/* Error 41 */ +class Repast_Error_41: public std::invalid_argument{ +public: + Repast_Error_41(int max, int actual, std::string name): INVALID_ARG_OMIT_RANK(ERROR_NUMBER 41) + THROWN_BY "ConfigLexer::ConfigLexer(const string& file_name, boost::mpi::communicator* comm, int maxConfigFileSize)" + REASON "The properties file '" + name + " has an actual file size of " + VAL(actual) + + ", which exceeds the size passed as a maximum (" + VAL(max) + ")" + EXPLANATION "The implementation has chosen to provide a maximum file size for the properties file, which " + + "is used to facilitate the transfer of the file across processes; however, the actual size of " + + "the file is too large, meaning that the file could only be transferred incompletely. " + + "Note: the actual limit is max size provided - 1" + CAUSE "The file size specified is too small, or the properties file too large." + RESOLUTION "Provide a larger file size or shrink the properties file" +END_ERR + + +/* Error 42 */ +class Repast_Error_42: public std::invalid_argument{ +public: + Repast_Error_42(std::string configFileName): INVALID_ARG_OMIT_RANK(ERROR_NUMBER 42) + THROWN_BY "ConfigLexer::ConfigLexer(const string& file_name, boost::mpi::communicator* comm, int maxConfigFileSize)" + REASON "The file '" + configFileName + "' was not found." + EXPLANATION "The file to be used for configuring the logger was not found." + CAUSE "The file name may have been specified incorrectly, or the file is not present." + RESOLUTION "Ensure that the file name matches an existing configuration file." +END_ERR + + +/* Error 43 */ +class Repast_Error_43: public std::invalid_argument{ +public: + Repast_Error_43(std::string file_name): INVALID_ARG_OMIT_RANK(ERROR_NUMBER 43) + THROWN_BY "ConfigLexer::ConfigLexer(const string& file_name, boost::mpi::communicator* comm, int maxConfigFileSize)" + REASON "An unknown error occurred while reading the config file '" + file_name +"'" + EXPLANATION "The file could not be read." + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + +/* Error 44 */ +class Repast_Error_44: public std::invalid_argument{ +public: + Repast_Error_44(std::string val): INVALID_ARG(ERROR_NUMBER 44) + THROWN_BY "strToUInt(const string& val)" + REASON "String '" + val + "' cannot be converted to unsigned int" + EXPLANATION "The value represented by the string cannot be converted to an unsigned integer." + CAUSE "Generally this is caused by improper values in properties files or command-line properties." + RESOLUTION "Revise the string value to represent a valid unsigned int" +END_ERR + + +/* Error 45 */ +class Repast_Error_45: public std::invalid_argument{ +public: + Repast_Error_45(std::string val): INVALID_ARG(ERROR_NUMBER 45) + THROWN_BY "strToInt(const string& val)" + REASON "String '" + val + "' cannot be converted to int" + EXPLANATION "The value represented by the string cannot be converted to an integer." + CAUSE "Generally this is caused by improper values in properties files or command-line properties." + RESOLUTION "Revise the string value to represent a valid int" +END_ERR + + +/* Error 46 */ +class Repast_Error_46: public std::invalid_argument{ +public: + Repast_Error_46(std::string val): INVALID_ARG(ERROR_NUMBER 46) + THROWN_BY "strToDoublet(const string& val)" + REASON "String '" + val + "' cannot be converted to double" + EXPLANATION "The value represented by the string cannot be converted to a double." + CAUSE "Generally this is caused by improper values in properties files or command-line properties." + RESOLUTION "Revise the string value to represent a valid double" +END_ERR + + + +/* Error 47 */ +template +class Repast_Error_47: public std::out_of_range{ +public: + Repast_Error_47(int matrixDimensions, int pointDimensions, T index): OUT_OF_RANGE(ERROR_NUMBER 47) + THROWN_BY "Matrix::boundsCheck(const Point& index)" + REASON "Number of dimensions in index point (" + make_str(index) + " = " + VAL(pointDimensions) + ") does not equal matrix dimensions (" + std::to_string(matrixDimensions) + ")" + EXPLANATION "The dimensions of the index point must match those of the matrix to check boundaries" + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + + +/* Error 48 */ +template +class Repast_Error_48: public std::out_of_range{ +public: + Repast_Error_48(int dimensionIndex, int pointValue, T index, int size): OUT_OF_RANGE(ERROR_NUMBER 48) + THROWN_BY "Matrix::boundsCheck(const Point& index)" + REASON "Value of coordinate " + VAL(dimensionIndex) + " in point " + make_str(index) + " is " + VAL(pointValue) + " which is outside range 0 - " + VAL(size) + EXPLANATION "The point must specify a valid entry in the matrix; the values that make up the point must " + + "be greater than zero and less than the size of the matrix on the given dimension" + CAUSE "Unknown" + RESOLUTION "Unknown" +END_ERR + + +/* Error 49 */ +template +class Repast_Error_49: public std::invalid_argument{ +public: + Repast_Error_49(size_t dims, T gridDimensions): INVALID_ARG(ERROR_NUMBER 49) + THROWN_BY "SharedBaseGrid::SharedBaseGrid(std::string name, " + + "GridDimensions gridDims, std::vector processDims, int buffer, boost::mpi::communicator* comm)" + REASON "Invalid number of grid dimensions (" + VAL (dims) + "): " + make_str(gridDimensions) + EXPLANATION "SharedBaseGrid can only use 1- or 2-dimensional grids" + CAUSE "Improper model construction" + RESOLUTION "Modify the grid to be only 1 or 2 dimensions" +END_ERR + + +/* Error 50 */ +template +class Repast_Error_50: public std::invalid_argument{ +public: + Repast_Error_50(size_t dims, T gridDimensions, int processDims): INVALID_ARG(ERROR_NUMBER 50) + THROWN_BY "SharedBaseGrid::SharedBaseGrid(std::string name, " + + "GridDimensions gridDims, std::vector processDims, int buffer, boost::mpi::communicator* comm)" + REASON "Invalid number of grid dimensions (" + VAL (dims) + "): " + make_str(gridDimensions) + " " + "Does not match number of process dimensions (" + VAL(processDims) + ")" + EXPLANATION "SharedBaseGrid dimensions must match number of process dimensions" + CAUSE "Improper model construction, or improper process topology" + RESOLUTION "Modify the grid or the process toplogy to match" +END_ERR + + +/* Error 51 */ +class Repast_Error_51: public std::invalid_argument{ +public: + Repast_Error_51(int dimCount, int extent, double pCount): INVALID_ARG(ERROR_NUMBER 51) + THROWN_BY "SharedBaseGrid::SharedBaseGrid(std::string name, " + + "GridDimensions gridDims, std::vector processDims, int buffer, boost::mpi::communicator* comm)" + REASON "Number of processes in a given dimension must divide evenly into the extent of that dimension; " + + "In dimension " + VAL(dimCount) + " the extent is " + VAL(extent) + " but the process count is " + VAL(pCount) + EXPLANATION "Repast HPC will try to apportion the total simulation grid among some set of processes; to " + + "achieve this, the grid dimensions of the space must divide evenly among the processes. So, if " + + "the total space is 900 in the x dimension and 900 in the y dimension, and there are 9 processes " + + "in a 3 x 3 grid, this will work, with 300 x 300 on each process. However, if the space is 1000 x 1000 " + + " a 3 x 3 grid cannot be used." + CAUSE "The extent of space and the number of processes in the x and y directions, as specified " + + "in the properties file, are not compatible." + RESOLUTION "Modify the model parameters to make the space evenly divisible by the processes in each " + "dimension." +END_ERR + + + +/* Error 52 */ +class Repast_Error_52: public std::invalid_argument{ +public: + Repast_Error_52(int maxFileSize, int actualSize, std::string name): INVALID_ARG(ERROR_NUMBER 52) + THROWN_BY "Properties::readFile(const std::string& file, boost::mpi::communicator* comm, int maxPropFileSize)" + REASON "Actual file size (" + VAL(actualSize) + ") exceeds specified maximum file size (" + VAL(maxFileSize) + " for Properties file '" + name + "'" + EXPLANATION "A maximum expected file size can be specified to make the sharing of the properties file " + + "among processes more efficient; an error is thrown if the actual file size exceeds this " + + "value. Note: Actual limit is max size - 2" + CAUSE "The actual properties file size is larger than the specified expected maximum" + RESOLUTION "Reduce the size of the properties file or specify a larger maximum" +END_ERR + + + +/* Error 53 */ +class Repast_Error_53: public std::invalid_argument{ +public: + Repast_Error_53(std::string fileName): INVALID_ARG(ERROR_NUMBER 53) + THROWN_BY "Properties::readFile(const std::string& file, boost::mpi::communicator* comm, int maxPropFileSize)" + REASON "Properties file '" + fileName + "' not found." + EXPLANATION "The file specified as the properties file is not present" + CAUSE "The path or name of the file may be incorrect, or the file may be missing" + RESOLUTION "Specify the name of an existing file" +END_ERR + + +/* Error 54 */ +class Repast_Error_54: public std::invalid_argument{ +public: + Repast_Error_54(const std::string line, const std::string file): INVALID_ARG(ERROR_NUMBER 54) + THROWN_BY "Properties::readFile(const std::string& file, boost::mpi::communicator* comm, int maxPropFileSize)" + REASON "Line '" + line + "' in Properties file '" + file + "' does not include '='" + EXPLANATION "Properties must be specified in 'KEY = value' (or 'KEY=value') format" + CAUSE "Properties file contains improperly formatted line." + RESOLUTION "Modify the incorrect line in the properties file." +END_ERR + + +/* Error 55 */ +class Repast_Error_55: public std::invalid_argument{ +public: + Repast_Error_55(const std::string line, const std::string file): INVALID_ARG(ERROR_NUMBER 55) + THROWN_BY "Properties::readFile(const std::string& file, boost::mpi::communicator* comm, int maxPropFileSize)" + REASON "Line '" + line + "' in Properties file '" + file + "' does not include a key before the '='" + EXPLANATION "Properties must be specified in 'KEY = value' (or 'KEY=value') format" + + "both key and value are required." + CAUSE "Properties file contains improperly formatted line" + RESOLUTION "Modify the incorrect line in the properties file." +END_ERR + + +/* Error 56 */ +class Repast_Error_56: public std::invalid_argument{ +public: + Repast_Error_56(const std::string line, const std::string file): INVALID_ARG(ERROR_NUMBER 56) + THROWN_BY "Properties::readFile(const std::string& file, boost::mpi::communicator* comm, int maxPropFileSize)" + REASON "Line '" + line + "' in Properties file '" + file + "' does not include a value after the '='" + EXPLANATION "Properties must be specified in 'KEY = value' (or 'KEY=value') format; " + + "both key and value are required." + CAUSE "Properties file contains improperly formatted line" + RESOLUTION "Modify the incorrect line in the properties file." +END_ERR + +/* Error 57 */ +class Repast_Error_57: public std::invalid_argument{ +public: + Repast_Error_57(): INVALID_ARG(ERROR_NUMBER 57) + THROWN_BY "initializeSeed(Properties& props, boost::mpi::communicator* comm)" + REASON "No communicator provided for use with global 'AUTO' seed." + EXPLANATION "'AUTO' specified for global.random.seed, but initializeSeed(Properties&, boost::mpi::communicator* = 0) is called with no communicator pointer. Automatically generated random seed (from process 0) cannot be shared to all processes." + CAUSE "Properties file cannot use AUTO for global.random.seed with this code" + RESOLUTION "Modify the incorrect line in the properties file, or alter the code to provide a communicator for initializeSeed" +END_ERR + +/* TEMPLATE +class Repast_Error_: public std::invalid_argument{ +public: + Repast_Error_(): INVALID_ARG(ERROR_NUMBER 00) + THROWN_BY "" + REASON "" + EXPLANATION "" + CAUSE "" + RESOLUTION "" +END_ERR +*/ + +} // End namespace + +#endif /* REPAST_ERRORS_H */ diff --git a/libs/repast_hpc/RepastProcess.cpp b/libs/repast_hpc/RepastProcess.cpp new file mode 100644 index 0000000..f776def --- /dev/null +++ b/libs/repast_hpc/RepastProcess.cpp @@ -0,0 +1,328 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * RepastProcess.cpp + * + * Created on: Jan 5, 2009 + * Author: nick + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "RepastProcess.h" +#include "logger.h" +#include "SRManager.h" +#include "Utilities.h" + +BOOST_CLASS_EXPORT_GUID(repast::SpecializedProjectionInfoPacket, + "SpecializedProjectionInfoPacket_DOUBLE"); + +BOOST_CLASS_EXPORT_GUID(repast::SpecializedProjectionInfoPacket, + "SpecializedProjectionInfoPacket_INT"); + +using namespace std; +namespace mpi = boost::mpi; + +namespace repast { + +RepastProcess* RepastProcess::_instance = 0; + +RepastProcess::RepastProcess(boost::mpi::communicator* comm) : world(comm), runner(new ScheduleRunner(world)), + rank_(world->rank()), worldSize_(world->size()), + procsToSendProjInfoTo(NULL), procsToRecvProjInfoFrom(NULL), procsToSendAgentStatusInfoTo(NULL), + procsToRecvAgentStatusInfoFrom(NULL) { + + //world = comm; + //runner = new ScheduleRunner(world); + //rank_ = world->rank(); + //worldSize_ = world->size(); + +#ifndef SHARE_AGENTS_BY_SET + importer_exporter = new DEFAULT_IMPORTER_EXPORTER_CLASS(); +#else + importer_exporter = new ImporterExporter_BY_SET(); +#endif + + int topRank = world->size() - 1; + int recv = (rank_ > 0 ? rank_ - 1 : topRank); + int send = (rank_ < topRank ? rank_ + 1 : 0); + + // "register" the SpecializedProjectionInfoPacket with + // boost archive, iff running multiprocess. + if (send != recv) { + SpecializedProjectionInfoPacket test1; + SpecializedProjectionInfoPacket recvT1; + SpecializedProjectionInfoPacket test2; + SpecializedProjectionInfoPacket recvT2; + + boost::mpi::request requests[4]; + requests[0] = world->isend(send, 0, test1); + requests[1] = world->isend(send, 1, test2); + requests[2] = world->irecv(recv, 0, recvT1); + requests[3] = world->irecv(recv, 1, recvT2); + boost::mpi::wait_all(requests, requests + 4); + } +} + +RepastProcess* RepastProcess::init(string propsfile, + boost::mpi::communicator* comm, int maxConfigFileSize) { + if (_instance != 0) { + // reinitializing so delete the old instance + delete _instance; + + } + + boost::mpi::communicator* tmpWorld = ( + comm != 0 ? comm : new boost::mpi::communicator()); + + if (propsfile.length() > 0) + Log4CL::configure(tmpWorld->rank(), propsfile, tmpWorld, + maxConfigFileSize); + else + Log4CL::configure(tmpWorld->rank()); + _instance = new RepastProcess(tmpWorld); + + return _instance; +} + +RepastProcess* RepastProcess::instance() { + if (_instance == 0) + throw Repast_Error_39(); // RepastProcess must be initialized before calling instance + return _instance; +} + +boost::mpi::communicator* RepastProcess::communicator() { + return instance()->getCommunicator(); +} + +void RepastProcess::done() { + Log4CL::instance()->close(); +} + +void RepastProcess::addExportedAgent(int importingProcess, AgentId id) { +// exporter.addExportedAgent(importingProcess, id); +} + +void RepastProcess::addImportedAgent(AgentId id) { +// importer.incrementImportedAgentCount(id.currentRank()); +} + +void RepastProcess::agentRemoved(const AgentId& id) { + movedAgents.erase(id); + importer_exporter->agentRemoved(id); +} + +void RepastProcess::moveAgent(const AgentId& id, int process) { + AgentId newId(id); + newId.currentRank(process); + MovedAgentSetType::const_iterator iter = movedAgents.find(newId); + if (iter == movedAgents.end()) { + importer_exporter->agentMoved(id, process); + movedAgents.insert(newId); + } else { + AgentId other = *iter; + if (other.currentRank() != process) { + std::cout << rank_ << " : " << other << ", " << id + << " trying to move to " << process << std::endl; + throw Repast_Error_40(id, rank_, other.currentRank(), + process); // Cannot move agent to two different processes during the same iteration + } + } +} + +void RepastProcess::initiateAgentRequest(AgentRequest& request +#ifdef SHARE_AGENTS_BY_SET + , std::string setName, AGENT_IMPORTER_EXPORTER_TYPE setType +#endif + ) { + + // Record and process the outgoing request for agents from other processes +#ifndef SHARE_AGENTS_BY_SET + importer_exporter->registerOutgoingRequests(request); +#else + importer_exporter->registerOutgoingRequests(request, setName, setType); +#endif + + int* countsOfRequests = new int[worldSize_]; + for (int i = 0; i < worldSize_; ++i) + countsOfRequests[i] = 0; // OOPS! This was not included in version 1.0.1 final + int maxNumRequests = 0; + + const vector& requestedAgents = request.requestedAgents(); + const vector& cancellations = request.cancellations(); + + vector::const_iterator agentId; + vector::const_iterator agentIdEnd; + + agentIdEnd = requestedAgents.end(); + for (agentId = requestedAgents.begin(); agentId != agentIdEnd; agentId++) { + int target = agentId->currentRank(); + countsOfRequests[target]++; + if (countsOfRequests[target] > maxNumRequests) + maxNumRequests++; + } + + agentIdEnd = cancellations.end(); + for (agentId = cancellations.begin(); agentId != agentIdEnd; agentId++) { + int target = agentId->currentRank(); + countsOfRequests[target]++; + if (countsOfRequests[target] > maxNumRequests) + maxNumRequests++; + } + + int maxGlobalNumRequests = 0; + + MPI_Allreduce(&maxNumRequests, &maxGlobalNumRequests, 1, MPI_INT, MPI_MAX, + *world); + + // Reset and reuse the values from countsOfRequests + for (int i = 0; i < worldSize_; i++) + countsOfRequests[i] = 0; + + // Create an array for the actual send; note 'padding' for extra values + // Size of an individual element is 3 ints; don't need to send 'current rank' + int dataElementSize = (maxGlobalNumRequests + 2) * 3; + int dataSize = dataElementSize * worldSize_; + int* data = new int[dataSize]; + int* rec = new int[dataSize]; + + // Initialize + for (int i = 0; i < dataSize; i++) + data[i] = -1; // Can't be zero + + // Now move the data into the send buffer: + agentIdEnd = requestedAgents.end(); + for (agentId = requestedAgents.begin(); agentId != agentIdEnd; agentId++) { + int target = agentId->currentRank(); + int pos = target * dataElementSize + countsOfRequests[target] * 3; + data[pos] = agentId->id(); + data[pos + 1] = agentId->startingRank(); + data[pos + 2] = agentId->agentType(); + countsOfRequests[target]++; + } + + for (int i = 0; i < worldSize_; i++) + countsOfRequests[i]++; // Skip one entry to mark boundary between requests and cancellations + + agentIdEnd = cancellations.end(); + for (agentId = cancellations.begin(); agentId != agentIdEnd; agentId++) { + int target = agentId->currentRank(); + int pos = target * dataElementSize + countsOfRequests[target] * 3; + data[pos] = agentId->id(); + data[pos + 1] = agentId->startingRank(); + data[pos + 2] = agentId->agentType(); + countsOfRequests[target]++; + } + + // Exchange data + MPI_Alltoall(data, dataElementSize, MPI_INT, rec, dataElementSize, MPI_INT, + *world); + delete[] data; // Done with this... + + // Now re-package the received data as the vector that is needed + + vector reqsRecd; + for (int i = 0; i < worldSize_; i++) { + if (i != rank_) { // This isn't necessary as long as a process doesn't request agents from itself (!); this acts as an error trap if this happens + int index = i * dataElementSize; + // If there were no requests OR cancellations sent, skip + if (!((rec[index] == -1) && rec[index + 3] == -1)) { + // Create the agent request + AgentRequest req(i, rank_); + // Add all the requests + while (rec[index] != -1) { + AgentId id(rec[index], rec[index + 1], rec[index + 2]); + id.currentRank(rank_); + req.addRequest(id); + index += 3; + } + index += 3; + while (rec[index] != -1) { + AgentId id(rec[index], rec[index + 1], rec[index + 2]); + id.currentRank(rank_); + req.addCancellation(id); + index += 3; + } + // Add (a copy of) the request to the vector + reqsRecd.push_back(req); + } + } + } + + delete[] countsOfRequests; + delete[] rec; + + // Set up export of agents requested by other processes +#ifndef SHARE_AGENTS_BY_SET + importer_exporter->registerIncomingRequests(reqsRecd); +#else + importer_exporter->registerIncomingRequests(reqsRecd, setName); +#endif + +} + +RepastProcess::~RepastProcess() { + delete runner; + delete importer_exporter; + + delete procsToSendProjInfoTo; + delete procsToRecvProjInfoFrom; + + delete procsToSendAgentStatusInfoTo; + delete procsToRecvAgentStatusInfoFrom; + + for(size_t i = 0; i < cartesianTopologies.size(); i++) delete cartesianTopologies[i]; + + _instance = 0; +} + +CartesianTopology* RepastProcess::getCartesianTopology(std::vector processesPerDim, bool spaceIsPeriodic){ + for(size_t i = 0; i < cartesianTopologies.size(); i++){ + if(cartesianTopologies[i]->matches(processesPerDim, spaceIsPeriodic)) return cartesianTopologies[i]; + } + // If there were no matches + CartesianTopology* newCartTop = new CartesianTopology(processesPerDim, spaceIsPeriodic, world); + cartesianTopologies.push_back(newCartTop); + return newCartTop; +} + +} + diff --git a/libs/repast_hpc/RepastProcess.h b/libs/repast_hpc/RepastProcess.h new file mode 100644 index 0000000..e23f897 --- /dev/null +++ b/libs/repast_hpc/RepastProcess.h @@ -0,0 +1,1156 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * RepastProcess.h + * + * Created on: Jan 5, 2009 + * Author: nick + */ + +#ifndef REPASTPROCESS_H_ +#define REPASTPROCESS_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "Schedule.h" +#include "AgentId.h" +#include "SharedContext.h" +#include "AgentRequest.h" +#include "AgentStatus.h" +#include "mpi_constants.h" +#include "SRManager.h" +#include "RepastErrors.h" +#include "AgentImporterExporter.h" +#include "CartesianTopology.h" + +// these are for the timings logging +#include "Utilities.h" +#include "logger.h" + +/** + * \mainpage Repast HPC: A High-Performance Agent-Based Modeling Platform + * + * By Argonne National Laboratory, 2009-2018 + * + * \section intro_sec What is Repast HPC? + * + * Repast HPC is an Agent-Based Modeling Platform in the spirit of Repast Simphony + * but designed for top-500 high-performance computing systems (supercomputers). + */ + +namespace repast { + +/** + * Contains information sent as agents are exchanged, either in response to + * requests or agent movement. Contains both agent raw information + * (of type 'Content') and projection information. + * + * Note: A 'Packet' is responsible for deleting the objects to which it points + * This is essentially not optional: when boost sends the Packet via MPI the locations + * at which it places the different elements are not known (no 'new' is called + * in the user code). Some code must be written to track these down and delete, + * and it is manifestly easier to provide that code in the Packet itself + * than to rewrite where needed, inspecting the Packet for the locations + */ +template +class Request_Packet { + + friend class boost::serialization::access; + +public: + std::vector* agentContentPtr; + std::map >* projectionInfoPtr; + + Request_Packet() : + agentContentPtr(0), projectionInfoPtr(0) { + } + + Request_Packet(std::vector* agentContent, + std::map >* projectionInfo) : + agentContentPtr(agentContent), projectionInfoPtr(projectionInfo) { + } + + ~Request_Packet() { + delete agentContentPtr; + agentContentPtr = 0; + + if (projectionInfoPtr != 0) { + for (std::map >::iterator + iter = projectionInfoPtr->begin(), iterEnd = + projectionInfoPtr->end(); iter != iterEnd; ++iter) { + for (std::vector::iterator PIPIter = + iter->second.begin(), PIPIterEnd = iter->second.end(); + PIPIter != PIPIterEnd; ++PIPIter) { + delete *PIPIter; + } + } + } + delete projectionInfoPtr; + projectionInfoPtr = 0; + + } + + template + void serialize(Archive& ar, const unsigned int version) { + ar & agentContentPtr; + ar & projectionInfoPtr; + } + +}; + +/** + * Class that contains information sent in conjunction with synchronizing + * agent status (agents moving or being removed from the simulation). + * May contain secondary agent information (that is, agents that must newly be + * created as non-local agents on the receiving process due to obligations + * of projection contracts and the new existence of the agents being moved + * to that process). + * + * Note the unusual requirement of the deletion of exporter information. + */ +template +class SyncStatus_Packet { + + friend class boost::serialization::access; + +public: + std::vector* agentContentPtr; + std::map >* projectionInfoPtr; + std::set* secondaryIdsPtr; + AgentExporterInfo* exporterInfoPtr; + + SyncStatus_Packet() : + agentContentPtr(0), projectionInfoPtr(0), secondaryIdsPtr(0), exporterInfoPtr( + 0) { + } + + SyncStatus_Packet(std::vector* agentContent, + std::map >* projectionInfo, + std::set* secondaryIds, AgentExporterInfo* exporterInfo) : + agentContentPtr(agentContent), projectionInfoPtr(projectionInfo), secondaryIdsPtr( + secondaryIds), exporterInfoPtr(exporterInfo) { + } + + ~SyncStatus_Packet() { + delete agentContentPtr; + agentContentPtr = 0; + + if (projectionInfoPtr != 0) { + for (std::map >::iterator + iter = projectionInfoPtr->begin(), iterEnd = + projectionInfoPtr->end(); iter != iterEnd; ++iter) { + for (std::vector::iterator PIPIter = + iter->second.begin(), PIPIterEnd = iter->second.end(); + PIPIter != PIPIterEnd; ++PIPIter) { + delete *PIPIter; + } + } + } + delete projectionInfoPtr; + projectionInfoPtr = 0; + + delete secondaryIdsPtr; + secondaryIdsPtr = 0; + + } + + /** + * This method includes a very odd construction that arises because + * the Packet _must_ delete the exporter info on the process to which + * it has been sent, but it _cannot_ delete the exporter info on the + * process from which it was sent. The solution is to call this function + * manually on the receiving process, but not call it on the sending proc. + * + * The pointer returned allows the abbreviation: + * + * delete instance.deleteExporterInfo(); + */ + SyncStatus_Packet* deleteExporterInfo() { + delete exporterInfoPtr; + exporterInfoPtr = 0; + return this; + } + + template + void serialize(Archive& ar, const unsigned int version) { + ar & agentContentPtr; + ar & projectionInfoPtr; + ar & secondaryIdsPtr; + ar & exporterInfoPtr; + } + +}; + +/** + * Encapsulates the process in which repast is running and + * manages interprocess communication etc. This is singleton to + * insure that there is one per actual process. + */ +class RepastProcess: public boost::noncopyable { + +public: + enum EXCHANGE_PATTERN { + POLL, USE_CURRENT, USE_LAST_OR_POLL, USE_LAST_OR_USE_CURRENT + }; + +private: + + typedef boost::unordered_set MovedAgentSetType; + + static RepastProcess* _instance; + + boost::mpi::communicator* world; + ScheduleRunner* runner; + int rank_; + int worldSize_; + +#ifndef SHARE_AGENTS_BY_SET + AbstractImporterExporter* importer_exporter; +#else + ImporterExporter_BY_SET* importer_exporter; +#endif + + // key is process and value are requests from importers + // that the key-process should now export to. Used when + // an agent moves from this process and we need + // to tell the process-it-moves-to where it should + // export agents to + std::map*> importers; + MovedAgentSetType movedAgents; + + // called by request agents function to initiate the request +#ifndef SHARE_AGENTS_BY_SET + void initiateAgentRequest(AgentRequest& requests); +#else + void initiateAgentRequest(AgentRequest& requests, std::string setName = + DEFAULT_AGENT_REQUEST_SET, AGENT_IMPORTER_EXPORTER_TYPE setType = + DEFAULT_ENUM_SYMBOL); +#endif + + std::vector* procsToSendProjInfoTo; + std::vector* procsToRecvProjInfoFrom; + + std::vector* procsToSendAgentStatusInfoTo; + std::vector* procsToRecvAgentStatusInfoFrom; + + std::vector cartesianTopologies; + +protected: + RepastProcess(boost::mpi::communicator* comm = 0); + + void saveProjInfoSRProcs(std::vector& sends, std::vector& recvs) { + if (procsToSendProjInfoTo == NULL) { + procsToSendProjInfoTo = new std::vector; + procsToRecvProjInfoFrom = new std::vector; + } else { + procsToSendProjInfoTo->clear(); + procsToRecvProjInfoFrom->clear(); + } + procsToSendProjInfoTo->assign(sends.begin(), sends.end()); + procsToRecvProjInfoFrom->assign(recvs.begin(), recvs.end()); + } + + void saveAgentStatusInfoSRProcs(std::vector& sends, + std::vector& recvs) { + if (procsToSendAgentStatusInfoTo == NULL) { + procsToSendAgentStatusInfoTo = new std::vector; + procsToRecvAgentStatusInfoFrom = new std::vector; + } else { + procsToSendAgentStatusInfoTo->clear(); + procsToRecvAgentStatusInfoFrom->clear(); + } + procsToSendAgentStatusInfoTo->assign(sends.begin(), sends.end()); + procsToRecvAgentStatusInfoFrom->assign(recvs.begin(), recvs.end()); + } + +public: + + /** + * Initialize this RepastProcess. This must be called before the + * RepastProcess is used. If a configuration properties file is specified + * this properties file will be used to configure logging. + * + * @param propsfile a configuration properties file. This can be an + * empty string. + */ + static RepastProcess* init(std::string propsfile, + boost::mpi::communicator* comm = 0, int maxConfigFileSize = + MAX_CONFIG_FILE_SIZE); + + /** + * Gets this RepastProcess. + * + * @return this RepastProcess instance. + */ + static RepastProcess* instance(); + + static boost::mpi::communicator* communicator(); + + virtual ~RepastProcess(); + + /** + * NON USER API. + * + * Notifies this RepastProcess that the specified agent + * has been removed (e.g. the agent "died"). + */ + void agentRemoved(const AgentId& id); + + /** + * NON USER API. + * + * Notifies this RepastProcess that the specified agent + * should be moved from this process to the specified process. + * + * @param id the id of the agent to be moved + * @param process the process to move the agent to + */ + void moveAgent(const AgentId& id, int process); + + /** + * NON USER API. + * + * Notifies this RepastProcess that it is exporting the specified + * agent to the specified process. This sort of notification is + * done automatically when requesting agents, but agents may get + * added in other ways. + */ + void addExportedAgent(int importingProcess, AgentId id); + + /** + * NON USER API. + * + * Notifies this RepastProcess that it is importing + * the specified agent. This sort of notification + * is normally done automatically when requesting agents, but imports can + * occur in other ways. + */ + void addImportedAgent(AgentId id); + + /** + * Gets the rank of this process. + * + * @return the rank of this process. + */ + int rank() const { + return rank_; + } + + /** + * Gets the number of processes in the world. + */ + int worldSize() const { + return worldSize_; + } + + /** + * Notifes this RepastProcess that simulation has completed. This should be called + * when the simulation has completed. + */ + void done(); + + /** + * Gets the ScheduleRunner used by this + * RepastProcess. + * + * @return the ScheduleRunner used by this RepastProcess. + */ + ScheduleRunner& getScheduleRunner() { + return *runner; + } + + boost::mpi::communicator* getCommunicator() { + return world; + } + + + CartesianTopology* getCartesianTopology(std::vector processesPerDim, bool spaceIsPeriodic); + + +#ifdef SHARE_AGENTS_BY_SET + void dropImporterExporterSet(std::string setName) { + importer_exporter->dropSet(setName); + } +#endif + + std::string ImporterExporterVersion() { + return "" + importer_exporter->version(); + } + + std::string ImporterExporterReport() { + return importer_exporter->getReport(); + } + + // Repast Process should handle four specific tasks for controlling parallelization: + // + // 1. Requesting agents that are to be shared + // 2. Synchronizing the state values for shared agents + // 3. Synchronizing the Projection info for all shared agents + // 4. Synchronizing agent status values, including removing agents and moving them across processes + // + // At the end of all four of these operations, the simulation should be in a valid state + // (Though not necessarily a 'current' state: synchronizing state values does NOT update Projection + // information. This is a performance consideration: ideally we would always have the simulation in + // a 100% current state, but because the simulation is being parallelized, a choice must always be made + // about what information is updated.) + + /** + * Request agents from other processes. + */ + template + void requestAgents(SharedContext& context, AgentRequest& request, + Provider& provider, Updater& updater, AgentCreator& creator +#ifdef SHARE_AGENTS_BY_SET + , std::string setName = DEFAULT_AGENT_REQUEST_SET, + AGENT_IMPORTER_EXPORTER_TYPE setType = DEFAULT_ENUM_SYMBOL +#endif + ); + + /** + * Synchronizes the state values of shared agents. Does not change the Projection information + * for those agents. + */ + template + void synchronizeAgentStates(Provider& provider, Updater& updater +#ifdef SHARE_AGENTS_BY_SET + , std::string setName = REQUEST_AGENTS_ALL +#endif + ); + + /** + * Synchronizes the Projection information for shared projections. + */ + template + void synchronizeProjectionInfo(SharedContext& context, + Provider& provider, Updater& updater, AgentCreator& creator, + EXCHANGE_PATTERN exchangePattern = POLL +#ifdef SHARE_AGENTS_BY_SET + , bool declareNoAgentsKeptOnAnyProcess = false +#endif + ); + + /** + * Synchronizes the status (moved or died) of all agents across processes. + * + * @param context the SharedContext that contains the agents on this proceses + * @param provider the class that provides agents given an AgentRequest + * @param creator creates agents of type T given Content. + * + * @tparam T the type of agents in the context + * @tparam Content the serializable struct or class that describes + * an agents state. + * @tparam Provider a class that provides Content, when given an AgentRequest, + * implementing void provideContent(const repast::AgentRequest&, std::vector& out) + * @tparam AgentCreator a class that can create agents from Content, implementing + * T* createAgent(Content&). + * + */ + template + void synchronizeAgentStatus(SharedContext& context, Provider& provider, + Updater& updater, AgentCreator& creator, + EXCHANGE_PATTERN exchangePattern = POLL); + +}; + +/** + * Requests agents from one process to others. Copies of the requested agents' + * Content are retrieved from their respective processes, created using + * the AgentCreator and added to the specified context. + * + * @param context the context to which the requested agents will be added + * @param request the AgentRequest containing the ids of the requested agents + * @param provider provides Content for a given an AgentRequest + * @param creator creates agents of type T given Content. + * + * + * @tparam T the type of the agents in the context + * @tparam Content the serializable struct or class that describes the + * state of agents + * @tparam Provider given an AgentRequest, a Provider provides the Content + * for the requested agents, implementing void provideContent(const AgentRequest&, + * std::vector&) + * @tparam AgentCreator a class that can create agents from Content, implementing + * T* createAgent(Content&). + * + */ +template +void RepastProcess::requestAgents(SharedContext& context, + AgentRequest& request, Provider& provider, Updater& updater, + AgentCreator& creator +#ifdef SHARE_AGENTS_BY_SET + , std::string setName, AGENT_IMPORTER_EXPORTER_TYPE setType +#endif + ) { + + // Initiate the new requests +#ifdef SHARE_AGENTS_BY_SET + initiateAgentRequest(request, setName, setType); +#else + initiateAgentRequest(request); +#endif + + // Establish which processes are sending to/receiving from this one +#ifdef SHARE_AGENTS_BY_SET + const std::set& exporters = importer_exporter->getExportingProcesses( + setName); + const std::map& agentsToExport = + importer_exporter->getAgentsToExport(setName); +#else + const std::set& exporters = importer_exporter->getExportingProcesses(); + const std::map& agentsToExport = importer_exporter->getAgentsToExport(); +#endif + + // Construct MPI requests (Receives and Sends) + std::vector requests; // MPI Requests (receives and sends) + + // Construct Receives + std::vector*> toReceive; + + for (std::set::const_iterator iter = exporters.begin(); + iter != exporters.end(); ++iter) { + Request_Packet* packet; + toReceive.push_back(packet = new Request_Packet()); + requests.push_back(world->irecv(*iter, 23, *packet)); + } + + // Construct Sends + boost::ptr_list >* toSend = new boost::ptr_list< + Request_Packet >; + + for (std::map::const_iterator iter = + agentsToExport.begin(); iter != agentsToExport.end(); ++iter) { + + // Agent Content + std::vector* content = new std::vector; + provider.provideContent(iter->second, *content); + + // Projection Info + std::map >* projInfo = + new std::map >; + context.getProjectionInfo(iter->second, *projInfo); + + Request_Packet* packet; + toSend->push_back( + packet = new Request_Packet(content, projInfo)); + requests.push_back(world->isend(iter->first, 23, *packet)); + } + + // Wait until all sends/receives complete + boost::mpi::wait_all(requests.begin(), requests.end()); + + // Clear sent data + delete toSend; + + // Process (and delete) received data + for (typename std::vector*>::iterator iter = + toReceive.begin(), iterEnd = toReceive.end(); iter != iterEnd; + ++iter) { + std::vector* content = (*iter)->agentContentPtr; + for (typename std::vector::const_iterator contentIter = + content->begin(), contentIterEnd = content->end(); + contentIter != contentIterEnd; ++contentIter) { + T* out = creator.createAgent(*contentIter); + T* inContext = context.addAgent(out); + if (inContext != out) { // This agent was already on this process + updater.updateAgent(*contentIter); + delete out; + } + } + context.setProjectionInfo(*((*iter)->projectionInfoPtr)); + delete *iter; + } + +} + +template +void RepastProcess::synchronizeAgentStates(Provider& provider, Updater& updater +#ifdef SHARE_AGENTS_BY_SET + , std::string setName +#endif + ) { + + // Establish which processes are sending/receiving from this one +#ifdef SHARE_AGENTS_BY_SET + const std::set& processesToReceiveFrom = + importer_exporter->getExportingProcesses(setName); + const std::map& agentsToExport = + importer_exporter->getAgentsToExport(setName); +#else + const std::set& processesToReceiveFrom = importer_exporter->getExportingProcesses(); + const std::map& agentsToExport = importer_exporter->getAgentsToExport(); +#endif + + // Construct MPI Requests (sends and receives) + std::vector requests; + + // Construct Receives + std::vector*> received; + for (std::set::const_iterator iter = processesToReceiveFrom.begin(), + iterEnd = processesToReceiveFrom.end(); iter != iterEnd; ++iter) { + std::vector* content = new std::vector(); + requests.push_back(world->irecv(*iter, 47, *content)); + received.push_back(content); + } + + // Construct Sends + boost::ptr_list >* toSend = new boost::ptr_list< + std::vector >; + std::vector* content; + + for (std::map::const_iterator iter = + agentsToExport.begin(), iterEnd = agentsToExport.end(); + iter != iterEnd; ++iter) { + toSend->push_back(content = new std::vector); + provider.provideContent(iter->second, *content); + requests.push_back(world->isend(iter->first, 47, *content)); + } + + // Wait until all sends and receives are complete + boost::mpi::wait_all(requests.begin(), requests.end()); + + // Clear sent data + delete toSend; + + // Process (and clear) received data + for (typename std::vector*>::iterator iter = + received.begin(), iterEnd = received.end(); iter != iterEnd; + ++iter) { + content = *iter; + for (typename std::vector::const_iterator agentIter = + content->begin(), agentIterEnd = content->end(); + agentIter != agentIterEnd; ++agentIter) { + updater.updateAgent(*agentIter); + } + delete content; + } + +} + +template +void RepastProcess::synchronizeProjectionInfo(SharedContext& context, + Provider& provider, Updater& updater, AgentCreator& creator, + EXCHANGE_PATTERN exchangePattern +#ifdef SHARE_AGENTS_BY_SET + , bool declareNoAgentsKeptOnAnyProcess +#endif + ) { + + // Generate sets of agents to delete or not delete + std::set agentsToKeep; + + bool agentsMayBeKept = +#ifdef SHARE_AGENTS_BY_SET + context.keepsAgentsOnSyncProj() || !declareNoAgentsKeptOnAnyProcess; +#else + context.keepsAgentsOnSyncProj(); +#endif + + // If 'By Set': Construct 'Keep list' from all non-default I/E requests (agents being imported) +#ifdef SHARE_AGENTS_BY_SET + if (declareNoAgentsKeptOnAnyProcess) { + importer_exporter->clear(); + } else { + importer_exporter->getSetOfAgentsBeingImported(agentsToKeep, + DEFAULT_AGENT_REQUEST_SET); + importer_exporter->clear(DEFAULT_AGENT_REQUEST_SET); + } +#else + importer_exporter->clear(); +#endif + + // Determine all agents that the context doesn't need and are not on 'Keep' list, adding those that it needs to the 'Keep' list + std::set agentsToDrop; + context.getNonlocalAgentsToDrop(agentsToKeep, agentsToDrop); + + // Drop all of the agents that can be dropped + std::set::iterator dropIter = agentsToDrop.begin(), dropIterEnd = + agentsToDrop.end(); + while (dropIter != dropIterEnd) { + context.removeAgent(*dropIter); + agentRemoved(*dropIter); + dropIter++; + } + + // And drop all unneeded projection information, even for the agents being kept + context.cleanProjectionInfo(agentsToKeep); + + // Initiate Agent Request (so that I/E will have agents needed by other processes) + if (agentsMayBeKept) { + AgentRequest req; + for (std::set::iterator iter = agentsToKeep.begin(), iterEnd = + agentsToKeep.end(); iter != iterEnd; ++iter) { + req.addRequest(*iter); // TO DO: Better optimized constructor + } + initiateAgentRequest(req); // Note: will use default I/E + } + + // Determine which agents will be 'pushed' to other processes + std::map > agentsToPush; + context.getAgentsToPushToOtherProcesses(agentsToPush); + + // Add these to I/E as exports + std::vector requests; + for (std::map >::iterator iter = + agentsToPush.begin(), iterEnd = agentsToPush.end(); iter != iterEnd; + ++iter) { + AgentRequest req(iter->first); + for (std::set::iterator i = iter->second.begin(), iEnd = + iter->second.end(); i != iEnd; i++) + req.addRequest(*i); + requests.push_back(req); + } + +#ifdef SHARE_AGENTS_BY_SET + importer_exporter->registerIncomingRequests(requests, + DEFAULT_AGENT_REQUEST_SET); +#else + importer_exporter->registerIncomingRequests(requests); +#endif + + // Exchange agent & projection information + // Establish which processes are sending to/receiving from this one +#ifdef SHARE_AGENTS_BY_SET + const std::map& tmpAgentsToExport = + importer_exporter->getAgentsToExport(DEFAULT_AGENT_REQUEST_SET); +#else + const std::map& tmpAgentsToExport = importer_exporter->getAgentsToExport(); +#endif + + std::map agentsToExport = tmpAgentsToExport; // Copy? + + std::vector psToSendTo; + std::vector psToReceiveFrom; + if (exchangePattern == USE_CURRENT + || ((exchangePattern == USE_LAST_OR_USE_CURRENT) + && (procsToSendProjInfoTo == NULL))) { + std::set sends, recvs; + context.getProjInfoExchangePartners(sends, recvs); + psToSendTo.assign(sends.begin(), sends.end()); + psToReceiveFrom.assign(recvs.begin(), recvs.end()); + // Add dummy requests to create empty sends + for (std::vector::iterator iter = psToSendTo.begin(), iterEnd = + psToSendTo.end(); iter != iterEnd; ++iter) { + int dest = *iter; + if (agentsToExport.find(dest) == agentsToExport.end()) { + AgentRequest dummy(rank_, dest); + agentsToExport[dest] = dummy; + } + } + } else if (exchangePattern == POLL + || ((exchangePattern == USE_LAST_OR_POLL) + && (procsToSendProjInfoTo == NULL))) { + for (std::map::const_iterator iter = + agentsToExport.begin(), iterEnd = agentsToExport.end(); + iter != iterEnd; ++iter) { + psToSendTo.push_back(iter->first); + } + SRManager manager(world); + manager.retrieveSources(psToSendTo, psToReceiveFrom, + AGENT_MOVED_SENDERS); + } else { + psToSendTo.assign(procsToSendProjInfoTo->begin(), + procsToSendProjInfoTo->end()); + psToReceiveFrom.assign(procsToRecvProjInfoFrom->begin(), + procsToRecvProjInfoFrom->end()); + + // Add dummy requests to create empty sends + for (std::vector::iterator iter = psToSendTo.begin(), iterEnd = + psToSendTo.end(); iter != iterEnd; ++iter) { + int dest = *iter; + if (agentsToExport.find(dest) == agentsToExport.end()) { + AgentRequest dummy(rank_, dest); + agentsToExport[dest] = dummy; + } + } + } + + saveProjInfoSRProcs(psToSendTo, psToReceiveFrom); + + // Construct MPI requests (Receives and Sends) + std::vector MPIRequests; // MPI Requests (receives and sends) + + // Construct Receives + std::map*> toReceive; + + for (std::vector::iterator iter = psToReceiveFrom.begin(), iterEnd = + psToReceiveFrom.end(); iter != iterEnd; ++iter) { + Request_Packet* packet; + toReceive[*iter] = (packet = new Request_Packet()); + MPIRequests.push_back(world->irecv(*iter, 23, *packet)); + } + + // Construct Sends + boost::ptr_list >* toSend = new boost::ptr_list< + Request_Packet >; + + for (std::map::const_iterator iter = + agentsToExport.begin(), iterEnd = agentsToExport.end(); + iter != iterEnd; ++iter) { + int dest = iter->first; + const AgentRequest& rq = iter->second; + + // Agent Content + std::vector* contentVector = new std::vector; + provider.provideContent(rq, *contentVector); + + // Projection Info + std::map >* projInfo = + new std::map >; + context.getProjectionInfo(rq, *projInfo, true, 0, dest); // Will collect the edges but not the secondary IDs + + Request_Packet* packet; + toSend->push_back( + packet = new Request_Packet(contentVector, projInfo)); + MPIRequests.push_back(world->isend(dest, 23, *packet)); + } + + // Wait until all sends/receives complete + boost::mpi::wait_all(MPIRequests.begin(), MPIRequests.end()); + + // Clear sent data + delete toSend; + + // Process received data (and clear) + for (typename std::map*>::iterator iter = + toReceive.begin(), iterEnd = toReceive.end(); iter != iterEnd; + ++iter) { + std::vector* contentVector = iter->second->agentContentPtr; + AgentRequest requestToRegister(iter->first); + for (typename std::vector::const_iterator contentIter = + contentVector->begin(), contentIterEnd = contentVector->end(); + contentIter != contentIterEnd; ++contentIter) { + T* newAgent = creator.createAgent(*contentIter); + T* agentInContext = context.addAgent(newAgent); + if (agentInContext != newAgent) { // This agent was already on this process + updater.updateAgent(*contentIter); + delete newAgent; + } else { + // Add the agent to the agent request that will be processed as if it were an OUTGOING request + requestToRegister.addRequest(agentInContext->getId()); + } + } + + context.setProjectionInfo(*(iter->second->projectionInfoPtr)); + delete iter->second; + // Register these as requests, so that the importer/exporter will know these agents will be sent + importer_exporter->registerOutgoingRequests(requestToRegister); + } +} + +template +void RepastProcess::synchronizeAgentStatus(SharedContext& context, + Provider& provider, Updater& updater, AgentCreator& creator, + EXCHANGE_PATTERN exchangePattern) { + + // Step 1: Exchange information about agents whose status will be updated. + // + // Status Updates have been added to the importer_exporter and can now be exchanged + // across processes. All processes will now know that an agent that has previously + // been managed by process A will either be eliminated from the simulation or + // moved to process B. + std::vector*> statusUpdates; + importer_exporter->exchangeAgentStatusUpdates(*world, statusUpdates); + + for (size_t i = 0, n = statusUpdates.size(); i < n; i++) { + std::vector* vec = statusUpdates[i]; + for (size_t j = 0, k = vec->size(); j < k; ++j) { + AgentStatus& status = (*vec)[j]; + if (status.getStatus() == AgentStatus::REMOVED) { + importer_exporter->importedAgentIsRemoved(status.getOldId()); // Notify importer/exporter that this agent will not be imported anymore + context.importedAgentRemoved(status.getOldId()); // Remove from context; agent cannot exist on this process after removal from home process + } else if (status.getStatus() == AgentStatus::MOVED) { + if (rank_ != status.getNewId().currentRank()) { + // Notify importer that this agent will not be imported from the original + // process, but will instead be imported from its new home + importer_exporter->importedAgentIsMoved(status.getOldId(), + status.getNewId().currentRank()); + } else { + // Notify importer that the agent will not be imported anymore because this is its home process now + importer_exporter->importedAgentIsNowLocal( + status.getOldId()); + } + // Find it and update its id + T* agent = context.getAgent(status.getOldId()); + if (agent == (void*) 0) + throw Repast_Error_32(status.getOldId()); // Agent not found + agent->getId().currentRank(status.getNewId().currentRank()); + } + } + delete vec; + } + + // Step 2: Send moving agents' information to new home processes + // + // First, some basic data structures must be created for some bookkeeping we will need later + std::set agentsToDrop; // A list of agents that will be removed from this process + std::set psMovedTo; // A list of the processes that will be receiving moving agents + std::map agentRequests; // A map of these receiving processes and a list of the IDs of the agents going to them + + // This loop applies to all agents moving off of this process (from 'movedAgents'); it: + // Re-sets the 'current Process' ID values of the agents moving away from this process to their new values + // Adds the agents to the lists of agents to be removed from this process + // Creates the map of agent requests per receiving process and adds the agents to it + for (MovedAgentSetType::const_iterator iter = movedAgents.begin(), iterEnd = + movedAgents.end(); iter != iterEnd; ++iter) { + AgentId id = *iter; + context.getAgent(id)->getId().currentRank(id.currentRank()); + agentsToDrop.insert(id); + int currentProc = id.currentRank(); + if (psMovedTo.insert(currentProc).second) { + AgentRequest req = AgentRequest(currentProc, rank_); + req.addRequest(id); + agentRequests[currentProc] = req; + } else + agentRequests[currentProc].addRequest(id); + } + movedAgents.clear(); + + // Next, coordinate the send/receive pairs (which processes send to which) + // + // Three different methods can be used: + // 1) POLL: Assume no knowledge of which processes will be sending to this one; do a full exchange + // where all processes inform all other processes of whether they need to send to them + // 2) USE_CURRENT: Assume that the context (and, by implication, all projections in the context) + // know which processes they need to send to and which they will receive from. For example, a grid + // projection will know its 8 neighbors; a graph projection _might_ know which processes own + // nodes to which it is connected + // 3) USE_LAST: Assume that the set of processes to which sends go and from which sends are received + // will be unchanged from the last time this loop was run; once established, just keep using + // the same set + // + // Variants 'USE_LAST_OR_POLL' and 'USE_LAST_OR_USE_CURRENT' allow for the case in which one method (either + // POLL or USE_CURRENT) is used for the initial pass through the loop and thereafter the sets are assumed + // unchanged. + std::vector psToSendTo; // Convert set to vector + std::vector psToReceiveFrom; + + if (exchangePattern == USE_CURRENT + || ((exchangePattern == USE_LAST_OR_USE_CURRENT) + && (procsToSendAgentStatusInfoTo == NULL))) { + std::set sends, recvs; + context.getAgentStatusInfoExchangePartners(sends, recvs); + psToSendTo.assign(sends.begin(), sends.end()); + psToReceiveFrom.assign(recvs.begin(), recvs.end()); + // Add dummy requests to create empty sends + for (std::vector::iterator iter = psToSendTo.begin(), iterEnd = + psToSendTo.end(); iter != iterEnd; ++iter) { + int dest = *iter; + if (agentRequests.find(dest) == agentRequests.end()) { + AgentRequest dummy(rank_, dest); + agentRequests[dest] = dummy; + } + } + } else if (exchangePattern == POLL + || ((exchangePattern == USE_LAST_OR_POLL) + && (procsToSendAgentStatusInfoTo == NULL))) { + for (std::map::const_iterator iter = + agentRequests.begin(), iterEnd = agentRequests.end(); + iter != iterEnd; ++iter) { + psToSendTo.push_back(iter->first); + } + SRManager manager(world); + manager.retrieveSources(psToSendTo, psToReceiveFrom, + AGENT_MOVED_SENDERS); + } else { + psToSendTo.assign(procsToSendAgentStatusInfoTo->begin(), + procsToSendAgentStatusInfoTo->end()); + psToReceiveFrom.assign(procsToRecvAgentStatusInfoFrom->begin(), + procsToRecvAgentStatusInfoFrom->end()); + + // Add dummy requests to create empty sends + for (std::vector::iterator iter = psToSendTo.begin(), iterEnd = + psToSendTo.end(); iter != iterEnd; ++iter) { + int dest = *iter; + if (agentRequests.find(dest) == agentRequests.end()) { + AgentRequest dummy(rank_, dest); + agentRequests[dest] = dummy; + } + } + } + + saveAgentStatusInfoSRProcs(psToSendTo, psToReceiveFrom); + + // Determine if any projection in the context will need to send 'secondary' agent data: + bool sendSecondaryData = context.sendsSecondaryDataOnStatusExchange(); + + // Create MPI Sends and Receives + std::vector requests; + + // STEP 5: Create the receives + std::vector*> packetsRecd; + + for (std::vector::const_iterator iter = psToReceiveFrom.begin(); + iter != psToReceiveFrom.end(); ++iter) { + int source = *iter; + SyncStatus_Packet* packetToRecv = + new SyncStatus_Packet; + requests.push_back( + world->irecv(source, AGENT_MOVED_AGENT, *packetToRecv)); + packetsRecd.push_back(packetToRecv); + } + + // STEP 6: Assemble data to send + boost::ptr_list >* packetsToSend = + new boost::ptr_list >; + + for (std::map::iterator iter = + agentRequests.begin(); iter != agentRequests.end(); ++iter) { + // Agent Content + std::vector* content = new std::vector; + provider.provideContent(iter->second, *content); + + // Projection Info and Secondary Ids + std::map >* projInfo = + new std::map >; + std::set* secondaryIds = ( + sendSecondaryData ? new std::set : 0); + context.getProjectionInfo(iter->second, *projInfo, sendSecondaryData, + secondaryIds); + + // Send the information for the secondary agents, too: + if (secondaryIds != 0) { // use 'sendSecondaryData' instead? Should be equivalent... + AgentRequest sidReq; + for (std::set::iterator sidIter = secondaryIds->begin(), + sidIterEnd = secondaryIds->end(); sidIter != sidIterEnd; + ++sidIter) + sidReq.addRequest(*sidIter); + provider.provideContent(sidReq, *content); // Only their state data is needed, not any projection info + } + + // Agent Exporter Info + AgentExporterInfo* agentImporterInfoPtr = + importer_exporter->getAgentExportInfo(iter->first); + + SyncStatus_Packet* packetToSend; + packetsToSend->push_back( + packetToSend = new SyncStatus_Packet(content, projInfo, + secondaryIds, agentImporterInfoPtr)); + + requests.push_back( + world->isend(iter->first, AGENT_MOVED_AGENT, *packetToSend)); + } + boost::mpi::wait_all(requests.begin(), requests.end()); + delete packetsToSend; + + importer_exporter->clearAgentExportInfo(); + + // STEP 9: Remove the agents that are moving to other processes and are not needed here + std::set agentsToKeep; + context.getRequiredAgents(agentsToDrop, agentsToKeep, + Projection::SECONDARY); + + for (std::set::iterator idIter = agentsToDrop.begin(), idIterEnd = + agentsToDrop.end(); idIter != idIterEnd; ++idIter) + context.removeAgent(*idIter); + + // STEP 10: Insert the newly received agents that moved to this process and update exporters + typename std::vector*>::iterator packetIter; + typename std::vector*>::iterator packetIterEnd = + packetsRecd.end(); + AgentRequest secondaryAgentsToRequest(rank_); + for (packetIter = packetsRecd.begin(); packetIter != packetIterEnd; + ++packetIter) { + std::vector* content = (*packetIter)->agentContentPtr; + typename std::vector::iterator contentIter = content->begin(); + while (contentIter != content->end()) { + T* out = creator.createAgent(*contentIter); + T* inContext = context.addAgent(out); + if (inContext != out) { // Already contain the agent + // If the agent is local on this rank, do nothing (the agent received must be a secondary agent) + // If the agent is non-local on this rank + if (inContext->getId().currentRank() != rank_) { + // If the arriving agent has a current rank equal to this rank, this + // is an incoming, newly arrived local agent that already existed on this + // process as a secondary agent; it should be updated and its currentRank in + // its ID set to the local rank + if (out->getId().currentRank() == rank_) { + updater.updateAgent(*contentIter); + inContext->getId().currentRank(rank_); + } + // Otherwise, it's a secondary agent arriving from another process, when it + // already exists as a non-local agent on this process; leave the original alone + // and discard the new version. + } + delete out; + } else { // Agent was not already on this rank and is not a new local agent; must process it as a new request + if (out->getId().currentRank() != rank_) + secondaryAgentsToRequest.addRequest(out->getId()); + } + contentIter++; + } + + // Update the importer/exporter to reflect the newly arrived local agents + importer_exporter->incorporateAgentExporterInfo( + *((*packetIter)->exporterInfoPtr)); + importer_exporter->clearExportToSpecificProc(rank_); // Export Info may include 'exports' to self; remove these + + } + + // STEP 11: Newly received secondary agents must be coordinated with current processes + if (sendSecondaryData) + initiateAgentRequest(secondaryAgentsToRequest); + + // STEP 12: Set all the Projection Info, including graph edges, and clear the received data + for (packetIter = packetsRecd.begin(); packetIter != packetIterEnd; + ++packetIter) { + context.setProjectionInfo(*((*packetIter)->projectionInfoPtr)); + delete (*packetIter)->deleteExporterInfo(); // Exporter Info is only deleted from the received packets, not the sent ones... + } + +} + +} + +#endif /* REPASTPROCESS_H_ */ diff --git a/libs/repast_hpc/SRManager.cpp b/libs/repast_hpc/SRManager.cpp new file mode 100644 index 0000000..5329ebb --- /dev/null +++ b/libs/repast_hpc/SRManager.cpp @@ -0,0 +1,101 @@ +/* + *Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SRManager.cpp + * + * Created on: 2 Dec 2011 + * Author: JTM + */ + +#include "SRManager.h" + +#include + +using namespace std; + +SRManager::SRManager(boost::mpi::communicator* comm): _comm(comm){ + int s = _comm->size(); + mySend = new int[s]; + myRecv = new int[s]; + send = mySend; + recv = myRecv; + clear(); +} + +SRManager::SRManager(boost::mpi::communicator* comm, int* toSend, int* toRecv): _comm(comm), send(toSend), recv(toRecv){ + mySend = NULL; + myRecv = NULL; + if(recv == NULL){ + myRecv = new int[_comm->size()]; + recv = myRecv; + int *r = recv; + for(unsigned int i = _comm->size(); i != 0; i--, r++){ *r = 0; } + } +} + +SRManager::~SRManager(){ + delete [] mySend; + delete [] myRecv; +} + + +void SRManager::mark(int pos){ + mySend[pos] = 1; +} + +void SRManager::setVal(int pos, int val){ + mySend[pos] = val; +} + +void SRManager::clear(){ + int *s = send; + int *r = recv; + for(unsigned int i = _comm->size(); i != 0; i--, s++, r++){ *s = 0; *r = 0; } +} + +void SRManager::retrieveSources(){ + MPI_Alltoall(send, 1, MPI_INT, recv, 1, MPI_INT, (*_comm)); +} + +void SRManager::retrieveSources(std::vector& sources){ + retrieveSources(); + const int e = _comm->size(); + int *r = recv; + for(int i = 0; i != e; i++, r++) if(*r != 0) sources.push_back(i); +} + +void SRManager::retrieveSources(const std::vector& targets, std::vector& sources, int tag){ + std::vector::const_iterator iEnd = targets.end(); + for(std::vector::const_iterator iter = targets.begin(); iter != iEnd; iter++) send[*iter] = 1; + retrieveSources(sources); +} diff --git a/libs/repast_hpc/SRManager.h b/libs/repast_hpc/SRManager.h new file mode 100644 index 0000000..7e86219 --- /dev/null +++ b/libs/repast_hpc/SRManager.h @@ -0,0 +1,147 @@ +/* + *Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * SRManager.h + * + * Created on: 2 Dec 2011 + * Author: JTM + */ + +#ifndef SRMANAGER_H_ +#define SRMANAGER_H_ + +#include + +#include + +/** + * Coordinates send and receive between processes + * by notifying processes to expect a send from + * X other processes. + */ +class SRManager{ + +private: + boost::mpi::communicator* _comm; + int *send; + int *recv; + int *mySend; + int *myRecv; + +public: + /** + * Creates an SRManager that uses the specified communicator. + * + * @param comm the communicator to use + */ + SRManager(boost::mpi::communicator* comm); + + /** + * Creates an SRManager that uses the specified communicator, + * using the user-specified arrays instead of its internal arrays. + * The ability to use external arrays is a convenience for + * conditions in which it is useful to maintain the array + * of values for other purposes but exchange them using the SRManager. + * + * @param comm the communicator to use + * @param toSend the array to be used as the send array + * @param toRecv the array to be used as the receive array + * If the pointer passed for the receive array is null, an internal + * array will be used. This is to provide for situations in which + * the user wishes to maintain the send array but not the receive + * array. + */ + SRManager(boost::mpi::communicator* comm, int* toSend, int* toRecv); + + ~SRManager(); + + /** + * Marks the position in the array as 'true' (sets to one). + * + * @param pos the position in the array to be set, AKA the processor + * to which information will be sent. + */ + void mark(int pos); + + /** + * Sets the value at the given index in the array. Note: Does not perform error + * checking; user should ensure that index value is valid. + * + * @param pos index value for position in array to be set + * @param val value to which the array element should be set + */ + void setVal(int pos, int val); + + /** + * Clears the send and receive arrays (sets all values to 0). + */ + inline void clear(); + + /** + * Performs the actual send operation, populating the receive array with + * values from the other processes' send arrays. + */ + void retrieveSources(); + + + /** + * Performs the send operation and populates the vector passed with + * values representing all elements in the array that have non-zero + * values after the receive. + * + * @param sources vector that will have a list of all processes that + * sent non-zero values to this one + */ + void retrieveSources(std::vector& sources); + + /** + * Populates the send array based on the values listed in the 'targets' + * vector (which should be a list of process IDs to which this processer + * will send information) then performs the send operation, then populates + * the vector passed with values representing all elements in the receive + * array that have non-zero values after the receive. + * + * @param targets vector of integers representing process to which this one + * intends to send information + * @param sources vector that will be populated with list of integers representing + * processes that will send this process information + * @tag optional parameter, now obsolete (included for backward compatibility with + * boost-based SRManager prior to 2.0 release. + */ + void retrieveSources(const std::vector& targets, std::vector& sources, int tag = 0); + +}; + + + +#endif /* SRMANAGER_H_ */ diff --git a/libs/repast_hpc/SVDataSet.cpp b/libs/repast_hpc/SVDataSet.cpp new file mode 100644 index 0000000..36c727d --- /dev/null +++ b/libs/repast_hpc/SVDataSet.cpp @@ -0,0 +1,154 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DataSet.cpp + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#include + +#include + +#include "SVDataSet.h" +#include "RepastProcess.h" +#include "io.h" +#include "RepastErrors.h" + +namespace fs = boost::filesystem; + +namespace repast { + +SVDataSet::SVDataSet(const std::string& file, const std::string& separator, const Schedule* schedule) : + _separator(separator), _schedule(schedule), out(), open(true) { + rank = RepastProcess::instance()->rank(); + if (rank == 0) { + fs::path filepath(file); + if (!fs::exists(filepath.parent_path())) fs::create_directories(filepath.parent_path()); + int i = 1; + std::string stem = filepath.stem().string(); + while(fs::exists(filepath)){ // This will increment i until it hits a unique name + i++; + std::stringstream ss; + ss << stem << "_" << i << filepath.extension().string(); + fs::path newName(filepath.parent_path() / ss.str()); + filepath = newName; + } + out.open(filepath.string().c_str()); + } +} + +SVDataSet::~SVDataSet() { + close(); +} + +void SVDataSet::close() { + if (open) { + if (rank == 0) { + out.close(); + } + for (size_t i = 0, n = dataSources.size(); i < n; ++i) { + delete dataSources[i]; + } + dataSources.clear(); + + for (size_t i = 0, n = vars.size(); i < n; ++i) { + delete vars[i]; + } + open = false; + } +} + +void SVDataSet::init() { + if (rank == 0) { + out << "\"tick\""; + for (size_t i = 0; i < dataSources.size(); i++) { + SVDataSource * ds = dataSources[i]; + SVDataSource::DataType type = ds->type(); + Variable* var; + if (type == SVDataSource::INT) + var = new IntVariable(); + else + var = new DoubleVariable(); + vars.push_back(var); + out << _separator << "\"" << ds->name() << "\""; + } + out << std::endl; + out.flush(); + } +} + +void SVDataSet::record() { + if (!open) throw Repast_Error_28(); // Data set not open + if (rank == 0) { + ticks.push_back(_schedule->getCurrentTick()); + } + for (size_t i = 0; i < dataSources.size(); i++) { + dataSources[i]->record(); + } +} + +void SVDataSet::write() { + if (!open) throw Repast_Error_29(); + for (size_t i = 0; i < dataSources.size(); i++) { + SVDataSource * ds = dataSources[i]; + Variable* var = 0; + if (rank == 0) { + var = vars[i]; + } + ds->write(var); + } + + if (rank == 0) { + for (size_t ti = 0, k = ticks.size(); ti < k; ++ti) { + out << ticks[ti]; + for (size_t i = 0, n = vars.size(); i < n; ++i) { + Variable* var = vars[i]; + out << _separator; + var->write(ti, out); + } + out << std::endl; + } + + for (size_t i = 0, n = vars.size(); i < n; ++i) { + vars[i]->clear(); + } + out.flush(); + } + + ticks.clear(); +} + +} diff --git a/libs/repast_hpc/SVDataSet.h b/libs/repast_hpc/SVDataSet.h new file mode 100644 index 0000000..b4865d7 --- /dev/null +++ b/libs/repast_hpc/SVDataSet.h @@ -0,0 +1,108 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DataSet.h + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#ifndef SVDATASET_H_ +#define SVDATASET_H_ + +#include +#include + +#include "Schedule.h" +#include "Variable.h" +#include "SVDataSource.h" +#include "DataSet.h" + +namespace repast { + +class SVDataSetBuilder; + +/** + * Encapsulates data recording to a single plain text file, separating the recorded + * values using a specified separator value. An SVDataSet uses rank 0 to + * write to a single file from multiple pan-process data sources. A SVDataSet + * should be built using a SVDataSetBuilder. + */ +class SVDataSet: public DataSet { + +private: + friend class SVDataSetBuilder; + + std::string _separator; + std::vector dataSources; + std::vector ticks; + std::vector vars; + const Schedule* _schedule; + + std::ofstream out; + bool open; + int rank; + + void init(); + + /** + * Creates a DataSet that will write to the specified file and use the specified + * string as a data value separator. Tick info will be gathered from the specified schedule. + */ + // private so only SVDataSetBuilder can create it + SVDataSet(const std::string& file, const std::string& separator, const Schedule* schedule); + +public: + + ~SVDataSet(); + + /** + * Records data from any added data sources. + */ + void record(); + + /** + * Writes any recorded data to a file. + */ + void write(); + + /** + * Closes the data set. + */ + void close(); +}; + +} + +#endif /* DATASET_H_ */ diff --git a/libs/repast_hpc/SVDataSetBuilder.cpp b/libs/repast_hpc/SVDataSetBuilder.cpp new file mode 100644 index 0000000..e04f51a --- /dev/null +++ b/libs/repast_hpc/SVDataSetBuilder.cpp @@ -0,0 +1,64 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DataSetBuilder.cpp + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#include "SVDataSetBuilder.h" +#include "RepastErrors.h" + +namespace repast { + +SVDataSetBuilder::SVDataSetBuilder(const std::string& file, const std::string& separator, const Schedule& schedule) : + returned(false) { + dataSet = new SVDataSet(file, separator, &schedule); +} + +SVDataSetBuilder& SVDataSetBuilder::addDataSource(SVDataSource* source) { + if (returned) throw Repast_Error_33(); // Data sources can no longer be added after builder dataset constructed + dataSet->dataSources.push_back(source); + return *this; +} + +SVDataSet* SVDataSetBuilder::createDataSet() { + if (returned) throw Repast_Error_34(); // DataSetBuilder can only create a single dataset + dataSet->init(); + returned = true; + return dataSet; +} + +} diff --git a/libs/repast_hpc/SVDataSetBuilder.h b/libs/repast_hpc/SVDataSetBuilder.h new file mode 100644 index 0000000..39a9c5e --- /dev/null +++ b/libs/repast_hpc/SVDataSetBuilder.h @@ -0,0 +1,142 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DataSetBuilder.h + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#ifndef SVDATASETBUILDER_H_ +#define SVDATASETBUILDER_H_ + +#include "SVDataSet.h" +#include "ReducibleDataSource.h" +#include "Schedule.h" + +namespace repast { + +/** + * Creates a SVDataSource with the specified name that will retreive int data from the + * specified TDataSource, and perform the specified reduction Op on it. This function + * is used to add data sources to an SVDataSetBuilder prior to creating an SVDataSet + * from it. + * + * @param name the name of the data source. This will be the name of the variable for which + * data is collected from the TDataSource. + * @param intDataSource the actual source of the data that will be recorded. + * @param op the reduction operation to perform on the recorded data when combining + * the data across processes. + * + * @tparam Op an associative binary function or function object that work with ints. For example, + * std::plus, or mpi::minimum and so on. + */ +template +SVDataSource* createSVDataSource(std::string name, TDataSource* intDataSource, Op op) { + return new ReducibleDataSource (name, intDataSource, op); +} + +/** + * Creates a SVDataSource with the specified name that will retreive double data from the + * specified TDataSource, and perform the specified reduction Op on it. This function + * is used to add data sources to an SVDataSetBuilder prior to creating an SVDataSet + * from it. + * + * @param name the name of the data source. This will be the name of the variable for which + * data is collected from the TDataSource. + * @param doubleDataSource the actual source of the data that will be recorded. + * @param op the reduction operation to perform on the recorded data when combining + * the data across processes. + * + * @tparam Op an associative binary function or function object that work with ints. For example, + * std::plus, or mpi::minimum and so on. + */ +template +SVDataSource* createSVDataSource(std::string name, TDataSource* doubleDataSource, Op op) { + return new ReducibleDataSource (name, doubleDataSource, op); +} + +/** + * Used to build SVDataSets to record data in plain text tabular format. Steps for use + * are: + *
    + *
  1. Create a SVDataSetBuilder. + *
  2. Add SVDataSources to the builder using the createSVDataSource functions. Each + * data source defines a column in the output and where the data for that column will be retrieved. + * Recording data on the SVDataSet produced by the builder will record this data for + * each column. + *
  3. Call createDataSet to create the SVDataSet. + *
  4. Schedule calls to record and write on the SVDataSet. + *
+ */ +class SVDataSetBuilder { + +private: + SVDataSet* dataSet; + bool returned; + +public: + /** + * Creates a SVDataSetBuilder that will create a SVDataSet that will write to the specified file and use the specified + * string as a data value separator. Tick info will be gathered from the specified schedule. + * + * @param file the file path where the data will be recorded to + * @param separator a string used to separate the data values (e.g. a ","). + * + */ + SVDataSetBuilder(const std::string& file, const std::string& separator, const Schedule& schedule); + ~SVDataSetBuilder() { + } + + /** + * Adds a DataSource to the DataSet produced by this builder. The createDataSource functions can + * be used to create Data Sources. Each + * data source defines a column in the output and where the data for that column will be retrieved. + * Recording data on the SVDataSet produced by the builder will record this data for + * each column. + * + * @param source the data source to add + */ + SVDataSetBuilder& addDataSource(SVDataSource* source); + + /** + * Creates the DataSource defined by this builder. This can only be called once. + * The caller is responsible for properly deleting the returned pointer. + */ + SVDataSet* createDataSet(); +}; + +} + +#endif /* DATASETBUILDER_H_ */ diff --git a/libs/repast_hpc/SVDataSource.h b/libs/repast_hpc/SVDataSource.h new file mode 100644 index 0000000..3da24e9 --- /dev/null +++ b/libs/repast_hpc/SVDataSource.h @@ -0,0 +1,102 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DataSource.h + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#ifndef SVDATASOURCE_H_ +#define SVDATASOURCE_H_ + +#include + +#include "Variable.h" + +namespace repast { + +/** + * Data source for data to be written into separated-value + * data sets. + */ +class SVDataSource { + +protected: + std::string _name; + +public: + enum DataType {INT, DOUBLE}; + + SVDataSource(const std::string& name) : _name(name) {} + virtual ~SVDataSource() {}; + virtual void record() = 0; + virtual void write(Variable* var) = 0; + virtual DataType type() const = 0; + + const std::string name() const { + return _name; + } +}; + + +/** + * Base class for specialized int and double type classes + */ +template +struct data_type_traits {}; + +/** + * Int data types for SVDataSource objects + */ +template <> +struct data_type_traits { + static inline SVDataSource::DataType data_type() { + return SVDataSource::INT; + } +}; + +/** + * Double data types for SVDataSource objects + */ +template <> +struct data_type_traits { + static inline SVDataSource::DataType data_type() { + return SVDataSource::DOUBLE; + } +}; + +} + +#endif /* DATASOURCE_H_ */ diff --git a/libs/repast_hpc/Schedule.cpp b/libs/repast_hpc/Schedule.cpp new file mode 100644 index 0000000..f4d8870 --- /dev/null +++ b/libs/repast_hpc/Schedule.cpp @@ -0,0 +1,198 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Schedule.cpp + * + * Created on: Jan 5, 2009 + * Author: nick + */ + +#include "Schedule.h" +#include "Utilities.h" +#include "logger.h" + +#include +#include +#include + +namespace repast { + +RepastEvent::~RepastEvent() { +//std::cout << func_ptr.use_count() << std::endl; +} + +Functor::~Functor() { +} + +ScheduledEvent::ScheduledEvent(double at, RepastEvent *evt) : + event(evt), start(at) { +} + +ScheduledEvent::~ScheduledEvent() { + delete event; +} + +OneTimeEvent::OneTimeEvent(double at, RepastEvent *evt) : + ScheduledEvent(at, evt) { +} + +OneTimeEvent::~OneTimeEvent() { +} + +// does nothing as its a one time event +bool OneTimeEvent::reschedule(std::priority_queue, EventCompare>& queue) { + return false; +} + +RepeatingEvent::RepeatingEvent(double start, double _interval, RepastEvent *evt) : + ScheduledEvent(start, evt), interval(_interval) { +} + +RepeatingEvent::~RepeatingEvent() { +} + +bool RepeatingEvent::reschedule( + std::priority_queue, EventCompare>& queue) { + event->tick += interval; + queue.push(this); + return true; +} + +Schedule::Schedule() : queue(), currentTick(0) { + +} + +Schedule::~Schedule() { + while (!queue.empty()) { + ScheduledEvent *evt = queue.top(); + + queue.pop(); + delete evt; + } +} + +ScheduledEvent* Schedule::schedule_event(double start, FunctorPtr func) { + RepastEvent *evt = new RepastEvent(); + evt->func_ptr = func; + evt->tick = start; + OneTimeEvent *event = new OneTimeEvent(start, evt); + queue.push(event); + return event; +} + +ScheduledEvent* Schedule::schedule_event(double start, double interval, FunctorPtr func) { + RepastEvent *evt = new RepastEvent(); + evt->func_ptr = func; + evt->tick = start; + RepeatingEvent *event = new RepeatingEvent(start, interval, evt); + queue.push(event); + return event; +} + +void Schedule::execute() { + if (!queue.empty()) { + ScheduledEvent *evt = queue.top(); + double next = evt->get_event()->tick; + currentTick = next; + //std::cout << "execute at: " << currentTick << std::endl; + bool go = true; + while (go) { + queue.pop(); + Functor *func = evt->get_event()->func_ptr.get(); + (*func)(); + bool isLive = evt->reschedule(queue); + if (!isLive) delete evt; + if (queue.empty()) + go = false; + else { + evt = queue.top(); + go = evt->get_event()->tick == next; + } + } + } + //std::cout << "execute at: " << getCurrentTick() << std::endl; +} + +ScheduleRunner::ScheduleRunner(boost::mpi::communicator* communicator) : go(true), comm(communicator) { +} + +ScheduleRunner::~ScheduleRunner() {} + +void ScheduleRunner::nextTick() { + localNextTick = schedule_.getNextTick(); +} + +void ScheduleRunner::scheduleStop(double at) { + MethodFunctor *mf = new MethodFunctor (this, &ScheduleRunner::stop); + schedule_.schedule_event(at, Schedule::FunctorPtr(mf)); + nextTick(); +} + +ScheduledEvent* ScheduleRunner::scheduleEvent(double at, Schedule::FunctorPtr func) { + ScheduledEvent *evt = schedule_.schedule_event(at, func); + nextTick(); + return evt; +} + +ScheduledEvent* ScheduleRunner::scheduleEvent(double start, double interval, Schedule::FunctorPtr func) { + ScheduledEvent *evt = schedule_.schedule_event(start, interval, func); + nextTick(); + return evt; +} + +void ScheduleRunner::stop() { + go = false; +} + +void ScheduleRunner::scheduleEndEvent(Schedule::FunctorPtr func) { + endEvents.push_back(func); +} + +void ScheduleRunner::run() { + //Timer timer; + while (go) { + //timer.start(); + all_reduce(*comm, localNextTick, globalNextTick, boost::mpi::minimum());//&localNextTick, &globalNextTick, 1, MPI::DOUBLE, MPI::MIN); + //Log4CL::instance()->get_logger("root").log(INFO, "schedule idle, time: " + boost::lexical_cast(timer.stop())); + if (localNextTick == globalNextTick) + schedule_.execute(); + nextTick(); + } + // execute end events + for (size_t i = 0; i < endEvents.size(); i++) { + (*endEvents[i])(); + } +} + +} diff --git a/libs/repast_hpc/Schedule.h b/libs/repast_hpc/Schedule.h new file mode 100644 index 0000000..025b85a --- /dev/null +++ b/libs/repast_hpc/Schedule.h @@ -0,0 +1,321 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Schedule.h + * + * Created on: Jan 5, 2009 + * Author: nick + */ + +#ifndef SCHEDULE_H_ +#define SCHEDULE_H_ + +#include +#include +#include +#include +#include + +namespace repast { + +/** + * Functor interface. + */ +class Functor { +public: + virtual ~Functor() = 0; + virtual void operator()() = 0; +}; + +/** + * Adapts a no-arg method call on an object instance + * to a Functor interface. This is used by the Schedule + * code to schedule method calls on objects. + * + * @tparam T the object type on which the call will be made. + */ +template +class MethodFunctor: public Functor { +private: + void (T::*fptr)(); + T *obj; +public: + MethodFunctor(T *_obj, void(T::*_fptr)()) : + fptr(_fptr), obj(_obj) { + } + ; + ~MethodFunctor() { + } + ; + void operator()() { + (obj->*fptr)(); + } +}; + +/** + * General class linking a function pointer to a specific tick. + */ +class RepastEvent { + +public: + double tick; + boost::shared_ptr func_ptr; + + virtual ~RepastEvent(); + +}; + +class EventCompare; + +/** + * The object that is placed (scheduled) in the priority queue for execution. + */ +class ScheduledEvent { + +protected: + RepastEvent *event; + double start; + +public: + friend class EventCompare; + ScheduledEvent(double, RepastEvent *); + virtual ~ScheduledEvent(); + /** + * Returns true if this event is rescheduled on the specified queue, otherwise false. + */ + virtual bool reschedule(std::priority_queue, EventCompare>&) = 0; + + /** + * Gets the RepastEvent that this ScheduleEvent wraps. + */ + RepastEvent* get_event() { + return event; + } + ; +}; + +/** + * ScheduledEvent that will only execute only once. + */ +class OneTimeEvent: public ScheduledEvent { + +public: + OneTimeEvent(double, RepastEvent*); + ~OneTimeEvent(); + /** + * Always returns false, as it does not reschedule itself. + */ + virtual bool reschedule(std::priority_queue, EventCompare>&); +}; + +/** + * ScheduledEvent that executes repeatedly. This will + * reschedule itself repeatedly at the appropriate interval. + */ +class RepeatingEvent: public ScheduledEvent { + +private: + double interval; + +public: + RepeatingEvent(double start, double _interval, RepastEvent*); + ~RepeatingEvent(); + virtual bool reschedule(std::priority_queue, EventCompare>&); +}; + +/** + * Compares ScheduledEvents based on their tick times. + */ +class EventCompare { +public: + int operator()(const ScheduledEvent* one, const ScheduledEvent* two) { + double tick1 = one->event->tick; + double tick2 = two->event->tick; + return tick1 > tick2 ? 1 : 0; + } +}; + +/** + * The simulation schedule queue. This wraps a priority queue + * to schedule repast ScheduledEvents. + */ +class Schedule { +private: + typedef std::priority_queue, EventCompare> QueueType; + QueueType queue; + double currentTick; + +public: + /** + * Typedef of for the functors that get scheduled. + */ + typedef boost::shared_ptr FunctorPtr; + virtual ~Schedule(); + Schedule(); + + /** + * Schedule the specified functor to execute once at the specified tick. + * + * @param at the tick to execute at + * @param functor the functor to schedule + * + * @return the event that has been scheduled + */ + ScheduledEvent* schedule_event(double at, FunctorPtr functor); + + /** + * Schedules the specified functor to execute start at start, and at the specified interval + * thereafter. + * + * @param start + * @param interval + * @param func + * + * @return the event that has been scheduled + */ + ScheduledEvent* schedule_event(double start, double interval, FunctorPtr func); + void execute(); + + /** + * Gets the current simulation tick. + * + * @return the current simulation tick. + */ + double getCurrentTick() const { + return currentTick; + } + ; + + /** + * Gets the next tick at which the next events will be executed. + * + * @return the next tick at which the next events will be executed. + */ + double getNextTick() const { + if (queue.empty()) + return -1; + return queue.top()->get_event()->tick; + } + ; +}; + +/** + * Runs the Schedule by popping events off of the Schedule and executing them; + * also provides methods for scheduling events. Simulation events should be + * scheduled for execution using this class which is accessible via + * RepastProcess::instance()->getScheduleRunner() + */ +class ScheduleRunner: public boost::noncopyable { + +private: + + bool go; + Schedule schedule_; + double globalNextTick, localNextTick; + void nextTick(); + boost::mpi::communicator* comm; + std::vector > endEvents; + +public: + ScheduleRunner(boost::mpi::communicator* communicator); + ~ScheduleRunner(); + + /** + * Schedules the Functor to execute at the specified tick. + * + * @param at the time to execute at + * @param func the functor to execute + * + * @return the event that was scheduled for the func + */ + ScheduledEvent* scheduleEvent(double at, Schedule::FunctorPtr func); + + /** + * Schedules the Functor to execute at the specified start tick + * and every interval thereafter. + * + * @param start the time to start at + * @param interval the interval to execute at + * @param func the functor to execute + * + * @return the event that was scheduled for the func + */ + ScheduledEvent* scheduleEvent(double start, double interval, Schedule::FunctorPtr func); + + /** + * Schedules the specified functor to execute when the simulation ends. + * + * @param func the functor to execute when the simulatione ends + */ + void scheduleEndEvent(Schedule::FunctorPtr func); + + /** + * Schedules the simulation to stop at the specified tick. + * + * @param at the tick at which the simulation should stop + */ + void scheduleStop(double at); + + /** + * Starts and runs the simulation schedule. + */ + void run(); + + /** + * Gets the current tick. + * + * @return the current tick + */ + double currentTick() { + return schedule_.getCurrentTick(); + } + + /** + * Stops the simulation. + */ + void stop(); + + /** + * Gets the schedule executed by this simulation runner. + * + * @return the schedule used by this simulation runner. + */ + const Schedule& schedule() { + return schedule_; + } +}; + +} + +#endif /* SCHEDULE_H_ */ diff --git a/libs/repast_hpc/SharedBaseGrid.cpp b/libs/repast_hpc/SharedBaseGrid.cpp new file mode 100644 index 0000000..dddaef6 --- /dev/null +++ b/libs/repast_hpc/SharedBaseGrid.cpp @@ -0,0 +1,138 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SharedGrid.cpp + * + * Created on: Sep 11, 2009 + * Author: nick + */ + +#include "SharedBaseGrid.h" + +#include +#include + +using namespace std; + +namespace repast { + + + +Neighbor::Neighbor(int rank, GridDimensions bounds) : + _rank(rank), _bounds(bounds) { +} + +Neighbors::Neighbors(int numberOfNeighbors){ + nghs.assign(numberOfNeighbors, 0); +} + +void Neighbors::addNeighbor(Neighbor* ngh, RelativeLocation relLoc) { + nghs[relLoc.getIndex()] = ngh; +} + +Neighbor* Neighbors::neighbor(RelativeLocation relLoc) const { + return nghs[relLoc.getIndex()]; +} + +Neighbor* Neighbors::findNeighbor(const std::vector& pt) { + for (std::vector::iterator iter = nghs.begin(); iter != nghs.end(); ++iter) { + Neighbor* ngh = *iter; + if ((ngh != 0) && (ngh->bounds().contains(pt))) + return ngh; + } + return 0; +} + +Neighbor* Neighbors::findNeighbor(const std::vector& pt) { + for (std::vector::iterator iter = nghs.begin(); iter != nghs.end(); ++iter) { + Neighbor* ngh = *iter; + if ((ngh != 0) && (ngh->bounds().contains(pt))) + return ngh; + } + return 0; +} + +Neighbors::~Neighbors() { + for (vector::iterator iter = nghs.begin(); iter != nghs.end(); iter++) { + Neighbor* ngh = *iter; + delete ngh; + } +} + +ostream& operator<<(ostream& os, const Neighbors& nghs) { +// Neighbor* ngh = nghs.neighbor(Neighbors::NW); +// if (ngh != 0) { +// os << "\tNW ngh: " << ngh->rank() << ": " << ngh->bounds() << "\n"; +// } +// +// ngh = nghs.neighbor(Neighbors::NE); +// if (ngh != 0) { +// os << "\tNE ngh: " << ngh->rank() << ": " << ngh->bounds() << "\n"; +// } +// +// ngh = nghs.neighbor(Neighbors::SW); +// if (ngh != 0) { +// os << "\tSW ngh: " << ngh->rank() << ": " << ngh->bounds() << "\n"; +// } +// +// ngh = nghs.neighbor(Neighbors::SE); +// if (ngh != 0) { +// os << "\tSE ngh: " << ngh->rank() << ": " << ngh->bounds() << "\n"; +// } +// +// ngh = nghs.neighbor(Neighbors::N); +// if (ngh != 0) { +// os << "\tN ngh: " << ngh->rank() << ": " << ngh->bounds() << "\n"; +// } +// +// ngh = nghs.neighbor(Neighbors::S); +// if (ngh != 0) { +// os << "\tS ngh: " << ngh->rank() << ": " << ngh->bounds() << "\n"; +// } +// +// ngh = nghs.neighbor(Neighbors::E); +// if (ngh != 0) { +// os << "\tE ngh: " << ngh->rank() << ": " << ngh->bounds() << "\n"; +// } +// +// ngh = nghs.neighbor(Neighbors::W); +// if (ngh != 0) { +// os << "\tW ngh: " << ngh->rank() << ": " << ngh->bounds() << "\n"; +// } + + return os; + +} + +} diff --git a/libs/repast_hpc/SharedBaseGrid.h b/libs/repast_hpc/SharedBaseGrid.h new file mode 100644 index 0000000..23e8661 --- /dev/null +++ b/libs/repast_hpc/SharedBaseGrid.h @@ -0,0 +1,488 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SharedBaseGrid.h + * + * Created on: Sep 10, 2009 + * Author: nick + */ + +#ifndef SHAREDBASEGRID_H_ +#define SHAREDBASEGRID_H_ + +#include +#include +#include +#include +#include + +#include "BaseGrid.h" +#include "GridComponents.h" +#include "MultipleOccupancy.h" +#include "RepastProcess.h" +#include "logger.h" +#include "SRManager.h" +#include "RepastErrors.h" +#include "RelativeLocation.h" +#include "CartesianTopology.h" + +namespace repast { + + +/** + * Contains the rank and boundaries of a semantically adjacent + * process (that is, a process that manages the space that is + * adjacent to the simulation space managed by this process). + */ +class Neighbor { + +private: + int _rank; + GridDimensions _bounds; + +public: + Neighbor(int rank, GridDimensions bounds); + + int rank() const { return _rank; } + GridDimensions bounds() const { return _bounds; } + +}; + + +/** + * Provides lookup of grid topology process neighbors given + * a point in the pan process grid. + */ +class Neighbors { + + friend std::ostream& operator<<(std::ostream& os, const Neighbors& nghs); + +private: + + std::vector nghs; + + +public: + + Neighbors(int numberOfNeighbors); + virtual ~Neighbors(); + + /** + * Adds a neighbor at the specified location. + * + */ + void addNeighbor(Neighbor* ngh, RelativeLocation relLoc); + + /** + * Gets the neighbor at the specified location. + * + * @param location the location of the neighbor. + */ + Neighbor* neighbor(RelativeLocation) const; + + /** + * Finds the neighbor that contains the specified point. + * + * @return the found neighbor + */ + Neighbor* findNeighbor(const std::vector& pt); + + /** + * Finds the neighbor that contains the specified point. + * + * @return the found neighbor + */ + Neighbor* findNeighbor(const std::vector& pt); + + void getNeighborRanks(std::set& ranks) { + for(std::vector::iterator iter = nghs.begin(), iterEnd=nghs.end(); iter != iterEnd; ++iter) { + Neighbor* ngh = *iter; + if (ngh != 0) { + ranks.insert((*iter)->rank()); + } + } + } + + Neighbor* getNeighborByIndex(int index){ + if(index < 0|| index >= nghs.size()) return 0; + return nghs[index]; + } + +}; + +std::ostream& operator<<(std::ostream& os, const Neighbors& nghs); + + + +/** + * Grid / Space implementation specialized for the distributed context. + * Each SharedBaseGrid of the same name running on different processes + * is part of a pan process grid. This class manages this local part of + * the grid and its communication with its process neighbors. Users + * can specify a buffer size that determines how much of the neighboring grids + * are visible in this grid. For example, if this grid originates at 0x0 and ends + * at 3x3, a buffer of 1 would make the locations (4,0), (4,1) (4,2) ... (4,4) + * and (0,4), (1,4)... (4,4) visible in this grid. The SharedBaseGrid takes many + * template parameters. Default variations of these that define typical grids and + * spaces are given in SharedGrids in SharedSpace.h + * + * @tparam T the type of objects contained by this BaseGrid + * @tparam GPTransformer transforms cell points according to the topology (e.g. periodic) + * of the BaseGrid. + * @tparam Adder determines how objects are added to the grid from its associated context. + * @tparam GPType the coordinate type of the grid point locations. This must + * be an int or a double. + */ +template +class SharedBaseGrid: public BaseGrid , GPTransformer, Adder, GPType> { + +private: + CartesianTopology* cartTopology; + +protected: + int _buffer; + GridDimensions localBounds; + GridDimensions globalBounds; + Neighbors* nghs; + // vector of ids of agents in this spaces buffer + std::vector buffered; +// GridDimensions createSendBufferBounds(std::vector relativeLocation); + + virtual void synchMoveTo(const AgentId& id, const Point& pt) = 0; + + int rank; + typedef typename repast::BaseGrid , GPTransformer, Adder, GPType> GridBaseType; + boost::mpi::communicator* comm; + +public: + void balance(); + + // overriding moveTo that takes newLocation hides the moveTo in the + // Grid base class that takes a Point. This using directive + // makes the Point arg moveTo available. + using GridBaseType::moveTo; + + /** + * Creates a SharedGrid with the specified name. + * + * @param name the name of this SharedBaseGrid + * @param gridDims the dimensions of the entire pan-process grid + * @param processDims the number of processes in each dimension. This must + * divide evenly into gridDims. + * @param buffer the size of the buffer between this part of the pan-process grid + * and its neighbors. + */ + SharedBaseGrid(std::string name, GridDimensions gridDims, std::vector processDims, int buffer, boost::mpi::communicator* communicator); + virtual ~SharedBaseGrid(); + + /** + * Gets the global bounds for this grid + * + * @return the global bounds of this SharedGrid. + */ + virtual GridDimensions const bounds() const { + return globalBounds; + } + + + /** + * Gets the local bounds of this SharedGrid. The local bounds + * are the dimensions of the section of the pan-process grid represented + * by this SharedGrid. + * + * @return the local bounds of this SharedGrid. + */ + virtual const GridDimensions dimensions() const { + return localBounds; + } + + // doc inherited from BaseGrid.h + virtual bool moveTo(const AgentId& id, const std::vector& newLocation); + + // doc inherited from BaseGrid.h + virtual bool moveTo(const AgentId& id, const Point& pt); + + virtual void removeAgent(T* agent); + + // doc inherited from Projection.h + + virtual void getRequiredAgents(std::set& agentsToTest, std::set& agentsRequired){ } // Grids don't keep agents + + virtual void getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush); + + + virtual void getInfoExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom){ + nghs->getNeighborRanks(psToSendTo); + nghs->getNeighborRanks(psToReceiveFrom); + } + + virtual void getAgentStatusExchangePartners(std::set& psToSendTo, std::set& psToReceiveFrom){ + nghs->getNeighborRanks(psToSendTo); + nghs->getNeighborRanks(psToReceiveFrom); + } + + virtual void updateProjectionInfo(ProjectionInfoPacket* pip, Context* context); + +}; + +template +SharedBaseGrid::SharedBaseGrid(std::string name, GridDimensions gridDims, std::vector< + int> processDims, int buffer, boost::mpi::communicator* communicator) : + GridBaseType(name, gridDims), _buffer(buffer), comm(communicator), globalBounds(gridDims) { + + int dimCount = gridDims.dimensionCount(); + if (processDims.size() != dimCount) + throw Repast_Error_50(dimCount, gridDims, processDims.size()); // Number of grid dimensions must be equal to number of process dimensions + + rank = comm->rank(); + bool periodic = GridBaseType::gpTransformer.isPeriodic(); + + cartTopology = RepastProcess::instance()->getCartesianTopology(processDims, periodic); + + std::vector coords; + cartTopology->getCoordinates(rank, coords); + + localBounds = cartTopology->getDimensions(rank, gridDims); + GridBaseType::adder.init(localBounds, this); + + RelativeLocation relLocUntrimmed(dimCount); + RelativeLocation relLoc = cartTopology->trim(rank, relLocUntrimmed); + + nghs = new Neighbors(relLoc.getMaxIndex() + 1); + + do{ + vector currentVal = relLoc.getCurrentValue(); + int rankOfNeighbor = cartTopology->getRank(coords, currentVal); + if(rankOfNeighbor != rank && rankOfNeighbor != MPI_PROC_NULL){ // Note: the test for MPI_PROC_NULL is vestigial; by trimming the Relative Location, there should never be any + Neighbor* ngh = new Neighbor(rankOfNeighbor, cartTopology->getDimensions(rankOfNeighbor, gridDims)); + nghs->addNeighbor(ngh, relLoc); + } + }while(relLoc.increment()); + +} + +template +SharedBaseGrid::~SharedBaseGrid() { + delete nghs; +} + + +//template +//GridDimensions SharedBaseGrid::createSendBufferBounds(std::vector relativeLocation) { +// Point localOrigin = localBounds.origin(); +// Point localExtent = localBounds.extents(); +// +//// switch (location) { +//// double xStart, yStart; +////case Neighbors::E: +//// xStart = localOrigin.getX() + localExtent.getX() - _buffer; +//// return GridDimensions(Point (xStart, localOrigin.getY()), Point (_buffer, localExtent.getY())); +//// +////case Neighbors::W: +//// return GridDimensions(localOrigin, Point (_buffer, localExtent.getY())); +//// +////case Neighbors::N: +//// return GridDimensions(localOrigin, Point (localExtent.getX(), _buffer)); +//// +////case Neighbors::S: +//// yStart = localOrigin.getY() + localExtent.getY() - _buffer; +//// return GridDimensions(Point (localOrigin.getX(), yStart), Point (localExtent.getX(), _buffer)); +//// +////case Neighbors::NE: +//// xStart = localOrigin.getX() + localExtent.getX() - _buffer; +//// return GridDimensions(Point (xStart, localOrigin.getY()), Point (_buffer, _buffer)); +//// +////case Neighbors::NW: +//// return GridDimensions(Point (localOrigin.getX(), localOrigin.getY()), Point (_buffer, _buffer)); +//// +////case Neighbors::SE: +//// xStart = localOrigin.getX() + localExtent.getX() - _buffer; +//// yStart = localOrigin.getY() + localExtent.getY() - _buffer; +//// return GridDimensions(Point (xStart, yStart), Point (_buffer, _buffer)); +//// +////case Neighbors::SW: +//// yStart = localOrigin.getY() + localExtent.getY() - _buffer; +//// return GridDimensions(Point (localOrigin.getX(), yStart), Point (_buffer, _buffer)); +//// } +// +// return GridDimensions(); +//} + + +template +void SharedBaseGrid::balance() { + int r = comm->rank(); + typename GridBaseType::LocationMapConstIter iterEnd = GridBaseType::locationsEnd(); + for (typename GridBaseType::LocationMapConstIter iter = GridBaseType::locationsBegin(); iter != iterEnd; ++iter) { + AgentId id = iter->second->ptr->getId(); + if(id.currentRank() == r){ // Local agents only + Point loc = iter->second->point; + if(!localBounds.contains(loc)){ // If inside bounds, ignore + Neighbor* ngh = nghs->findNeighbor(loc.coords()); + RepastProcess::instance()->moveAgent(id, ngh->rank()); + } + } + } +} + +template +bool SharedBaseGrid::moveTo(const AgentId& id, const Point& newLocation) { + return SharedBaseGrid::moveTo(id, newLocation.coords()); +} + +template +bool SharedBaseGrid::moveTo(const AgentId& id, const std::vector& newLocation) { + return GridBaseType::moveTo(id, newLocation); +} + +template +void SharedBaseGrid::removeAgent(T* agent) { + GridBaseType::removeAgent(agent); +} + + +// Beta + +template +void SharedBaseGrid::getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush){ + + if(_buffer == 0) return; // A buffer zone of zero means that no agents will be pushed. + + int numDims = localBounds.dimensionCount(); + RelativeLocation relLocOrig(numDims); + + RelativeLocation relLoc = cartTopology->trim(comm->rank(), relLocOrig); + + // First, create a zone around the center of this process, inside all of + // of the buffer zones + std::vector unbufferedOrigin; + std::vector unbufferedExtents; + + for(int i = 0; i < numDims; i++){ + bool hasLeft = relLoc.getMinimumAt(i) < 0; + bool hasRight = relLoc.getMaximumAt(i) > 0; + unbufferedOrigin.push_back(localBounds.origin(i) + (hasLeft ? _buffer : 0)); + unbufferedExtents.push_back(localBounds.extents(i) - (hasLeft ? _buffer : 0) - (hasRight ? _buffer : 0)); + } + + Point ubO(unbufferedOrigin); + Point ubE(unbufferedExtents); + GridDimensions unbuffered(ubO, ubE); + + // And create grid boundaries for all the buffer zones + int numOutgoing = relLoc.getMaxIndex() + 1; + + GridDimensions** outgoing = new GridDimensions*[numOutgoing]; + int* outRanks = new int[numOutgoing]; + + do{ + std::vector bufferOrigin; + std::vector bufferExtents; + + bool isEgo = true; + for(int i = 0; i < numDims; i++){ + int rel = relLoc[i]; + + if(rel == 0){ + bufferOrigin.push_back(localBounds.origin(i)); + bufferExtents.push_back(localBounds.extents(i)); + } + else{ + if(rel < 0){ + bufferOrigin.push_back(localBounds.origin(i)); + } + else{ + bufferOrigin.push_back(localBounds.origin(i) + localBounds.extents(i) - _buffer); + } + bufferExtents.push_back(_buffer); + isEgo = false; + } + } + + // Should not add self! + int index = relLoc.getIndex(); + if(!isEgo){ + outgoing[index] = new GridDimensions(Point(bufferOrigin), Point (bufferExtents)); + outRanks[index] =nghs->getNeighborByIndex(index)->rank(); + } + else{ + outgoing[index] = 0; + outRanks[index] = 0; + } + + }while(relLoc.increment()); + + + // Local agents that are in other processes' 'buffer zones' must be exported to those other processes. + int r = comm->rank(); + std::set::iterator idIter = agentsToTest.begin(); + while(idIter != agentsToTest.end()){ + AgentId id = *idIter; + bool found = false; + if(id.currentRank() == r){ // Local agents only + std::vector locationVector; + GridBaseType::getLocation(id, locationVector); + Point loc(locationVector); + if(!unbuffered.contains(loc)){ + for(int i = 0; i < numOutgoing; i++){ + if ((outgoing[i] != NULL) && (outgoing[i]->contains(loc))) { + agentsToPush[outRanks[i]].insert(id); + found = true; + } + } + } + } + if(found){ + std::set::iterator tmp = idIter; + idIter++; + agentsToTest.erase(tmp); + } + else{ + idIter++; + } + } +// if(NW_set.size() > 0) agentsToPush[NW_rank].insert(NW_set.begin(), NW_set.end()); + delete[] outgoing; + delete[] outRanks; +} + +template +void SharedBaseGrid::updateProjectionInfo(ProjectionInfoPacket* pip, Context* context){ + SpecializedProjectionInfoPacket* spip = static_cast*>(pip); + synchMoveTo(spip->id, spip->data); +} + +} + +#endif /* SHAREDBASEGRID_H_ */ diff --git a/libs/repast_hpc/SharedContext.cpp b/libs/repast_hpc/SharedContext.cpp new file mode 100644 index 0000000..cedf31d --- /dev/null +++ b/libs/repast_hpc/SharedContext.cpp @@ -0,0 +1,50 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SharedContext.cpp + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#include "SharedContext.h" +#include "RepastProcess.h" + +namespace repast { + +void rpRemoveAgent(const AgentId& id) { + RepastProcess::instance()->agentRemoved(id); +} + +} diff --git a/libs/repast_hpc/SharedContext.h b/libs/repast_hpc/SharedContext.h new file mode 100644 index 0000000..3883090 --- /dev/null +++ b/libs/repast_hpc/SharedContext.h @@ -0,0 +1,1104 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SharedContext.h + * + * Created on: Jun 18, 2009 + * Author: nick + */ + +#ifndef SHAREDCONEXT_HPP_ +#define SHAREDCONEXT_HPP_ + +#include "Context.h" +#include "RepastErrors.h" + +#include +#include + +namespace repast { + +/** + * Used in a filter iterator to filter on local agents only + */ +template +struct IsLocalAgent { + int rank; + IsLocalAgent(int rankInCommunicator): rank(rankInCommunicator){ } + + bool operator()(const boost::shared_ptr& ptr) { + return ptr->getId().currentRank() == rank; + } + +}; + +/** + * Used in a filter iterator to filter on local or non-local agents only + */ +template +struct AgentStateFilter{ + int rank; + bool local; + AgentStateFilter(int rankInCommunicator): rank(rankInCommunicator){ + local = true; + } + AgentStateFilter(bool localFlag, int rankInCommunicator): rank(rankInCommunicator), local(localFlag) { } + + bool operator()(const boost::shared_ptr& ptr) { + return (local ? ptr->getId().currentRank() == rank : ptr->getId().currentRank() != rank); + } +}; +// +///* +// * An instance of the AgentStateFilter that filters for local agents +// */ +//template +//struct LocalFilter: public AgentStateFilter{ +// LocalFilter(int rank): AgentStateFilter(true, rank){} +//}; +// +///* +// * An instance of the AgentStateFilter that filters for nonlocal agents +// */ +//template +//struct NonLocalFilter: public AgentStateFilter{ +// NonLocalFilter(int rank): AgentStateFilter(false, rank){} +//}; +// + +/** + * Used to remove agents. + */ +void rpRemoveAgent(const AgentId& id); + + +/** + * Context implementation specialized for the parallel distributed + * simulation. A SharedContext contains both local, that is, agents + * whose behavior is run on the SharedContext's process and foreign agents, + * that is, copies of agents whose behavior is run on some other process. + * + * @param T the type of agents in the context. + */ +template +class SharedContext: public Context { + +private: + + typedef typename boost::unordered_map RefMap; + + // holds reference count to foreign agents that are + // referenced by projections. If a projection removes an + // agent from a context, this should be checked to make sure + // no other projections hold a reference before actually deleting + // the agent. + RefMap projRefMap; + int _rank; + +public: + + // Create single instances for these and reuse them + IsLocalAgent localPredicate; + AgentStateFilter LOCAL_FILTER; + AgentStateFilter NON_LOCAL_FILTER; + + // For more efficient 'push' during projection sync + std::vector getAgentsToPushProjOrder; + + typedef typename boost::filter_iterator , typename Context::const_iterator> const_local_iterator; + + typedef typename boost::filter_iterator , typename Context::const_iterator> const_state_aware_iterator; + typedef typename boost::filter_iterator , typename Context::const_bytype_iterator> const_state_aware_bytype_iterator; + + typedef typename Projection::RADIUS RADIUS; + + SharedContext(boost::mpi::communicator* comm); + virtual ~SharedContext(); + + /** + * Gets the start of iterator over the local agents in this context. + * The iterator derefrences into shared_ptr. The actual + * agent can be accessed by dereferencing the iter: (*iter)->getId() for example. + * + * @return the start of iterator over the local agents in this context. + */ + const_local_iterator localBegin() const; + + /** + * Gets the end of an iterator over the local agents in this context. + * The iterator derefrences into shared_ptr. The actual + * agent can be accessed by derefrenceing the iter: (*iter)->getId() for example. + */ + const_local_iterator localEnd() const; + + /** + * Removes the specified agent from this context. If the + * agent is non-local, this checks to make sure that it + * is not referenced by any projection before its removed. + * + * @param id the id of the agent to remove + */ + void removeAgent(const AgentId id); + + /** + * Removes the specified agent from this context. If the + * agent is non-local, this checks to make sure that it + * is not referenced by any projection before its removed. + * + * @param agent the agent to remove + */ + void removeAgent(T* agent); + + /** + * Notifies this context that the specified non-local agent + * has been removed and this context should then delete that + * agent from itself. + * + * @param id the id of the agent that was removed + */ + void importedAgentRemoved(const AgentId& id); + + /** + * Increments the projection reference count for the specified + * agent. + * + * @param id the id of the agent + */ + void incrementProjRefCount(const AgentId& id); + + /** + * Decrements the projection reference count for the specified agent. + * + * @param id the id of the agent + */ + void decrementProjRefCount(const AgentId& id); + + /* + * Used as an argument to the 'selectAgents' routines; + * cannot use an 'int' because doing so would mask + * the different versions of these routines + */ + enum filterLocalFlag{ + LOCAL = 1, + NON_LOCAL = 0 + }; + + + // Unhide these from the parent class so operation is transparent + using Context::begin; + using Context::end; + using Context::byTypeBegin; + using Context::byTypeEnd; + using Context::filteredBegin; + using Context::filteredEnd; + using Context::byTypeFilteredBegin; + using Context::byTypeFilteredEnd; + using Context::size; + + /** + * Gets the start of an iterator that will iterate over only local or non-local agents + * + * @param local flag indicating whether local or non-local agents are to be included + */ + const_state_aware_iterator begin(filterLocalFlag local); + + /** + * Gets the end of an iterator that will iterate over only local or non-local agents + * + * @param local flag indicating whether local or non-local agents are to be included + */ + const_state_aware_iterator end(filterLocalFlag local); + + /** + * Gets the start of an iterator that will iterate over only local or non-local agents of a certain + * type (per their AgentId value) + * + * @param local flag indicating whether local or non-local agents are to be included + * @param type type to included + */ + const_state_aware_bytype_iterator byTypeBegin(filterLocalFlag local, int type); + + /** + * Gets the end of an iterator that will iterate over only local or non-local agents of a certain + * type (per their AgentId value) + * + * @param local flag indicating whether local or non-local agents are to be included + * @param type type to included + */ + const_state_aware_bytype_iterator byTypeEnd(filterLocalFlag local, int type); + + + /** + * Gets the start of an iterator that will iterate over only local or non-local agents meeting + * the criteria of the user-defined struct (see IsLocalAgent for an example) + * + * @param local flag indicating whether local or non-local agents are to be included + * @param filter struct with unary operator (boost::shared_ptr) that returns + * true or false; used to selectively include agents. + * + * @tparam filterStruct the class of the filter to be used + */ + template + boost::filter_iterator::const_state_aware_iterator> filteredBegin(filterLocalFlag local, filterStruct& fStruct); + + /** + * Gets the end of an iterator that will iterate over only local or non-local agents meeting + * the criteria of the user-defined struct (see IsLocalAgent for an example) + * + * @param local flag indicating whether local or non-local agents are to be included + * @param filter struct with unary operator (boost::shared_ptr) that returns + * true or false; used to selectively include agents. + * + * @tparam filterStruct the class of the filter to be used + */ + template + boost::filter_iterator::const_state_aware_iterator> filteredEnd(filterLocalFlag local, filterStruct& fStruct); + + + /** + * Gets the start of an iterator that will iterate over only local or non-local agents + * of the specified type and meeting the criteria of the user-defined struct (see IsLocalAgent for an example) + * + * @param local flag indicating whether local or non-local agents are to be included + * @param type type to be included + * @param filter struct with unary operator (boost::shared_ptr) that returns + * true or false; used to selectively include agents. + * + * @tparam filterStruct the class of the filter to be used + */ + template + boost::filter_iterator::const_state_aware_bytype_iterator> byTypeFilteredBegin(filterLocalFlag local, int type, filterStruct& fStruct); + + /** + * Gets the end of an iterator that will iterate over only local or non-local agents + * of the specified type and meeting the criteria of the user-defined struct (see IsLocalAgent for an example) + * + * @param local flag indicating whether local or non-local agents are to be included + * @param type type to be included + * @param filter struct with unary operator (boost::shared_ptr) that returns + * true or false; used to selectively include agents. + * + * @tparam filterStruct the class of the filter to be used + */ + template + boost::filter_iterator::const_state_aware_bytype_iterator> byTypeFilteredEnd(filterLocalFlag local, int type, filterStruct& fStruct); + + + // Select Methods + + // Unhide the parent class's versions + using Context::selectAgents; + + + /** + * Gets a set of pointers to all local or non-local agents in this context. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(filterLocalFlag localOrNonLocalOnly, std::set& selectedAgents, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to all local or non-local agents in this context. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(filterLocalFlag localOrNonLocalOnly, std::vector& selectedAgents, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to a specified number of randomly selected + * local or non-local agents. + * + * If the set passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::set& selectedAgents, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to a specified number of randomly selected + * local or non-local agents. + * + * If the vector passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::vector& selectedAgents, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to all local or non-local agents in this context + * of a specified type (per their AgentId values). + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(filterLocalFlag localOrNonLocalOnly, std::set& selectedAgents, int type, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to all local or non-local agents in this context + * of a specified type (per their AgentId values). + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(filterLocalFlag localOrNonLocalOnly, std::vector& selectedAgents, int type, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to a specified number of randomly selected local or non-local agents + * of a specified type (per their AgentId values). + * + * If the set passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::set& selectedAgents, int type, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to a specified number of randomly selected + * local or non-local agents of a specified type (per their AgentId values). + * + * If the vector passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + */ + void selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::vector& selectedAgents, int type, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to all local or non-local agents in this + * context matching a user-defined filter. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(filterLocalFlag localOrNonLocalOnly, std::set& selectedAgents, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to all local or non-local agents in this context + * matching a user-defined filter. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(filterLocalFlag localOrNonLocalOnly, std::vector& selectedAgents, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to a specified number of randomly selected local or non-local agents + * matching a user-defined filter. + * + * If the set passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::set& selectedAgents, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to a specified number of randomly selected local or non-local agents + * matching a user-defined filter. + * + * If the vector passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::vector& selectedAgents, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to all local or non-local agents in this context + * of a specified type (per their AgentId values) and matching a user-defined filter. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(filterLocalFlag localOrNonLocalOnly, std::set& selectedAgents, int type, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to all local or non-local agents in this context + * of a specified type (per their AgentId values) and matching a user-defined filter. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(filterLocalFlag localOrNonLocalOnly, std::vector& selectedAgents, int type, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a set of pointers to a specified number of randomly selected local or non-local agents + * of a specified type (per their AgentId values) and matching a user-defined filter. + * + * If the set passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original set will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a set into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::set& selectedAgents, int type, filterStruct& filter, bool remove = false, int popSize = -1); + + /** + * Gets a randomly ordered vector of pointers to a specified number of randomly selected + * local or non-local agents of a specified type (per their AgentId values) and + * matching a user-defined filter. + * + * If the vector passed contains any elements when this method is called, + * the agents pointed to by those elements will be omitted from the selection. + * + * If the 'remove' parameter is set to true, any elements in the + * original vector will be removed before the method returns. + * + * The popSize parameter is used when the method is repeatedly called + * on a population whose size is known. Calls to this method typically begin + * by determining the size of the (valid) population to be sampled; if this is known, + * it can be provided here, improving performance. + * + * @param localOrNonLocalOnly flag that indicates that the agents selected + * will be drawn only from agents either local or non-local to this process + * @param count the number of agents to be selected. If this exceeds the number + * that can possibly be selected, all possible agents will be selected + * @param [out] selectedAgents a vector into which the pointers to the agents will be placed + * @param type numeric type of agent to be selected + * @param filter user-defined filter specifying any criteria agents to be selected + * must meet + * @param remove if true, remove any elements originally in the set before the + * set is returned (default is false) + * @param popSize size of the population from which the sample will be drawn + * + * @tparam filterStruct the type of the filter to be applied to the agents + */ + template + void selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::vector& selectedAgents, int type, filterStruct& filter, bool remove = false, int popSize = -1); + + + // Beta + + /** + * Returns true if any of the projections in this context will try to 'keep' non-local agents + * during a synchronize projection operation. (Generally graphs keep local agents that are + * part of master copies of links, but spaces do not keep any local agents.) + */ + bool keepsAgentsOnSyncProj(); + + bool sendsSecondaryDataOnStatusExchange(); + + void getProjInfoExchangePartners(std::set& sends, std::set& recvs); + + void getAgentStatusInfoExchangePartners(std::set& sends, std::set& recvs); + + /** + * Given a set of agents to test, returns the set of those agents that must be kept in order + * to keep required projection information. + */ + void getRequiredAgents(std::set& agentsToTest, std::set& agentsToKeep, RADIUS radius = Projection::PRIMARY); + + /** + * Given an initial set of agents that must be kept a priori, add any agents that must be kept due to + * projection requirements, and return the set of all non-local agents that can be dropped. + */ + void getNonlocalAgentsToDrop(std::set& agentsToKeep, std::set& agentsToDrop, RADIUS radius = Projection::PRIMARY); + + void getAgentsToPushToOtherProcesses(std::map >& agentsToPush); + + virtual void addProjection(Projection* projection); + +}; + +template +SharedContext::SharedContext(boost::mpi::communicator* comm) : Context (), _rank(comm->rank()), localPredicate(comm->rank()), + LOCAL_FILTER(true, comm->rank()), + NON_LOCAL_FILTER(false, comm->rank()){ +} + +template +SharedContext::~SharedContext() { } + +template +void SharedContext::removeAgent(T* agent) { + removeAgent(agent->getId()); +} + +template +void SharedContext::removeAgent(const AgentId id) { + if (id.currentRank() != _rank) { + if (projRefMap.find(id) == projRefMap.end()) { + Context::removeAgent(id); + } + } else { + Context::removeAgent(id); + rpRemoveAgent(id); + } +} + +template +void SharedContext::importedAgentRemoved(const AgentId& id) { + projRefMap.erase(id); + Context::removeAgent(id); +} + +template +void SharedContext::incrementProjRefCount(const AgentId& id) { + if (id.currentRank() != _rank) { + RefMap::iterator iter = projRefMap.find(id); + if (iter == projRefMap.end()) { + projRefMap[id] = 1; + } else { + projRefMap[id] = ++(iter->second); + } + } +} + +template +void SharedContext::decrementProjRefCount(const AgentId& id) { + if (id.currentRank() == _rank) return; + + RefMap::iterator iter = projRefMap.find(id); + if (iter == projRefMap.end()) throw Repast_Error_31(id); // Id is not in the projection reference map + + int count = --(iter->second); + if (count == 0) projRefMap.erase(iter); + else projRefMap[id] = count; + +} + +// Local Agents Only + +template +boost::filter_iterator , typename Context::const_iterator> SharedContext::localBegin() const { + return const_local_iterator(localPredicate, Context::begin(), Context::end()); +} + +template +boost::filter_iterator , typename Context::const_iterator> SharedContext::localEnd() const { + return const_local_iterator(localPredicate, Context::end(), Context::end()); +} + + +// Iterator creation + +template +boost::filter_iterator , typename Context::const_iterator> SharedContext::begin(filterLocalFlag local){ + if(local) return const_state_aware_iterator(LOCAL_FILTER, Context::begin(), Context::end()); + else return const_state_aware_iterator(NON_LOCAL_FILTER, Context::begin(), Context::end()); +} + +template +boost::filter_iterator , typename Context::const_iterator> SharedContext::end(filterLocalFlag local){ + if(local) return const_state_aware_iterator(LOCAL_FILTER, Context::end(), Context::end()); + else return const_state_aware_iterator(NON_LOCAL_FILTER, Context::end(), Context::end()); +} + +template +boost::filter_iterator , typename Context::const_bytype_iterator> SharedContext::byTypeBegin(filterLocalFlag local, int type){ + if(local) return const_state_aware_bytype_iterator(LOCAL_FILTER, Context::byTypeBegin(type), Context::byTypeEnd(type)); + else return const_state_aware_bytype_iterator(NON_LOCAL_FILTER, Context::byTypeBegin(type), Context::byTypeEnd(type)); +} + +template +boost::filter_iterator , typename Context::const_bytype_iterator> SharedContext::byTypeEnd(filterLocalFlag local, int type){ + if(local) return const_state_aware_bytype_iterator(LOCAL_FILTER, Context::byTypeEnd(type), Context::byTypeEnd(type)); + else return const_state_aware_bytype_iterator(NON_LOCAL_FILTER, Context::byTypeEnd(type), Context::byTypeEnd(type)); +} + +template +template +boost::filter_iterator::const_state_aware_iterator> SharedContext::filteredBegin(filterLocalFlag local, filterStruct& fStruct){ + return boost::filter_iterator::const_state_aware_iterator> (fStruct, SharedContext::begin(local), SharedContext::end(local)); +} + +template +template +boost::filter_iterator::const_state_aware_iterator> SharedContext::filteredEnd(filterLocalFlag local, filterStruct& fStruct){ + return boost::filter_iterator::const_state_aware_iterator> (fStruct, SharedContext::end(local), SharedContext::end(local)); +} + + +template +template +boost::filter_iterator::const_state_aware_bytype_iterator> SharedContext::byTypeFilteredBegin(filterLocalFlag local, int type, filterStruct& fStruct){ + return boost::filter_iterator::const_state_aware_bytype_iterator> (fStruct, SharedContext::byTypeBegin(local, type), SharedContext::byTypeEnd(local, type)); +} + +template +template +boost::filter_iterator::const_state_aware_bytype_iterator> SharedContext::byTypeFilteredEnd(filterLocalFlag local, int type, filterStruct& fStruct){ + return boost::filter_iterator::const_state_aware_bytype_iterator> (fStruct, SharedContext::byTypeEnd(local, type), SharedContext::byTypeEnd(local, type)); +} + + +// Agent Selection + +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, std::set& selectedAgents, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(begin(localOrNonLocalOnly), end(localOrNonLocalOnly), size(), selectedAgents, remove); + else selectNElementsAtRandom(begin(localOrNonLocalOnly), popSize, size(), selectedAgents, remove); +} + +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, std::vector& selectedAgents, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(begin(localOrNonLocalOnly), end(localOrNonLocalOnly), size(), selectedAgents, remove); + else selectNElementsInRandomOrder(begin(localOrNonLocalOnly), popSize, size(), selectedAgents, remove); +} + +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::set& selectedAgents, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(begin(localOrNonLocalOnly), end(localOrNonLocalOnly), count, selectedAgents, remove); + else selectNElementsAtRandom(begin(localOrNonLocalOnly), popSize, count, selectedAgents, remove); +} + +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::vector& selectedAgents, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(begin(localOrNonLocalOnly), end(localOrNonLocalOnly), count, selectedAgents, remove); + else selectNElementsInRandomOrder(begin(localOrNonLocalOnly), popSize, count, selectedAgents, remove); +} + +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, std::set& selectedAgents, int type, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(byTypeBegin(localOrNonLocalOnly, type), byTypeEnd(localOrNonLocalOnly, type), size(), selectedAgents, remove); + else selectNElementsAtRandom(byTypeBegin(localOrNonLocalOnly, type), popSize, size(), selectedAgents, remove); +} + +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, std::vector& selectedAgents, int type, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(byTypeBegin(localOrNonLocalOnly, type), byTypeEnd(localOrNonLocalOnly, type), size(), selectedAgents, remove); + else selectNElementsInRandomOrder(byTypeBegin(localOrNonLocalOnly, type), popSize, size(), selectedAgents, remove); +} + +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::set& selectedAgents, int type, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(byTypeBegin(localOrNonLocalOnly, type), byTypeEnd(localOrNonLocalOnly, type), count, selectedAgents, remove); + else selectNElementsAtRandom(byTypeBegin(localOrNonLocalOnly, type), popSize, count, selectedAgents, remove); +} + +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::vector& selectedAgents, int type, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(byTypeBegin(localOrNonLocalOnly, type), byTypeEnd(localOrNonLocalOnly, type), count, selectedAgents, remove); + else selectNElementsInRandomOrder(byTypeBegin(localOrNonLocalOnly, type), popSize, count, selectedAgents, remove); +} + +template +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, std::set& selectedAgents, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(filteredBegin(localOrNonLocalOnly, filter), filteredEnd(localOrNonLocalOnly, filter), size(), selectedAgents, remove); + else selectNElementsAtRandom(filteredBegin(localOrNonLocalOnly, filter), popSize, size(), selectedAgents, remove); +} + +template +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, std::vector& selectedAgents, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(filteredBegin(localOrNonLocalOnly, filter), filteredEnd(localOrNonLocalOnly, filter), size(), selectedAgents, remove); + else selectNElementsInRandomOrder(filteredBegin(localOrNonLocalOnly, filter), popSize, size(), selectedAgents, remove); +} + +template +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::set& selectedAgents, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(filteredBegin(localOrNonLocalOnly, filter), filteredEnd(localOrNonLocalOnly, filter), count, selectedAgents, remove); + else selectNElementsAtRandom(filteredBegin(localOrNonLocalOnly, filter), popSize, count, selectedAgents, remove); +} + +template +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::vector& selectedAgents, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(filteredBegin(localOrNonLocalOnly, filter), filteredEnd(localOrNonLocalOnly, filter), count, selectedAgents, remove); + else selectNElementsInRandomOrder(filteredBegin(localOrNonLocalOnly, filter), popSize, count, selectedAgents, remove); +} + +template +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, std::set& selectedAgents, int type, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(byTypeFilteredBegin(localOrNonLocalOnly, type, filter), byTypeFilteredEnd(localOrNonLocalOnly, type, filter), size(), selectedAgents, remove); + else selectNElementsAtRandom(byTypeFilteredBegin(localOrNonLocalOnly, type, filter), popSize, size(), selectedAgents, remove); +} + +template +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, std::vector& selectedAgents, int type, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(byTypeFilteredBegin(localOrNonLocalOnly, type, filter), byTypeFilteredEnd(localOrNonLocalOnly, type, filter), size(), selectedAgents, remove); + else selectNElementsInRandomOrder(byTypeFilteredBegin(localOrNonLocalOnly, type, filter), popSize, size(), selectedAgents, remove); +} + +template +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::set& selectedAgents, int type, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsAtRandom(byTypeFilteredBegin(localOrNonLocalOnly, type, filter), byTypeFilteredEnd(localOrNonLocalOnly, type, filter), count, selectedAgents, remove); + else selectNElementsAtRandom(byTypeFilteredBegin(localOrNonLocalOnly, type, filter), popSize, count, selectedAgents, remove); +} + +template +template +void SharedContext::selectAgents(filterLocalFlag localOrNonLocalOnly, int count, std::vector& selectedAgents, int type, filterStruct& filter, bool remove, int popSize){ + if(popSize <= -1) selectNElementsInRandomOrder(byTypeFilteredBegin(localOrNonLocalOnly, type, filter), byTypeFilteredEnd(localOrNonLocalOnly, type, filter), count, selectedAgents, remove); + else selectNElementsInRandomOrder(byTypeFilteredBegin(localOrNonLocalOnly, type, filter), popSize, count, selectedAgents, remove); +} + + + + +// Beta + +template +bool SharedContext::keepsAgentsOnSyncProj(){ + typename std::vector *>::iterator iter = Context::projections.begin(); + typename std::vector *>::iterator iterEnd = Context::projections.end(); + while((iter != iterEnd)){ + if((*iter)->keepsAgentsOnSyncProj()) return true; + iter++; + } + return false; +} + +template +bool SharedContext::sendsSecondaryDataOnStatusExchange(){ + for(typename std::vector *>::iterator iter = Context::projections.begin(), iterEnd = Context::projections.end(); iter != iterEnd; iter++){ + if((*iter)->sendsSecondaryAgentsOnStatusExchange()) return true; + } + return false; +} + +template +void SharedContext::getProjInfoExchangePartners(std::set& sends, std::set& recvs){ + for(typename std::vector *>::iterator iter = Context::projections.begin(), iterEnd = Context::projections.end(); iter != iterEnd; iter++){ + (*iter)->getInfoExchangePartners(sends, recvs); + } +} + + +template +void SharedContext::getAgentStatusInfoExchangePartners(std::set& sends, std::set& recvs){ + for(typename std::vector *>::iterator iter = Context::projections.begin(), iterEnd = Context::projections.end(); iter != iterEnd; iter++){ + (*iter)->getAgentStatusExchangePartners(sends, recvs); + } +} + + +template +void SharedContext::getRequiredAgents(std::set& agentsToTest, std::set& agentsToKeep, RADIUS radius){ + typename std::vector *>::iterator iter = Context::projections.begin(); + typename std::vector *>::iterator iterEnd = Context::projections.end(); + while((iter != iterEnd) && (agentsToTest.size() > 0)){ + (*iter)->getRequiredAgents(agentsToTest, agentsToKeep, radius); + iter++; + } +} + +template +void SharedContext::getNonlocalAgentsToDrop(std::set& agentsToKeep, std::set& agentsToDrop, RADIUS radius){ + if(agentsToKeep.size() > 0){ + const_state_aware_iterator iter = begin(NON_LOCAL), iterEnd = end(NON_LOCAL); + std::set::iterator notFound = agentsToKeep.end(); + while(iter != iterEnd){ + AgentId id = (*iter)->getId(); + if(agentsToKeep.find(id) == notFound) agentsToDrop.insert((*iter)->getId()); + iter++; + } + } + else{ + const_state_aware_iterator iter = begin(NON_LOCAL), iterEnd = end(NON_LOCAL); + while(iter != iterEnd){ + agentsToDrop.insert((*iter)->getId()); + iter++; + } + } + getRequiredAgents(agentsToDrop, agentsToKeep, radius); +} + +template +void SharedContext::getAgentsToPushToOtherProcesses(std::map >& agentsToPush){ + std::vector tmp; + std::set agentsToTest; + for(const_state_aware_iterator iter = begin(LOCAL), iterEnd = end(LOCAL); iter != iterEnd; ++iter){ + tmp.push_back((*iter)->getId()); + } + agentsToTest.insert(tmp.begin(), tmp.end()); + for(typename std::vector::iterator iter = getAgentsToPushProjOrder.begin(), iterEnd = getAgentsToPushProjOrder.end(); iter != iterEnd; iter++){ + Context::getProjection(*iter)->getAgentsToPush(agentsToTest, agentsToPush); + } +} + +template +void SharedContext::addProjection(Projection* projection){ + int sizeBefore = Context::projections.size(); + Context::addProjection(projection); + if(sizeBefore != Context::projections.size()) getAgentsToPushProjOrder.push_back(projection->name()); +} + + + + +} +#endif /* SHAREDCONEXT_HPP_ */ diff --git a/libs/repast_hpc/SharedContinuousSpace.h b/libs/repast_hpc/SharedContinuousSpace.h new file mode 100644 index 0000000..5ea8973 --- /dev/null +++ b/libs/repast_hpc/SharedContinuousSpace.h @@ -0,0 +1,101 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SharedContinuousSpace.h + * + * Created on: Jul 20, 2010 + * Author: nick + */ + +#ifndef SHAREDCONTINUOUSSPACE_H_ +#define SHAREDCONTINUOUSSPACE_H_ + +#include + +#include "SharedBaseGrid.h" + +namespace repast { + +/** + * Continuous space SharedBaseGrid implementation. This + * primarily adds the buffer synchronization appropriate for this + * type. Default templated typical SharedContinuousSpaces are defined in SharedGrids. + * + * @see SharedBaseGrid for more details. + * + * @tparam T the type of objects contained by this BaseGrid + * @tparam GPTransformer transforms cell points according to the topology (e.g. periodic) + * of the BaseGrid. + * @tparam Adder determines how objects are added to the grid from its associated context. + */ +template +class SharedContinuousSpace: public SharedBaseGrid { + +protected: + virtual void synchMoveTo(const AgentId& id, const Point& pt); + +private: + + typedef SharedBaseGrid SharedBaseGridType; + +public: + virtual ~SharedContinuousSpace(); + SharedContinuousSpace(std::string name, GridDimensions gridDims, std::vector processDims, int buffer, boost::mpi::communicator* communicator); + +}; + +template +SharedContinuousSpace::SharedContinuousSpace(std::string name, GridDimensions gridDims, + std::vector processDims, int buffer, boost::mpi::communicator* communicator) : + SharedBaseGrid (name, gridDims, processDims, buffer, communicator) { +} + +template +void SharedContinuousSpace::synchMoveTo(const AgentId& id, const Point& pt) { + //unlikely chance that agent could have + // moved and then "died" and so removed from sending context, in which case + // it would never get sent to this grid. + if (SharedBaseGridType::GridBaseType::contains(id)) { + SharedBaseGridType::GridBaseType::moveTo(id, pt.coords()); + } +} + + +template +SharedContinuousSpace::~SharedContinuousSpace() { +} + +} + +#endif /* SHAREDCONTINUOUSSPACE_H_ */ diff --git a/libs/repast_hpc/SharedDiscreteSpace.h b/libs/repast_hpc/SharedDiscreteSpace.h new file mode 100644 index 0000000..52bcefb --- /dev/null +++ b/libs/repast_hpc/SharedDiscreteSpace.h @@ -0,0 +1,319 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SharedDiscreteSpace.h + * + * Created on: Jul 20, 2010 + * Author: nick + */ + +#ifndef SHAREDDISCRETESPACE_H_ +#define SHAREDDISCRETESPACE_H_ + +#include + +#include "SharedBaseGrid.h" + +namespace repast { + +/** + * Discrete matrix-like SharedBaseGrid implementation. This + * primarily adds the buffer synchronization appropriate for this + * type. Default templated typical SharedGrid types are defined in SharedGrids. + * + * @see SharedBaseGrid for more details. + * + * @tparam T the type of objects contained by this BaseGrid + * @tparam GPTransformer transforms cell points according to the topology (e.g. periodic) + * of the BaseGrid. + * @tparam Adder determines how objects are added to the grid from its associated context. + */ +template +class SharedDiscreteSpace: public SharedBaseGrid { + +protected: + virtual void synchMoveTo(const AgentId& id, const Point& pt); + +private: + + typedef SharedBaseGrid SharedBaseGridType; + +public: + virtual ~SharedDiscreteSpace(); + SharedDiscreteSpace(std::string name, GridDimensions gridDims, std::vector processDims, int buffer, boost::mpi::communicator* communicator); + +// virtual void getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush); + +}; + +template +SharedDiscreteSpace::SharedDiscreteSpace(std::string name, GridDimensions gridDims, + std::vector processDims, int buffer, boost::mpi::communicator* communicator) : + SharedBaseGrid (name, gridDims, processDims, buffer, communicator) { +} + +template +void SharedDiscreteSpace::synchMoveTo(const AgentId& id, const Point& pt) { + //unlikely chance that agent could have + // moved and then "died" and so removed from sending context, in which case + // it would never get sent to this grid. + if (SharedBaseGridType::GridBaseType::contains(id)) { + SharedBaseGridType::GridBaseType::moveTo(id, pt.coords()); + } +} + + +template +SharedDiscreteSpace::~SharedDiscreteSpace() { +} + + +//template +//void SharedDiscreteSpace::getAgentsToPush(std::set& agentsToTest, std::map >& agentsToPush){ +// +// int buffer = SharedBaseGrid::_buffer; +// if(buffer == 0) return; // A buffer zone of zero means that no agents will be pushed. +// +// // The most efficient algorithm will vary, and +// // can be impacted by the number of agents vs. the +// // number of grid cells and the distribution of agents +// // within the space. +// +// +// // This implementation will assume that agents are distributed +// // evenly throughout the space and that the space is large +// // relative to the number of agents in it. and the buffer +// // zones comparatively small. +// +// // Consequently, the strategy will be to look through the +// // cells in the buffer zones, collecting all the +// // agents therein. +// +// // An increase in efficiency can be gained +// // by considering the overlapping areas of the buffer zones only +// // once (instead of three times) +// +// +// // In a general case, we might not want to do this, but +// // for the current configuration, we can make this (perhaps much) more efficient +// // this way: +// Point localOrigin = SharedBaseGrid::localBounds.origin(); +// Point localExtent = SharedBaseGrid::localBounds.extents(); +// +// int X1 = localOrigin.getX(); +// int X2 = localOrigin.getX() + buffer; +// int X3 = localOrigin.getX() + localExtent.getX() - buffer; +// int X4 = localOrigin.getX() + localExtent.getX(); +// +// int Y1 = localOrigin.getY(); +// int Y2 = localOrigin.getY() + buffer; +// int Y3 = localOrigin.getY() + localExtent.getY() - buffer; +// int Y4 = localOrigin.getY() + localExtent.getY(); +// +// Neighbor* neighbor; +// +// neighbor = SharedBaseGrid::nghs.neighbor(Neighbors::NW); +// int NW_rank = (neighbor == 0 ? -1 : neighbor->rank()); +// neighbor = SharedBaseGrid::nghs.neighbor(Neighbors::N ); +// int N_rank = (neighbor == 0 ? -1 : neighbor->rank()); +// neighbor = SharedBaseGrid::nghs.neighbor(Neighbors::NE); +// int NE_rank = (neighbor == 0 ? -1 : neighbor->rank()); +// neighbor = SharedBaseGrid::nghs.neighbor(Neighbors::E ); +// int E_rank = (neighbor == 0 ? -1 : neighbor->rank()); +// neighbor = SharedBaseGrid::nghs.neighbor(Neighbors::SE); +// int SE_rank = (neighbor == 0 ? -1 : neighbor->rank()); +// neighbor = SharedBaseGrid::nghs.neighbor(Neighbors::S ); +// int S_rank = (neighbor == 0 ? -1 : neighbor->rank()); +// neighbor = SharedBaseGrid::nghs.neighbor(Neighbors::SW); +// int SW_rank = (neighbor == 0 ? -1 : neighbor->rank()); +// neighbor = SharedBaseGrid::nghs.neighbor(Neighbors::W ); +// int W_rank = (neighbor == 0 ? -1 : neighbor->rank()); +// +// +// std::set NW_set; +// std::set N_set; +// std::set NE_set; +// std::set E_set; +// std::set SE_set; +// std::set S_set; +// std::set SW_set; +// std::set W_set; +// +// +// // NW +// for(int x = X1; x < X2; x++){ +// for(int y = Y1; y < Y2; y++){ +// Point pt(x, y); +// std::vector out; +// SharedBaseGridType::GridBaseType::getObjectsAt(pt, out); +// for (size_t i = 0, n = out.size(); i < n; ++i) { +// AgentId id = out[i]->getId(); +// agentsToTest.erase(id); +// NW_set.insert(id); +// } +// } +// } +// // N +// for(int x = X2; x < X3; x++){ +// for(int y = Y1; y < Y2; y++){ +// Point pt(x, y); +// std::vector out; +// SharedBaseGridType::GridBaseType::getObjectsAt(pt, out); +// for (size_t i = 0, n = out.size(); i < n; ++i) { +// AgentId id = out[i]->getId(); +// agentsToTest.erase(id); +// N_set.insert(id); +// } +// } +// } +// // NE +// for(int x = X3; x < X4; x++){ +// for(int y = Y1; y < Y2; y++){ +// Point pt(x, y); +// std::vector out; +// SharedBaseGridType::GridBaseType::getObjectsAt(pt, out); +// for (size_t i = 0, n = out.size(); i < n; ++i) { +// AgentId id = out[i]->getId(); +// agentsToTest.erase(id); +// NE_set.insert(id); +// } +// } +// } +// // E +// for(int x = X3; x < X4; x++){ +// for(int y = Y2; y < Y3; y++){ +// Point pt(x, y); +// std::vector out; +// SharedBaseGridType::GridBaseType::getObjectsAt(pt, out); +// for (size_t i = 0, n = out.size(); i < n; ++i) { +// AgentId id = out[i]->getId(); +// agentsToTest.erase(id); +// E_set.insert(id); +// } +// } +// } +// // SE +// for(int x = X3; x < X4; x++){ +// for(int y = Y3; y < Y4; y++){ +// Point pt(x, y); +// std::vector out; +// SharedBaseGridType::GridBaseType::getObjectsAt(pt, out); +// for (size_t i = 0, n = out.size(); i < n; ++i) { +// AgentId id = out[i]->getId(); +// agentsToTest.erase(id); +// SE_set.insert(id); +// } +// } +// } +// // S +// for(int x = X2; x < X3; x++){ +// for(int y = Y3; y < Y4; y++){ +// Point pt(x, y); +// std::vector out; +// SharedBaseGridType::GridBaseType::getObjectsAt(pt, out); +// for (size_t i = 0, n = out.size(); i < n; ++i) { +// AgentId id = out[i]->getId(); +// agentsToTest.erase(id); +// S_set.insert(id); +// } +// } +// } +// // SW +// for(int x = X1; x < X2; x++){ +// for(int y = Y3; y < Y4; y++){ +// Point pt(x, y); +// std::vector out; +// SharedBaseGridType::GridBaseType::getObjectsAt(pt, out); +// for (size_t i = 0, n = out.size(); i < n; ++i) { +// AgentId id = out[i]->getId(); +// agentsToTest.erase(id); +// SW_set.insert(id); +// } +// } +// } +// // W +// for(int x = X1; x < X2; x++){ +// for(int y = Y2; y < Y3; y++){ +// Point pt(x, y); +// std::vector out; +// SharedBaseGridType::GridBaseType::getObjectsAt(pt, out); +// for (size_t i = 0, n = out.size(); i < n; ++i) { +// AgentId id = out[i]->getId(); +// agentsToTest.erase(id); +// W_set.insert(id); +// } +// } +// } +// +// if(NW_set.size() > 0){ +// if(W_rank > -1) agentsToPush[ W_rank].insert(NW_set.begin(), NW_set.end()); +// if(NW_rank > -1) agentsToPush[NW_rank].insert(NW_set.begin(), NW_set.end()); +// if(N_rank > -1) agentsToPush[ N_rank].insert(NW_set.begin(), NW_set.end()); +// } +// if( N_set.size() > 0){ +// if(N_rank > -1) agentsToPush[ N_rank].insert( N_set.begin(), N_set.end()); +// } +// if(NE_set.size() > 0){ +// if(N_rank > -1) agentsToPush[ N_rank].insert(NE_set.begin(), NE_set.end()); +// if(NE_rank > -1) agentsToPush[NE_rank].insert(NE_set.begin(), NE_set.end()); +// if(E_rank > -1) agentsToPush[ E_rank].insert(NE_set.begin(), NE_set.end()); +// } +// if( E_set.size() > 0){ +// if(E_rank > -1) agentsToPush[ E_rank].insert( E_set.begin(), E_set.end()); +// } +// if(SE_set.size() > 0){ +// if(E_rank > -1) agentsToPush[ E_rank].insert(SE_set.begin(), SE_set.end()); +// if(SE_rank > -1) agentsToPush[SE_rank].insert(SE_set.begin(), SE_set.end()); +// if(S_rank > -1) agentsToPush[ S_rank].insert(SE_set.begin(), SE_set.end()); +// } +// if( S_set.size() > 0){ +// if(S_rank > -1) agentsToPush[ S_rank].insert( S_set.begin(), S_set.end()); +// } +// if(SW_set.size() > 0){ +// if(S_rank > -1) agentsToPush[ S_rank].insert(SW_set.begin(), SW_set.end()); +// if(SW_rank > -1) agentsToPush[SW_rank].insert(SW_set.begin(), SW_set.end()); +// if(W_rank > -1) agentsToPush[ W_rank].insert(SW_set.begin(), SW_set.end()); +// } +// if( W_set.size() > 0){ +// if(W_rank > -1) agentsToPush[ W_rank].insert( W_set.begin(), W_set.end()); +// } +// +//} + + + + +} + +#endif /* SHAREDDISCRETESPACE_H_ */ diff --git a/libs/repast_hpc/SharedNetwork.h b/libs/repast_hpc/SharedNetwork.h new file mode 100644 index 0000000..f7ac783 --- /dev/null +++ b/libs/repast_hpc/SharedNetwork.h @@ -0,0 +1,242 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SharedNetwork.h + * + * Created on: May 27, 2009 + * Author: nick + */ + +#ifndef SHAREDNETWORK_H_ +#define SHAREDNETWORK_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "Graph.h" +#include "RepastProcess.h" +#include "logger.h" +#include "Utilities.h" +#include "SRManager.h" +#include "RepastErrors.h" + + +namespace repast { + +const int NET_SR_PAIR = 2000; +const int NET_RECV_PROC = 2001; +const int NET_EDGE_UPDATE = 2002; +const int NET_EXPORTERS_A = 2003; +const int NET_EXPORTERS_B = 2004; +const int NET_EXPORT_REQUESTS = 2005; +const int NET_EDGE_SYNC = 2006; +const int NET_EDGE_REMOVE_SYNC = 2007; + + +/** + * Network implementation that can be shared across processes. + * Networks are shared across processes by creating edges between + * local and non-local agents on a process. The createComplementaryEdges + * function will create complementary edges across processes in + * those cases. For example, if an edge is created between A1 and B2 on process 1 + * where B2 is copy of B1 on process 2, then creating complementary edges + * will create a copy of that edge on process 2, importing A1 into process 2 + * if necessary. + * + * @tparam V the agent (vertex) type + * @tparam E the edge type. The edge type must be contain a constructor + * that takes a source and target of type V and extends RepastEdge. RepastEdge + * can also be used. + */ +template +class SharedNetwork: public Graph { +private: + + template + friend void createComplementaryEdges(SharedNetwork* net, SharedContext& context, + EdgeManager& edgeManager, AgentCreator& creator); + + template + friend void synchEdges(SharedNetwork*, EdgeManager&); + + boost::unordered_set fAgents; + std::vector > sharedEdges; + int rank, worldSize; + std::map senders; + // maps removed edges to the process to inform that the edges + // have been deleted + std::map > > removedEdges; + +protected: + + virtual bool addAgent(boost::shared_ptr agent); + virtual void removeAgent(V* agent); + + virtual void doAddEdge(boost::shared_ptr edge); + +public: + + using Graph::addEdge; + using Graph::removeEdge; + + /** + * Creates a SharedNetwork with the specified name and + * whether or not the network is directed. + * + * @param the network name + * @param directed if true the network will be directed, otherwise not. + */ + SharedNetwork(std::string name, bool directed, EcM* edgeContentMgr); + virtual ~SharedNetwork() { + } + + /** + * NON USER API. + * + * Increments the count of edges that are sent from rank to this + * network. + */ + void addSender(int rank); + + /** + * NON USER API + * Decrements the count of edges that are sent from rank to this + * network. + */ + void removeSender(int rank); + + // doc inherited from Graphs + void removeEdge(V* source, V* target); + + /** + * Add an edge to this SharedNetwork. + * + * @param edge the edge to add + */ + void addEdge(boost::shared_ptr edge); + + /** + * Synchronizes any removed edges that are have been copied + * across processes. + */ + void synchRemovedEdges(); + + /** + * Returns true if this is a master link; will be a master link if + * its master node is local. The master node is usually the edge 'source', + * but if the usesTargetAsMaster flag is set to true then the 'target' + * is the master node. + */ + virtual bool isMaster(E* e){ + return (e->usesTargetAsMaster() ? e->target()->getId().currentRank() : e->source()->getId().currentRank()) == rank; + } +}; + +/* Shared Network Definition */ + +template +SharedNetwork::SharedNetwork(std::string name, bool directed, EcM* edgeContentMgr) : + Graph (name, directed, edgeContentMgr) { + rank = RepastProcess::instance()->rank(); + worldSize = RepastProcess::instance()->worldSize(); +} + +template +void SharedNetwork::addSender(int rank) { + std::map::iterator iter = senders.find(rank); + if (iter == senders.end()) { + senders[rank] = 1; + } else { + senders[rank] = senders[rank] + 1; + } +} + +template +void SharedNetwork::removeSender(int rank) { + std::map::iterator iter = senders.find(rank); + if (iter == senders.end()) throw Repast_Error_30(rank); // Cannot remove non-existent sender + + int val = iter->second; + if (val == 1) + senders.erase(rank); + else + senders[rank] = val - 1; +} + +template +bool SharedNetwork::addAgent(boost::shared_ptr agent) { + AgentId id = agent->getId(); + if (id.currentRank() != rank) { + // add to foreign ids + fAgents.insert(id); + } + return Graph::addAgent(agent); +} + +template +void SharedNetwork::removeEdge(V* source, V* target) { + boost::shared_ptr edge = Graph::findEdge(source, target); + Graph::removeEdge(source, target); + +} + +template +void SharedNetwork::removeAgent(V* agent) { + AgentId id = agent->getId(); + if (id.currentRank() != rank) { + fAgents.erase(id); + } + Graph::removeAgent(agent); +} +template +void SharedNetwork::addEdge(boost::shared_ptr edge) { + SharedNetwork::doAddEdge(edge); +} + +template +void SharedNetwork::doAddEdge(boost::shared_ptr edge) { + Graph::doAddEdge(edge); +} + +} + +#endif /* SHAREDNETWORK_H_ */ diff --git a/libs/repast_hpc/SharedSpaces.h b/libs/repast_hpc/SharedSpaces.h new file mode 100644 index 0000000..2119d62 --- /dev/null +++ b/libs/repast_hpc/SharedSpaces.h @@ -0,0 +1,86 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SharedSpaces.h + * + * Created on: Jul 20, 2010 + * Author: nick + */ + +#ifndef SHAREDSPACES_H_ +#define SHAREDSPACES_H_ + +#include "SharedDiscreteSpace.h" +#include "SharedContinuousSpace.h" + +namespace repast { + +/** + * Struct within which multiple kinds of shared space are typedef-ed. + */ +template +struct SharedSpaces { + + /** + * Discrete grid space with periodic (toroidal) borders. Any added + * agents are not given a location, but are in "grid limbo" until + * moved via a grid move call. + */ + typedef SharedDiscreteSpace > SharedWrappedDiscreteSpace; + + /** + * Discrete grid space with strict borders. Any added + * agents are not given a location, but are in "grid limbo" until + * moved via a grid move call. + */ + typedef SharedDiscreteSpace > SharedStrictDiscreteSpace; + + /** + * Continuous space with periodic (toroidal) borders. Any added + * agents are not given a location, but are in "grid limbo" until + * moved via a grid move call. + */ + typedef SharedContinuousSpace > SharedWrappedContinuousSpace; + + /** + * Continuous space with strict borders. Any added + * agents are not given a location, but are in "grid limbo" until + * moved via a grid move call. + */ + typedef SharedContinuousSpace > SharedStrictContinuousSpace; +}; + +} + +#endif /* SHAREDSPACE_H_ */ diff --git a/libs/repast_hpc/SingleOccupancy.h b/libs/repast_hpc/SingleOccupancy.h new file mode 100644 index 0000000..846ba71 --- /dev/null +++ b/libs/repast_hpc/SingleOccupancy.h @@ -0,0 +1,139 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * SingleOccupancy.h + * + * Created on: Aug 11, 2010 + * Author: nick + */ + +#ifndef SINGLEOCCUPANCY_H_ +#define SINGLEOCCUPANCY_H_ + +#include +#include + +#include "Point.h" + +namespace repast { + +/** + * Single Occupancy cell accessor for accessing the occupants of locations + * in a Grid. Each locations can have only a single occupant. + * + * @param T the type of object in the Grid + * @param GPType the coordinate type of the grid point locations. This must + * be an int or a double. + */ +template +class SingleOccupancy { + +private: + typedef typename boost::unordered_map , boost::shared_ptr, HashGridPoint > LocationMap; + typedef typename LocationMap::iterator LocationMapIter; + typedef typename LocationMap::const_iterator LocationMapConstIter; + + LocationMap locations; + +public: + + /** + * Gets the object found at the specified location. + * + * @param location the location to get the object at + * @return the first object found at the specified location or 0 if there + * are no objects at the specified location. + */ + T* get(const Point& location) const; + + /** + * Gets the item found at the specified location. + * + * @param location the location to get the item at + * @param [out] the found item will be returned in this vector + */ + void getAll(const Point& location, std::vector& out) const; + + /** + * Puts the specified item at the specified location. + * + * @param agent the item to put + * @param location the location to put the item at + */ + bool put(boost::shared_ptr& agent, const Point& location); + + /** + * Removes the specified item from the specified location. + * + * @param agent the item to remove + * @param location the location to remove the item from + */ + void remove(boost::shared_ptr& agent, const Point& location); + +}; + +template +T* SingleOccupancy::get(const Point& location) const { + LocationMapConstIter iter = locations.find(location); + if (iter == locations.end()) + return NULL; + return iter->second.get(); +} + +template +void SingleOccupancy::getAll(const Point& location, std::vector& out) const { + T* agent = get(location); + if (agent != NULL) { + out.push_back(agent); + } +} + +template +bool SingleOccupancy::put(boost::shared_ptr& agent, const Point& location) { + LocationMapIter iter = locations.find(location); + // already occupied + if (iter != locations.end()) + return false; + locations[location] = agent; + return true; +} + +template +void SingleOccupancy::remove(boost::shared_ptr& agent, const Point& location) { + locations.erase(location); +} + +} + +#endif /* SINGLEOCCUPANCY_H_ */ diff --git a/libs/repast_hpc/Spaces.h b/libs/repast_hpc/Spaces.h new file mode 100644 index 0000000..543edad --- /dev/null +++ b/libs/repast_hpc/Spaces.h @@ -0,0 +1,73 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Spaces.h + * + * Created on: Jul 19, 2010 + * Author: nick + */ + +#ifndef SPACES_H_ +#define SPACES_H_ + +#include "BaseGrid.h" +#include "GridComponents.h" +#include "MultipleOccupancy.h" +#include "SingleOccupancy.h" + +namespace repast { + +// Some useful typedefs for standard grid types: + +/** + * Struct within which multiple kinds of space are typedef-ed. + */ +template +struct Spaces { + + typedef BaseGrid, StrictBorders, SimpleAdder , int> SingleStrictDiscreteSpace; + typedef BaseGrid, WrapAroundBorders, SimpleAdder , int> SingleWrappedDiscreteSpace; + typedef BaseGrid, StrictBorders, SimpleAdder , int> MultipleStrictDiscreteSpace; + typedef BaseGrid, WrapAroundBorders, SimpleAdder , int> MultipleWrappedDiscreteSpace; + + typedef BaseGrid, StrictBorders, SimpleAdder , double> SingleStrictContinuousSpace; + typedef BaseGrid, WrapAroundBorders, SimpleAdder , double> SingleWrappedContinuousSpace; + typedef BaseGrid, StrictBorders, SimpleAdder , double> MultipleStrictContinuousSpace; + typedef BaseGrid, WrapAroundBorders, SimpleAdder , double> MultipleWrappedContinuousSpace; + +}; + +} + +#endif /* SPACES_H_ */ diff --git a/libs/repast_hpc/TDataSource.h b/libs/repast_hpc/TDataSource.h new file mode 100644 index 0000000..52c1b87 --- /dev/null +++ b/libs/repast_hpc/TDataSource.h @@ -0,0 +1,69 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * TDataSource.h + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#ifndef TDATASOURCE_H_ +#define TDATASOURCE_H_ + +namespace repast { + +/** + * Interface for class that act + * as datasoures for DataSets. + * + * @tparam T the type of the data + */ +template +class TDataSource { + +public: + virtual ~TDataSource() {}; + + /** + * Gets the data. + * + * @return the current data. + */ + virtual T getData() = 0; +}; + +} + + +#endif /* TDATASOURCE_H_ */ diff --git a/libs/repast_hpc/UndirectedVertex.h b/libs/repast_hpc/UndirectedVertex.h new file mode 100644 index 0000000..2d02b76 --- /dev/null +++ b/libs/repast_hpc/UndirectedVertex.h @@ -0,0 +1,158 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * UndirectedVertex.h + * + * Created on: Oct 13, 2010 + * Author: nick + */ + +#ifndef UNDIRECTEDVERTEX_H_ +#define UNDIRECTEDVERTEX_H_ + +#include "Vertex.h" + +namespace repast { + +/** + * A vertex in an undirected network. + * + * @tparam V the vertex type + * @tparam E the edge type. The edge type must be or extend RepastEdge. + */ +template +class UndirectedVertex : public Vertex { + +private: + typedef typename Vertex::AdjListMap AdjListMap; + typedef typename Vertex::AdjListMap::iterator AdjListMapIterator; + typedef typename Vertex::EdgeType EdgeType; + + AdjListMap* adjMap; + +public: + UndirectedVertex(boost::shared_ptr item); + virtual ~UndirectedVertex(); + + // doc inherited from Vertex.h + virtual boost::shared_ptr removeEdge(Vertex* other, EdgeType type); + + // doc inherited from Vertex.h + virtual boost::shared_ptr findEdge(Vertex* other, EdgeType type); + + // doc inherited from Vertex.h + virtual void addEdge(Vertex* other, boost::shared_ptr edge, EdgeType type); + + // doc inherited from Vertex.h + virtual void successors(std::vector& out); + + // doc inherited from Vertex.h + virtual void predecessors(std::vector& out); + + // doc inherited from Vertex.h + virtual void adjacent(std::vector& out); + + // doc inherited from Vertex.h + virtual void edges(EdgeType type , std::vector >& out); + + // doc inherited from Vertex.h + int inDegree(); + + // doc inherited from Vertex.h + int outDegree(); +}; + +template +UndirectedVertex::UndirectedVertex(boost::shared_ptr item) : Vertex(item) { + adjMap = new AdjListMap(); +} + +template +UndirectedVertex::~UndirectedVertex() { + delete adjMap; +} + +template +boost::shared_ptr UndirectedVertex::removeEdge(Vertex* other, EdgeType type) { + return Vertex::removeEdge(other, adjMap); +} + +template +boost::shared_ptr UndirectedVertex::findEdge(Vertex* other, EdgeType type) { + boost::shared_ptr ret; + AdjListMapIterator iter = adjMap->find(other); + return (iter != adjMap->end() ? iter->second : ret); +} + +template +void UndirectedVertex::addEdge(Vertex* other, boost::shared_ptr edge, EdgeType type) { + (*adjMap)[other] = edge; +} + +template +void UndirectedVertex::successors(std::vector& out) { + this->getItems(adjMap, out); +} + +template +void UndirectedVertex::predecessors(std::vector& out) { + this->getItems(adjMap, out); +} + +template +void UndirectedVertex::adjacent(std::vector& out) { + this->getItems(adjMap, out); +} + +template +int UndirectedVertex::inDegree() { + return adjMap->size(); +} + +template +int UndirectedVertex::outDegree() { + return adjMap->size(); +} + +template +void UndirectedVertex::edges(EdgeType type , std::vector >& out) { + Vertex::edges(adjMap, out); +} + + + +} + + +#endif /* UNDIRECTEDVERTEX_H_ */ diff --git a/libs/repast_hpc/Utilities.cpp b/libs/repast_hpc/Utilities.cpp new file mode 100644 index 0000000..9996969 --- /dev/null +++ b/libs/repast_hpc/Utilities.cpp @@ -0,0 +1,113 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Utilities.cpp + * + * Created on: Oct 27, 2009 + * Author: nick + */ + +#include "Utilities.h" +#include "RepastErrors.h" + +#include +#include + +using namespace std; + +namespace repast { + +Timer::Timer() : startTime(0) {} + +void Timer::start() { + startTime = clock(); +} + +long double Timer::stop() { + long double diff = clock() - startTime; + startTime = 0; + return diff / CLOCKS_PER_SEC; +} + +void tokenize(const string& str, vector& tokens, const string& delimiters) { + // Skip delimiters at beginning. + string::size_type lastPos = str.find_first_not_of(delimiters, 0); + // Find first "non-delimiter". + string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (string::npos != pos || string::npos != lastPos) { + // Found a token, add it to the vector. + tokens.push_back(str.substr(lastPos, pos - lastPos)); + // Skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(delimiters, pos); + // Find next "non-delimiter" + pos = str.find_first_of(delimiters, lastPos); + } +} + +string trim(const string& str) { + size_t start = str.find_first_not_of(" \t\n\r"); + if (start == string::npos) return ""; + size_t endpos = str.find_last_not_of(" \t\n\r"); + return str.substr(start, endpos - start + 1); +} + +boost::uint32_t strToUInt(const string& val) { + boost::uint32_t i; + istringstream stream(val); + if (stream >> i) + return i; + else + throw Repast_Error_44(val); // Value is not convertible to unsigned integer +} + +int strToInt(const string& val) { + int i; + istringstream stream(val); + if (stream >> i) + return i; + else + throw Repast_Error_45(val); // Value is not convertible to integer +} + +double strToDouble(const string& val) { + double i; + istringstream stream(val); + if (stream >> i) + return i; + else + throw Repast_Error_46(val); // Value is not convertible to double +} + +} diff --git a/libs/repast_hpc/Utilities.h b/libs/repast_hpc/Utilities.h new file mode 100644 index 0000000..ab7cf44 --- /dev/null +++ b/libs/repast_hpc/Utilities.h @@ -0,0 +1,128 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Utilities.h + * + * Created on: Oct 27, 2009 + * Author: nick + */ + +#ifndef UTILITIES_H_ +#define UTILITIES_H_ + +#include +#include +#include + +#include + +namespace repast { + +/** + * Simple timing class. Calling start() starts the timer, + * calling stop returns the milliseconds since calling start. + */ +class Timer { + +private: + clock_t startTime; + +public: + Timer(); + + /** + * Starts the timer. + */ + void start(); + + /** + * Stops the timer and returns the number of milliseconds elapsed since + * calling start(). + * + * @return the number of milliseconds elapsed since + * calling start(). + */ + long double stop(); +}; + +/** + * Tokenizes str and puts the results into tokens. default delimiters is + * " ". + * + * @param str the string to tokenize + * @param [out] tokens returns the tokens + * @param delimiter the string that delimits the tokens + */ +void tokenize(const std::string& str, std::vector& tokens, const std::string& delimiters = " "); + +/** + * Trims white space from sides of str. + * + * @param str the string to trim + * + * @return the trimmed string. + */ +std::string trim(const std::string& str); + +/** + * Converts the string to a unsigned int. + * + * @param val the string representation of the int + * + * @return the unsigned int + */ +boost::uint32_t strToUInt(const std::string& val); + + +/** + * Converts the string to a int. + * + * @param val the string representation of the int + * + * @return the int + */ +int strToInt(const std::string& val); + +/** + * Converts the string to a double. + * + * @param val the string representation of the double. + * + * @return the double. + */ +double strToDouble(const std::string& val); + +} + +#endif /* UTILITIES_H_ */ diff --git a/libs/repast_hpc/VN2DGridQuery.h b/libs/repast_hpc/VN2DGridQuery.h new file mode 100644 index 0000000..8066740 --- /dev/null +++ b/libs/repast_hpc/VN2DGridQuery.h @@ -0,0 +1,122 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * VN2DGridQuery.h + * + * Created on: Aug 12, 2010 + * Author: nick + */ + +#ifndef VN2DGRIDQUERY_H_ +#define VN2DGRIDQUERY_H_ + +#include "Grid2DQuery.h" + +namespace repast { + +/** + * Neighborhood query that gathers neighbors in a Von Neumann (N, S, E, W) + * neighborhood. + * + * @tparam T the type of agents in the Grid + * + */ +template +class VN2DGridQuery: public Grid2DQuery { + +public: + + VN2DGridQuery(const Grid* grid); + virtual ~VN2DGridQuery() { + } + + /** + * Queries the Grid for the Von Neumann neighbors surrounding the center point within a specified range. + * + * @param center the center of the neighborhood + * @param range the range of the neighborhood out from the center + * @param includeCenter whether or not to include any agents at the center + * @param [out] the neighboring agents will be returned in this vector + */ + virtual void query(const Point& center, int range, bool includeCenter, std::vector& out) const; +}; + +template +VN2DGridQuery::VN2DGridQuery(const Grid* grid) : + Grid2DQuery (grid) { +} + +template +void VN2DGridQuery::query(const Point& center, int range, bool includeCenter, std::vector& out) const { + if (includeCenter) { + Grid2DQuery::_grid->getObjectsAt(center, out); + } + + int yMin = center[1] - range; + if (!Grid2DQuery::_grid->isPeriodic() && yMin < Grid2DQuery::minMax[1][0]) + yMin = Grid2DQuery::minMax[1][0]; + + int yMax = center[1]; + for (int y = yMin; y < yMax; y++) { + Grid2DQuery::_grid->getObjectsAt(Point (center[0], y), out); + } + + yMax = center[1] + range + 1; + if (!Grid2DQuery::_grid->isPeriodic() && yMax >= Grid2DQuery::minMax[1][1]) + yMax = Grid2DQuery::minMax[1][1]; + // skip the center + yMin = center[1] + 1; + for (int y = yMin; y < yMax; y++) { + Grid2DQuery::_grid->getObjectsAt(Point (center[0], y), out); + } + + int xMin = center[0] - range; + if (!Grid2DQuery::_grid->isPeriodic() && xMin < Grid2DQuery::minMax[0][0]) + xMin = Grid2DQuery::minMax[0][0]; + for (int x = xMin; x < center[0]; x++) { + Grid2DQuery::_grid->getObjectsAt(Point (x, center[1]), out); + } + + int xMax = center[0] + range + 1; + if (!Grid2DQuery::_grid->isPeriodic() && xMax >= Grid2DQuery::minMax[0][1]) + xMax = Grid2DQuery::minMax[0][1]; + xMin = center[0] + 1; + for (int x = xMin; x < xMax; x++) { + Grid2DQuery::_grid->getObjectsAt(Point (x, center[1]), out); + } +} + +} + +#endif /* VN2DGRIDQUERY_H_ */ diff --git a/libs/repast_hpc/ValueLayer.cpp b/libs/repast_hpc/ValueLayer.cpp new file mode 100644 index 0000000..813c729 --- /dev/null +++ b/libs/repast_hpc/ValueLayer.cpp @@ -0,0 +1,52 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * ValueLayer.cpp + * + * Created on: Apr 8, 2010 + * Author: nick + */ + +#include "ValueLayer.h" +#include "GridComponents.h" + +#include + +namespace repast { + +BaseValueLayer::BaseValueLayer(const std::string& name) : + _name(name) { +} + +} diff --git a/libs/repast_hpc/ValueLayer.h b/libs/repast_hpc/ValueLayer.h new file mode 100644 index 0000000..1cace65 --- /dev/null +++ b/libs/repast_hpc/ValueLayer.h @@ -0,0 +1,427 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * ValueLayer.h + * + * Created on: Apr 1, 2010 + * Author: nick + */ + +#ifndef VALUELAYER_H_ +#define VALUELAYER_H_ + +#include + +#include "Point.h" +#include "GridDimensions.h" +#include "matrix.h" + +#include + +namespace repast { + +/** + * Base implementation of a ValueLayer. A ValueLayer + * stores values by location. + */ +class BaseValueLayer: public boost::noncopyable { + +protected: + std::string _name; + +public: + /** + * Creates a BaseValueLayer with the specified name. + */ + BaseValueLayer(const std::string& name); + virtual ~BaseValueLayer() { + } + + /** + * Gets the value layer's name. + * + * @return the name of the value layer. + */ + std::string name() const { + return _name; + } +}; + +/** + * A collection that stores values at point locations. + * + * @tparam ValueType the type stored by the value layer. + * @tparam the coordinate type (int or double) of the point locations. + */ +template +class ValueLayer: public BaseValueLayer { + +protected: + GridDimensions _dimensions; + + /** + * Translates pt by dimensions origin. + */ + void translate(std::vector& pt); + +public: + ValueLayer(const std::string& name, const GridDimensions& dimensions); + virtual ~ValueLayer() { + } + + /** + * Gets the value at the specified point. If no value + * has been set at the specified point then this returns + * some default value. Subclasses will determine the default value. + * + * param pt the location to get the value of + * + * @return the value at the specified point, or if no value has been + * set, then some default value. + */ + virtual ValueType& get(const Point& pt) = 0; + + /** + * Sets the value at the specified point. + * + * @param value the value + * @param pt the point where the value should be stored + * + */ + virtual void set(const ValueType& value, const Point& pt) = 0; + + /** + * Gets the value at the specified point. If no value + * has been set at the specified point then this returns + * some default value. Subclasses will determine the default value. + * + * param pt the location to get the value of + * + * @return the value at the specified point, or if no value has been + * set, then some default value. + */ + ValueType& operator[](const Point& pt); + + /** + * Gets the value at the specified point. If no value + * has been set at the specified point then this returns + * some default value. Subclasses will determine the default value. + * + * param pt the location to get the value of + * + * @return the value at the specified point, or if no value has been + * set, then some default value. + */ + const ValueType& operator[](const Point& pt) const; + + /** + * Gets the dimensions of this ValueLayer. + * + * @return the dimensions of this ValueLayer. + */ + const GridDimensions dimensions() const { + return _dimensions; + } + + /** + * Gets the extents of this ValueLayer. + * + * @return the extents of this ValueLayer. + */ + const Point shape() const { + return _dimensions.extents(); + } +}; + +template +ValueLayer::ValueLayer(const std::string& name, const GridDimensions& dimensions) : + BaseValueLayer(name), _dimensions(dimensions) { +} + +template +void ValueLayer::translate(std::vector& pt) { + for (size_t i = 0; i < _dimensions.dimensionCount(); i++) { + pt[i] -= _dimensions.origin(i); + } +} + +template +ValueType& ValueLayer::operator[](const Point& pt) { + return get(pt); +} + +template +const ValueType& ValueLayer::operator[](const Point& pt) const { + return get(pt); +} + +/** + * Creates ValueLayer whose location coordinates are ints. + * + * @tparam ValueType the type of what the value layer stores. + * @tparam Borders the type of borders (wrapped / periodic, strict). Border types + * can be found in GridComponents.h + */ +template +class DiscreteValueLayer: public ValueLayer { + +private: + Matrix* matrix; + bool _dense; + Borders borders; + + void create(bool dense, Matrix* other); + +public: + DiscreteValueLayer(const DiscreteValueLayer& other); + DiscreteValueLayer& operator=(const DiscreteValueLayer& rhs); + + /** + * Creates a DiscreteValueLayer whose cells contain a default value of ValueType() + * with the specified dimensions. + * + * @param name the name of the DiscreteValueLayer + * @param dimension the dimensions of the DiscreteValueLayer + * @param dense whether or not the ValueLayer will be densely populated or not + * @param defaultValue the default value to return if no value has been + * set of a location. The default is the result of ValueType(). + */ + DiscreteValueLayer(const std::string& name, const GridDimensions& dimensions, bool dense, + const ValueType& defaultValue = ValueType()); + ~DiscreteValueLayer(); + + /** + * Gets the value at the specified point. If no value + * has been set at the specified point then this returns + * the default value. + * + * param pt the location to get the value of + * + * @return the value at the specified point, or if no value has been + * set, then the default value. + */ + ValueType& get(const Point& pt); + + /** + * Sets the value at the specified point. + * + * @param value the value + * @param pt the point where the value should be stored + * + */ + void set(const ValueType& value, const Point& pt); +}; + +template +void DiscreteValueLayer::create(bool dense, Matrix* other) { + if (dense) { + matrix = new DenseMatrix (*(dynamic_cast*> (other))); + } else { + matrix = new SparseMatrix (*(dynamic_cast*> (other))); + } + +} + +template +DiscreteValueLayer::DiscreteValueLayer(const DiscreteValueLayer& other) : + ValueLayer (other.name(), other.dimensions()), _dense(other._dense), borders(other.dimensions()) { + create(_dense, other.matrix); +} + +template +DiscreteValueLayer& DiscreteValueLayer::operator=(const DiscreteValueLayer< + ValueType, Borders>& rhs) { + if (&rhs != this) { + delete matrix; + ValueLayer::_name = rhs.name(); + ValueLayer::_dimensions = rhs.dimensions(); + _dense = rhs._dense; + create(_dense, rhs.matrix); + borders = Borders(ValueLayer::_dimensions); + } + return *this; +} + +template +DiscreteValueLayer::~DiscreteValueLayer() { + delete matrix; +} + +template +DiscreteValueLayer::DiscreteValueLayer(const std::string& name, const GridDimensions& dimensions, + bool dense, const ValueType& defaultValue) : + ValueLayer (name, dimensions), _dense(dense) , borders(Borders(ValueLayer::_dimensions)) { + + // this is dangerous but at some point dimensions has been converted to take + // double extents and we don't have time to convert everything + const std::vector& coords = dimensions.extents().coords(); + std::vector converted_coords; + for (double d : coords) { + converted_coords.push_back(static_cast(d)); + } + if (dense) { + matrix = new DenseMatrix (Point(converted_coords), defaultValue); + } else { + matrix = new SparseMatrix (Point(converted_coords), defaultValue); + } +} + +template +ValueType& DiscreteValueLayer::get(const Point& pt) { + std::vector out(pt.dimensionCount()); + borders.transform(pt.coords(), out); + if (_dense) + ValueLayer::translate(out); + return matrix->get(Point (out)); +} + +template +void DiscreteValueLayer::set(const ValueType& value, const Point& pt) { + std::vector out(pt.dimensionCount()); + borders.transform(pt.coords(), out); + if (_dense) + ValueLayer::translate(out); + return matrix->set(value, Point (out)); +} + +/** + * Continous value layer whose location coordinates are double. + * + * @tparam ValueType the type of what the value layer stores. + * @tparam Borders the type of borders (wrapped / periodic, strict). Border types + * can be found in GridComponents.h + */ + +template +class ContinuousValueLayer: public ValueLayer { + +private: + + Borders borders; + std::map, ValueType> values; + ValueType _defaultValue; + + typedef typename std::map, ValueType>::iterator values_iter; + +public: + ContinuousValueLayer(const ContinuousValueLayer& other); + ContinuousValueLayer& operator=(const ContinuousValueLayer& rhs); + + /** + * Creates a ContinuousValueLayer whose cells contain a default value of ValueType() + * with the specified dimensions. + * + * @param name the name of the ContinuousValueLayer + * @param dimension the dimensions of the ContinuousValueLayer + * @param dense whether or not the ValueLayer will be densely populated or not + * @param defaultValue the default value to return if no value has been + * set of a location. The default is the result of ValueType(). + */ + ContinuousValueLayer(const std::string& name, const GridDimensions& dimensions, const ValueType& defaultValue = + ValueType()); + ~ContinuousValueLayer() { + } + + /** + * Gets the value at the specified point. If no value + * has been set at the specified point then this returns + * the default value. + * + * param pt the location to get the value of + * + * @return the value at the specified point, or if no value has been + * set, then the default value. + */ + ValueType& get(const Point& pt); + + /** + * Sets the value at the specified point. + * + * @param value the value + * @param pt the point where the value should be stored + * + */ + void set(const ValueType& value, const Point& pt); +}; + + +template +ContinuousValueLayer::ContinuousValueLayer(const ContinuousValueLayer& other) : + ValueLayer (other._name, other._dimensions), values(other.values), _defaultValue( + other._defaultValue), borders(other._dimensions) { +} + +template +ContinuousValueLayer& ContinuousValueLayer::operator=( + const ContinuousValueLayer& rhs) { + + if (&rhs != this) { + values.clear(); + values.insert(rhs.values.begin(), rhs.values.end()); + ValueLayer::_name = rhs.name(); + ValueLayer::_dimensions = rhs.dimensions(); + _defaultValue = rhs._defaultValue; + borders = Borders(ValueLayer::_dimensions); + } + return *this; +} + +template +ContinuousValueLayer::ContinuousValueLayer(const std::string& name, + const GridDimensions& dimensions, const ValueType& defaultValue) : + ValueLayer (name, dimensions), _defaultValue(defaultValue), borders(ValueLayer::_dimensions) { +} + +template +ValueType& ContinuousValueLayer::get(const Point& pt) { + std::vector out(pt.dimensionCount()); + borders.transform(pt.coords(), out); + // need to insert do an insert and return reference to + // result so that assignment via [] uses a reference in + // the map. insert inserts the entry if it doesn't exist + // and returns the entry or the existing entry + std::pair res = values.insert(std::make_pair(out, _defaultValue)); + return res.first->second; +} + +template +void ContinuousValueLayer::set(const ValueType& value, const Point& pt) { + std::vector out(pt.dimensionCount()); + borders.transform(pt.coords(), out); + values[out] = value; +} + +} + +#endif /* VALUELAYER_H_ */ diff --git a/libs/repast_hpc/ValueLayerND.cpp b/libs/repast_hpc/ValueLayerND.cpp new file mode 100644 index 0000000..4819975 --- /dev/null +++ b/libs/repast_hpc/ValueLayerND.cpp @@ -0,0 +1,78 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * ValueLayerND.cpp + * + * Created on: July 18, 2016 + * Author: jtm + */ +#include "ValueLayerND.h" +#include "RepastProcess.h" +#include "Point.h" + +#include + +using namespace std; + +namespace repast { + +template<> +MPI_Datatype AbstractValueLayerND::getRawMPIDataType(){ + return MPI_INT; +} + +template<> +MPI_Datatype AbstractValueLayerND::getRawMPIDataType(){ + return MPI_DOUBLE; +} + +template<> +MPI_Datatype AbstractValueLayerND::getRawMPIDataType(){ + return MPI_LONG; +} + +template<> +MPI_Datatype AbstractValueLayerND::getRawMPIDataType(){ + return MPI_SHORT; +} + +template<> +MPI_Datatype AbstractValueLayerND::getRawMPIDataType(){ + return MPI_FLOAT; +} + + + +} + diff --git a/libs/repast_hpc/ValueLayerND.h b/libs/repast_hpc/ValueLayerND.h new file mode 100644 index 0000000..ee013f3 --- /dev/null +++ b/libs/repast_hpc/ValueLayerND.h @@ -0,0 +1,1809 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * ValueLayerND.h + * + * Created on: July 18, 2016 + * Author: jtm + */ + +#ifndef VALUELAYERND_H_ +#define VALUELAYERND_H_ + +#include + +#include "mpi.h" + +#include "Point.h" +#include "GridDimensions.h" +#include "RepastProcess.h" + + +using namespace std; + +namespace repast { + +/** + * The RankDatum struct stores the data that the ValueLayerND + * class will need for each of its 3^N - 1 neighboring ranks. + * N.B.: We could use a map from rank to the rest of the data, but + * we will rarely need to index it that way, and instead can just + * loop through it + * + */ +struct RankDatum{ + int rank; + MPI_Datatype datatype; + int sendPtrOffset; + int receivePtrOffset; + int sendDir; // Integer representing the direction a send will be sent, in N-space + int recvDir; // Integer representing the direction a receive will have been sent, in N-space +}; + +/** + * The DimensionDatum class stores all of the data that the + * ValueLayerND class will need for each dimension in the + * N-dimensional space. This mainly includes coordinate boundaries + * along each dimension, but these boundaries include local, + * global, and a few other memoized variants. + */ +template +class DimensionDatum{ +public: + + /** + * Constructor + * + * @param indx + * @param globalBoundaries global simulation space boundaries + * @param localBoundaries local boundaries of this process's space + * @param buffer the size of the buffer zone + * @param isPeriodic true if the space is periodic, false if not + */ + DimensionDatum(int indx, GridDimensions globalBoundaries, GridDimensions localBoundaries, int buffer, bool isPeriodic); + + /** + * Destructor + */ + virtual ~DimensionDatum(){} + + int globalCoordinateMin, globalCoordinateMax; // Coordinates of the global simulation boundaries + int localBoundariesMin, localBoundariesMax; // Coordinates of the local process boundaries in this layer + int simplifiedBoundariesMin, simplifiedBoundariesMax; // Coordinates of the 'simplified' coordinates (assuming local boundary origins and NO wrapping) + int leftBufferSize, rightBufferSize; // Buffer size + int matchingCoordinateMin, matchingCoordinateMax; // Region within simplified boundaries within which coordinates match Global Simulation Coordinate system + bool periodic; // True if the global simulation space is periodic on this dimension + bool atLeftBound, atRightBound; // True if the local boundaries abut the global boundaries (whether periodic or not) + bool spaceContinuesLeft, spaceContinuesRight; // False if the local boundaries abut the (non-periodic) global boundaries + int globalWidth; // Global width of the simulation boundaries in simulation units + int localWidth; // Width of the local boundaries + int width; // Total width (units = sizeof T) on this dimension (local extents + buffer sizes if not against global nonperiodic bounds) + int widthInBytes; + + /** + * Gets the size of the data to be sent during synchronization + * + * @return the size of the data to be sent for this dimension. + */ + int getSendReceiveSize(int relativeLocation); + + /** + * Given a coordinate in global simulation coordinates, + * returns the value in simplified coordinates. + * + * For example, suppose the global space is from 0 to 100, + * and the local space is from 0 to 10, and the buffer + * zone value is 3. The simplified coordinates for + * this region of the local space will be -3 to 13. + * If the value passed to this function + * is 99, this function will return -1. + * + * @param originalCoord the original coordinate + * + * @return the coordinate adjusted for local boundaries system + */ + int getTransformedCoord(int originalCoord); + + /** + * Given a coordinate, returns the index of that coordinate. + * The original coordinate may be in global simulation coordinates + * or in 'simplified' coordinates. If it is not in simplified + * coordinates, the first step is to simplify. + * + * For example, suppose the global space is from 0 to 100, + * and the local space is from 0 to 10, and the buffer zone + * value is 3. Passing 99 in global coordinates is equivalent + * to passing -1 in simplified coordinates. The index value + * in both cases is ((-1) - (-3)) or 2. + * + * @param originalCoord the original coordinate to be transformed + * @param isSimplified true if the original coordinate is already in + * simplified coordinates + */ + int getIndexedCoord(int originalCoord, bool isSimplified = false); + + /** + * Returns true if the specified coordinate is within the local boundaries + * on this dimension. + * + * @param originalCoordinate the coordinate to be tested + * + * @return true if the coordinate is within the local boundaries + */ + bool isInLocalBounds(int originalCoord); + + + /** + * Writes a report to the std out file + * + * @param dimensionNumber passed to the written report, identifying which + * dimension this is + */ + void report(int dimensionNumber){ + std::cout << repast::RepastProcess::instance()->rank() << " " << dimensionNumber << " " << + "global (" << globalCoordinateMin << ", " << globalCoordinateMax << ") " << + "local (" << localBoundariesMin << ", " << localBoundariesMax << ") " << + "simple (" << simplifiedBoundariesMin << ", " << simplifiedBoundariesMax << ") " << + "match (" << matchingCoordinateMin << ", " << matchingCoordinateMax << ") " << + "globalWidth = " << globalWidth << " localWidth = " << localWidth << " width = " << width << " bytes = " << widthInBytes << std::endl; + } +}; + +template +DimensionDatum::DimensionDatum(int indx, GridDimensions globalBoundaries, GridDimensions localBoundaries, int buffer, bool isPeriodic): + leftBufferSize(buffer), rightBufferSize(buffer), periodic(isPeriodic){ + globalCoordinateMin = globalBoundaries.origin(indx); + globalCoordinateMax = globalBoundaries.origin(indx) + globalBoundaries.extents(indx); + localBoundariesMin = localBoundaries.origin(indx); + localBoundariesMax = localBoundaries.origin(indx) + localBoundaries.extents(indx); + + atLeftBound = localBoundariesMin == globalCoordinateMin; + atRightBound = localBoundariesMax == globalCoordinateMax; + + spaceContinuesLeft = !atLeftBound || periodic; + spaceContinuesRight = !atRightBound || periodic; + + simplifiedBoundariesMin = localBoundariesMin - leftBufferSize; + simplifiedBoundariesMax = localBoundariesMax + rightBufferSize; + + matchingCoordinateMin = localBoundariesMin; + if(spaceContinuesLeft && !atLeftBound ) matchingCoordinateMin -= leftBufferSize; + + matchingCoordinateMax = localBoundariesMax; + if(spaceContinuesRight && !atRightBound) matchingCoordinateMax += rightBufferSize; + + globalWidth = globalCoordinateMax - globalCoordinateMin; + localWidth = localBoundariesMax - localBoundariesMin; + width = leftBufferSize + localWidth + rightBufferSize; + widthInBytes = width * (sizeof(T)); +} + + +template +int DimensionDatum::getSendReceiveSize(int relativeLocation){ + switch(relativeLocation){ + case -1: return leftBufferSize; + case 1: return rightBufferSize; + case 0: + default: + return localWidth; + } +} + +template +int DimensionDatum::getTransformedCoord(int originalCoord){ + if(originalCoord < matchingCoordinateMin){ // Assume (!) original is on right (!) side of periodic boundary, starting at some value + return matchingCoordinateMax + (originalCoord - globalCoordinateMin); + } + else if(originalCoord > matchingCoordinateMax){ + return matchingCoordinateMin - (globalCoordinateMax - originalCoord); + } + else return originalCoord; // Within matching boundaries; no need to transform + +} + +template +int DimensionDatum::getIndexedCoord(int originalCoord, bool isSimplified){ + return (isSimplified ? originalCoord : getTransformedCoord(originalCoord)) - simplifiedBoundariesMin; +} + +template +bool DimensionDatum::isInLocalBounds(int originalCoord){ + return originalCoord >= localBoundariesMin && originalCoord < localBoundariesMax; +} + +/*******************************************************************/ + +/** + * An AbstractValueLayerND is the abstract parent class for N-dimensional value + * layers + */ +template +class AbstractValueLayerND{ + +private: + bool dummy; // Used for cases when error flag is not requested + +protected: + CartesianTopology* cartTopology; + GridDimensions localBoundaries; + int length; // Total length of the entire array (one data space) + + int numDims; // Number of dimensions + bool globalSpaceIsPeriodic; // True if the global space is periodic + + vector places; // Multipliers to calculate index, for each dimension + vector strides; // Sizes of each dimensions, in bytes + vector > dimensionData; // List of data for each dimension + RankDatum* neighborData; // List of data for each adjacent rank + int neighborCount; // Count of adjacent ranks + MPI_Request* requests; // Pointer to MPI requests (for wait operations) + + int instanceID; // Unique ID for managing MPI requests without mix-ups + int syncCount; + + /** + * Constructor + * + * @param processesPerDim number of processes in each dimension + * @param globalBoundaries global boundaries for the simulation + * @param bufferSize size of the buffer zone + * @param periodic true if the space is periodic, false otherwise + */ + AbstractValueLayerND(vector processesPerDim, GridDimensions globalBoundaries, int bufferSize, bool periodic); + virtual ~AbstractValueLayerND(); + + +public: + + static int instanceCount; + + /** + * Returns true only if the coordinates given are within the local boundaries + * + * @param coords Coordinates to be tested + * @return true if the coordinates are within the local boundaries + */ + virtual bool isInLocalBounds(vector coords); + + /* + * Returns true only if the coordinates given are within the local boundaries + * + * @param coords Coordinates to be tested + * @return true if the coordinates are within the local boundaries + */ + virtual bool isInLocalBounds(Point location); + + /** + * Gets the local boundaries for this process's part of the + * value layer + * + * @return the local boundaries + */ + const GridDimensions& getLocalBoundaries(){ + return localBoundaries; + } + +protected: + // Methods implemented in this class but visible only to child classes: + + /** + * Gets a vector of the indexed locations. If the + * value passed is already simplified (transformed), + * does not transform. + * + * @param location the location to be transformed + * @param isSimplified true if the coordinates are already in simplified + * form + * @return the transformed coordinates + */ + vector getIndexes(vector location, bool isSimplified = false); + + /** + * Given a location in global simulation coordinates, + * get the offset from the global base pointer to the + * position in the global array representing that location. + * The location may be simplified (transformed); if it is + * not, it is first simplified before the index is calculated. + * + * @param location the location to be transformed + * @param isSimplified true if the coordinates are already in simplified + * form + * @return the index from the global base pointer to the position + * in the array in memory + */ + int getIndex(vector location, bool isSimplified = false); + + + /** + * Given a location in global simulation coordinates, + * get the offset from the global base pointer to the + * position in the global array representing that location. + * The location may be simplified (transformed); if it is + * not, it is first simplified before the index is calculated. + * + * @param location the location to be transformed + * @return the index from the global base pointer to the position + * in the array in memory + */ + int getIndex(Point location); + + + // Virtual methods (implemented by child classes + + /** + * Initializes the array to the specified value + * + * Usage: + * + * initialize(val); // Initializes only the local space + * initialize(val, true); // Initializes the entire space + * initialize(val, false); // Initializes only the local space (default) + * initialize(val, true, false); // Initializes only the buffer zone + * initialize(val, false, true); // Initializes only the local space (default) + * initialize(val, true, true); // Initializes the entire space + * initialize(val, false, false); // Does nothing + * + * @param initialValue Value to be placed in the array + * @param fillBufferZone true if the value should be placed in all local cells + * @param fillLocal true if the value should be placed in all non-local cells + */ + virtual void initialize(T initialValue, bool fillBufferZone = false, bool fillLocal = true) = 0; + + /** + * Initializes the array to the specified values + * + * initialize(val1, val2); // Initializes the local space to val1 and the buffer zones to val2 + * @param initialLocalValue value to be placed in local cells + * @param initialBufferZoneValue value to be placed in non-local cells + */ + virtual void initialize(T initialLocalValue, T initialBufferZoneValue) = 0; + + /** + * Adds to the value in the grid at a specific location + * Returns the new value. If the location is not within the local + * boundaries, sets the error flag to 'true', otherwise it will be + * set to 'false' + * + * @param val Value to be placed + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T addValueAt(T val, Point location, bool& errFlag) = 0; + + /** + * Adds to the value in the grid at the specific location + * Returns the new value. If the location is not within the local + * boundaries, sets the error flag to 'true', otherwise it will be + * set to 'false' + * + * @param val Value to be added + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T addValueAt(T val, vector location, bool& errFlag) = 0; + + /** + * Sets the value in the grid at a specific location + * Returns the new value. If the location is not within the local + * boundaries, sets the error flag to 'true', otherwise it will be + * set to 'false' + * + * @param val Value to be added + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T setValueAt(T val, Point location, bool& errFlag) = 0; + + /** + * Sets the value in the grid at the specific location + * Returns the new value. If the location is not within the local + * boundaries, sets the error flag to 'true', otherwise it will be + * set to 'false' + * + * @param val Value to be added + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T setValueAt(T val, vector location, bool& errFlag) = 0; + + /** + * Gets the value in the grid at a specific location. If the location is not within the + * boundaries, sets the error flag to 'true', otherwise it will be + * set to 'false' + * + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the value in the cell + */ + virtual T getValueAt(Point location, bool& errFlag) = 0; + + /** + * Gets the value in the grid at a specific location. If the location is not within the + * boundaries, sets the error flag to 'true', otherwise it will be + * set to 'false' + * + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the value in the cell + */ + virtual T getValueAt(vector location, bool& errFlag) = 0; + + /** + * Synchronizes across processes. This copies + * the values in the interior 'buffer zones' from + * self and sends to adjacent processes, while + * receiving data from adjacent processes and + * placing it in the appropriate exterior buffer + * zones. + */ + virtual void synchronize() = 0; + +private: + + /** + * Gets an MPI data type given the RelativeLocation + * and an index indicating which entry in the RelativeLocation + * is being requested. Typical use will be recursive: if + * there are N dimensions, this class will be called with + * dimensionIndex = N - 1, which will call itself with N-2, + * repeating until N = 0. + * + * @param relLoc a RelativeLocation object that describes the + * shape of the requested MPI data type. + * @datatype prototype for the datatype requested + */ + void getMPIDataType(RelativeLocation relLoc, MPI_Datatype &datatype); + + /** + * A variant of the getMPIDataType function, this + * one assumes that you are retrieving a block with side + * 2 x radius + 1 in all dimensions; + * + * @param radius size of the data type to retrieve + * @prototype for the datatype requested + */ + void getMPIDataType(int radius, MPI_Datatype &datatype); + + /** + * Gets an MPI data type given the list of side lengths + * + *@param sideLengths lengths of each side of the datatype to be returned + *@param datatype prototype for the datatype + *@param dimensionIndex index number of the dimension (for + *@param recursive calling from high dimensions down to 1 + */ + void getMPIDataType(vector sideLengths, MPI_Datatype &datatype, int dimensionIndex); + + /** + * Gets the raw MPI datatype from which all others are built + * @return the raw MPI datatype for 'T' for this class + */ + MPI_Datatype getRawMPIDataType(); + + + /** + * Given a relative location, calculates the index value for the + * first unit that should be in the 'send' buffer. Generally, + * if the relative location value for a given dimension is + * -1 or 0, the offset in that dimension should be equal to the + * buffer zone width, and if it is 1, the offset should be equal + * to the local width (technically buffer + local - buffer) + * Note: Assumes RelativeLocation will only include values + * of -1, 0, and 1 + * + * @relLoc the RelativeLocation requested + * + * @return index of the cell at the specified relative location + */ + int getSendPointerOffset(RelativeLocation relLoc); + + /** + * Given a relative location, calculates the index value for the + * first unit that should be in the 'receive' buffer. Generally, + * if the relative location value for a given dimension is + * -1, the offset should be zero; if it is 0, the offset should + * be equal to the buffer width; and if it is 1, the offset + * should be equal to the buffer width + the local width + * (or, equivalently, the total width - buffer width) + * Note: Assumes RelativeLocation will only include values + * of -1, 0, and 1 + * + * @relLoc the RelativeLocation requested + * + * @return index of the cell at the specified relative location + */ + int getReceivePointerOffset(RelativeLocation relLoc); + + +}; + + + +template +int AbstractValueLayerND::instanceCount = 0; + +template +AbstractValueLayerND::AbstractValueLayerND(vector processesPerDim, GridDimensions globalBoundaries,int bufferSize, bool periodic): globalSpaceIsPeriodic(periodic), syncCount(0){ + instanceID = AbstractValueLayerND::instanceCount; + AbstractValueLayerND::instanceCount++; + cartTopology = RepastProcess::instance()->getCartesianTopology(processesPerDim, periodic); + // Calculate the size to be used for the buffers + numDims = processesPerDim.size(); + + int rank = RepastProcess::instance()->rank(); + localBoundaries = cartTopology->getDimensions(rank, globalBoundaries); + + // First create the basic coordinate data per dimension + length = 1; + int val = 1; + for(int i = 0; i < numDims; i++){ + DimensionDatum datum(i, globalBoundaries, localBoundaries, bufferSize, periodic); + length *= datum.width; + dimensionData.push_back(datum); + places.push_back(val); + strides.push_back(val * sizeof(T)); + val *= dimensionData[i].width; + } + + // Now create the rank-based data per neighbor + RelativeLocation relLoc(numDims); + RelativeLocation relLocTrimmed = cartTopology->trim(rank, relLoc); // Initialized to minima + + vector myCoordinates; + cartTopology->getCoordinates(rank, myCoordinates); + + neighborData = new RankDatum[relLoc.getMaxIndex()]; + neighborCount = 0; + do{ + if(relLoc.validNonCenter()){ // Skip 0,0,0,0,0 + RankDatum* datum; + datum = &neighborData[neighborCount]; + // Collect the information about this rank here + getMPIDataType(relLoc, datum->datatype); + datum->sendPtrOffset = getSendPointerOffset(relLoc); + datum->receivePtrOffset = getReceivePointerOffset(relLoc); + vector current = relLoc.getCurrentValue(); + datum->rank = cartTopology->getRank(myCoordinates, current); + datum->sendDir = RelativeLocation::getDirectionIndex(current); + datum->recvDir = RelativeLocation::getReverseDirectionIndex(current); + + neighborCount++; + } + }while(relLoc.increment()); + + // Create arrays for MPI requests and results (statuses) + requests = new MPI_Request[neighborCount * 2]; +} + +template +AbstractValueLayerND::~AbstractValueLayerND(){ + delete[] neighborData; // Should Free MPI Datatypes first... + delete[] requests; +} + +template +bool AbstractValueLayerND::isInLocalBounds(vector coords){ + for(int i = 0; i < numDims; i++){ + DimensionDatum* datum = &dimensionData[i]; + if(!datum->isInLocalBounds(coords[i])) return false; + } + return true; +} + +template +bool AbstractValueLayerND::isInLocalBounds(Point location){ + return isInLocalBounds(location.coords()); +} + +template +vector AbstractValueLayerND::getIndexes(vector location, bool isSimplified){ + vector ret; + ret.assign(numDims, 0); // Make the right amount of space + for(int i = 0; i < numDims; i++) ret[i] = dimensionData[i].getIndexedCoord(location[i], isSimplified); + return ret; +} + +template +int AbstractValueLayerND::getIndex(vector location, bool isSimplified){ + vector indexed = getIndexes(location, isSimplified); + int val = 0; + for(int i = numDims - 1; i >= 0; i--) val += indexed[i] * places[i]; + if(val < 0 || val > length) val = -1; + return val; +} + +template +int AbstractValueLayerND::getIndex(Point location){ + return getIndex(location.coords()); +} + + +template +void AbstractValueLayerND::getMPIDataType(RelativeLocation relLoc, MPI_Datatype &datatype){ + vector sideLengths; + for(int i = 0; i < numDims; i++) sideLengths.push_back(dimensionData[i].getSendReceiveSize(relLoc[i])); + getMPIDataType(sideLengths, datatype, numDims - 1); +} + +template +void AbstractValueLayerND::getMPIDataType(int radius, MPI_Datatype &datatype){ + vector sideLengths; + sideLengths.assign(numDims, 2 * radius + 1); + getMPIDataType(sideLengths, datatype, numDims - 1); +} + +template +void AbstractValueLayerND::getMPIDataType(vector sideLengths, MPI_Datatype &datatype, int dimensionIndex){ + if(dimensionIndex == 0){ + MPI_Type_contiguous(sideLengths[dimensionIndex], getRawMPIDataType(), &datatype); + } + else{ + MPI_Datatype innerType; + getMPIDataType(sideLengths, innerType, dimensionIndex - 1); + MPI_Type_hvector(sideLengths[dimensionIndex], // Count + 1, // BlockLength: just one of the inner data type + strides[dimensionIndex], // Stride, in bytes + innerType, // Inner Datatype + &datatype); + } + // Commit? + MPI_Type_commit(&datatype); +} + + +template +int AbstractValueLayerND::getSendPointerOffset(RelativeLocation relLoc){ + int ret = 0; + for(int i = 0; i < numDims; i++){ + DimensionDatum* datum = &dimensionData[i]; + ret += (relLoc[i] <= 0 ? datum->leftBufferSize : datum->width - (2 * datum->rightBufferSize)) * places[i]; + } + return ret; +} + +template +int AbstractValueLayerND::getReceivePointerOffset(RelativeLocation relLoc){ + int ret = 0; + for(int i = 0; i < numDims; i++){ + DimensionDatum* datum = &dimensionData[i]; + ret += (relLoc[i] < 0 ? 0 : (relLoc[i] == 0 ? datum->leftBufferSize : datum->width - datum->rightBufferSize)) * places[i]; + } + return ret; +} + + +/** + * The ValueLayerND class is an N-dimensional layer of + * values. + * + * The most complex part of the ValueLayerND class is the + * interaction with MPI. Cross-process synchronization requires + * that blocks of cells (technically volumes in N-space) be + * sent across processes. MPI Derived Datatypes are used + * to achieve this. + * + * The memory for the N-Dimensional array is organized + * as a nested loop. Assume that the dimensions for + * the array are d1, d2, d3 ... dN. The extent of the grid + * in each dimension is e1, e2, e3 ... eN. Note that this + * includes both the space within the local boundaries + * and the adjacent buffer zones. For convenience + * we pre-calculate a vector M1, M2, M3 ... MN of multipliers; each + * entry is equal to the product of all the extents of + * lower-numbered dimensions, with M1 = 1. The address of a cell + * a locations l1, l2, l3 ... lN will be: + * + * l1 * M1 + l2 * M2 + l3 * M3 ... lN * MN + * + * A volume in this space will not occupy a contiguous + * block of memory. It would be more convenient for the MPI + * call if it did. However, the MPI specification indicates + * that derived data types can be used to define complex + * regions of memory; the MPI implementation can optimize + * the sending and receiving of these. + * + * In this class, an MPI Datatype is defined to represent + * the volume of space being sent to and received from + * each of the 3N - 1 adjacent processes. + * + * One important note is that the send and receive data types + * for a given exchange partner will be identical; only the + * starting pointer need be changed to switch from sending + * to receiving. + * + * (It should be noted that MPI allows these data types to + * 'match' if they are structurally compatible. They need + * not actually be identical. So, consider a send/receive + * pair where one of the pair is against the global + * simulation boundaries but the other is not. The actual + * pattern of loops and steps that creates the send will + * be different from the pattern that creates the receive, + * but MPI will recognize that these are 'matchable' and + * will perform the communication.) + * + * The data type can be defined recursively using MPI's + * HVector function for all types except the innermost, + * which is a contiguous block of double values. + * + * The memory space allocated by this object includes + * buffer zones on all 2N sides and all intercardinal + * directions, even if the space is adjacent to a strict + * boundary edge. + */ +template +class ValueLayerND: public AbstractValueLayerND{ + +private: + T* dataSpace; // Pointer to the data space + +public: + + ValueLayerND(vector processesPerDim, GridDimensions globalBoundaries, int bufferSize, + bool periodic, T initialValue = 0, T initialBufferZoneValue = 0); + virtual ~ValueLayerND(); + + /** + * Inherited from AbstractValueLayerND + */ + virtual void initialize(T initialValue, bool fillBufferZone = false, bool fillLocal = true); + + /** + * Inherited from AbstractValueLayerND + */ + virtual void initialize(T initialLocalValue, T initialBufferZoneValue); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T addValueAt(T val, Point location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T addValueAt(T val, vector location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T setValueAt(T val, Point location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T setValueAt(T val, vector location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T getValueAt(Point location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T getValueAt(vector location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual void synchronize(); + + /** + * Write the values in this ValueLayer to a .csv file. + * + * The file format is: + * + * Dim_0,Dim_1,Dim_2,VAL + * 0,0,0,100 + * 0,0,1,200 + * + * The column header indicates the dimensionl the value in the 'VAL' + * column is the value in the ValueLayer at the coordinates specified + * by the values in the first N columns. + * + * In many common situations, many cells will contain zero; to + * keep file sizes small, zeros are not written. + * + * @param fileLocation path to the file location for output + * @param filetag infix for the file name + * @writeSharedBoundaryAreas if true, the data output will include + * the adjacent processes' buffer zones as they exist in this + * array; if false, these will be omitted + */ + void write(string fileLocation, string filetag, bool writeSharedBoundaryAreas = false); + + +private: + + + /** + * Fills a dimension of space with the given value. Used for initialization + * and clearing only. + * + * @param localValue the value to be placed in local cells + * @param bufferZoneValue the value to be placed in non-local cells + * @param doBufferZone if true, places values in the buffer zone + * @param doLocal if true, places values in the local cells + * @param dataSpacePointer pointer to the first cell in the data array + * @param dimIndex index number of this dimension, for recursive calls + */ + void fillDimension(T localValue, T bufferZoneValue, bool doBufferZone, bool doLocal, T* dataSpacePointer, int dimIndex); + + /* + * Writes one dimension's information to the specified csv file. + * + * @param outfile output file + * @param dataSpacePointer pointer to the data space to be written + * @param currentPosition position currently being written (for recursive calls) + * @param dimIndex dimension currently being written (for recursive calls) + * @param writeSharedBoundaryAreas if true, write the areas that are non-local to this process + */ + void writeDimension(std::ofstream& outfile, T* dataSpacePointer, int* currentPosition, int dimIndex, bool writeSharedBoundaryAreas = false); + +}; + + + + + +/** + * ValueLayerNDSU is a version of the ValueLayerND class that + * facilitates SynchronousUpdating: that is, a process can + * use the current values in the value layer and create a set of new + * values, then 'switch' to using the new values. It does this by using + * two memory banks. + */ +template +class ValueLayerNDSU: public AbstractValueLayerND{ + +protected: + + T* dataSpace1; // Permanent pointer to bank 1 of the data space + T* dataSpace2; // Permanent pointer to bank 2 of the data space + T* currentDataSpace; // Temporary pointer to the active data space + T* otherDataSpace; // Temporary pointer to the inactive data space + +public: + + ValueLayerNDSU(vector processesPerDim, GridDimensions globalBoundaries, int bufferSize, bool periodic, T initialValue = 0, T initialBufferZoneValue = 0); + virtual ~ValueLayerNDSU(); + + /** + * Inherited from AbstractValueLayerND + */ + virtual void initialize(T initialValue, bool fillBufferZone = false, bool fillLocal = true); + + /** + * Inherited from AbstractValueLayerND + */ + virtual void initialize(T initialLocalValue, T initialBufferZoneValue); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T addValueAt(T val, Point location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T addValueAt(T val, vector location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T setValueAt(T val, Point location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T setValueAt(T val, vector location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T getValueAt(Point location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual T getValueAt(vector location, bool& errFlag); + + /** + * Inherited from AbstractValueLayerND + */ + virtual void synchronize(); + + /** + * Write this rank's data to a CSV file + */ + virtual void write(string fileLocation, string filetag, bool writeSharedBoundaryAreas = false); + + /** + * Switch from one value layer to the other. + */ + void switchValueLayer(); + + /** + * Adds the specified value to the value in the non-current + * data bank at the given location + * + * @param val Value to be added + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T addSecondaryValueAt(T val, Point location, bool& errFlag); + + /** + * Adds the specified value to the value in the non-current + * data bank at the given location + * + * @param val Value to be added + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T addSecondaryValueAt(T val, vector location, bool& errFlag); + + /** + * Sets the specified value to the value in the non-current + * data bank at the given location + * + * @param val Value to be added + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T setSecondaryValueAt(T val, Point location, bool& errFlag); + + /** + * Sets the specified value to the value in the non-current + * data bank at the given location + * + * @param val Value to be added + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T setSecondaryValueAt(T val, vector location, bool& errFlag); + + /** + * Gets the specified value to the value in the non-current + * data bank at the given location + * + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the value in the cell + */ + virtual T getSecondaryValueAt(Point location, bool& errFlag); + + /** + * Gets the specified value to the value in the non-current + * data bank at the given location + * + * @param location location where value is to be placed + * @errFlag a flag that will be set to 'false' if an error occurs + * @return the new value in the cell + */ + virtual T getSecondaryValueAt(vector location, bool& errFlag); + + /** + * Copies the data in the current value layer to the secondary layer + */ + virtual void copyCurrentToSecondary(); + + /** + * Copies the data in the secondary layer to the current value layer + */ + virtual void copySecondaryToCurrent(); + + /** + * ValueLayerNDSU can do something that no other value layer- or, in + * fact, any other object in Repast HPC- can do: it can take information + * from non-local processes and allow it to flow back to the local process. + * + * This is strictly forbidden in every other context in Repast HPC. Consider + * a local process 0 with an Agent 'A' in one corner of its space. Because + * 'A' is in the buffer zone, copies of 'A' are made on the other processes + * adjacent to process 0; in a normal 2-D space this could include three + * other processes, p1, p2, and p3. Now suppose that p1, p2, and p3 are all + * allowed to modify their copies of Agent 'A', and then, further, that we + * wish to migrate these changes back to process 0. How do we reconcile + * the changes? In general there is no answer. + * + * ValueLayerNDSU, however, makes one special provision for allowing non- + * local information to flow back to the local process. Assume that as in + * the agent example, there is a corner of the value layer on p0 that is + * adjacent to p1, p2, and p3. Copies of the space are made to the other + * processes. Now assume that these other processes add values to these + * regions of space. The simulation is responsible for assuring that these + * additive operations are independent of the simulation synchronization: + * that in the specific simulation being undertaken, the additions + * that are occurring without synchronization are semantically acceptable. + * + * The 'flowback' method takes these values and performs what is akin to an + * MPI 'gather' operation: taking the values from processes p1, p2, and p3 and + * adding them to the values on p0. The result of the operation is that + * the values on each process reflect the original values plus the values that + * were found in the non-local processes, with all values summed. + * + * N.B.: All values in the secondary layer are set to zeros. Values on the original + * process inside and outside the buffer zones are unchanged. To update + * the values outside the buffer zones to their new values, perform a 'synchronize' + * operation after the 'flowback' operation is completed. + */ + void flowback(); + +private: + + /** + * Fills a dimension of space with the given value. Used for initialization + * and clearing only. + * + * @param localValue the value to be placed in local cells + * @param bufferZoneValue the value to be placed in non-local cells + * @param doBufferZone if true, places values in the buffer zone + * @param doLocal if true, places values in the local cells + * @param dataSpace1Pointer pointer to the first cell in the #1 data array + * @param dataSpace2Pointer pointer to the first cell in the #2 data array + * @param dimIndex index number of this dimension, for recursive calls + */ + void fillDimension(T localValue, T bufferZoneValue, bool doBufferZone, bool doLocal, T* dataSpace1Pointer, T* dataSpace2Pointer, int dimIndex); + + /* + * Writes one dimension's information to the specified csv file. + * + * @param outfile output file + * @param dataSpacePointer pointer to the data space to be written + * @param currentPosition position currently being written (for recursive calls) + * @param dimIndex dimension currently being written (for recursive calls) + * @param writeSharedBoundaryAreas if true, write the areas that are non-local to this process + */ + void writeDimension(std::ofstream& outfile, T* dataSpacePointer, int* currentPosition, int dimIndex, bool writeSharedBoundaryAreas = false); + + /** + * This is based on the 'fillDimension', but it takes values from the + * LOCAL portion of the otherDataSpace and sums them into the LOCAL portion + * of the current data space. + */ + void sumInto(T* dataSpace1Pointer, T* dataSpace2Pointer, int dimIndex); + +}; + + + +template +ValueLayerND::ValueLayerND(vector processesPerDim, GridDimensions globalBoundaries, int bufferSize, bool periodic, + T initialValue, T initialBufferZoneValue): AbstractValueLayerND(processesPerDim, globalBoundaries, bufferSize, periodic){ + + // Create the actual arrays for the data + dataSpace = new T[AbstractValueLayerND::length]; + + // Finally, fill the data with the initial values + initialize(initialValue, initialBufferZoneValue); + + // And synchronize + synchronize(); + +} + +template +ValueLayerND::~ValueLayerND(){ + delete[] dataSpace; +} + +template +void ValueLayerND::initialize(T initialValue, bool fillBufferZone, bool fillLocal){ + fillDimension(initialValue, initialValue, fillBufferZone, fillLocal, dataSpace, AbstractValueLayerND::numDims - 1); +} + +template +void ValueLayerND::initialize(T initialLocalValue, T initialBufferZoneValue){ + fillDimension(initialLocalValue, initialBufferZoneValue, true, true, dataSpace, AbstractValueLayerND::numDims - 1); +} + +template +T ValueLayerND::addValueAt(T val, Point location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return val; + } + T* pt = &dataSpace[indx]; + return (*pt = *pt + val); +} + +template +T ValueLayerND::addValueAt(T val, vector location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return val; + } + + T* pt = &dataSpace[indx]; + return (*pt = *pt + val); +} + +template +T ValueLayerND::setValueAt(T val, Point location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return val; + } + T* pt = &dataSpace[indx]; + return (*pt = val); +} + +template +T ValueLayerND::setValueAt(T val, vector location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return val; + } + T* pt = &dataSpace[indx]; + return (*pt = val); +} + +template +T ValueLayerND::getValueAt(vector location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return 0; + } + return dataSpace[indx]; +} + +template +T ValueLayerND::getValueAt(Point location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return 0; + } + return dataSpace[indx]; +} + +template +void ValueLayerND::synchronize(){ + AbstractValueLayerND::syncCount++; + if(AbstractValueLayerND::syncCount > 9) AbstractValueLayerND::syncCount = 0; + int mpiTag = AbstractValueLayerND::instanceID * 10 + AbstractValueLayerND::syncCount; + // Note: the syncCount and send/recv directions are used to create a unique tag value for the + // mpi sends and receives. The tag value must be unique in two ways: first, successive calls to this + // function must be different enough that they can't be confused. The 'syncCount' value is used to + // achieve this, and it will loop from 0-9 and then repeat. The second, the tag must sometimes + // differentiate between sends and receives that are going to the same rank. If a dimension + // has only 2 processes but wrap-around borders, then one process may be sending to the other + // process twice (once left and once right). The 'sendDir' and 'recvDir' values trap this + + // For each entry in neighbors: + MPI_Status statuses[AbstractValueLayerND::neighborCount * 2]; + for(int i = 0; i < AbstractValueLayerND::neighborCount; i++){ + MPI_Isend(&dataSpace[AbstractValueLayerND::neighborData[i].sendPtrOffset], 1, AbstractValueLayerND::neighborData[i].datatype, + AbstractValueLayerND::neighborData[i].rank, 10 * (AbstractValueLayerND::neighborData[i].sendDir + 1) + mpiTag, AbstractValueLayerND::cartTopology->topologyComm, &AbstractValueLayerND::requests[i]); + MPI_Irecv(&dataSpace[AbstractValueLayerND::neighborData[i].receivePtrOffset], 1, AbstractValueLayerND::neighborData[i].datatype, + AbstractValueLayerND::neighborData[i].rank, 10 * (AbstractValueLayerND::neighborData[i].recvDir + 1) + mpiTag, AbstractValueLayerND::cartTopology->topologyComm, &AbstractValueLayerND::requests[AbstractValueLayerND::neighborCount + i]); + } + MPI_Waitall(AbstractValueLayerND::neighborCount * 2, AbstractValueLayerND::requests, statuses); +} + + +template +void ValueLayerND::write(string fileLocation, string fileTag, bool writeSharedBoundaryAreas){ + std::ofstream outfile; + std::ostringstream stream; + int rank = repast::RepastProcess::instance()->rank(); + stream << fileLocation << "ValueLayer_" << fileTag << "_" << rank << ".csv"; + std::string filename = stream.str(); + + const char * c = filename.c_str(); + outfile.open(c, std::ios_base::trunc | std::ios_base::out); // it will not delete the content of file, will add a new line + + // Write headers + for(int i = 0; i < AbstractValueLayerND::numDims; i++) outfile << "DIM_" << i << ","; + outfile << "VALUE" << endl; + + int* positions = new int[AbstractValueLayerND::numDims]; + for(int i = 0; i < AbstractValueLayerND::numDims; i++) positions[i] = 0; + + writeDimension(outfile, dataSpace, positions, AbstractValueLayerND::numDims - 1, writeSharedBoundaryAreas); + + outfile.close(); +} + + +template +void ValueLayerND::fillDimension(T localValue, T bufferValue, bool doBufferZone, bool doLocal, T* dataSpacePointer, int dimIndex){ + if(!doBufferZone && !doLocal) return; + int bufferEdge = AbstractValueLayerND::dimensionData[dimIndex].leftBufferSize; + int localEdge = bufferEdge + AbstractValueLayerND::dimensionData[dimIndex].localWidth; + int upperBound = localEdge + AbstractValueLayerND::dimensionData[dimIndex].rightBufferSize; + + int pointerIncrement = AbstractValueLayerND::places[dimIndex]; + + + int i = 0; + for(; i < bufferEdge; i++){ + if(doBufferZone){ + if(dimIndex == 0){ + *dataSpacePointer = bufferValue; + } + else{ + fillDimension(bufferValue, bufferValue, doBufferZone, doLocal, dataSpacePointer, dimIndex - 1); + } + } + // Increment the pointers + dataSpacePointer += pointerIncrement; + } + for(; i < localEdge; i++){ + if(doLocal){ + if(dimIndex == 0){ + *dataSpacePointer = localValue; + } + else{ + fillDimension(localValue, bufferValue, doBufferZone, doLocal, dataSpacePointer, dimIndex - 1); + } + } + // Increment the pointers + dataSpacePointer += pointerIncrement; + } + if(doBufferZone){ // Note: we don't need to finish this at all if not doing buffer zone + for(; i < upperBound; i++){ + if(dimIndex == 0){ + *dataSpacePointer = bufferValue; + } + else{ + fillDimension(bufferValue, bufferValue, doBufferZone, doLocal, dataSpacePointer, dimIndex - 1); + } + } + dataSpacePointer += pointerIncrement; + } + +} + +template +void ValueLayerND::writeDimension(std::ofstream& outfile, T* dataSpacePointer, int* currentPosition, int dimIndex, bool writeSharedBoundaryAreas){ + int bufferEdge = AbstractValueLayerND::dimensionData[dimIndex].leftBufferSize; + int localEdge = bufferEdge + AbstractValueLayerND::dimensionData[dimIndex].localWidth; + int upperBound = localEdge + AbstractValueLayerND::dimensionData[dimIndex].rightBufferSize; + + int pointerIncrement = AbstractValueLayerND::places[dimIndex]; + int i = 0; + for(; i < bufferEdge; i++){ + currentPosition[dimIndex] = i; + if(writeSharedBoundaryAreas){ + if(dimIndex == 0){ + T val = *dataSpacePointer; + if(val != 0){ + for(int j = 0; j < AbstractValueLayerND::numDims; j++) outfile << (currentPosition[j] - AbstractValueLayerND::dimensionData[j].leftBufferSize + AbstractValueLayerND::dimensionData[j].localBoundariesMin) << ","; + outfile << val << endl; + } + } + else{ + writeDimension(outfile, dataSpacePointer, currentPosition, dimIndex - 1, writeSharedBoundaryAreas); + } + } + // Increment the pointers + dataSpacePointer += pointerIncrement; + } + for(; i < localEdge; i++){ + currentPosition[dimIndex] = i; + if(dimIndex == 0){ + T val = *dataSpacePointer; + if(val != 0){ + for(int j = 0; j < AbstractValueLayerND::numDims; j++) outfile << (currentPosition[j] - AbstractValueLayerND::dimensionData[j].leftBufferSize + AbstractValueLayerND::dimensionData[j].localBoundariesMin) << ","; + outfile << val << endl; + } + } + else{ + writeDimension(outfile, dataSpacePointer, currentPosition, dimIndex - 1, writeSharedBoundaryAreas); + } + // Increment the pointers + dataSpacePointer += pointerIncrement; + } + if(writeSharedBoundaryAreas){ // Note: we don't need to finish this at all if not doing buffer zone + for(; i < upperBound; i++){ + currentPosition[dimIndex] = i; + if(dimIndex == 0){ + T val = *dataSpacePointer; + if(val != 0){ + for(int j = 0; j < AbstractValueLayerND::numDims; j++) outfile << (currentPosition[j] - AbstractValueLayerND::dimensionData[j].leftBufferSize + AbstractValueLayerND::dimensionData[j].localBoundariesMin) << ","; + outfile << *dataSpacePointer << endl; + } + } + else{ + writeDimension(outfile, dataSpacePointer, currentPosition, dimIndex - 1, writeSharedBoundaryAreas); + } + } + dataSpacePointer += pointerIncrement; + } + +} + + + + +template +ValueLayerNDSU::ValueLayerNDSU(vector processesPerDim, GridDimensions globalBoundaries, int bufferSize, bool periodic, + T initialValue, T initialBufferZoneValue): AbstractValueLayerND(processesPerDim, globalBoundaries, bufferSize, periodic){ + + // Create the actual arrays for the data + dataSpace1 = new T[AbstractValueLayerND::length]; + dataSpace2 = new T[AbstractValueLayerND::length]; + currentDataSpace = dataSpace1; + otherDataSpace = dataSpace2; + + // Finally, fill the data with the initial values + initialize(initialValue, initialBufferZoneValue); + + // And synchronize + synchronize(); + +} + +template +ValueLayerNDSU::~ValueLayerNDSU(){ + delete[] currentDataSpace; + delete[] otherDataSpace; +} + +template +void ValueLayerNDSU::initialize(T initialValue, bool fillBufferZone, bool fillLocal){ + fillDimension(initialValue, initialValue, fillBufferZone, fillLocal, dataSpace1, dataSpace2, AbstractValueLayerND::numDims - 1); +} + +template +void ValueLayerNDSU::initialize(T initialLocalValue, T initialBufferZoneValue){ + fillDimension(initialLocalValue, initialBufferZoneValue, true, true, dataSpace1, dataSpace2, AbstractValueLayerND::numDims - 1); +} + +template +T ValueLayerNDSU::addValueAt(T val, Point location, bool& errFlag){ + int indx = this->getIndex(location); + if(indx == -1) return nan(""); + T* pt = ¤tDataSpace[indx]; + return (*pt = *pt + val); +} + +template +T ValueLayerNDSU::addValueAt(T val, vector location, bool& errFlag){ + int indx = this->getIndex(location); + if(indx == -1) return nan(""); + T* pt = ¤tDataSpace[indx]; + return (*pt = *pt + val); +} + +template +T ValueLayerNDSU::setValueAt(T val, Point location, bool& errFlag){ + int indx = this->getIndex(location); + if(indx == -1) return nan(""); + T* pt = ¤tDataSpace[indx]; + return (*pt = val); +} + +template +T ValueLayerNDSU::setValueAt(T val, vector location, bool& errFlag){ + int indx = this->getIndex(location); + if(indx == -1) return nan(""); + T* pt = ¤tDataSpace[indx]; + return (*pt = val); +} + +template +T ValueLayerNDSU::getValueAt(Point location, bool& errFlag){ + int indx = this->getIndex(location); + if(indx == -1) return nan(""); + return currentDataSpace[indx]; +} + +template +T ValueLayerNDSU::getValueAt(vector location, bool& errFlag){ + int indx = this->getIndex(location); + if(indx == -1) return nan(""); + return currentDataSpace[indx]; +} + + +template +void ValueLayerNDSU::synchronize(){ + AbstractValueLayerND::syncCount++; + if(AbstractValueLayerND::syncCount > 9) AbstractValueLayerND::syncCount = 0; + int mpiTag = AbstractValueLayerND::instanceID * 10 + AbstractValueLayerND::syncCount; + // Note: the syncCount and send/recv directions are used to create a unique tag value for the + // mpi sends and receives. The tag value must be unique in two ways: first, successive calls to this + // function must be different enough that they can't be confused. The 'syncCount' value is used to + // achieve this, and it will loop from 0-9 and then repeat. The second, the tag must sometimes + // differentiate between sends and receives that are going to the same rank. If a dimension + // has only 2 processes but wrap-around borders, then one process may be sending to the other + // process twice (once left and once right). The 'sendDir' and 'recvDir' values trap this + + // For each entry in neighbors: + MPI_Status statuses[AbstractValueLayerND::neighborCount * 2]; + for(int i = 0; i < AbstractValueLayerND::neighborCount; i++){ + MPI_Isend(¤tDataSpace[AbstractValueLayerND::neighborData[i].sendPtrOffset], 1, AbstractValueLayerND::neighborData[i].datatype, + AbstractValueLayerND::neighborData[i].rank, 10 * (AbstractValueLayerND::neighborData[i].sendDir + 1) + mpiTag, AbstractValueLayerND::cartTopology->topologyComm, &AbstractValueLayerND::requests[i]); + MPI_Irecv(¤tDataSpace[AbstractValueLayerND::neighborData[i].receivePtrOffset], 1, AbstractValueLayerND::neighborData[i].datatype, + AbstractValueLayerND::neighborData[i].rank, 10 * (AbstractValueLayerND::neighborData[i].recvDir + 1) + mpiTag, AbstractValueLayerND::cartTopology->topologyComm, &AbstractValueLayerND::requests[AbstractValueLayerND::neighborCount + i]); + } + MPI_Waitall(AbstractValueLayerND::neighborCount * 2, AbstractValueLayerND::requests, statuses); +} + +template +void ValueLayerNDSU::write(string fileLocation, string fileTag, bool writeSharedBoundaryAreas){ + std::ofstream outfile; + std::ostringstream stream; + int rank = repast::RepastProcess::instance()->rank(); + stream << fileLocation << "ValueLayer_" << fileTag << "_" << rank << ".csv"; + std::string filename = stream.str(); + + const char * c = filename.c_str(); + outfile.open(c, std::ios_base::trunc | std::ios_base::out); // it will not delete the content of file, will add a new line + + // Write headers + for(int i = 0; i < AbstractValueLayerND::numDims; i++) outfile << "DIM_" << i << ","; + outfile << "VALUE" << endl; + + int* positions = new int[AbstractValueLayerND::numDims]; + for(int i = 0; i < AbstractValueLayerND::numDims; i++) positions[i] = 0; + + writeDimension(outfile, currentDataSpace, positions, AbstractValueLayerND::numDims - 1, writeSharedBoundaryAreas); + + outfile.close(); +} + +template +void ValueLayerNDSU::switchValueLayer(){ + // Switch the data banks + T* tempDataSpace = currentDataSpace; + currentDataSpace = otherDataSpace; + otherDataSpace = tempDataSpace; +} + +template +T ValueLayerNDSU::addSecondaryValueAt(T val, Point location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return val; + } + T* pt = &otherDataSpace[indx]; + return (*pt = *pt + val); +} + +template +T ValueLayerNDSU::addSecondaryValueAt(T val, vector location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return val; + } + T* pt = &otherDataSpace[indx]; + return (*pt = *pt + val); +} + +template +T ValueLayerNDSU::setSecondaryValueAt(T val, Point location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return val; + } + T* pt = &otherDataSpace[indx]; + return (*pt = val); +} + +template +T ValueLayerNDSU::setSecondaryValueAt(T val, vector location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return val; + } + T* pt = &otherDataSpace[indx]; + return (*pt = val); +} + +template +T ValueLayerNDSU::getSecondaryValueAt(Point location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return 0; + } + return otherDataSpace[indx]; +} + +template +T ValueLayerNDSU::getSecondaryValueAt(vector location, bool& errFlag){ + errFlag = false; + int indx = this->getIndex(location); + if(indx == -1){ + errFlag = true; + return 0; + } + return otherDataSpace[indx]; +} + +template +void ValueLayerNDSU::copyCurrentToSecondary(){ + T d = 0; + memcpy(otherDataSpace, currentDataSpace, AbstractValueLayerND::length * sizeof d); +} + +template +void ValueLayerNDSU::copySecondaryToCurrent(){ + T d = 0; + memcpy(currentDataSpace, otherDataSpace, AbstractValueLayerND::length * sizeof d); +} + +template +void ValueLayerNDSU::flowback(){ + std::cout << " valueLayer is executing flowback on rank " << repast::RepastProcess::instance()->rank() << std::endl; + // Fill the other data space with zeros + T d = 0; + memset(otherDataSpace, 0, AbstractValueLayerND::length * sizeof d); + + // Loop through the (3^N - 1)/2 directions + + // Note: TODO: Do all the sends as 'waitall', then process all the receives, with the final + // sends waitall being the block before returning to simulation operation + + // For each direction: + RelativeLocation relLoc(AbstractValueLayerND::numDims); + int listSize = AbstractValueLayerND::neighborCount; + std::cout << " valueLayer is executing flowback on rank " << repast::RepastProcess::instance()->rank() << " LIST SIZE = " << listSize << std::endl; + MPI_Request* flowbackRequests = new MPI_Request[4]; // No more than four at a time (may be only 2) + do{ + int LDir = relLoc.getIndex(); + int RDir = RelativeLocation::getReverseDirectionIndex(relLoc.getCurrentValue()); + // Need to create a send to the L and a send to the R, then matching receives + // We create these in pairs because we can take a small advantage of non-blocking + // sends but be assured that the L and R data are not going to interfere + RankDatum* datum; + + + int countOfRequests = 0; + for(int i = 0; i < listSize; i++){ // To do: Shouldn't loop through this, but instead should index once + datum = & AbstractValueLayerND::neighborData[i]; + // Note: (!!!) Yes, we are using the 'send' pointer for the _receive_ and the 'receive' pointer for the send... + if(datum->sendDir == LDir){ + MPI_Isend(¤tDataSpace[datum->receivePtrOffset], 1, datum->datatype, + datum->rank, 1001, AbstractValueLayerND::cartTopology->topologyComm, &flowbackRequests[countOfRequests]); + countOfRequests++; + } + else if(datum->sendDir == RDir){ + MPI_Isend(¤tDataSpace[datum->receivePtrOffset], 1, datum->datatype, + datum->rank, 2002, AbstractValueLayerND::cartTopology->topologyComm, &flowbackRequests[countOfRequests]); + countOfRequests++; + } + if(datum->recvDir == LDir){ + MPI_Irecv(&otherDataSpace[datum->sendPtrOffset], 1, datum->datatype, + datum->rank, 1001, AbstractValueLayerND::cartTopology->topologyComm, &flowbackRequests[countOfRequests]); + countOfRequests++; + } + else if(datum->recvDir == RDir){ + MPI_Irecv(&otherDataSpace[datum->sendPtrOffset], 1, datum->datatype, + datum->rank, 2002, AbstractValueLayerND::cartTopology->topologyComm, &flowbackRequests[countOfRequests]); + countOfRequests++; + } + } + MPI_Status statuses[countOfRequests]; + // Perform the sends and receives + std::cout << " valueLayer is executing flowback on rank " << repast::RepastProcess::instance()->rank() << " DOING WAITALL WITH LDIRECTION " << LDir << std::endl; + MPI_Waitall(countOfRequests, flowbackRequests, statuses); + std::cout << " valueLayer is executing flowback on rank " << repast::RepastProcess::instance()->rank() << " DONE WITH WAITALL... " << std::endl; + // Copy the received data from the other data space into + // the current data space, summing the values and clearing + // the other data space to zeros + sumInto(currentDataSpace, otherDataSpace, AbstractValueLayerND::numDims - 1); + + relLoc.increment(); + }while(relLoc.validNonCenter()); // Note: STOP at 0,0,0,0...: because each location creates a send and a receive in both L and R, only need to do half + + delete[] flowbackRequests; // Cleanup + +} + +template +void ValueLayerNDSU::fillDimension(T localValue, T bufferValue, bool doBufferZone, bool doLocal, T* dataSpace1Pointer, T* dataSpace2Pointer, int dimIndex){ + if(!doBufferZone && !doLocal) return; + int bufferEdge = AbstractValueLayerND::dimensionData[dimIndex].leftBufferSize; + int localEdge = bufferEdge + AbstractValueLayerND::dimensionData[dimIndex].localWidth; + int upperBound = localEdge + AbstractValueLayerND::dimensionData[dimIndex].rightBufferSize; + + int pointerIncrement = AbstractValueLayerND::places[dimIndex]; + + + int i = 0; + for(; i < bufferEdge; i++){ + if(doBufferZone){ + if(dimIndex == 0){ + *dataSpace1Pointer = bufferValue; + *dataSpace2Pointer = bufferValue; + } + else{ + fillDimension(bufferValue, bufferValue, doBufferZone, doLocal, dataSpace1Pointer, dataSpace2Pointer, dimIndex - 1); + } + } + // Increment the pointers + dataSpace1Pointer += pointerIncrement; + dataSpace2Pointer += pointerIncrement; + } + for(; i < localEdge; i++){ + if(doLocal){ + if(dimIndex == 0){ + *dataSpace1Pointer = localValue; + *dataSpace2Pointer = localValue; + } + else{ + fillDimension(localValue, bufferValue, doBufferZone, doLocal, dataSpace1Pointer, dataSpace2Pointer, dimIndex - 1); + } + } + // Increment the pointers + dataSpace1Pointer += pointerIncrement; + dataSpace2Pointer += pointerIncrement; + } + if(doBufferZone){ // Note: we don't need to finish this at all if not doing buffer zone + for(; i < upperBound; i++){ + if(dimIndex == 0){ + *dataSpace1Pointer = bufferValue; + *dataSpace2Pointer = bufferValue; + } + else{ + fillDimension(bufferValue, bufferValue, doBufferZone, doLocal, dataSpace1Pointer, dataSpace2Pointer, dimIndex - 1); + } + } + dataSpace1Pointer += pointerIncrement; + dataSpace2Pointer += pointerIncrement; + } + +} + +template +void ValueLayerNDSU::writeDimension(std::ofstream& outfile, T* dataSpacePointer, int* currentPosition, int dimIndex, bool writeSharedBoundaryAreas){ + int bufferEdge = AbstractValueLayerND::dimensionData[dimIndex].leftBufferSize; + int localEdge = bufferEdge + AbstractValueLayerND::dimensionData[dimIndex].localWidth; + int upperBound = localEdge + AbstractValueLayerND::dimensionData[dimIndex].rightBufferSize; + + int pointerIncrement = AbstractValueLayerND::places[dimIndex]; + int i = 0; + for(; i < bufferEdge; i++){ + currentPosition[dimIndex] = i; + if(writeSharedBoundaryAreas){ + if(dimIndex == 0){ + T val = *dataSpacePointer; + if(val != 0){ + for(int j = 0; j < AbstractValueLayerND::numDims; j++) outfile << (currentPosition[j] - AbstractValueLayerND::dimensionData[j].leftBufferSize + AbstractValueLayerND::dimensionData[j].localBoundariesMin) << ","; + outfile << val << endl; + } + } + else{ + writeDimension(outfile, dataSpacePointer, currentPosition, dimIndex - 1, writeSharedBoundaryAreas); + } + } + // Increment the pointers + dataSpacePointer += pointerIncrement; + } + for(; i < localEdge; i++){ + currentPosition[dimIndex] = i; + if(dimIndex == 0){ + T val = *dataSpacePointer; + if(val != 0){ + for(int j = 0; j < AbstractValueLayerND::numDims; j++) outfile << (currentPosition[j] - AbstractValueLayerND::dimensionData[j].leftBufferSize + AbstractValueLayerND::dimensionData[j].localBoundariesMin) << ","; + outfile << val << endl; + } + } + else{ + writeDimension(outfile, dataSpacePointer, currentPosition, dimIndex - 1, writeSharedBoundaryAreas); + } + // Increment the pointers + dataSpacePointer += pointerIncrement; + } + if(writeSharedBoundaryAreas){ // Note: we don't need to finish this at all if not doing buffer zone + for(; i < upperBound; i++){ + currentPosition[dimIndex] = i; + if(dimIndex == 0){ + T val = *dataSpacePointer; + if(val != 0){ + for(int j = 0; j < AbstractValueLayerND::numDims; j++) outfile << (currentPosition[j] - AbstractValueLayerND::dimensionData[j].leftBufferSize + AbstractValueLayerND::dimensionData[j].localBoundariesMin) << ","; + outfile << *dataSpacePointer << endl; + } + } + else{ + writeDimension(outfile, dataSpacePointer, currentPosition, dimIndex - 1, writeSharedBoundaryAreas); + } + } + dataSpacePointer += pointerIncrement; + } + +} + + +template +void ValueLayerNDSU::sumInto(T* dataSpace1Pointer, T* dataSpace2Pointer, int dimIndex){ + //std::cout << " ON RANK " << repast::RepastProcess::instance()->rank() << " SUMINTO RUNNING WITH DIM INDEX OF " << dimIndex << std::endl; + int bufferEdge = AbstractValueLayerND::dimensionData[dimIndex].leftBufferSize; + int localEdge = bufferEdge + AbstractValueLayerND::dimensionData[dimIndex].localWidth; + //int upperBound = localEdge + AbstractValueLayerND::dimensionData[dimIndex].rightBufferSize; + + int pointerIncrement = AbstractValueLayerND::places[dimIndex]; + int leftPointerSkip = pointerIncrement * AbstractValueLayerND::dimensionData[dimIndex].leftBufferSize; + //int rightPointerSkip = pointerIncrement * AbstractValueLayerND::dimensionData[dimIndex].rightBufferSize; + + // Skip the left buffer zone + dataSpace1Pointer += leftPointerSkip; + dataSpace2Pointer += leftPointerSkip; + + // Loop local edge to local edge + for(int i = bufferEdge; i < localEdge; i++){ + if(dimIndex == 0){ + //if(*dataSpace2Pointer > 0) std::cout << " ON RANK " << repast::RepastProcess::instance()->rank() << " SUMINTO FOUND VALUE OF " << *dataSpace2Pointer << " ADDING TO " << *dataSpace1Pointer << " at " << i << " dimIndex " << dimIndex << std::endl; + *dataSpace1Pointer += *dataSpace2Pointer; // Add space 2's value into space 1 + *dataSpace2Pointer = 0; // Zero out space 2 + } + else sumInto(dataSpace1Pointer, dataSpace2Pointer, dimIndex - 1); // Recursive call + dataSpace1Pointer += pointerIncrement; + dataSpace2Pointer += pointerIncrement; + } + +} + + + + + + + +} + +#endif /* VALUELAYERND_H_ */ diff --git a/libs/repast_hpc/Variable.cpp b/libs/repast_hpc/Variable.cpp new file mode 100644 index 0000000..9f2f91d --- /dev/null +++ b/libs/repast_hpc/Variable.cpp @@ -0,0 +1,69 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Variable.cpp + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#include "Variable.h" + +namespace repast { + +void DoubleVariable::write(size_t index, std::ofstream& out) { + out << data.at(index); +} +void DoubleVariable::insert(double* array, size_t size) { + data.insert(data.begin(), array, array + size); +} +void DoubleVariable::insert(int* array, size_t size) { + for (size_t i = 0; i < size; ++i) { + data.push_back(array[i]); + } +} + +void IntVariable::write(size_t index, std::ofstream& out) { + out << data.at(index); +} +void IntVariable::insert(int* array, size_t size) { + data.insert(data.begin(), array, array + size); +} +void IntVariable::insert(double* array, size_t size) { + for (size_t i = 0; i < size; ++i) { + data.push_back((int) array[i]); + } +} + +} diff --git a/libs/repast_hpc/Variable.h b/libs/repast_hpc/Variable.h new file mode 100644 index 0000000..11986e9 --- /dev/null +++ b/libs/repast_hpc/Variable.h @@ -0,0 +1,130 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Variable.h + * + * Created on: Aug 23, 2010 + * Author: nick + */ + +#ifndef VARIABLE_H_ +#define VARIABLE_H_ + +#include +#include + +namespace repast { + +/** + * Used in SVDataSet to manage and store the data. + */ +class Variable { + +public: + virtual ~Variable() {} + + /** + * Writes the data at the specified index to the specified ofstream. + * + * @param index the index of the data to write + * @param out the ofstream to write the data to + */ + virtual void write(size_t index, std::ofstream& out) = 0; + + + /** + * Inserts all the doubles in the double array into the collection + * of data stored in this Variable. + * + * @param array the array to insert + * @param size the size of the array + */ + virtual void insert(double* array, size_t size) = 0; + + + /** + * Inserts all the ints in the int array into the collection + * of data stored in this Variable. + * + * @param array the array to insert + * @param size the size of the array + */ + virtual void insert(int* array, size_t size) = 0; + + /** + * Clears this Variable of all the data stored in it. + */ + virtual void clear() = 0; + +}; + +/** + * Used in SVDataSet to manage integer data. + */ +class IntVariable: public Variable { + +private: + std::vector data; + +public: + + virtual void write(size_t index, std::ofstream& out); + virtual void insert(double* array, size_t size); + virtual void insert(int* array, size_t size); + virtual void clear() { + data.clear(); + } +}; + +/** + * Used in SVDataSet to manage double data. + */ +class DoubleVariable: public Variable { + +private: + std::vector data; + +public: + + virtual void write(size_t index, std::ofstream& out); + virtual void insert(double* array, size_t size); + virtual void insert(int* array, size_t size); + virtual void clear() { + data.clear(); + } +}; + +} + +#endif /* VARIABLE_H_ */ diff --git a/libs/repast_hpc/Vertex.h b/libs/repast_hpc/Vertex.h new file mode 100644 index 0000000..bb44652 --- /dev/null +++ b/libs/repast_hpc/Vertex.h @@ -0,0 +1,238 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Vertex.h + * + * Created on: Oct 13, 2010 + * Author: nick + */ + +#ifndef VERTEX_H_ +#define VERTEX_H_ + +#include "AgentId.h" + +#include +#include + +namespace repast { + +template +class Vertex; + +/** + * Hashes a Vertex using the hashcode of the AgentId that + * the vertex contains. + */ +template +struct HashVertex { + std::size_t operator()(Vertex* vertex) const { + return vertex->item()->getId().hashcode(); + } +}; + +/** + * Unary function used in the transform_iterator that allows an + * iterator over the vertex map to return the node. + */ +template +struct NodeGetter: public std::unary_function< + typename boost::unordered_map*, HashId>::value_type, V*> { + V* operator()(const typename boost::unordered_map*, HashId>::value_type& value) const { + return value.second->ptr.get(); + } +}; + +/** + + * Used internally by repast graphs / networks to encapsulate Vertices. + * + * @tparam V the type of object stored by in a Vertex. + * @tparam E the edge type of the network. + */ +template +class Vertex { + +public: + /** + * Typedef for the adjacency list map that contains the other Vertices that + * this Vertex links to. + */ + typedef boost::unordered_map*, boost::shared_ptr, HashVertex > AdjListMap; + typedef typename AdjListMap::iterator AdjListMapIterator; + + /** + * Enum the identifies whether an edge is incoming or outgoing. + */ + enum EdgeType { + INCOMING, OUTGOING + }; + + /** + * Creates a Vertex that contains the specified item. + * + * @param item the item the Vertex should contain + */ + Vertex(boost::shared_ptr item); + virtual ~Vertex() { + } + + /** + * Removes the edge of the specified type between this Vertex and the + * specified Vertex. + * + * @param other the other end of the edge + * @param type the type of edge to remove + * + * @return the removed edge if such an edge was found, otherwise 0. + */ + virtual boost::shared_ptr removeEdge(Vertex* other, EdgeType type) = 0; + + /** + * Finds the edge of the specified type between this Vertex and the + * specified vertex. + * + * @param other the other end of the edge + * @param type the type of edge to remove + * + * @return the found edge, or 0. + */ + virtual boost::shared_ptr findEdge(Vertex* other, EdgeType type) = 0; + + /** + * Adds an edge of the specified type between this Vertex and the + * specified vertex. + * + * @param edge the edge to add + * @param other the other end of the edge + * @param type the type of edge to add + */ + virtual void addEdge(Vertex* other, boost::shared_ptr edge, EdgeType type) = 0; + + /** + * Gets the successors of this Vertex. + * + * @param [out] the vector where any successors will be put + */ + virtual void successors(std::vector& out) = 0; + + /** + * Gets the predecessors of this Vertex. + * + * @param [out] the vector where any predecessors will be put + */ + virtual void predecessors(std::vector& out) = 0; + + /** + * Gets the Vertices adjacent to this Vertex. + * + * @param [out] the vector where the adjacent vectors will be put + */ + virtual void adjacent(std::vector& out) = 0; + + /** + * Gets all the edges of the specified type in which this Vertex + * participates and return them in out. + * + * @param type the type of edges to get + * @param [out] where the edges will be put. + */ + virtual void edges(EdgeType type, std::vector >& out) = 0; + + /** + * Gets the in degree of this Vertex. + * + * @return the in degree of this Vertex. + */ + virtual int inDegree() = 0; + + /** + * Gets the out degree of this Vertex. + * + * @return the out degree of this Vertex. + */ + virtual int outDegree() = 0; + + /** + * Gets the item that this Vertex contains. + * + * @return the item. + */ + boost::shared_ptr item() const { + return ptr; + } + +protected: + friend struct NodeGetter ; + boost::shared_ptr ptr; + boost::shared_ptr removeEdge(Vertex* other, AdjListMap* adjMap); + void getItems(AdjListMap *adjMap, std::vector& out); + void edges(AdjListMap *adjMap, std::vector >& out); +}; + +template +Vertex::Vertex(boost::shared_ptr item) : + ptr(item) { +} + +template +boost::shared_ptr Vertex::removeEdge(Vertex* other, AdjListMap* adjMap) { + boost::shared_ptr ret; + AdjListMapIterator iter = adjMap->find(other); + if (iter != adjMap->end()) { + ret = iter->second; + adjMap->erase(iter); + } + return ret; +} + +template +void Vertex::getItems(AdjListMap *adjMap, std::vector& out) { + const AdjListMapIterator iterEnd = adjMap->end(); + for (AdjListMapIterator iter = adjMap->begin(); iter != iterEnd; ++iter) { + out.push_back(iter->first->item().get()); + } +} + +template +void Vertex::edges(AdjListMap *adjMap, std::vector >& out){ + const AdjListMapIterator mapEnd = adjMap->end(); + for (AdjListMapIterator iter = adjMap->begin(); iter != mapEnd; ++iter) { + out.push_back(iter->second); + } +} + +} + +#endif /* VERTEX_H_ */ diff --git a/libs/repast_hpc/WeightedRandomSelector.h b/libs/repast_hpc/WeightedRandomSelector.h new file mode 100644 index 0000000..1ac43f3 --- /dev/null +++ b/libs/repast_hpc/WeightedRandomSelector.h @@ -0,0 +1,162 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * WeightedRandomSelector.h + * + * Created on: 12 June 2015 + * Author: murphy + */ + +#ifndef WEIGHTEDRANDOMSELECTOR_H_ +#define WEIGHTEDRANDOMSELECTOR_H_ + +#include + +#include "Random.h" + +namespace repast { + +template +class WeightedRandomSelector{ + +private: + + static bool dblComp (double lhs, double rhs) { return lhs > rhs; } // Note: we want these sorted in reverse order + + std::multimap* scoresAndObjects; + std::set* contentSet; + double total; + +public: + WeightedRandomSelector(); + + virtual ~WeightedRandomSelector(); + + void set(T* instance, double score); + + double remove(T* instance); + + bool contains(T* instance); + + T* getRandomInstance(); + + void clear(); + + size_t size(); + + void report(); + +}; + +template +WeightedRandomSelector::WeightedRandomSelector(){ + scoresAndObjects = new std::multimap(dblComp); + contentSet = new std::set(); + total = 0; +}; + +template +WeightedRandomSelector::~WeightedRandomSelector(){ + delete scoresAndObjects; + delete contentSet; +} + +template +void WeightedRandomSelector::set(T* instance, double score){ + remove(instance); + if(score <= 0) return; // Setting to zero removes from set + scoresAndObjects->emplace(score, instance); + contentSet->emplace(instance); + total += score; +} + +template +double WeightedRandomSelector::remove(T* instance){ + if(contentSet->find(instance) != contentSet->end()){ + contentSet->erase(instance); + for(typename std::multimap::iterator iter = scoresAndObjects->begin(); iter != scoresAndObjects->end(); iter++){ + if(iter->second == instance){ + double score = iter->first; + total -= score; + scoresAndObjects->erase(iter); + return score; + } + } + } + return 0; +} + +template +bool WeightedRandomSelector::contains(T* instance){ + return contentSet->find() != contentSet->end(); +} + +template +T* WeightedRandomSelector::getRandomInstance(){ + if(scoresAndObjects->size() == 0) return 0; + double val = Random::instance()->nextDouble() * total; + double sum = 0; + for(typename std::multimap::iterator iter = scoresAndObjects->begin(); iter != scoresAndObjects->end(); iter++){ + sum += iter->first; + if(sum > val) return iter->second; + } + // Can't happen, but if it did, return the last entry + typename std::multimap::iterator iter = scoresAndObjects->end(); + iter--; + return iter->second; +} + +template +void WeightedRandomSelector::clear(){ + scoresAndObjects->clear(); + contentSet->clear(); + total = 0; +} + +template +size_t WeightedRandomSelector::size(){ + return contentSet->size(); +} + +template +void WeightedRandomSelector::report(){ + for(typename std::multimap::iterator iter = scoresAndObjects->begin(); iter != scoresAndObjects->end(); iter++){ + std::cout << " " << *(iter->second) << " == " << iter->first << std::endl; + } +} + + +} + +#endif /* AGENTID_H_ */ diff --git a/libs/repast_hpc/cmake/Modules/FindNetCDF.cmake b/libs/repast_hpc/cmake/Modules/FindNetCDF.cmake new file mode 100644 index 0000000..ad0062d --- /dev/null +++ b/libs/repast_hpc/cmake/Modules/FindNetCDF.cmake @@ -0,0 +1,71 @@ +# - Find NetCDF +# Find the native NetCDF includes and library +# +# NETCDF_INCLUDES - where to find netcdf.h, etc +# NETCDF_LIBRARIES - Link these libraries when using NetCDF +# NETCDF_FOUND - True if NetCDF found including required interfaces (see below) +# +# Your package can require certain interfaces to be FOUND by setting these +# +# NETCDF_CXX - require the C++ interface and link the C++ library +# NETCDF_F77 - require the F77 interface and link the fortran library +# NETCDF_F90 - require the F90 interface and link the fortran library +# +# The following are not for general use and are included in +# NETCDF_LIBRARIES if the corresponding option above is set. +# +# NETCDF_LIBRARIES_C - Just the C interface +# NETCDF_LIBRARIES_CXX - C++ interface, if available +# NETCDF_LIBRARIES_F77 - Fortran 77 interface, if available +# NETCDF_LIBRARIES_F90 - Fortran 90 interface, if available +# +# Normal usage would be: +# set (NETCDF_F90 "YES") +# find_package (NetCDF REQUIRED) +# target_link_libraries (uses_f90_interface ${NETCDF_LIBRARIES}) +# target_link_libraries (only_uses_c_interface ${NETCDF_LIBRARIES_C}) + +if (NETCDF_INCLUDES AND NETCDF_LIBRARIES) + # Already in cache, be silent + set (NETCDF_FIND_QUIETLY TRUE) +endif (NETCDF_INCLUDES AND NETCDF_LIBRARIES) + +find_path (NETCDF_INCLUDES netcdf.h + HINTS NETCDF_DIR ENV NETCDF_DIR) + +find_library (NETCDF_LIBRARIES_C NAMES netcdf) +mark_as_advanced(NETCDF_LIBRARIES_C) + +set (NetCDF_has_interfaces "YES") # will be set to NO if we're missing any interfaces +set (NetCDF_libs "${NETCDF_LIBRARIES_C}") + +get_filename_component (NetCDF_lib_dirs "${NETCDF_LIBRARIES_C}" PATH) + +macro (NetCDF_check_interface lang header libs) + if (NETCDF_${lang}) + find_path (NETCDF_INCLUDES_${lang} NAMES ${header} + HINTS "${NETCDF_INCLUDES}" NO_DEFAULT_PATH) + find_library (NETCDF_LIBRARIES_${lang} NAMES ${libs} + HINTS "${NetCDF_lib_dirs}" NO_DEFAULT_PATH) + mark_as_advanced (NETCDF_INCLUDES_${lang} NETCDF_LIBRARIES_${lang}) + if (NETCDF_INCLUDES_${lang} AND NETCDF_LIBRARIES_${lang}) + list (INSERT NetCDF_libs 0 ${NETCDF_LIBRARIES_${lang}}) # prepend so that -lnetcdf is last + else (NETCDF_INCLUDES_${lang} AND NETCDF_LIBRARIES_${lang}) + set (NetCDF_has_interfaces "NO") + message (STATUS "Failed to find NetCDF interface for ${lang}") + endif (NETCDF_INCLUDES_${lang} AND NETCDF_LIBRARIES_${lang}) + endif (NETCDF_${lang}) +endmacro (NetCDF_check_interface) + +NetCDF_check_interface (CXX netcdfcpp.h netcdf_c++) +NetCDF_check_interface (F77 netcdf.inc netcdff) +NetCDF_check_interface (F90 netcdf.mod netcdff) + +set (NETCDF_LIBRARIES "${NetCDF_libs}" CACHE STRING "All NetCDF libraries required for interface level") + +# handle the QUIETLY and REQUIRED arguments and set NETCDF_FOUND to TRUE if +# all listed variables are TRUE +include (FindPackageHandleStandardArgs) +find_package_handle_standard_args (NetCDF DEFAULT_MSG NETCDF_LIBRARIES NETCDF_INCLUDES NetCDF_has_interfaces) + +mark_as_advanced (NETCDF_LIBRARIES NETCDF_INCLUDES) \ No newline at end of file diff --git a/libs/repast_hpc/initialize_random.cpp b/libs/repast_hpc/initialize_random.cpp new file mode 100644 index 0000000..31c219c --- /dev/null +++ b/libs/repast_hpc/initialize_random.cpp @@ -0,0 +1,231 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DistributionFactory.cpp + * + * Created on: Oct 27, 2009 + * Author: nick + */ + +#include "boost/random/mersenne_twister.hpp" +#include +#include +#include + +#include "initialize_random.h" +#include "Random.h" +#include "Utilities.h" + +#include "RepastProcess.h" // TESTING ONLY + +#include + +using namespace std; + +namespace repast { + +const size_t DIST_TAG_LENGTH = 13; + +const string DBL_UNI_DIST = "double_uniform"; +const string INT_UNI_DIST = "int_uniform"; +const string TRIANGLE_DIST = "triangle"; +const string CAUCHY_DIST = "cauchy"; +const string EXPONENTIAL_DIST = "exponential"; +const string NORMAL_DIST = "normal"; +const string LOG_NORMAL_DIST = "lognormal"; + +void initializeSeed(const Properties& props, boost::mpi::communicator* comm); + +void createDblUni(string& name, vector& params) { + if (params.size() != 3) + throw Repast_Error_19(name, params); // Wrong number of parameters + double from = strToDouble(trim(params[1])); + double to = strToDouble(trim(params[2])); + Random* random = Random::instance(); + _RealUniformGenerator gen(random->engine(), boost::uniform_real<>(from, to)); + NumberGenerator* ng = new DefaultNumberGenerator<_RealUniformGenerator> (gen); + random->putGenerator(name, ng); +} + +void createIntUni(string& name, vector& params) { + if (params.size() != 3) + throw Repast_Error_20(name, params); // Wrong number of parameters + int from = strToInt(trim(params[1])); + int to = strToInt(trim(params[2])); + + Random* random = Random::instance(); + _IntUniformGenerator gen(random->engine(), boost::uniform_int<>(from, to)); + NumberGenerator* ng = new DefaultNumberGenerator<_IntUniformGenerator> (gen); + random->putGenerator(name, ng); +} + +void createTriangle(string& name, vector& params) { + if (params.size() != 4) + throw Repast_Error_21(name, params); // Wrong number of parameters + double lower = strToDouble(trim(params[1])); + double mostLikely = strToDouble(trim(params[2])); + double upper = strToDouble(trim(params[3])); + + Random* random = Random::instance(); + boost::triangle_distribution<> dist(lower, mostLikely, upper); + _TriangleGenerator gen(random->engine(), dist); + NumberGenerator* ng = new DefaultNumberGenerator<_TriangleGenerator> (gen); + random->putGenerator(name, ng); +} + +void createCauchy(string& name, vector& params) { + if (params.size() != 3) + throw Repast_Error_22(name, params); // Wrong number of parameters + double median = strToDouble(trim(params[1])); + double sigma = strToDouble(trim(params[2])); + + Random* random = Random::instance(); + boost::cauchy_distribution<> dist(median, sigma); + _CauchyGenerator gen(random->engine(), dist); + NumberGenerator* ng = new DefaultNumberGenerator<_CauchyGenerator> (gen); + random->putGenerator(name, ng); +} + +void createExponential(string& name, vector& params) { + if (params.size() != 2) + throw Repast_Error_23(name, params); // Wrong number of parameters + double lambda = strToDouble(trim(params[1])); + + Random* random = Random::instance(); + boost::exponential_distribution<> dist(lambda); + _ExponentialGenerator gen(random->engine(), dist); + NumberGenerator* ng = new DefaultNumberGenerator<_ExponentialGenerator> (gen); + random->putGenerator(name, ng); +} + +void createNormal(string& name, vector& params) { + if (params.size() != 3) + throw Repast_Error_24(name, params); // Wrong number of parameters + double mean = strToDouble(trim(params[1])); + double sigma = strToDouble(trim(params[2])); + + Random* random = Random::instance(); + boost::normal_distribution<> dist(mean, sigma); + _NormalGenerator gen(random->engine(), dist); + NumberGenerator* ng = new DefaultNumberGenerator<_NormalGenerator> (gen); + random->putGenerator(name, ng); +} + +void createLogNormal(string& name, vector& params) { + if (params.size() != 3) + throw Repast_Error_25(name, params); // Wrong number of parameters + double mean = strToDouble(trim(params[1])); + double sigma = strToDouble(trim(params[2])); + + Random* random = Random::instance(); + boost::lognormal_distribution<> dist(mean, sigma); + _LogNormalGenerator gen(random->engine(), dist); + NumberGenerator* ng = new DefaultNumberGenerator<_LogNormalGenerator> (gen); + random->putGenerator(name, ng); +} + +void initializeRandom(Properties& props, boost::mpi::communicator* comm) { + initializeSeed(props, comm); + for (Properties::key_iterator iter = props.keys_begin(); iter != props.keys_end(); iter++) { + string key = *iter; + // starts with distribution tag + if (key.find("distribution.", 0) == 0 && key.size() > DIST_TAG_LENGTH) { + string name = key.substr(13); + vector params; + tokenize(props.getProperty(key), params, ","); + if (params.size() < 1) + throw Repast_Error_26(name, params); // Wrong number of parameters + const string type = params[0]; + if (type == DBL_UNI_DIST) + createDblUni(name, params); + else if (type == INT_UNI_DIST) + createIntUni(name, params); + else if (type == TRIANGLE_DIST) + createTriangle(name, params); + else if (type == CAUCHY_DIST) + createCauchy(name, params); + else if (type == EXPONENTIAL_DIST) + createExponential(name, params); + else if (type == NORMAL_DIST) + createNormal(name, params); + else if (type == LOG_NORMAL_DIST) + createLogNormal(name, params); + else + throw Repast_Error_27(name, params); // Unrecognized type name + } + } +} + +void initializeSeed(Properties& props, boost::mpi::communicator* comm) { + // Default value for seed is local proc's system time + boost::uint32_t seed = (boost::uint32_t)time(0); + + if(props.contains(GLOBAL_RANDOM_SEED_PROPERTY)){ + std::string propVal = props.getProperty(GLOBAL_RANDOM_SEED_PROPERTY); + if(propVal.compare("AUTO") == 0){ + if(comm == 0) throw Repast_Error_57(); // Needs communicator to share global seed + boost::mpi::broadcast(*comm, seed, 0); + props.putProperty(GLOBAL_RANDOM_SEED_PROPERTY, seed); + } + else{ + seed = strToUInt(propVal); + } + } + else{ + // If no global seed, then each proc will use its own seed + if(props.contains(RANDOM_SEED_PROPERTY)){ + std::string propVal = props.getProperty(RANDOM_SEED_PROPERTY); + if(propVal.compare("AUTO") == 0){ + if(comm != 0) boost::mpi::broadcast(*comm, seed, 0); + props.putProperty(RANDOM_SEED_PROPERTY, seed); + } + else{ + seed = (boost::uint32_t)strToUInt(propVal); + } + } + if(comm != 0){ + boost::mt19937 gen; + boost::uniform_real<> dist(0, boost::numeric::bounds::highest()); + gen.seed(seed); + boost::variate_generator > localRNG(gen, dist); + for(int i = 0; i < comm->rank(); i++) seed = localRNG(); // The assignment only matters on the last time through, but calling the generator is requisite. + } + stringstream ss; + ss << std::fixed << seed; + props.putProperty(RANDOM_SEED_PROPERTY, ss.str()); + } + Random::initialize(seed); +} + +} diff --git a/libs/repast_hpc/initialize_random.h b/libs/repast_hpc/initialize_random.h new file mode 100644 index 0000000..505030f --- /dev/null +++ b/libs/repast_hpc/initialize_random.h @@ -0,0 +1,129 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * DistributionFactory.h + * + * Created on: Oct 27, 2009 + * Author: nick + */ + +#ifndef INITRANDOM_H_ +#define INITRANDOM_H_ + +#include + +#include "Properties.h" + +#define GLOBAL_RANDOM_SEED_PROPERTY "global.random.seed" +#define RANDOM_SEED_PROPERTY "random.seed" + +namespace repast { + +/** + * Initializes the Random singleton with any properties (seed, + * distributions) found in the properties object. + */ +void initializeRandom(Properties& props, boost::mpi::communicator* comm = 0); + +/* + * Generates a random number seed to use to initialize the random singleton + * + * Usage: + * + * Two properties can be specified in the properties object: "global.random.seed" + * and "random.seed" + * + * If global.random.seed is used, it must include either a numeric value usable as a seed + * OR the string 'AUTO'. If the numeric value is specified, this value is used as the + * random number seed on all processes. If the 'AUTO' value is specified, a seed is generated + * from system time on process 0 and communicated to all processes, so that all processes + * use the same seed. Note that if 'AUTO is used, a communicator must be provided to this + * function or an error will be thrown. + * + * If no global.random.seed is present in the properties collection, each processor will have + * its own random number seed. The seed is determined by the value for random.seed + * in the properties object and the presence or absence of a communicator object passed to + * this function. The value of random.seed must be either a numeric value usable as a seed + * or the string 'AUTO'; if no value for random.seed is specified, behavior is identical + * to random.number=AUTO. If the 'AUTO' value is specified or the property is omitted, a seed is + * generated from the system time; note that each process may have a different value for the + * seed because the system time will not necessarily be synchronized (though it cannot also + * be guaranteed that each process will have a unique value, and the values will likely be close + * together and thus not be completely independent). If a communicator object is passed to + * the function, the seeds used on each process will be different, but will be derived from + * the seed on process 0. + * + * If either 'global.random.seed' or 'random.seed' is "AUTO" in the properties collection, + * it is re-set with the value used for the seed; if both are absent, a random.seed value is added. + * If the seeds are different on each process, the property value in each process's instance + * will reflect the seed that it used; for seeds created using a communicator, the seed on + * process '0' should be preferentially recorded because it can be used to recreate the + * whole simulation run. + * + * In summary: + * + * If 'global.random.seed' IS in the properties collection: + * + * global.random.seed=: + * All processes will use this value as the random number seed. + * + * global.random.seed=AUTO: + * A seed based on system time will be generated on proc 0 and sent to all other processes; + * a communicator must be passed to this function. + * + * + * If 'global.random.seed' is NOT in the properties collection: + * + * random.seed=AUTO (or omitted) & No communicator passed: + * All processes use local system time to create their random number seeds + * + * random.seed= & No communicator passed: + * All processes use the value of random.seed (if all properties collections are the + * same on all processes, the seed is effectively global). + * + * random.seed=AUTO (or omitted) & Communicator passed: + * System time on process 0 is used to create a seed that is broadcast to all other processes; + * this value is then used by each process to derive different random seeds according to + * rank, and these are then used to initialize the random number generation system. + * + * random.seed= & Communicator passed: + * The random number seed in the properties collection will be used by each process to create + * different random seeds according to rank; these are then used to initialize the random + * number generation system. + */ +void initializeSeed(Properties& props, boost::mpi::communicator* comm); + +} + +#endif diff --git a/libs/repast_hpc/io.cpp b/libs/repast_hpc/io.cpp new file mode 100644 index 0000000..31b5a42 --- /dev/null +++ b/libs/repast_hpc/io.cpp @@ -0,0 +1,109 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * io.cpp + * + * Created on: Sep 25, 2009 + * Author: nick + */ + +#include "io.h" + +#include +#include +#include + +namespace repast { + +using namespace std; + +void str_trim(string &str) { + size_t pos = str.find_last_not_of(" \t"); + if (pos != string::npos) { + str.erase(pos + 1); + pos = str.find_first_not_of(" \t"); + if (pos != string::npos) { + str.erase(0, pos); + } + } else { + str = ""; + } +} + +void timestamp2(string& str) { + struct tm *tmp; + time_t t; + + t = time(NULL); + tmp = localtime(&t); + ostringstream os; + + os << setw(4); + os << (tmp->tm_year + 1900); + os << setfill('0') << setw(2); + os << (tmp->tm_mon + 1); + os << setfill('0') << setw(2); + os << tmp->tm_mday; + + os << setw(2) << setfill('0'); + os << tmp->tm_hour; + os << setw(2) << setfill('0') << tmp->tm_min; + os << setw(2) << setfill('0') << tmp->tm_sec; + str = os.str(); +} + +void timestamp(string& str) { + struct tm *tmp; + time_t t; + + t = time(NULL); + tmp = localtime(&t); + ostringstream os; + os << setfill('0') << setw(2); + os << tmp->tm_mday << "."; + + os << setfill('0') << setw(2); + os << (tmp->tm_mon + 1) << "."; + + os << setw(4); + os << (tmp->tm_year + 1900) << " "; + + os << setw(2) << setfill('0'); + os << tmp->tm_hour << ":"; + os << setw(2) << setfill('0') << tmp->tm_min << ":"; + os << setw(2) << setfill('0') << tmp->tm_sec; + str = os.str(); +} + +} diff --git a/libs/repast_hpc/io.h b/libs/repast_hpc/io.h new file mode 100644 index 0000000..fc5856d --- /dev/null +++ b/libs/repast_hpc/io.h @@ -0,0 +1,66 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * io.h + * + * Created on: Sep 25, 2009 + * Author: nick + */ + +#ifndef IO_H_ +#define IO_H_ + +#include + +namespace repast { + +/** + * Inplace string trim. + */ +void str_trim(std::string &str); + +/** + * Sets str to localtime in format of dd.mm.yyyy hh:mm:ss. + */ +void timestamp(std::string& str); + +/** + * Sets str to localtime in format of yyyymmddhhmmss. + */ +void timestamp2(std::string& str); + +} + + +#endif /* IO_H_ */ diff --git a/libs/repast_hpc/logger.cpp b/libs/repast_hpc/logger.cpp new file mode 100644 index 0000000..f720556 --- /dev/null +++ b/libs/repast_hpc/logger.cpp @@ -0,0 +1,834 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * logger.cpp + * + * Created on: Dec 16, 2008 + * Author: nick + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "logger.h" +#include "io.h" +#include "RepastErrors.h" + + +namespace fs = boost::filesystem; + + +using namespace std; + +namespace repast { + +typedef enum _TOKEN { + END = 0, ROOT, LOGGER, APPENDER, APPENDER_FILE, APPENDER_SIZE, APPENDER_BIDX, ERRORT +} TOKEN; + +const string ROOT_LOGGER_TAG = "logger.root"; +const string LOGGER_TAG = "logger."; +const string APPENDER_TAG = "appender."; +const string FILE_TAG = ".File"; +const string SIZE_TAG = ".MaxFileSize"; +const string BACK_IDX_TAG = ".MaxBackupIndex"; + +// DEBUG, INFO, WARN, ERROR, FATAL +const int LEVEL_COUNT = 5; +const char *LEVELS[5] = { "DEBUG", "INFO", "WARN", "ERROR", "FATAL" }; + +int count_char(const string& str, const char& to_count) { + int count = 0; + for (int i = 0, n = str.length(); i < n; i++) { + if (str[i] == to_count) + count++; + } + return count; +} + +bool ends_with(const string& str, const string& ends) { + size_t pos = str.rfind(ends, str.length()); + if (pos == string::npos) + return false; + + return str.length() - pos == ends.length(); +} + +class ConfigLexer { + +private: + int _line; +// ifstream* in; + istringstream* in; + + string _value, _key, _error; + + void format_error(const char* msg); + bool is_root(); + bool is_logger(); + bool is_appender(); + bool is_appender_file(); + bool is_appender_size(); + bool is_appender_bidx(); + +public: + ConfigLexer(const string& file_name, boost::mpi::communicator* comm = 0, int maxConfigFileSize = MAX_CONFIG_FILE_SIZE); + ~ConfigLexer(); + + TOKEN next_token(); + string key(); + string value(); + string error(); + int line(); + void reset(); +}; + +ConfigLexer::ConfigLexer(const string& file_name, boost::mpi::communicator* comm, int maxConfigFileSize) : + _line(0), _value(""), _key(""), _error("") { + + char* CONFIGFILEBUFFER = new char[maxConfigFileSize]; // All procs allocate memory for the properties file + + if(comm == 0 || comm->rank() == 0){ // If no communicator is passed, all ranks read props file + ifstream fileInStream(file_name.c_str()); + if (fileInStream.is_open()){ + fileInStream.read(CONFIGFILEBUFFER, maxConfigFileSize); + // Check if fail: + if(fileInStream.gcount() >= (maxConfigFileSize - 1)){ + throw repast::Repast_Error_41(maxConfigFileSize, fileInStream.gcount(), file_name); // Config file exceeds maximum allowed size + } + CONFIGFILEBUFFER[fileInStream.gcount()] = '\0'; // Add a null terminator + fileInStream.close(); + } else { + throw repast::Repast_Error_42(file_name); // Config file not found + } + } + if(comm != 0){ // If a communicator was passed, proc 0 broadcasts to all other procs + MPI_Bcast(CONFIGFILEBUFFER, maxConfigFileSize, MPI_CHAR, 0, *comm); + } + + std::string P(CONFIGFILEBUFFER); + delete[] CONFIGFILEBUFFER; + + in = new istringstream(P, ios_base::in); + + if (in == NULL || in->fail()) { + string err = "Error opening config file '" + file_name + "'"; + if (in != NULL) + delete in; + throw repast::Repast_Error_43(file_name); // Unknown error + } +} + +ConfigLexer::~ConfigLexer() { +// if (in != NULL) +// in->close(); + delete in; +} + +bool ConfigLexer::is_root() { + return _key == ROOT_LOGGER_TAG; +} + +bool ConfigLexer::is_logger() { + return _key.find(LOGGER_TAG) == 0; +} + +// its an appender if it starts with appender +// and there is a single dot +bool ConfigLexer::is_appender() { + return _key.find(APPENDER_TAG) == 0 && count_char(_key, '.') == 1; +} + +// its an appender.File if starts with +// appender., has two dots, and ends with File. +bool ConfigLexer::is_appender_file() { + return _key.find(APPENDER_TAG) == 0 && count_char(_key, '.') == 2 && ends_with(_key, FILE_TAG); +} + +// starts with appender, has two dots +// and ends with SIZE tag. +bool ConfigLexer::is_appender_size() { + return _key.find(APPENDER_TAG) == 0 && count_char(_key, '.') == 2 && ends_with(_key, SIZE_TAG); +} + +// starts with appender, has two dots +// and ends with BAC_IDX tag +bool ConfigLexer::is_appender_bidx() { + return _key.find(APPENDER_TAG) == 0 && count_char(_key, '.') == 2 && ends_with(_key, BACK_IDX_TAG); +} + +void ConfigLexer::format_error(const char* msg) { + stringstream str; + str << "Error in line " << _line << ": " << msg << endl; + _error = str.str(); +} + +string ConfigLexer::key() { + return _key; +} + +string ConfigLexer::value() { + return _value; +} + +string ConfigLexer::error() { + return _error; +} + +int ConfigLexer::line() { + return _line; +} + +TOKEN ConfigLexer::next_token() { + string str; + while (getline(*in, str)) { + _line++; + repast::str_trim(str); + if (str.length() > 0 && str[0] != '#') { + size_t pos = str.find_first_of("="); + if (pos == string::npos) { + format_error("'=' is missing."); + return ERRORT; + } + + _key = str.substr(0, pos); + repast::str_trim(_key); + if (_key.length() == 0) { + format_error("key is missing."); + return ERRORT; + } + + _value = ""; + if (str.length() > pos) { + _value = str.substr(pos + 1, str.length()); + } + repast::str_trim(_value); + if (_value.length() == 0) { + format_error("value is missing."); + return ERRORT; + } + + if (is_root()) + return ROOT; + if (is_logger()) + return LOGGER; + if (is_appender()) + return APPENDER; + if (is_appender_file()) + return APPENDER_FILE; + + if (is_appender_size()) + return APPENDER_SIZE; + if (is_appender_bidx()) + return APPENDER_BIDX; + + format_error("unexpected token"); + return ERRORT; + } + } + return END; +} + +void ConfigLexer::reset() { + in->seekg(0, ios_base::beg); +} + +Appender::Appender(const string name) : + _name(name) { +} +Appender::~Appender() { +} + +class CoutAppender: public Appender { + +public: + CoutAppender(); + ~CoutAppender() { + } + ; + void write(const string& line); +}; + +class CerrAppender: public Appender { + +public: + CerrAppender(); + ~CerrAppender() { + } + + void write(const string& line); +}; + +CerrAppender::CerrAppender() : + Appender("stderr") { +} + +void CerrAppender::write(const string& line) { + cout << line; +} + +CoutAppender::CoutAppender() : + Appender("stdout") { +} + +void CoutAppender::write(const string& line) { + cout << line; +} + +class RollingFileAppender: public Appender { + +public: + RollingFileAppender(const string name, const string file_name, int max_backup, int max_size); + ~RollingFileAppender(); + + virtual void write(const string& log_line); + virtual void close(); + +private: + MPI_File out; + string file_name; + int max_backup; + long max_size, cur_size; + bool isOpen; + + void resize_check(); + void init_cur_size(); + +}; + +RollingFileAppender::RollingFileAppender(const string name, const string file_name, int max_backup, int max_size) : + Appender(name), file_name(file_name), max_backup(max_backup), max_size(max_size), cur_size(-1), isOpen(false) { + +} + +RollingFileAppender::~RollingFileAppender() { + if (isOpen) { + MPI_File_sync(out); + MPI_File_close(&out); + } +} + +void RollingFileAppender::close() { + if (isOpen) { + MPI_File_sync(out); + MPI_File_close(&out); + isOpen = false; + } +} + +void RollingFileAppender::init_cur_size() { + + // check to see if the file exists + ifstream fin(file_name.c_str(), ifstream::in); + if (fin.fail()) { + // file does not yet exist + cur_size = 0; + } else { + fin.seekg(0, ios_base::end); + // size in bytes + cur_size = fin.tellg(); + fin.close(); + } + +} + +void RollingFileAppender::resize_check() { + + if (cur_size == -1) + init_cur_size(); + + if (cur_size > max_size) { + if (isOpen) { + // close the file + MPI_File_sync(out); + MPI_File_close(&out); + isOpen = false; + } + + for (int i = max_backup - 1; i >= 0; i--) { + ostringstream from; + if (i == 0) { + from << file_name; + } else { + from << file_name << i; + } + + ifstream fin(from.str().c_str(), ifstream::in); + + if (!fin.fail()) { + fin.close(); + ostringstream to; + to << file_name << (i + 1); + rename(from.str().c_str(), to.str().c_str()); + } + } + + cur_size = 0; + } + + if (!isOpen) { + + // reopen the mpi out + int mode = MPI_MODE_CREATE | MPI_MODE_WRONLY | MPI_MODE_APPEND; + fs::path filepath(file_name); + if (!fs::exists(filepath.parent_path())) { + fs::create_directories(filepath.parent_path()); + } + //out = MPI::File::Open(MPI::COMM_SELF, file_name.c_str(), mode, MPI::INFO_NULL); + MPI_File_open(MPI_COMM_SELF, (char*)file_name.c_str(), mode, MPI_INFO_NULL, &out); + isOpen = true; + } +} + +void RollingFileAppender::write(const string& log_line) { + resize_check(); + int count = log_line.length(); + MPI_Status status; + MPI_File_write(out, (void*)log_line.c_str(), count, MPI_CHAR, &status); + cur_size += count; +} + +Logger::Logger(const string name, LOG_LEVEL level, int proc_id) : + name(name), level(level), proc_id(proc_id) { +} + +void Logger::format_msg(LOG_LEVEL level, const string& msg, string& to_format) { + string ts; + repast::timestamp(ts); + ostringstream os; + os << ts << " [" << proc_id << "] " << LEVELS[level] << " " << name << " " << msg << endl; + to_format = os.str(); +} + +void Logger::log(LOG_LEVEL level, const std::string msg) { + string formatted_msg; + if (level >= this->level) { + formatted_msg = ""; + format_msg(level, msg, formatted_msg); + for (vector::iterator iter = appenders.begin(); iter != appenders.end(); ++iter) { + Appender* app = *iter; + app->write(formatted_msg); + } + } +} + +void Logger::close() { + for (vector::iterator iter = appenders.begin(); iter != appenders.end(); ++iter) { + Appender* app = *iter; + app->close(); + } +} + +void Logger::add_appender(Appender *appender) { + appenders.push_back(appender); +} + +AppenderBuilder::AppenderBuilder(const string name) : + name(name) { +} + +Appender* AppenderBuilder::build() { + if (name == "stdout") { + return new CoutAppender(); + } else if (name == "stderr") { + return new CerrAppender(); + } else { + return new RollingFileAppender(name, file_name, max_idx, max_size); + } +} + +Log4CLConfigurator::Log4CLConfigurator() : + line(0) { + app_map["stdout"] = new AppenderBuilder("stdout"); + app_map["stderr"] = new AppenderBuilder("stderr"); +} + +void Log4CLConfigurator::error_warn() { + cerr << "WARN: " << error << endl; +} + +int Log4CLConfigurator::parse_level(const string& str) const { + for (int i = 0; i < LEVEL_COUNT; i++) { + if (str == LEVELS[i]) + return i; + } + return -1; +} + +void Log4CLConfigurator::create_root_logger(const string& value) { + create_named_logger("root", value); +} + +void Log4CLConfigurator::create_logger(const string& key, const string& value) { + string name = key.substr(LOGGER_TAG.length()); + if (name.length() == 0) { + ostringstream os; + os << "Error in line " << line << ", logger is missing a name"; + error = os.str(); + error_warn(); + return; + } + + create_named_logger(name, value); +} + +void Log4CLConfigurator::create_appender(const string& key, const string& value) { + string name = key.substr(APPENDER_TAG.length()); + if (name.length() == 0) { + ostringstream os; + os << "Error in line " << line << ", appender is missing a name"; + error = os.str(); + error_warn(); + return; + } + + if (app_map.find(name) == app_map.end()) { + // add an appender builder for this appender name + AppenderBuilder* builder = new AppenderBuilder(name); + app_map[name] = builder; + builder->max_idx = 1; + // megabyte + builder->max_size = 1024 * 1024 * 10; + } +} + +AppenderBuilder* Log4CLConfigurator::get_appender_builder(const string& key) { + size_t start_pos = APPENDER_TAG.length(); + size_t end_pos = key.rfind(".", key.length()); + if (start_pos == end_pos) { + ostringstream os; + os << "Error in line " << line << ", appender is missing a name"; + error = os.str(); + error_warn(); + return NULL; + } + + ostringstream stream; + for (size_t i = start_pos; i < end_pos; i++) { + stream << key[i]; + } + string name = stream.str(); + + AppenderBuilder *builder; + map::const_iterator item = app_map.find(name); + if (item == app_map.end()) { + builder = new AppenderBuilder(name); + app_map[name] = builder; + builder->max_idx = 1; + // megabyte + builder->max_size = 1024 * 1024 * 10; + } else { + builder = item->second; + } + + return builder; +} + +void Log4CLConfigurator::create_appender_file(const string& key, const string& value) { + AppenderBuilder *builder = get_appender_builder(key); + if (builder != NULL) { + // split file_name on last "." and append a _proc_id to it + // if no "." then just append "_" + string file_name = value; + string parent_path; + size_t pos = value.find_last_of('/'); + if (pos != string::npos) { + file_name = value.substr(pos + 1); + parent_path = value.substr(0, pos + 1); + } + + pos = file_name.find_last_of('.'); + ostringstream os; + if (pos != string::npos) { + os << file_name.substr(0, pos) << "_" << proc_id << file_name.substr(pos, file_name.length()); + } else { + os << file_name << "_" << proc_id; + } + file_name = os.str(); + + builder->file_name = parent_path + file_name; + } +} + +void Log4CLConfigurator::create_appender_size(const string& key, const string& value) { + AppenderBuilder* builder = get_appender_builder(key); + if (builder != NULL) { + // given in K but we store in bytes + builder->max_size = atol(value.c_str()) * 1024; + } + +} + +void Log4CLConfigurator::create_appender_bidx(const string& key, const string& value) { + AppenderBuilder* builder = get_appender_builder(key); + if (builder != NULL) { + builder->max_idx = atoi(value.c_str()); + } +} + +void Log4CLConfigurator::create_named_logger(const string& name, const string& value) { + + string token; + istringstream stream(value); + int level = -1; + bool do_level = true; + while (getline(stream, token, ',')) { + repast::str_trim(token); + if (do_level) { + transform(token.begin(), token.end(), token.begin(), ::toupper); + level = parse_level(token); + do_level = false; + } else { + // its an appender name + if (app_map.find(token) == app_map.end()) { + // add an appender builder for this appender name + AppenderBuilder* builder = new AppenderBuilder(token); + app_map[token] = builder; + } + + vector *v; + map*>::const_iterator item = logger_app_map.find(name); + if (item == logger_app_map.end()) { + v = new vector (); + logger_app_map[name] = v; + } else { + v = item->second; + } + v->push_back(token); + + } + } + + if (level == -1) { + error = "Invalid log level for " + name + " logger"; + error_warn(); + return; + + } + + Logger* logger = new Logger(name, (LOG_LEVEL) level, proc_id); + logger_map[name] = logger; +} + +Log4CL* Log4CLConfigurator::configure(const string& config_file, int proc_id, boost::mpi::communicator* comm, int maxConfigFileSize) { + ConfigLexer lexer(config_file, comm, maxConfigFileSize); + this->proc_id = proc_id; + + TOKEN tok; + + while (((tok = lexer.next_token()) != END)) { + line = lexer.line(); + switch (tok) { + case ROOT: + create_root_logger(lexer.value()); + break; + case LOGGER: + create_logger(lexer.key(), lexer.value()); + break; + case APPENDER: + create_appender(lexer.key(), lexer.value()); + break; + case APPENDER_FILE: + create_appender_file(lexer.key(), lexer.value()); + break; + case APPENDER_SIZE: + create_appender_size(lexer.key(), lexer.value()); + break; + case APPENDER_BIDX: + create_appender_bidx(lexer.key(), lexer.value()); + break; + case ERRORT: + error = lexer.error(); + error_warn(); + break; + default: + error_warn(); + } + } + + return create_log4cl(); +} + +Log4CL* Log4CLConfigurator::create_log4cl() { + Log4CL* log4CL = new Log4CL(); + // create the appenders + map amap; + + for (map::const_iterator iter = app_map.begin(); iter != app_map.end(); ++iter) { + AppenderBuilder *builder = iter->second; + Appender *appender = builder->build(); + amap[iter->first] = appender; + log4CL->appenders.push_back(appender); + } + + for (map::const_iterator iter = logger_map.begin(); iter != logger_map.end(); ++iter) { + + Logger* logger = iter->second; + log4CL->logger_map[iter->first] = logger; + vector* app_list = logger_app_map[iter->first]; + + for (vector::const_iterator siter = app_list->begin(); siter != app_list->end(); ++siter) { + string app_name = *siter; + logger->add_appender(amap[app_name]); + } + } + + // make sure there is a root logger and if not make one. + if (logger_map.find("root") == logger_map.end()) { + Logger *root = new Logger("root", WARN, proc_id); + log4CL->logger_map["root"] = root; + + if (amap.find("stdout") == amap.end()) { + Appender* out = new CoutAppender(); + root->add_appender(out); + log4CL->appenders.push_back(out); + } else { + root->add_appender(amap["stdout"]); + } + } + + for (map::const_iterator iter = app_map.begin(); iter != app_map.end(); ++iter) { + AppenderBuilder *builder = iter->second; + delete builder; + } + app_map.clear(); + + for (map::const_iterator iter = logger_map.begin(); iter != logger_map.end(); ++iter) { + + vector* app_list = logger_app_map[iter->first]; + delete app_list; + } + logger_map.clear(); + + return log4CL; +} + +Logger& Log4CL::get_logger(std::string logger_name) { + map::const_iterator item = logger_map.find(logger_name); + if (item == logger_map.end()) { + cerr << "Unable to find logger '" << logger_name << "'. Returning root logger." << endl; + return *logger_map["root"]; + } + + return *(item->second); +} + +Log4CL::Log4CL() { +} + +Log4CL::~Log4CL() { + for (map::iterator iter = logger_map.begin(); iter != logger_map.end(); ++iter) { + Logger *logger = iter->second; + logger->close(); + delete logger; + } + + for (vector::iterator iter = appenders.begin(); iter != appenders.end(); ++iter) { + Appender *appender = *iter; + delete appender; + } + + delete _instance; +} + +Log4CL* Log4CL::_instance = 0; + +Log4CL* Log4CL::instance() { + return _instance; +} + +void Log4CL::configure(int proc_id, const std::string& config_file, boost::mpi::communicator* comm, int maxConfigFileSize) { + + try { + Log4CLConfigurator configurator; + _instance = configurator.configure(config_file, proc_id, comm, maxConfigFileSize); + + } catch (invalid_argument& ex) { + cerr << "ERROR opening logging config file " << ex.what() << endl; + delete _instance; + _instance = NULL; + return; + } +} + +void Log4CL::configure(int proc_id) { + _instance = new Log4CL(); + Logger *root = new Logger("root", WARN, proc_id); + _instance->logger_map["root"] = root; + + CoutAppender *out = new CoutAppender(); + root->add_appender(out); + _instance->appenders.push_back(out); + +} + +void Log4CL::close() { + for (map::iterator iter = logger_map.begin(); iter != logger_map.end(); ++iter) { + Logger *logger = iter->second; + logger->close(); + } +} + +} + +/* + int main(int argc, char **argv) { + + MPI::Init(argc, argv); + int proc_id = MPI::COMM_WORLD.Get_rank(); + + Log4CL::configure(proc_id, "../config.props"); + Logger& logger = Log4CL::instance()->get_logger("root"); + logger.log(ERROR, "root msg\n"); + + Logger& logger2 = Log4CL::instance()->get_logger("debug.log"); + logger2.log(ERROR, "logger msg\n"); + + Log4CL::instance()->close(); + + MPI::Finalize(); + } + */ diff --git a/libs/repast_hpc/logger.h b/libs/repast_hpc/logger.h new file mode 100644 index 0000000..14c01fd --- /dev/null +++ b/libs/repast_hpc/logger.h @@ -0,0 +1,162 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * logger.h + * + * Created on: + * Author: nick + */ + +#ifndef LOGGER_H_ +#define LOGGER_H_ + +#include +#include +#include + +#define MAX_CONFIG_FILE_SIZE 16384 + +namespace repast { + +typedef enum _LogLevel {DEBUG, INFO, WARN, ERROR, FATAL} LOG_LEVEL; + +class Appender { + +public: + Appender(const std::string name); + virtual void write(const std::string& line) = 0; + virtual void close() {} + + const std::string& name() const { + return _name; + } + + virtual ~Appender() = 0; + +protected: + const std::string _name; +}; + +class Logger { + +public: + Logger(const std::string, LOG_LEVEL, int proc_id); + + void log(LOG_LEVEL, const std::string msg); + void close(); + void add_appender(Appender *appender); + +private: + const std::string name; + const LOG_LEVEL level; + int proc_id; + std::vector appenders; + + void format_msg(LOG_LEVEL level, const std::string& msg, std::string& to_format); +}; + +class AppenderBuilder { + +public: + AppenderBuilder(const std::string name); + + std::string name; + std::string file_name; + long max_size; + int max_idx; + + Appender* build(); +}; + +class Log4CL; + +class Log4CLConfigurator { + +private: + void error_warn(); + + std::string error; + int line, proc_id; + + std::map app_map; + std::map logger_map; + // key: logger name, value: vector of appenders names for + // that logger + std::map*> logger_app_map; + + int parse_level(const std::string& str) const; + + void create_root_logger(const std::string& value); + void create_logger(const std::string& key, const std::string& value); + void create_named_logger(const std::string& name, const std::string& value); + + void create_appender(const std::string& key, const std::string& value); + void create_appender_file(const std::string& key, const std::string& value); + void create_appender_size(const std::string& key, const std::string& value); + void create_appender_bidx(const std::string& key, const std::string& value); + + Log4CL* create_log4cl(); + + AppenderBuilder* get_appender_builder(const std::string& key); + +public: + Log4CLConfigurator(); + Log4CL* configure(const std::string& config_file, int proc_id, boost::mpi::communicator* comm = 0, int maxConfigFileSize = MAX_CONFIG_FILE_SIZE); +}; + +class Log4CL { + friend class Log4CLConfigurator; + +public: + ~Log4CL(); + static Log4CL* instance(); + static void configure(int, const std::string&, boost::mpi::communicator* comm = 0, int maxConfigFileSize = MAX_CONFIG_FILE_SIZE); + static void configure(int); + + Logger& get_logger(std::string logger_name); + void close(); + +protected: + Log4CL(); + +private: + static Log4CL *_instance; + + std::map logger_map; + std::vector appenders; +}; +} + + +#endif /* LOGGER_H_ */ diff --git a/libs/repast_hpc/matrix.h b/libs/repast_hpc/matrix.h new file mode 100644 index 0000000..9cb34c3 --- /dev/null +++ b/libs/repast_hpc/matrix.h @@ -0,0 +1,318 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * matrix.h + * + * Created on: + * Author: nick + */ + +#ifndef MATRIX_H_ +#define MATRIX_H_ + +#include +#include +#include + +#include "Point.h" +#include "RepastErrors.h" + +namespace repast { + +/** + * Base class for matrix implementations. + */ +template +class Matrix { + +protected: + + int* stride; + T defValue; + Point _size; + int dCount; + + int calcIndex(const Point& index); + void boundsCheck(const Point& index); + void create(); + +public: + + /** + * Creates a matrix of the specified size and with the specified default value. + * + * \param size the size of the matrix in each dimension + */ + explicit Matrix(const Point& size, const T& defaultValue = T()); + virtual ~Matrix(); + + /** + * Gets the value at the specified index. + */ + virtual T& get(const Point& index) = 0; + + /** + * Sets the value at the specified index. + */ + virtual void set(const T& value, const Point& index) = 0; + + T& operator[](const Point& index); + const T& operator[](const Point& index) const; + + /** + * Gets the default value of any unset matrix cell. + */ + const T& defaultValue() const { + return defValue; + } + + /** + * Gets the shape (i.e. the length of each dimensions) of the matrix. + */ + const Point shape() const { + return _size; + } +}; + +template +Matrix::Matrix(const Point& size, const T& defaultValue) : + defValue(defaultValue), _size(size), dCount(size.dimensionCount()) { + create(); +} + +template +void Matrix::create() { + int tmpStride = 1; + stride = new int[dCount]; + for (int i = dCount - 1; i >= 0; i--) { + stride[i] = tmpStride; + tmpStride *= _size.getCoordinate(i); + } +} + +template +Matrix::~Matrix() { + delete[] stride; +} + +template +void Matrix::boundsCheck(const Point& index) { + if (index.dimensionCount() != dCount) + throw Repast_Error_47 >(dCount, index.dimensionCount(), index); // Number of index dimensions != number of matrix dimensions + for (int i = 0; i < dCount; i++) { + if (index.getCoordinate(i) < 0 || index.getCoordinate(i) >= _size.getCoordinate(i)) + throw Repast_Error_48 >(i, index.getCoordinate(i), index, _size.getCoordinate(i)); // Matrix Bounds Check: index is out of range + } +} + +template +int Matrix::calcIndex(const Point& index) { + int vIndex = 0; + for (size_t i = 0; i < index.dimensionCount(); i++) { + vIndex = vIndex + index[i] * stride[i]; + } + return vIndex; +} + +template +T& Matrix::operator[](const Point& index) { + return get(index); +} + +template +const T& Matrix::operator[](const Point& index) const { + return get(index); +} + +/** + * A dense matrix implementation that stores each cell individually. + */ +template +class DenseMatrix: public Matrix { + +private: + std::vector values; + +public: + /** + * Creates a DenseMatrix as a copy of the specified DenseMatrix. + */ + DenseMatrix(const DenseMatrix&); + DenseMatrix& operator=(const DenseMatrix&); + + /** + * Creates a DenseMatrix of the specified shape and default value. + */ + explicit DenseMatrix(const Point& shape, const T& defValue = T()); + + ~DenseMatrix() { + } + + /** + * Gets the value at the specified index. + */ + T& get(const Point& index); + + /** + * Sets the value at the specified index. + */ + void set(const T& value, const Point& index); + +}; + +template +DenseMatrix::DenseMatrix(const DenseMatrix& other) : + Matrix (other._size, other.defaultValue()), values(other.values) { + +} + +template +DenseMatrix& DenseMatrix::operator=(const DenseMatrix& rhs) { + if (&rhs != this) { + delete[] Matrix::stride; + Matrix::_size = rhs._size; + Matrix::dCount = rhs.dCount; + Matrix::defValue = rhs.defaultValue(); + Matrix::create(); + + values = std::vector(rhs.values.begin(), rhs.values.end()); + } + return *this; +} + +template +DenseMatrix::DenseMatrix(const Point& size, const T& defValue) : + Matrix (size) { + int _size = 1; + for (int i = 0; i < Matrix::dCount; i++) { + _size *= size.getCoordinate(i); + } + values = std::vector(_size, defValue); +} + +template +T& DenseMatrix::get(const Point& index) { + Matrix::boundsCheck(index); + int vIndex = Matrix::calcIndex(index); + return values[vIndex]; +} + +template +void DenseMatrix::set(const T& value, const Point& index) { + Matrix::boundsCheck(index); + int vIndex = Matrix::calcIndex(index); + values[vIndex] = value; +} + +/** + * A sparse matrix implementation that stores values in a map. This should be used + * when the majority of the matrix cells contain the default value. + */ +template +class SparseMatrix: public Matrix { + +private: + std::map map; + + typedef typename std::map::iterator map_iter; + +public: + SparseMatrix(const SparseMatrix&); + SparseMatrix& operator=(const SparseMatrix&); + + /** + * Creates a DenseMatrix of the specified shape and default value. + */ + explicit SparseMatrix(const Point& size, const T& defValue = T()); + ~SparseMatrix() { + } + + /** + * Gets the value at the specified index. + */ + T& get(const Point& index); + + /** + * Sets the value at the specified index. + */ + void set(const T& value, const Point& index); + +}; + +template +SparseMatrix::SparseMatrix(const SparseMatrix& other) : + Matrix (other._size, other.defaultValue()), map(other.map) { +} + +template +SparseMatrix& SparseMatrix::operator=(const SparseMatrix& rhs) { + if (&rhs != this) { + delete[] Matrix::stride; + Matrix::_size = rhs._size; + Matrix::dCount = rhs.dCount; + Matrix::defValue = rhs.defaultValue(); + Matrix::create(); + + map = std::map(rhs.map.begin(), rhs.map.end()); + } + return *this; +} + +template +SparseMatrix::SparseMatrix(const Point& size, const T& defValue) : + Matrix (size, defValue) { +} + +template +T& SparseMatrix::get(const Point& index) { + Matrix::boundsCheck(index); + int vIndex = Matrix::calcIndex(index); + // need to insert do an insert and return reference to + // result so that assignment via [] uses a reference in + // the map. insert inserts the entry if it doesn't exist + // and returns the entry or the existing entry + std::pair res = map.insert(std::make_pair(vIndex, Matrix::defValue)); + return res.first->second; +} + +template +void SparseMatrix::set(const T& value, const Point& index) { + Matrix::boundsCheck(index); + int vIndex = Matrix::calcIndex(index); + map[vIndex] = value; +} + +} + +#endif /* MATRIX_H_ */ diff --git a/libs/repast_hpc/module.mk b/libs/repast_hpc/module.mk new file mode 100644 index 0000000..41cf4b7 --- /dev/null +++ b/libs/repast_hpc/module.mk @@ -0,0 +1,37 @@ +SOURCES = AgentId.cpp \ +AgentImporterExporter.cpp \ +AgentRequest.cpp \ +AgentStatus.cpp \ +CartesianTopology.cpp \ +Graph.cpp \ +GridComponents.cpp \ +GridDimensions.cpp \ +initialize_random.cpp \ +io.cpp \ +logger.cpp \ +NCDataSet.cpp \ +NCDataSetBuilder.cpp \ +NetworkBuilder.cpp \ +Properties.cpp \ +Random.cpp \ +RelativeLocation.cpp \ +RepastErrors.cpp \ +RepastProcess.cpp \ +Schedule.cpp \ +SharedBaseGrid.cpp \ +SharedContext.cpp \ +spatial_math.cpp \ +SRManager.cpp \ +SVDataSet.cpp \ +SVDataSetBuilder.cpp \ +Utilities.cpp \ +ValueLayer.cpp \ +ValueLayerND.cpp \ +Variable.cpp \ +ValueLayer.cpp \ +Variable.cpp + +local_dir := repast_hpc +local_src := $(addprefix $(local_dir)/, $(SOURCES)) + +repast_hpc_src += $(local_src) diff --git a/libs/repast_hpc/mpi_constants.h b/libs/repast_hpc/mpi_constants.h new file mode 100644 index 0000000..89d0ff1 --- /dev/null +++ b/libs/repast_hpc/mpi_constants.h @@ -0,0 +1,64 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * mpi_constants.h + * + * Created on: Aug 19, 2010 + * Author: nick + */ + +#ifndef MPI_CONSTANTS_H_ +#define MPI_CONSTANTS_H_ + +namespace repast { + +const int AGENT_REQUEST_TAG = 1000; +const int AGENT_TO_SEND_COUNT = 1001; +const int AGENT_TO_SEND_REQUEST = 1002; +const int AGENT_TAG = 1003; +const int AGENT_SYNC_STATE_TAG = 1004; +const int AGENT_SYNC_STATUS_COUNT = 1005; +const int AGENT_SYNC_STATUS = 1006; + +const int EXPORTER_UPDATE_SENDERS = 1007; +const int EXPORTER_UPDATE_IMPORTERS = 1008; + +const int AGENT_MOVED_SENDERS = 1009; +const int AGENT_MOVED_AGENT = 1010; + + +} + + +#endif /* MPI_CONSTANTS_H_ */ diff --git a/libs/repast_hpc/spatial_math.cpp b/libs/repast_hpc/spatial_math.cpp new file mode 100644 index 0000000..206bf2a --- /dev/null +++ b/libs/repast_hpc/spatial_math.cpp @@ -0,0 +1,86 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * spatial_math.cpp + * + * Created on: Aug 10, 2010 + * Author: nick + */ + +#include "spatial_math.h" + +#include +#include + +#ifdef _MSC_VER +// Round a double +// Note that behavior may not be as expected at max and min double values +double rint(double x){ + return ( x < 0 ? + ceil(x - 0.5) : // base value is negative + floor(x + 0.5)); // base value is positive + +} +#endif + +namespace repast { + +void _rotate(double* plane, double angle) { + double x = plane[0]; + double y = plane[1]; + plane[0] = x * cos(angle) - y * sin(angle); + plane[1] = y * cos(angle) + x * sin(angle); +} + +template<> +std::vector calculateDisplacement(int dimCount, int unitDimension, double scale, const std::vector& anglesInRadians) { + Point res = calculateDisplacement( dimCount, unitDimension, scale, anglesInRadians); + std::vector displacement(dimCount, 0); + std::vector coords = res.coords(); + for (size_t i = 0; i < coords.size(); ++i) { + displacement[i] = rint(coords[i]); + } + return displacement; +} + +double toRadians(double angdeg) { + return angdeg / 180 * PI; +} + +double toDegrees(double angrad) { + return angrad * 180.0 / PI; +} + + +} diff --git a/libs/repast_hpc/spatial_math.h b/libs/repast_hpc/spatial_math.h new file mode 100644 index 0000000..4abcfe8 --- /dev/null +++ b/libs/repast_hpc/spatial_math.h @@ -0,0 +1,108 @@ +/* + * Repast for High Performance Computing (Repast HPC) + * + * Copyright (c) 2010 Argonne National Laboratory + * All rights reserved. + * + * Redistribution and use in source and binary forms, with + * or without modification, are permitted provided that the following + * conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the Argonne National Laboratory nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * spatial_math.h + * + * Created on: Aug 10, 2010 + * Author: nick + */ + +#ifndef SPATIAL_MATH_H_ +#define SPATIAL_MATH_H_ + +#include +#include "Point.h" + +namespace repast { + +const double PI = 3.141592; + +void _rotate(double* plane, double angle); + +template +std::vector calculateDisplacement(int dimCount, int unitDimension, double scale, + const std::vector& anglesInRadians) { + + std::vector displacement(dimCount, 0); + displacement[unitDimension] = 1; + double tmp[] = { 0, 0 }; + int c = 0; + for (int i = 0; i < dimCount; i++) { + if (i == unitDimension) { + continue; + } else if (i > unitDimension) { + tmp[0] = displacement[unitDimension]; + tmp[1] = displacement[i]; + _rotate(tmp, anglesInRadians[c]); + displacement[unitDimension] = tmp[0]; + displacement[i] = tmp[1]; + } else if (i < unitDimension) { + tmp[0] = displacement[i]; + tmp[1] = displacement[unitDimension]; + _rotate(tmp, anglesInRadians[c]); + displacement[unitDimension] = tmp[1]; + displacement[i] = tmp[0]; + } + c++; + } + for (size_t i = 0; i < displacement.size(); ++i) { + displacement[i] = displacement[i] * scale; + } + return displacement; +} + +template<> +std::vector calculateDisplacement (int dimCount, int unitDimension, double scale,const std::vector& anglesInRadians); + +/** + * Converts degrees to radians. + * + * @param angdeg the angle in degrees + * + * @return the angle in radians. + */ +double toRadians(double angdeg); + +/** + * Converts radians to degrees. + * + * @param angrad the angle in radians + * + * @return the angle in degrees. + */ +double toDegrees(double angrad); + +} + +#endif /* SPATIAL_MATH_H_ */ diff --git a/libs/repasthpc b/libs/repasthpc deleted file mode 160000 index 6ab51ec..0000000 --- a/libs/repasthpc +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6ab51ec8ce0f3be67164849c9520c860d14abfd1 -- GitLab From 48d09b1315dcf0477c6964906326e2182cc49a87 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 20 Aug 2021 15:37:27 +0200 Subject: [PATCH 08/12] move invocation of node_init after node config is saved --- src/villas_interface/villas_interface.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/villas_interface/villas_interface.cpp b/src/villas_interface/villas_interface.cpp index 7863448..e47c816 100644 --- a/src/villas_interface/villas_interface.cpp +++ b/src/villas_interface/villas_interface.cpp @@ -114,14 +114,6 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob //parse configuration of node IO->log_info("Villas_interface: node configuration"); - ret = node_init(n, type); - if (ret) { - //IO->log_info("Villas_interface: node_init failed for node " + _name + - //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); - throw std::runtime_error("Villas_interface: node_init failed for node " + _name + - " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); - } - n->name = strdup(_name.c_str()); n->in.enabled = 1; n->out.enabled = 1; @@ -203,6 +195,15 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob + std::string(type->name) + " not supported."); } + // init VILLASnode based on config from above + ret = node_init(n, type); + if (ret) { + //IO->log_info("Villas_interface: node_init failed for node " + _name + + //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + throw std::runtime_error("Villas_interface: node_init failed for node " + _name + + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + } + // generate list of signals for villas node based on meta information of message type // configures behavior-specific input and output signals // this does the job of "node_direction_parse(...)" -- GitLab From 986b05a75aa2bb1d5cb1f07100ce684798630ff4 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 24 Aug 2021 12:11:56 +0200 Subject: [PATCH 09/12] throw exception if node_type_lookup fails to stop execution; throw exception if instantiation of villas_interface is tried but DistAIX is compiled without libvillas --- src/villas_interface/villas_interface.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/villas_interface/villas_interface.cpp b/src/villas_interface/villas_interface.cpp index e47c816..54a7d98 100644 --- a/src/villas_interface/villas_interface.cpp +++ b/src/villas_interface/villas_interface.cpp @@ -79,7 +79,8 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob IO->log_info("Villas_interface: Node type lookup for node " + _name + " Type: " + _config->type_name + "."); type = node_type_lookup(_config->type_name); if(type== nullptr){ - IO->log_info("\"Villas_interface: ERROR: something went wrong in node type lookup"); + IO->log_info("Villas_interface: ERROR: something went wrong in node type lookup"); + throw std::runtime_error("Villas_interface: node_type_lookup failed for type " + _config->type_name); } if(with_node) { @@ -264,13 +265,14 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob } #else - //Warn the user + //Warn the user and throw exception IO->log_info("ERROR: Your agent behavior is trying to instantiate a Villas_interface, but DistAIX is compiled without VILLASnode support.\n" "This will very likely cause a simulation failure!\n" "Pass option -villas to build_and_install.sh script to compile DistAIX with VILLASnode support"); std::cerr << "ERROR: Your agent behavior is trying to instantiate a Villas_interface, but DistAIX is compiled without VILLASnode support." << std::endl; std::cerr << "This will very likely cause a simulation failure!" << std::endl; std::cerr << "Pass option -villas to build_and_install.sh script to compile DistAIX with VILLASnode support" << std::endl; + throw std::runtime_error("Villas_interface: Trying to create instance of VILLAS_interface, but DistAIX is compiled without libvillas. Check log files for more infos. I'm stopping here."); #endif } -- GitLab From 599e481b683c3cd107b6e8c97912b3a28f32bbd2 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 24 Aug 2021 14:14:21 +0200 Subject: [PATCH 10/12] node startup working for mqtt ping pong --- src/villas_interface/villas_interface.cpp | 39 +++++++++++++---------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/villas_interface/villas_interface.cpp b/src/villas_interface/villas_interface.cpp index 54a7d98..fe5f83a 100644 --- a/src/villas_interface/villas_interface.cpp +++ b/src/villas_interface/villas_interface.cpp @@ -87,12 +87,12 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob int ret; //for return values of villas functions //set several states of node internal objects to STATE_DESTROYED - //n = (struct vnode*) malloc(sizeof(struct vnode)); - //n->state = State::DESTROYED; - //n->in.state = State::DESTROYED; - //n->out.state = State::DESTROYED; - //n->in.signals.state = State::DESTROYED; - //n->out.signals.state = State::DESTROYED; + n = (struct vnode*) malloc(sizeof(struct vnode)); + n->state = State::DESTROYED; + n->in.state = State::DESTROYED; + n->out.state = State::DESTROYED; + n->in.signals.state = State::DESTROYED; + n->out.signals.state = State::DESTROYED; //Init memory pool for this node (used to get memory for sample in send_message) p = (struct pool*) malloc(sizeof(struct pool)); @@ -112,8 +112,16 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob } + // init VILLASnode based on config from above + IO->log_info("Calling node_init"); + ret = node_init(n, type); + if (ret) { + throw std::runtime_error("Villas_interface: node_init failed for node " + _name + + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); + } + //parse configuration of node - IO->log_info("Villas_interface: node configuration"); + IO->log_info("Villas_interface: parsing provided node configuration"); n->name = strdup(_name.c_str()); n->in.enabled = 1; @@ -123,11 +131,12 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob if(std::string(type->name) == "mqtt") { //configure the node for mqtt - + IO->log_info("parsing config for mqtt"); //set mqtt parameters auto *m = (mqtt *) n->_vd; //set broker + IO->log_info("Setting the broker"); m->host = strdup(_config->type_config.mqtt_conf->broker.c_str()) ; //set publish topic (if any) @@ -154,7 +163,9 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob m->pool.state = State::DESTROYED; //lookup the format type for this mqtt node + IO->log_info("lookup the format type of this node"); m->formatter = villas::node::FormatFactory::make(_config->format_name); + IO->log_info("parsing completed for mqtt"); } else if (std::string(type->name) == "nanomsg") { //configure_node_nanomsg(); //configure the node for nanomsg @@ -190,24 +201,18 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob } } m->formatter = villas::node::FormatFactory::make(_config->format_name); + IO->log_info("parsing completed for nanomsg"); } else { //IO->log_info("Villas_interface: Error: node type " + std::string(type->name) + " not supported by DistAIX."); throw std::runtime_error("Villas_interface: Error in node " + _name + ": node type " + std::string(type->name) + " not supported."); } - // init VILLASnode based on config from above - ret = node_init(n, type); - if (ret) { - //IO->log_info("Villas_interface: node_init failed for node " + _name + - //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); - throw std::runtime_error("Villas_interface: node_init failed for node " + _name + - " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); - } // generate list of signals for villas node based on meta information of message type // configures behavior-specific input and output signals // this does the job of "node_direction_parse(...)" + IO->log_info("configuring signals based on meta infos"); for(auto & i : meta){ SignalType sig_type; @@ -244,6 +249,7 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob n->out.state = State::PARSED; //check node + IO->log_info("Calling node_check"); ret = node_check(n); if (ret) { //IO->log_info("Villas_interface: node_check failed for node " + _name + @@ -253,6 +259,7 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob } //prepare node + IO->log_info("Calling node_prepare"); ret = node_prepare(n); if (ret) { //IO->log_info("Villas_interface: node_prepare failed for node " + _name + -- GitLab From e253f0bde6b98339391a8f8c6a2f31256989ec38 Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Tue, 24 Aug 2021 14:14:29 +0200 Subject: [PATCH 11/12] code cleanup, removing comments and additional debug log messages --- src/villas_interface/villas_interface.cpp | 32 +++++++---------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/villas_interface/villas_interface.cpp b/src/villas_interface/villas_interface.cpp index fe5f83a..3fefc26 100644 --- a/src/villas_interface/villas_interface.cpp +++ b/src/villas_interface/villas_interface.cpp @@ -105,38 +105,35 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob IO->log_info("Villas_interface: Memory pool allocation for " + std::to_string(meta.size())+ " signals and blocksize=" + std::to_string(blocksize)); ret = pool_init(p, 1024, blocksize, &(memory_heap)); if (ret) { - //IO->log_info("Villas_interface: pool_init failed for node " + _name + - //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); throw std::runtime_error("Villas_interface: pool_init failed for node " + _name + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); } // init VILLASnode based on config from above - IO->log_info("Calling node_init"); + IO->log_info("Villas_interface: node_init"); ret = node_init(n, type); if (ret) { throw std::runtime_error("Villas_interface: node_init failed for node " + _name + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); - } + } //parse configuration of node - IO->log_info("Villas_interface: parsing provided node configuration"); + IO->log_info("Villas_interface: parsing node configuration"); n->name = strdup(_name.c_str()); n->in.enabled = 1; n->out.enabled = 1; n->out.vectorize = 1; //only one sample at a time n->in.vectorize = 1; - + IO->log_info("Villas_interface: parsing config for " + std::string(type->name)); if(std::string(type->name) == "mqtt") { //configure the node for mqtt - IO->log_info("parsing config for mqtt"); + //set mqtt parameters auto *m = (mqtt *) n->_vd; //set broker - IO->log_info("Setting the broker"); m->host = strdup(_config->type_config.mqtt_conf->broker.c_str()) ; //set publish topic (if any) @@ -163,27 +160,21 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob m->pool.state = State::DESTROYED; //lookup the format type for this mqtt node - IO->log_info("lookup the format type of this node"); m->formatter = villas::node::FormatFactory::make(_config->format_name); - IO->log_info("parsing completed for mqtt"); } else if (std::string(type->name) == "nanomsg") { - //configure_node_nanomsg(); //configure the node for nanomsg + //configure the node for nanomsg // set nanomsg parameters auto *m = (nanomsg *) n->_vd; ret = vlist_init(&m->out.endpoints); if (ret) { - //IO->log_info("Villas_interface: vlist_init of out.endpoints failed for node " + _name + - // " with return value " + std::to_string(ret)); throw std::runtime_error("Villas_interface: vlist_init of out.endpoints failed for node " + _name + " with return value " + std::to_string(ret)); } ret = vlist_init(&m->in.endpoints); if (ret) { - //IO->log_info("Villas_interface: vlist_init of in.endpoints failed for node " + _name + - //" with return value " + std::to_string(ret)); throw std::runtime_error("Villas_interface: vlist_init of in.endpoints failed for node " + _name + " with return value " + std::to_string(ret)); } @@ -201,7 +192,6 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob } } m->formatter = villas::node::FormatFactory::make(_config->format_name); - IO->log_info("parsing completed for nanomsg"); } else { //IO->log_info("Villas_interface: Error: node type " + std::string(type->name) + " not supported by DistAIX."); throw std::runtime_error("Villas_interface: Error in node " + _name + ": node type " @@ -212,7 +202,7 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob // generate list of signals for villas node based on meta information of message type // configures behavior-specific input and output signals // this does the job of "node_direction_parse(...)" - IO->log_info("configuring signals based on meta infos"); + IO->log_info("Villas_interface: configure signals"); for(auto & i : meta){ SignalType sig_type; @@ -249,21 +239,17 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob n->out.state = State::PARSED; //check node - IO->log_info("Calling node_check"); + IO->log_info("Villas_interface: node_check"); ret = node_check(n); if (ret) { - //IO->log_info("Villas_interface: node_check failed for node " + _name + - //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); throw std::runtime_error("Villas_interface: node_check failed for node " + _name + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); } //prepare node - IO->log_info("Calling node_prepare"); + IO->log_info("Villas_interface: node_prepare"); ret = node_prepare(n); if (ret) { - //IO->log_info("Villas_interface: node_prepare failed for node " + _name + - //" and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); throw std::runtime_error("Villas_interface: node_prepare failed for node " + _name + " and node type " + std::string(type->name) + " with return value " + std::to_string(ret)); } -- GitLab From e1ccd33f3cb4877129c3668117d9ec3404e3d77e Mon Sep 17 00:00:00 2001 From: Sonja Happ Date: Fri, 7 Jan 2022 15:23:31 +0100 Subject: [PATCH 12/12] Update to latest version of VILLASnode, include MQTT SSL params --- Dockerfile | 6 +++--- include/villas_interface/villas_interface.h | 14 ++++++++++-- libs/villasnode | 2 +- props/model.props | 11 +++++++++- src/model/model_config.cpp | 24 +++++++++++++++++---- src/villas_interface/villas_interface.cpp | 9 ++++++++ 6 files changed, 55 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 7786722..4ff33eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -120,7 +120,7 @@ RUN cd /tmp && mkdir nanomsg && cd nanomsg && \ RUN cd /tmp && \ git clone --recursive https://github.com/fmtlib/fmt.git && \ mkdir -p fmt/build && cd fmt/build && \ - git checkout 5.2.0 && \ + git checkout 5.3.0 && \ cmake -DBUILD_SHARED_LIBS=1 .. && make -j$(nproc) install && \ rm -rf /tmp/* @@ -128,8 +128,8 @@ RUN cd /tmp && \ RUN cd /tmp && \ git clone --recursive https://github.com/gabime/spdlog.git && \ mkdir -p spdlog/build && cd spdlog/build && \ - git checkout v1.3.1 && \ - cmake -DCMAKE_BUILD_TYPE=Release -DSPDLOG_FMT_EXTERNAL=1 -DSPDLOG_BUILD_BENCH=OFF .. && make -j$(nproc) install && \ + git checkout v1.6.0 && \ + cmake -DSPDLOG_BUILD_SHARED=ON -DCMAKE_BUILD_TYPE=Release -DSPDLOG_FMT_EXTERNAL=1 -DSPDLOG_BUILD_BENCH=OFF .. && make -j$(nproc) install && \ rm -rf /tmp/* # set library environment variables diff --git a/include/villas_interface/villas_interface.h b/include/villas_interface/villas_interface.h index ca91c7c..2f2d70e 100644 --- a/include/villas_interface/villas_interface.h +++ b/include/villas_interface/villas_interface.h @@ -54,12 +54,22 @@ static inline int my_log2i(long long x) { struct mqtt_data{ std::string broker; int port; - bool retain; + int retain; int keepalive; - bool ssl_enabled; std::string publish; std::string subscribe; int qos; + + int ssl_enabled; + int ssl_insecure; + int ssl_cert_reqs; + std::string ssl_tls_version; + std::string ssl_cafile; + std::string ssl_capath; + std::string ssl_certfile; + std::string ssl_ciphers; + std::string ssl_keyfile; + }; /*! \brief Struct that holds general info about the villas node type nanomsg */ diff --git a/libs/villasnode b/libs/villasnode index 51e1952..e0242dc 160000 --- a/libs/villasnode +++ b/libs/villasnode @@ -1 +1 @@ -Subproject commit 51e1952a0aaa2ee2bc44f7739ba1be0beb172c44 +Subproject commit e0242dc544a3138e56cb550e5c9b9d12ae979593 diff --git a/props/model.props b/props/model.props index 2e5151e..4b1758d 100644 --- a/props/model.props +++ b/props/model.props @@ -176,8 +176,17 @@ villas.mqtt.broker = localhost villas.mqtt.port = 1883 villas.mqtt.qos = 0 villas.mqtt.retain = 0 -villas.mqtt.ssl_enabled = 0 villas.mqtt.keepalive = 5 +villas.mqtt.ssl_enabled = 0 +villas.mqtt.ssl_insecure = 1 +# SSL Cert. requirements: SSL_VERIFY_NONE(0) or SSL_VERIFY_PEER(1) +villas.mqtt.ssl_cert_reqs = 0 +villas.mqtt.ssl_cafile = cafile +villas.mqtt.ssl_capath = capath +villas.mqtt.ssl_certfile = certfile +villas.mqtt.ssl_keyfile = keyfile +villas.mqtt.ssl_ciphers = ciphers +villas.mqtt.ssl_tls_version = 1.3 # set a default topic to which all agents subscribe/ publish # per default if nothing else is specified in the agent's behavior diff --git a/src/model/model_config.cpp b/src/model/model_config.cpp index a9bd283..3f696c5 100644 --- a/src/model/model_config.cpp +++ b/src/model/model_config.cpp @@ -168,17 +168,33 @@ void Model::read_villas_config() { props->getProperty("villas.mqtt.port")); villas_config_agents->type_config.mqtt_conf->qos = repast::strToInt( props->getProperty("villas.mqtt.qos")); - villas_config_agents->type_config.mqtt_conf->retain = (bool) repast::strToInt( + villas_config_agents->type_config.mqtt_conf->retain = repast::strToInt( props->getProperty("villas.mqtt.retain")); - villas_config_agents->type_config.mqtt_conf->ssl_enabled = (bool) repast::strToInt( - props->getProperty("villas.mqtt.ssl_enabled")); villas_config_agents->type_config.mqtt_conf->keepalive = repast::strToInt( props->getProperty("villas.mqtt.keepalive")); villas_config_agents->type_config.mqtt_conf->subscribe = props->getProperty( "villas.mqtt.subscribe"); villas_config_agents->type_config.mqtt_conf->publish = props->getProperty( "villas.mqtt.publish"); - + // MQTT ssl params + villas_config_agents->type_config.mqtt_conf->ssl_enabled = repast::strToInt( + props->getProperty("villas.mqtt.ssl_enabled")); + villas_config_agents->type_config.mqtt_conf->ssl_insecure = repast::strToInt( + props->getProperty("villas.mqtt.ssl_insecure")); + villas_config_agents->type_config.mqtt_conf->ssl_cert_reqs = repast::strToInt( + props->getProperty("villas.mqtt.ssl_cert_reqs")); + villas_config_agents->type_config.mqtt_conf->ssl_cafile = props->getProperty( + "villas.mqtt.ssl_cafile"); + villas_config_agents->type_config.mqtt_conf->ssl_capath = props->getProperty( + "villas.mqtt.ssl_capath"); + villas_config_agents->type_config.mqtt_conf->ssl_certfile = props->getProperty( + "villas.mqtt.ssl_certfile"); + villas_config_agents->type_config.mqtt_conf->ssl_keyfile = props->getProperty( + "villas.mqtt.ssl_keyfile"); + villas_config_agents->type_config.mqtt_conf->ssl_ciphers = props->getProperty( + "villas.mqtt.ssl_ciphers"); + villas_config_agents->type_config.mqtt_conf->ssl_tls_version = props->getProperty( + "villas.mqtt.ssl_tls_version"); } else if (villas_config_model->type_name == "nanomsg") { diff --git a/src/villas_interface/villas_interface.cpp b/src/villas_interface/villas_interface.cpp index 3fefc26..0399b44 100644 --- a/src/villas_interface/villas_interface.cpp +++ b/src/villas_interface/villas_interface.cpp @@ -155,7 +155,16 @@ Villas_interface::Villas_interface(villas_node_config *_config, IO_object *IO_ob m->retain = _config->type_config.mqtt_conf->retain; m->keepalive = _config->type_config.mqtt_conf->keepalive; m->qos = _config->type_config.mqtt_conf->qos; + //SSL config params m->ssl.enabled = _config->type_config.mqtt_conf->ssl_enabled; + m->ssl.insecure = _config->type_config.mqtt_conf->ssl_insecure; + m->ssl.cert_reqs = _config->type_config.mqtt_conf->ssl_cert_reqs; + m->ssl.tls_version = strdup(_config->type_config.mqtt_conf->ssl_tls_version.c_str()); + m->ssl.cafile = strdup(_config->type_config.mqtt_conf->ssl_cafile.c_str()); + m->ssl.capath = strdup(_config->type_config.mqtt_conf->ssl_capath.c_str()); + m->ssl.certfile = strdup(_config->type_config.mqtt_conf->ssl_certfile.c_str()); + m->ssl.ciphers = strdup(_config->type_config.mqtt_conf->ssl_ciphers.c_str()); + m->ssl.keyfile = strdup(_config->type_config.mqtt_conf->ssl_keyfile.c_str()); m->queue.queue.state = State::DESTROYED; m->pool.state = State::DESTROYED; -- GitLab