Subversion Repositories public iLand

Rev

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

Rev Author Line No. Line
1
 
3 Werner 2
 
3
#include "tree.h"
83 Werner 4
#include "grid.h"
3 Werner 5
 
83 Werner 6
#include "stamp.h"
90 Werner 7
#include "species.h"
107 Werner 8
#include "ressourceunit.h"
38 Werner 9
 
110 Werner 10
// static varaibles
106 Werner 11
FloatGrid *Tree::mGrid = 0;
12
FloatGrid *Tree::mHeightGrid = 0;
40 Werner 13
int Tree::m_statPrint=0;
48 Werner 14
int Tree::m_statAboveZ=0;
105 Werner 15
int Tree::m_statCreated=0;
40 Werner 16
int Tree::m_nextId=0;
53 Werner 17
float Tree::lafactor = 1.;
106 Werner 18
int Tree::mDebugid = -1;
40 Werner 19
 
110 Werner 20
// lifecycle
3 Werner 21
Tree::Tree()
22
{
106 Werner 23
    mDbh = 0;
24
    mHeight = 0;
25
    mSpecies = 0;
107 Werner 26
    mRU = 0;
106 Werner 27
    mId = m_nextId++;
105 Werner 28
    m_statCreated++;
3 Werner 29
}
38 Werner 30
 
15 Werner 31
/** get distance and direction between two points.
38 Werner 32
  returns the distance (m), and the angle between PStart and PEnd (radians) in referenced param rAngle. */
3 Werner 33
float dist_and_direction(const QPointF &PStart, const QPointF &PEnd, float &rAngle)
34
{
35
    float dx = PEnd.x() - PStart.x();
36
    float dy = PEnd.y() - PStart.y();
37
    float d = sqrt(dx*dx + dy*dy);
38
    // direction:
39
    rAngle = atan2(dx, dy);
40
    return d;
41
}
42
 
43
 
38 Werner 44
void Tree::setup()
45
{
106 Werner 46
    if (mDbh<=0 || mHeight<=0)
38 Werner 47
        return;
48
    // check stamp
106 Werner 49
   Q_ASSERT_X(mSpecies!=0, "Tree::setup()", "species is NULL");
50
   mStamp = mSpecies->stamp(mDbh, mHeight);
110 Werner 51
 
52
   calcBiomassCompartments();
53
 
38 Werner 54
}
39 Werner 55
 
110 Werner 56
//////////////////////////////////////////////////
57
////  Light functions (Pattern-stuff)
58
//////////////////////////////////////////////////
59
 
70 Werner 60
#define NOFULLDBG
77 Werner 61
//#define NOFULLOPT
62
/*
39 Werner 63
void Tree::applyStamp()
64
{
65
    Q_ASSERT(m_grid!=0);
66
    if (!m_stamp)
67
        return;
68
 
40 Werner 69
    QPoint pos = m_grid->indexAt(m_Position);
70
    int offset = m_stamp->offset();
71
    pos-=QPoint(offset, offset);
72
    QPoint p;
73
 
60 Werner 74
    float local_dom; // height of Z* on the current position
40 Werner 75
    int x,y;
58 Werner 76
    float value;
51 Werner 77
    QPoint dbg(10,20);
70 Werner 78
    #ifndef NOFULLDBG
79
    qDebug() <<"indexstampx indexstamy gridx gridy local_dom_height stampvalue 1-value*la/dom grid_before gridvalue_after";
80
    #endif
40 Werner 81
    for (x=0;x<m_stamp->size();++x) {
82
        for (y=0;y<m_stamp->size(); ++y) {
83
           p = pos + QPoint(x,y);
51 Werner 84
           // debug pixel
61 Werner 85
           //if (p==dbg)
86
           //    qDebug() << "#" << id() << "value;"<<(*m_stamp)(x,y)<<"domH"<<dom;
51 Werner 87
 
88
           if (m_grid->isIndexValid(p)) {
89
               // mulitplicative:
53 Werner 90
               //m_grid->valueAtIndex(p)*=(*m_stamp)(x,y);
51 Werner 91
               // additiv:
58 Werner 92
               // multiplicatie, v2
75 Werner 93
               // a optimization for 2m vs 10m grids: local_dom = m_dominanceGrid->valueAtIndex(p.x()/5, p.y()/5);
94
               // effect is about 20% of the application time
60 Werner 95
               local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) ); // todo: could be done more effectively (here 2 transormations are performed)...
96
               if (local_dom<=0.f) {
61 Werner 97
                   //qDebug() << "invalid height at " << m_grid->cellCoordinates(p) << "of" << local_dom;
98
                   local_dom = 2.;
60 Werner 99
               }
100
               value = (*m_stamp)(x,y);
101
               value = 1. - value*lafactor / local_dom;
59 Werner 102
               value = qMax(value, 0.02f);
70 Werner 103
#ifndef NOFULLDBG
104
                qDebug() << x << y << p.x() << p.y() << local_dom << (*m_stamp)(x,y) << 1. - (*m_stamp)(x,y)*lafactor / local_dom  << m_grid->valueAtIndex(p) << m_grid->valueAtIndex(p)*value;
105
#endif
106
 
58 Werner 107
               m_grid->valueAtIndex(p)*= value;
51 Werner 108
           }
40 Werner 109
        }
110
    }
58 Werner 111
 
112
    m_statPrint++; // count # of stamp applications...
113
}
77 Werner 114
*/
58 Werner 115
 
