Modelling above- and below-ground competition together Winfried Kurth University of Göttingen Chair...

Post on 13-Dec-2015

215 views 1 download

Transcript of Modelling above- and below-ground competition together Winfried Kurth University of Göttingen Chair...

Modelling above- and below-ground competition together

Winfried Kurth

University of Göttingen Chair for Computer Graphics and Ecological Informatics

wk<at>informatik.uni-goettingen.de

Summer School „Modelling and Simulation with GroIMP“, Göttingen, 27 Sept.- 1st Oct. 2010

Problem:

observation from ecology (A. Polle, oral communication):

under optimal conditions (plenty of resources):

mono-species stand gives higher yield than mixed stand

under stress (lack of resources):

mixed stand outperforms mono-species stand

biomass

stress

mono-species

multi-species

How to model this in a simple way in GroIMP ?

assume 2 species, competing at two levels (e.g., above-/below-ground)

- at 1st level: allelopathic interaction

i.e. species A inhibits growth of species B in its neighbourhood, competition between A and B is stronger than between A and A or between B and B

- at 2nd level: complementarity between species A and B,

e.g. both compete for the same soil resource but at different depths intraspecific competition is stronger than that between A and B

Now if we decrease the availability of the soil resource (i.e., induce stress), the competition at 2nd level will gradually dominate that at 1st level; hence the advantage of the monospecies composition will vanish.

Objects of the model:

module Stand;module Seedling(int species);

module Tree(float size, int age, int species) /* size = radius */ { double nutr = satNut; } ==> if (species == 1) ( c:Cone.(setLength(3*size), setRadius(size)) {{c.setColor(0x007700);}} ) else ( Translate(0, 0, 2*size) Scale(1.0, 1.0, 2.0) s:Sphere.(setRadius(size)) {{s.setColor(0x22ee00);}} ); module Soil;

module SoilPatch(int k) extends Box(3.0, 3.0, 3.0).(setColor(0xffaa88)) { float nutrAvail = startNutr; float nutrDemand, nutrTransf; int nbPlants; }

Competition at level 1 (allelopathy):

boolean notOutcompeted(Tree a) { return empty( (* t:Tree, ((t!=a) && (( t[species] == a[species] && distance(a, t) <= inhib1 * t[size] ) || ( t[species] != a[species] && distance(a, t) <= inhib2 * t[size] )) && (t[size] >= a[size]) ) *) ); }

a

t

const double inhib1 = 1.; /* inhibition zone: competitors of same species */const double inhib2 = 2.; /* inhibition zone: competitors of other species */

Competition at level 2 (complementarity):

each plant takes nutrients from all soil patches within its depletion radius

species 1 from layer 1, species 2 from layer 2

Renewal of nutrients in a soil patch (from influx, mineralization etc.):

protected void updateNutr() [ p:SoilPatch ::> { p[nutrAvail] += renewRate; p[nutrAvail] = Math.min(p[nutrAvail], patchCapacity); p[nbPlants] = 0; p[nutrTransf] = 0.; p[nutrDemand] = 0.; colorize(p); } ]

Checking the total demand exerted on a single soil patch:

protected void checkDemand() [ t:Tree, p:SoilPatch, (inDepletionZone(p, t)) ::> { p[nutrDemand] += maxConsumption; p[nbPlants]++; } ]

number of plants taking nutrients from this soil patch p

How to check if p is in the depletion zone of t :

boolean inDepletionZone(SoilPatch p, Tree a) { return (p[k] == a[species] && projDistance(p, a) <= depletionRadius); }

double projDistance(SoilPatch p, Tree a) /* distance in xy plane */ { double dx = p[Location.X] - a[Location.X]; double dy = p[Location.Y] - a[Location.Y]; return Math.sqrt(dx*dx + dy*dy); }

The amount of nutrient which one plant can really obtain from this soil patch is calculated and written to the soil patch attribute nutrTransf :

protected void writeDemand() [ p:SoilPatch ::> { if (p[nbPlants] > 0) p[nutrTransf] = Math.min(p[nutrAvail], p[nutrDemand]) / (double) p[nbPlants]; } ]

Nutrient transfer from soil to plants takes place:

protected void transferNutr() [ t:Tree, p:SoilPatch, (inDepletionZone(p, t)) ::> { t[nutr] += p[nutrTransf]; p[nutrAvail] -= p[nutrTransf]; } ]

