Subversion Repositories public iLand

Rev

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

Rev 1118 Rev 1157
1
Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/resourceunit.cpp':
1
Redirecting to URL 'https://iland.boku.ac.at/svn/iland/tags/release_1.0/src/core/resourceunit.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
20
21
/** @class ResourceUnit
21
/** @class ResourceUnit
22
  ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
22
  ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
23
  (Climate, Soil, Water, ...).
23
  (Climate, Soil, Water, ...).
24
  @ingroup core
24
  @ingroup core
25
  A resource unit has a size of (currently) 100x100m. Many processes in iLand operate on the level of a ResourceUnit.
25
  A resource unit has a size of (currently) 100x100m. Many processes in iLand operate on the level of a ResourceUnit.
26
  Each resource unit has the same Climate and other properties (e.g. available nitrogen).
26
  Each resource unit has the same Climate and other properties (e.g. available nitrogen).
27
  Proceses on this level are, inter alia, NPP Production (see Production3PG), water calculations (WaterCycle), the modeling
27
  Proceses on this level are, inter alia, NPP Production (see Production3PG), water calculations (WaterCycle), the modeling
28
  of dead trees (Snag) and soil processes (Soil).
28
  of dead trees (Snag) and soil processes (Soil).
29

29

30
  */
30
  */
31
#include <QtCore>
31
#include <QtCore>
32
#include "global.h"
32
#include "global.h"
33
33
34
#include "resourceunit.h"
34
#include "resourceunit.h"
35
#include "resourceunitspecies.h"
35
#include "resourceunitspecies.h"
36
#include "speciesset.h"
36
#include "speciesset.h"
37
#include "species.h"
37
#include "species.h"
38
#include "production3pg.h"
38
#include "production3pg.h"
39
#include "model.h"
39
#include "model.h"
40
#include "climate.h"
40
#include "climate.h"
41
#include "watercycle.h"
41
#include "watercycle.h"
42
#include "snag.h"
42
#include "snag.h"
43
#include "soil.h"
43
#include "soil.h"
44
#include "helper.h"
44
#include "helper.h"
45
45
46
ResourceUnit::~ResourceUnit()
46
ResourceUnit::~ResourceUnit()
47
{
47
{
48
    if (mWater)
48
    if (mWater)
49
        delete mWater;
49
        delete mWater;
50
    mWater = 0;
50
    mWater = 0;
51
    if (mSnag)
51
    if (mSnag)
52
        delete mSnag;
52
        delete mSnag;
53
    if (mSoil)
53
    if (mSoil)
54
        delete mSoil;
54
        delete mSoil;
55
55
56
    qDeleteAll(mRUSpecies);
56
    qDeleteAll(mRUSpecies);
57
57
58
    mSnag = 0;
58
    mSnag = 0;
59
    mSoil = 0;
59
    mSoil = 0;
60
}
60
}
61
61
62
ResourceUnit::ResourceUnit(const int index)
62
ResourceUnit::ResourceUnit(const int index)
63
{
63
{
64
    qDeleteAll(mRUSpecies);
64
    qDeleteAll(mRUSpecies);
65
    mSpeciesSet = 0;
65
    mSpeciesSet = 0;
66
    mClimate = 0;
66
    mClimate = 0;
67
    mPixelCount=0;
67
    mPixelCount=0;
68
    mStockedArea = 0;
68
    mStockedArea = 0;
69
    mStockedPixelCount = 0;
69
    mStockedPixelCount = 0;
-
 
70
    mStockableArea = 0;
70
    mAggregatedWLA = 0.;
71
    mAggregatedWLA = 0.;
71
    mAggregatedLA = 0.;
72
    mAggregatedLA = 0.;
72
    mAggregatedLR = 0.;
73
    mAggregatedLR = 0.;
73
    mEffectiveArea = 0.;
74
    mEffectiveArea = 0.;
74
    mLRI_modification = 0.;
75
    mLRI_modification = 0.;
75
    mIndex = index;
76
    mIndex = index;
76
    mSaplingHeightMap = 0;
77
    mSaplingHeightMap = 0;
77
    mEffectiveArea_perWLA = 0.;
78
    mEffectiveArea_perWLA = 0.;
78
    mWater = new WaterCycle();
79
    mWater = new WaterCycle();
79
    mSnag = 0;
80
    mSnag = 0;
80
    mSoil = 0;
81
    mSoil = 0;
81
    mID = 0;
82
    mID = 0;
82
}
83
}
83
84
84
void ResourceUnit::setup()
85
void ResourceUnit::setup()
85
{
86
{
86
    mWater->setup(this);
87
    mWater->setup(this);
87
88
88
    if (mSnag)
89
    if (mSnag)
89
        delete mSnag;
90
        delete mSnag;
90
    mSnag=0;
91
    mSnag=0;
91
    if (mSoil)
92
    if (mSoil)
92
        delete mSoil;
93
        delete mSoil;
93
    mSoil=0;
94
    mSoil=0;
94
    if (Model::settings().carbonCycleEnabled) {
95
    if (Model::settings().carbonCycleEnabled) {
95
        mSoil = new Soil(this);
96
        mSoil = new Soil(this);
96
        mSnag = new Snag;
97
        mSnag = new Snag;
97
        mSnag->setup(this);
98
        mSnag->setup(this);
98
        const XmlHelper &xml=GlobalSettings::instance()->settings();
99
        const XmlHelper &xml=GlobalSettings::instance()->settings();
99
100
100
        // setup contents of the soil of the RU; use values for C and N (kg/ha)
101
        // setup contents of the soil of the RU; use values for C and N (kg/ha)
101
        mSoil->setInitialState(CNPool(xml.valueDouble("model.site.youngLabileC", -1),
102
        mSoil->setInitialState(CNPool(xml.valueDouble("model.site.youngLabileC", -1),
102
                                      xml.valueDouble("model.site.youngLabileN", -1),
103
                                      xml.valueDouble("model.site.youngLabileN", -1),
103
                                      xml.valueDouble("model.site.youngLabileDecompRate", -1)),
104
                                      xml.valueDouble("model.site.youngLabileDecompRate", -1)),
104
                               CNPool(xml.valueDouble("model.site.youngRefractoryC", -1),
105
                               CNPool(xml.valueDouble("model.site.youngRefractoryC", -1),
105
                                      xml.valueDouble("model.site.youngRefractoryN", -1),
106
                                      xml.valueDouble("model.site.youngRefractoryN", -1),
106
                                      xml.valueDouble("model.site.youngRefractoryDecompRate", -1)),
107
                                      xml.valueDouble("model.site.youngRefractoryDecompRate", -1)),
107
                               CNPair(xml.valueDouble("model.site.somC", -1), xml.valueDouble("model.site.somN", -1)));
108
                               CNPair(xml.valueDouble("model.site.somC", -1), xml.valueDouble("model.site.somN", -1)));
108
    }
109
    }
109
110
110
    // setup variables
111
    // setup variables
111
    mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40);
112
    mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40);