77 Werner 116
void Tree::applyStamp()
117
{
106 Werner 118
    Q_ASSERT(mGrid!=0 && mStamp!=0 && mRU!=0);
77 Werner 119
 
106 Werner 120
    QPoint pos = mGrid->indexAt(mPosition);
121
    int offset = mStamp->offset();
77 Werner 122
    pos-=QPoint(offset, offset);
123
 
124
    float local_dom; // height of Z* on the current position
125
    int x,y;
126
    float value;
106 Werner 127
    int gr_stamp = mStamp->size();
77 Werner 128
    int grid_x, grid_y;
129
    float *grid_value;
106 Werner 130
    if (!mGrid->isIndexValid(pos) || !mGrid->isIndexValid(pos+QPoint(gr_stamp, gr_stamp))) {
77 Werner 131
        // todo: in this case we should use another algorithm!!!
132
        return;
133
    }
134
 
135
    for (y=0;y<gr_stamp; ++y) {
136
        grid_y = pos.y() + y;
106 Werner 137
        grid_value = mGrid->ptr(pos.x(), grid_y);
77 Werner 138
        for (x=0;x<gr_stamp;++x) {
139
            // suppose there is no stamping outside
140
            grid_x = pos.x() + x;
141
 
106 Werner 142
            local_dom = mHeightGrid->valueAtIndex(grid_x/5, grid_y/5);
143
            value = (*mStamp)(x,y); // stampvalue
77 Werner 144
            value = 1. - value*lafactor / local_dom; // calculated value
145
            value = qMax(value, 0.02f); // limit value
146
 
147
            *grid_value++ *= value;
148
        }
149
    }
150
 
151
    m_statPrint++; // count # of stamp applications...
152
}
153
 
74 Werner 154
 /*
155
void Tree::heightGrid_old()
58 Werner 156
{
59 Werner 157
    // height of Z*
74 Werner 158
    const float cellsize = m_dominanceGrid->cellsize();
62 Werner 159
    if (m_Height < cellsize/2.) {
59 Werner 160
        float &dom = m_dominanceGrid->valueAt(m_Position); // modifyable reference
161
        dom = qMax(dom, m_Height/2.f);
162
    } else {
163
        QPoint p = m_dominanceGrid->indexAt(m_Position); // pos of tree on height grid
62 Werner 164
 
165
        int ringcount = int(floor(m_Height / cellsize));
59 Werner 166
        int ix, iy;
167
        int ring;
168
        QPoint pos;
62 Werner 169
        float hdom;
170
        float h_out = fmod(m_Height, cellsize) / 2.;
59 Werner 171
        for (ix=-ringcount;ix<=ringcount;ix++)
172
            for (iy=-ringcount; iy<=+ringcount; iy++) {
173
            ring = qMax(abs(ix), abs(iy));
174
            QPoint pos(ix+p.x(), iy+p.y());
175
            if (m_dominanceGrid->isIndexValid(pos)) {
176
                // apply value....
62 Werner 177
                if (ring==0) {
178
                    hdom = m_Height- cellsize/4.;
179
                } else if (ring==abs(ringcount)) {
180
                    // outermost ring: use again height/2.
181
                    hdom = h_out;
182
                } else {
183
                    hdom = m_Height- ring*cellsize;
184
                }
185
                m_dominanceGrid->valueAtIndex(pos) = qMax(m_dominanceGrid->valueAtIndex(pos), hdom);
59 Werner 186
            }
45 Werner 187
 
59 Werner 188
        }
189
    }
190
 
74 Werner 191
} */
192
 
