Subversion Repositories public iLand

Rev

Rev 1220 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 1220 Rev 1221
1
Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/saplings.cpp':
1
Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/saplings.cpp':
2
/********************************************************************************************
2
/********************************************************************************************
3
**    iLand - an individual based forest landscape and disturbance model
3
**    iLand - an individual based forest landscape and disturbance model
4
**    http://iland.boku.ac.at
4
**    http://iland.boku.ac.at
5
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
5
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
6
**
6
**
7
**    This program is free software: you can redistribute it and/or modify
7
**    This program is free software: you can redistribute it and/or modify
8
**    it under the terms of the GNU General Public License as published by
8
**    it under the terms of the GNU General Public License as published by
9
**    the Free Software Foundation, either version 3 of the License, or
9
**    the Free Software Foundation, either version 3 of the License, or
10
**    (at your option) any later version.
10
**    (at your option) any later version.
11
**
11
**
12
**    This program is distributed in the hope that it will be useful,
12
**    This program is distributed in the hope that it will be useful,
13
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
**    GNU General Public License for more details.
15
**    GNU General Public License for more details.
16
**
16
**
17
**    You should have received a copy of the GNU General Public License
17
**    You should have received a copy of the GNU General Public License
18
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
********************************************************************************************/
19
********************************************************************************************/
20
#include "global.h"
20
#include "global.h"
21
#include "saplings.h"
21
#include "saplings.h"
22
22
23
#include "globalsettings.h"
23
#include "globalsettings.h"
24
#include "model.h"
24
#include "model.h"
25
#include "resourceunit.h"
25
#include "resourceunit.h"
26
#include "resourceunitspecies.h"
26
#include "resourceunitspecies.h"
27
#include "establishment.h"
27
#include "establishment.h"
28
#include "species.h"
28
#include "species.h"
29
#include "seeddispersal.h"
29
#include "seeddispersal.h"
30
#include "mapgrid.h"
30
#include "mapgrid.h"
31
31
32
double Saplings::mRecruitmentVariation = 0.1; // +/- 10%
32
double Saplings::mRecruitmentVariation = 0.1; // +/- 10%
33
double Saplings::mBrowsingPressure = 0.;
33
double Saplings::mBrowsingPressure = 0.;
34
34
35
35
36
Saplings::Saplings()
36
Saplings::Saplings()
37
{
37
{
38
38
39
}
39
}
40
40
41
void Saplings::setup()
41
void Saplings::setup()
42
{
42
{
43
    //mGrid.setup(GlobalSettings::instance()->model()->grid()->metricRect(), GlobalSettings::instance()->model()->grid()->cellsize());
43
    //mGrid.setup(GlobalSettings::instance()->model()->grid()->metricRect(), GlobalSettings::instance()->model()->grid()->cellsize());
44
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
44
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
45
    // mask out out-of-project areas
45
    // mask out out-of-project areas
46
    HeightGrid *hg = GlobalSettings::instance()->model()->heightGrid();
46
    HeightGrid *hg = GlobalSettings::instance()->model()->heightGrid();
47
    for (int i=0; i<lif_grid->count(); ++i) {
47
    for (int i=0; i<lif_grid->count(); ++i) {
48
        SaplingCell *s = cell(lif_grid->indexOf(i), false); // false: retrieve also invalid cells
48
        SaplingCell *s = cell(lif_grid->indexOf(i), false); // false: retrieve also invalid cells
49
        if (s) {
49
        if (s) {
50
            if (!hg->valueAtIndex(lif_grid->index5(i)).isValid())
50
            if (!hg->valueAtIndex(lif_grid->index5(i)).isValid())
51
                s->state = SaplingCell::CellInvalid;
51
                s->state = SaplingCell::CellInvalid;
52
            else
52
            else
53
                s->state = SaplingCell::CellFree;
53
                s->state = SaplingCell::CellFree;
54
        }
54
        }
55
55
56
    }
56
    }
57
57
58
}
58
}
59
59
60
void Saplings::calculateInitialStatistics(const ResourceUnit *ru)
60
void Saplings::calculateInitialStatistics(const ResourceUnit *ru)
61
{
61
{
62
    SaplingCell *sap_cells = ru->saplingCellArray();
62
    SaplingCell *sap_cells = ru->saplingCellArray();
63
    if (!sap_cells)
63
    if (!sap_cells)
64
        return;
64
        return;
65
65
66
    SaplingCell *s = sap_cells;
66
    SaplingCell *s = sap_cells;
67
67
68
    for (int i=0; i<cPxPerHectare; ++i, ++s) {
68
    for (int i=0; i<cPxPerHectare; ++i, ++s) {
69
        if (s->state != SaplingCell::CellInvalid) {
69
        if (s->state != SaplingCell::CellInvalid) {
70
            int cohorts_on_px = s->n_occupied();
70
            int cohorts_on_px = s->n_occupied();
71
            for (int j=0;j<NSAPCELLS;++j) {
71
            for (int j=0;j<NSAPCELLS;++j) {
72
                if (s->saplings[j].is_occupied()) {
72
                if (s->saplings[j].is_occupied()) {
73
                    SaplingTree &tree=s->saplings[j];
73
                    SaplingTree &tree=s->saplings[j];
74
                    ResourceUnitSpecies *rus = tree.resourceUnitSpecies(ru);
74
                    ResourceUnitSpecies *rus = tree.resourceUnitSpecies(ru);
75
                    rus->saplingStat().mLiving++;
75
                    rus->saplingStat().mLiving++;
76
                    double n_repr = rus->species()->saplingGrowthParameters().representedStemNumberH(tree.height) / static_cast<double>(cohorts_on_px);
76
                    double n_repr = rus->species()->saplingGrowthParameters().representedStemNumberH(tree.height) / static_cast<double>(cohorts_on_px);
77
                    if (tree.height>1.3f)
77
                    if (tree.height>1.3f)
78
                        rus->saplingStat().mLivingSaplings += n_repr;
78
                        rus->saplingStat().mLivingSaplings += n_repr;
79
                    else
79
                    else
80
                        rus->saplingStat().mLivingSmallSaplings += n_repr;
80
                        rus->saplingStat().mLivingSmallSaplings += n_repr;
81
81
82
                    rus->saplingStat().mAvgHeight+=tree.height;
82
                    rus->saplingStat().mAvgHeight+=tree.height;
83
                    rus->saplingStat().mAvgAge+=tree.age;
83
                    rus->saplingStat().mAvgAge+=tree.age;
84
84
85
                }
85
                }
86
            }
86
            }
87
        }
87
        }
88
    }
88
    }
89
89
90
90
91
}
91
}
92
92
93
void Saplings::establishment(const ResourceUnit *ru)
93
void Saplings::establishment(const ResourceUnit *ru)
94
{
94
{
95
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
95
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
96
96
97
    QPoint imap = ru->cornerPointOffset(); // offset on LIF/saplings grid
97
    QPoint imap = ru->cornerPointOffset(); // offset on LIF/saplings grid
98
    QPoint iseedmap = QPoint(imap.x()/10, imap.y()/10); // seed-map has 20m resolution, LIF 2m -> factor 10
98
    QPoint iseedmap = QPoint(imap.x()/10, imap.y()/10); // seed-map has 20m resolution, LIF 2m -> factor 10
99
99
100
    for (QList<ResourceUnitSpecies*>::const_iterator i=ru->ruSpecies().constBegin(); i!=ru->ruSpecies().constEnd(); ++i)
100
    for (QList<ResourceUnitSpecies*>::const_iterator i=ru->ruSpecies().constBegin(); i!=ru->ruSpecies().constEnd(); ++i)
101
        (*i)->saplingStat().clearStatistics();
101
        (*i)->saplingStat().clearStatistics();
102
102
103
    double lif_corr[cPxPerHectare];
103
    double lif_corr[cPxPerHectare];
104
    for (int i=0;i<cPxPerHectare;++i)
104
    for (int i=0;i<cPxPerHectare;++i)
105
        lif_corr[i]=-1.;
105
        lif_corr[i]=-1.;
106
106
107
    int species_idx;
107
    int species_idx;
108
    QVector<int>::const_iterator sbegin, send;
108
    QVector<int>::const_iterator sbegin, send;
109
    ru->speciesSet()->randomSpeciesOrder(sbegin, send);
109
    ru->speciesSet()->randomSpeciesOrder(sbegin, send);
110
    for (QVector<int>::const_iterator s_idx=sbegin; s_idx!=send;++s_idx) {
110
    for (QVector<int>::const_iterator s_idx=sbegin; s_idx!=send;++s_idx) {
111
111
112
        // start from a random species (and cycle through the available species)
112
        // start from a random species (and cycle through the available species)
113
        species_idx = *s_idx;
113
        species_idx = *s_idx;
114
114
115
        ResourceUnitSpecies *rus = ru->ruSpecies()[species_idx];
115
        ResourceUnitSpecies *rus = ru->ruSpecies()[species_idx];
116
        rus->establishment().clear();
116
        rus->establishment().clear();
117
117
118
        // check if there are seeds of the given species on the resource unit
118
        // check if there are seeds of the given species on the resource unit
119
        float seeds = 0.f;
119
        float seeds = 0.f;
120
        Grid<float> &seedmap =  const_cast<Grid<float>& >(rus->species()->seedDispersal()->seedMap());
120
        Grid<float> &seedmap =  const_cast<Grid<float>& >(rus->species()->seedDispersal()->seedMap());
121
        for (int iy=0;iy<5;++iy) {
121
        for (int iy=0;iy<5;++iy) {
122
            float *p = seedmap.ptr(iseedmap.x(), iseedmap.y());
122
            float *p = seedmap.ptr(iseedmap.x(), iseedmap.y());
123
            for (int ix=0;ix<5;++ix)
123
            for (int ix=0;ix<5;++ix)
124
                seeds += *p++;
124
                seeds += *p++;
125
        }
125
        }
126
        // if there are no seeds: no need to do more
126
        // if there are no seeds: no need to do more
127
        if (seeds==0.f)
127
        if (seeds==0.f)
128
            continue;
128
            continue;
129
129
130
        // calculate the abiotic environment (TACA)
130
        // calculate the abiotic environment (TACA)
131
        rus->establishment().calculateAbioticEnvironment();
131
        rus->establishment().calculateAbioticEnvironment();
132
        double abiotic_env = rus->establishment().abioticEnvironment();
132
        double abiotic_env = rus->establishment().abioticEnvironment();
133
        if (abiotic_env==0.) {
133
        if (abiotic_env==0.) {
134
            rus->establishment().writeDebugOutputs();
134
            rus->establishment().writeDebugOutputs();
135
            continue;
135
            continue;
136
        }
136
        }
137
137
138
        // loop over all 2m cells on this resource unit
138
        // loop over all 2m cells on this resource unit
139
        SaplingCell *sap_cells = ru->saplingCellArray();
139
        SaplingCell *sap_cells = ru->saplingCellArray();
140
        SaplingCell *s;
140
        SaplingCell *s;
141
        int isc = 0; // index on 2m cell
141
        int isc = 0; // index on 2m cell
142
        for (int iy=0; iy<cPxPerRU; ++iy) {
142
        for (int iy=0; iy<cPxPerRU; ++iy) {
143
            s = &sap_cells[iy*cPxPerRU]; // pointer to a row
143
            s = &sap_cells[iy*cPxPerRU]; // pointer to a row
144
            isc = lif_grid->index(imap.x(), imap.y()+iy);
144
            isc = lif_grid->index(imap.x(), imap.y()+iy);
145
145
146
            for (int ix=0;ix<cPxPerRU; ++ix, ++s, ++isc) {
146
            for (int ix=0;ix<cPxPerRU; ++ix, ++s, ++isc) {
147
                if (s->state == SaplingCell::CellFree) {
147
                if (s->state == SaplingCell::CellFree) {
148
                    // is a sapling of the current species already on the pixel?
148
                    // is a sapling of the current species already on the pixel?
149
                    // * test for sapling height already in cell state
149
                    // * test for sapling height already in cell state
150
                    // * test for grass-cover already in cell state
150
                    // * test for grass-cover already in cell state
151
                    SaplingTree *stree=0;
151
                    SaplingTree *stree=0;
152
                    SaplingTree *slot=s->saplings;
152
                    SaplingTree *slot=s->saplings;
153
                    for (int i=0;i<NSAPCELLS;++i, ++slot) {
153
                    for (int i=0;i<NSAPCELLS;++i, ++slot) {
154
                        if (!stree && !slot->is_occupied())
154
                        if (!stree && !slot->is_occupied())
155
                            stree=slot;
155
                            stree=slot;
156
                        if (slot->species_index == species_idx) {
156
                        if (slot->species_index == species_idx) {
157
                            stree=0;
157
                            stree=0;
158
                            break;
158
                            break;
159
                        }
159
                        }
160
                    }
160
                    }
161
161
162
                    if (stree) {
162
                    if (stree) {
163
                        // grass cover?
163
                        // grass cover?
164
                        float seed_map_value = seedmap[lif_grid->index10(isc)];
164
                        float seed_map_value = seedmap[lif_grid->index10(isc)];
165
                        if (seed_map_value==0.f)
165
                        if (seed_map_value==0.f)
166
                            continue;
166
                            continue;
167
                        float lif_value = (*lif_grid)[isc];
167
                        float lif_value = (*lif_grid)[isc];
168
168
169
                        double &lif_corrected = lif_corr[iy*cPxPerRU+ix];
169
                        double &lif_corrected = lif_corr[iy*cPxPerRU+ix];
170
                        // calculate the LIFcorrected only once per pixel; the relative height is 0 (light level on the forest floor)
170
                        // calculate the LIFcorrected only once per pixel; the relative height is 0 (light level on the forest floor)
171
                        if (lif_corrected<0.)
171
                        if (lif_corrected<0.)
172
                            lif_corrected = rus->species()->speciesSet()->LRIcorrection(lif_value, 0.);
172
                            lif_corrected = rus->species()->speciesSet()->LRIcorrection(lif_value, 0.);
173
173
174
                        // check for the combination of seed availability and light on the forest floor
174
                        // check for the combination of seed availability and light on the forest floor
175
                        if (drandom() < seed_map_value*lif_corrected*abiotic_env ) {
175
                        if (drandom() < seed_map_value*lif_corrected*abiotic_env ) {
176
                            // ok, lets add a sapling at the given position (age is incremented later)
176
                            // ok, lets add a sapling at the given position (age is incremented later)
177
                            stree->setSapling(0.05f, 0, species_idx);
177
                            stree->setSapling(0.05f, 0, species_idx);
178
                            s->checkState();
178
                            s->checkState();
179
                            rus->saplingStat().mAdded++;
179
                            rus->saplingStat().mAdded++;
180
180
181
                        }
181
                        }
182
182
183
                    }
183
                    }
184
184
185
                }
185
                }
186
            }
186
            }
187
        }
187
        }
188
        // create debug output related to establishment
188
        // create debug output related to establishment
189
        rus->establishment().writeDebugOutputs();
189
        rus->establishment().writeDebugOutputs();
190
    }
190
    }
191
191
192
}
192
}
193
193
194
void Saplings::saplingGrowth(const ResourceUnit *ru)
194
void Saplings::saplingGrowth(const ResourceUnit *ru)
195
{
195
{
196
    HeightGrid *height_grid = GlobalSettings::instance()->model()->heightGrid();
196
    HeightGrid *height_grid = GlobalSettings::instance()->model()->heightGrid();
197
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
197
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
198
198
199
    QPoint imap = ru->cornerPointOffset();
199
    QPoint imap = ru->cornerPointOffset();
200
    bool need_check=false;
200
    bool need_check=false;
201
    SaplingCell *sap_cells = ru->saplingCellArray();
201
    SaplingCell *sap_cells = ru->saplingCellArray();
202
202
203
    for (int iy=0; iy<cPxPerRU; ++iy) {
203
    for (int iy=0; iy<cPxPerRU; ++iy) {
204
        SaplingCell *s = &sap_cells[iy*cPxPerRU]; // ptr to row
204
        SaplingCell *s = &sap_cells[iy*cPxPerRU]; // ptr to row
205
        int isc = lif_grid->index(imap.x(), imap.y()+iy);
205
        int isc = lif_grid->index(imap.x(), imap.y()+iy);
206
206
207
        for (int ix=0;ix<cPxPerRU; ++ix, ++s, ++isc) {
207
        for (int ix=0;ix<cPxPerRU; ++ix, ++s, ++isc) {
208
            if (s->state != SaplingCell::CellInvalid) {
208
            if (s->state != SaplingCell::CellInvalid) {
209
                need_check=false;
209
                need_check=false;
210
                int n_on_px = s->n_occupied();
210
                int n_on_px = s->n_occupied();
211
                for (int i=0;i<NSAPCELLS;++i) {
211
                for (int i=0;i<NSAPCELLS;++i) {
212
                    if (s->saplings[i].is_occupied()) {
212
                    if (s->saplings[i].is_occupied()) {
213
                        // growth of this sapling tree
213
                        // growth of this sapling tree
214
                        const HeightGridValue &hgv = (*height_grid)[height_grid->index5(isc)];
214
                        const HeightGridValue &hgv = (*height_grid)[height_grid->index5(isc)];
215
                        float lif_value = (*lif_grid)[isc];
215
                        float lif_value = (*lif_grid)[isc];
216
216
217
                        need_check |= growSapling(ru, *s, s->saplings[i], isc, hgv.height, lif_value, n_on_px);
217
                        need_check |= growSapling(ru, *s, s->saplings[i], isc, hgv.height, lif_value, n_on_px);
218
                    }
218
                    }
219
                }
219
                }
220
                if (need_check)
220
                if (need_check)
221
                    s->checkState();
221
                    s->checkState();
222
222
223
            }
223
            }
224
        }
224
        }
225
    }
225
    }
226
226
227
227
228
    // store statistics on saplings/regeneration
228
    // store statistics on saplings/regeneration
229
    for (QList<ResourceUnitSpecies*>::const_iterator i=ru->ruSpecies().constBegin(); i!=ru->ruSpecies().constEnd(); ++i) {
229
    for (QList<ResourceUnitSpecies*>::const_iterator i=ru->ruSpecies().constBegin(); i!=ru->ruSpecies().constEnd(); ++i) {
230
        (*i)->saplingStat().calculate((*i)->species(), const_cast<ResourceUnit*>(ru));
230
        (*i)->saplingStat().calculate((*i)->species(), const_cast<ResourceUnit*>(ru));
231
        (*i)->statistics().add(&((*i)->saplingStat()));
231
        (*i)->statistics().add(&((*i)->saplingStat()));
232
    }
232
    }
233
233
234
    // debug output related to saplings
234
    // debug output related to saplings
235
    if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dSaplingGrowth)) {
235
    if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dSaplingGrowth)) {
236
236
237
        // establishment details
237
        // establishment details
238
        for (QList<ResourceUnitSpecies*>::const_iterator it=ru->ruSpecies().constBegin();it!=ru->ruSpecies().constEnd();++it) {
238
        for (QList<ResourceUnitSpecies*>::const_iterator it=ru->ruSpecies().constBegin();it!=ru->ruSpecies().constEnd();++it) {
239
            if ((*it)->saplingStat().livingCohorts() == 0)
239
            if ((*it)->saplingStat().livingCohorts() == 0)
240
                continue;
240
                continue;
241
            DebugList &out = GlobalSettings::instance()->debugList(ru->index(), GlobalSettings::dSaplingGrowth);
241
            DebugList &out = GlobalSettings::instance()->debugList(ru->index(), GlobalSettings::dSaplingGrowth);
242
            out << (*it)->species()->id() << ru->index() <<ru->id();
242
            out << (*it)->species()->id() << ru->index() <<ru->id();
243
            out << (*it)->saplingStat().livingCohorts() << (*it)->saplingStat().averageHeight() << (*it)->saplingStat().averageAge()
243
            out << (*it)->saplingStat().livingCohorts() << (*it)->saplingStat().averageHeight() << (*it)->saplingStat().averageAge()
244
                << (*it)->saplingStat().averageDeltaHPot() << (*it)->saplingStat().averageDeltaHRealized();
244
                << (*it)->saplingStat().averageDeltaHPot() << (*it)->saplingStat().averageDeltaHRealized();
245
            out << (*it)->saplingStat().newSaplings() << (*it)->saplingStat().diedSaplings()
245
            out << (*it)->saplingStat().newSaplings() << (*it)->saplingStat().diedSaplings()
246
                << (*it)->saplingStat().recruitedSaplings() <<(*it)->species()->saplingGrowthParameters().referenceRatio;
246
                << (*it)->saplingStat().recruitedSaplings() <<(*it)->species()->saplingGrowthParameters().referenceRatio;
247
        }
247
        }
248
    }