112
113
113
    // if dynamic coupling of soil nitrogen is enabled, a starting value for available N is calculated
114
    // if dynamic coupling of soil nitrogen is enabled, a starting value for available N is calculated
114
    if (mSoil && Model::settings().useDynamicAvailableNitrogen && Model::settings().carbonCycleEnabled) {
115
    if (mSoil && Model::settings().useDynamicAvailableNitrogen && Model::settings().carbonCycleEnabled) {
115
        mSoil->setClimateFactor(1.);
116
        mSoil->setClimateFactor(1.);
116
        mSoil->calculateYear();
117
        mSoil->calculateYear();
117
        mUnitVariables.nitrogenAvailable = soil()->availableNitrogen();
118
        mUnitVariables.nitrogenAvailable = soil()->availableNitrogen();
118
    }
119
    }
119
    mHasDeadTrees = false;
120
    mHasDeadTrees = false;
120
    mAverageAging = 0.;
121
    mAverageAging = 0.;
121
122
122
}
123
}
123
void ResourceUnit::setBoundingBox(const QRectF &bb)
124
void ResourceUnit::setBoundingBox(const QRectF &bb)
124
{
125
{
125
    mBoundingBox = bb;
126
    mBoundingBox = bb;
126
    mCornerOffset = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft());
127
    mCornerOffset = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft());