193
/** heightGrid()
194
  This function calculates the "dominant height field". This grid is coarser as the fine-scaled light-grid.
195
 
196
*/
197
void Tree::heightGrid()
198
{
199
    // height of Z*
106 Werner 200
    const float cellsize = mHeightGrid->cellsize();
74 Werner 201
 
106 Werner 202
    QPoint p = mHeightGrid->indexAt(mPosition); // pos of tree on height grid
203
    QPoint competition_grid = mGrid->indexAt(mPosition);
74 Werner 204
 
205
    int index_eastwest = competition_grid.x() % 5; // 4: very west, 0 east edge
206
    int index_northsouth = competition_grid.y() % 5; // 4: northern edge, 0: southern edge
207
    int dist[9];
208
    dist[3] = index_northsouth * 2 + 1; // south
209
    dist[1] = index_eastwest * 2 + 1; // west
210
    dist[5] = 10 - dist[3]; // north
211
    dist[7] = 10 - dist[1]; // east
212
    dist[8] = qMax(dist[5], dist[7]); // north-east
213
    dist[6] = qMax(dist[3], dist[7]); // south-east
214
    dist[0] = qMax(dist[3], dist[1]); // south-west
215
    dist[2] = qMax(dist[5], dist[1]); // north-west
75 Werner 216
    dist[4] = 0; // center cell
76 Werner 217
    /* the scheme of indices is as follows:  if sign(ix)= -1, if ix<0, 0 for ix=0, 1 for ix>0 (detto iy), then:
218
       index = 4 + 3*sign(ix) + sign(iy) transforms combinations of directions to unique ids (0..8), which are used above.
219
        e.g.: sign(ix) = -1, sign(iy) = 1 (=north-west) -> index = 4 + -3 + 1 = 2
220
    */
74 Werner 221
 
222
 
106 Werner 223
    int ringcount = int(floor(mHeight / cellsize)) + 1;
74 Werner 224
    int ix, iy;
225
    int ring;
226
    QPoint pos;
227
    float hdom;
228
 
229
    for (ix=-ringcount;ix<=ringcount;ix++)
230
        for (iy=-ringcount; iy<=+ringcount; iy++) {
231
        ring = qMax(abs(ix), abs(iy));
232
        QPoint pos(ix+p.x(), iy+p.y());
106 Werner 233
        if (mHeightGrid->isIndexValid(pos)) {
234
            float &rHGrid = mHeightGrid->valueAtIndex(pos);
235
            if (rHGrid > mHeight) // skip calculation if grid is higher than tree
74 Werner 236
                continue;
237
            int direction = 4 + (ix?(ix<0?-3:3):0) + (iy?(iy<0?-1:1):0); // 4 + 3*sgn(x) + sgn(y)
106 Werner 238
            hdom = mHeight - dist[direction];
74 Werner 239
            if (ring>1)
240
                hdom -= (ring-1)*10;
241
 
242
            rHGrid = qMax(rHGrid, hdom); // write value
243
        } // is valid
244
    } // for (y)
39 Werner 245
}
40 Werner 246
 
