Title: | Spatial Individual-Plant Modelling |
---|---|
Description: | A platform for computing competition indices and experimenting with spatially explicit individual-based vegetation models. |
Authors: | Oscar Garcia <https://orcid.org/0000-0002-8995-1341> |
Maintainer: | Oscar Garcia <[email protected]> |
License: | GPL (>= 2) |
Version: | 1.6 |
Built: | 2024-11-23 03:00:55 UTC |
Source: | https://github.com/ogarciav/siplab |
A platform for experimenting with spatially explicit individual-based plant modelling
Package: | siplab |
Type: | Package |
Version: | 1.6 |
License: | GPL |
The main top level functions are pairwise()
, and assimilation()
.
pairwise()
computes the competition indices most commonly used in individual-tree distance-dependent (or spatially explicit) forest growth models. These indices are based on a sum of functions of size and distance between the subject plant and each of its competitors. They represent an aggregate of pairwise interactions, the angular configuration of competitors and any higher-order interactions are ignored. Each index is characterized by a specific interaction function, here called a kernel, and by a definition of competitors.
assimilation()
deals with “fully spatial” models, computing “assimilation indices” that aim at a mechanistic approximation of effective resource capture. One starts with a spatial resource distribution that is typically assumed to be uniform, Plants exert competitive pressure depending on size and distance, described by influence functions. The resource available at each point is allocated to plants according to their local influence and to a partition rule. Finally, the resource uptake may be weighted by an efficiency function that depends on size and distance, and is spatially integrated to obtain the plant's assimilation index.
Several examples of influence and efficiency functions are pre-programmed, and others can be easily produced.
The edges()
function is useful for handling edge effects.
Some sample data sets are included, see links below.
The package is built on top of the spatstat library (http://spatstat.org/), which needs to be installed first.
Oscar García
Maintainer: O. Garcia <[email protected]>
García, O. “Siplab, a spatial individual-based plant modelling system”. Computational Ecology and Software 4(4), 215-222. 2014. (http://www.iaees.org/publications/journals/ces/articles/2014-4(4)/2014-4(4).asp).
García, O. “A generic approach to spatial individual-based modelling and simulation of plant communities”. Mathematical and Computational Forestry and Nat.-Res. Sci. (MCFNS) 6(1), 36-47. 2014. (http://mcfns.net/index.php/Journal/article/view/6_36).
https://github.com/ogarciav/siplab.
http://forestgrowth.unbc.ca/siplab/ (no longer maintained).
Example siplab data sets: boreasNP
, boreasNS
, boreasSA
, boreasSP
.
Some spatstat standard data sets may also be of interest: finpines
, longleaf
, spruces
, waka
.
For tutorials try the vignettes. E. g., in R type help.start()
to open the help browser, and navigate to Packages > siplab > Vignettes.
# Pretend that the data is given as a simple data frame data <- as.data.frame(spruces) # from a spatstat data set head(data) # x-y coordinates in a 56x38 m plot, marks are dbh in meters data$marks = data$marks * 100 # dbh in cm # Convert to a point pattern object datap <- as.ppp(data, c(0, 56, 0, 38)) # plot limits (minx, maxx, miny, maxy) # or datap <- ppp(data$x, data$y, c(0, 56), c(0, 38), marks = data$marks) # Hegyi (1974) index (as usual without his original 1-foot distance offset) hegyi <- pairwise(datap, maxR = 6, kernel = powers_ker, kerpar = list(pi=1, pj=1, pr=1, smark=1)) head(marks(hegyi)) # ZOI model zoi <- assimilation(datap, influence=zoi_inf, infpar=c(k=0.2, smark=1), asym=1)
# Pretend that the data is given as a simple data frame data <- as.data.frame(spruces) # from a spatstat data set head(data) # x-y coordinates in a 56x38 m plot, marks are dbh in meters data$marks = data$marks * 100 # dbh in cm # Convert to a point pattern object datap <- as.ppp(data, c(0, 56, 0, 38)) # plot limits (minx, maxx, miny, maxy) # or datap <- ppp(data$x, data$y, c(0, 56), c(0, 38), marks = data$marks) # Hegyi (1974) index (as usual without his original 1-foot distance offset) hegyi <- pairwise(datap, maxR = 6, kernel = powers_ker, kerpar = list(pi=1, pj=1, pr=1, smark=1)) head(marks(hegyi)) # ZOI model zoi <- assimilation(datap, influence=zoi_inf, infpar=c(k=0.2, smark=1), asym=1)
This is the main function in siplab for computing assimilation indices. Optionally, it computes also a free-growing index, and/or the assimilation centroid.
assimilation(plants, pixsize = 0.2, resource = 1, influence = gnomon_inf, infpar = NULL, asym = Inf, efficiency = flat_eff, effpar = NULL, plot = TRUE, afree = FALSE, centroid = FALSE) assimilation_pix(plants, pixsize = 0.2, resource = 1, influence = gnomon_inf, infpar = NULL, asym = Inf, efficiency = flat_eff, effpar = NULL, plot = TRUE, afree = FALSE, centroid = FALSE)
assimilation(plants, pixsize = 0.2, resource = 1, influence = gnomon_inf, infpar = NULL, asym = Inf, efficiency = flat_eff, effpar = NULL, plot = TRUE, afree = FALSE, centroid = FALSE) assimilation_pix(plants, pixsize = 0.2, resource = 1, influence = gnomon_inf, infpar = NULL, asym = Inf, efficiency = flat_eff, effpar = NULL, plot = TRUE, afree = FALSE, centroid = FALSE)
plants |
A spatstat point pattern object (class |
pixsize |
Resolution, approximate step size in the pixel grid. Default 0.2. |
resource |
Either a pixel image (class |
influence |
Function for computing influence values. Must take arguments |
infpar |
Parameter(s) for |
asym |
Asymmetry parameter |
efficiency |
Efficiency function for weighting the point-wise resource uptake. Must take arguments |
effpar |
Parameter(s) for |
plot |
If |
afree |
If |
centroid |
If |
assimilation()
and assimilation_pix()
are functionally equivalent, but the code in assimilation_pix()
is somewhat clearer and slower. It may be useful for documentation purposes, and as a basis for user modification.
Computation starts with a resource intensity map at a spatial resolution given by pixsize
. Typically the resource distribution is assumed to be uniform (the default). Plants exert competitive pressure depending on size and distance, described by the influence
function. The resource available at each pixel is allotted to plants according to their influence and to an allotment rule parametrized by asym
. Finally, the resource uptake is weighted by the efficiency
function, and is spatially integrated to obtain the plant's assimilation index. Besides size, influence and efficiency functions can include other plant attributes, such as species.
Returns the input point pattern plants
, with the marks replaced by a data frame containing the original marks followed by one or more columns containing the computed results. The additional column are the assimilation indices in column aindex
, and optionally the free-growing index in afree
, and/or the x and y centroid coordinates in cx
and cy
.
Requires the spatstat package.
Oscar García.
https://github.com/ogarciav/siplab.
García, O. (2013) “A generic approach to spatial individual-based modelling and simulation of plant communities”. Mathematical and Computational Forestry and Nat.-Res. Sci. (MCFNS) 6(1), 36-47. 2014.
a <- assimilation(finpines, infpar=list(a=1, b=4, smark="height"), afree=TRUE) summary(a) system.time(assimilation_pix(finpines)) system.time(assimilation(finpines))
a <- assimilation(finpines, infpar=list(a=1, b=4, smark="height"), afree=TRUE) summary(a) system.time(assimilation_pix(finpines)) system.time(assimilation(finpines))
Four data sets from the Boreal Ecosystem–Atmosphere Study (BOREAS, Rich and Fournier 1999), as used by García (2006). These are approximately evenaged and single-species unmanaged natural forests, from a northern study area in Manitoba and a southern study area in Saskatchewan, central Canada. Tree coordinates and diameters at breast height (dbh) were measured for all trees taller than 2 m on areas of 50 m 60 m, subdivided into subplots on a 10 m grid. Tree heights were estimated from height-dbh regressions based on a sample of height measurements. The data here excludes dead trees, and also excludes some trees with coordinates just outside the observation window.
The 4 data sets are:
boreasNP
:Northern study area, Jack pine
boreasNS
:Northern study area, black spruce
boreasSA
:Southern study area, trembling aspen
boreasSP
:Southern study area, Jack pine
Each data set is a spatstat marked point pattern object (class ppp
). The marks are a data frame with dbh
(cm), height
(m), species
, a dominance
classification, and a subplot id.
Rich, P.M., and Fournier, R. (1999) BOREAS TE-23 map plot data [online]. Oak Ridge National Laboratory Distributed Active Archive Center, Oak Ridge, Tennessee. Available from http://daac.ornl.gov.
García, O. (2006) Scale and spatial structure effects on tree size distributions: Implications for growth and yield modelling. Canadian Journal of Forest Research 36(11), 2983–2993. doi:10.1139/x06-116.
summary(boreasNP) plot(boreasNP) ## Not run: aNP <- assimilation(boreasNP) # this may take a few minutes!
summary(boreasNP) plot(boreasNP) ## Not run: aNP <- assimilation(boreasNP) # this may take a few minutes!
Shrink a point pattern, or expand it through replication.
edges(plants, width) core(plants, distance)
edges(plants, width) core(plants, distance)
plants |
A spatstat point pattern object (class |
width |
Distance from the edges to shrink, if negative, or to expand, if positive. |
distance |
Distance from the edges. |
When computing assimilation or competition indices, those near the edges of the study region are distorted because the outside is empty. Common solutions to this problem are not to use indices computed for plants near the edges, or (with rectangular regions) to attach translated copies, thus changing the topology intothat of a torus. This function implements both strategies. When expanding, the extentt of the copies to be used can be specified to avoid unnecessary computation.
Typically, in the first case the indices are computed for the full pattern, and then the edges are discarded using edges()
with a negative width
. In the second case, the point pattern is first expanded with edges(plants, width)
, the indices are computed for the expanded pattern, and then the resulta are restricted to the original size with edges(result, -width)
.
core()
returns a logical vector indicating which plants are at more than the given distance from the edges. Thus, plants[core(plants, width)]
is equivalent to edges(plants, -width)
.
edges()
returns a point pattern with the same structure as plants
.
If width
is negative, the parts of the pattern that are at a distance less than -width
from an edge are discarded.
If width
is positive, the pattern is first expanded by surrounding it with 8 shifted copies (the window must be rectangular). Then, the parts of the pattern that are at a distance greater than width
from an edge of the original pattern are discarded.
If width
= 0, plants
is returned unchanged.
core()
returns a logical vector with TRUE
for the plants that are at more than the given distance from the edges, and FALSE
for the rest.
Requires the spatstat package.
Oscar García.
https://github.com/ogarciav/siplab
finpines edges(finpines, 3) edges(finpines, -3)
finpines edges(finpines, 3) edges(finpines, -3)
Compute efficiency values depending on distance and plant marks, for use in assimilation()
.
Note: In previous versions of siplab the function names had
.eff
in place of _eff
.
flat_eff(dx, dy, marks) tass_eff(dx, dy, marks, par = list(b = 3.52 * 0.975, c = 6.1, smark = 1)) gates_eff(dx, dy, marks, par = list(a = 1, b = 4, smark = 1)) gnomon_eff(dx, dy, marks, par = list(a = 1, b = 4, smark = 1)) power_eff(dx, dy, marks = NULL, par = list(a = 1, b = 0.25, smark = NULL))
flat_eff(dx, dy, marks) tass_eff(dx, dy, marks, par = list(b = 3.52 * 0.975, c = 6.1, smark = 1)) gates_eff(dx, dy, marks, par = list(a = 1, b = 4, smark = 1)) gnomon_eff(dx, dy, marks, par = list(a = 1, b = 4, smark = 1)) power_eff(dx, dy, marks = NULL, par = list(a = 1, b = 0.25, smark = NULL))
dx |
Vector of x-distances. Coordinates x for a number of points minus coordinate x of the subject plant. |
dy |
Vector of y-distances. Coordinates y for a number of points minus coordinate y of the subject plant. |
marks |
Mark data for the subject plant. |
par |
Optional vector or list of parameters. |
The user can program her/his own efficiency function. It must take the arguments dx
, dy
, marks
, and optionally par
.
Efficiency function values are normally non-negative. Otherwise, they are set to 0 in assimilation()
.
The values of par
are taken from the argument effpar
of assimilation()
, if not NULL
. Otherwise the default is used.
smark
in par
must be 1 or “mark” if there is only one mark. If the marks are a data frame, smark
must be the number or name of the column with the plant size variable.
flat_eff()
returns 1, independently of plant size or distance.
tass_eff()
, gates_eff()
, and gnomon_eff()
are proportional to their influence function counterparts (see influence
), scaled to be 1 at the origin.
power_eff()
is , where
is the plant-to-point distance. It does not depend on plant size, as in Garcia (2022). On second thoughts, this might make more sense than the functions above. Parameters
marks
and smarks
are ignored.
Vector of efficiency values, of length equal to the length of dx and dy.
Oscar García.
https://github.com/ogarciav/siplab
García, O. “A generic approach to spatial individual-based modelling and simulation of plant communities”. Mathematical and Computational Forestry and Nat.-Res. Sci. (MCFNS) 6(1), 36-47. 2014.
García, O. “Plasticity as a link between spatially explicit, distance-independent, and whole-stand forest growth models”. Forest Science 68(1), 1-7. 2022.
# Example multi-species efficiency function (spruce/hardwoods) multi_eff <- function (dx, dy, marks, par) { out <- numeric(length(dx)) s <- marks$SPECIES == "Spruce" out[s] <- gnomon_eff(dx[s], dy[s], marks[s, ], par=list(a=par$aS, b=par$bS, smark=par$smark)) out[!s] <- gnomon_eff(dx[!s], dy[!s], marks[!s, ], par=list(a=par$aH, b=par$bH, smark=par$smark)) # Hardwoods return(out) }
# Example multi-species efficiency function (spruce/hardwoods) multi_eff <- function (dx, dy, marks, par) { out <- numeric(length(dx)) s <- marks$SPECIES == "Spruce" out[s] <- gnomon_eff(dx[s], dy[s], marks[s, ], par=list(a=par$aS, b=par$bS, smark=par$smark)) out[!s] <- gnomon_eff(dx[!s], dy[!s], marks[!s, ], par=list(a=par$aH, b=par$bH, smark=par$smark)) # Hardwoods return(out) }
Compute influence values depending on distance and plant marks, for use in assimilation()
.
Note: In previous versions of siplab the function names had
.inf
in place of _inf
.
zoi_inf(dx, dy, marks, par = list(k = 0.2, smark = 1)) tass_inf(dx, dy, marks, par = list(b = 3.52 * 0.975, c = 6.1, smark = 1)) gates_inf(dx, dy, marks, par = list(a = 1, b = 4, smark = 1)) gnomon_inf(dx, dy, marks, par = list(a = 1, b = 4, smark = 1))
zoi_inf(dx, dy, marks, par = list(k = 0.2, smark = 1)) tass_inf(dx, dy, marks, par = list(b = 3.52 * 0.975, c = 6.1, smark = 1)) gates_inf(dx, dy, marks, par = list(a = 1, b = 4, smark = 1)) gnomon_inf(dx, dy, marks, par = list(a = 1, b = 4, smark = 1))
dx |
Vector of x-distances. Coordinates x for a number of points minus coordinate x of the subjectt plant. |
dy |
Vector of y-distances. Coordinates y for a number of points minus coordinate y of the subjectt plant. |
marks |
Mark data for the subject plant. |
par |
List of parameters. |
The user can program her/his own influence function. It must take the arguments dx
, dy
, marks
, and optionally par
.
Influence function values are normally non-negative. Otherwise, they are set to 0 in assimilation()
.
The values of par
are taken from the argument infpar
of assimilation()
, if not NULL
. Otherwise the default is used.
smark
in par
must be 1 or “mark” if there is only one mark. If the marks are a data frame, smark
must be the number or name of the column with the plant size variable.
Let be the plant size, and
be the Euclidean plant-to-point distance
. Then the built-in influence functions are:
zoi_inf()
:1 if , 0 otherwise
tass_inf()
:gates_inf()
:gnomon_inf()
:Other influence functions can be written following these examples.
Vector of influence values, of length equal to the length of dx and dy.
Oscar García.
https://github.com/ogarciav/siplab
García, O. “A generic approach to spatial individual-based modelling and simulation of plant communities”. Mathematical and Computational Forestry and Nat.-Res. Sci. (MCFNS) 6(1), 36-47. 2014.
# Example multi-species influence function (spruce/hardwoods) multi_inf <- function (dx, dy, marks, par) { out <- numeric(length(dx)) s <- marks$SPECIES == "Spruce" out[s] <- gnomon_inf(dx[s], dy[s], marks[s, ], par=list(a=par$aS, b=par$bS, smark=par$smark)) out[!s] <- gnomon_inf(dx[!s], dy[!s], marks[!s, ], par=list(a=par$aH, b=par$bH, smark=par$smark)) # Hardwoods return(out) }
# Example multi-species influence function (spruce/hardwoods) multi_inf <- function (dx, dy, marks, par) { out <- numeric(length(dx)) s <- marks$SPECIES == "Spruce" out[s] <- gnomon_inf(dx[s], dy[s], marks[s, ], par=list(a=par$aS, b=par$bS, smark=par$smark)) out[!s] <- gnomon_inf(dx[!s], dy[!s], marks[!s, ], par=list(a=par$aH, b=par$bH, smark=par$smark)) # Hardwoods return(out) }
Functions representing the effect of a competitor on a subject plant, depending on distance and plant sizes (marks). For use in pairwise()
.
Note: In previous versions of siplab the function names had
.ker
in place of _ker
.
powers_ker(imarks, jmarks, dists, dranks, par = list(pi=1, pj=1, pr=1, smark = 1)) staebler_ker(imarks, jmarks, dists, dranks, par = list(k=0.1, p=1, smark=1)) spurr_ker(imarks, jmarks, dists, dranks, par = list(type=1, smark=1))
powers_ker(imarks, jmarks, dists, dranks, par = list(pi=1, pj=1, pr=1, smark = 1)) staebler_ker(imarks, jmarks, dists, dranks, par = list(k=0.1, p=1, smark=1)) spurr_ker(imarks, jmarks, dists, dranks, par = list(type=1, smark=1))
imarks |
Marks for the subject plant, a 1-row data frame. |
jmarks |
Data frame with marks for competitors |
dists |
Vector of distances between the subject plant and the competitors. |
dranks |
Distance ranks. |
par |
List of parameters. |
The values of par
are taken from the argument kerpar
of pairwise()
, if not NULL
.
smark
in par
must be 1 or “mark” if there is only one mark. If the marks are a data frame, smark
must be the number or name of the column with the plant size variable.
powers_ker()
is a general form that includes many examples from the literature. If is the size of the subject plant,
the size of the competitor, and
is the distance between them, then this kernel is
. For instance, the popular Hegyi's index corresponds to
pi=1, pj=1, pr=1
.
This and other examples could be coded directly if computational efficiency is important, see the example below.
staebler_ker()
is the width of the overlap of zones of influence (ZOI), used by Staebler in 1951. Assumes that the ZOI radius is , where
is size.
spurr_ker()
is an example of an index that depends on distance ranks: equations (9.5a), (9.5b) of Burkhart and Tomé (2012).
Competition kernels seem to be limited only by the researchers imagination. Others can be written following these examples.
Vector of length equal to the length of dists
.
Oscar García.
https://github.com/ogarciav/siplab
Burkhart, H. E. and Tomé, M. (2012) Modeling Forest Trees and Stands. Springer.
García, O. “Siplab, a spatial individual-based plant modelling system”. Computational Ecology and Software 4(4), 215-222. 2014.
# Originally Hegyi added one foot to the distance: hegyiorig_ker <- function(imarks, jmarks, dists, ...) { # Assume coordinates in meters, and a single mark (dbh) (jmarks$mark / imarks$mark) / (dists + 0.30481) }
# Originally Hegyi added one foot to the distance: hegyiorig_ker <- function(imarks, jmarks, dists, ...) { # Assume coordinates in meters, and a single mark (dbh) (jmarks$mark / imarks$mark) / (dists + 0.30481) }
This function computes competition indices based on pairs of plants, ignoring higher-order interactions.
pairwise(plants, maxN = NULL, maxR = NULL, select = NULL, selpar = NULL, kernel, kerpar = NULL)
pairwise(plants, maxN = NULL, maxR = NULL, select = NULL, selpar = NULL, kernel, kerpar = NULL)
plants |
A spatstat point pattern object (class |
maxN |
Maximum number of nearest neighbors to include as potential competitors. Default is NULL (no restriction). |
maxR |
Maximum radius to search for potential competitors. Default is NULL (no restriction). |
select |
Optional user-supplied selection function for choosing competitors. Must take arguments |
selpar |
Parameter(s) for |
kernel |
Competition kernel function for computing the effect of competitor |
kerpar |
Parameter(s) for |
Traditionally, the competition index for a subject plant is obtained in two stages: (1) Choose a set of competitors of
by some selection rule. (2) Compute a measure of the effect of each competitor
on plant
, and add over
. This effect of
on
is normally a function of the sizes of both plants and of the distance between them, which we call a competition kernel. The kernel may depend on other plant attributes, like species, and in some rare instances on the distance ranks or on the number of competitors. Conceptually, the first stage is not strictly necessary, it could be replaced by specifying zero kernel values (the effect of the selection is usually to truncate the kernel function beyond some distance). However, a separate selection rule may be more transparent, and may reduce the computational effort in searching for neighbors.
Some simple selection rules can be implemented by giving a value to maxN
and/or maxR
. In any case, reasonable limits on these variables may be advisable for reducing computation.
More complex rules can be specified by the select
function, with parameters in selpar
. See select
for examples. If more than one of maxN
, maxR
or select
are given, the intersection of the selection criteria is used.
Kernel computation is specified by the kernel
function and the parameters in kerpar
. See kernel
for examples.
Returns the input point pattern plants
, with the marks replaced by a data frame containing the original marks followed by the competition index in a column named cindex
.
Requires the spatstat package.
Oscar García.
https://github.com/ogarciav/siplab
García, O. “Siplab, a spatial individual-based plant modelling system”. Computational Ecology and Software 4(4), 215-222. 2014.
# Hegyi (1974) index (no distance offset, as usual) summary(pairwise(finpines, maxR = 6, kernel=powers_ker))
# Hegyi (1974) index (no distance offset, as usual) summary(pairwise(finpines, maxR = 6, kernel=powers_ker))
Functions returning TRUE for plants that compete with a given subject plant, or FALSE otherwise. The decision can depend on distance and plant marks. For use in pairwise()
.
Note: In previous versions of siplab the function names had
.sel
in place of _sel
.
size_sel(imarks, jmarks, dists, dranks, par = list(k = 0.2, smark = 1)) powlinear_sel(imarks, jmarks, dists, dranks, par = list(ki = 0.2, kj = 0, p = 1, r0 = 0, smark=1))
size_sel(imarks, jmarks, dists, dranks, par = list(k = 0.2, smark = 1)) powlinear_sel(imarks, jmarks, dists, dranks, par = list(ki = 0.2, kj = 0, p = 1, r0 = 0, smark=1))
imarks |
Marks for the subject plant, a 1-row data frame. |
jmarks |
Data frame with marks for potential competitors |
dists |
Vector of distances between the subject plant and the potential competitors. |
dranks |
Distance ranks. |
par |
List of parameters. |
The values of par
are taken from the argument selpar
of pairwise()
, if not NULL
.
smark
in par
must be 1 or “mark” if there is only one mark. If the marks are a data frame, smark
must be the number or name of the column with the plant size variable.
size_sel()
is a simple example where competitors are selected within a radius proportional to plant size. This corresponds to the second example in Section 9.2.1 of Burkhart and Tomé (2012).
Note that their first example (fixed radius) is implemented by giving a value to maxR
in pairwise()
, no select
function is needed. Similarly, their third example (fixed number of nearest neighbors) is obtained by giving a value to maxN
.
powlinear_sel()
is a general form that covers all the other examples in Burkhart and Tomé (2012) by choosing specific parameters values (except for the competition elimination angle, which depends on relative positions among competitors and not only on distances).
It implements a condition distance < ki * sizei^p + kj * sizej^p + r0
, with the following special cases:
Multiple of crown radius: kj=0, p=1, r0=0, smark="crownwidth"
.
Angle count sampling: ki=0, p=1, r0=0, smark="dbh"
.
Areas of influence overlap: ki=kj, p=1, r0=0
, if the radius is a linear function of size (p
not 1 for an allometric relationship).
Vertical search cone: If the height of the cone vertex is constant, proportional to tree height, or more generally some linear function , then
ki
= ,
kj
= ,
p=1
, r0
= ,
smark="height"
.
These and other examples could be coded directly if computational efficiency is important.
Logical vector of length equal to the length of dists
.
Oscar García.
https://github.com/ogarciav/siplab
Burkhart, H. E. and Tomé, M. (2012) Modeling Forest Trees and Stands. Springer.