22 Identifying and formatting submodels
In the early stages of model development, it is common to work within a single .nlogo file. This approach keeps all code—setup, agent logic, data collection, and plotting—in one place, which is convenient for learning and experimentation. However, as models grow in complexity, maintaining a long monolithic file becomes difficult. To make large projects easier to navigate, debug, and extend, NetLogo allows modularisation through the __includes directive.
22.1 Why Modularise?
Breaking a model into smaller source files improves:
- Readability: Each file focuses on a specific function (e.g., setup, environment, agent behaviour, data collection).
- Reusability: Procedures can be shared across projects.
- Maintenance: Updating or replacing a subsystem (e.g., weather generation) doesn’t require scrolling through hundreds of lines.
- Collaboration: Multiple researchers can work on separate components concurrently.
These have great consequences for supporting transparent and replicable research. Instead of one opaque model file, a modular structure makes explicit where, for example, environmental data, agent rules, or calibration routines are defined—facilitating peer review, teaching, and long-term model reuse.
22.2 The __includes Directive
NetLogo allows a model to load additional source files (.nls, for NetLogo Source) at runtime. The syntax, placed at the top of your main .nlogo file, is:
__includes [
"modules/setup.nls"
"modules/agents.nls"
"modules/environment.nls"
"modules/data_collection.nls"
]
NOTE: Storing these files in a subdirectory, like ‘modules’, is not a requirement, but it will be preferable if the model directory contains anything besides a single
.nlogofile (e.g., input or output data files, documentation, etc).
Each included file may contain any NetLogo code (procedures, reporters, variable declarations). They could even correspond to a single element each (e.g., “setup.nls” containing only the setup procedure). The criteria for splitting those elements and naming the files is ultimately for you to decide. You should aim to have not too much nor too little code in files, but this is no golden rule and there are many instances where grouping or separating elements is more desirable. Using a combination of function (e.g., “setup”, “data collection”), object (e.g., “patches”, a given breed of agent) and topic (e.g., vegetation growth, movement, communication) can make good intermediate solutions. Remember the reasons for going modular and think in terms of what helps you pursuing them.
When the model (.nlogo file) loads, all included .nls files are merged into a single shared namespace, so procedures defined in one file are visible to all others. You may also consult and modify the source code directly in NetLogo since, once loaded, they will appear in the drop down list at the top of the “Code” tab.
22.3 Breaking Down The Pond Trade Model
To break down the Pond Trade model developed within one .nlogo file, we can refactor it into modules stored in a structured directory, for example:
PondTrade/
├── PondTrade.nlogo ; main file (interface + globals + includes)
└── modules/
├── main.nls ; stricly, setup and go procedures
├── map.nls ; Map and terrain generation
├── output.nls ; Plotting, displays, outputs, and computing model statistics
├── routes.nls ; Route calculation, pathfinding, and connectivity logic.
├── settlements.nls ; Creating and managing settlements
└── traders.nls ; trader creation, activation, movement, and trading logic
Inside the main model:
__includes [
"modules/PondTrade/main.nls"
"modules/PondTrade/map.nls"
"modules/PondTrade/settlements.nls"
"modules/PondTrade/routes.nls"
"modules/PondTrade/traders.nls"
"modules/PondTrade/output.nls"
]
Each .nls file then contains code that is more closely related, even though they are inevitably connect to others. For example, map.nls stores create-map and smooth-coast-line, but while smooth-coast-line is only called by create-map, create-map in turn is called by setup.
This structure makes it easier to focus on specific model components, understand or modify them, without being overwhelmed by the entire codebase.
22.4 Refactoring for a better modularity
Once we split our code into logical parts, we might already gain some useful insights about how to better respect modularity without changing the code behaviour. We have one such a case in map.nls.
Here is the call paths involving the two procedures in this file:
We can see that assign-path-cost has a somewhat ambiguous position: it is clearly related to route calculation, but it is actually called in map, inside create-map, not in another procedure in routes or directly in setup.
to create-map
...
smooth-coast-line
assign-path-cost
ask patches [ paint-terrain ]
end
This makes our code a “spaghetti code”. The refactoring measure that can solve this is straightforward: we move the call for assign-path-cost from create-map to setup. Since it was already positioned at the end of create-map, moving it immediately following the call for create-map in setup will not change the model behaviour. The cost is simply a slightly longer setup procedure.
to setup
clear-all
reset-ticks
; set the random seed so we can reproduce the same experiment
random-seed seed
set patchesCount count patches
create-map
assign-path-cost
create-coastal-settlements
set-routes
create-traders-per-settlement
update-output
update-display
update-plots
end
Refactoring is a process that never finishes. Do you see any other improvements to our implementation of the model so far? If so, please try them and if successful, create a pull request with your changes to our course-guide repository.
22.5 🧠 Activity
Refactor another model using __includes. The Artificial Anasazi model offers a good case, but feel free to try another model.
Here is short summary of the steps you should follow:
22.5.1 🧩 Step 1 — Create the Folder Structure
- Make a new directory.
- Inside it, create a
modulessubdirectory. - Copy your original model
.nlogofile.
newModelDirectory/
├── modelCopy.nlogo
└── modules/
22.5.2 ⚙️ Step 2 — Add the __includes Statement
At the very top of modelCopy.nlogo, add:
__includes [
"modules/setup.nls"
...
]
This tells NetLogo to merge all these modules into one program when the model is loaded.
22.5.3 ✂️ Step 3 — Split the Code
Now open modelCopy.nlogo and move procedures, one at a time, to new .nls files in modules/ (create these as txt files, then change the file extension; opening the entire folder in an IDE will make this easier). With the .nlogo file always open, save and check for error every time a module is separated.
Here are a few examples of modules:
| Type of Procedure | Move it to file | Example |
|---|---|---|
| World setup and initialization | setup.nls |
to setup |
| Agent behaviour (movement, decisions) | turtles.nls |
to go, to forage, to trade |
| Environment updates | environment.nls |
to update-water, to grow-resources |
| Trade and interaction logic | trade.nls |
to exchange-goods, to evaluate-partner |
| Data collection and plotting | data.nls |
to record-stats, to export-csv |
| Helper and math functions | utils.nls |
to-report clamp01 [x], to-report random-between [a b] |
Remember — you do not need to re-declare globals or interface variables in every file. All modules share the same namespace once included.
To go fully modular, the final version of the new .nlogo file should contain only the __includes directive and all breed and variable declarations.
22.5.4 🧪 Step 4 — Test the Modular Model
- Check that it runs exactly like the original version.
- If an error appears such as “Nothing named CLAMP01”, ensure that the file containing that procedure (
utils.nls) is listed in the__includes.