Subversion Repositories public iLand

Rev

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

Rev 450 Rev 451
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
/** @class ResourceUnit
2
/** @class ResourceUnit
3
  ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
3
  ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
4
  (Climate, Soil, Water, ...).
4
  (Climate, Soil, Water, ...).
5

5

6
  */
6
  */
7
#include <QtCore>
7
#include <QtCore>
8
#include "global.h"
8
#include "global.h"
9
9
10
#include "resourceunit.h"
10
#include "resourceunit.h"
11
#include "resourceunitspecies.h"
11
#include "resourceunitspecies.h"
12
#include "speciesset.h"
12
#include "speciesset.h"
13
#include "species.h"
13
#include "species.h"
14
#include "production3pg.h"
14
#include "production3pg.h"
15
#include "model.h"
15
#include "model.h"
16
#include "climate.h"
16
#include "climate.h"
17
#include "watercycle.h"
17
#include "watercycle.h"
18
#include "helper.h"
18
#include "helper.h"
19
19
20
ResourceUnit::~ResourceUnit()
20
ResourceUnit::~ResourceUnit()
21
{
21
{
22
    if (mWater)
22
    if (mWater)
23
        delete mWater;
23
        delete mWater;
24
}
24
}
25
25
26
ResourceUnit::ResourceUnit(const int index)
26
ResourceUnit::ResourceUnit(const int index)
27
{
27
{
28
    mSpeciesSet = 0;
28
    mSpeciesSet = 0;
29
    mClimate = 0;
29
    mClimate = 0;
30
    mPixelCount=0;
30
    mPixelCount=0;
31
    mIndex = index;
31
    mIndex = index;
32
    mSaplingHeightMap = 0;
32
    mSaplingHeightMap = 0;
33
    mWater = new WaterCycle();
33
    mWater = new WaterCycle();
34
34
35
    mTrees.reserve(100); // start with space for 100 trees.
35
    mTrees.reserve(100); // start with space for 100 trees.
36
}
36
}
37
37
38
void ResourceUnit::setup()
38
void ResourceUnit::setup()
39
{
39
{
40
    mWater->setup(this);
40
    mWater->setup(this);
41
    // setup variables
41
    // setup variables
42
    mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40);
42
    mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40);
43
    mAverageAging = 0.;
43
    mAverageAging = 0.;
44
44
-
 
45
}
-
 
46
void ResourceUnit::setBoundingBox(const QRectF &bb)
-
 
47
{
-
 
48
    mBoundingBox = bb;
-
 
49
    mCornerCoord = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft());