247
double Tree::readStamp()
248
{
106 Werner 249
    if (!mStamp)
51 Werner 250
        return 0.;
106 Werner 251
    const Stamp *stamp = mStamp->reader();
40 Werner 252
    if (!stamp)
253
        return 0.;
106 Werner 254
    QPoint pos = mGrid->indexAt(mPosition);
40 Werner 255
    int offset = stamp->offset();
256
    pos-=QPoint(offset, offset);
257
    QPoint p;
258
 
259
    int x,y;
260
    double sum=0.;
261
    for (x=0;x<stamp->size();++x) {
262
        for (y=0;y<stamp->size(); ++y) {
263
           p = pos + QPoint(x,y);
106 Werner 264
           if (mGrid->isIndexValid(p))
265
               sum += mGrid->valueAtIndex(p) * (*stamp)(x,y);
40 Werner 266
        }
267
    }
106 Werner 268
    float eigenvalue = mStamp->readSum() * lafactor;
269
    mLRI = sum - eigenvalue;// additive
270
    float dom_height = (*mHeightGrid)[mPosition];
53 Werner 271
    if (dom_height>0.)
106 Werner 272
       mLRI = mLRI / dom_height;
53 Werner 273
 
274
    //mImpact = sum + eigenvalue;// multiplicative
48 Werner 275
    // read dominance field...
53 Werner 276
 
106 Werner 277
    if (dom_height < mHeight) {
48 Werner 278
        // if tree is higher than Z*, the dominance height
279
        // a part of the crown is in "full light".
280
        // total value = zstar/treeheight*value + 1-zstar/height
281
        // reformulated to:
106 Werner 282
        mLRI =  mLRI * dom_height/mHeight ;
48 Werner 283
        m_statAboveZ++;
284
    }
106 Werner 285
    if (fabs(mLRI < 0.000001))
286
        mLRI = 0.f;
287
    qDebug() << "Tree #"<< id() << "value" << sum << "eigenvalue" << eigenvalue << "Impact" << mLRI;
288
    return mLRI;
40 Werner 289
}
290
 
78 Werner 291
/*
58 Werner 292
double Tree::readStampMul()
293
{
294
    if (!m_stamp)
295
        return 0.;
296
    const Stamp *reader = m_stamp->reader();
297
    if (!reader)
298
        return 0.;
299
    QPoint pos_reader = m_grid->indexAt(m_Position);
300
 
301
    int offset_reader = reader->offset();
302
    int offset_writer = m_stamp->offset();
303
    int d_offset = offset_writer - offset_reader;
304
 
305
    QPoint pos_writer=pos_reader - QPoint(offset_writer, offset_writer);
306
    pos_reader-=QPoint(offset_reader, offset_reader);
307
    QPoint p;
308
 
309
    float dom_height = (*m_dominanceGrid)[m_Position];
70 Werner 310
    float local_dom;
58 Werner 311
 
312
    int x,y;
313
    double sum=0.;
314
    double value, own_value;
315
    for (x=0;x<reader->size();++x) {
316
        for (y=0;y<reader->size(); ++y) {
317
            p = pos_reader + QPoint(x,y);
318
            if (m_grid->isIndexValid(p)) {
70 Werner 319
                local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) );
320
                own_value = 1. - m_stamp->offsetValue(x,y,d_offset)*lafactor /local_dom; // old: dom_height;
59 Werner 321
                own_value = qMax(own_value, 0.02);
58 Werner 322
                value =  m_grid->valueAtIndex(p) / own_value; // remove impact of focal tree
323
                if (value>0.)
324
                    sum += value * (*reader)(x,y);
325
                //value = (1. - m_stamp->offsetValue(x,y,d_offset)/dom_height);
326
                //value = (1 - m_grid->valueAtIndex(p)) / (m_stamp->offsetValue(x,y,d_offset)/dom_height * lafactor);
327
                if (isDebugging()) {
328
                    qDebug() << "Tree" << id() << "Coord" << p << "gridvalue" << m_grid->valueAtIndex(p) << "value" << value << "reader" << (*reader)(x,y) << "writer" << m_stamp->offsetValue(x,y,d_offset);
329
                }
330
 
331
            }
332
        }
333
    }
334
    mImpact = sum;
335
    // read dominance field...
336
    if (dom_height < m_Height) {
337
        // if tree is higher than Z*, the dominance height
338
        // a part of the crown is in "full light".
339
        m_statAboveZ++;
340
        mImpact = 1. - (1. - mImpact)*dom_height/m_Height;
341
    }
342
    if (mImpact > 1)
343
        mImpact = 1.f;
61 Werner 344
    //qDebug() << "Tree #"<< id() << "value" << sum << "Impact" << mImpact;
58 Werner 345
    return mImpact;
78 Werner 346
}*/
347
 