127
}
128
}
128
129
129
/// set species and setup the species-per-RU-data
130
/// set species and setup the species-per-RU-data
130
void ResourceUnit::setSpeciesSet(SpeciesSet *set)
131
void ResourceUnit::setSpeciesSet(SpeciesSet *set)
131
{
132
{
132
    mSpeciesSet = set;
133
    mSpeciesSet = set;
133
    qDeleteAll(mRUSpecies);
134
    qDeleteAll(mRUSpecies);
134
135
135
    //mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated
136
    //mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated
136
    for (int i=0;i<set->count();i++) {
137
    for (int i=0;i<set->count();i++) {
137
        Species *s = const_cast<Species*>(mSpeciesSet->species(i));
138
        Species *s = const_cast<Species*>(mSpeciesSet->species(i));
138
        if (!s)
139
        if (!s)
139
            throw IException("ResourceUnit::setSpeciesSet: invalid index!");
140
            throw IException("ResourceUnit::setSpeciesSet: invalid index!");
140
141
141
        ResourceUnitSpecies *rus = new ResourceUnitSpecies();
142
        ResourceUnitSpecies *rus = new ResourceUnitSpecies();
142
        mRUSpecies.push_back(rus);
143
        mRUSpecies.push_back(rus);
143
        rus->setup(s, this);
144
        rus->setup(s, this);
144
        /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
145
        /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
145
           If the container memory is relocated (QVector), the pointer gets invalid!!!
146
           If the container memory is relocated (QVector), the pointer gets invalid!!!
146
           Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
147
           Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
147
        //mRUSpecies[i].setup(s,this); // setup this element
148
        //mRUSpecies[i].setup(s,this); // setup this element
148
149
149
    }
150
    }
150
}
151
}
151
152
152
ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species)
153
ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species)
153
{
154
{
154
    return *mRUSpecies[species->index()];
155
    return *mRUSpecies[species->index()];
155
}
156
}
156
157
157
const ResourceUnitSpecies *ResourceUnit::constResourceUnitSpecies(const Species *species) const
158
const ResourceUnitSpecies *ResourceUnit::constResourceUnitSpecies(const Species *species) const
158
{
159
{
159
    return mRUSpecies[species->index()];
160
    return mRUSpecies[species->index()];
160
}
161
}
161
162
162
Tree &ResourceUnit::newTree()
163
Tree &ResourceUnit::newTree()
163
{
164
{
164
    // start simple: just append to the vector...
165
    // start simple: just append to the vector...
165
    if (mTrees.isEmpty())
166
    if (mTrees.isEmpty())
166
        mTrees.reserve(100); // reserve a junk of memory for trees
167
        mTrees.reserve(100); // reserve a junk of memory for trees
167
168
168
    mTrees.append(Tree());
169
    mTrees.append(Tree());
169
    return mTrees.back();
170
    return mTrees.back();
170
}
171
}
171
int ResourceUnit::newTreeIndex()
172
int ResourceUnit::newTreeIndex()
172
{
173
{
173
    newTree();
174
    newTree();
174
    return mTrees.count()-1; // return index of the last tree
175
    return mTrees.count()-1; // return index of the last tree
175
}
176
}
176
177
177
/// remove dead trees from tree list
178
/// remove dead trees from tree list
178
/// reduce size of vector if lots of space is free
179
/// reduce size of vector if lots of space is free
179
/// tests showed that this way of cleanup is very fast,
180
/// tests showed that this way of cleanup is very fast,
180
/// because no memory allocations are performed (simple memmove())
181
/// because no memory allocations are performed (simple memmove())
181
/// when trees are moved.
182
/// when trees are moved.
182
void ResourceUnit::cleanTreeList()
183
void ResourceUnit::cleanTreeList()
183
{
184
{
184
    if (!mHasDeadTrees)
185
    if (!mHasDeadTrees)
185
        return;
186
        return;
186
187
187
    QVector<Tree>::iterator last=mTrees.end()-1;
188
    QVector<Tree>::iterator last=mTrees.end()-1;
188
    QVector<Tree>::iterator current = mTrees.begin();
189
    QVector<Tree>::iterator current = mTrees.begin();
189
    while (last>=current && (*last).isDead())
190
    while (last>=current && (*last).isDead())
190
        --last;
191
        --last;
191
192
192
    while (current<last) {
193
    while (current<last) {
193
        if ((*current).isDead()) {
194
        if ((*current).isDead()) {
194
            *current = *last; // copy data!
195
            *current = *last; // copy data!
195
            --last; //
196
            --last; //
196
            while (last>=current && (*last).isDead())
197
            while (last>=current && (*last).isDead())
197
                --last;
198
                --last;
198
        }
199
        }
199
        ++current;
200
        ++current;
200
    }
201
    }
201
    ++last; // last points now to the first dead tree
202
    ++last; // last points now to the first dead tree
202
203
203
    // free ressources
204
    // free ressources
204
    if (last!=mTrees.end()) {
205
    if (last!=mTrees.end()) {
205
        mTrees.erase(last, mTrees.end());
206
        mTrees.erase(last, mTrees.end());
206
        if (mTrees.capacity()>100) {
207
        if (mTrees.capacity()>100) {
207
            if (mTrees.count() / double(mTrees.capacity()) < 0.2) {
208
            if (mTrees.count() / double(mTrees.capacity()) < 0.2) {
208
                //int target_size = mTrees.count()*2;
209
                //int target_size = mTrees.count()*2;
209
                //qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size;
210
                //qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size;
210
                //mTrees.reserve(qMax(target_size, 100));
211
                //mTrees.reserve(qMax(target_size, 100));
211
                if (logLevelDebug())
212
                if (logLevelDebug())
212
                    qDebug() << "reduce tree storage of RU" << index() << " from " << mTrees.capacity() << "to" << mTrees.count();
213
                    qDebug() << "reduce tree storage of RU" << index() << " from " << mTrees.capacity() << "to" << mTrees.count();
213
                mTrees.squeeze();
214
                mTrees.squeeze();
214
            }
215
            }
215
        }
216
        }
216
    }
217
    }
217
    mHasDeadTrees = false; // reset flag
218
    mHasDeadTrees = false; // reset flag
218
}
219
}
219
220
220
void ResourceUnit::newYear()
221
void ResourceUnit::newYear()
221
{
222
{
222
    mAggregatedWLA = 0.;
223
    mAggregatedWLA = 0.;
223
    mAggregatedLA = 0.;
224
    mAggregatedLA = 0.;
224
    mAggregatedLR = 0.;
225
    mAggregatedLR = 0.;
225
    mEffectiveArea = 0.;
226
    mEffectiveArea = 0.;
226
    mPixelCount = mStockedPixelCount = 0;
227
    mPixelCount = mStockedPixelCount = 0;
227
    snagNewYear();
228
    snagNewYear();
228
    if (mSoil)
229
    if (mSoil)
229
        mSoil->newYear();
230
        mSoil->newYear();
230
    // clear statistics global and per species...
231
    // clear statistics global and per species...
231
    QList<ResourceUnitSpecies*>::const_iterator i;
232
    QList<ResourceUnitSpecies*>::const_iterator i;
232
    QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd();
233
    QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd();
233
    mStatistics.clear();
234
    mStatistics.clear();
234
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
235
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
235
        (*i)->statisticsDead().clear();
236
        (*i)->statisticsDead().clear();
236
        (*i)->statisticsMgmt().clear();
237
        (*i)->statisticsMgmt().clear();
237
        (*i)->changeSapling().newYear();
238
        (*i)->changeSapling().newYear();
238
    }
239
    }