248
    }
249
249
250
}
250
}
251
251
252
SaplingCell *Saplings::cell(QPoint lif_coords, bool only_valid, ResourceUnit **rRUPtr)
252
SaplingCell *Saplings::cell(QPoint lif_coords, bool only_valid, ResourceUnit **rRUPtr)
253
{
253
{
254
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
254
    FloatGrid *lif_grid = GlobalSettings::instance()->model()->grid();
255
255
256
    // in this case, getting the actual cell is quite cumbersome: first, retrieve the resource unit, then the
256
    // in this case, getting the actual cell is quite cumbersome: first, retrieve the resource unit, then the
257
    // cell based on the offset of the given coordiantes relative to the corner of the resource unit.
257
    // cell based on the offset of the given coordiantes relative to the corner of the resource unit.
258
    ResourceUnit *ru = GlobalSettings::instance()->model()->ru(lif_grid->cellCenterPoint(lif_coords));
258
    ResourceUnit *ru = GlobalSettings::instance()->model()->ru(lif_grid->cellCenterPoint(lif_coords));
259
    if (rRUPtr)
259
    if (rRUPtr)
260
        *rRUPtr = ru;
260
        *rRUPtr = ru;
261
261
262
    if (ru) {
262
    if (ru) {
263
        QPoint local_coords = lif_coords - ru->cornerPointOffset();
263
        QPoint local_coords = lif_coords - ru->cornerPointOffset();
264
        int idx = local_coords.y() * cPxPerRU + local_coords.x();
264
        int idx = local_coords.y() * cPxPerRU + local_coords.x();
265
        DBGMODE( if (idx<0 || idx>=cPxPerHectare)
265
        DBGMODE( if (idx<0 || idx>=cPxPerHectare)
266
                 qDebug("invalid coords in Saplings::cell");
266
                 qDebug("invalid coords in Saplings::cell");
267
                    );
267
                    );
268
        SaplingCell *s=&ru->saplingCellArray()[idx];
268
        SaplingCell *s=&ru->saplingCellArray()[idx];
269
        if (s && (!only_valid || s->state!=SaplingCell::CellInvalid))
269
        if (s && (!only_valid || s->state!=SaplingCell::CellInvalid))
270
            return s;
270
            return s;
271
    }
271
    }
272
    return 0;
272
    return 0;
273
}
273
}
274
274
275
void Saplings::clearSaplings(const QRectF &rectangle, const bool remove_biomass)
275
void Saplings::clearSaplings(const QRectF &rectangle, const bool remove_biomass)
276
{
276
{
277
    GridRunner<float> runner(GlobalSettings::instance()->model()->grid(), rectangle);
277
    GridRunner<float> runner(GlobalSettings::instance()->model()->grid(), rectangle);
278
    ResourceUnit *ru;
278
    ResourceUnit *ru;
279
    while (runner.next()) {
279
    while (runner.next()) {
280
        SaplingCell *s = cell(runner.currentIndex(), true, &ru);
280
        SaplingCell *s = cell(runner.currentIndex(), true, &ru);
281
        if (s) {
281
        if (s) {
282
            clearSaplings(s, ru, remove_biomass);
282
            clearSaplings(s, ru, remove_biomass);
283
        }
283
        }
284
284
285
    }
285
    }
286
}
286
}
287
287
288
void Saplings::clearSaplings(SaplingCell *s, ResourceUnit *ru, const bool remove_biomass)
288
void Saplings::clearSaplings(SaplingCell *s, ResourceUnit *ru, const bool remove_biomass)
289
{
289
{
290
    if (s) {
290
    if (s) {
291
        for (int i=0;i<NSAPCELLS;++i)
291
        for (int i=0;i<NSAPCELLS;++i)
292
            if (s->saplings[i].is_occupied()) {
292
            if (s->saplings[i].is_occupied()) {
293
                if (!remove_biomass) {
293
                if (!remove_biomass) {
294
                    ResourceUnitSpecies *rus = s->saplings[i].resourceUnitSpecies(ru);
294
                    ResourceUnitSpecies *rus = s->saplings[i].resourceUnitSpecies(ru);
295
                    if (!rus && !rus->species()) {
295
                    if (!rus && !rus->species()) {
296
                        qDebug() << "Saplings::clearSaplings(): invalid resource unit!!!";
296
                        qDebug() << "Saplings::clearSaplings(): invalid resource unit!!!";
297
                        return;
297
                        return;
298
                    }
298
                    }
299
                    rus->saplingStat().addCarbonOfDeadSapling( s->saplings[i].height / rus->species()->saplingGrowthParameters().hdSapling * 100.f );
299
                    rus->saplingStat().addCarbonOfDeadSapling( s->saplings[i].height / rus->species()->saplingGrowthParameters().hdSapling * 100.f );
300
                }
300
                }
301
                s->saplings[i].clear();
301
                s->saplings[i].clear();
302
            }
302
            }
303
        s->checkState();
303
        s->checkState();
304
304
305
    }
305
    }
306
306
307
}
307
}
308
308
309
int Saplings::addSprout(const Tree *t)
309
int Saplings::addSprout(const Tree *t)
310
{
310
{
311
    if (t->species()->saplingGrowthParameters().sproutGrowth==0.)
311
    if (t->species()->saplingGrowthParameters().sproutGrowth==0.)
312
        return 0;
312
        return 0;
313
    SaplingCell *sc = cell(t->positionIndex());
313
    SaplingCell *sc = cell(t->positionIndex());
314
    if (!sc)
314
    if (!sc)
315
        return 0;
315
        return 0;
316
    clearSaplings(sc, const_cast<ResourceUnit*>(t->ru()), false );
316
    clearSaplings(sc, const_cast<ResourceUnit*>(t->ru()), false );
317
    SaplingTree *st=sc->addSapling(0.05f, 0, t->species()->index());
317
    SaplingTree *st=sc->addSapling(0.05f, 0, t->species()->index());
318
    if (st)
318
    if (st)
319
        st->set_sprout(true);
319
        st->set_sprout(true);
320
320
321
    // neighboring cells
321
    // neighboring cells
322
    double crown_area = t->crownRadius()*t->crownRadius() * M_PI; //m2
322
    double crown_area = t->crownRadius()*t->crownRadius() * M_PI; //m2
323
    // calculate how many cells on the ground are covered by the crown (this is a rather rough estimate)
323
    // calculate how many cells on the ground are covered by the crown (this is a rather rough estimate)
324
    // n_cells: in addition to the original cell
324
    // n_cells: in addition to the original cell
325
    int n_cells = static_cast<int>(round( crown_area / static_cast<double>(cPxSize*cPxSize) - 1.));
325
    int n_cells = static_cast<int>(round( crown_area / static_cast<double>(cPxSize*cPxSize) - 1.));
326
    if (n_cells>0) {
326
    if (n_cells>0) {
327
        ResourceUnit *ru;
327
        ResourceUnit *ru;
328
        static const int offsets_x[8] = {1,1,0,-1,-1,-1,0,1};
328
        static const int offsets_x[8] = {1,1,0,-1,-1,-1,0,1};
329
        static const int offsets_y[8] = {0,1,1,1,0,-1,-1,-1};
329
        static const int offsets_y[8] = {0,1,1,1,0,-1,-1,-1};
330
        int s=irandom(0,8);
330
        int s=irandom(0,8);
331
        while(n_cells) {
331
        while(n_cells) {
332
            sc = cell(t->positionIndex()+QPoint(offsets_x[s], offsets_y[s]),true,&ru);
332
            sc = cell(t->positionIndex()+QPoint(offsets_x[s], offsets_y[s]),true,&ru);
333
            if (sc) {
333
            if (sc) {
334
                clearSaplings(sc, ru, false );
334
                clearSaplings(sc, ru, false );
335
                SaplingTree *st=sc->addSapling(0.05f, 0, t->species()->index());
335
                SaplingTree *st=sc->addSapling(0.05f, 0, t->species()->index());
336
                if (st)
336
                if (st)
337
                    st->set_sprout(true);
337
                    st->set_sprout(true);
338
            }
338
            }
339
339
340
            s = (s+1)%8; --n_cells;
340
            s = (s+1)%8; --n_cells;
341
        }
341
        }
342
    }
342
    }
343
    return 1;
343
    return 1;
344
}
344
}
345
345
346
void Saplings::updateBrowsingPressure()
346
void Saplings::updateBrowsingPressure()
347
{
347
{
348
    if (GlobalSettings::instance()->settings().valueBool("model.settings.browsing.enabled"))
348
    if (GlobalSettings::instance()->settings().valueBool("model.settings.browsing.enabled"))
349
        Saplings::mBrowsingPressure = GlobalSettings::instance()->settings().valueDouble("model.settings.browsing.browsingPressure");
349
        Saplings::mBrowsingPressure = GlobalSettings::instance()->settings().valueDouble("model.settings.browsing.browsingPressure");
350
    else
350
    else
351
        Saplings::mBrowsingPressure = 0.;
351
        Saplings::mBrowsingPressure = 0.;
352
}
352
}
353
353
354
bool Saplings::growSapling(const ResourceUnit *ru, SaplingCell &scell, SaplingTree &tree, int isc, float dom_height, float lif_value, int cohorts_on_px)
354
bool Saplings::growSapling(const ResourceUnit *ru, SaplingCell &scell, SaplingTree &tree, int isc, float dom_height, float lif_value, int cohorts_on_px)
355
{
355
{
356
    ResourceUnitSpecies *rus = tree.resourceUnitSpecies(ru);
356
    ResourceUnitSpecies *rus = tree.resourceUnitSpecies(ru);
357
357
358
    const Species *species = rus->species();
358
    const Species *species = rus->species();
359
359
360
    // (1) calculate height growth potential for the tree (uses linerization of expressions...)
360
    // (1) calculate height growth potential for the tree (uses linerization of expressions...)
361
    double h_pot = species->saplingGrowthParameters().heightGrowthPotential.calculate(tree.height);
361
    double h_pot = species->saplingGrowthParameters().heightGrowthPotential.calculate(tree.height);
362
    double delta_h_pot = h_pot - tree.height;
362
    double delta_h_pot = h_pot - tree.height;
363
363
364
    // (2) reduce height growth potential with species growth response f_env_yr and with light state (i.e. LIF-value) of home-pixel.
364
    // (2) reduce height growth potential with species growth response f_env_yr and with light state (i.e. LIF-value) of home-pixel.
365
    if (dom_height==0.f)
365
    if (dom_height==0.f)
366
        throw IException(QString("growSapling: height grid at %1/%2 has value 0").arg(isc));
366
        throw IException(QString("growSapling: height grid at %1/%2 has value 0").arg(isc));
367
367
368
    double rel_height = tree.height / dom_height;
368
    double rel_height = tree.height / dom_height;
369
369
370
    double lif_corrected = species->speciesSet()->LRIcorrection(lif_value, rel_height); // correction based on height
370
    double lif_corrected = species->speciesSet()->LRIcorrection(lif_value, rel_height); // correction based on height
371
371
372
    double lr = species->lightResponse(lif_corrected); // species specific light response (LUI, light utilization index)
372
    double lr = species->lightResponse(lif_corrected); // species specific light response (LUI, light utilization index)
373
373
374
    rus->calculate(true); // calculate the 3pg module (this is done only once per RU); true: call comes from regeneration
374
    rus->calculate(true); // calculate the 3pg module (this is done only once per RU); true: call comes from regeneration
375
    double f_env_yr = rus->prod3PG().fEnvYear();
375
    double f_env_yr = rus->prod3PG().fEnvYear();
376
376
377
    double delta_h_factor = f_env_yr * lr; // relative growth
377
    double delta_h_factor = f_env_yr * lr; // relative growth
378
378
379
    if (h_pot<0. || delta_h_pot<0. || lif_corrected<0. || lif_corrected>1. || delta_h_factor<0. || delta_h_factor>1. )
379
    if (h_pot<0. || delta_h_pot<0. || lif_corrected<0. || lif_corrected>1. || delta_h_factor<0. || delta_h_factor>1. )
380
        qDebug() << "invalid values in Sapling::growSapling";
380
        qDebug() << "invalid values in Sapling::growSapling";
381
381
382
    // sprouts grow faster. Sprouts therefore are less prone to stress (threshold), and can grow higher than the growth potential.
382
    // sprouts grow faster. Sprouts therefore are less prone to stress (threshold), and can grow higher than the growth potential.
383
    if (tree.is_sprout())
383
    if (tree.is_sprout())
384
        delta_h_factor = delta_h_factor *species->saplingGrowthParameters().sproutGrowth;
384
        delta_h_factor = delta_h_factor *species->saplingGrowthParameters().sproutGrowth;
385
385
386
    // check browsing
386
    // check browsing
387
    if (mBrowsingPressure>0. && tree.height<=2.f) {
387
    if (mBrowsingPressure>0. && tree.height<=2.f) {
388
        double p = rus->species()->saplingGrowthParameters().browsingProbability;
388
        double p = rus->species()->saplingGrowthParameters().browsingProbability;
389
        // calculate modifed annual browsing probability via odds-ratios
389
        // calculate modifed annual browsing probability via odds-ratios
390
        // odds = p/(1-p) -> odds_mod = odds * browsingPressure -> p_mod = odds_mod /( 1 + odds_mod) === p*pressure/(1-p+p*pressure)
390
        // odds = p/(1-p) -> odds_mod = odds * browsingPressure -> p_mod = odds_mod /( 1 + odds_mod) === p*pressure/(1-p+p*pressure)
391
        double p_browse = p*mBrowsingPressure / (1. - p + p*mBrowsingPressure);
391
        double p_browse = p*mBrowsingPressure / (1. - p + p*mBrowsingPressure);
392
        if (drandom() < p_browse) {
392
        if (drandom() < p_browse) {
393
            delta_h_factor = 0.;
393
            delta_h_factor = 0.;
394
        }
394
        }
395
    }
395
    }
396
396
397
    // check mortality of saplings
397
    // check mortality of saplings
398
    if (delta_h_factor < species->saplingGrowthParameters().stressThreshold) {
398
    if (delta_h_factor < species->saplingGrowthParameters().stressThreshold) {
399
        tree.stress_years++;
399
        tree.stress_years++;
400
        if (tree.stress_years > species->saplingGrowthParameters().maxStressYears) {
400
        if (tree.stress_years > species->saplingGrowthParameters().maxStressYears) {
401
            // sapling dies...
401
            // sapling dies...
402
            rus->saplingStat().addCarbonOfDeadSapling( tree.height / species->saplingGrowthParameters().hdSapling * 100.f );
402
            rus->saplingStat().addCarbonOfDeadSapling( tree.height / species->saplingGrowthParameters().hdSapling * 100.f );
403
            tree.clear();
403
            tree.clear();
404
            return true; // need cleanup
404
            return true; // need cleanup
405
        }
405
        }
406
    } else {
406
    } else {
407
        tree.stress_years=0; // reset stress counter
407
        tree.stress_years=0; // reset stress counter
408
    }
408
    }
409
    DBG_IF(delta_h_pot*delta_h_factor < 0.f || (!tree.is_sprout() && delta_h_pot*delta_h_factor > 2.), "Sapling::growSapling", "inplausible height growth.");
409
    DBG_IF(delta_h_pot*delta_h_factor < 0.f || (!tree.is_sprout() && delta_h_pot*delta_h_factor > 2.), "Sapling::growSapling", "inplausible height growth.");
410
410
411
    // grow
411
    // grow
412
    tree.height += delta_h_pot * delta_h_factor;
412
    tree.height += delta_h_pot * delta_h_factor;
413
    tree.age++; // increase age of sapling by 1
413
    tree.age++; // increase age of sapling by 1
414
414
415
    // recruitment?
415
    // recruitment?
416
    if (tree.height > 4.f) {
416
    if (tree.height > 4.f) {
417
        rus->saplingStat().mRecruited++;
417
        rus->saplingStat().mRecruited++;
418
418
419
        float dbh = tree.height / species->saplingGrowthParameters().hdSapling * 100.f;
419
        float dbh = tree.height / species->saplingGrowthParameters().hdSapling * 100.f;
420
        // the number of trees to create (result is in trees per pixel)
420
        // the number of trees to create (result is in trees per pixel)
421
        double n_trees = species->saplingGrowthParameters().representedStemNumber(dbh);
421
        double n_trees = species->saplingGrowthParameters().representedStemNumber(dbh);
422
        int to_establish = static_cast<int>( n_trees );
422
        int to_establish = static_cast<int>( n_trees );
423
423
424
        // if n_trees is not an integer, choose randomly if we should add a tree.
424
        // if n_trees is not an integer, choose randomly if we should add a tree.
425
        // e.g.: n_trees = 2.3 -> add 2 trees with 70% probability, and add 3 trees with p=30%.
425
        // e.g.: n_trees = 2.3 -> add 2 trees with 70% probability, and add 3 trees with p=30%.
426
        if (drandom() < (n_trees-to_establish) || to_establish==0)
426
        if (drandom() < (n_trees-to_establish) || to_establish==0)
427
            to_establish++;
427
            to_establish++;
428
428
429
        // add a new tree
429
        // add a new tree
430
        for (int i=0;i<to_establish;i++) {
430
        for (int i=0;i<to_establish;i++) {
431
            Tree &bigtree = const_cast<ResourceUnit*>(ru)->newTree();
431
            Tree &bigtree = const_cast<ResourceUnit*>(ru)->newTree();
432
432
433
            bigtree.setPosition(GlobalSettings::instance()->model()->grid()->indexOf(isc));
433
            bigtree.setPosition(GlobalSettings::instance()->model()->grid()->indexOf(isc));
434
            // add variation: add +/-N% to dbh and *independently* to height.
434
            // add variation: add +/-N% to dbh and *independently* to height.
435
            bigtree.setDbh(static_cast<float>(dbh * nrandom(1. - mRecruitmentVariation, 1. + mRecruitmentVariation)));
435
            bigtree.setDbh(static_cast<float>(dbh * nrandom(1. - mRecruitmentVariation, 1. + mRecruitmentVariation)));
436
            bigtree.setHeight(static_cast<float>(tree.height * nrandom(1. - mRecruitmentVariation, 1. + mRecruitmentVariation)));
436
            bigtree.setHeight(static_cast<float>(tree.height * nrandom(1. - mRecruitmentVariation, 1. + mRecruitmentVariation)));
437
            bigtree.setSpecies( const_cast<Species*>(species) );
437
            bigtree.setSpecies( const_cast<Species*>(species) );
438
            bigtree.setAge(tree.age,tree.height);
438
            bigtree.setAge(tree.age,tree.height);
439
            bigtree.setRU(const_cast<ResourceUnit*>(ru));
439
            bigtree.setRU(const_cast<ResourceUnit*>(ru));
440
            bigtree.setup();
440
            bigtree.setup();
441
            const Tree *t = &bigtree;
441
            const Tree *t = &bigtree;
442
            const_cast<ResourceUnitSpecies*>(rus)->statistics().add(t, 0); // count the newly created trees already in the stats
442
            const_cast<ResourceUnitSpecies*>(rus)->statistics().add(t, 0); // count the newly created trees already in the stats
443
        }
443
        }
444
        // clear all regeneration from this pixel (including this tree)
444
        // clear all regeneration from this pixel (including this tree)
445
        tree.clear(); // clear this tree (no carbon flow to the ground)
445
        tree.clear(); // clear this tree (no carbon flow to the ground)
446
        for (int i=0;i<NSAPCELLS;++i) {
446
        for (int i=0;i<NSAPCELLS;++i) {
447
            if (scell.saplings[i].is_occupied()) {
447
            if (scell.saplings[i].is_occupied()) {
448
                // add carbon to the ground
448
                // add carbon to the ground
449
                ResourceUnitSpecies *srus = scell.saplings[i].resourceUnitSpecies(ru);
449
                ResourceUnitSpecies *srus = scell.saplings[i].resourceUnitSpecies(ru);
450
                srus->saplingStat().addCarbonOfDeadSapling( scell.saplings[i].height / srus->species()->saplingGrowthParameters().hdSapling * 100.f );
450
                srus->saplingStat().addCarbonOfDeadSapling( scell.saplings[i].height / srus->species()->saplingGrowthParameters().hdSapling * 100.f );
451
                scell.saplings[i].clear();
451
                scell.saplings[i].clear();
452
            }
452
            }
453
        }
453
        }
454
        return true; // need cleanup
454
        return true; // need cleanup
455
    }
455
    }
456
    // book keeping (only for survivors) for the sapling of the resource unit / species
456
    // book keeping (only for survivors) for the sapling of the resource unit / species
457
    SaplingStat &ss = rus->saplingStat();
457
    SaplingStat &ss = rus->saplingStat();
458
    double n_repr = species->saplingGrowthParameters().representedStemNumberH(tree.height) / static_cast<double>(cohorts_on_px);
458
    double n_repr = species->saplingGrowthParameters().representedStemNumberH(tree.height) / static_cast<double>(cohorts_on_px);
459
    if (tree.height>1.3f)
459
    if (tree.height>1.3f)
460
        ss.mLivingSaplings += n_repr;
460
        ss.mLivingSaplings += n_repr;
461
    else
461
    else
462
        ss.mLivingSmallSaplings += n_repr;
462
        ss.mLivingSmallSaplings += n_repr;
463
    ss.mLiving++;
463
    ss.mLiving++;
464
    ss.mAvgHeight+=tree.height;
464
    ss.mAvgHeight+=tree.height;
465
    ss.mAvgAge+=tree.age;
465
    ss.mAvgAge+=tree.age;
466
    ss.mAvgDeltaHPot+=delta_h_pot;
466
    ss.mAvgDeltaHPot+=delta_h_pot;
467
    ss.mAvgHRealized += delta_h_pot * delta_h_factor;
467
    ss.mAvgHRealized += delta_h_pot * delta_h_factor;
468
    return false;
468
    return false;
469
}
469
}
470
470
471
void SaplingStat::clearStatistics()
471
void SaplingStat::clearStatistics()
472
{
472
{
473
    mRecruited=mDied=mLiving=0;
473
    mRecruited=mDied=mLiving=0;
474
    mLivingSaplings=0.; mLivingSmallSaplings=0.;
474
    mLivingSaplings=0.; mLivingSmallSaplings=0.;
475
    mSumDbhDied=0.;
475
    mSumDbhDied=0.;
476
    mAvgHeight=0.;
476
    mAvgHeight=0.;
477
    mAvgAge=0.;
477
    mAvgAge=0.;
478
    mAvgDeltaHPot=mAvgHRealized=0.;
478
    mAvgDeltaHPot=mAvgHRealized=0.;
479
    mAdded=0;
479
    mAdded=0;
480
480
481
}
481
}
482
482
483
void SaplingStat::calculate(const Species *species, ResourceUnit *ru)
483
void SaplingStat::calculate(const Species *species, ResourceUnit *ru)
484
{
484
{
485
    if (mLiving) {
485
    if (mLiving) {
486
        mAvgHeight /= double(mLiving);
486
        mAvgHeight /= double(mLiving);
487
        mAvgAge /= double(mLiving);
487
        mAvgAge /= double(mLiving);
488
        mAvgDeltaHPot /= double(mLiving);
488
        mAvgDeltaHPot /= double(mLiving);
489
        mAvgHRealized /= double(mLiving);
489
        mAvgHRealized /= double(mLiving);
490
    }
490
    }
491
    if (GlobalSettings::instance()->currentYear()==0)
491
    if (GlobalSettings::instance()->currentYear()==0)
492
        return; // no need for carbon flows in initial run
492
        return; // no need for carbon flows in initial run
493
493
494
    // calculate carbon balance
494
    // calculate carbon balance
495
    CNPair old_state = mCarbonLiving;
495
    CNPair old_state = mCarbonLiving;
496
    mCarbonLiving.clear();
496
    mCarbonLiving.clear();
497
497
498
    CNPair dead_wood, dead_fine; // pools for mortality
498
    CNPair dead_wood, dead_fine; // pools for mortality
499
    // average dbh
499
    // average dbh
500
    if (mLiving>0) {
500
    if (mLiving>0) {
501
        // calculate the avg dbh and number of stems
501
        // calculate the avg dbh and number of stems
502
        double avg_dbh = mAvgHeight / species->saplingGrowthParameters().hdSapling * 100.;
502
        double avg_dbh = mAvgHeight / species->saplingGrowthParameters().hdSapling * 100.;
503
        // the number of "real" stems is given by the Reineke formula
503
        // the number of "real" stems is given by the Reineke formula
504
        double n = mLivingSaplings; // total number of saplings (>0.05m)
504
        double n = mLivingSaplings; // total number of saplings (>0.05m)
505
505
506
        // woody parts: stem, branchse and coarse roots
506
        // woody parts: stem, branchse and coarse roots
507
        double woody_bm = species->biomassWoody(avg_dbh) + species->biomassBranch(avg_dbh) + species->biomassRoot(avg_dbh);
507
        double woody_bm = species->biomassWoody(avg_dbh) + species->biomassBranch(avg_dbh) + species->biomassRoot(avg_dbh);
508
        double foliage = species->biomassFoliage(avg_dbh);
508
        double foliage = species->biomassFoliage(avg_dbh);
509
        double fineroot = foliage*species->finerootFoliageRatio();
509
        double fineroot = foliage*species->finerootFoliageRatio();
510
510
511
        mCarbonLiving.addBiomass( woody_bm*n, species->cnWood()  );
511
        mCarbonLiving.addBiomass( woody_bm*n, species->cnWood()  );
512
        mCarbonLiving.addBiomass( foliage*n, species->cnFoliage()  );
512
        mCarbonLiving.addBiomass( foliage*n, species->cnFoliage()  );
513
        mCarbonLiving.addBiomass( fineroot*n, species->cnFineroot()  );
513
        mCarbonLiving.addBiomass( fineroot*n, species->cnFineroot()  );
514
514
515
        DBGMODE(
515
        DBGMODE(
516
        if (isnan(mCarbonLiving.C))
516
        if (isnan(mCarbonLiving.C))
517
            qDebug("carbon NaN in SaplingStat::calculate (living trees).");
517
            qDebug("carbon NaN in SaplingStat::calculate (living trees).");
518
                );
518
                );
519
519
520
        // turnover
520
        // turnover
521
        if (ru->snag())
521
        if (ru->snag())
522
            ru->snag()->addTurnoverLitter(species, foliage*species->turnoverLeaf(), fineroot*species->turnoverRoot());
522
            ru->snag()->addTurnoverLitter(species, foliage*species->turnoverLeaf(), fineroot*species->turnoverRoot());
523
523
524
        // calculate the "mortality from competition", i.e. carbon that stems from reduction of stem numbers
524
        // calculate the "mortality from competition", i.e. carbon that stems from reduction of stem numbers
525
        // from Reinekes formula.
525
        // from Reinekes formula.
526
        //
526
        //
527
        if (avg_dbh>1.) {
527
        if (avg_dbh>1.) {
528
            double avg_dbh_before = (mAvgHeight - mAvgHRealized) / species->saplingGrowthParameters().hdSapling * 100.;
528
            double avg_dbh_before = (mAvgHeight - mAvgHRealized) / species->saplingGrowthParameters().hdSapling * 100.;
529
            double n_before = mLiving * species->saplingGrowthParameters().representedStemNumber( qMax(1.,avg_dbh_before) );
529
            double n_before = mLiving * species->saplingGrowthParameters().representedStemNumber( qMax(1.,avg_dbh_before) );
530
            if (n<n_before) {
530
            if (n<n_before) {
531
                dead_wood.addBiomass( woody_bm * (n_before-n), species->cnWood() );
531
                dead_wood.addBiomass( woody_bm * (n_before-n), species->cnWood() );
532
                dead_fine.addBiomass( foliage * (n_before-n), species->cnFoliage()  );
532
                dead_fine.addBiomass( foliage * (n_before-n), species->cnFoliage()  );
533
                dead_fine.addBiomass( fineroot * (n_before-n), species->cnFineroot()  );
533
                dead_fine.addBiomass( fineroot * (n_before-n), species->cnFineroot()  );
534
                DBGMODE(
534
                DBGMODE(
535
                if (isnan(dead_fine.C))
535
                if (isnan(dead_fine.C))
536
                    qDebug("carbon NaN in SaplingStat::calculate (self thinning).");
536
                    qDebug("carbon NaN in SaplingStat::calculate (self thinning).");
537
                        );
537
                        );
538
538
539
            }
539
            }
540
        }
540
        }
541
541
542
    }
542
    }
543
    if (mDied) {
543
    if (mDied) {
544
        double avg_dbh_dead = mSumDbhDied / double(mDied);
544
        double avg_dbh_dead = mSumDbhDied / double(mDied);
545
        double n = mDied * species->saplingGrowthParameters().representedStemNumber( avg_dbh_dead );
545
        double n = mDied * species->saplingGrowthParameters().representedStemNumber( avg_dbh_dead );
546
        // woody parts: stem, branchse and coarse roots
546
        // woody parts: stem, branchse and coarse roots
547
547
548
        dead_wood.addBiomass( ( species->biomassWoody(avg_dbh_dead) + species->biomassBranch(avg_dbh_dead) + species->biomassRoot(avg_dbh_dead)) * n, species->cnWood()  );
548
        dead_wood.addBiomass( ( species->biomassWoody(avg_dbh_dead) + species->biomassBranch(avg_dbh_dead) + species->biomassRoot(avg_dbh_dead)) * n, species->cnWood()  );
549
        double foliage = species->biomassFoliage(avg_dbh_dead)*n;
549
        double foliage = species->biomassFoliage(avg_dbh_dead)*n;
550
550
551
        dead_fine.addBiomass( foliage, species->cnFoliage()  );
551
        dead_fine.addBiomass( foliage, species->cnFoliage()  );
552
        dead_fine.addBiomass( foliage*species->finerootFoliageRatio(), species->cnFineroot()  );
552
        dead_fine.addBiomass( foliage*species->finerootFoliageRatio(), species->cnFineroot()  );
553
        DBGMODE(
553
        DBGMODE(
554
        if (isnan(dead_fine.C))
554
        if (isnan(dead_fine.C))
555
            qDebug("carbon NaN in SaplingStat::calculate (died trees).");
555
            qDebug("carbon NaN in SaplingStat::calculate (died trees).");
556
                );
556
                );
557
557
558
    }
558
    }
559
    if (!dead_wood.isEmpty() || !dead_fine.isEmpty())
559
    if (!dead_wood.isEmpty() || !dead_fine.isEmpty())
560
        if (ru->snag())
560
        if (ru->snag())
561
            ru->snag()->addToSoil(species, dead_wood, dead_fine);
561
            ru->snag()->addToSoil(species, dead_wood, dead_fine);
562
562
563
    // calculate net growth:
563
    // calculate net growth:
564
    // delta of stocks
564
    // delta of stocks
565
    mCarbonGain = mCarbonLiving + dead_fine + dead_wood - old_state;
565
    mCarbonGain = mCarbonLiving + dead_fine + dead_wood - old_state;
566
    if (mCarbonGain.C < 0)
566
    if (mCarbonGain.C < 0)
567
        mCarbonGain.clear();
567
        mCarbonGain.clear();
568
568
569
569
570
    GlobalSettings::instance()->systemStatistics()->saplingCount+=mLiving;
570
    GlobalSettings::instance()->systemStatistics()->saplingCount+=mLiving;
571
    GlobalSettings::instance()->systemStatistics()->newSaplings+=mAdded;
571
    GlobalSettings::instance()->systemStatistics()->newSaplings+=mAdded;
572
572
573
}
573
}
574
574
575
double SaplingStat::livingStemNumber(const Species *species, double &rAvgDbh, double &rAvgHeight, double &rAvgAge) const
575
double SaplingStat::livingStemNumber(const Species *species, double &rAvgDbh, double &rAvgHeight, double &rAvgAge) const
576
{
576
{
577
     rAvgHeight = averageHeight();
577
     rAvgHeight = averageHeight();
578
     rAvgDbh = rAvgHeight / species->saplingGrowthParameters().hdSapling * 100.f;
578
     rAvgDbh = rAvgHeight / species->saplingGrowthParameters().hdSapling * 100.f;
579
     rAvgAge = averageAge();
579
     rAvgAge = averageAge();
580
     double n= species->saplingGrowthParameters().representedStemNumber(rAvgDbh);
580
     double n= species->saplingGrowthParameters().representedStemNumber(rAvgDbh);
581
     return n;
581
     return n;
582
// *** old code (sapling.cpp) ***
582
// *** old code (sapling.cpp) ***
583
//    double total = 0.;
583
//    double total = 0.;
584
//    double dbh_sum = 0.;
584
//    double dbh_sum = 0.;
585
//    double h_sum = 0.;
585
//    double h_sum = 0.;
586
//    double age_sum = 0.;
586
//    double age_sum = 0.;
587
//    const SaplingGrowthParameters &p = mRUS->species()->saplingGrowthParameters();
587
//    const SaplingGrowthParameters &p = mRUS->species()->saplingGrowthParameters();
588
//    for (QVector<SaplingTreeOld>::const_iterator it = mSaplingTrees.constBegin(); it!=mSaplingTrees.constEnd(); ++it) {
588
//    for (QVector<SaplingTreeOld>::const_iterator it = mSaplingTrees.constBegin(); it!=mSaplingTrees.constEnd(); ++it) {
589
//        float dbh = it->height / p.hdSapling * 100.f;
589
//        float dbh = it->height / p.hdSapling * 100.f;
590
//        if (dbh<1.) // minimum size: 1cm
590
//        if (dbh<1.) // minimum size: 1cm
591
//            continue;
591
//            continue;
592
//        double n = p.representedStemNumber(dbh); // one cohort on the pixel represents that number of trees
592
//        double n = p.representedStemNumber(dbh); // one cohort on the pixel represents that number of trees
593
//        dbh_sum += n*dbh;
593
//        dbh_sum += n*dbh;
594
//        h_sum += n*it->height;
594
//        h_sum += n*it->height;
595
//        age_sum += n*it->age.age;
595
//        age_sum += n*it->age.age;
596
//        total += n;
596
//        total += n;
597
//    }
597
//    }
598
//    if (total>0.) {
598
//    if (total>0.) {
599
//        dbh_sum /= total;
599
//        dbh_sum /= total;
600
//        h_sum /= total;
600
//        h_sum /= total;
601
//        age_sum /= total;
601
//        age_sum /= total;
602
//    }
602
//    }
603
//    rAvgDbh = dbh_sum;
603
//    rAvgDbh = dbh_sum;
604
//    rAvgHeight = h_sum;
604
//    rAvgHeight = h_sum;
605
//    rAvgAge = age_sum;
605
//    rAvgAge = age_sum;
606
//    return total;
606
//    return total;
607
}
607
}
608
608
609
ResourceUnitSpecies *SaplingTree::resourceUnitSpecies(const ResourceUnit *ru)
609
ResourceUnitSpecies *SaplingTree::resourceUnitSpecies(const ResourceUnit *ru)
610
{
610
{
611
    if (!ru || !is_occupied())
611
    if (!ru || !is_occupied())
612
        return 0;
612
        return 0;
613
    ResourceUnitSpecies *rus = ru->resourceUnitSpecies(species_index);
613
    ResourceUnitSpecies *rus = ru->resourceUnitSpecies(species_index);
614
    return rus;
614
    return rus;
615
}
615
}
616
616
617
SaplingCellRunner::SaplingCellRunner(const int stand_id, const MapGrid *stand_grid)
617
SaplingCellRunner::SaplingCellRunner(const int stand_id, const MapGrid *stand_grid)
618
{
618
{
619
    mRunner = 0;
619
    mRunner = 0;
620
    mRU = 0;
620
    mRU = 0;
621
    mStandId = stand_id;
621
    mStandId = stand_id;
622
    mStandGrid = stand_grid ? stand_grid : GlobalSettings::instance()->model()->standGrid();
622
    mStandGrid = stand_grid ? stand_grid : GlobalSettings::instance()->model()->standGrid();
623
    QRectF box = mStandGrid->boundingBox(stand_id);
623
    QRectF box = mStandGrid->boundingBox(stand_id);
624
    mRunner = new GridRunner<float>(GlobalSettings::instance()->model()->grid(), box);
624
    mRunner = new GridRunner<float>(GlobalSettings::instance()->model()->grid(), box);
625
625
626
}
626
}
627
627
628
SaplingCellRunner::~SaplingCellRunner()
628
SaplingCellRunner::~SaplingCellRunner()
629
{
629
{
630
    if (mRunner)
630
    if (mRunner)
631
        delete mRunner;
631
        delete mRunner;
632
}
632
}
633
633
634
SaplingCell *SaplingCellRunner::next()
634
SaplingCell *SaplingCellRunner::next()
635
{
635
{
636
    if (!mRunner)
636
    if (!mRunner)
637
        return 0;
637
        return 0;
638
    while (float *n = mRunner->next()) {
638
    while (float *n = mRunner->next()) {
639
        if (!n)
639
        if (!n)
640
            return 0; // end of the bounding box
640
            return 0; // end of the bounding box
641
        if (mStandGrid->standIDFromLIFCoord(mRunner->currentIndex()) != mStandId)
641
        if (mStandGrid->standIDFromLIFCoord(mRunner->currentIndex()) != mStandId)
642
            continue; // pixel does not belong to the target stand
642
            continue; // pixel does not belong to the target stand
643
        mRU = GlobalSettings::instance()->model()->ru(mRunner->currentCoord());
643
        mRU = GlobalSettings::instance()->model()->ru(mRunner->currentCoord());
644
        SaplingCell *sc=0;
644
        SaplingCell *sc=0;
645
        if (mRU)
645
        if (mRU)
646
            sc=mRU->saplingCell(mRunner->currentIndex());
646
            sc=mRU->saplingCell(mRunner->currentIndex());
647
        if (sc)
647
        if (sc)
648
            return sc;
648
            return sc;
649
        qDebug() << "SaplingCellRunner::next(): unexected missing SaplingCell!";
649
        qDebug() << "SaplingCellRunner::next(): unexected missing SaplingCell!";
650
        return 0;
650
        return 0;
651
    }
651
    }
652
    return 0;
652
    return 0;
653
}
653
}
654
654
655
QPointF SaplingCellRunner::currentCoord() const
655
QPointF SaplingCellRunner::currentCoord() const
656
{
656
{
657
    return mRunner->currentCoord();
657
    return mRunner->currentCoord();
658
}
658
}
659
 
659