Netlogo calculate all turtles of a type that are touching - while-loop

In Netlogo, I have a grid of turtles all touching, and I want to count the number of green turtles (MHC = 3 in code below) that form each cluster of green turtles (amid the other colours). Perhaps I am going about it all wrong, but it seems very very hard.
I have tried while loops designed to start with a single green cell (unconnected to any previous green cluster) and assign a number to its turtles-own variable block. Then each green neighbor in-radius 1 receives the same number, and so on and so on, until each touching green cell receives the same number. Then the next cluster will receive a new number and start over.
However, unless it's just a question of bad bracketing, it really doesn't seem to work. Here is the functional code (which just creates the grid of colour changing turtles):
turtles-own[MHC block]
globals[prWound]
to set-up
clear-all
reset-ticks
ask patches [sprout 1 [set color magenta]]
ask turtles [set MHC 2]
set prWound 0.0001
end
to rules
ask turtles with [MHC = 0][set color red]
ask turtles with [MHC = 1][set color green]
ask turtles with [MHC = 2][set color magenta]
ask turtles with [MHC = 3][set color blue]
ask turtles with [MHC = 4][set color orange]
ask turtles [if random 100 < 1 [set MHC (random 5)] ;vary MHC betwen 0-4,
set block 0
if random-float 1 < prWound [ask turtles in-radius 4 [die] die]
if any? patches in-radius 1 with [not any? turtles-here] and random 100 < 50 [if random 100 < 2.5
[set MHC (random 5)] hatch 1 [move-to one-of patches in-radius 1 with [not any? turtles-here]
]
tick
end
to go
rules
end
Here is the part where I try to add block values that I cannot get to work (added just before the tick):
ask turtles with [MHC = 1][
if block = 0 [set block (max([block] of turtles) + 1) ]
while [any? [turtles with [MHC = 1 and block = 0] in-radius 1] of turtles with [block = [block] of myself]]
[if any? [turtles with [MHC = 1 and block = 0] in-radius 1] of turtles with [block = [block] of myself]
[set block ([block] of myself)]]
]
I think the in-radius might be at least one of the problems - I am not sure it can be used this way.

Update: simpler approach
I leave my initial reply below unchanged, however see that a much simpler approach can be taken:
to count-blocks
set block-now 0
ask turtles [set block 0]
while [any? turtles with [condition]] [
set block-now block-now + 1
ask one-of turtles with [condition] [
join-and-search
]
]
end
to join-and-search
set block block-now
if any? (turtles-on neighbors) with [condition] [
ask (turtles-on neighbors) with [condition] [
join-and-search
]
]
end
to-report condition
ifelse (color = green and block = 0)
[report TRUE]
[report FALSE]
end
Note that, although while is used only once in this case, to join-and-search in actual fact creates a loop by calling itself, with the recursive call being performed only if any? (turtles on neighbor) with [condition] exist; which makes the candidate? passage (i.e. becoming a candidate, recruiting candidates, stop being a candidate) not required here.
I think that just a warning is due in this case: I don't know if it is best practice to let a procedure call itself. On the one hand, it sounds like something worth flagging; on the other hand, it seems to me that this join-and-search could not be any more problematic than any other loop built with a weird condition.
Initial reply
While trying to solve this, I myself discovered something that I did not consider about in-radius, and surely there lies part of the problem.
Before disclosing that, however, let me say that I am not sure if this in-radius-thing is everything that was wrong with your attempt: by the time I found out about it, I had already taken my approach to the problem.
In general, however, one piece of advice: keep your code as tidy and as readable as possible (including indentation) - it becomes a lot easier to spot where a problem lies.
That said, the main elements of my approach:
Two while loops: the first one checks whether there are any eligible turtles in the whole simulation that will initiate a new block; the second one (nested within the first one) checks whether there are any turtles left to be allocated to the current block being evaluated.
A candidate? turtles-own variable, which constitutes the condition for the second while loop. When being allocated to a block, each turtle also performs a search of its neighbours. If there are any turtles that should be added to the current block, then these get candidate? = TRUE and the inner loop starts again.
Also, I've split the relatively few commands in many procedures with relevant names. This makes both the code more readable but also more scalable: when you're going to expand on the model's variables, agentsets, conditions etc it will be easier to add line of codes to allocated sections and check whether a particular section is working on its own.
to-report condition and the global variable block-now exist mainly for readability.
As of now, this code re-counts blocks at every go (and blocks may change number between one iteration of go and the other). It will surely be possible to adapt the approach to the case in which you want to keep blocks' numbers across go iterations.
globals [
prWound
block-now
]
turtles-own [
MHC
block
candidate?
]
to setup
clear-all
reset-ticks
ask patches [sprout 1 [set color magenta]]
ask turtles [set MHC 2]
set prWound 0.0001
end
to go
rules
count-blocks
tick
end
to rules
ask turtles with [MHC = 0][set color red]
ask turtles with [MHC = 1][set color green]
ask turtles with [MHC = 2][set color magenta]
ask turtles with [MHC = 3][set color blue]
ask turtles with [MHC = 4][set color orange]
ask turtles [
if random 100 < 1 [set MHC (random 5)]
set block 0
if random-float 1 < prWound [ask turtles in-radius 4 [die] die]
if any? patches in-radius 1 with [not any? turtles-here] and random 100 < 50 [
if random 100 < 2.5 [
set MHC random 5
]
hatch 1 [move-to one-of patches in-radius 1 with [not any? turtles-here]]
]
]
end
to count-blocks
set block-now 0
ask turtles [
set block 0
]
while [any? turtles with [condition]] [start-count-round]
end
to start-count-round
set block-now (block-now + 1)
ask turtles [
set candidate? FALSE
]
ask one-of turtles with [condition] [set candidate? TRUE]
while [any? turtles with [candidate?]] [
ask turtles with [candidate?] [
join
search
conclude
]
]
end
to join
set block block-now
end
to search
let target (turtles-on neighbors) with [condition and not candidate?]
ask target [set candidate? TRUE]
end
to conclude
set candidate? FALSE
end
to-report condition
ifelse (color = green and block = 0)
[report TRUE]
[report FALSE]
end
Before
Click to see image
After
Click to see image
What about in-radius?
While it may seem intuitive that a turtle looking for turtles in-radius 1 will find the turtles standing on any of the immediate neighboring patches, that is not the case: the input number for in-radius is in fact a distance - i.e. not the number of patches that need to be crossed.
In terms of distance, turtles standing on horizontally- or vertically-neighboring patches will be at a distance of 1. Instead, turtles standing on diagonally-neighboring patches will be at a distance of 1.4:
observer> clear-all
observer> ask patch 0 0 [sprout 1]
observer> ask patch 0 1 [sprout 1]
observer> ask patch 1 1 [sprout 1]
observer> ask turtle 0 [show distance turtle 1]
(turtle 0): 1
observer> ask turtle 0 [show distance turtle 2]
(turtle 0): 1.4142135623730951
This is the reason why not even my approach was working until I replaced let target turtles in-radius 1 with [condition and not candidate?] with let target (turtles-on neighbors) with [condition and not candidate?].
Note that you use in-radius twice in the first chunk of code that you shared. While in one case that is just patches in-radius 1, which you can replace with neighbors, in another instance it is turtles in-radius 4. You might want to consider the effect of that distance thing in the latter case.
A final note on the code
Just to make sure: are you sure that the order of things in to rules is what you want? For how things are now, turtles change their MHC value but only change colour in the subsequent round of go (but, by that time, they will have changed MHC again).

Related

netlogo - trouble with exiting while loop when condition is false

I have a list of ordered patches x, and I ask each of these patches to find the patches within a certain radius of themselves (this resulting list is called x_radius). I basically want to iterate through each x and then the corresponding patches in x_radius (taking the first item in x and then taking that item's first item in x_radius etc) and check if a patch variable food for a given x_radius patch is equal to or less than .5 (here I have it set to a random number for simplicity). If it is greater than .5, I would like to stop the loop. So, what I am thinking the order of commands to achieve this should be is:
Ask 1st patch in x to find patches in radius
Ask 1st patch in x_radius of the 1st patch in x to find their food amount
If their food amount is <= .5, ask the 2nd patch in x_radius of the 1st patch in x to find their food
Repeat until any patch in any x_radius of any x patch is > .5
This is a simplified bit of the code I am working on:
foreach x [p ->
ask p [
set x_radius patches in-radius 5
while [all? x_radius [food <= .5]] [
foreach x_radius [n ->
ask n [
set food random-float 1.0]
]
]
]
I see that the while loop is not exiting when the one of the x_radius patches to be evaluated has food. The code basically goes through all of the x patches without evaluating the x_radius patches, so somewhere my logic is messy. I've always had some trouble with while loops; I have a feeling it is because food is set within the while loop, but I am referencing it before? Does anyone see where I am going wrong?
UPDATE: The following code gets me closer to what I want, however, if an x_radius patch has a negative food value, the code keeps evaluating that same patch over and over again, when I would want to move on to the next x_radius patch. Do-calculations is a dummy function that calculates some value:
foreach x [p ->
ask p [
set x_radius patches in-radius 5
set food 0
foreach sort x_radius [n ->
while [food <= .5] [
ask n [
set food do-calculations]
]
]
]
]
]

Add variables from two breeds Netlogo

I'm trying to intoxicate bees by being near the flower. Toxicity is a flower-own variable, however, I want to add the number of said toxicity to the established mortality for the bees. I get the error that toxicity is not a bees variable, which is true, but how do I add this. The idea is that the flower has a set toxicity and if someone is near it gets intoxicated making them die faster. Thanks in advance, the code should work copy past.
breed [flowers flower]
breed [bees bee]
turtles-own
[
mortality
tipo_exposicion
t_supervivencia
]
flowers-own [toxicity]
to setup
clear-all
create-flowers 5
[
set shape "flower"
set color red
set size 4
move-to one-of patches
]
ask patches [set pcolor 63]
create-bees 100
[
setxy random-xcor random-ycor
]
ask bees
[
set shape "person"
set size 1
set color yellow
set t_supervivencia 0
]
ask flowers [set color gray]
reset-ticks
end
to go
ask bees [
move-bees
]
ask bees[intoxicarse]
ask flowers [ morir_bees_eating]
tick
end
to move-bees
right random 60
forward 1
set t_supervivencia t_supervivencia + 1
set mortality mortality + 1
end
to intoxicarse
ask bees in-radius 10 [if any? flowers with [color = gray][
set mortality mortality + toxicity]]
end
to morir_bees_eating
if color = gray [set toxicity 34]
end
Let me first "translate" what the intoxicarse procedure is doing right now:
It is called for any bee. So lets say first it calls the procedure on bee 1. Then, it looks for bees within a radius of 10 (ask bees in-radius 10). For every close bee, it will search in the set of all flowers (not just the close ones) if there are any gray ones. If there are any flowers in the world with gray color, all bees that are close to bee 1 would increase their mortality.
Here's a suggestion on how you could change your code:
create an agentset with let, that contains all close flowers with gray color. You can combine in-radius and with.
if there are close flowers (any?) then you can get their toxicity with of. One point, that isn't clear to me is, what happens, when there are more than one close flower. Should their toxicity sum up? Or should only one of the close flowers intoxicate the bee? You can use sum or one-of.
let close_flowers flowers in-radius 10 with [color = gray]
if any? close_flowers
[
let this_toxicity [toxicity] of one-of close_flowers ;get toxicity from one close flower
let this_toxicity sum [toxicity] of close_flowers ;sum of toxicity
set mortality mortality + this_toxicity
]
I would have the flowers call intoxicarse and write it more simply as
to intoxicarse
ask bees in-radius 10 [set mortality mortality + toxicity]
end

Netlogo gis extension: How to set world size to give specific scale to patches

I'm developing a epidemiological model using GIS data of a small town. One of the submodels in my model is about "infections": I need that an infected agent has a certain probability to infect other agents which are on his same patch.
In order to properly model this fact, I need my patches to have a specific area, for example 100 square meters. There is a way in which I can set the world size so that I am sure of the exact area a single patch is representing?
Thank you very much.
First of all, you might check the stackoverflow guide to asking questions. Having a minimal reproducible example also helps. Following the guidelines of Stackoverflow helps our community ;)
The way you define the patch scale with the GIS extensions is indeed not very clear. A good tutorial is available in Chapter 6 of this book.
First, have a raster file (e.g. .asc) with a defined resolution (e.g. 10 x 10 m). You can take a look on how to do this in QGIS and other GIS softwares. Make sure to export it to .asc and reproject it to your target SRC, otherwise you might fall in this problem.
Here's a simple code for you.
patches-own [ infectability ]
to setup-patches
; define a rasterfile:
set rastermap gis:load-dataset "C:/folder/yourfile.asc"
; define SRC:
gis:load-coordinate-system "C:/folder/yourfile.prj"
; make each raster cell = patch in NetLogo
let width floor (gis:width-of rastermap / 2)
let height floor (gis:height-of rastermap / 2)
resize-world (-1 * width ) width (-1 * height ) height
; define your patch size in pixels (makes your world size bigger/smaller in the Interface):
set-patch-size 1
; define world boundaries:
gis:set-world-envelope gis:envelope-of rastermap
; apply the raster data to your patches:
gis:apply-raster rastermap infectability
; make your patches look dangerous:
ask patches with [ infectability > 0.8 ] [ set pcolor red ]
end
After that, you will have to use some procedures making turtles ask patches to access the patch variable infectability. Good luck! ;)

Netlogo - Id like to set the size of a turtle as the size of the patch it's standing on

Id like to set the size of a turtle as the size of the patch it's standing on.
Even better I need turtles which are bigger as 4 or 16 patches.
If for example i have a squared world with 16x16 patches id like to have turtles that can be big 1x1 or 2x2 or 4x4 etc....
and the turtle should overlap perfectly the patches: it might be 1 patch (1x1 case), 4 (2x2 case) etc...
abott setting the size of the turtle equal to the sie of the patch for perfect overlapping in trying wit this code:
hatch-turtle 1 [set size [size] of patch-here ]
but it gives me the error:
A patch can't access a turtle variable without specifying which turtle.
Maybe try some variation of:
ask turtles [ set size patch-size ]
perhaps scaling by a multiplier as needed. Note that size is a per-turtle variable, but patch-size is a global reporter, because all patches are always the same size in pixels.
Note that size is measured in patches, while patch-size is measured in pixels.
I really don't understand at all what you're trying to do here, but the above is legal NetLogo code, anyway.
A turtle's size is measured in units of patches, so if you want your turtles to be the same size as the patches they are standing on, that's:
ask turtles [ set size 1 ]
but 1 is the default size, so in order to get this behavior, you actually don't need to do anything at all.
This answer comes years after the question was asked, but I leave it here hoping that it helps others who may encounter the same problem (as I did). Below I first clarify the problem and then offer a solution.
Clarification: It is implied by the problem that OP has defined a square shape for the turtles. The default size of square turtles in NetLogo is 1, which means that by default a square turtle should completely fill a patch. However, OP still observed blank space between square turtles that are placed next to each other. The aim of this answer is to remove that blank space for square turtles of size 1.
Solution: To solve this problem, note that the default square shape of turtles in NetLogo is made up of a colored inner area and a thick colorless border. The blank space that the OP observed between the turtles was in fact composed of the colorless borders of square shapes. In order to produce a figure with colored squares placed immediately adjacent to each other (that is, without any apparent space between them), it suffices to define a new square shape with no border. This new square shape should be defined such that the inner area of the square fills the entire patch. This can be done using the Turtle Shapes Editor from the Tools menu: find the square shape, create a duplicate of it, and modify the new shape in the graphical editor. To modify the shape, click on its top-left corner and drag that corner to the top-left corner of the graphical editor window. Then do the same with the bottom-right corner.

Netlogo terrain creation and diffusion

I need some help setting up a particular terrain. I have a world that is 200x200 patches and each patch has a size of 2 pixels. What I am trying to do is to make a hill starting at the origin, and then have the altitude evenly spread out to the edges of the world.
The origin would have around the highest altitude: 999, and patches around the edges would have the altitudes closes to 0. From the edges of the world, the altitude should constantly increase, until it gets to the origin However, I can't seem to get the hill to extend out to the edges of the world - there is a little bump in the middle, and the rest of the world is completely flat.
Can anyone help on setting up the terrain and explain how I can get the altitude to diffuse properly?
Here's the code I have so far:
patches-own [altitude]
to setup
clear-all
ask patch 0 0 [set altitude 1.0]
repeat 100 [diffuse altitude 0.25] ;; this needs to be changed?
scale-patches
color-patches
end
to scale-patches
let low [altitude] of min-one-of patches [altitude] ;; altitude of the lowest patch
let high [altitude] of max-one-of patches [altitude] ;; altitude of the highest patch
let range high - low ; difference from lowest to highest
ask patches [
set altitude altitude - low ; Shift every patch down so lowest altitude is 0
set altitude altitude * 999.0 / range ; Scale every patch so that the lowest is 0 and highest is 999
]
end
to color-patches
ask patches [set pcolor scale-color green altitude 0 1000]
end
How about replacing these two lines:
ask patch 0 0 [set altitude 1.0]
repeat 100 [diffuse altitude 0.25] ;; this needs to be changed?
with this:
ask patches [ set altitude world-width - distance patch 0 0 ]
It doesn't use diffusion, but maybe it solves your problem anyway?