Subversion Repositories public iLand

Rev

Rev 496 | Rev 503 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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