Deforestation in Brazil
Model was written in NetLogo 6.2.0
•
Viewed 379 times
•
Downloaded 21 times
•
Run 0 times
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
Comments and Questions
Please start the discussion about this model!
(You'll first need to log in.)
Click to Run Model
; NOTES ; [1] When running Behavior Space experiments, it is recommended that 'setup.BS' be used as setup command (rather than the normal 'setup'). For ; an explanation, see comment to the 'to setup.BS' procedure. ; [2] The declaration of every procedure is followed by a notation stating which context (observer, turtles, patches) it belongs to. ; [3] Documentation for the 'numanal' extension is available in the extension's installation folder. Such folder can generally be found ; in the subfolder: ; Library/Application Support/NetLogo (on Mac OS X) ; AppData\Roaming\NetLogo\6.2\extensions (on Windows) ; .netlogo (on Linux) ; For more information on where to find NetLogo extensions, see: https://ccl.northwestern.edu/netlogo/docs/extensions.html#where-extensions-are-located. ; Tip for Windows: a quick way to access the AppData folder (which might be hidden) is to type %appdata% in your File Explorer search bar. ; ; A NOTE ON COMPARING BEAN PRICES AND COFFEE PRICES ; Bean prices in Brazil: https://www.tridge.com/intelligences/common-bean/BR ; Coffee prices in Brazil: https://www.tridge.com/intelligences/coffee-bean/BR ; Producer prices can also be found from the FAO: http://www.fao.org/faostat/en/#data/PP ; These sources suggest that the ratio of the price per kg of coffee to that of beans may range from 0.15 to 8.5. ; 0.15 comes from comparing the cheapest coffee price per kg found to the most expensive bean price. ; 8.5 comes from comparing the most expensive coffee price per kg found to the least expensive bean price ; The (2016) FAO producer price data puts the ratio at 1.85 (using 'beans' and 'coffee, green') ; Bean are mainly consumed domestically and prices at farmgate are volatile ; In our case, we are assuming a focus on coffee exports. extensions [numanal] ; ****************************************************************************************************************************************** ; *********************************************************** CREATING VARIABLES *********************************************************** ; ****************************************************************************************************************************************** globals [ ; The following globals are set once at the beginning of the run ('to set.globals') and are never changed. ha.to.patch ; [number] Each patch is 900 sqm. Multiplying ha-based values by this variable gives the corresponding patch-based values. farm.hrs.day ; [hours/day] Hours spent on farm per day. beans.labour ; [person-days/ha/yr] Amount of work needed for one hectare of beans, as performed by one adult in one year. beans.labour.patch ; [person-days/patch/yr] Amount of work needed for one patch of beans, as performed by one adult in one year. beans.yield ; [kg/ha/yr] Yearly yield of beans on one hectare with full fertility. beans.yield.patch ; [kg/patch/year] Yearly yield of beans on one patch with full fertility. beans.kcal ; [kcal/kg] Kilocalories from 1kg of beans. beans.kcal.patch ; [kcal/patch/yr] Kilocalories yielded by each patch planted in beans with full fertility in one year. corn.labour ; [person-days/ha/yr] Amount of work needed for one hectare of corn, as performed by one adult in one year. corn.labour.patch ; [person-days/patch/yr] Amount of work needed for one patch of corn, as performed by one adult in one year. corn.yield ; [kg/ha/yr] Yearly yield of corn on one hectare with full fertility. corn.yield.patch corn.kcal ; [kcal/kg] Kilocalories from 1kg of corn. corn.kcal.patch ; [kcal/patch/yr] Kilocalories yielded by each patch planted in corn with full fertility in one year. coffee.labour ; [person-days/ha/yr] Amount of work needed for one hectare of coffee, as performed by one adult in one year. coffee.labour.patch ; [person-days/patch/yr] Amount of work needed for one patch of coffee, as performed by one adult in one year. coffee.yield ; [kg/ha/yr] Yearly yield of coffee on one hectare with full fertility. coffee.yield.patch ; [kg/patch/yr] Yearly yield of coffee on one patch with full fertility. coffee.kcal ; [equivalent of kcal/kg] Equivalent of kilocaries from 1kg of coffee, as calculated according to conversion to beans. coffee.kcal.patch ; [equivalent of kcal/patch/year] Equivalent of kilocalories yielded by each patch planted in coffee with full fertility in one year, as calculated according to conversion to beans. coffee.exchange.price ; [USD] Foreign currency earned through export of coffee produced in the model since the start. price.adjustment ; As opposed to the previous ones, the following globals are updated once a year (i.e. once per 'to go'). households.entered ; [number] The number of households that has entered the model since the start. households.escaped ; [number] The number of households that has escaped the model since the start, as a result of not being able to fulfil conditions. coffee.produced.overall ; [kg] Kilograms of coffee produced in the model since the start. foreign.currency.from.coffee.export.tot ; [USD] ; While all the globals above are initialised in 'to set.globals' (including 'coffee.produced.overall' and 'coffee.exchange.price', which are included there only for tidyness purposes), ; these two variables below are initialised in 'to setup', each for a different reason: see comments below. behavior.space.run? ; This variable is initialised at each setup because it identifies whether the run is happening in Behavior Space ('behavior.space.run? = TRUE') or in the ; usual NetLogo interface ('behavior.space.run? = FALSE'). Checking for this variable allows to activate/deactivate the implementation of the model's visuals. ; If the run is happening in NetLogo, the visual representations are turned off - this saves time and memory, especially considering the number of pathces. control ; This variable is used to manage the loading time (especially in Behavior Space) reulting from the process of importing the initial map from an external file. ; The variable is initialised ('control = 1') by the end of the setup procedure, signalling that the 'to import.map' procedure has taken place and that there ; is no need to perform it again. Infact the imported data (mainly patches-own variables) is never automatically deleted from the model, unless either 'clear-all' ; or 'clear-patches' are manually invoked. It is exactly for this purpose that 'clear-all' is not used in the model (see custom procedure 'to clear.selected.fields' ; instead), and all non-imported patches-own variables are reset separately at the beginning of each run (via the 'to initial.map' procedure, called by 'to setup'). ; This is the recommended approach found in the NetLogo website - see https://ccl.northwestern.edu/netlogo/docs/behaviorspace.html (-> "Run options: parallel runs). ] patches-own [ ; The first group of four patches-own variables represent those that are imported via 'to import-world' (called by 'to import.map'). These variables are only imported when 'control = 0', thus at ; the beginning of the first run, and are never reset across runs unless something explicitely clears them (e.g. 'clear-all' or 'clear-patches'). Given this role that they have, they are never ; set nor changed in the code - apart from landcover in patches where 'landcover = 1' (i.e. patches with forest). In fact the initial possible values or landcover are 1, 2, 3 and 6 (see comment ; below to see what they mean). While landcovers 2, 3 and 6 never change, landcover 1 changes if it is deforested. Over time, patches whose initial landcove was 1 might take landcover values ; of 4, 5 or 0. This is the reason why, to restore the initial map, at every setup patches with those landcovers are brought back to have 'landcover = 1' (see 'to set.initial.map'). landcover ; [index] A variable showing the current land cover. Values: 0 = abandoned, 1 = forest, 2 = water, 3 = basin, 4 = crop, 5 = pasture, 6 = road. map1.lot ; [number] A variable identifying the lot number to which the patch belongs to in the 'baseline' map. map2.lot ; [number] A variable identifying the lot number to which the patch belongs to in the 'wetlands' map. direction ; [abbreviation] A variable identifying the direction of the lot from the access road. Values: "n" = North, "s" = South, 0 = none (the patch is not in a lot). ; As opposed to the previous ones, the following patch variables change everytime. Given that 'clear-patches' is never called in the model, these are manually reset at every setup via ; the 'to set.initial.map' procedure. lot ; [number] A variable identifying the lot number to which a patch belongs. owner ; [agent] The household to which a patch (and all the other patches from the same lot) belongs. which.crop ; [index] A variable showing the current crop. Values: 0 = no crop, 1 = beans, 2 = corn, 3 = coffee. fertility ; [percentage] The level of fertility directly affecting yield on land. Min: 0 (0%); Max: 1 (100%). fertility.impact ; [percentage] The relative impact of fertility on output. kcal.yielded ; [kcal/year] The kcalories that the patch is yielling per year according to the planted crop and the level of fertility. workforce.trapped ; [person-days/year] The amount of workforce engaged on this patch while this is planted with some crop. abandoned ; [index] A number showing the abandonment status of the patch. If 'abandoned > 0', the patch has been abandoned. The value increases every year, until the patch become pasture. coffee.produced ; [kgal/year] Kilograms of coffee produced by the patch in the current year. ] turtles-own [ ; Most of the turtles-own variables are set in the 'to create.families' procedure (via procedures called from within there). Exceptions are: 'my.lot.available' (can be changed during the simulation); ; 'workforce.available' and 'kcal.left' (set and updated during 'to calculate.ex-ante'); 'plant.beans', 'plant.corn', 'plant.coffee', 'illegal?' and 'leaving?' (set and/or updated during 'to plan'). behaviour ; [string] A string identifying the type of behaviour held by the household, as determined by the user in the Interface. my.lot ; [agentset] The full patch agentset (i.e. group of patches) constituiting this households' lot. my.lot.f ; [agentset] That part of the lot whose landcover is forest. my.lot.available ; [agentset] That part of 'my.lot.f' that is available for deforestation according to the level of forest protection and to the propensity to deforest illegally. members ; [count] The quantity of members in the household. females ; [count] The quantity of adult females in the household. males ; [count] The quantity of adult males in the household. children ; [count] The quantity of children in the household. elderly ; [count] The quantity of elderly in the household.. tot.workforce ; [person-days/yr] The amount of workforce overall usable by the household, as person-days per year. workforce.available ; [person-days/yr] The amount of workforce currently free in the household (overall workforce minus trapped workforce) tot.kcal.required ; [kcal/yr] The yearly kcalories requirement of the household, based on its demographics. kcal.left ; [kcal/yr] The kcalories that the household needs to find, in addition of those already obtained from current crops, to fulfil the yearly requirement. plant.beans ; [count] The number of patches that the household intends to plant with beans - according to behaviour, land available, workforce available and kcalories required. plant.corn ; [count] The number of patches that the household intends to plant with corn - according to behaviour, land available, workforce available and kcalories required. plant.coffee ; [count] The number of patches that the household intends to plant with coffee - according to behaviour, land available, workforce available and kcalories required. my.solution ; [kcal] The solution of the planning algorithym for what concerns the kcalories produced by the household. my.preference ; [percentage] If the household has a preference for corn, this is the minimum share it will want to have planted with corn (0.25, 0.50 or 0.75). direction.t ; [abbreviation] A variable identifying the direction of the lot belonging to this household. Values: "n" = North, "s" = South. illegal? ; [boolean] Whether the household would deforest beyond the legal limit. This variable is determined once per household. Before being determined, the value is 0. leaving? ; [boolean] Whether the household will leave the simulation at the end of the year. Defaults to 'FALSE'. ] ; ****************************************************************************************************************************************** ; ************************************************************ SETUP PROCEDURES ************************************************************ ; ****************************************************************************************************************************************** to setup ; [OBSERVER CONTEXT] ; The first block of commands checks if 'control = 0'. The 'control' variable is used to reduce loading time in Behavior Space: it is set to ; 1 at the end of the very first run, and is never brought back to 0. This signals that only at the beginning of the very first run the model ; will need to perform 'import-world' (called by 'import.map') to import the patch-related data, which is a time-consuming process. For all ; subsequent runs (i.e. when 'control != 0') the second command is exectued. This avoids using 'clear-all', which would delete the imported ; data and bring 'control' back to 0. Instead, all the things that should not be kept (e.g. ticks, turtles, plots etc) are deleted separately; ; The desired values of globals and patches-own variables are set to their desired starting level at the end of the run (those procedures are ; called from within the 'to go' procedure, see 'to reset.globals' and 'to reset.patches'. clear.selected.fields ; These four globals below need to be reset everytime setup occurs (unlike the ones that are set with 'set.globals' below, which never change ; and therefore are only set on the first setup, i.e. when control = 0). set households.entered 0 set households.escaped 0 set coffee.produced.overall 0 set foreign.currency.from.coffee.export.tot 0 if control = 0 [ import.map set.globals ] set.initial.map set control 1 set behavior.space.run? FALSE end to setup.BS ; [OBSERVER CONTEXT] ; It is advised that this is used as setup command for Behavior Space experiments. The 'behavior.space.run?' condition is checked everytime that ; a modification to the map (e.g. patches colours) might be performed. If 'behavior.space.run? = TRUE', the map modification is not performed. ; With 116466 patches making up the map, this saves a considerable amount of time and memory. setup set behavior.space.run? TRUE end to clear.selected.fields ; [OBSERVER CONTEXT] clear-ticks reset-ticks clear-turtles clear-drawing clear-all-plots clear-output end to import.map ; [OBSERVER CONTEXT] set-patch-size 1.05 import-world "netlogomap" end to set.globals ; [OBSERVER CONTEXT] ; For each variable, the unit of measure is reported in square brakets. set ha.to.patch 0.09 ; [fraction] set farm.hrs.day 8 ; [hours] Hours spent on farm per day. set beans.labour 683 / farm.hrs.day ; [person-days/ha/yr] Amount of work needed for one hectare of beans, as performed by one adult in one year. Source for 683: Ibarrola-Rivas et al. (2016). set beans.labour.patch (beans.labour * ha.to.patch) ; [person-days/patch/yr] set beans.yield 300 ; [kg/ha/yr] Yearly yield of beans on one hectare of healthy soil. Source: Ibarrola-Rivas et al. (2016) set beans.yield.patch (beans.yield * ha.to.patch) ; [kg/patch/yr] set beans.kcal 180 * 10 ; [kcal/kg] Kilocalories from 1kg of beans. Source: https://fdc.nal.usda.gov/fdc-app.html#/food-details/1100374/nutrients. set beans.kcal.patch (beans.kcal * beans.yield * ha.to.patch) ; [kcal/patch/yr] set corn.labour 383 / farm.hrs.day ; [person-days/ha/yr] Amount of work needed for one hectare of corn, as performed by one adult in one year. Source for 383: Ibarrola-Rivas et al. (2016). set corn.labour.patch (corn.labour * ha.to.patch) ; [person-days/patch/yr] set corn.yield 226 ; [kg/ha/yr] Yearly yield of corn on one hectare of healthy soil. Source: Ibarrola-Rivas et al. (2016) set corn.yield.patch (corn.yield * ha.to.patch) ; [kg/patch/yr] set corn.kcal 86 * 10 ; [kcal/kg] Kilocalories from 1kg of corn. Source: https://fdc.nal.usda.gov/fdc-app.html#/food-details/1103351/nutrients. set corn.kcal.patch (corn.kcal * corn.yield * ha.to.patch) ; [kcal/patch/yr] set coffee.labour 114 ; [person-days/ha/yr] Amount of work needed for one hectare of coffee, as performed by one adult in one year. Source: de Souza et al. (2012). set coffee.labour.patch (coffee.labour * ha.to.patch) ; [person-days/patch/yr] set coffee.yield 540 ; [kg/ha/yr] Yearly yield of coffee on one hectare with full fertility. Source: Ibarrola-Rivas et al. (2016) set coffee.yield.patch (coffee.yield * ha.to.patch) ; [kg/patch/yr] set coffee.kcal (beans.kcal * bean.to.coffee.kcal.conversion) ; [Equivalent of kcal/kg] set coffee.kcal.patch (coffee.kcal * coffee.yield * ha.to.patch) ; [Equivalent of kcal/patch/yr] end to set.initial.map ; [OBSERVER CONTEXT] ask patches [ ifelse which.map = "baseline" [set lot map1.lot] [set lot map2.lot] ; Populates 'lot' based on which map is being used. set fertility 1 ] set.fertility.impact ; The list below contains avaerage closing prices for coffee on the international market, in USD/KG, from 1975 to 2020 (https://www.macrotrends.net/2535/coffee-prices-historical-chart-data) set coffee.exchange.price (list 2.46 2.24 2.5 2.94 3.02 2.94 3.93 2.78 3.86 5.59 3.62 2.77 2.92 2.6 2.39 2.38 1.69 1.38 1.19 1.23 1.99 2.3 2.85 3.95 2.55 3.23 3.19 1.51 1.44 1.89 2 2.33 2.89 2.57 4.4 3.28 3.16 2.9 2.99 2.7 3.49 3.92 3.45 5.11 3.14 1.45) ; The list below converts USD from 1975 to 2020 in 2020 USD. (https://www.usinflationcalculator.com/) set price.adjustment (list 0.21 0.22 0.23 0.25 0.28 0.32 0.35 0.37 0.38 0.4 0.42 0.42 0.44 0.46 0.48 0.51 0.53 0.54 0.56 0.57 0.59 0.61 0.62 0.63 0.64 0.67 0.68 0.7 0.71 0.73 0.75 0.78 0.8 0.83 0.83 0.84 0.87 0.89 0.9 0.91 0.92 0.93 0.95 0.97 0.99 1) ; The commands above are performed on every run. ; The commands below are performed from the second run on (when 'control != 0'). ; For what concerns the first command below, that is the case because only from the second run there is the ; need to restore the map (in the first run it is imported). For what concerns the other commands, that is ; the case because in the first run they are set to 0 as their default value - which leaves the need to manually ; setting them to 0 only from the second run on (given that no 'clear-all' nor 'clear-globals' are used). if control != 0 [ask patches [ if landcover = 4 or landcover = 5 or landcover = 0 [set landcover 1] ; Restores initial forest landcover. set owner 0 set which.crop 0 set kcal.yielded 0 set workforce.trapped 0 set abandoned 0 set coffee.produced 0 ] ] if control != 0 and not behavior.space.run? [set.initial.colours] ; Only if not in the first run and not in Behavior Space: visually restores the initial landcover. end to set.fertility.impact ; [OBSERVER CONTEXT] ; As the fertility of the soil decreases, it will affect production. However, it may not affect kcalories produced from ; a patch of land in an exactly proportionate way. The code here enables these two effects to be separated. ; Specifically, it presumes that kcalories production is affected less significantly than the decrease of fertility itself. ; This represents the idea that certain human interventions can help support crop growth (e.g. types of fertilization). ; The 'fertility.impact' value is set once for each patch, and so can vary across patches within the simulation. ; In this first block of code, the 'fertility.impact' value is set randomly once for each forested patch belonging to ; a lot (i.e. each patch that might be deforested during the simulation). ; Specifically, the structure of the function to generate such value is: '(random-float x) + y', which means that the ; value will be between (y) and (x + y). ask patches with [landcover = 1 and map1.lot != 0] [ set fertility.impact (random-float 0.2) + 0.3 ] ; This second block of code makes it possible for the 'fertility.impact' value to not be TOTALLY random across the ; landscape (considering that the value might be dependant on some soil characteristics). This is achieved by asking ; each patch with a fertility.impact value to make it the mean of the value belonging to its neihboring patches. ask patches with [fertility.impact > 0] [ if not empty? [fertility.impact] of neighbors with [fertility.impact > 0] [ set fertility.impact mean [fertility.impact] of neighbors with [fertility.impact > 0] ] ] end ; ******************************************************************************************************************************************* ; ************************************************************** GO PROCEDURES ************************************************************** ; ******************************************************************************************************************************************* to go ; [OBSERVER CONTEXT] if ticks > (simulation.end - 1976) [stop] create.families calculate.ex-ante ask turtles [plan] crop calculate.ex-post escape tick end ; ************************************************ BEGINNING OF HOUSEHOLDS CREATION ************************************************* to create.families ; [OBSERVER CONTEXT] ; A specific amount of households enters the model based on the year (tick). As soon as they are created, ; each household is given its specific characteristics ('set.household'), which include having a lot assigned ; ('to choose.lot'). ; The number of households entering the simulation has been originally taken from the literature for a similar ; case. However, than number has been multiplied by 0.35119047619047619047619047619048 to reflect the re-sizing ; of the map. ; The first line below creates a list of one element, i.e. the number of available lots. let x (list (count patches with [ lot != 0 and owner = 0 ] / 1072)) let y 0 (ifelse ticks = 0 [set y 20] ; Initial number was 57. ticks < 6 [set y 8] ; Initial number was 24. ticks = 6 [set y 6] ; Initial number was 16. ticks < 30 [set y 3] ; Initial number was 8. [set y 0] ) ; This block of commands adds 'y' (as conditionally determined above) to the list, which thus becomes a list of two elements, and ; then creates as many households as the lower ('min') number from the list. Considering that 'x' is the number of available lots ; and that 'y' is the potential number of households entering the model, and considerint that households are created in a number ; equalling the minimum between 'x' and 'y', this means that there will never be more households entering the simulation than lots ; available. set x fput y x create-turtles min x [ hide-turtle set.household ] set households.entered households.entered + min x ; Conceptually, the 'set.behaviour' procedure could have been called from within the block of commands above (i.e. 'create-turtles [...]'). However, ; that would have been a turtle-context and would have meant having each turtle evaluating the conditions needed to assign behaviours. Calling it ; from outside the 'create-turtles' procedure, instead, means calling it from the observer-context and thus having only the observer (which is one) ; evaluating the same conditions (therefore saving time and memory). set.behaviour end to set.household ; [TURTLE CONTEXT] ; It is recommended that 'choose.lot' remains the first of the list. In fact, turtles might leave the simulation before settling into a lot. ; In that case, all the following computations are avoided. choose.lot set.numerosity set.individuals set.workforce set.tot.kcal.required set leaving? FALSE end to choose.lot ; [TURTLE CONTEXT] ; The lot-assignment process takes place in four steps. ; In the first line, the household finds a patch that belongs to a lot owned by no one, and sets "myself" as the owner of that patch. ; In the second line, the household assigns to "my.lot" that patch's "lot" value. [at this stage, "my.lot" is a number] ; In the third line, all patches with that same "lot" value are asked to set that household (identified by "myself") as their owner. ; In the fourth line, the group of patches with that household as owner becomes the "my.lot" household's variable. [at this stage, "my.lot" is an agentset] ask one-of patches with [lot != 0 and owner = 0] [set owner myself] set my.lot [lot] of one-of patches with [owner = myself] ask patches with [lot = [my.lot] of myself] [set owner myself] set my.lot patches with [owner = myself] ; While the 'my.lot' variable stores the entire lot (which is useful for patch-management purposes), the 'my.lot.f' variable below ; stores patches of my lot that are forest (which is useful for managing the deforestation side of things). In fact, the other patches ; might be wetland or water, and it is not possible for households to crop there. set my.lot.f my.lot with [landcover = 1] if count my.lot.f < (legal.farming * count my.lot) / 100 [ clear.lot set households.entered households.entered - 1 die ] ; The next block of code sets the portion of lot available for deforestation as the one closest to the road. ; This is done by first checking the direction of patches in 'my.lot' (i.e. 'n' = North, 's' = South), and ; then setting as 'my.lot.available' the percentage (as determined in the Interface) of the lot with the ; highest or lowest y-coordinates. set direction.t [direction] of one-of my.lot ifelse direction.t = "n" [set my.lot.available min-n-of (ceiling (legal.farming * count my.lot / 100)) my.lot.f [pycor]] [set my.lot.available max-n-of (ceiling (legal.farming * count my.lot / 100)) my.lot.f [pycor]] ; These four lines below ask households to move to the center of their lots and to become visible. Useless for most practical purposes, ; but better to keep it here. if not behavior.space.run? [ move-to patch ((min [pxcor] of my.lot + max [pxcor] of my.lot) / 2) ((min [pycor] of my.lot + max [pycor] of my.lot) / 2) set hidden? FALSE set size 8 set color white ] end to set.numerosity ; [TURTLE CONTEXT] ; The first line creates a random number from 0.00 to 99.99. The subsequent block of commands determines the number of members ; of the family based on household-numerosity data for rural Brazil. let x precision random-float 100 2 (ifelse x < 20.24 [set members 3] x < 42.12 [set members 4] x < 62.07 [set members 5] x < 76.20 [set members 6] x < 85.81 [set members 7] x < 92.04 [set members 8] x < 96.05 [set members 9] [set members 10] ) end to set.individuals ; [TURTLE CONTEXT] ; This block of commands assigns each individual to either a woman, a man, a child or an elderly, based on data from case studies in the same region. ; Only adults are differentiated by gender. repeat members [ let x precision random-float 100 2 (ifelse x < 25.66 [set females females + 1] x < 59.07 [set males males + 1] x < 98.67 [set children children + 1] [set elderly elderly + 1] ) ] ; This block of commands avoids the case of a household having only children and/or elderly. In fact, if a household has been created with only ; children and/or elderly, the following lines of code take one of them and turning it into an adult. if (females = 0) and (males = 0) [ ifelse children > 0 [set children children - 1] [set elderly elderly - 1] ifelse precision random-float 100 2 < 43.45 [set females females + 1] [set males males + 1] ] end to set.workforce ; [TURTLE CONTEXT] ; Source for child-labour: United States Department of Labor - Bureau of International Labor Affairs (no date). Numbers are 2.9% and 55.6%. let working.children 0 let working.elderly 0 if children > 0 [ repeat children [ if precision random-float 100 2 < 1.61 [ set working.children working.children + 1 ] ] ] if elderly > 0 [ repeat elderly [ if precision random-float 100 1 < 2.9 [ set working.elderly working.elderly + 1 ] ] ] set tot.workforce (females + males + ((working.children + working.elderly) / 4)) * 365 end to set.tot.kcal.required ; [TURTLE CONTEXT] ; This approach is based on a paper estimating an adult-equivalent conversion factor to determine the consumption ; unit of a households (assigning then a requirement of 2550 kcal/day). The coefficients are derived from empirical ; research on the Brazilian population. The resulting figure represents the kilocalories requirement of a household ; per year, based on the demographics of its members. set tot.kcal.required round ((precision (females * 0.827 + males * 1.0704 + children * 0.750667 + elderly * 0.825) 2) * 2550 * 365) end to set.behaviour ; [OBSERVER CONTEXT] (ifelse global.behaviour = "subsistence.optimised" [ask turtles with [behaviour = 0] [set behaviour "subsistence.optimised"]] global.behaviour = "subsistence.preference" [ask turtles with [behaviour = 0] [set behaviour "subsistence.preference"]] global.behaviour = "all.resources.coffee" [ask turtles with [behaviour = 0] [set behaviour "all.resources.coffee"]] global.behaviour = "all.resources.mixed" [ask turtles with [behaviour = 0] [set behaviour "all.resources.mixed"]] global.behaviour = "random.behaviour" [allocate.random.behaviour] ) ask turtles with [behaviour = "subsistence.preference" and my.preference = 0] [ ; potentially this can go as a separate procedure, but maybe it is ok here. let x random 100 (ifelse x < 33 [set my.preference 0.25] x < 66 [set my.preference 0.5] [set my.preference 0.75] ) ] end to allocate.random.behaviour ; [OBSERVER CONTEXT] ask turtles with [behaviour = 0] [ let x random 100 (ifelse x < 25 [set behaviour "subsistence.optimised"] x < 50 [set behaviour "subsistence.preference"] x < 75 [set behaviour "all.resources.coffee"] [set behaviour "all.resources.mixed"] ) ] end ; **************************************************** END OF HOUSEHOLDS CREATION **************************************************** ; ************************************************ BEGINNING OF EX-ANTE CALCULATIONS ************************************************* to calculate.ex-ante ; [OBSERVER CONTEXT] ; This block of commands increases the count of the years that patches have been abandoned. If they have been abandoned for more than 5 years, the assumption is that they are ; taken as pasture land by cattle ranchers. ask patches with [abandoned > 0 and abandoned < 6] [ set abandoned abandoned + 1 if abandoned = 6 [become.pasture] ] ; This block of commands first decreases the fertility (which can result in patches becoming abandoned, see 'to become.abandoned') and then asks patches that are still cropped to calculate ; how many kcalories they produce and how much workforce they need. The fact that the same condition ('landcover = 4') is tested twice is not a msitake. In fact, as a result of the first command ; ('decrease.fertility'), some of the patches that initially had 'landcover = 4' might now have 'landcover = 0'. Therefore, the need for a second landcover test. ask patches with [landcover = 4] [ decrease.fertility if landcover = 4 [calculate.output] ] ; Firstly ask households to compare the total calories they require against the calories that will be produced from any land they have already ; converted to the production of crop. This determines how many additional calories (if any) they need to provide through new deforestation ; and cropping. Secondly, households are also asked to determine the amount of workforce they have which is not already committed to working the land ; that has already been put to cropping. This is a measure of the resources they have to put to new land conversion and cropping. ; Technical note: sometimes the way NetLogo handles decimal places means that when it should show workforce.available = 0, it will actually ; show a very, very small negative number (e.g. -0.0000000000000001). Whenever this happens, it needs to be over-written and reset to 0.00. ask turtles [ set kcal.left (tot.kcal.required - sum [kcal.yielded] of my.lot) set workforce.available (tot.workforce - sum [workforce.trapped] of my.lot) if workforce.available < 0 [set workforce.available 0.00] ] end to become.pasture ; [PATCH CONTEXT] set landcover 5 if not behavior.space.run? [set pcolor yellow] end to decrease.fertility ; [PATCH CONTEXT] ; Although tropical soils can support tropical forests well, they do not support agricultural production for very long. ; In fact, soil fertility declines fairly quickly as compared to other soil types. Most plots of land are abandoned after 5 years. ; Thus, the patches in this simulation also lose fertility with time (20% per year for 5 years) set fertility precision (fertility - 0.2) 1 if fertility <= 0 [become.abandoned] end to become.abandoned ; [PATCH CONTEXT] set abandoned 1 set landcover 0 set which.crop 0 set kcal.yielded 0 set workforce.trapped 0 set coffee.produced 0 if not behavior.space.run? [set pcolor grey] end to calculate.output ; [PATCH CONTEXT] ; As the fertility of the soil decreases, it will affect production. However, it may not affect kcalories produced ; from a patch of land in an exactly proportionate way. The code here enables these two effects to be separated. ; For beans and corn, kcalories come from eating the crops grown. For coffee, kcalories come from selling the coffee ; and being able to use the money to buy beans for consumption. let lost.fertility (1 - fertility) let scaled.fertility.impact (lost.fertility * fertility.impact) let reduction.multiplier (1 - scaled.fertility.impact) if which.crop = 1 [ set kcal.yielded (beans.kcal.patch * reduction.multiplier) set workforce.trapped beans.labour.patch ] if which.crop = 2 [ set kcal.yielded (corn.kcal.patch * reduction.multiplier) set workforce.trapped corn.labour.patch ] ; For what concerns the calculations for coffee below, note that kcalories from coffee production (and, ; specifically, the 'coffee.kcal.patch' variable) are not intended as coming from consumption, but rather ; from the purchasing power that one household obtains from producing and selling coffee. In fact, a ; conversion has been applied to go from coffee production to kcalories from consuming purchased beans. ; How the value (which is dependent on 'bean.to.coffee.kcal.conversion' in the Interface) is calculated ; can be seen in 'to set.globals'. ; Also, note that coffee is the only crop for which the quantity produced ('coffee.produced', below) is ; calculated, as this is needed to keep track of the contribution to international exports. if which.crop = 3 [ set kcal.yielded (coffee.kcal.patch * reduction.multiplier) set workforce.trapped coffee.labour.patch ] end ; **************************************************** END OF EX-ANTE CALCULATIONS *************************************************** ; **************************************************** BEGINNING OF PLANNING PHASE *************************************************** to plan ; [TURTLE CONTEXT] (ifelse behaviour = "subsistence.optimised" [plan.subsistence.optimised] behaviour = "subsistence.preference" [plan.subsistence.preference] behaviour = "all.resources.coffee" [plan.all.resources.coffee] behaviour = "all.resources.mixed" [plan.all.resources.mixed] ) end to plan.subsistence.optimised ; [TURTLE CONTEXT] set plant.beans 0 ; These two lines are only for reset purposes and for making sure that no unwanted values are set plant.corn 0 ; carried through the code from previous (simulated) years. ; This condition ('if kcal.left > 0') encloses all rest of the procedure. The logic is: the optimisation problem ; needs to be performed only by households that have a pending need for kcalories. if kcal.left > 0 [ let x count my.lot.available with [landcover = 1] let objective (list beans.kcal.patch corn.kcal.patch) ; Version 1 ;let objective (list 1 1) ; Version 2 let constraint1 (list (list beans.kcal.patch corn.kcal.patch) "<=" kcal.left) ; Version 1 ;let constraint1 (list (list beans.kcal.patch corn.kcal.patch) ">=" kcal.left) ; Version 2 let constraint2 (list (list beans.labour.patch corn.labour.patch) "<=" workforce.available) let constraint3 (list (list 1 1) "<=" x) let constraints (list constraint1 constraint2 constraint3) let goal TRUE ; Version 1 ;let goal FALSE ; Version 2 let nonnegative TRUE let solution (numanal:LPsimplex objective constraints goal nonnegative) set plant.beans floor first first solution set plant.corn floor last first solution set my.solution ceiling last solution ; This block of code checks if the solution to the optimisation is less than the kcalories needed. If that is the case, first ; it is checked if the household already decided whether to deforest illegally or not (default value of 'illegal' is 0, so ; 'illegal' being 0 means that the household hasn't chosen yet). If they have not chosen yet (i.e. if 'illegal? = 0'), then ; the households chooses ('check.illegal'). Instead, if they have choosen already, it means that they were not able to satisfy ; their needs with their choice, and thus 'leaving?' is set to TRUE - which means that the household will leave the simulation ; at the end of the 'go'. if my.solution < kcal.left [ ifelse illegal? = 0 [ifelse imitation? [imitate] [check.illegal]] [set leaving? TRUE] ] ] end to plan.subsistence.preference ; [TURTLE CONTEXT] ; an idea might be that if a turtle doesn't meet kcal, can lower preference. ; Note: the structure of 'constraint4' follows from the following formulation of the constraint (example made with 'my.preference = 0.25'): ; (ha of corn) >= (0.25)(ha of corn + ha of beans) ; which can be rearranged as: ; (-0.25)(ha of beans) + (0.75)(ha of corn) >= 0 ; which, in general terms, is: ; (0 - my.preference)(ha of beans) + (1 - my.preference)(ha of corn) >= 0. set plant.beans 0 ; These two lines are only for reset purposes and for making sure that no unwanted values are set plant.corn 0 ; carried through the code from previous (simulated) years. if kcal.left > 0 [ let x count my.lot.available with [landcover = 1] let y (0 - my.preference) let z (1 - my.preference) let objective (list beans.kcal.patch corn.kcal.patch) ; Version 1 ;let objective (list 1 1) ; Version 2 let constraint1 (list (list beans.kcal.patch corn.kcal.patch) "<=" kcal.left); Version 1 ;let constraint1 (list (list beans.kcal.patch corn.kcal.patch) ">=" kcal.left) ; Version 2 let constraint2 (list (list beans.labour.patch corn.labour.patch) "<=" workforce.available) let constraint3 (list (list 1 1) "<=" x) let constraint4 (list (list y z) ">=" 0) let constraints (list constraint1 constraint2 constraint3 constraint4) let goal TRUE ; Version 1 ;let goal FALSE ; Version 2 let nonnegative TRUE let solution (numanal:LPsimplex objective constraints goal nonnegative) set plant.beans floor first first solution set plant.corn floor last first solution set my.solution ceiling last solution ; This block of code checks if the solution to the optimisation is less than the kcalories needed. If that is the case, first ; it is checked if the household already decided whether to deforest illegally or not (default value of 'illegal' is 0, so ; 'illegal' being 0 means that the household hasn't chosen yet). If they have not chosen yet (i.e. if 'illegal? = 0'), then ; the households chooses ('check.illegal'). Instead, if they have choosen already, it means that they were not able to satisfy ; their needs with their choice, and thus 'leaving?' is set to TRUE - which means that the household will leave the simulation ; at the end of the 'go'. if my.solution < kcal.left [ ifelse illegal? = 0 [ifelse imitation? [imitate] [check.illegal]] [set leaving? TRUE] ] ] end to plan.all.resources.coffee ; [TURTLE CONTEXT] ; Coffee is a cash crop. It is sold for money, and that money can be used to by other food in order to meet nutritional needs. ; If coffee is sold at a very high price, then 1 kg of coffee can buy many more calories than households would get from 1 kg of beans. ; However, if coffee is sold at a low price, then 1 kg of coffee might not buy as many calories as households would get from 1 kg of beans. ; This is reflected in the slider called 'coffee.to.bean.kcal.conversion'. This is the multiplier showing what percentage of calories 1 kg ; of coffee can buy relative to what a household can get from growing 1 kg of beans. When the value is set to 1, it means 1 kg of coffee ; buys exactly the same calories as a household would get from growing 1 kg of beans. If the value is set to 1.5, it means that 1 kg of ; coffee buys 50% more calories than a household would get from growing 1 kg of beans. ; The x variable calculated as below equals the number of patches that a household is able to clear for coffee based on the available ; workforce and the required labour for coffee. This can be seen by keeping track of the units of measure of the three variables ; involved in the calculation, and see how they reduce each other to just. set plant.coffee 0 ; This is only for reset purposes. ; The line of code below checks how many patches (x) the household would be able to plant with coffee. Under this behaviour, the goal ; of the household is to use 100% of the households'available workforce. let x floor (workforce.available / coffee.labour.patch) ; If the condition below is met, it means the household has enough land available to use up all of its workforce for coffee. ; If that is the case, the household checks whether the coffee produced would eventually supply enough calories. ifelse x <= count my.lot.available with [landcover = 1] ; If the households has enough land to use up all of its labour... [let tot.kcal.from.coffee (coffee.kcal.patch * x) ; ...then it checks if that would meed nutritional requirements. ifelse tot.kcal.from.coffee >= kcal.left ; If nutritional requirements are met... [set plant.coffee x] ; ...then it plants that amount of coffee ... [set leaving? TRUE]] ; ...otherwise, it leaves the simulation- [ifelse illegal? = 0 ; If nutritional requirements are not met, it checks if a decision has been taken on illegal behaviour. [ifelse imitation? ; If a decision has not been made yet, it checks whether it should imitate other households. [imitate] ; If that is the case, it imitates... [check.illegal]] ; ...otherwise, it takes the decision on illegal behaviour on its own. [set leaving? TRUE]] ; If a decision has already been made on illegal behaviour, it leaves the simulation. ; NOTE ; The above code should ensure that the only households that remain in the simulation while growing only coffee are those that can: ; 1) use 100% of their available labour on production (work-a-holic agents who would not be happy to use less than 100% of their labour). ; 2) are able to meet at least 100% of the nutritional needs from the money they get from selling their coffee. ; Said another way, if they cannot meet their nutritional needs from the sale of their coffee, while usingn 100% of their labour, ; then they leave the simulation. end to plan.all.resources.mixed ; [TURTLE CONTEXT] ; First of all, the normal 'to plan.subsistence.optimised' is performed. Later, the 'leaving?' condition is checked to see if the ; household has the potential to do more work. If 'leaving? = FALSE' (i.e. 'not leaving? = TRUE'), first of all 'workforce.available' ; is re-calculated. This is because normally 'workforce.available' is updated in 'to calculate.ex-ante', but this will only happen ; in the next round. This means that the share of workforce engaged with land just cleared as a result of 'plan.subsistence.optimised' ; has not yet been subtracted from 'workforce.available'. The same reasoning applies to the re-calculation of the count of 'my.lot.available'. ; STEP 1: reset crop-patch values. set plant.beans 0 set plant.corn 0 set plant.coffee 0 if kcal.left > 0 [ ; STEP 2 Determine how many patches of beans and corn to plant. let x count my.lot.available with [landcover = 1] let objective (list beans.kcal.patch corn.kcal.patch) let constraint1 (list (list beans.kcal.patch corn.kcal.patch) "<=" kcal.left) let constraint2 (list (list beans.labour.patch corn.labour.patch) "<=" workforce.available) let constraint3 (list (list 1 1) "<=" x) let constraints (list constraint1 constraint2 constraint3) let goal TRUE let nonnegative TRUE let solution (numanal:LPsimplex objective constraints goal nonnegative) set plant.beans first first solution set plant.corn last first solution set my.solution last solution ; STEP 3: Consider coffee production on top of the results for beans and corn. Agents will only seek to do this if they are UNABLE to meet ; their caloric needs from beans and corn. They are subsistence agents, and not in the business of maximising production for financial gain. if my.solution < kcal.left [ let workforce.available.coffee (workforce.available - (plant.beans * beans.labour.patch) - (plant.corn * corn.labour.patch)) if workforce.available.coffee < 0 [set workforce.available.coffee 0] let y ((count my.lot.available with [landcover = 1] - plant.beans - plant.corn)) let z floor (workforce.available.coffee / coffee.labour.patch) ifelse z <= y [set plant.coffee z] [set plant.coffee y] ; STEP 4: Consider kcalories from coffee production. ; Coffee is a cash crop. It is sold for money, and that money can be used to by other food in order to meet nutritional needs. ; If coffee is sold at a very high price, then 1 kg of coffee can buy many more calories than households would get from 1 kg of beans. ; However, if coffee is sold at a low price, then 1 kg of coffee might not buy as many calories as households would get from 1 kg of beans. ; This is reflected in the slider called 'coffee.to.bean.kcal.conversion'. This is the multiplier showing what percentage of calories 1 kg ; of coffee can buy relative to what a household can get from growing 1 kg of beans. When the value is set to 1, it means 1 kg of coffee ; buys exactly the same calories as a household would get from growing 1 kg of beans. If the value is set to 1.5, it means that 1 kg of ; coffee buys 50% more calories than a household would get from growing 1 kg of beans. let tot.kcal.from.coffee (plant.coffee * coffee.kcal.patch) let tot.kcal.from.farming (my.solution + tot.kcal.from.coffee) if tot.kcal.from.farming < kcal.left [ ; In the test of this condition, it is implicit that if tot.kcal.from.farming >= kcal.left, nothing needs to change. ifelse illegal? = 0 [ifelse imitation? [imitate] [check.illegal]] [set leaving? TRUE] ] ] ] end to imitate ; [TURTLE CONTEXT] ; The first command in response to the first 'ifelse' is the exact copy of the second command in response to the first ; 'ifelse' in 'to check.illegal'. What those lines do is to check if a household is an illegal deforester based on the ; probability set in the Interface. The reason why that block of code is copy-pasted instead of just calling the procedure ; 'to check.illegal' is that such procedure contains a call to 'to imitate'. Therefore, if 'imitation? = TRUE' the turtle ; would go back and forth between 'to check.illegal' and 'to imitate' until the first 'ifelse' in 'to imitate' is FALSE, ; thus always obtaining 'illegal? = FALSE' in the end. To avoid this loop, only the bit of code after ; 'ifelse imitation? [imitate]' is copied here. ifelse random 3 < 1 ; This (i.e. 66% chance to imitate) is an arbitrary assumption. A different option could be to have this set in the [check.illegal] ; interface, but the downside would be having too many parameters to mess about with. [ifelse any? turtles with [illegal? != 0] [let x turtles with [illegal? != 0] let y [illegal?] of min-one-of x [distance myself] set illegal? y] [check.illegal]] end to check.illegal ; [TURTLE CONTEXT] ; NOTE TO THE PROGRAMMER: Anything that gets changed after '[imitate]' should be copied to the first command in response ; to the first 'ifelse' in 'to imitate'. To read the reason, check the comment to 'to imitate' below. ifelse random 100 < illegal.propensity [set illegal? TRUE set my.lot.available my.lot.f plan] [set illegal? FALSE plan] end ; ******************************************************* END OF PLANNING PHASE ****************************************************** ; ******************************************************* BEGINNING OF CROPPING ****************************************************** to crop ; [OBSERVER CONTEXT] ask turtles [ (ifelse direction.t = "n" [ ask min-n-of plant.beans my.lot.available with [landcover = 1] [pycor] [ set landcover 4 set which.crop 1 ] ask min-n-of plant.corn my.lot.available with [landcover = 1] [pycor] [ set landcover 4 set which.crop 2 ] ask min-n-of plant.coffee my.lot.available with [landcover = 1] [pycor] [ set landcover 4 set which.crop 3 ] ] direction.t = "s" [ ask max-n-of plant.beans my.lot.available with [landcover = 1] [pycor] [ set landcover 4 set which.crop 1 ] ask max-n-of plant.corn my.lot.available with [landcover = 1] [pycor] [ set landcover 4 set which.crop 2 ] ask max-n-of plant.coffee my.lot.available with [landcover = 1] [pycor] [ set landcover 4 set which.crop 3 ] ] ) ] if not behavior.space.run? [update.colours] end ; ********************************************************** END OF CROPPING ********************************************************* ; *************************************************** BEGINNING EX-POST CALCULATIONS ************************************************* to calculate.ex-post ; [OBSERVER CONTEXT] ask patches with [which.crop = 3] [ let lost.fertility (1 - fertility) let scaled.fertility.impact (lost.fertility * fertility.impact) let reduction.multiplier (1 - scaled.fertility.impact) ; The following line calculates coffee currently produced on each patch. set coffee.produced (coffee.yield.patch * reduction.multiplier) ] ; The following line calculates the coffee produced over the whole simulation (by adding all the coffee grown in this tick to ; all the coffee grown in previous ticks). if any? patches with [coffee.produced > 0] [ set coffee.produced.overall (coffee.produced.overall + sum [coffee.produced] of patches with [which.crop = 3]) let foreign.currency.from.coffee.export.now (sum [coffee.produced] of patches with [which.crop = 3] * first coffee.exchange.price * first price.adjustment) set foreign.currency.from.coffee.export.tot (foreign.currency.from.coffee.export.tot + foreign.currency.from.coffee.export.now) ] set coffee.exchange.price but-first coffee.exchange.price set price.adjustment but-first price.adjustment end ; **************************************************** END OF EX-POST CALCULATIONS *************************************************** ; **************************************************** BEGINNING OF EXIT PROCEDURES ************************************************** to escape ; [OBSERVER CONTEXT] ask turtles with [leaving? = TRUE] [ clear.lot set households.escaped households.escaped + 1 die ] end to clear.lot ; [TURTLE CONTEXT] if my.lot.available != 0 [ if any? my.lot.available with [landcover = 4] [ ask my.lot.available with [landcover = 4] [ become.abandoned ] ] ] ask my.lot [set owner 0] set my.lot 0 end ; ******************************************************************************************************************************************* ; **************************************************************** UTILITIES **************************************************************** ; ******************************************************************************************************************************************* to go5 ; [OBSERVER CONTEXT] let rounds.left (simulation.end - 1975 - ticks) ifelse rounds.left > 5 [repeat 5 [go] stop] [repeat rounds.left [go] stop] end to go10 ; [OBSERVER CONTEXT] let rounds.left (simulation.end - 1975 - ticks) ifelse rounds.left > 10 [repeat 10 [go] stop] [repeat rounds.left [go] stop] end to set.initial.colours ; [OBSERVER CONTEXT] ; In order to restore the initial colours, this procedure does not need to ask to change landcovers because that has already been done ; by 'to set.initial.map'. In fact, when this procedure kicks in, patches that originally had forest have already returned to have ; 'landcover = 1'. Thus, this procedure only takes care of the colours part. ask patches with [landcover = 1] [set pcolor 53] end to update.colours ; [OBSERVER CONTEXT] ask patches [ (ifelse landcover = 4 [set pcolor orange] ; Crops (any) landcover = 5 [set pcolor yellow] ; Pasture landcover = 0 [set pcolor grey] ; Abandoned land ) ] end
There is only one version of this model, created almost 4 years ago by Matteo Sposato.
Attached files
File | Type | Description | Last updated | |
---|---|---|---|---|
Deforestation in Brazil.png | preview | Preview for 'Deforestation in Brazil' | almost 4 years ago, by Matteo Sposato | Download |
netlogomap | data | This file is imported with 'import-world' and contains information to build the map of the model. | almost 4 years ago, by Matteo Sposato | Download |
This model does not have any ancestors.
This model does not have any descendants.