Invoking Methods#
For both Places and Agents, we can invoke a member method on all valid instances using the callAll function. The requested method will be executed on all valid Place or Agent instances concurrently.
The callAll function, provided by both Places and Agents, requires two arguments:
functionId: The integer id identifying the member method to be invoked.argument: The argument value for invoking the member method, which will be dispatched to all the instances. This argument is optional, if the requested method takes no arguments, it can be omitted.
Note that the second argument of the callAll method can only be one of the two following cases:
A single non-pointer trivially copyable value, such as an integer, float, or classes containing only trivially copyable member variables. Typical counterexample includes
std::vector,std::stringetc.A container with contiguous storage with trivially copyable elements. To be precise, all containers that can be accepted by
std::data()andstd::size()methods are valid. Typical counterexample includesstd::map(it is a container, but not contiguous) andstd::vector<std::vector<int>>(elements are not trivially copyable).
Important
The std::pair and std::tuple types seems to be valid, but they are actually not trivially copyable in C++ (blame the C++ std committee for that). In this case, either use cuda::std::pair / cuda::std::tuple or define your own struct to hold the values.
The behavior of callAll for Places and Agents is very similar, but differs slightly due to different definitions for “valid” instances. Usually users are not required to pay attention to such details, but it is still good to know.
For
Places,callAllwill only be invoked on instances that are not within the Ghost Regions.For
Agents,callAllwill only be invoked on instances that are alive. In other word, if an instance is terminated, it will not be invoked bycallAll.
Consider an example of a custom 2D Place class named MyPlace, which contains a member method named myFunc that takes in an integer argument. To correctly invoke it through callAll, we need the following steps:
Assign an integer id for this method. This is typically done by defining an enum.
Route the method invocation requests from
callMethodtomyFuncIn the simulation loop, invoke
callAllwith the id and necessary arguments.
class MyPlace final : public mass::Place {
public:
__universal__ explicit MyPlace(const mass::Index localIndex): Place(localIndex) {}
enum Function {
MY_FUNC // The id assigned to `myFunc`
};
__device__ void callMethod(const FunctionId functionId, void*const arg) {
switch (functionId) {
case MY_FUNC:
myFunc(*reinterpret_cast<int*>(arg));
break;
default:
break;
}
}
__device__ void myFunc(const int arg) {
// implementation ...
}
}
// In simulation loop
places->callAll(
MyPlace::Function::MY_FUNC, // Id of the member method to call
100 // Argument to be passed to the member method
);
Invoking Methods on Places with Different Arguments for each Instance#
Sometimes a simulation may need to invoke a member method on each Place instance while providing different argument value for each of them. This is particularly useful for initializing the instances at the beginning. For this use case, we can use a separated function named callAllWithPerPlaceArg.
Similar to callAll, it also takes in the identifier of the member method and the argument to be passed to the member method. However, the second argument has to be a container with contiguous storage containing the different argument value for each Place instance. The size of this container must be equal to the total number of Place instances in the Places grid. Then before calling the requested member method, use the local index provided by the getLocalIndex method to retrieve the argument value correspond to the current instance.
class MyPlace final : public mass::Place {
public:
__universal__ explicit MyPlace(const mass::Index localIndex): Place(localIndex) {}
enum Function {
MY_FUNC // The id assigned to `myFunc`
};
__device__ void callMethod(const FunctionId functionId, void*const arg) {
switch (functionId) {
case MY_FUNC:
// retrieve the argument value for this instance using the local index.
myFunc(reinterpret_cast<int*>(arg)[getLocalIndex()]);
break;
default:
break;
}
}
__device__ void myFunc(const int arg) {
// implementation ...
}
}
// In simulation loop
const auto args = std::vector<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
places->callAllWithPerPlaceArg(
MyPlace::Function::MY_FUNC, // Id of the member method to call
args // Per-instance argument values
);
Initialize Agents by Invoking Methods with Different Arguments for each Instance#
Similar to Places, sometime we may also need to invoke a member method on each Agent instance while providing different argument value for each of them. However, due to current limitation, we are not able to do this any time, especially after some instances have been terminated or migrated. As a result, the API designed for this use case, named initializeInitialAgents, can only be invoked at the beginning of a simulation before any Agent instances terminate, migrate or spawn any children.
Similar to callAllWithPerPlaceArg, initializeInitialAgents takes in the identifier of the member method and a container with contiguous storage containing the different argument value for each Agent instance. The size of this container must be equal to the total number of initial Agent instances globally across all devices. And again, before invoking the requested member method, use the local index provided by the getLocalIndex method to retrieve the argument value correspond to the current instance.
class MyAgent final : public mass::Agent {
public:
__universal__ explicit MyAgent(const mass::Index localIndex) : Agent(localIndex) {}
enum Function {
INIT // The id assigned to `init`
};
__device__ void callMethod(const FunctionId functionId, void*const arg) {
switch (functionId) {
case INIT:
// retrieve the argument value for this instance using the local index.
init(reinterpret_cast<int*>(arg)[getLocalIndex()]);
break;
default:
break;
}
}
__device__ void init(const int arg) {
// implementation ...
}
};
// In simulation loop
const auto args = std::vector<int> { 1, 2, 3, 4, 5, 6, 7, 8 };
agents->initializeInitialAgents(
MyAgent::Function::INIT, // Id of the member method to call
args // Per-instance argument values
);