239
240
240
}
241
}
241
242
242
/** production() is the "stand-level" part of the biomass production (3PG).
243
/** production() is the "stand-level" part of the biomass production (3PG).
243
    - The amount of radiation intercepted by the stand is calculated
244
    - The amount of radiation intercepted by the stand is calculated
244
    - the water cycle is calculated
245
    - the water cycle is calculated
245
    - statistics for each species are cleared
246
    - statistics for each species are cleared
246
    - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
247
    - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
247
    see also: http://iland.boku.ac.at/individual+tree+light+availability */
248
    see also: http://iland.boku.ac.at/individual+tree+light+availability */
248
void ResourceUnit::production()
249
void ResourceUnit::production()
249
{
250
{
250
251
251
    if (mAggregatedWLA==0. || mPixelCount==0) {
252
    if (mAggregatedWLA==0. || mPixelCount==0) {
252
        // clear statistics of resourceunitspecies
253
        // clear statistics of resourceunitspecies
253
        for ( QList<ResourceUnitSpecies*>::const_iterator i=mRUSpecies.constBegin(); i!=mRUSpecies.constEnd(); ++i)
254
        for ( QList<ResourceUnitSpecies*>::const_iterator i=mRUSpecies.constBegin(); i!=mRUSpecies.constEnd(); ++i)
254
            (*i)->statistics().clear();
255
            (*i)->statistics().clear();
255
        mEffectiveArea = 0.;
256
        mEffectiveArea = 0.;
256
        mStockedArea = 0.;
257
        mStockedArea = 0.;
257
        return;
258
        return;
258
    }
259
    }
259
260
260
    // the pixel counters are filled during the height-grid-calculations
261
    // the pixel counters are filled during the height-grid-calculations
261
    mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m)
262
    mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m)
262
    if (leafAreaIndex()<3.) {
263
    if (leafAreaIndex()<3.) {
263
        // estimate stocked area based on crown projections
264
        // estimate stocked area based on crown projections
264
        double crown_area = 0.;
265
        double crown_area = 0.;
265
        for (int i=0;i<mTrees.count();++i)
266
        for (int i=0;i<mTrees.count();++i)
266
            crown_area += mTrees.at(i).isDead() ? 0. : mTrees.at(i).stamp()->reader()->crownArea();
267
            crown_area += mTrees.at(i).isDead() ? 0. : mTrees.at(i).stamp()->reader()->crownArea();
267
268
268
        qDebug() << "crown area: lai" << leafAreaIndex() << "stocked area (pixels)" << mStockedArea << " area (crown)" << crown_area;
-
 
269
        if (leafAreaIndex()<2.) {
-
 
270
            mStockedArea = crown_area;
-
 
-
 
269
        if (logLevelDebug())
-
 
270
            qDebug() << "crown area: lai" << leafAreaIndex() << "stocked area (pixels)" << mStockedArea << " area (crown)" << crown_area;
-
 
271
        if (leafAreaIndex()<1.) {
-
 
272
            mStockedArea = std::min(crown_area, mStockedArea);
271
        } else {
273
        } else {
272
274
273
            double px_frac = leafAreaIndex()-2.; // 0 at LAI=2, 1 at LAI=3
-
 
274
            mStockedArea = mStockedArea * px_frac + crown_area * (1. - px_frac);
-
 
-
 
275
            double px_frac = (leafAreaIndex()-1.)/2.; // 0 at LAI=1, 1 at LAI=3
-
 
276
            mStockedArea = mStockedArea * px_frac + std::min(crown_area, mStockedArea) * (1. - px_frac);
275
        }
277
        }
276
        if (mStockedArea==0.)
278
        if (mStockedArea==0.)
277
            return;
279
            return;
278
    }
280
    }
279
281
280
    // calculate the leaf area index (LAI)
282
    // calculate the leaf area index (LAI)
281
    double LAI = mAggregatedLA / mStockedArea;
283
    double LAI = mAggregatedLA / mStockedArea;
282
    // calculate the intercepted radiation fraction using the law of Beer Lambert
284
    // calculate the intercepted radiation fraction using the law of Beer Lambert
283
    const double k = Model::settings().lightExtinctionCoefficient;
285
    const double k = Model::settings().lightExtinctionCoefficient;
284
    double interception_fraction = 1. - exp(-k * LAI);
286
    double interception_fraction = 1. - exp(-k * LAI);
285
    mEffectiveArea = mStockedArea * interception_fraction; // m2
287
    mEffectiveArea = mStockedArea * interception_fraction; // m2
286
288
287
    // calculate the total weighted leaf area on this RU:
289
    // calculate the total weighted leaf area on this RU:
288
    mLRI_modification = interception_fraction *  mStockedArea / mAggregatedWLA; // p_WLA
