Subversion Repositories public iLand

Rev

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

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