-
Hi, I am building a small ecosystem simulation. As part of it, I plan to have a 3D grid structure for the static environment with a diffusion process. The environment blocks will seek equilibrium through the diffusion, but other agent types will destabilize it through their actions. Now I am wondering what is the best approach to accomplish the diffusion inside of the agents simulation?
I really like FlameGPU so far. Great job & thank you! |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 1 reply
-
Hi @isolin . Thanks for using FLAME GPU and happy to answer your questions. It sounds like an interesting model.
|
Beta Was this translation helpful? Give feedback.
-
Hi, thank you for the answer! I looked at the Game of Life example (somehow yet I only looked at the docs and ignored the examples folder) and you are right that in that case gathering works perfectly. The reason is that the binary decision of life or death requires data from the surrounding neighborhood, but in that time step it only has an effect on the processed cell. In a diffusion process a physical quantity moves from one cell to another, so you need to update both the provider and the receiver in the same time step. That makes a stable system with a constant amount of the resources (the game of life is stable only in "special" cases). Sorry for the long explanation, you probably know all this, but maybe if someone else reads this, it may be helpful :) The Sugarscape belongs to the same problem class, as sugar rates change independently of the neighborhood and movement is just a read operation over the set of sighted cell. Thinking further, I realized that sine diffusion is a bilateral process, the naive solution actually computes the same twice. So for now I will try two solutions paths and report on their performance in a few days:
I will let you know about my findings once I get back to it, perhaps next weekend. |
Beta Was this translation helpful? Give feedback.
-
Thank you very much for the example! Most important of all I learned about the 3D visualization from it. I fought a lot with my diffusion, since in opposite to your example with a sphere-like topology I have a bounding box as hard boundary. Without a proper handling of edge cells I was losing energy in the system. Moreover, my application case is precipitation entering soil, descending deeper due to gravity and later eventually evaporating and leaving the system again. So I needed to take case of vertical and horizontal diffusion separately. It's a very simplified model, but it should be fine for my next goal to grow virtual plants. I found it useful to consider cells at My final solution works in 4 steps and I found scattering easier to keep under control:
Steps 3 and 4 go as follows: FLAMEGPU_AGENT_FUNCTION(soil_hdiff, flamegpu::MessageArray3D, flamegpu::MessageArray3D) {
//[left out] read agent variables ix, iy, iz (coords) and water (current water amount)
const float waterSides[4] = {
max(0.0f, (ix > 0 ? water - FLAMEGPU->message_in.at(ix - 1, iy, iz).getVariable<float>("water") : 0.0f)),
max(0.0f, (ix < sizeX ? water - FLAMEGPU->message_in.at(ix + 1, iy, iz).getVariable<float>("water") : 0.0f)),
max(0.0f, (iy > 0 ? water - FLAMEGPU->message_in.at(ix, iy - 1, iz).getVariable<float>("water") : 0.0f)),
max(0.0f, (iy < sizeY ? water - FLAMEGPU->message_in.at(ix, iy + 1, iz).getVariable<float>("water") : 0.0f))
};
const float waterSidesSum = waterSides[0] + waterSides[1] + waterSides[2] + waterSides[3];
if (waterSidesSum > 0.001f) //avoid micro-diffusion that would cause numerical instabilities
{
float sideFlow = water * sidewaysDiffusionCoef; //decide how much water can scatter to those neighbors with a lower water level
FLAMEGPU->setVariable<float>("water", water - sideFlow);
sideFlow /= waterSidesSum;
for(int i = 0; i < 4; ++i) FLAMEGPU->message_out.setVariable<float, 4>("water_sides", i, waterSides[i] * sideFlow);
}
else //no diffusion if the difference is too low
{
FLAMEGPU->setVariable<float>("water", water);
for(int i = 0; i < 4; ++i) FLAMEGPU->message_out.setVariable<float, 4>("water_sides", i, 0.0f);
}
FLAMEGPU->message_out.setIndex(ix, iy, iz);
return flamegpu::ALIVE;
} FLAMEGPU_AGENT_FUNCTION(soil_receives_diffusion, flamegpu::MessageArray3D, flamegpu::MessageArray3D) {
//[left out] read agent variables ix, iy, iz (coords) and water (current water amount)
//gathering the water "donated" by the neighbors
if (ix > 0) water += FLAMEGPU->message_in.at(ix - 1, iy, iz).getVariable<float, 4>("water_sides", 1);
if (ix < sizeX) water += FLAMEGPU->message_in.at(ix + 1, iy, iz).getVariable<float, 4>("water_sides", 0);
if (iy > 0) water += FLAMEGPU->message_in.at(ix, iy - 1, iz).getVariable<float, 4>("water_sides", 3);
if (iy < sizeY) water += FLAMEGPU->message_in.at(ix, iy + 1, iz).getVariable<float, 4>("water_sides", 2);
FLAMEGPU->setVariable<float>("water", water);
return flamegpu::ALIVE;
} |
Beta Was this translation helpful? Give feedback.
Hi @isolin . Thanks for using FLAME GPU and happy to answer your questions. It sounds like an interesting model.
MessageArray3D
is not wrapped so message reads from edge positions will return fewer messages (see next point about use of message gathers). This avoids the need for you to implement conditions. The implementation behind the scenes will of course have some compromises regarding divergence. The implementation inside the simulat…