290
    mLRI_modification = interception_fraction *  mStockedArea / mAggregatedWLA; // p_WLA
289
    if (mLRI_modification == 0.)
291
    if (mLRI_modification == 0.)
290
        qDebug() << "lri modifaction==0!";
292
        qDebug() << "lri modifaction==0!";
291
293
292
    if (logLevelDebug()) {
294
    if (logLevelDebug()) {
293
    DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3")
295
    DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3")
294
            .arg(LAI)
296
            .arg(LAI)
295
            .arg(interception_fraction)
297
            .arg(interception_fraction)
296
            .arg(mLRI_modification)
298
            .arg(mLRI_modification)
297
            .arg(mStockedArea);
299
            .arg(mStockedArea);
298
    );
300
    );
299
    }
301
    }
300
302
301
    // calculate LAI fractions
303
    // calculate LAI fractions
302
    QList<ResourceUnitSpecies*>::const_iterator i;
304
    QList<ResourceUnitSpecies*>::const_iterator i;
303
    QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd();
305
    QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd();
304
    double ru_lai = leafAreaIndex();
306
    double ru_lai = leafAreaIndex();
305
    if (ru_lai < 1.)
307
    if (ru_lai < 1.)
306
        ru_lai = 1.;
308
        ru_lai = 1.;
307
    // note: LAIFactors are only 1 if sum of LAI is > 1. (see WaterCycle)
309
    // note: LAIFactors are only 1 if sum of LAI is > 1. (see WaterCycle)
308
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
310
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
309
        double lai_factor = (*i)->statistics().leafAreaIndex() / ru_lai;
311
        double lai_factor = (*i)->statistics().leafAreaIndex() / ru_lai;
310
        DBGMODE(
-
 
311
        if (lai_factor > 1.)
-
 
312
            qDebug() << "LAI factor > 1";
-
 
313
        );
-
 
-
 
312
-
 
313
        //DBGMODE(
-
 
314
        if (lai_factor > 1.) {
-
 
315
                        const ResourceUnitSpecies* rus=*i;
-
 
316
                        qDebug() << "LAI factor > 1: species ru-index:" << rus->species()->name() << rus->ru()->index();
-
 
317
                    }
-
 
318
        //);
314
        (*i)->setLAIfactor( lai_factor );
319
        (*i)->setLAIfactor( lai_factor );
315
    }
320
    }
316
321
317
    // soil water model - this determines soil water contents needed for response calculations
322
    // soil water model - this determines soil water contents needed for response calculations
318
    {
323
    {
319
    mWater->run();
324
    mWater->run();
320
    }
325
    }
321
326
322
    // invoke species specific calculation (3PG)
327
    // invoke species specific calculation (3PG)
323
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
328
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
-
 
329
        //DBGMODE(
-
 
330
        if ((*i)->LAIfactor() > 1.) {
-
 
331
                    const ResourceUnitSpecies* rus=*i;
-
 
332
                    qDebug() << "LAI factor > 1: species ru-index value:" << rus->species()->name() << rus->ru()->index() << rus->LAIfactor();
-
 
333
                    }
-
 
334
        //);
324
        (*i)->calculate(); // CALCULATE 3PG
335
        (*i)->calculate(); // CALCULATE 3PG
325
        if (logLevelInfo() &&  (*i)->LAIfactor()>0)
336
        if (logLevelInfo() &&  (*i)->LAIfactor()>0)
326
            qDebug() << "ru" << mIndex << "species" << (*i)->species()->id() << "LAIfraction" << (*i)->LAIfactor() << "raw_gpp_m2"
337
            qDebug() << "ru" << mIndex << "species" << (*i)->species()->id() << "LAIfraction" << (*i)->LAIfactor() << "raw_gpp_m2"
327
                     << (*i)->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:"
338
                     << (*i)->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:"
328
                     << productiveArea()*(*i)->prod3PG().GPPperArea()
339
                     << productiveArea()*(*i)->prod3PG().GPPperArea()
329
                     << "aging(lastyear):" << averageAging() << "f_env,yr:" << (*i)->prod3PG().fEnvYear();
340
                     << "aging(lastyear):" << averageAging() << "f_env,yr:" << (*i)->prod3PG().fEnvYear();
330
    }
341
    }
331
}
342
}
332
343
333
void ResourceUnit::calculateInterceptedArea()
344
void ResourceUnit::calculateInterceptedArea()
334
{
345
{
335
    if (mAggregatedLR==0) {
346
    if (mAggregatedLR==0) {
336
        mEffectiveArea_perWLA = 0.;
347
        mEffectiveArea_perWLA = 0.;
337
        return;
348
        return;
338
    }
349
    }
339
    Q_ASSERT(mAggregatedLR>0.);
350
    Q_ASSERT(mAggregatedLR>0.);
340
    mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR;
351
    mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR;
341
    if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR  << "eff.area./wla:" << mEffectiveArea_perWLA;
352
    if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR  << "eff.area./wla:" << mEffectiveArea_perWLA;
342
}
353
}
343
354
344
// function is called immediately before the growth of individuals
355
// function is called immediately before the growth of individuals
345
void ResourceUnit::beforeGrow()
356
void ResourceUnit::beforeGrow()
346
{
357
{
347
    mAverageAging = 0.;
358
    mAverageAging = 0.;
348
}
359
}
349
360
350
// function is called after finishing the indivdual growth / mortality.
361
// function is called after finishing the indivdual growth / mortality.
351
void ResourceUnit::afterGrow()
362
void ResourceUnit::afterGrow()
352
{
363
{
353
    mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees)
364
    mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees)
