Subversion Repositories public iLand

Rev

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