Introducing leaf growth
Open the file LeafGrowth.gsz.
Currently, the leaves and internodes of our plant just “pop up” with their final size, although in a real plant they would, of course, grow. Growth in length can be modelled easily: first, we need to give both Leaf
and Internode
several new parameters: length
and age
for Internode
; and length
, width
and age
for Leaf
(L. 9 and L. 14).
module Internode(super.length) extends Cylinder(length, 0.05) { {setShader(internodemat);} } module Leaf(super.length, super.width, float al) extends Box(length,width,0.01). (setShader(new AlgorithmSwitchShader(new RGBAShader(0, 1, 0), GREEN)));
Then we initialize the length
(and width
) with a small value (0.1 and 0.07), and set the initial age
to 1 in the init()
method:
Bud(r, p, o), (r < 10 && p == 0 && o <= 3) ==> RV(-0.1) Internode(0.1, 1) Node [RL(BRANCH_ANGLE) Bud(r, PHYLLOCHRON, o+1) ] [ RL(LEAF_ANGLE) Leaf(0.1, 0.07, 0)] RH(GOLDEN_ANGLE) RV(-0.1) Internode(0.1) Bud(r+1, PHYLLOCHRON, o);
Next, we need to find an appropriate function that describes growth. The logistic function is actually quite accurate when it comes to description of organ length. It is one of the many sigmoid functions. Its derivative can be used to describe the growth rate as a function of organ age. We declare the derivative of this logistic function as a new public method in the code, before the init()
method:
//logistic function, used to determine growth rate: public float logistic (float maxdim, int time, float phylloM, float slope) { return(slope*maxdim*Math.exp(-slope*(time-phylloM)))/ ((Math.exp(-slope*(time-phylloM))+1)**2); }
The function has three parameters, for now we are only interested in organ age. This we declare as a further (integer) parameter in Leaf and Internode:
module Internode(super.length, int age) extends Cylinder(length, 0.05) { {setShader(internodemat);} } module Leaf(super.length, super.width, float al, int age) extends Box(length,width,0.01). (setShader(new AlgorithmSwitchShader(new RGBAShader(0, 1, 0), GREEN)));
Next, we need to initialize age
with 1 in the run()
method (L. 103).
Bud(r, p, o), (r < 10 && p == 0 && o <= 3) ==> RV(-0.1) Internode(0.1, 1) Node [RL(BRANCH_ANGLE) Bud(r, PHYLLOCHRON, o+1) ] [ RL(LEAF_ANGLE) Leaf(0.1, 0.07, 0, 1)] RH(GOLDEN_ANGLE) RV(-0.1) Internode(0.1, 1) Bud(r+1, PHYLLOCHRON, o);
Then, we extend the Leaf
rule in the method that we now rename to absorbAndGrow()
to model leaf ageing and extension in length and width:
protected void absorbAndGrow() [ lf:Leaf ::> { lf[al] = lm.getAbsorbedPower3d(lf).integrate()*2.25; //println(lf[al]); lf.(setShader(new AlgorithmSwitchShader(new RGBAShader(lf[al]/5.0, lf[al]*2,lf[al]/100.0),GREEN))); //ageing and growth of leaf: lf[age]++; lf[length] += logistic(3,lf[age],40,0.1); lf[width] = lf[length]*0.7; } ]
Finally, we add another execution (update) rule to model internode extension:
protected void absorbAndGrow() [ lf:Leaf ::> { lf[al] = lm.getAbsorbedPower3d(lf).integrate()*2.25; //println(lf[al]); lf.(setShader(new AlgorithmSwitchShader(new RGBAShader(lf[al]/5.0, lf[al]*2,lf[al]/100.0),GREEN))); //ageing and growth of leaf: lf[age]++; lf[length] += logistic(3,lf[age],40,0.1); lf[width] = lf[length]*0.7; } //extension of internode: itn:Internode ::> { itn[age]++; itn[length] += logistic(1,itn[age],10,0.1); } ]
Task: Change the leaf growth parameters to obtain a very flat or else a rather steep light interception curve! (see figures below)