Create Agents#

Agents is a set of autonomous entities moving around and interacting with the environment during the simulation. Unlike Places, it is not a grid, but a big array of instances where some of them are “alive” while the others are “dead” (i.e.: still present in memory but will not participate in simulation). It is optional for a simulation and should always be associated with a Places instance. Each individual entity, called an Agent instance, should always reside on a Place instance.

Define Custom Agent Class#

Similar to Places, before instantiating an instance of Agents, we need to first define the behavior of each Agent instance in the simulation by defining a class inheriting from mass::Agent, which requires implementing the following methods:

  • The constructor. It always takes in a mass::Index representing the flattened local index of this instance in the block, which should be passed to the base class constructor. Usually the body of the constructor should be empty since all the attributes are not created at this time. It should be decorated with __universal__.

  • The callMethod. This method is called when the callAll method is invoked to the Places instance. It is responsible for dispatching method invocation requests to the actual member method based on the given mass::FunctionId and the arguments as void*. Typical implementation of this method is using a big switch statement on the FunctionId. It should be decorated with __device__.

For better identifying the custom methods and dispatch invocation requests in callMethod, it is recommended to use an enum as their identifier (the FunctionId).

#include <mass/Agent.h>

class MyAgent final : public mass::Agent {
public:
    __universal__ explicit MyAgent(const mass::Index localIndex) : mass::Agent(localIndex) {}

    enum Function {
        FUNC1, FUNC2
    };

    __device__ void callMethod(const mass::FunctionId functionId, void*const args) {
        switch (functionId) {
        case FUNC1: func1(); break;
        case FUNC2: func2(*static_cast<const int*>(args)); break;
        default: break;
        }
    }

    __device__ void func1() {}
    __device__ void func2(const int arg) {}
};

Instantiate Agents Instance#

With the custom Agent class defined, we can create an instance of Agents using the defined MyAgent class. This is done using the mass::makeAgents function, which takes in the integer handle of the created Agents instance and a configuration instance of type mass::AgentsConfig. The integer handle can be any value as long as it is unique among all Agents instances in the same simulation. As for the AgentsConfig structure, it contains the following options.

Note that since an Agents should always be associated with a Places instance, we should always create the Places instance first before creating an Agents.

AgentsConfig::associatedPlacesHandle#

The integer handle of the Places instance that this Agents instance will be associated with. This is a required option since an Agents should always be associated with a Places instance. Typical ways to configure this option is first using the getHandle method on the Places instance the created Agents will be associated to, and then pass it to AgentsConfig::associatedPlacesHandle.

AgentsConfig::initialAgentsConfig#

The configuration for the initial set of alive Agents instances. This can be either a single integer or a span of mass::Index.

  • If a single integer $N$ is provided, it means that there will initially be $N$ alive Agent instances activated, which will be assigned to the Place instances in a round-robin fashion. In other words, the first Agent instance is assigned to the Place instance with flatten index 0, the second Agent instance is assigned to the Place instance with flatten index 1, and so on, wrapping around when the last Place instance is reached.

  • If a span of mass::Index $S$ is provided, it means that there will initially be $|S|$ alive Agent instances activated where the $i$-th Agent instance will be assigned to the Place instance with flatten index $S[i]$. In other words, the first agent instance assigned to the Place instance with index $S[0]$, the second agent instance assigned to the Place instance with index $S[1]$, and so on.

AgentsConfig::globalAgentSlotCount#

An integer value specifying the total number of slots for Agent instances. In other words, the maximum number of alive Agent instances that can be activated during the simulation. It should be larger than the number of initial alive agents configured in AgentsConfig::initialAgentsConfig