Skip to content

Commit 1c699f6

Browse files
committed
modified abstract submodel to be a little bit more general
1 parent cfebbb9 commit 1c699f6

7 files changed

Lines changed: 82 additions & 40 deletions

File tree

examples/cpp/beesandflowers/src/main.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ void define_model(ModelDescription &model) {
213213
flamegpu::stockAgent::submodels::SingleAgentDiscreteMovement move_sub_logic;
214214

215215
// Initialize the submodel
216-
auto move_sub_desc = move_sub_logic.addSingleAgentDiscreteMovementSubmodel(model, ENV_DIM, ENV_DIM);
216+
move_sub_logic.addSingleAgentDiscreteMovementSubmodel(model, ENV_DIM, ENV_DIM);
217217

218218
// Bind parent agents to submodel
219219
move_sub_logic.setMovingAgent("bee",
@@ -250,7 +250,7 @@ void define_model(ModelDescription &model) {
250250
l0.addAgentFunction(calculate_priority);
251251

252252
LayerDescription l1 = model.newLayer();
253-
l1.addSubModel(move_sub_desc);
253+
l1.addSubModel(move_sub_logic.getSubModelDescription());
254254

255255
LayerDescription l2 = model.newLayer();
256256
l2.addAgentFunction(update_hunger_wait);

examples/cpp/sugarscapeSubModelAPI/src/main.cu

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ int main(int argc, const char ** argv) {
131131
* Submodel Configuration
132132
*/
133133
flamegpu::stockAgent::submodels::SingleAgentDiscreteMovement move_sub_logic;
134-
auto move_sub_desc = move_sub_logic.addSingleAgentDiscreteMovementSubmodel(model, GRID_WIDTH, GRID_HEIGHT);
134+
move_sub_logic.addSingleAgentDiscreteMovementSubmodel(model, GRID_WIDTH, GRID_HEIGHT);
135135

136136
// Bind Bug to the submodel's moving agent
137137
move_sub_logic.setMovingAgent("bug",
@@ -167,7 +167,7 @@ int main(int argc, const char ** argv) {
167167
sugar_cell.newFunction("growback", growback);
168168

169169
// Layer 1: Movement
170-
model.newLayer().addSubModel(move_sub_desc);
170+
model.newLayer().addSubModel(move_sub_logic.getSubModelDescription());
171171

172172
// Layer 2: Life logic (Metabolism and Growback can happen in parallel)
173173
{

include/flamegpu/stockAgent/subModels/AbstractSubmodels.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#ifndef INCLUDE_FLAMEGPU_STOCKAGENT_SUBMODELS_ABSTRACTSUBMODELS_H_
22
#define INCLUDE_FLAMEGPU_STOCKAGENT_SUBMODELS_ABSTRACTSUBMODELS_H_
33

4+
#include <string>
45
#include "flamegpu/flamegpu.h"
56

67
namespace flamegpu {
@@ -13,8 +14,23 @@ namespace submodels {
1314
*/
1415
class AbstractSubmodel {
1516
public:
16-
virtual void validate() = 0;
1717
virtual ~AbstractSubmodel() = default;
18+
19+
/**
20+
* Returns the underlying FLAME GPU SubModelDescription.
21+
* Throws if the submodel hasn't been initialized/added to a model yet.
22+
*/
23+
virtual flamegpu::SubModelDescription getSubModelDescription() const = 0;
24+
25+
/**
26+
* Validates that all required agents and variables have been mapped.
27+
*/
28+
virtual void validate() = 0;
29+
30+
/**
31+
* Returns the name of the submodel instance.
32+
*/
33+
virtual std::string getName() const = 0;
1834
};
1935

2036

include/flamegpu/stockAgent/subModels/SingleAgentDiscreteMovement.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <map>
55
#include <string>
66
#include <memory>
7+
#include <optional>
78

89
#include "AbstractSubmodels.h"
910

@@ -50,12 +51,15 @@ class SingleAgentDiscreteMovement : public AbstractSubmodel {
5051
const std::map<std::string, std::string>& state_map = {},
5152
bool auto_map = false);
5253

54+
flamegpu::SubModelDescription getSubModelDescription() const override;
55+
5356
void validate() override;
5457

58+
std::string getName() const override;
59+
5560
private:
56-
// Internal pointer allows SingleAgentDiscreteMovement to be default-constructible
57-
std::unique_ptr<flamegpu::SubModelDescription> smm;
58-
bool is_initialized = false;
61+
// SubModelDescription is a proxy. We use optional to allow late initialization without unique_ptr.
62+
std::optional<flamegpu::SubModelDescription> smm;
5963

6064
// Internal setup methods
6165
void setMessages(flamegpu::AgentDescription &movingAgent, flamegpu::AgentDescription &envAgent, int ENV_SIZE_X, int ENV_SIZE_Y);

src/flamegpu/stockAgent/subModels/SingleAgentDiscreteMovement.cu

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,8 @@ flamegpu::SubModelDescription SingleAgentDiscreteMovement::addSingleAgentDiscret
293293
sub_model_move.addInitFunction(initial_occupancy_sync);
294294
sub_model_move.addExitCondition(move_exit_condition);
295295

296-
this->smm = std::make_unique<SubModelDescription>(model.newSubModel("MovementInstance", sub_model_move));
296+
this->smm = model.newSubModel("MovementInstance", sub_model_move);
297297
this->smm->setMaxSteps(5);
298-
this->is_initialized = true;
299298

300299
return *(this->smm);
301300
}
@@ -304,7 +303,7 @@ void SingleAgentDiscreteMovement::setMovingAgent(const string& parent_name,
304303
const map<string, string>& var_map,
305304
const map<string, string>& state_map,
306305
bool auto_map) {
307-
if (!is_initialized) {
306+
if (!smm.has_value()) {
308307
throw exception::InvalidSubModel("SingleAgentDiscreteMovement submodel was not initialized. Call addSingleAgentDiscreteMovementSubmodel() first.");
309308
}
310309
auto agent_map = this->smm->bindAgent(INTERNAL_MOVING_AGENT_NAME, parent_name, auto_map, auto_map);
@@ -316,33 +315,44 @@ void SingleAgentDiscreteMovement::setEnvironmentAgent(const string& parent_name,
316315
const map<string, string>& var_map,
317316
const map<string, string>& state_map,
318317
bool auto_map) {
319-
if (!is_initialized) {
318+
if (!smm.has_value()) {
320319
throw exception::InvalidSubModel("SingleAgentDiscreteMovement submodel was not initialized. Call addSingleAgentDiscreteMovementSubmodel() first.");
321320
}
322321
auto agent_map = this->smm->bindAgent(INTERNAL_ENV_AGENT_NAME, parent_name, auto_map, auto_map);
323322
for (auto const& [internal_var, parent_var] : var_map) { agent_map.mapVariable(internal_var, parent_var); }
324323
for (auto const& [internal_state, parent_state] : state_map) { agent_map.mapState(internal_state, parent_state); }
325324
}
326325

327-
void SingleAgentDiscreteMovement::defineMessageSubmodule(ModelDescription &smm, int ENV_SIZE_X, int ENV_SIZE_Y) {
328-
auto m1 = smm.newMessage<MessageArray2D>("cell_status");
326+
flamegpu::SubModelDescription SingleAgentDiscreteMovement::getSubModelDescription() const {
327+
if (!smm.has_value()) {
328+
throw exception::InvalidSubModel("SingleAgentDiscreteMovement submodel was not initialized.");
329+
}
330+
return *smm;
331+
}
332+
333+
std::string SingleAgentDiscreteMovement::getName() const {
334+
return "SingleAgentDiscreteMovement";
335+
}
336+
337+
void SingleAgentDiscreteMovement::defineMessageSubmodule(ModelDescription &smm_desc, int ENV_SIZE_X, int ENV_SIZE_Y) {
338+
auto m1 = smm_desc.newMessage<MessageArray2D>("cell_status");
329339
m1.newVariable<float>("cell_score");
330340
m1.newVariable<int>("is_occupied");
331341
m1.setDimensions(ENV_SIZE_X, ENV_SIZE_Y);
332342

333-
auto m2 = smm.newMessage<MessageArray2D>("move_requests");
343+
auto m2 = smm_desc.newMessage<MessageArray2D>("move_requests");
334344
m2.newVariable<id_t>("requester_id");
335345
m2.newVariable<int>("target_x");
336346
m2.newVariable<int>("target_y");
337347
m2.newVariable<float>("priority");
338348
m2.setDimensions(ENV_SIZE_X, ENV_SIZE_Y);
339349

340-
auto m3 = smm.newMessage<MessageArray2D>("move_responses");
350+
auto m3 = smm_desc.newMessage<MessageArray2D>("move_responses");
341351
m3.newVariable<id_t>("winner_id");
342352
m3.newVariable<float>("cell_score");
343353
m3.setDimensions(ENV_SIZE_X, ENV_SIZE_Y);
344354

345-
auto m4 = smm.newMessage<MessageArray2D>("new_locations");
355+
auto m4 = smm_desc.newMessage<MessageArray2D>("new_locations");
346356
m4.newVariable<id_t>("moving_agent_id");
347357
m4.setDimensions(ENV_SIZE_X, ENV_SIZE_Y);
348358
}
@@ -366,16 +376,16 @@ void SingleAgentDiscreteMovement::setMessages(AgentDescription &movingAgent, Age
366376
envAgent_update_occupancy_fn.setMessageInput("new_locations");
367377
}
368378

369-
void SingleAgentDiscreteMovement::defineLayer(ModelDescription &smm) {
370-
smm.newLayer().addAgentFunction(envAgent_broadcast_status);
371-
smm.newLayer().addAgentFunction(movingAgent_request_move);
372-
smm.newLayer().addAgentFunction(envAgent_resolve_conflict);
373-
smm.newLayer().addAgentFunction(movingAgent_execute_move);
374-
smm.newLayer().addAgentFunction(envAgent_update_occupancy);
379+
void SingleAgentDiscreteMovement::defineLayer(ModelDescription &smm_desc) {
380+
smm_desc.newLayer().addAgentFunction(envAgent_broadcast_status);
381+
smm_desc.newLayer().addAgentFunction(movingAgent_request_move);
382+
smm_desc.newLayer().addAgentFunction(envAgent_resolve_conflict);
383+
smm_desc.newLayer().addAgentFunction(movingAgent_execute_move);
384+
smm_desc.newLayer().addAgentFunction(envAgent_update_occupancy);
375385
}
376386

377387
void SingleAgentDiscreteMovement::validate() {
378-
if (!this->is_initialized) {
388+
if (!this->smm.has_value()) {
379389
throw exception::InvalidSubModel("SingleAgentDiscreteMovement submodel was not initialized. Call addSingleAgentDiscreteMovementSubmodel() first.");
380390
}
381391

tests/python/stockAgent/test_single_agent_discrete_movement.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ def test_initialization(self):
5656
except pyflamegpu.FLAMEGPURuntimeException as e:
5757
pytest.fail(f"validate() threw {e.type()} unexpectedly: {e.what()}")
5858

59+
# Check getName and getSubModelDescription
60+
assert move_submodel.getName() == "SingleAgentDiscreteMovement"
61+
assert move_submodel.getSubModelDescription() is not None
62+
5963
"""
6064
Test 2: Basic Greedy Movement
6165
Verifies that an agent at (0,0) moves to (1,1) if that cell has a higher score.
@@ -66,10 +70,10 @@ def test_simple_move(self):
6670
HEIGHT = 3
6771

6872
move_submodel = pyflamegpu.SingleAgentDiscreteMovement()
69-
smd = move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT)
73+
move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT)
7074

7175
# Submodels must be added to a layer to be executed during the simulation step
72-
model.newLayer().addSubModel(smd)
76+
model.newLayer().addSubModel(move_submodel.getSubModelDescription())
7377

7478
# Define parent agents with necessary variables
7579
agent = model.newAgent("agent")
@@ -147,8 +151,8 @@ def test_collision_avoidance(self):
147151
HEIGHT = 3
148152

149153
move_submodel = pyflamegpu.SingleAgentDiscreteMovement()
150-
smd = move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT)
151-
model.newLayer().addSubModel(smd)
154+
move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT)
155+
model.newLayer().addSubModel(move_submodel.getSubModelDescription())
152156

153157
agent = model.newAgent("agent")
154158
agent.newVariableInt("x")
@@ -226,8 +230,8 @@ def test_resource_memory(self):
226230
HEIGHT = 3
227231

228232
move_submodel = pyflamegpu.SingleAgentDiscreteMovement()
229-
smd = move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT)
230-
model.newLayer().addSubModel(smd)
233+
move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT)
234+
model.newLayer().addSubModel(move_submodel.getSubModelDescription())
231235

232236
agent = model.newAgent("agent")
233237
agent.newVariableInt("x")
@@ -297,8 +301,8 @@ def test_grid_boundaries(self):
297301
HEIGHT = 2
298302

299303
move_submodel = pyflamegpu.SingleAgentDiscreteMovement()
300-
smd = move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT)
301-
model.newLayer().addSubModel(smd)
304+
move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT)
305+
model.newLayer().addSubModel(move_submodel.getSubModelDescription())
302306

303307
agent = model.newAgent("agent")
304308
agent.newVariableInt("x")
@@ -357,3 +361,7 @@ def test_grid_boundaries(self):
357361
if __name__ == "__main__":
358362
import unittest
359363
unittest.main()
364+
365+
if __name__ == "__main__":
366+
import unittest
367+
unittest.main()

tests/test_cases/stockAgent/test_single_agent_discrete_movement.cu

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ TEST(SingleAgentDiscreteMovementTest, Initialization) {
4646

4747
// Validation should pass now
4848
EXPECT_NO_THROW(move_submodel.validate());
49+
50+
// Check getName and getSubModelDescription
51+
EXPECT_EQ(move_submodel.getName(), "SingleAgentDiscreteMovement");
52+
EXPECT_NO_THROW(move_submodel.getSubModelDescription());
4953
}
5054

5155
/**
@@ -58,10 +62,10 @@ TEST(SingleAgentDiscreteMovementTest, SimpleMove) {
5862
int HEIGHT = 3;
5963

6064
SingleAgentDiscreteMovement move_submodel;
61-
auto smd = move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT);
65+
move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT);
6266

6367
// Submodels must be added to a layer to be executed during the simulation step
64-
model.newLayer().addSubModel(smd);
68+
model.newLayer().addSubModel(move_submodel.getSubModelDescription());
6569

6670
// Define parent agents with necessary variables
6771
auto agent = model.newAgent("agent");
@@ -135,8 +139,8 @@ TEST(SingleAgentDiscreteMovementTest, CollisionAvoidance) {
135139
int HEIGHT = 3;
136140

137141
SingleAgentDiscreteMovement move_submodel;
138-
auto smd = move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT);
139-
model.newLayer().addSubModel(smd);
142+
move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT);
143+
model.newLayer().addSubModel(move_submodel.getSubModelDescription());
140144

141145
auto agent = model.newAgent("agent");
142146
agent.newVariable<int>("x");
@@ -211,8 +215,8 @@ TEST(SingleAgentDiscreteMovementTest, ResourceMemory) {
211215
int HEIGHT = 3;
212216

213217
SingleAgentDiscreteMovement move_submodel;
214-
auto smd = move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT);
215-
model.newLayer().addSubModel(smd);
218+
move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT);
219+
model.newLayer().addSubModel(move_submodel.getSubModelDescription());
216220

217221
auto agent = model.newAgent("agent");
218222
agent.newVariable<int>("x");
@@ -276,8 +280,8 @@ TEST(SingleAgentDiscreteMovementTest, GridBoundaries) {
276280
int HEIGHT = 2;
277281

278282
SingleAgentDiscreteMovement move_submodel;
279-
auto smd = move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT);
280-
model.newLayer().addSubModel(smd);
283+
move_submodel.addSingleAgentDiscreteMovementSubmodel(model, WIDTH, HEIGHT);
284+
model.newLayer().addSubModel(move_submodel.getSubModelDescription());
281285

282286
auto agent = model.newAgent("agent");
283287
agent.newVariable<int>("x");

0 commit comments

Comments
 (0)