Netlogo: Nested if/ifelse statements - if-statement

Building my first ABM using Netlogo and have a problem involving ifelse statements and how to use them. I'm modelling agents response to flooded properties. The concepts are as follows:
If an agent is flooded, they will consider adopting protective measures (if they haven't already).
If an agent has adopted protective measures, and are flooded, the success of the measure is calculated.
My code is as follows:
to process-property
let $random-flood-number random-float 1
ask properties [
set flood-damage-list-consequent replace-item 1 flood-damage-list-consequent (item 1 flood-damage-list-initial * (1 - PLP-reduction))
set flood-damage-list-consequent replace-item 2 flood-damage-list-consequent (item 2 flood-damage-list-initial * (1 - PLP-reduction))'
ifelse $random-flood-number < probability-flooding
[
set flooded? TRUE
set number-of-times-flooded (number-of-times-flooded + 1)
if plp-adopted? != TRUE [
calculate-adoption-intention
]
]
[
set flooded? FALSE
]
]
ask properties with [plp-adopted? = TRUE] [
plp-reliability-analysis
]
end
to plp-reliability-analysis
if plp-abandoned? = TRUE [stop]
if flooded? = TRUE [
if number-of-times-flooded > 1 [
let plp-reliability-factor 0.77 ;;This variable represents the probability of success that Manual PLP will offer full reduction in damage. Taken from JBA (2012;2014).
ifelse random-float 1 < plp-reliability-factor
[
set plp-deployed-successful? TRUE
set PLP-reduction 0.25
set successful-flood-damage-reduction (sum flood-damage-list-initial * PLP-reduction)
]
[
set plp-deployed-successful? FALSE
set PLP-reduction 0.9
set unsuccessful-flood-damage-reduction (sum flood-damage-list-initial * PLP-reduction)
calculate-abandonment-intention
]
]
]
end
I have written the following code as an error check, which i keep getting:
if flooded? = FALSE and plp-deployed-successful? = TRUE [error["Properties should only deploy PLP when they are flooded"]]
I believe the problem lies in the ifelse statements in "plp-reliability-analysis" procedure. I'm new to Netlogo and am confused as to when to use an 'if' or 'ifelse' statement. If someone could explain and have a look at the above code i'd be very grateful.
Thanks,
David

The difference between if and ifelse is that:
if is used when you want to run some piece of code only under certain conditions
ifelse is used when you want to run some piece of code under some condition and a different piece of code if the condition is not met.
Have a look at this shortened version of your code. Note that I moved the opening bracket to the beginning of the line to line up the start and end of code blocks. I also put end bracket on the same line for very short code blocks, but the bracketing is the same as yours.
to process-property
let $random-flood-number random-float 1
ask properties
[ ifelse $random-flood-number < probability-flooding
[ set flooded? TRUE ]
[ set flooded? FALSE ]
]
ask properties with [plp-adopted? = TRUE]
[ plp-reliability-analysis
]
end
to plp-reliability-analysis
if flooded? = TRUE
[ if number-of-times-flooded > 1
[ let plp-reliability-factor 0.77
ifelse random-float 1 < plp-reliability-factor
[ set plp-deployed-successful? TRUE ]
[ set plp-deployed-successful? FALSE ]
]
]
end
You draw a random number and assign it to the variable $random-flood-number. Then you ask every property agent to compare that number to the value of probability-flooding. However, you never draw a new random number. So if it's true for one property, it will be true for all the properties. Given it is a flood model, that is presumably intentional as all houses are equally affected by flooding.
Imagine that a low number is drawn and they all get flooded. All the ones with plp-adopted? are then sent to the plp-reliability-analysis procedure. For all of them, the variable flooded? is true so the code block is run.
The first line is if number-of-times-flooded > 1. The first time a flood occurs, the number-of-times-flooded is changed from 0 to 1. That will fail the test (did you mean to use >= instead of > ?) and the remainder of the code will not be run. It will simply jump to the end bracket.
[ let plp-reliability-factor 0.77
ifelse random-float 1 < plp-reliability-factor
[ set plp-deployed-successful? TRUE ]
[ set plp-deployed-successful? FALSE ]
]
But for the second and later code, it will run and 77% of the properties will have the plp recorded as successful, and the others set to unsuccessful.
So, how do you end up with some properties having the combination of false flood? and true plp-deployed-successful?.
Now jump forward in time and 2 (or more) floods have occurred. A flood has just happened, so 77% of the properties with plp-adopted? have true plp-deployed-successful? This time, there is not a flood and all properties have flooded? set to false. Those with plp-adopted? are sent to the plp-reliability-analysis procedure. However, flooded? is now false so the code block does not run and they retain their values of plp-deployed-successful? from the previous run through.

Related

Let agents follow its neighbour but got the error "FACE expected input to be an agent but got NOBODY instead."

I try to move each agents to its related agents (defined by high trust connection and in my view) if there are any such agents, and else move the agent to one of its neighbours. However, I get the error "FACE expected input to be an agent but got NOBODY instead." and I don't know how to solve it. I think the use != nobody might cause the error. Any suggestions on how to solve this?
This is my code:
breed [ persons a-person ]
undirected-link-breed [ connections connection ]
connections-own [ trust ]
persons-own [ neighbours ]
to setup
clear-all
create-persons 10
[ setxy random-xcor random-ycor ]
setup-connections
reset-ticks
end
to setup-connections
ask persons
[
create-connections-with other persons
[ set trust 0.6 ]
]
end
to go
setNeighbours
movePersons
tick
end
to setNeighbours
ask persons [ set neighbours other persons in-cone 4 360 ]
end
to movePersons
ask persons
[
let highlyRelatedPersons (turtle-set [other-end] of my-out-connections with [trust = 0.6]) in-cone 4 360
let relatedPersons (turtle-set [other-end] of my-out-connections with [trust = 0.6])
ifelse any? highlyRelatedPersons
[
face one-of highlyRelatedPersons
]
[
let weaklyRelatedNeighbour one-of neighbours with [not member? self highlyRelatedPersons] ;select one of my neighbours that is not a highlyRelatedPerson
ifelse weaklyRelatedNeighbour != nobody
[
face weaklyRelatedNeighbour
]
[
face one-of relatedPersons
]
]
]
end
You are correct in what is causing the error
highlyRelatedPersons is an agentset and while an agentset can be empty, an agentset can never be nobody. one-of highlyRelatedPersons however, will be nobody if the agentset is empty, so you could use ifelse (one-of highlyRelatedPersons != nobody).
That said, is easier to just check if the agentset is empty by using any?:
ifelse any? highlyRelatedPersons
Your code also never actually creates any links for highlyRelatedPersons and relatedPersons is always empty. This means that the code will always return false on the first ifelse and move on to the second. Generally most turtles will have neighbours they can face, but sometimes there are none so they try to face one-of relatedPersons, which again causes a crash since relatedPersons is an empty agentset.

NetLogo sort list of agents by a specific value

I have a model with 5000+ farmers and several factories. Sometimes a new factory is built, at which point I want the factory to do the following:
Create a list with all farmers and then sort that list according to distance of farmer to the factory (from low to high).
I have tried doing this with a table,
ask factory 1 [ask farmers [set distance-to-factory distance myself]]
ask factory 1 [set a table:group-agents farmers [distance-to-factory]]
but then the resulting agentsets are not ordered from low to high or vice versa. Moreover, I want the factory to be able afterwards to ask single agents from the ordered table (or list) to do something:
After ordering the farmers by their distance-to-factory, I want the factory to be able to ask farmers from that list to deliver their goods (i.e. the closest farmer is asked first, but when it has no goods, the second closest farmer is asked and so on).
Your help is much appreciated!
You need to create an agent variable for the factory that stores the list of farmers in distance order. Here is a full example, run it and inspect a factory to convince yourself that it works.
breed [factories factory]
breed [farmers farmer]
factories-own [my-farmers]
to setup
clear-all
create-farmers 100
[ setxy random-xcor random-ycor
set color yellow
set shape "circle"
set size 0.5
]
create-factories 3
[ setxy random-xcor random-ycor
set color red
set shape "house"
set size 2
initialise-factory
]
reset-ticks
end
to initialise-factory
set my-farmers sort-on [distance myself] farmers
end
Look at the initialise-factory procedure. The sort-on primitive operating on an agentset returns a list. And the [distance myself] of ... is calculating the distance back to the factory (because the factory is doing the asking and is therefore myself). So the list is sorted by distance to the factory.
Once you have created the list, you use list procedures (eg the item primitive) to do the asking specific farmers.
There are indeed multiple factories and the distance-to-factory is to the most recently created factory, but the my-farmers list that each factory has doesn't change.
I already used the profiler extension on a previous version of the model and that one was very slow because of every factory asking each farmer every time (once a year) if they had goods:
let closest-farmer (min-one-of farmers with [status = 0] [distance myself])
That is why I thought of every factory creating a list of farmers from closest to furthest, so that factories can run through that list. Below you find a more elaborate piece of code, I hope this helps you to get a better image.
breed [factories factory]
breed [farmers farmer]
globals [
count-down
total-capacity-factories
price-goods
]
farmers-own [
area
goods-per-area
goods
distance-to-factory
status
]
factories-own [
my-farmers
goods-delivered
capacity
revenues-this-year
total-revenues
]
to setup
clear-all
create-farmers 1000 [
setxy random-xcor random-ycor
set area one-of [10 50 100]
set goods-per-area one-of [5 10 15]
set color yellow
set shape "circle"
set size 0.5
]
create-factories 20 [
setxy random-xcor random-ycor
set color red
set shape "house"
set size 2
initialise-factory
]
set market-demand-goods 250000
set total-capacity-factories sum [capacity] of factories
set count-down 11
reset-ticks
end
to go
if count-down = 11 [
change-market-demand
ask farmers [
set status 0
set goods 0
]
]
if count-down = 10 [
ask farmers [
sow-seeds
]
]
if count-down = 5 [
if market-demand-goods - [capacity] of one-of factories > total-capacity-factories [
build-factory
]
]
if count-down = 2 [
ask farmers [
harvest-goods
]
ask factories [
receive-harvested-goods
sell-goods
]
]
set count-down count-down - 1
if count-down = 0 [
set count-down 11
]
end
to initialise-factory
set capacity 10000
ask farmers [
set distance-to-factory distance myself
]
set my-farmers (sort-on [distance-to-factory] farmers)
end
to change-market-demand
let chance random 100
if chance < 33 [
set market-demand-goods market-demand-goods - 50000
]
if chance >= 33 and chance < 66 [
set market-demand-goods market-demand-goods + 10000
]
if chance >= 66 [
set market-demand-goods market-demand-goods + 50000
]
let chance2 random 100
if chance2 < 50 [
set price-goods price-goods + 1
]
if chance2 >= 50 [
set price-goods price-goods - 1
]
end
to sow-seeds
set color brown
end
to build-factory
loop [
if total-capacity-factories >= market-demand-goods [stop]
create-factory 1 [
setxy random-xcor random-ycor
set color red
set shape "house"
set size 2
initialise-factory
set total-capacity-factories (total-capacity-factories + [capacity] of myself
]
end
to harvest-goods
set goods area * goods-per-area
end
to receive-harvested-goods
let i 0
loop [
if goods-delivered >= capacity [stop]
let closest-farmer-with-goods (item i my-farmers)
ifelse [status] of closest-farmer-with-goods = 0 [
set goods-delivered + [goods] of closest-farmer-with-goods
ask closest-farmer-with-goods [
set status "goods-delivered"
]
]
[set i i + 1]
]
end
to sell-goods
set revenues-this-year goods-delivered * price-goods
set total-revenues total-revenues + revenues-this-year
end
#JenB, thanks for the help! I also found out that it can be done as follows:
hatch factory 1 [
ask farmers [set distance-to-factory distance myself]
set my-farmers (sort-on [distance-to-factory] farmers)
]
Then I designed the following code for asking from the my-farmers list:
let i 0
loop [
if goods-delivered > capacity [stop]
let closest-farmer-with-goods (item i my-farmers)
ifelse [status] of closest-farmer-with-goods = 0 [ (; aka goods are not delivered yet to another factory)
set goods-delivered + [goods] of closest-farmer-with-goods
]
[set i i + 1] ; *else*
]
But this makes the model quite slow. Do you know how to make this simpler?

Netlogo comparing list of global variables to numbers

I apologise in advance for how simple the answer probably is to this question, I am very new to netlogo and very out of my depth.
I am trying to read a water-temperature from a file and consequently get my turtles to die/breed depending on the temperature. I have got the file to read finally, and set water-temperature as a global variable, however I am now stuck on the comparison part. It won't let me compare the variable to a number because I think the variable is a list. The following error message comes up;
The > operator can only be used on two numbers, two strings, or two agents of the same type, but not on a list and a number.
error while turtle 7 running >
called by procedure REPRODUCE
called by procedure GO
called by Button 'go'
Code is below;
globals [ year
month
water-temperature ]
extensions [ csv ]
to setup
ca
load-data
create-turtles 50
[ set size 1
set color red
setxy random-xcor random-ycor ]
reset-ticks
end
to go
ask turtles [ move
reproduce ]
run-temperature
end
to load-data
file-close-all
file-open "C:\\Users\\Hannah\\Documents\\Summer research project\\test3.csv"
end
to run-temperature
file-close-all
file-open "C:\\Users\\Hannah\\Documents\\Summer research project\\test3.csv"
while [ not file-at-end? ] [
set water-temperature csv:from-row file-read-line
tick ]
file-close
end
to move
rt random 50
lt random 50
fd 1
end
to reproduce
if water-temperature > 35 [ die ]
if water-temperature > 30 and water-temperature < 34 [ hatch 1 rt random-float 360 fd 1 ]
if water-temperature > 25 and water-temperature < 29 [ hatch 2 rt random-float 360 fd 1 ]
if water-temperature > 20 and water-temperature < 24 [ hatch 3 rt random-float 360 fd 1 ]
end
I would be so grateful for any help!
Thanks :)
Hannah
welcome to Stack Overflow. Can you please provide an example of the first few lines of your "test3.csv" file? That will help get your question sorted- if you have a header or multiple columns that could be causing your problems- multiple columns might be getting read in as a list. As well, I think you want file-read instead of file-read-line.
A few other things- your load-data procedure is unnecessary as far as I can tell (you only need the loading to occur in run-temperature).
More importantly, your code right now says something like: "All turtles, move and reproduce. Now, read the whole temperature file line by line." The problem is that your while statement is saying "until you have yet reached the end of the file, read a line, tick, and move to the next one." Additionally, your model will tick once per line, without the turtles ever doing anything- it probably is simpler to just have your tick at the very end of your go procedure. It is likely better to avoid the use of while in your go procedure in this scenario, as it will loop until the while condition is satisfied.
It might be easier to just read your whole test.csv and store it in a variable for easier access- here is one example. Using this setup:
globals [
water-temperature
water-temperature-list
]
to setup
ca
crt 50 [
setxy random-xcor random-ycor
]
First, tell Netlogo water-temperature-list is a list using set and []. Then, do the same file close/open as before to prep your file for reading. Then, use a similar while loop to read your temperatures into water-temperature-list, using lput:
set water-temperature-list []
file-close-all
file-open "test3.csv"
while [ not file-at-end? ] [
set water-temperature-list lput file-read water-temperature-list
]
file-close-all
reset-ticks
end
Now your model more simply access those values, since they are stored in a model variable directly. You can easily use the ticks value with item as an index for that list- for example, on tick 0 the first element in the list will be accessed, on tick 1 the second element, and so on. For example:
to go
set water-temperature item ticks water-temperature-list
ask turtles [
if water-temperature > 30 [
die
]
if water-temperature <= 30 [
rt random 60
fd 1
]
]
tick
end
Note that with this setup once you get to the end of your temperatures, there will be an error telling you that Netlogo can't find the next list element- you'll have to put a stop condition somewhere to prevent that.
I know that is an alternative to your approach but I hope that it's helpful. For another similar but more complicated example, check out this model by Uri Wilensky.

IFELSE [Compare the length of two separate lists] [ACT]

Following my question yesterday, I was trying to implement the code with filter and length. I think I achieved the filter but now I need the agents to compare their list length with other agent (activities) list. Here is my code:
ask agentes [
set culture-tags n-values 17 [random 2]
set shared-culture (filter [ i -> i = 0 ] culture-tags) ;count 0s and
create shared-culture
]
end
to move-agentes
ask agentes [
let nearest-activity min-one-of (activities) [ distance myself ]
ifelse any? activities in-radius sight-radius with
[ length t-shared-culture = length shared-culture ] ;as many 0s in t-shared-culture (specified later in the setup for activities) as in shared-culture
[ face nearest-activity ]
[ rt random 40
lt random 40
fd 0.3
]
]
Netlogo prints the next error: "ACTIVITIES breed does not own variable SHARED-CULTURE error while activity 176 running SHARED-CULTURE".
I imagine the mistake must be here: [ length t-shared-culture = length shared-culture ] but I don't know how to make both lists to interact.

Netlogo : inequality doesn't work properly under foreach

It supposes to be a simple code, but I don't know why it doesn't work properly.
I want to change a color of non-white turtle back into white if a condition is fulfilled. I put inequality as the condition.
for example, if the amount of red turtle > = 5, then [ do something ].
No error message for the code, but I found that [ do something ] codes are executed before the condition is fulfilled. For example, it is executed when the amount of turtle is 1 or 4. And I also found that there are moments when it reach > = 5, [ do something] code is not executed.
Below is the code
to seize-value
ask consumers [set type-of-value ( list blue red green) ]
foreach type-of-value [
if count consumers with [color = ?] > = 5 [
let z consumers with [color = ?]
ask z [ set color white ]
ask consumers with [color = white] [set value? false]
ask one-of cocreation-patches [ sprout 1 [gen-prevalue]]
]]
end
I've tried using a single color, instead of a list of color (without - foreach) it doesn't work either.
Can anyone help with this?
Thank you
You have something like the following at the top of your code to set up type-of-value as an agent variable:
breed [ consumers consumer ]
consumers-own [ type-of-value ]
However, you are treating it as a global variable in your code. First you say ask consumers [set type-of-value ( list blue red green) ] to set the AGENT variable called type-of-value to the list of colours. But you end that ask [] statement before starting the foreach.
Unless the consumers have different lists of colours, what you really want is something more like this (untested). Note that I have also removed your multiple creations of the same agentset (for efficiency):
globals [ type-of-value ]
to setup
clear-all
...
set type-of-value ( list blue red green)
...
reset-ticks
end
to seize-value
*type "seize-value on tick " print ticks
foreach type-of-value
[ let changers consumers with [color = ?]
*print ?
*print count changers
if count changers >= 5
[ ask changers
[ set color white
set value? false
]
ask one-of cocreation-patches [ sprout 1 [gen-prevalue] ]
]
]
end
UPDATE for debugging I have added three lines that will output key information for debugging. They are marked with an asterisk (*). Add those lines (without the *) and look at the output.