FLAMEGPU / FLAMEGPU2

FLAME GPU 2 is a GPU accelerated agent based modelling framework for CUDA C++ and Python
https://flamegpu.com
MIT License
105 stars 20 forks source link

Spatial messaging shrinks interaction radius to a factor of width, when not a factor #1157

Closed Robadob closed 9 months ago

Robadob commented 10 months ago

https://github.com/FLAMEGPU/FLAMEGPU2/blob/3801865978db1f1dd4d0652792501ef3f298e0d1/src/flamegpu/runtime/messaging/MessageSpatial2D.cu#L44

Consider: Width 105 Radius 10

This currently creates 11 bins. Therefore the 11th bin would only be half occupied (covering 5 width, rather than 10). Therefore any agents in the 10th bin, will only access the 11th bin and not wrap to the 1st bin. Therefore missing out on messages in that direction, creating bias.

The solution would be to floor() rather than ceil() and accept that the 10th bin will be denser as it covers any overflow width.

Robadob commented 10 months ago

False alarm, it works though I don't know why.

FLAMEGPU_AGENT_FUNCTION(in_wrapped_not_factor, MessageSpatial2D, MessageNone) {
    const float x1 = FLAMEGPU->getVariable<float>("x");
    const float y1 = FLAMEGPU->getVariable<float>("y");
    unsigned int count = 0;
    unsigned int id[3];
    // Count how many messages we recieved (including our own)
    for (const auto& message : FLAMEGPU->message_in.wrap(x1, y1)) {
        id[count] = message.getVariable<unsigned int>("id");
        ++count;
    }
    FLAMEGPU->setVariable<unsigned int>("count", count);
    return ALIVE;
}
TEST(Spatial2DMessageTest, wrapped_not_factor) {
    // This tests that bug #1157 is fixed
    // When the interaction radius is not a factor of the width
    // that agent's near the max env bound all have the full interaction radius
    ModelDescription m("model");
    MessageSpatial2D::Description message = m.newMessage<MessageSpatial2D>("location");
    message.setMin(0, 0);
    message.setMax(105, 105);
    message.setRadius(10);
    message.newVariable<flamegpu::id_t>("id");  // unused by current test
    AgentDescription agent = m.newAgent("agent");
    agent.newVariable<float>("x");
    agent.newVariable<float>("y");
    agent.newVariable<unsigned int>("count", 0);
    AgentFunctionDescription fo = agent.newFunction("out", out_mandatory2D);
    fo.setMessageOutput(message);
    AgentFunctionDescription fi = agent.newFunction("in", in_wrapped_not_factor);
    fi.setMessageInput(message);
    LayerDescription lo = m.newLayer();
    lo.addAgentFunction(fo);
    LayerDescription li = m.newLayer();
    li.addAgentFunction(fi);
    // Set pop in model
    CUDASimulation c(m);
    // Create an agent in the middle of each edge
    AgentVector population(agent, 6);
    // Initialise agents
    // Vertical pair that can interact
    // Bottom side
    AgentVector::Agent i1 = population[0];
    i1.setVariable<float>("x", 105.0f / 2);
    i1.setVariable<float>("y", 99.0f);
    // Bottom side boundary
    AgentVector::Agent i2 = population[1];
    i2.setVariable<float>("x", 105.0f / 2);
    i2.setVariable<float>("y", 105.0f);
    // Top side
    AgentVector::Agent i3 = population[2];
    i3.setVariable<float>("x", 105.0f / 2);
    i3.setVariable<float>("y", 0.1f);
    // Horizontal pair that can interact
    // Right side
    AgentVector::Agent i4 = population[3];
    i4.setVariable<float>("x", 99.0f);
    i4.setVariable<float>("y", 105.0f / 2);
    // Right side boundary
    AgentVector::Agent i5 = population[4];
    i5.setVariable<float>("x", 105.0f);
    i5.setVariable<float>("y", 105.0f / 2);
    // Left side
    AgentVector::Agent i6 = population[5];
    i6.setVariable<float>("x", 0.1f);
    i6.setVariable<float>("y", 105.0f / 2);
    c.setPopulationData(population);
    c.SimulationConfig().steps = 1;
    EXPECT_NO_THROW(c.simulate());
    // Recover the results and check they match what was expected
    c.getPopulationData(population);
    // Validate each agent has same result
    for (AgentVector::Agent ai : population) {
        EXPECT_EQ(3u, ai.getVariable<unsigned int>("count"));
    }
}
Robadob commented 10 months ago

https://github.com/FLAMEGPU/FLAMEGPU2/blob/3801865978db1f1dd4d0652792501ef3f298e0d1/include/flamegpu/runtime/messaging/MessageSpatial2D/MessageSpatial2DDevice.cuh#L602

There does exist a bug.

The maths here places a value of 99, into bin 10, rather than bin 9 (with 105 width, 10 rad).

This essentially means the com radius is shrunk when not a factor of the environment width.

Robadob commented 10 months ago

From @mondus in meeting, fix line 602 from previous comment and block wrapped from operating when interaction radius is not a factor of width.