Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BugFix: Agent ID's were not static during host functions. #1270

Merged
merged 5 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions include/flamegpu/simulation/AgentVector.h
Original file line number Diff line number Diff line change
Expand Up @@ -600,9 +600,11 @@ class AgentVector {
*/
virtual void _requireLength() const { }
/**
* Notify any subclasses that all variables are about to be accessed
* Should be called by operations which move agents (e.g. insert/erase)
* @note This is not called in conjunction with _insert() or _erase()
* Resize the capacity of the AgentVector
* @param count The new capacity of the agent vector
* @param init Whether any new agents should be default init
* @note If count < size() agent data will be lost
* @warning This method does not update _size, the caller of this method should update size
*/
void internal_resize(size_type count, bool init);
/**
Expand Down
24 changes: 7 additions & 17 deletions src/flamegpu/runtime/agent/DeviceAgentVector_impl.cu
Original file line number Diff line number Diff line change
Expand Up @@ -185,17 +185,20 @@ void DeviceAgentVector_impl::_insert(size_type pos, size_type count) {
_require(ID_VARIABLE_NAME);
id_t *h_ptr = static_cast<id_t*>(d->second->getDataPtr());
for (unsigned int i = pos; i < pos + count; ++i) {
// Always assign ID, as AgentVector should reset these to unset, but this saves us checking
// if (h_ptr[i] == ID_NOT_SET) {
// Do not reassign ID if it has been set during HostAgentCreation
if (h_ptr[i] == ID_NOT_SET) {
h_ptr[i] = cuda_agent.nextID();
// }
}
}
_changedAfter(ID_VARIABLE_NAME, pos);
} else {
THROW exception::InvalidOperation("Internal agent ID variable was not found, "
"in DeviceAgentVector_impl._insert().");
}
}
// Update change detail for all variables
for (const auto& [v, _] : agent->variables) {
_changedAfter(v, pos);
}
// No unbound buffers, return
if (unbound_buffers.empty())
return;
Expand Down Expand Up @@ -239,19 +242,6 @@ void DeviceAgentVector_impl::_insert(size_type pos, size_type count) {
if (unbound_host_buffer_size != _size) {
THROW exception::InvalidOperation("Unbound buffers have gone out of sync, in DeviceAgentVector::_insert().\n");
}
// Update change detail for all variables
for (const auto& v : agent->variables) {
// Does it exist in change map
auto change = change_detail.find(v.first);
if (change == change_detail.end()) {
change_detail.emplace(v.first, std::pair<size_type, size_type>{pos, _size});
} else {
// Inclusive min bound
change->second.first = change->second.first > pos ? pos : change->second.first;
// Exclusive max bound
change->second.second = _size;
}
}
}
void DeviceAgentVector_impl::_erase(size_type pos, size_type count) {
// No unbound buffers, return
Expand Down
8 changes: 0 additions & 8 deletions src/flamegpu/simulation/AgentVector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -538,18 +538,10 @@ void AgentVector::internal_resize(size_type count, bool init) {
for (const auto& v : agent->variables) {
// For each variable inside agent, add it to the map or replace it in the map
const auto it = _data->find(v.first);
const size_t variable_size = v.second.type_size * v.second.elements;
if (it == _data->end()) {
// Need to create the variable's vector
auto t = std::unique_ptr<detail::GenericMemoryVector>(v.second.memory_vector->clone());
t->resize(count);
// Default init all new elements
if (init) {
char* t_data = static_cast<char*>(t->getDataPtr());
for (unsigned int i = 0; i < count; ++i) {
memcpy(t_data + i * variable_size, v.second.default_value, variable_size);
}
}
_data->emplace(v.first, std::move(t));
} else {
// Need to resize the variables vector
Expand Down
112 changes: 112 additions & 0 deletions tests/test_cases/runtime/agent/test_host_agent_creation.cu
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,118 @@ TEST(HostAgentCreationTest, AgentID_MultipleAgents) {
}
ASSERT_EQ(ids_b.size(), 2 * POP_SIZE); // No collisions
}
FLAMEGPU_INIT_FUNCTION(AgentID_Consistent1) {
for (unsigned int i = 0; i < 10; ++i) {
auto a = FLAMEGPU->agent("agent").newAgent();
a.setVariable<id_t>("id_copy", a.getID());
}
}
FLAMEGPU_INIT_FUNCTION(AgentID_Consistent2) {
DeviceAgentVector v = FLAMEGPU->agent("agent").getPopulationData();
for (auto a : v) {
id_t t1 = a.getID();
id_t t2 = a.getVariable<id_t>("id_copy");
EXPECT_EQ(t1, t2);
}
}
FLAMEGPU_STEP_FUNCTION(AgentID_Consistent_Step) {
DeviceAgentVector v = FLAMEGPU->agent("agent").getPopulationData();
for (const auto &a : v) {
id_t t1 = a.getID();
id_t t2 = a.getVariable<id_t>("id_copy");
EXPECT_EQ(t1, t2);
EXPECT_NE(t1, ID_NOT_SET);
EXPECT_NE(t2, ID_NOT_SET);
}
}
TEST(HostAgentCreationTest, AgentID_Consistent) {
// PTHeywood found that when agents were created in a host function they would be given an ID
// This would then be reassigned, if they were accessed via a DeviceAgentVector in the same host function
// This was also present with multiple init functions, as shown in this test.
// Pull request #1270

ModelDescription model("test_agentid");
AgentDescription agent = model.newAgent("agent");
agent.newVariable<id_t>("id_copy", ID_NOT_SET);

model.addInitFunction(AgentID_Consistent1);
model.addInitFunction(AgentID_Consistent2);
model.addStepFunction(AgentID_Consistent_Step);

CUDASimulation sim(model);

sim.SimulationConfig().steps = 2;

sim.simulate();
}
FLAMEGPU_INIT_FUNCTION(AgentID_Consistent_Combined) {
for (unsigned int i = 0; i < 10; ++i) {
auto a = FLAMEGPU->agent("agent").newAgent();
a.setVariable<id_t>("id_copy", a.getID());
}
DeviceAgentVector v = FLAMEGPU->agent("agent").getPopulationData();
for (auto a : v) {
id_t t1 = a.getID();
id_t t2 = a.getVariable<id_t>("id_copy");
EXPECT_EQ(t1, t2);
}
}
TEST(HostAgentCreationTest, AgentID_Consistent_Combined) {
// Duplicate of AgentID_Consistent, but with new agent creation and then DAV iteration in the same agent function
ModelDescription model("test_agentid");
AgentDescription agent = model.newAgent("agent");
agent.newVariable<id_t>("id_copy", ID_NOT_SET);

model.addInitFunction(AgentID_Consistent_Combined);
model.addStepFunction(AgentID_Consistent_Step);

CUDASimulation sim(model);

sim.SimulationConfig().steps = 1;

sim.simulate();
}
FLAMEGPU_AGENT_FUNCTION(AgentID_Consistent_Agent, flamegpu::MessageNone, flamegpu::MessageNone) {
FLAMEGPU->setVariable<id_t>("id_copy_from_device", FLAMEGPU->getID());
FLAMEGPU->setVariable<id_t>("id_copy_copy_from_device", FLAMEGPU->getVariable<id_t>("id_copy"));
return flamegpu::ALIVE;
}
FLAMEGPU_STEP_FUNCTION(AgentID_Consistent_Step_Agent) {
DeviceAgentVector v = FLAMEGPU->agent("agent").getPopulationData();
for (const auto &a : v) {
id_t t1 = a.getID();
id_t t2 = a.getVariable<id_t>("id_copy_from_device");
id_t t3 = a.getVariable<id_t>("id_copy_copy_from_device");
EXPECT_NE(t1, ID_NOT_SET);
EXPECT_NE(t2, ID_NOT_SET);
EXPECT_NE(t3, ID_NOT_SET);
EXPECT_EQ(t1, t2);
EXPECT_EQ(t2, t3);
}
}
TEST(HostAgentCreationTest, AgentID_Consistent_Device) {
// Duplicate as AgentID_Consistent, but checks data makes it to the GPU, both in getID and in agent data

ModelDescription model("test_agentid");
AgentDescription agent = model.newAgent("agent");
agent.newVariable<id_t>("id_copy", ID_NOT_SET);
agent.newVariable<id_t>("id_copy_from_device", ID_NOT_SET);
agent.newVariable<id_t>("id_copy_copy_from_device", ID_NOT_SET);

auto afd = agent.newFunction("AgentID_Consistent_Agent", AgentID_Consistent_Agent);

model.addInitFunction(AgentID_Consistent1);
model.addInitFunction(AgentID_Consistent2);
model.addStepFunction(AgentID_Consistent_Step_Agent);

model.newLayer().addAgentFunction(afd);

CUDASimulation sim(model);

sim.SimulationConfig().steps = 1;

sim.simulate();
}
#ifdef FLAMEGPU_USE_GLM
FLAMEGPU_STEP_FUNCTION(ArrayVarHostBirthSetGet_glm) {
auto t = FLAMEGPU->agent("agent_name");
Expand Down