107 Werner 348
void Tree::readStampMul()
78 Werner 349
{
106 Werner 350
    if (!mStamp)
107 Werner 351
        return;
106 Werner 352
    const Stamp *reader = mStamp->reader();
78 Werner 353
    if (!reader)
107 Werner 354
        return;
106 Werner 355
    QPoint pos_reader = mGrid->indexAt(mPosition);
78 Werner 356
 
357
    int offset_reader = reader->offset();
106 Werner 358
    int offset_writer = mStamp->offset();
78 Werner 359
    int d_offset = offset_writer - offset_reader; // offset on the *stamp* to the crown-cells
360
 
361
    QPoint pos_writer=pos_reader - QPoint(offset_writer, offset_writer);
362
    pos_reader-=QPoint(offset_reader, offset_reader);
363
    QPoint p;
364
 
365
    //float dom_height = (*m_dominanceGrid)[m_Position];
366
    float local_dom;
367
 
368
    int x,y;
369
    double sum=0.;
370
    double value, own_value;
371
    float *grid_value;
372
    int reader_size = reader->size();
373
    int rx = pos_reader.x();
374
    int ry = pos_reader.y();
375
    for (y=0;y<reader_size; ++y, ++ry) {
106 Werner 376
        grid_value = mGrid->ptr(rx, ry);
78 Werner 377
        for (x=0;x<reader_size;++x) {
378
 
379
            //p = pos_reader + QPoint(x,y);
380
            //if (m_grid->isIndexValid(p)) {
106 Werner 381
            local_dom = mHeightGrid->valueAtIndex((rx+x)/5, ry/5);
78 Werner 382
            //local_dom = m_dominanceGrid->valueAt( m_grid->cellCoordinates(p) );
106 Werner 383
            own_value = 1. - mStamp->offsetValue(x,y,d_offset)*lafactor /local_dom; // old: dom_height;
78 Werner 384
            own_value = qMax(own_value, 0.02);
385
            value =  *grid_value++ / own_value; // remove impact of focal tree
386
            //if (value>0.)
387
            sum += value * (*reader)(x,y);
388
 
389
            //} // isIndexValid
390
        }
391
    }
106 Werner 392
    mLRI = sum;
78 Werner 393
    // read dominance field...
394
    // this applies only if some trees are potentially *higher* than the dominant height grid
395
    //if (dom_height < m_Height) {
396
        // if tree is higher than Z*, the dominance height
397
        // a part of the crown is in "full light".
398
    //    m_statAboveZ++;
399
    //    mImpact = 1. - (1. - mImpact)*dom_height/m_Height;
400
    //}
106 Werner 401
    if (mLRI > 1)
402
        mLRI = 1.f;
78 Werner 403
    //qDebug() << "Tree #"<< id() << "value" << sum << "Impact" << mImpact;
110 Werner 404
    mRU->addWLA(mLRI * mLeafArea, mLeafArea);
58 Werner 405
}
406
 
40 Werner 407
void Tree::resetStatistics()
408
{
409
    m_statPrint=0;
105 Werner 410
    m_statCreated=0;
48 Werner 411
    m_statAboveZ=0;
40 Werner 412
    m_nextId=1;
413
}
107 Werner 414
 
110 Werner 415
//////////////////////////////////////////////////
416
////  Growth Functions
417
//////////////////////////////////////////////////
107 Werner 418
 
110 Werner 419
/// evaluate allometries and calculate LeafArea
420
void Tree::calcBiomassCompartments()
421
{
422
    mLeafMass = mSpecies->biomassLeaf(mDbh);
423
    mRootMass = mSpecies->biomassRoot(mDbh);
424
    mStemMass = mSpecies->biomassStem(mDbh);
425
    // LeafArea = LeafMass * specificLeafArea
426
    mLeafArea = mLeafMass * mSpecies->specificLeafArea();
427
}
428
 
429
 
107 Werner 430
void Tree::grow()
431
{
432
    // step 1: calculate radiation:
433
    double radiation = mRU->interceptedRadiation(mLRI * mLeafArea);
111 Werner 434
    double GPP_per_rad = 0.;
107 Werner 435
 
110 Werner 436
    calcBiomassCompartments();
437
 
107 Werner 438
}
439