protected void grow() [ a:Tree(size, age, k) ==> if (notOutcompeted(a) && a[nutr] >= minNeed) ( t:Tree(growthf(size, age, a[nutr]), age+1, k) { t[nutr] = 0.; } ); ]

public float growthf(double x, int age, double nutr) { double incr; if (nutr < minNeed) incr = 0.; else { if (nutr < satNut) incr = ((nutr-minNeed)/(satNut-minNeed))*maxIncr; else incr = maxIncr; } if (age < max_age) return (float) (x+incr); else return (float) x; }

Growth of a plant depends on its size, age, and accumulated nutrients during the last time step.

All nutrients are consumed during the growth step:

nutr

incr

minNeed satNut

maxIncr

protected void init() { step = 1; masstable.clear().setColumnKey(0, "biomass"); chart(masstable, XY_PLOT); int nbPx = (int) Math.floor((x_extens + depletionRadius) / patchDist); int nbPy = (int) Math.floor((y_extens + depletionRadius) / patchDist); for ( apply(3) ) /* 3 steps of rule application are carried out: */ [ Axiom ==> [ Translate(x_extens/2, y_extens/2, -1) /* soil surface: */ Box(0.2, x_extens, y_extens).(setColor(0xffffcc)) ] Stand Soil; Stand ==> for ((1:n1)) ( [ Translate(random(0, x_extens), random(0, y_extens), 0) Seedling(1) ] ) for ((1:n2)) ( [ Translate(random(0, x_extens), random(0, y_extens), 0) Seedling(2) ] ); Soil ==> for (int i: (1:nbPx)) ( for (int j: (1:nbPy)) ( for (int k: (1:2)) /* two layers of patches */ ([ Translate(-0.5*depletionRadius + i*patchDist, -0.5*depletionRadius + j*patchDist, -k*layerDist) p:SoilPatch(k) { colorize(p); } ]) ) ); s:Seedling(k) ==> if (s[Location.X] >= 0 && s[Location.Y] >= 0 && s[Location.X] <= x_extens && s[Location.Y] <= y_extens) ( Tree(random(1.0, 5.0), 1, k) ); /* size = random, age = 1, species = k */ ] }

Initialization of the whole scene:

Colorizing the patches for visualization of nutrient content:

void colorize(SoilPatch p) { double a = p[nutrAvail]; if (a >= 0.875*patchCapacity) p.setColor(0xff4444); else if (a >= 0.75*patchCapacity) p.setColor(0xff7744); else if (a >= 0.625*patchCapacity) p.setColor(0xffaa44); else if (a >= 0.5*patchCapacity) p.setColor(0xffdd44); else if (a >= 0.375*patchCapacity) p.setColor(0xffff55); else if (a >= 0.25*patchCapacity) p.setColor(0xffff88); else if (a >= 0.125*patchCapacity) p.setColor(0xffffbb); else p.setColor(0xffffff); }

(red yellow white)

Main rule block which controls the order of rule applicationsand adds a biomass entry after each growth step for a chart:

public void run() { updateNutr(); checkDemand(); writeDemand(); transferNutr(); grow(); masstable.addRow().set(0, sum( biomass( (* t:Tree *) ))); println(step); step++; }

public float biomass(Tree a) /* assumed as proportional to basal area */ { return (float) (Math.PI * a[size] * a[size]); }

const int n1 = 200; /* nb. of trees of species 1 */const int n2 = 200; /* nb. of trees of species 2 */const int max_age = 40; /* number of growth steps */const double x_extens = 500.;const double y_extens = 300.;const double inhib1 = 1.; /* inhibition zone: competitors of same species */const double inhib2 = 2.; /* inhibition zone: competitors of other species */

const double renewRate = 20.0; /* let this vary! */const double patchCapacity = 50.0; const double startNutr = 50.0; const double maxConsumption = 0.4; /* max. consumption of a tree from a single soil patch; 0.4 */const double patchDist = 15.;const double depletionRadius = 40.;const double layerDist = 5.;const double minNeed = 4.; /* necessary min. amount of nutrients for survival */const double satNut = 10.; /* saturation point of nutrient response function */const double maxIncr = 0.7; /* max. tree growth rate */

int step; /* step counter */

const DatasetRef masstable = new DatasetRef("stand biomass"); /* table for chart */

for completeness:parameters of the model and variable declarations

example file div_compet2b.rgg

Resulting stand biomass under different parameterizations

(unstressed = plenty of nutrients by high renewal rate in the soil;stressed = competition for nutrients is severely limiting)