@@ -774,6 +774,174 @@ TEST(TestCUDASimulation, setEnvironmentProperty) {
774774 s.simulate ();
775775}
776776
777+
778+ /* *
779+ * Test that simullations work with a large number of agent functions in a single layer (i.e. > 128).
780+ * This needs to be a non-trivial model in order to trigger the encountered issue described by #727
781+ * https://github.com/FLAMEGPU/FLAMEGPU2/issues/727
782+ * Message output, agent output and/or agent death should all trigger compactions which might highlight this issues.
783+ * Additionally other uses of CUDAScanCompaction in general should trigger this issue.
784+ * @todo - similar tests may be useful for most / all features which can occur in streams
785+ */
786+ FLAMEGPU_AGENT_FUNCTION (VeryWideLayerAgentDeath, MessageNone, MessageNone) {
787+ if (FLAMEGPU->getID () % 2 ) {
788+ return flamegpu::DEAD;
789+ }
790+ return flamegpu::ALIVE;
791+ }
792+ FLAMEGPU_AGENT_FUNCTION (VeryWideLayerAgentBirth, MessageNone, MessageNone) {
793+ if (FLAMEGPU->getID () % 2 ) {
794+ FLAMEGPU->agent_out .setVariable <unsigned int >(" b" , FLAMEGPU->getVariable <unsigned int >(" b" ));
795+ }
796+ return flamegpu::ALIVE;
797+ }
798+ FLAMEGPU_AGENT_FUNCTION (VeryWideLayerBucketOut, MessageNone, MessageBucket) {
799+ if (FLAMEGPU->getID () % 2 ) {
800+ FLAMEGPU->message_out .setVariable <flamegpu::id_t >(" id" , FLAMEGPU->getID ());
801+ FLAMEGPU->message_out .setKey (FLAMEGPU->getVariable <unsigned int >(" b" ));
802+ }
803+ return flamegpu::ALIVE;
804+ }
805+ FLAMEGPU_AGENT_FUNCTION (VeryWideLayerBucketIn, MessageBucket, MessageNone) {
806+ unsigned int c = 0 ;
807+ for (auto &message : FLAMEGPU->message_in (FLAMEGPU->getVariable <unsigned int >(" b" ))) {
808+ c++;
809+ }
810+ FLAMEGPU->setVariable <unsigned int >(" c" , c);
811+ return flamegpu::ALIVE;
812+ }
813+ TEST (TestCUDASimulation, VeryWideLayerDeath) {
814+ // The width of the model, i.e. the number of agent types
815+ constexpr unsigned int MODEL_WIDTH = 129 ;
816+ // How many individuals to place in each population. This should be irrelevant.
817+ constexpr unsigned int POP_SIZE = 32 ;
818+ // Create a model containing a very large number of different agent types, which all operate in the same layer.
819+ ModelDescription model (" VeryWideLayerDeath" );
820+ // Create the layer for all the agent functions to reside in
821+ auto & layer = model.newLayer ();
822+ // Define many agent types
823+ for (unsigned int idx = 0 ; idx < MODEL_WIDTH; idx++) {
824+ std::string agent_name (" agent_" + std::to_string (idx));
825+ std::string agent_function_name (agent_name + " _VeryWideLayerAgentDeath" );
826+ // Define the agent
827+ AgentDescription& agent = model.newAgent (agent_name);
828+ // Add a variable, to allow birth / bucket messaging
829+ agent.newVariable <unsigned int >(" b" , 0 );
830+ // Define an agent function which allows death
831+ auto & f = agent.newFunction (agent_function_name, VeryWideLayerAgentDeath);
832+ f.setAllowAgentDeath (true );
833+ // Add the function to the single model layer
834+ layer.addAgentFunction (f);
835+ }
836+ // Create the simulation
837+ CUDASimulation simulation (model);
838+ // Populate the simulation
839+ for (unsigned int idx = 0 ; idx < MODEL_WIDTH; idx++) {
840+ std::string agent_name (" agent_" + std::to_string (idx));
841+ AgentDescription& agent = model.Agent (agent_name);
842+ AgentVector population (agent, POP_SIZE);
843+ simulation.setPopulationData (population);
844+ }
845+ // Run a single simulation step.
846+ simulation.SimulationConfig ().steps = 1 ;
847+ simulation.applyConfig ();
848+ EXPECT_NO_THROW (simulation.simulate ());
849+ // If this runs then the test can be assumed to have passed, i.e. an error did not occur.
850+ }
851+ TEST (TestCUDASimulation, VeryWideLayerBirth) {
852+ // The width of the model, i.e. the number of agent types
853+ constexpr unsigned int MODEL_WIDTH = 129 ;
854+ // How many individuals to place in each population. This should be irrelevant.
855+ constexpr unsigned int POP_SIZE = 32 ;
856+ // Create a model containing a very large number of different agent types, which all operate in the same layer.
857+ ModelDescription model (" VeryWideLayerBirth" );
858+ // Create the layer for all the agent functions to reside in
859+ auto & layer = model.newLayer ();
860+ // Define many agent types
861+ for (unsigned int idx = 0 ; idx < MODEL_WIDTH; idx++) {
862+ std::string agent_name (" agent_" + std::to_string (idx));
863+ std::string agent_function_name (agent_name + " _VeryWideLayerAgentBirth" );
864+ // Define the agent
865+ AgentDescription& agent = model.newAgent (agent_name);
866+ // Add a variable, to allow birth / bucket messaging
867+ agent.newVariable <unsigned int >(" b" , 0 );
868+ // Define an agent function which allows birth
869+ auto & f = agent.newFunction (agent_function_name, VeryWideLayerAgentBirth);
870+ f.setAgentOutput (agent);
871+ // Add the function to the single model layer
872+ layer.addAgentFunction (f);
873+ }
874+ // Create the simulation
875+ CUDASimulation simulation (model);
876+ // Populate the simulation
877+ for (unsigned int idx = 0 ; idx < MODEL_WIDTH; idx++) {
878+ std::string agent_name (" agent_" + std::to_string (idx));
879+ AgentDescription& agent = model.Agent (agent_name);
880+ AgentVector population (agent, POP_SIZE);
881+ simulation.setPopulationData (population);
882+ }
883+ // Run a single simulation step.
884+ simulation.SimulationConfig ().steps = 1 ;
885+ simulation.applyConfig ();
886+ EXPECT_NO_THROW (simulation.simulate ());
887+ // If this runs then the test can be assumed to have passed, i.e. an error did not occur.
888+ }
889+ TEST (TestCUDASimulation, VeryWideLayerBucketMessaging) {
890+ // The width of the model, i.e. the number of agent types
891+ constexpr unsigned int MODEL_WIDTH = 129 ;
892+ // How many individuals to place in each population. This should be irrelevant.
893+ constexpr unsigned int POP_SIZE = 32 ;
894+ // How many buckets exist.
895+ constexpr unsigned int BUCKET_COUNT = POP_SIZE / 2 ;
896+ // Create a model containing a very large number of different agent types, which all operate in the same layer.
897+ ModelDescription model (" VeryWideLayerBucketMessaging" );
898+ // Create the layers for all the agent functions to reside in
899+ auto & layer_out = model.newLayer ();
900+ auto & layer_in = model.newLayer ();
901+ // Define many agent types and message types.
902+ for (unsigned int idx = 0 ; idx < MODEL_WIDTH; idx++) {
903+ std::string agent_name (" agent_" + std::to_string (idx));
904+ std::string agent_function_out_name (agent_name + " _VeryWideLayerBucketOut" );
905+ std::string agent_function_in_name (agent_name + " _VeryWideLayerBucketIn" );
906+ std::string message_name (" message_" + std::to_string (idx));
907+ // Define the message list
908+ MessageBucket::Description &message = model.newMessage <MessageBucket>(message_name);
909+ message.setBounds (0 , BUCKET_COUNT);
910+ message.newVariable <flamegpu::id_t >(" id" );
911+ // Define the agent
912+ AgentDescription& agent = model.newAgent (agent_name);
913+ // Add a variable, to allow bucket messaging
914+ agent.newVariable <unsigned int >(" b" , 0 );
915+ agent.newVariable <unsigned int >(" c" , 0 );
916+ // Define the message output and input agent functions
917+ auto & f_out = agent.newFunction (agent_function_out_name, VeryWideLayerBucketOut);
918+ f_out.setMessageOutput (message_name);
919+ auto & f_in = agent.newFunction (agent_function_in_name, VeryWideLayerBucketIn);
920+ f_in.setMessageInput (message_name);
921+ // Add the function to the single model layer
922+ layer_out.addAgentFunction (f_out);
923+ layer_in.addAgentFunction (f_in);
924+ }
925+ // Create the simulation
926+ CUDASimulation simulation (model);
927+ // Populate the simulation
928+ for (unsigned int idx = 0 ; idx < MODEL_WIDTH; idx++) {
929+ std::string agent_name (" agent_" + std::to_string (idx));
930+ AgentDescription& agent = model.Agent (agent_name);
931+ AgentVector population (agent, POP_SIZE);
932+ for (auto instance : population) {
933+ instance.setVariable <unsigned int >(" b" , instance.getID () / BUCKET_COUNT);
934+ instance.setVariable <unsigned int >(" c" , 0 );
935+ }
936+ simulation.setPopulationData (population);
937+ }
938+ // Run a single simulation step.
939+ simulation.SimulationConfig ().steps = 1 ;
940+ simulation.applyConfig ();
941+ EXPECT_NO_THROW (simulation.simulate ());
942+ // If this runs then the test can be assumed to have passed, i.e. an error did not occur.
943+ }
944+
777945} // namespace test_cuda_simulation
778946} // namespace tests
779947} // namespace flamegpu
0 commit comments