45
}
50
}
46
51
47
/// set species and setup the species-per-RU-data
52
/// set species and setup the species-per-RU-data
48
void ResourceUnit::setSpeciesSet(SpeciesSet *set)
53
void ResourceUnit::setSpeciesSet(SpeciesSet *set)
49
{
54
{
50
    mSpeciesSet = set;
55
    mSpeciesSet = set;
51
    mRUSpecies.clear();
56
    mRUSpecies.clear();
52
    mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated
57
    mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated
53
    for (int i=0;i<set->count();i++) {
58
    for (int i=0;i<set->count();i++) {
54
        Species *s = const_cast<Species*>(mSpeciesSet->species(i));
59
        Species *s = const_cast<Species*>(mSpeciesSet->species(i));
55
        if (!s)
60
        if (!s)
56
            throw IException("ResourceUnit::setSpeciesSet: invalid index!");
61
            throw IException("ResourceUnit::setSpeciesSet: invalid index!");
57
62
58
        /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
63
        /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
59
           If the container memory is relocated (QVector), the pointer gets invalid!!!
64
           If the container memory is relocated (QVector), the pointer gets invalid!!!
60
           Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
65
           Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
61
        mRUSpecies[i].setup(s,this); // setup this element
66
        mRUSpecies[i].setup(s,this); // setup this element
62
67
63
    }
68
    }
64
}
69
}
65
70
66
ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species)
71
ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species)
67
{
72
{
68
    return mRUSpecies[species->index()];
73
    return mRUSpecies[species->index()];
69
}
74
}
70
75
71
Tree &ResourceUnit::newTree()
76
Tree &ResourceUnit::newTree()
72
{
77
{
73
    // start simple: just append to the vector...
78
    // start simple: just append to the vector...
74
    mTrees.append(Tree());
79
    mTrees.append(Tree());
75
    return mTrees.back();
80
    return mTrees.back();
76
}
81
}
77
int ResourceUnit::newTreeIndex()
82
int ResourceUnit::newTreeIndex()
78
{
83
{
79
    // start simple: just append to the vector...
84
    // start simple: just append to the vector...
80
    mTrees.append(Tree());
85
    mTrees.append(Tree());
81
    return mTrees.count()-1;
86
    return mTrees.count()-1;
82
}
87
}
83
88
84
/// remove dead trees from tree list
89
/// remove dead trees from tree list
85
/// reduce size of vector if lots of space is free
90
/// reduce size of vector if lots of space is free
86
/// tests showed that this way of cleanup is very fast,
91
/// tests showed that this way of cleanup is very fast,
87
/// because no memory allocations are performed (simple memmove())
92
/// because no memory allocations are performed (simple memmove())
88
/// when trees are moved.
93
/// when trees are moved.
89
void ResourceUnit::cleanTreeList()
94
void ResourceUnit::cleanTreeList()
90
{
95
{
91
    QVector<Tree>::iterator last=mTrees.end()-1;
96
    QVector<Tree>::iterator last=mTrees.end()-1;
92
    QVector<Tree>::iterator current = mTrees.begin();
97
    QVector<Tree>::iterator current = mTrees.begin();
93
    while (last>=current && (*last).isDead())
98
    while (last>=current && (*last).isDead())
94
        --last;
99
        --last;
95
100
96
    while (current<last) {
101
    while (current<last) {
97
        if ((*current).isDead()) {
102
        if ((*current).isDead()) {
98
            *current = *last; // copy data!
103
            *current = *last; // copy data!
99
            --last; //
104
            --last; //
100
            while (last>=current && (*last).isDead())
105
            while (last>=current && (*last).isDead())
101
                --last;
106
                --last;
102
        }
107
        }
103
        ++current;
108
        ++current;
104
    }
109
    }
105
    ++last; // last points now to the first dead tree
110
    ++last; // last points now to the first dead tree
106
111
107
    // free ressources
112
    // free ressources
108
    if (last!=mTrees.end()) {
113
    if (last!=mTrees.end()) {
109
        mTrees.erase(last, mTrees.end());
114
        mTrees.erase(last, mTrees.end());
110
        if (mTrees.capacity()>100) {
115
        if (mTrees.capacity()>100) {
111
            if (mTrees.count() / double(mTrees.capacity()) < 0.2) {
116
            if (mTrees.count() / double(mTrees.capacity()) < 0.2) {
112
                int target_size = mTrees.count()*2;
117
                int target_size = mTrees.count()*2;
113
                qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size;
118
                qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size;
114
                mTrees.reserve(qMax(target_size, 100));
119
                mTrees.reserve(qMax(target_size, 100));
115
            }
120
            }
116
        }
121
        }
117
    }
122
    }
118
}
123
}
119
124
120
void ResourceUnit::newYear()
125
void ResourceUnit::newYear()
121
{
126
{
122
    mAggregatedWLA = 0.;
127
    mAggregatedWLA = 0.;
123
    mAggregatedLA = 0.;
128
    mAggregatedLA = 0.;
124
    mAggregatedLR = 0.;
129
    mAggregatedLR = 0.;
125
    mEffectiveArea = 0.;
130
    mEffectiveArea = 0.;
126
    mPixelCount = mStockedPixelCount = 0;
131
    mPixelCount = mStockedPixelCount = 0;
127
    // clear statistics global and per species...
132
    // clear statistics global and per species...
128
    ResourceUnitSpecies *i;
133
    ResourceUnitSpecies *i;
129
    QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end();
134
    QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end();
130
    mStatistics.clear();
135
    mStatistics.clear();
131
    for (i=mRUSpecies.begin(); i!=iend; ++i) {
136
    for (i=mRUSpecies.begin(); i!=iend; ++i) {
132
        i->statisticsDead().clear();
137
        i->statisticsDead().clear();
133
        i->statisticsMgmt().clear();
138
        i->statisticsMgmt().clear();
134
    }
139
    }
135
140
136
}
141
}
137
142
138
/** production() is the "stand-level" part of the biomass production (3PG).
143
/** production() is the "stand-level" part of the biomass production (3PG).
139
    - The amount of radiation intercepted by the stand is calculated
144
    - The amount of radiation intercepted by the stand is calculated
140
    - the water cycle is calculated
145
    - the water cycle is calculated
141
    - statistics for each species are cleared
146
    - statistics for each species are cleared
142
    - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
147
    - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
143
    see also: http://iland.boku.ac.at/individual+tree+light+availability */
148
    see also: http://iland.boku.ac.at/individual+tree+light+availability */
144
void ResourceUnit::production()
149
void ResourceUnit::production()
145
{
150
{
146
151
147
    if (mAggregatedWLA==0 || mPixelCount==0) {
152
    if (mAggregatedWLA==0 || mPixelCount==0) {
148
        // nothing to do...
153
        // nothing to do...
149
        return;
154
        return;
150
    }
155
    }
151
156
152
    // the pixel counters are filled during the height-grid-calculations
157
    // the pixel counters are filled during the height-grid-calculations
153
    mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m)
158
    mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m)