354
    if (mAverageAging>0. && mAverageAging<0.00001)
365
    if (mAverageAging>0. && mAverageAging<0.00001)
355
        qDebug() << "ru" << mIndex << "aging <0.00001";
366
        qDebug() << "ru" << mIndex << "aging <0.00001";
356
    if (mAverageAging<0. || mAverageAging>1.)
367
    if (mAverageAging<0. || mAverageAging>1.)
357
        qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex();
368
        qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex();
358
}
369
}
359
370
360
void ResourceUnit::yearEnd()
371
void ResourceUnit::yearEnd()
361
{
372
{
362
    // calculate statistics for all tree species of the ressource unit
373
    // calculate statistics for all tree species of the ressource unit
363
    int c = mRUSpecies.count();
374
    int c = mRUSpecies.count();
364
    for (int i=0;i<c; i++) {
375
    for (int i=0;i<c; i++) {
365
        mRUSpecies[i]->statisticsDead().calculate(); // calculate the dead trees
376
        mRUSpecies[i]->statisticsDead().calculate(); // calculate the dead trees
366
        mRUSpecies[i]->statisticsMgmt().calculate(); // stats of removed trees
377
        mRUSpecies[i]->statisticsMgmt().calculate(); // stats of removed trees
367
        mRUSpecies[i]->updateGWL(); // get sum of dead trees (died + removed)
378
        mRUSpecies[i]->updateGWL(); // get sum of dead trees (died + removed)
368
        mRUSpecies[i]->statistics().calculate(); // calculate the living (and add removed volume to gwl)
379
        mRUSpecies[i]->statistics().calculate(); // calculate the living (and add removed volume to gwl)
369
        mStatistics.add(mRUSpecies[i]->statistics());
380
        mStatistics.add(mRUSpecies[i]->statistics());
370
    }
381
    }
371
    mStatistics.calculate(); // aggreagte on stand level
382
    mStatistics.calculate(); // aggreagte on stand level
-
 
383
-
 
384
    // update carbon flows
-
 
385
    if (soil() && GlobalSettings::instance()->model()->settings().carbonCycleEnabled) {
-
 
386
        double area_factor = stockableArea() / cRUArea; //conversion factor
-
 
387
        mUnitVariables.carbonUptake = statistics().npp() * biomassCFraction;
-
 
388
        mUnitVariables.carbonUptake += statistics().nppSaplings() * biomassCFraction;
-
 
389
-
 
390
        double to_atm = snag()->fluxToAtmosphere().C / area_factor; // from snags, kgC/ha
-
 
391
        to_atm += soil()->fluxToAtmosphere().C *cRUArea/10.; // soil: t/ha -> t/m2 -> kg/ha
-
 
392
        mUnitVariables.carbonToAtm = to_atm;
-
 
393
-
 
394
        double to_dist = snag()->fluxToDisturbance().C / area_factor;
-
 
395
        to_dist += soil()->fluxToDisturbance().C * cRUArea/10.;
-
 
396
        double to_harvest = snag()->fluxToExtern().C / area_factor;
-
 
397
-
 
398
        mUnitVariables.NEP = mUnitVariables.carbonUptake - to_atm - to_dist - to_harvest; // kgC/ha
-
 
399
-
 
400
        // incremental values....
-
 
401
        mUnitVariables.cumCarbonUptake += mUnitVariables.carbonUptake;
-
 
402
        mUnitVariables.cumCarbonToAtm += mUnitVariables.carbonToAtm;
-
 
403
        mUnitVariables.cumNEP += mUnitVariables.NEP;
-
 
404
-
 
405
    }
372
406
373
}
407
}
374
408
375
void ResourceUnit::addTreeAgingForAllTrees()
409
void ResourceUnit::addTreeAgingForAllTrees()
376
{
410
{
377
    mAverageAging = 0.;
411
    mAverageAging = 0.;
378
    foreach(const Tree &t, mTrees) {
412
    foreach(const Tree &t, mTrees) {
379
        addTreeAging(t.leafArea(), t.species()->aging(t.height(), t.age()));
413
        addTreeAging(t.leafArea(), t.species()->aging(t.height(), t.age()));
380
    }
414
    }
381
415
382
}
416
}
383
417
384
/// refresh of tree based statistics.
418
/// refresh of tree based statistics.
385
/// WARNING: this function is only called once (during startup).
419
/// WARNING: this function is only called once (during startup).
386
/// see function "yearEnd()" above!!!
420
/// see function "yearEnd()" above!!!
387
void ResourceUnit::createStandStatistics()
421
void ResourceUnit::createStandStatistics()
388
{
422
{
389
    // clear statistics (ru-level and ru-species level)
423
    // clear statistics (ru-level and ru-species level)
390
    mStatistics.clear();
424
    mStatistics.clear();
391
    for (int i=0;i<mRUSpecies.count();i++) {
425
    for (int i=0;i<mRUSpecies.count();i++) {
392
        mRUSpecies[i]->statistics().clear();
426
        mRUSpecies[i]->statistics().clear();
393
        mRUSpecies[i]->statisticsDead().clear();
427
        mRUSpecies[i]->statisticsDead().clear();
394
        mRUSpecies[i]->statisticsMgmt().clear();
428
        mRUSpecies[i]->statisticsMgmt().clear();
395
    }
429
    }
396
430
397
    // add all trees to the statistics objects of the species
431
    // add all trees to the statistics objects of the species
398
    foreach(const Tree &t, mTrees) {
432
    foreach(const Tree &t, mTrees) {
399
        if (!t.isDead())
433
        if (!t.isDead())
400
            resourceUnitSpecies(t.species()).statistics().add(&t, 0);
434
            resourceUnitSpecies(t.species()).statistics().add(&t, 0);
401
    }
435
    }
402
    // summarize statistics for the whole resource unit
436
    // summarize statistics for the whole resource unit
403
    for (int i=0;i<mRUSpecies.count();i++) {
437
    for (int i=0;i<mRUSpecies.count();i++) {
404
        mRUSpecies[i]->statistics().calculate();
438
        mRUSpecies[i]->statistics().calculate();
405
        mStatistics.add(mRUSpecies[i]->statistics());
439
        mStatistics.add(mRUSpecies[i]->statistics());
406
    }
440
    }
407
    mStatistics.calculate();
441
    mStatistics.calculate();
408
    mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*stockableArea()):0.;
442
    mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*stockableArea()):0.;
