diff --git a/include/flamegpu/simulation/AgentVector_Agent.h b/include/flamegpu/simulation/AgentVector_Agent.h index d8e7aee2d..317c92224 100644 --- a/include/flamegpu/simulation/AgentVector_Agent.h +++ b/include/flamegpu/simulation/AgentVector_Agent.h @@ -249,6 +249,8 @@ void AgentVector_Agent::setVariable(const std::string &variable_name, const unsi } _parent->_require(variable_name); static_cast(v_buff->getDataPtr())[(index * (v_buff->getElements() / detail::type_decode::len_t)) + array_index] = value; + // Notify (_data was locked above) + _parent->_changed(variable_name, index); } #ifdef SWIG template diff --git a/src/flamegpu/io/Telemetry.cpp b/src/flamegpu/io/Telemetry.cpp index cc2b292ee..eb37f5709 100644 --- a/src/flamegpu/io/Telemetry.cpp +++ b/src/flamegpu/io/Telemetry.cpp @@ -372,8 +372,7 @@ std::string Telemetry::getUserId() { return cached_id; else return userId; - } - else { + } else { throw std::runtime_error("Unable to open user ID file for reading"); } } @@ -382,8 +381,7 @@ std::string Telemetry::getUserId() { if (file.is_open()) { file << userId; file.close(); - } - else { + } else { throw std::runtime_error("Unable to create user ID file"); } } catch (const std::exception&) { diff --git a/tests/test_cases/runtime/agent/test_device_agent_vector.cu b/tests/test_cases/runtime/agent/test_device_agent_vector.cu index 0181384c1..0c05c9ccd 100644 --- a/tests/test_cases/runtime/agent/test_device_agent_vector.cu +++ b/tests/test_cases/runtime/agent/test_device_agent_vector.cu @@ -22,6 +22,24 @@ FLAMEGPU_STEP_FUNCTION(SetGet) { ai.setVariable("int", ai.getVariable("int") + 12); } } +FLAMEGPU_STEP_FUNCTION(SetGetArray) { + // Accessing DeviceAgentVector like this would previously lead to an access violation (Issue #522, PR #751) + DeviceAgentVector av = FLAMEGPU->agent(AGENT_NAME).getPopulationData(); + for (AgentVector::Agent ai : av) { + auto t = ai.getVariable("int"); + t[0] += 11; + t[1] += 12; + t[2] += 13; + ai.setVariable("int", t); + } +} +FLAMEGPU_STEP_FUNCTION(SetGetArrayElement) { + // Accessing DeviceAgentVector like this would previously lead to an access violation (Issue #522, PR #751) + DeviceAgentVector av = FLAMEGPU->agent(AGENT_NAME).getPopulationData(); + for (AgentVector::Agent ai : av) { + ai.setVariable("int", 1, ai.getVariable("int", 1) + 12); + } +} FLAMEGPU_STEP_FUNCTION(SetGetHalf) { HostAgentAPI agent = FLAMEGPU->agent(AGENT_NAME); DeviceAgentVector av = agent.getPopulationData(); @@ -77,6 +95,85 @@ TEST(DeviceAgentVectorTest, SetGet) { ASSERT_EQ(av[i].getVariable("int"), static_cast(i) + 24); } } +TEST(DeviceAgentVectorTest, SetGetArray) { + // Initialise an agent population with values in a variable [0,1,2..N] + // Inside a step function, retrieve the agent population as a DeviceAgentVector + // Update all agents by adding 12 to their value + // After model completion, retrieve the agent population and check their values are [12,13,14..N+12] + ModelDescription model(MODEL_NAME); + AgentDescription agent = model.newAgent(AGENT_NAME); + agent.newVariable("int", { 0, 0, 0 }); + model.addStepFunction(SetGetArray); + + // Init agent pop + AgentVector av(agent, AGENT_COUNT); + for (unsigned int i = 0; i < AGENT_COUNT; ++i) + av[i].setVariable("int", { 0, static_cast(i), 0 }); + + // Create and step simulation + CUDASimulation sim(model); + sim.setPopulationData(av); + sim.step(); + + // Retrieve and validate agents match + sim.getPopulationData(av); + for (unsigned int i = 0; i < AGENT_COUNT; ++i) { + const std::array t1 = {11, static_cast(i) + 12, 13 }; + const std::array t2 = av[i].getVariable("int"); + ASSERT_EQ(t1, t2); + } + + // Step again + sim.step(); + + // Retrieve and validate agents match + sim.getPopulationData(av); + for (unsigned int i = 0; i < AGENT_COUNT; ++i) { + const std::array t1 = { 22, static_cast(i) + 24, 26 }; + const std::array t2 = av[i].getVariable("int"); + ASSERT_EQ(t1, t2); + } +} +TEST(DeviceAgentVectorTest, SetGetArrayElement) { + // Covers former bug, where updating an array element would not trigger sync + // Initialise an agent population with values in a variable [0,1,2..N] + // Inside a step function, retrieve the agent population as a DeviceAgentVector + // Update all agents by adding 12 to their value + // After model completion, retrieve the agent population and check their values are [12,13,14..N+12] + ModelDescription model(MODEL_NAME); + AgentDescription agent = model.newAgent(AGENT_NAME); + agent.newVariable("int", {0, 0, 0}); + model.addStepFunction(SetGetArrayElement); + + // Init agent pop + AgentVector av(agent, AGENT_COUNT); + for (unsigned int i = 0; i < AGENT_COUNT; ++i) + av[i].setVariable("int", { 0, static_cast(i), 0 }); + + // Create and step simulation + CUDASimulation sim(model); + sim.setPopulationData(av); + sim.step(); + + // Retrieve and validate agents match + sim.getPopulationData(av); + for (unsigned int i = 0; i < AGENT_COUNT; ++i) { + const std::array t1 = { 0, static_cast(i) + 12, 0 }; + const std::array t2 = av[i].getVariable("int"); + ASSERT_EQ(t1, t2); + } + + // Step again + sim.step(); + + // Retrieve and validate agents match + sim.getPopulationData(av); + for (unsigned int i = 0; i < AGENT_COUNT; ++i) { + const std::array t1 = { 0, static_cast(i) + 24, 0 }; + const std::array t2 = av[i].getVariable("int"); + ASSERT_EQ(t1, t2); + } +} TEST(DeviceAgentVectorTest, SetGetHalf) { // Initialise an agent population with values in a variable [0,1,2..N] // Inside a step function, retrieve the agent population as a DeviceAgentVector