154
159
155
    // calculate the leaf area index (LAI)
160
    // calculate the leaf area index (LAI)
156
    double LAI = mAggregatedLA / mStockedArea;
161
    double LAI = mAggregatedLA / mStockedArea;
157
    // calculate the intercepted radiation fraction using the law of Beer Lambert
162
    // calculate the intercepted radiation fraction using the law of Beer Lambert
158
    const double k = Model::settings().lightExtinctionCoefficient;
163
    const double k = Model::settings().lightExtinctionCoefficient;
159
    double interception_fraction = 1. - exp(-k * LAI);
164
    double interception_fraction = 1. - exp(-k * LAI);
160
    mEffectiveArea = mStockedArea * interception_fraction; // m2
165
    mEffectiveArea = mStockedArea * interception_fraction; // m2
161
166
162
    // calculate the total weighted leaf area on this RU:
167
    // calculate the total weighted leaf area on this RU:
163
    mLRI_modification = interception_fraction *  mStockedArea / mAggregatedWLA;
168
    mLRI_modification = interception_fraction *  mStockedArea / mAggregatedWLA;
164
    if (mLRI_modification == 0.)
169
    if (mLRI_modification == 0.)
165
        qDebug() << "lri modifaction==0!";
170
        qDebug() << "lri modifaction==0!";
166
171
167
172
168
    DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3")
173
    DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3")
169
            .arg(LAI)
174
            .arg(LAI)
170
            .arg(interception_fraction)
175
            .arg(interception_fraction)
171
            .arg(mLRI_modification)
176
            .arg(mLRI_modification)
172
            .arg(mStockedArea);
177
            .arg(mStockedArea);
173
    );
178
    );
174
179
175
    // calculate LAI fractions
180
    // calculate LAI fractions
176
    ResourceUnitSpecies *i;
181
    ResourceUnitSpecies *i;
177
    QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end();
182
    QVector<ResourceUnitSpecies>::iterator iend = mRUSpecies.end();
178
    for (i=mRUSpecies.begin(); i!=iend; ++i) {
183
    for (i=mRUSpecies.begin(); i!=iend; ++i) {
179
         i->setLAIfactor(i->statistics().leafAreaIndex() / leafAreaIndex());
184
         i->setLAIfactor(i->statistics().leafAreaIndex() / leafAreaIndex());
180
    }
185
    }
181
186
182
    // soil water model - this determines soil water contents needed for response calculations
187
    // soil water model - this determines soil water contents needed for response calculations
183
    {
188
    {
184
    DebugTimer tw("water:run");
189
    DebugTimer tw("water:run");
185
    mWater->run();
190
    mWater->run();
186
    }
191
    }
187
192
188
    // invoke species specific calculation (3PG)
193
    // invoke species specific calculation (3PG)
189
    for (i=mRUSpecies.begin(); i!=iend; ++i) {
194
    for (i=mRUSpecies.begin(); i!=iend; ++i) {
190
        i->calculate();
195
        i->calculate();
191
        if (logLevelInfo() &&  i->LAIfactor()>0)
196
        if (logLevelInfo() &&  i->LAIfactor()>0)
192
            qDebug() << "ru" << mIndex << "species" << (*i).species()->id() << "LAIfraction" << i->LAIfactor() << "raw_gpp_m2"
197
            qDebug() << "ru" << mIndex << "species" << (*i).species()->id() << "LAIfraction" << i->LAIfactor() << "raw_gpp_m2"
193
                     << i->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:"
198
                     << i->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:"
194
                     << productiveArea()*i->prod3PG().GPPperArea()
199
                     << productiveArea()*i->prod3PG().GPPperArea()
195
                     << "aging(lastyear):" << averageAging() << "f_env,yr:" << i->prod3PG().fEnvYear();
200
                     << "aging(lastyear):" << averageAging() << "f_env,yr:" << i->prod3PG().fEnvYear();
196
    }
201
    }