409
    if (mAverageAging<0. || mAverageAging>1.)
443
    if (mAverageAging<0. || mAverageAging>1.)
410
        qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex();
444
        qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex();
411
}
445
}
412
446
413
/** recreate statistics. This is necessary after events that changed the structure
447
/** recreate statistics. This is necessary after events that changed the structure
414
    of the stand *after* the growth of trees (where stand statistics are updated).
448
    of the stand *after* the growth of trees (where stand statistics are updated).
415
    An example is after disturbances.  */
449
    An example is after disturbances.  */
416
void ResourceUnit::recreateStandStatistics()
-
 
-
 
450
void ResourceUnit::recreateStandStatistics(bool recalculate_stats)
417
{
451
{
418
    for (int i=0;i<mRUSpecies.count();i++) {
452
    for (int i=0;i<mRUSpecies.count();i++) {
419
        mRUSpecies[i]->statistics().clear();
453
        mRUSpecies[i]->statistics().clear();
420
    }
454
    }
421
    foreach(const Tree &t, mTrees) {
455
    foreach(const Tree &t, mTrees) {
422
        resourceUnitSpecies(t.species()).statistics().add(&t, 0);
456
        resourceUnitSpecies(t.species()).statistics().add(&t, 0);
423
    }
457
    }
424
    for (int i=0;i<mRUSpecies.count();i++) {
-
 
425
        mRUSpecies[i]->statistics().calculate();
-
 
-
 
458
-
 
459
    if (recalculate_stats) {
-
 
460
        for (int i=0;i<mRUSpecies.count();i++) {
-
 
461
            mRUSpecies[i]->statistics().calculate();
-
 
462
        }
426
    }
463
    }
427
}
464
}
428
465
429
void ResourceUnit::setSaplingHeightMap(float *map_pointer)
466
void ResourceUnit::setSaplingHeightMap(float *map_pointer)
430
{
467
{
431
    // set to zero
468
    // set to zero
432
    mSaplingHeightMap=map_pointer;
469
    mSaplingHeightMap=map_pointer;
433
    if (!mSaplingHeightMap)
470
    if (!mSaplingHeightMap)
434
        return;
471
        return;
435
    for (int i=0;i<cPxPerRU*cPxPerRU;i++)
472
    for (int i=0;i<cPxPerRU*cPxPerRU;i++)
436
        mSaplingHeightMap[i]=0.f;
473
        mSaplingHeightMap[i]=0.f;
437
    // flag all trees in the resource unit
474
    // flag all trees in the resource unit
438
    for (QVector<Tree>::const_iterator i=mTrees.cbegin(); i!= mTrees.cend(); ++i) {
475
    for (QVector<Tree>::const_iterator i=mTrees.cbegin(); i!= mTrees.cend(); ++i) {
439
        setMaxSaplingHeightAt(i->positionIndex(), 10.f);
476
        setMaxSaplingHeightAt(i->positionIndex(), 10.f);
440
    }
477
    }
441
}
478
}
442
479
443
void ResourceUnit::setMaxSaplingHeightAt(const QPoint &position, const float height)
480
void ResourceUnit::setMaxSaplingHeightAt(const QPoint &position, const float height)
444
{
481
{
445
    Q_ASSERT(mSaplingHeightMap);
482
    Q_ASSERT(mSaplingHeightMap);
446
    int pixel_index = cPxPerRU*(position.x()-mCornerOffset.x())+(position.y()-mCornerOffset.y());
483
    int pixel_index = cPxPerRU*(position.x()-mCornerOffset.x())+(position.y()-mCornerOffset.y());
447
    if (pixel_index<0 || pixel_index>=cPxPerRU*cPxPerRU) {
484
    if (pixel_index<0 || pixel_index>=cPxPerRU*cPxPerRU) {
448
        qDebug() << "setSaplingHeightAt-Error for position" << position << "for RU at" << boundingBox() << "with corner" << mCornerOffset;
485
        qDebug() << "setSaplingHeightAt-Error for position" << position << "for RU at" << boundingBox() << "with corner" << mCornerOffset;
449
    } else {
486
    } else {
450
        if (mSaplingHeightMap[pixel_index]<height)
487
        if (mSaplingHeightMap[pixel_index]<height)
451
            mSaplingHeightMap[pixel_index]=height;
488
            mSaplingHeightMap[pixel_index]=height;
452
    }
489
    }
453
}
490
}
454
491
455
/// clear all saplings of all species on a given position (after recruitment)
492
/// clear all saplings of all species on a given position (after recruitment)
456
void ResourceUnit::clearSaplings(const QPoint &position)
493
void ResourceUnit::clearSaplings(const QPoint &position)
457
{
494
{
458
    foreach(ResourceUnitSpecies* rus, mRUSpecies)
495
    foreach(ResourceUnitSpecies* rus, mRUSpecies)
459
        rus->clearSaplings(position);
496
        rus->clearSaplings(position);
460
497
461
}
498
}
462
499
463
/// kill all saplings within a given rect
500
/// kill all saplings within a given rect
464
void ResourceUnit::clearSaplings(const QRectF pixel_rect, const bool remove_from_soil)
501
void ResourceUnit::clearSaplings(const QRectF pixel_rect, const bool remove_from_soil)
465
{
502
{
466
    foreach(ResourceUnitSpecies* rus, mRUSpecies) {
503
    foreach(ResourceUnitSpecies* rus, mRUSpecies) {
467
        rus->changeSapling().clearSaplings(pixel_rect, remove_from_soil);
504
        rus->changeSapling().clearSaplings(pixel_rect, remove_from_soil);
468
    }
505
    }
469
506
470
}
507
}
471
508
472
float ResourceUnit::saplingHeightForInit(const QPoint &position) const
-
 
