7 Forest management
Forest management is a key component of a forest model, particularly when used in areas where forests are strongly affected by human interventions.
iLand includes two sub-systems that handle forest management:
the “base” management is a rather simple, but still versatile approach which allows a wide range of altering the forest in the model (extracting of trees, planting of trees, …).
The Agent Based management Engine (ABE) is a newer variant. ABE is a powerful module that allows to simulate multiple management agents on a landscape. Agents can even react to changes in the environment by changing management plans and can apply a variety of different management activities in a dynamically scheduled manner. The setup and application of ABE, however, is somewhat complex.
The ABE model provides a framework to dynamically simulate adaptive forest management in multi-agent landscapes under changing environmental conditions. While the Rammer, W., Seidl, R., 2015 paper desribes ABE scientifically, this chapter provides more detail on both the concepts and the technical implementation.
From a technical point of view, forest management in iLand uses the integration of JavaScript with the model. Specifically, ABE uses a declarative programming paradigm. The section Section 6.3.4 provides more details.
7.1 Components of ABE
Agent and agent types
In ABE, all forest managers are represented by Agents. Agents are responsible for the management of a specific part of the landscape. Each Agent is of a certain Agent Type. An Agent Type corresponds to a manager archetype such as “farmer” or “forest company”, while an Agent represents an individual manager with specific properties, e.g., the agents’ age. The AgentType is created implicitly, when just an agent is created.
Stand treatment program (STP)
A stand treatment program consists basically of a collection of activity objects, which typically cover the silvicultural treatments to regenerate, tend and thin, and harvest a stand. In addition, the STP includes a definition of a time window for each activity, from which ABE autonomously derives the sequence and preliminary time of execution for each activity.
Management activities
Activities are the core element of forest management in ABE, as they are the elements that actually change the forest state in the ecosystem model by removing or planting trees. ABE provides a library of pre-defined activities that cover the most important aspects of forest management. Yet, the activity library can be easily extended by providing user-defined activities. Activities are fetched from the library by specifying the name of the activity type and by further defining the properties of the activity.
7.1.1 Defining a minimum ABE script
var stp = { U: 100,
thinning1: { /* defined below */ },
thinning2: {/* defined below */ },
clearcut: { /* defined below */ }
} .addManagement(stp, "program"); fmengine
Note that if you do not define a unit
and an agent
, a default unit (with the name _unit
and default agent (_agent
) are automatically created by iLand.
7.1.2 Accessing elements of ABE
There are two central objects for accessing ABE, namely fmengine
and stand
. On the one hand, fmengine
provides access to global properties of ABE, such as a list of all stands in the system. stand
, on the other hand, allows to access properties of the stand that is currently processed. Linked to a stand
are then the management unit
, the stand treatment program (stp
) or the current activity that is executed. For “normal” management activities you can just use stand
, as ABE takes care of everything. You can, however, also manually override the current stand that ABE processes - this can be useful for debugging or advanced scripting tasks. To set the “focus” of ABE to a specific stand, you just have to set fmengine.standId
to the numeric Id of this stand.
7.2 Inspection
7.2.1 Loop over all stands in a simulation
The property fmengine.standIds
lets you access all stands that are set up with ABE (i.e., stands for which at least basic information is available in the file referenced by abe.agentDataFile)
. In order to access objects related to this stand, you need to set fmengine.standId
to the respective stand:
// print id and name of STP for each stand:
for (const id of fmengine.standIds) {
// set the focus of ABE to this stand:
.standId = id;
fmengine// access data for the stand
console.log('Stand ' + id + ' managed by ' + stand.stp.name );
}
7.2.2 Check all available STPs
A list of all STPs currently available can be queried via fmengine.stpNames
. Note that since the names of STPs are unique, you can easily loop over all STPs:
// Show details for each STP.
for (var i=0; i<fmengine.stpNames.length; ++i) {
let s = fmengine.stpNames[i];
// Show a pop-up window with a detailed report for each STP
.alert(fmengine.stpByName(s).info);
Globals }
7.2.3 Assign a new STP to a stand
It’s always possible to assign a new STP to a stand or overwrite an already existing STP:
// create STPs, details omitted
.addManagement({ ... }, 'bau'); // default
fmengine.addManagement({ ... }, 'am1');
fmengine
.standId = 1;
fmengine
console.log(stand.stp.name); // -> "bau"
// set to a new STP identified by the name
.setSTP('am1');
standconsole.log(stand.stp.name); // -> "am1"
7.2.4 Dynamic update of a STP program
This is the case, when a STP changes during a simulation, i.e., when the definition of a STP changes dynamically. Note, that usually dynamic behavior is implemented within a static STP using conditional statements or similar. The spinup of iLand is an example that is changing STPs iteratively to improve the match of simulated with target forest state within a longer spinup simulation.
Updating a STP while it is currently executed is not possible! A workaround solution could look like this:
the STP that realizes that a new STP should be generated stores that information to a list (e.g.
stand_to_process.push_back(stand.id)
Using iLand events you can trigger the creation of new STPs; for example, use a
onYearEnd
event:function onYearEnd() { while (stands_to_process.length>0) { var stand_id = stands_to_process.pop(); console.log("*** processing stand" + stand_id ); // create a STP programmatically. Remember the definition // of a STP is just a collection of objects (representing // activities)... const stp = createNewStp( stand_id ); // update the STP for the stand (use unique STP names here) .updateManagement(stp, "mgmt_"+stand_id); fmengine }console.log('**** update stands finished *****'); }
7.3 Working with activities
Activities are the main building blocks of forest management in ABE. A detailed description is on the wiki: https://iland-model.org/ABE+activities
Here we cover some practical aspects of working with activities.
7.3.1 Defining activities
Activities are JavaScript objects, that (can) include also code. Therefore there are several ways how activities can be created; a straightforward is to create an explicit object:
// (1): explicit object
// a simple activity that runs when the stand is 10 years old
// and does not do much
const my_activity = {
type: 'general', schedule: 10,
action: function() { fmengine.log('Activity executed!')}
}
You can later use my_activity
as part of the definition of a STP. A more advanced approach facilitates the fact that functions can generate activities and uses JavaScript closures:
// (2) An activity generator
function createActivity(max_dbh, age) {
// the function is "hidden" here
// and can encapsulate complex behavior
// note that for instance 'max_dbh'
// is still visbile in do_x() - this is
// a closure.
function do_x() {
//
.log(max_dbh);
fmengine
}return {
type: 'general', schedule: age,
action: function() { do_x(); }
}
}
const my_act = createActivity(20, 50);
Note that in many instances the simple approach is sufficing. Note further, that usually an activity can react to the specifics of a stand (see next section) without the need to create new activities for every stand.
7.3.2 Stand variables and stand flags
Activities are executed for a stand, and stand-specific information is available typically via the stand
object. In this section, we introduce some patterns how this can be done in practice. Look at the following example that shows the action
function of a (general) activity.
...
: function() {
action// access properties of the stand, for instance volume
// see also: https://iland-model.org/apidoc/classes/Stand.html
if (stand.volume > 300 ) {
// do something only if standing volume / ha is high enough
const target_volume = stand.flag('target_volume');
// ... use target volume
}
}...
Some statistics on the current state of the stand are available via the stand
object, for example stand.volume
is the standing volume (m3/ha) (see https://iland-model.org/apidoc/classes/Stand.html for more variables). More interesting is the stand.flag()
in the example. Flags are stored as key-value pairs per stand; different stands can have different keys (or also the same keys) and can hold any (JavaScript) value, including JavaScript options. In the following, we discuss some example use of flags.
Example 1: Memory
Flags can be used as “memory”. For instance, a counter (per stand) for a repeating activity can be implemented via a flag, or flags can indicate that certain events happened already in the past (for example, a flag could indicate that a planting happened)
// counter example
type: 'general', schedule: {repeat: true, repeatInterval: 5 },
{ action: function() {
let event_happened = ....; // logic that evaluates event
if (event_happened) {
let cnt = stand.flag('my_counter');
if (cnt > 3) {
// the "event" happened already three times: time to
// do something specific!
}.setFlag('my_counter', cnt + 1);
stand
}
,
}onSetup: function() {
// the onSetup event is a good place to initialize the counter
.setFlag('my_counter', 1);
stand
} }
Example 2: Pass information
Flags can pass information between different activities of a STP. Think of a situation where an (earlier) activity makes a decision e.g. which species to promote, and a later activity needs to know the species to further protect.
// activity A (runs earlier)
...
: function() {
action// decide which species to promote, for example
const promoted = ['acps', 'frex']; // an array!
.setFlag('promoted', promoted);
stand...
}
// activity B (runs later)
...
: function() {
action// get list of promoted species
let p = stand.flag('promoted');
// convert to syntax used by expression
let save_string = `in(species, ${p.join(', ')})=false`;
// will be: "in(species, acps, frex)=false"
// do *not* load the promoted trees
.load(save_string);
trees }
Example 3: External properties
Flags can be used to provide extra information to ABE; for example, you could provide a flag providing the distance to the next forest road - which could influence the actual management! While you can always programmatically set flags, e.g., read a table, set flags for each record (see Section 6.3.5.3), iLand provides a much simpler way, namely the ABE data file (agentDataFile
). iLand reads data from some columns (e.g., id
, stp
, see https://iland-model.org/ABE+spatial+setup). If the file contains additional columns, then the values are automatically assigned to each stand as stand flags! See the following part of the agentDataFile
, that defines “target species” for each stand (Note that you could also define two different STPs here!).
id | stp | species | … |
---|---|---|---|
1 | bau | abal, fasy | … |
2 | bau | acps, frex | … |
… | … | … | … |
Now it is straight-forward to use this information:
...
: function() {
action// read the value from the 'species' column
let target_species = stand.flag('species');
// let's assume we want to run over something
// for each species in the list of species
for (const s of target_species.split(',')) {
.log(`processing species ${s}...`);
fmengine// do something...
}
}
7.4 Advanced topics
7.4.1 Working with patches
From a spatial perspective, ABE typically operates on stands, i.e., (pixelated) polygons which represent forest stands. Such stands are usually relatively homogeneous in composition and structure and are usually also the typical spatial entity of real-world forest management. As a user, you provide the logic how each single stand should be treated, and that logic is then applied to (potentially) multiple stands, one at a time. In many cases the management on a stand applies for the whole area of the stand - a clear-cut removes all trees from a stand just as a wall-to-wall planting affects the full area. The TreeList
and SaplingList
object of iLand are perfectly suited for such tasks. Not all management activities work that way - there is increasing interest and real-world application of more spatially explicit forest management. Such activities create gaps of different sizes to foster regeneration, harvest stands as sequence of strip cuts, or respond to disturbance by planting target species in crown openings created by disturbance. The patches
feature of iLand is designed to aid the implementation of such spatially explicit activities.
A patch
describes an area within a stand, and is basically a list of pixels on the stand grid (i.e., with 10m resolution). The pixels of a patch are usually, but not necessarily, adjacent, e.g., to form a gap in the forest. Each patch
is identified by a numeric integer ID and an area
, and a score
(see below).
The patches
property of a stand
holds a collection of 0 or more patches
, (the list
-property) and provides a number of functions to create or modify patches
. Internally, the object maintains a map of the stand and each 10m cell is either linked to none or exactly one patch
. However, 10x10m pixels can be part of multiple patches at the same time!
7.4.1.1 Using patches to manipulate trees and saplings
Both trees and saplings provide a variable patch
which can be used in every expression for filtering, loading etc. of tree- and sapling-lists. The value of patch
is -1 if the entity is not located on a patch (or if there are no patches on a stand).
Note, that you can use the patch
property also outside of ABE, e.g., to visualize patches in the iLand user interface!
Patches can also be used to specify locations the as target area for planting activities in ABE. To do this, set the patches
property of the planting activity. The property is either an expression that is evaluated for every 10m pixel of the stand (use the patch
variable to indicate the patch), or a Javascript array of patch-Ids.
7.4.1.2 Creating patches
You can create patches using the functions provided by the patches
object. It provides functions to create (a large number of) regularly spaced patches or to split a stand into several strips, e.g., for strip-cutting. Moreover, there are functions that allow to copy patches from another grid - this could be used for pre-defined patterns, but also to react to disturbances. Typically, those functions return multiple patches. Note that you need to explicitly update the list
variable in order to effectively use the patches!
See the section on Patches in the iLand documentation and the examples below for details.
7.4.1.3 Manipulating lists of patches
The list of individual patch
objects (each representing a single patch) can be accessed and manipulated from JavaScript via the stand.patches
property in the context of ABE. Thus, you can apply advanced JavaScript logic to select / remove patches! For an overview of available options see here.
Note that it is not possible to change individual elements of the list
, instead you have to set the list
to a new set of patch
objects! After changing the list, the internal map is updated and thus the new list is effectively used for subsequent use in expressions.
The following example is more complex and outlines the steps required to find “optimal” patches for specific use. The steps are:
create a large number of candidate patches
evaluate each candidate patch (based on vegetation on the patch) and calculate a score based on some user-defined metric
select the “best” patches