197
}
202
}
198
203
199
void ResourceUnit::calculateInterceptedArea()
204
void ResourceUnit::calculateInterceptedArea()
200
{
205
{
201
    if (mAggregatedLR==0) {
206
    if (mAggregatedLR==0) {
202
        mEffectiveArea_perWLA = 0.;
207
        mEffectiveArea_perWLA = 0.;
203
        return;
208
        return;
204
    }
209
    }
205
    Q_ASSERT(mAggregatedLR>0.);
210
    Q_ASSERT(mAggregatedLR>0.);
206
    mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR;
211
    mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR;
207
    if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR  << "eff.area./wla:" << mEffectiveArea_perWLA;
212
    if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR  << "eff.area./wla:" << mEffectiveArea_perWLA;
208
}
213
}
209
214
210
// function is called immediately before the growth of individuals
215
// function is called immediately before the growth of individuals
211
void ResourceUnit::beforeGrow()
216
void ResourceUnit::beforeGrow()
212
{
217
{
213
    mAverageAging = 0.;
218
    mAverageAging = 0.;
214
}
219
}
215
220
216
// function is called after finishing the indivdual growth / mortality.
221
// function is called after finishing the indivdual growth / mortality.
217
void ResourceUnit::afterGrow()
222
void ResourceUnit::afterGrow()
218
{
223
{
219
    mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees)
224
    mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees)
220
    if (mAverageAging>0. && mAverageAging<0.00001)
225
    if (mAverageAging>0. && mAverageAging<0.00001)
221
        qDebug() << "ru" << mIndex << "aging <0.00001";
226
        qDebug() << "ru" << mIndex << "aging <0.00001";
222
}
227
}
223
228
224
void ResourceUnit::yearEnd()
229
void ResourceUnit::yearEnd()
225
{
230
{
226
    // calculate statistics for all tree species of the ressource unit
231
    // calculate statistics for all tree species of the ressource unit
227
    int c = mRUSpecies.count();
232
    int c = mRUSpecies.count();
228
    for (int i=0;i<c; i++) {
233
    for (int i=0;i<c; i++) {
229
        mRUSpecies[i].statisticsDead().calculate(); // calculate the dead trees
234
        mRUSpecies[i].statisticsDead().calculate(); // calculate the dead trees
230
        mRUSpecies[i].statisticsMgmt().calculate(); // stats of removed trees
235
        mRUSpecies[i].statisticsMgmt().calculate(); // stats of removed trees
231
        mRUSpecies[i].updateGWL(); // get sum of dead trees (died + removed)
236
        mRUSpecies[i].updateGWL(); // get sum of dead trees (died + removed)
232
        mRUSpecies[i].statistics().calculate(); // calculate the living (and add removed volume to gwl)
237
        mRUSpecies[i].statistics().calculate(); // calculate the living (and add removed volume to gwl)
233
        mStatistics.add(mRUSpecies[i].statistics());
238
        mStatistics.add(mRUSpecies[i].statistics());
234
    }
239
    }
235
    mStatistics.calculate(); // aggreagte on stand level
240
    mStatistics.calculate(); // aggreagte on stand level
236
}
241
}
237
242
238
/// refresh of tree based statistics.
243
/// refresh of tree based statistics.
239
void ResourceUnit::createStandStatistics()
244
void ResourceUnit::createStandStatistics()
240
{
245
{
241
    // clear statistics (ru-level and ru-species level)
246
    // clear statistics (ru-level and ru-species level)
242
    mStatistics.clear();
247
    mStatistics.clear();
243
    for (int i=0;i<mRUSpecies.count();i++) {
248
    for (int i=0;i<mRUSpecies.count();i++) {
244
        mRUSpecies[i].statistics().clear();
249
        mRUSpecies[i].statistics().clear();
245
        mRUSpecies[i].statisticsDead().clear();
250
        mRUSpecies[i].statisticsDead().clear();
246
        mRUSpecies[i].statisticsMgmt().clear();
251
        mRUSpecies[i].statisticsMgmt().clear();
247
    }
252
    }
248
253
249
    // add all trees to the statistics objects of the species
254
    // add all trees to the statistics objects of the species
250
    foreach(const Tree &t, mTrees) {
255
    foreach(const Tree &t, mTrees) {
251
        if (!t.isDead())
256
        if (!t.isDead())
252
            resourceUnitSpecies(t.species()).statistics().add(&t, 0);
257
            resourceUnitSpecies(t.species()).statistics().add(&t, 0);
253
    }
258
    }
254
    // summarize statistics for the whole resource unit
259
    // summarize statistics for the whole resource unit
255
    for (int i=0;i<mRUSpecies.count();i++) {
260
    for (int i=0;i<mRUSpecies.count();i++) {
256
        mRUSpecies[i].statistics().calculate();
261
        mRUSpecies[i].statistics().calculate();
257
        mStatistics.add(mRUSpecies[i].statistics());
262
        mStatistics.add(mRUSpecies[i].statistics());
258
    }
263
    }
259
    mStatistics.calculate();
264
    mStatistics.calculate();
260
    mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*area()):0.;
265
    mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*area()):0.;
261
}
266
}
262
 
267