-
 
509
double ResourceUnit::saplingHeightForInit(const QPoint &position) const
473
{
510
{
474
    double maxh = 0.;
511
    double maxh = 0.;
475
    foreach(ResourceUnitSpecies* rus, mRUSpecies)
512
    foreach(ResourceUnitSpecies* rus, mRUSpecies)
476
        maxh = qMax(maxh, rus->sapling().heightAt(position));
513
        maxh = qMax(maxh, rus->sapling().heightAt(position));
477
    return maxh;
514
    return maxh;
478
}
515
}
479
516
480
void ResourceUnit::calculateCarbonCycle()
517
void ResourceUnit::calculateCarbonCycle()
481
{
518
{
482
    if (!snag())
519
    if (!snag())
483
        return;
520
        return;
484
521
485
    // (1) calculate the snag dynamics
522
    // (1) calculate the snag dynamics
486
    // because all carbon/nitrogen-flows from trees to the soil are routed through the snag-layer,
523
    // because all carbon/nitrogen-flows from trees to the soil are routed through the snag-layer,
487
    // all soil inputs (litter + deadwood) are collected in the Snag-object.
524
    // all soil inputs (litter + deadwood) are collected in the Snag-object.
488
    snag()->calculateYear();
525
    snag()->calculateYear();
489
    soil()->setClimateFactor( snag()->climateFactor() ); // the climate factor is only calculated once
526
    soil()->setClimateFactor( snag()->climateFactor() ); // the climate factor is only calculated once
490
    soil()->setSoilInput( snag()->labileFlux(), snag()->refractoryFlux());
527
    soil()->setSoilInput( snag()->labileFlux(), snag()->refractoryFlux());
491
    soil()->calculateYear(); // update the ICBM/2N model
528
    soil()->calculateYear(); // update the ICBM/2N model
492
    // use available nitrogen?
529
    // use available nitrogen?
493
    if (Model::settings().useDynamicAvailableNitrogen)
530
    if (Model::settings().useDynamicAvailableNitrogen)
494
        mUnitVariables.nitrogenAvailable = soil()->availableNitrogen();
531
        mUnitVariables.nitrogenAvailable = soil()->availableNitrogen();
495
532
496
    // debug output
533
    // debug output
497
    if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dCarbonCycle) && !snag()->isEmpty()) {
534
    if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dCarbonCycle) && !snag()->isEmpty()) {
498
        DebugList &out = GlobalSettings::instance()->debugList(index(), GlobalSettings::dCarbonCycle);
535
        DebugList &out = GlobalSettings::instance()->debugList(index(), GlobalSettings::dCarbonCycle);
499
        out << index() << id(); // resource unit index and id
536
        out << index() << id(); // resource unit index and id
500
        out << snag()->debugList(); // snag debug outs
537
        out << snag()->debugList(); // snag debug outs
501
        out << soil()->debugList(); // ICBM/2N debug outs
538
        out << soil()->debugList(); // ICBM/2N debug outs
502
    }
539
    }
503
540
504
}
541
}
505
542
506
543
507
 
544