Subversion Repositories public iLand

Rev

Rev 1221 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1033 werner 1
/********************************************************************************************
2
**    iLand - an individual based forest landscape and disturbance model
3
**    http://iland.boku.ac.at
4
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
5
**
6
**    This program is free software: you can redistribute it and/or modify
7
**    it under the terms of the GNU General Public License as published by
8
**    the Free Software Foundation, either version 3 of the License, or
9
**    (at your option) any later version.
10
**
11
**    This program is distributed in the hope that it will be useful,
12
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
**    GNU General Public License for more details.
15
**
16
**    You should have received a copy of the GNU General Public License
17
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
********************************************************************************************/
19
 
959 werner 20
#include "barkbeetlemodule.h"
21
 
22
#include "globalsettings.h"
23
#include "model.h"
961 werner 24
#include "modelcontroller.h"
959 werner 25
#include "resourceunit.h"
1009 werner 26
#include "species.h"
1008 werner 27
#include "debugtimer.h"
1014 werner 28
#include "outputmanager.h"
1054 werner 29
#include "climate.h"
1070 werner 30
#include "abe/forestmanagementengine.h"
959 werner 31
 
1010 werner 32
 
1095 werner 33
/** @defgroup beetlemodule iLand barkbeetle module
34
  The bark beetle module is a disturbance module within the iLand framework.
35
 
36
  See http://iland.boku.ac.at/barkbeetle for the science behind the module,
37
  and http://iland.boku.ac.at/barkbeetle+module for the implementation/ using side.
38
 */
39
 
40
 
1010 werner 41
int BarkBeetleCell::total_infested = 0;
1036 werner 42
int BarkBeetleCell::max_iteration = 0;
1010 werner 43
 
1095 werner 44
/** @class BarkBeetleModule
45
    @ingroup beetlemodule
46
    BarkBeetleModule is the main class for the bark beetle module. It calculates the development of generations, the spread and attack of beetles.
47
    It operates on a 10m grid.
1039 werner 48
 
1095 werner 49
  */
1053 werner 50
 
1095 werner 51
 
959 werner 52
BarkBeetleModule::BarkBeetleModule()
53
{
961 werner 54
    mLayers.setGrid(mGrid);
1008 werner 55
    mRULayers.setGrid(mRUGrid);
1024 werner 56
    mSimulate = false;
1037 werner 57
    mEnabled = false;
1055 werner 58
    mYear = 0;
961 werner 59
 
959 werner 60
}
61
 
1039 werner 62
BarkBeetleModule::~BarkBeetleModule()
63
{
64
}
65
 
959 werner 66
void BarkBeetleModule::setup()
67
{
68
    // setup the wind grid
69
    mGrid.setup(GlobalSettings::instance()->model()->heightGrid()->metricRect(), cellsize());
961 werner 70
    BarkBeetleCell cell;
71
    mGrid.initialize(cell);
959 werner 72
 
1008 werner 73
    mRUGrid.setup(GlobalSettings::instance()->model()->RUgrid().metricRect(), GlobalSettings::instance()->model()->RUgrid().cellsize());
74
    BarkBeetleRUCell ru_cell;
75
    mRUGrid.initialize(ru_cell);
76
 
961 werner 77
    GlobalSettings::instance()->controller()->addLayers(&mLayers, "bb");
1008 werner 78
    GlobalSettings::instance()->controller()->addLayers(&mRULayers, "bbru");
959 werner 79
 
1010 werner 80
    // load settings from the XML file
81
    loadParameters();
961 werner 82
 
959 werner 83
}
84
 
85
void BarkBeetleModule::setup(const ResourceUnit *ru)
86
{
1008 werner 87
    if (!ru)
88
        return;
1009 werner 89
    //qDebug() << "BB setup for RU" << ru->id();
959 werner 90
 
1054 werner 91
    double ru_value = GlobalSettings::instance()->settings().valueDouble("modules.barkbeetle.backgroundInfestationProbability");
92
    // probabilistic OR-calculation: p_1ha = 1 - (1 - p_pixel)^n   -> p_pixel = 1 - (1-p_ha)^(1/n)
93
    // we need the probability of damage starting on a single 10m - pixel.
94
    double pixel_value = 1. - pow(1. - ru_value, 1./( cellsize()*cellsize()));
95
 
96
    // set all pixel within the resource unit
97
    GridRunner<BarkBeetleCell> runner(mGrid, ru->boundingBox());
98
    while (BarkBeetleCell *cell = runner.next())
1157 werner 99
        cell->backgroundInfestationProbability = static_cast<float>(pixel_value);
1054 werner 100
 
959 werner 101
}
102
 
1009 werner 103
void BarkBeetleModule::loadParameters()
104
{
105
    const XmlHelper xml = GlobalSettings::instance()->settings().node("modules.barkbeetle");
1157 werner 106
    params.cohortsPerGeneration = xml.valueInt(".cohortsPerGeneration", params.cohortsPerGeneration);
107
    params.cohortsPerSisterbrood = xml.valueInt(".cohortsPerSisterbrood", params.cohortsPerSisterbrood);
1013 werner 108
    params.spreadKernelMaxDistance = xml.valueDouble(".spreadKernelMaxDistance", params.spreadKernelMaxDistance);
1009 werner 109
    params.spreadKernelFormula = xml.value(".spreadKernelFormula", "100*(1-x)^4");
1157 werner 110
    params.minDbh = static_cast<float>( xml.valueDouble(".minimumDbh", params.minDbh) );
1013 werner 111
    mKernelPDF.setup(params.spreadKernelFormula,0.,params.spreadKernelMaxDistance);
1009 werner 112
    params.backgroundInfestationProbability = xml.valueDouble(".backgroundInfestationProbability", params.backgroundInfestationProbability);
1157 werner 113
    params.initialInfestationProbability = xml.valueDouble(".initialInfestationProbability", params.initialInfestationProbability);
1066 werner 114
    params.stormInfestationProbability = xml.valueDouble(".stormInfestationProbability", params.stormInfestationProbability);
115
    params.deadTreeSelectivity = xml.valueDouble(".deadTreeSelectivity", params.deadTreeSelectivity);
1037 werner 116
 
1066 werner 117
 
1010 werner 118
    QString formula = xml.value(".colonizeProbabilityFormula", "0.1");
119
    mColonizeProbability.setExpression(formula);
1009 werner 120
 
1010 werner 121
    formula = xml.value(".winterMortalityFormula", "polygon(days, 0,0, 30, 0.6)");
122
    mWinterMortalityFormula.setExpression(formula);
1037 werner 123
 
1054 werner 124
    formula = xml.value(".outbreakClimateSensitivityFormula", "1");
125
    mOutbreakClimateSensitivityFormula.setExpression(formula);
126
    double **p = &mClimateVariables[0];
127
    *p++ = mOutbreakClimateSensitivityFormula.addVar("Pspring");
128
    *p++ = mOutbreakClimateSensitivityFormula.addVar("Psummer");
129
    *p++ = mOutbreakClimateSensitivityFormula.addVar("Pautumn");
130
    *p++ = mOutbreakClimateSensitivityFormula.addVar("Pwinter");
131
    *p++ = mOutbreakClimateSensitivityFormula.addVar("Tspring");
132
    *p++ = mOutbreakClimateSensitivityFormula.addVar("Tsummer");
133
    *p++ = mOutbreakClimateSensitivityFormula.addVar("Tautumn");
134
    *p++ = mOutbreakClimateSensitivityFormula.addVar("Twinter");
135
    mOutbreakClimateSensitivityFormula.parse();
136
 
1057 werner 137
    params.outbreakDurationMin = xml.valueDouble(".outbreakDurationMin", 0.);
138
    params.outbreakDurationMax = xml.valueDouble(".outbreakDurationMax", 0.);
139
    formula = xml.value(".outbreakDurationMortalityFormula", "0");
140
    mOutbreakDurationFormula.setExpression(formula);
141
 
1054 werner 142
    QString ref_table_name = xml.value(".referenceClimate.tableName");
143
    QString ref_climate_values = xml.value(".referenceClimate.seasonalPrecipSum");
144
    QStringList tmp = ref_climate_values.split(',');
145
    mRefClimateAverages.clear();
146
    foreach(QString v, tmp) mRefClimateAverages.push_back(v.toDouble());
147
    ref_climate_values = xml.value(".referenceClimate.seasonalTemperatureAverage");
148
    tmp = ref_climate_values.split(',');
149
    foreach(QString v, tmp) mRefClimateAverages.push_back(v.toDouble());
150
    if (mRefClimateAverages.count()!=8)
151
        throw IException("Barkbeetle Setup: Error: invalid values for seasonalPrecipSum or seasonalTemperatureAverage (4 ','-separated values expected).");
152
 
153
    mRefClimate = 0;
154
    foreach(const Climate *clim, GlobalSettings::instance()->model()->climates())
155
        if (clim->name()==ref_table_name) {
156
            mRefClimate = clim; break;
157
        }
158
    qDebug() << "refclimate" << mRefClimateAverages;
159
    if (!mRefClimate)
1055 werner 160
        throw IException(QString("Barkbeetle Setup: Error: a climate table '%1' (given in modules.barkbeetle.referenceClimate.tableName) for the barkbeetle reference climate does not exist.").arg(ref_table_name));
1054 werner 161
 
1010 werner 162
    params.winterMortalityBaseLevel = xml.valueDouble(".baseWinterMortality", 0.5);
1009 werner 163
 
1014 werner 164
    mAfterExecEvent = xml.value(".onAfterBarkbeetle");
165
 
1039 werner 166
 
1010 werner 167
    HeightGridValue *hgv = GlobalSettings::instance()->model()->heightGrid()->begin();
168
    for (BarkBeetleCell *b=mGrid.begin();b!=mGrid.end();++b, ++hgv) {
169
        b->reset();
170
        //scanResourceUnitTrees(mGrid.indexOf(b)); // scan all - not efficient
1024 werner 171
//        if (hgv->isValid()) {
172
//            b->dbh = drandom()<0.6 ? nrandom(20.,40.) : 0.; // random landscape
173
//            b->tree_stress = nrandom(0., 0.4);
174
//        }
1010 werner 175
    }
176
 
1039 werner 177
 
1024 werner 178
    yearBegin(); // also reset the "scanned" flags
179
 
1009 werner 180
}
181
 
1013 werner 182
void BarkBeetleModule::clearGrids()
959 werner 183
{
1013 werner 184
 
185
    for (BarkBeetleCell *b=mGrid.begin();b!=mGrid.end();++b)
1037 werner 186
        b->reset();
1013 werner 187
 
188
    BarkBeetleRUCell ru_cell;
189
    mRUGrid.initialize(ru_cell);
190
 
191
    BarkBeetleCell::resetCounters();
192
    stats.clear();
193
 
1039 werner 194
 
1013 werner 195
}
196
 
1024 werner 197
void BarkBeetleModule::loadAllVegetation()
198
{
1037 werner 199
    // refetch vegetation information (if necessary)
200
    foreach (const ResourceUnit *ru, GlobalSettings::instance()->model()->ruList()) {
1024 werner 201
        scanResourceUnitTrees(ru->boundingBox().center());
1037 werner 202
    }
203
 
204
    // save the damage information of the last year
205
    for (BarkBeetleRUCell *bbru=mRUGrid.begin(); bbru!=mRUGrid.end(); ++bbru) {
206
        bbru->killed_pixels = 0; // reset
207
    }
208
 
1039 werner 209
 
210
 
1024 werner 211
}
212
 
1013 werner 213
void BarkBeetleModule::run(int iteration)
214
{
1157 werner 215
    DebugTimer t("barkbeetle:total");
1010 werner 216
    // reset statistics
217
    BarkBeetleCell::resetCounters();
1013 werner 218
    int old_max_gen = stats.maxGenerations;
1010 werner 219
    stats.clear();
1013 werner 220
    mIteration = iteration;
1008 werner 221
 
1010 werner 222
    // calculate the potential bark beetle generations for each resource unit
1013 werner 223
    if (iteration==0)
224
        calculateGenerations();
225
    else
226
        stats.maxGenerations = old_max_gen; // save that value....
1010 werner 227
 
1054 werner 228
    // outbreak probability
229
    calculateOutbreakFactor();
230
 
1037 werner 231
    // load the vegetation (skipped if this is not the initial iteration)
1024 werner 232
    loadAllVegetation();
233
 
1037 werner 234
    // background probability of infestation, calculation of antagonist levels
1010 werner 235
    startSpread();
236
 
237
    // the spread of beetles (and attacking of trees)
238
    barkbeetleSpread();
239
 
1064 werner 240
    // write back the effects of the bark beetle module to the forest in iLand
1029 werner 241
    barkbeetleKill();
1010 werner 242
 
1013 werner 243
    // create some outputs....
1066 werner 244
    qDebug() << "iter/background-inf/winter-mort/storm-inf/N spread/N landed/N infested: " << mIteration << stats.infestedBackground << stats.NWinterMortality << stats.infestedStorm << stats.NCohortsSpread << stats.NCohortsLanded << stats.NInfested;
1014 werner 245
    GlobalSettings::instance()->outputManager()->execute("barkbeetle");
246
    //GlobalSettings::instance()->outputManager()->save();
1013 werner 247
 
1036 werner 248
    // execute the after bark-beetle infestation event
1014 werner 249
    if (!mAfterExecEvent.isEmpty()) {
250
        // evaluate the javascript function...
251
        GlobalSettings::instance()->executeJavascript(mAfterExecEvent);
252
    }
1013 werner 253
 
959 werner 254
}
255
 
1044 werner 256
void BarkBeetleModule::treeDeath(const Tree *tree)
257
{
258
    // do nothing if the tree was killed by bark beetles
259
    if (tree->isDeadBarkBeetle())
260
        return;
1070 werner 261
    // we only process trees here that are either
262
    // killed by storm or deliberately killed and dropped
263
    // by management and are not are already salvaged
264
    if ( !(tree->isDeadWind() || tree->isCutdown()))
1044 werner 265
        return;
1066 werner 266
 
1070 werner 267
 
1044 werner 268
    // ignore the death of trees that are too small or are not Norway spruce
269
    if (tree->dbh()<params.minDbh || tree->species()->id()!=QStringLiteral("piab"))
270
        return;
271
 
272
    BarkBeetleCell &cell = mGrid.valueAt(tree->position());
1070 werner 273
    if (tree->isDeadWind())
274
        cell.deadtrees = BarkBeetleCell::StormDamage;
275
    if (tree->isCutdown())
276
        cell.deadtrees = BarkBeetleCell::BeetleTrapTree;
1044 werner 277
 
278
 
279
 
280
}
281
 
1024 werner 282
void BarkBeetleModule::scanResourceUnitTrees(const QPointF &position)
1009 werner 283
{
284
    // if this resource unit was already scanned in this bark beetle event, then do nothing
285
    // the flags are reset
1024 werner 286
    if (!mRUGrid.coordValid(position))
1009 werner 287
        return;
288
 
1024 werner 289
    if (mRUGrid.valueAt(position).scanned)
1009 werner 290
        return;
291
 
1024 werner 292
    ResourceUnit *ru = GlobalSettings::instance()->model()->ru(position);
1009 werner 293
    if (!ru)
294
        return;
295
 
1037 werner 296
    BarkBeetleRUCell &ru_cell = mRUGrid.valueAt(position);
297
    ru_cell.host_pixels=0;
298
 
299
    // reset the DBH on all pixels within the resource unit
300
    GridRunner<BarkBeetleCell> runner(mGrid, ru->boundingBox());
301
    while (runner.next())
302
        runner.current()->dbh=0.f;
303
 
1009 werner 304
    QVector<Tree>::const_iterator tend = ru->trees().constEnd();
305
    for (QVector<Tree>::const_iterator t = ru->trees().constBegin(); t!=tend; ++t) {
306
        if (!t->isDead() && t->species()->id()==QStringLiteral("piab") && t->dbh()>params.minDbh) {
307
 
308
            const QPoint &tp = t->positionIndex();
309
            QPoint pcell(tp.x()/cPxPerHeight, tp.y()/cPxPerHeight);
310
 
311
            BarkBeetleCell &bb=mGrid.valueAtIndex(pcell);
1037 werner 312
            // count the host pixels (only once)
313
            if (bb.dbh==0.f)
314
                ru_cell.host_pixels++;
315
 
1024 werner 316
            if (t->dbh() > bb.dbh) {
1010 werner 317
                bb.dbh = t->dbh();
318
                bb.tree_stress = t->stressIndex();
319
            }
1009 werner 320
 
1037 werner 321
 
1009 werner 322
        }
323
    }
324
    // set the "processed" flag
1037 werner 325
    ru_cell.scanned = true;
1009 werner 326
}
327
 
328
 
1037 werner 329
 
959 werner 330
void BarkBeetleModule::yearBegin()
331
{
1024 werner 332
    // reset the scanned flag of resource units (force reload of stand structure)
1037 werner 333
    for (BarkBeetleRUCell *bbru=mRUGrid.begin();bbru!=mRUGrid.end();++bbru) {
1024 werner 334
        bbru->scanned = false;
1070 werner 335
        bbru->infested=0;
1037 werner 336
    }
1065 werner 337
 
338
    // reset the effect of wind-damaged trees and "fangbaueme"
339
    for (BarkBeetleCell *c=mGrid.begin(); c!=mGrid.end(); ++c)
340
        c->deadtrees = BarkBeetleCell::NoDeadTrees;
341
 
1055 werner 342
    mYear = GlobalSettings::instance()->instance()->currentYear();
959 werner 343
}
961 werner 344
 
1010 werner 345
void BarkBeetleModule::calculateGenerations()
346
{
347
    // calculate generations
348
    DebugTimer d("BB:generations");
349
    ResourceUnit **ruptr = GlobalSettings::instance()->model()->RUgrid().begin();
350
    BarkBeetleRUCell *bbptr = mRUGrid.begin();
351
    while (bbptr<mRUGrid.end()) {
352
        if (*ruptr) {
353
            bbptr->scanned = false;
1024 werner 354
            bbptr->killed_trees = false;
1010 werner 355
            bbptr->generations = mGenerations.calculateGenerations(*ruptr);
356
            bbptr->add_sister = mGenerations.hasSisterBrood();
357
            bbptr->cold_days = bbptr->cold_days_late + mGenerations.frostDaysEarly();
358
            bbptr->cold_days_late = mGenerations.frostDaysLate(); // save for next year
359
            stats.maxGenerations = std::max(int(bbptr->generations), stats.maxGenerations);
360
        }
361
        ++bbptr; ++ruptr;
961 werner 362
 
1010 werner 363
    }
364
}
365
 
1054 werner 366
void BarkBeetleModule::calculateOutbreakFactor()
367
{
1092 werner 368
    if (!mRefClimate) {
369
        mRc = 1.;
1054 werner 370
        return;
1092 werner 371
    }
1054 werner 372
    const double *t = mRefClimate->temperatureMonth();
373
    const double *p = mRefClimate->precipitationMonth();
374
    // Pspring, Psummer, Pautumn, Pwinter, Tspring, Tsummer, Tautumn, Twinter
375
    // seasonal sum precip -> relative values
376
    *mClimateVariables[0] = (p[2]+p[3]+p[4]) / mRefClimateAverages[0];
377
    *mClimateVariables[1] = (p[5]+p[6]+p[7]) / mRefClimateAverages[1];
378
    *mClimateVariables[2] = (p[8]+p[9]+p[10]) / mRefClimateAverages[2];
1055 werner 379
    *mClimateVariables[3] = (p[11]+p[0]+p[1])  / mRefClimateAverages[3]; // not really clean.... using all month of the current year
1054 werner 380
    // temperatures (mean monthly temp) -> delta
381
    *mClimateVariables[4] = (t[2]+t[3]+t[4])/3. - mRefClimateAverages[4];
382
    *mClimateVariables[5] = (t[5]+t[6]+t[7])/3. - mRefClimateAverages[5];
383
    *mClimateVariables[6] = (t[8]+t[9]+t[10])/3. - mRefClimateAverages[6];
384
    *mClimateVariables[7] = (t[11]+t[0]+t[1])/3. - mRefClimateAverages[7];
385
 
1055 werner 386
    mRc = qMax(mOutbreakClimateSensitivityFormula.execute(), 0.);
387
    qDebug() << "Barkbeelte: rc:" << mRc;
1054 werner 388
 
389
}
390
 
1010 werner 391
void BarkBeetleModule::startSpread()
392
{
393
    // calculate winter mortality
1055 werner 394
    //  probability of infestation
1010 werner 395
    for (BarkBeetleCell *b=mGrid.begin();b!=mGrid.end();++b) {
396
        if (b->infested) {
1021 werner 397
            stats.infestedStart++;
1092 werner 398
            // base mortality (Mbg)
1010 werner 399
            if (drandom()<params.winterMortalityBaseLevel) {
400
                // the beetles on the pixel died
401
                b->setInfested(false);
402
                stats.NWinterMortality++;
403
            } else {
1092 werner 404
                // winter mortality - maybe the beetles die due to low winter temperatures (Mw)
1024 werner 405
                int cold_days = mRUGrid.constValueAt(mGrid.cellCenterPoint(mGrid.indexOf(b))).cold_days;
1010 werner 406
                double p_winter = mWinterMortalityFormula.calculate(cold_days);
407
                if (drandom()<p_winter) {
408
                    b->setInfested(false);
409
                    stats.NWinterMortality++;
410
                }
411
            }
412
 
1055 werner 413
        } else if (b->isPotentialHost()) {
1157 werner 414
            if (mYear==1 && params.initialInfestationProbability>0.) {
415
                if (drandom() < params.initialInfestationProbability) {
416
                    b->setInfested(true);
1164 werner 417
                    b->outbreakYear = 1 - irandom(0,4); // initial outbreaks has an age of 1-4 years
1157 werner 418
                    stats.infestedBackground++;
419
                }
420
            } else if (b->backgroundInfestationProbability>0.f) {
421
                // calculate probability for an outbreak
422
                double odds_base = b->backgroundInfestationProbability / (1. - b->backgroundInfestationProbability);
423
                double p_mod = (odds_base*mRc) / (1. + odds_base*mRc);
424
                if (drandom() < p_mod) {
425
                    // background activation: 10 px
426
                    //clumpedBackgroundActivation(mGrid.indexOf(b));
427
                    b->setInfested(true);
428
                    b->outbreakYear = mYear; // this outbreak starts in the current year
429
                    stats.infestedBackground++;
430
                }
1055 werner 431
            }
1010 werner 432
        }
433
 
434
        b->n = 0;
1024 werner 435
        b->killed=false;
1157 werner 436
        b->killedYear = 0;
1055 werner 437
        b->packageOutbreakYear = 0.f;
1068 werner 438
 
1010 werner 439
    }
1064 werner 440
 
441
    prepareInteractions();
1070 werner 442
 
443
    // tell the forest management (at least if someone is interested)
444
    // if bark beetle attacks are likely
445
    ABE::ForestManagementEngine *abe = GlobalSettings::instance()->model()->ABEngine();
446
    if (abe) {
447
        // reset the scanned flag of resource units (force reload of stand structure)
448
        bool forest_changed = false;
449
        for (BarkBeetleRUCell *bbru=mRUGrid.begin();bbru!=mRUGrid.end();++bbru) {
450
            if (bbru->generations>=1. && bbru->infested>0) {
451
                // notify about potential bb-attack
452
                ResourceUnit *ru = GlobalSettings::instance()->model()->RUgrid()[mRUGrid.indexOf(bbru)];
453
                forest_changed |= abe->notifyBarkbeetleAttack(ru, bbru->generations, bbru->infested);
454
 
455
            }
456
 
457
        }
458
        if (forest_changed)
1077 werner 459
            prepareInteractions(true);
1070 werner 460
    }
461
 
1050 werner 462
}
1010 werner 463
 
1157 werner 464
int BarkBeetleModule::clumpedBackgroundActivation(QPoint start_idx)
465
{
466
    // we assume to start the infestation by randomly activating 8 cells
467
    // in the neighborhood of the starting point (a 5x5 grid)
468
    QRect rect(start_idx-QPoint(2,2), start_idx+QPoint(2,2));
469
    if (!mGrid.isIndexValid(rect.topLeft()) || !mGrid.isIndexValid(rect.bottomRight()))
470
        return 0;
471
    GridRunner<BarkBeetleCell> runner(mGrid, rect);
472
    int n_pot = 0;
473
    while (runner.next())
474
        if (runner.current()->isHost())
475
            ++n_pot;
476
 
477
    if (n_pot==0)
478
        return 0;
479
    runner.reset();
480
    double p_infest = 8. / n_pot;
481
    int n_infest=0;
482
    while (runner.next())
483
        if (runner.current()->isHost())
484
            if (drandom()<p_infest) {
485
                runner.current()->setInfested(true);
486
                runner.current()->outbreakYear = mYear; // this outbreak starts in the current year
487
                stats.infestedBackground++; ++n_infest;
488
            }
489
 
490
    return n_infest;
491
}
492
 
1077 werner 493
void BarkBeetleModule::prepareInteractions(bool update_interaction)
1050 werner 494
{
495
    // loop over all cells of the grid and decide
496
    // for each pixel if it is in the proximinity of (attractive) deadwood
497
    // we assume an influence within the 5x5 pixel neighborhood
498
 
1077 werner 499
    if (!update_interaction && params.stormInfestationProbability<1.) {
500
        // reduce the effect of wind-damaged trees for bark beetle spread (disable pixels with p=1-stormInfestationProbability), but do
501
        // it only during the first pass
502
        for (BarkBeetleCell *c=mGrid.begin(); c!=mGrid.end(); ++c)
503
            if (c->deadtrees == BarkBeetleCell::StormDamage && drandom()>params.stormInfestationProbability)
504
                c->deadtrees = BarkBeetleCell::NoDeadTrees;
505
    }
506
 
507
 
1050 werner 508
    for (int y=0;y<mGrid.sizeY();++y)
509
        for (int x=0;x<mGrid.sizeX();++x) {
1065 werner 510
            BarkBeetleCell &cell = mGrid.valueAtIndex(x,y);
511
            if (cell.deadtrees==BarkBeetleCell::NoDeadTrees) {
1050 werner 512
                int has_neighbors=0;
513
                for (int dy=-2;dy<=2;++dy)
514
                    for (int dx=-2;dx<=2;++dx)
1065 werner 515
                        has_neighbors += mGrid.isIndexValid(x+dx,y+dy) ? (mGrid(x+dx,y+dy).deadtrees==BarkBeetleCell::StormDamage || mGrid(x+dx,y+dy).deadtrees==BarkBeetleCell::BeetleTrapTree ? 1: 0) : 0;
1050 werner 516
 
1065 werner 517
                if (has_neighbors>0)
1092 werner 518
                    cell.deadtrees = BarkBeetleCell::SinkInVicinity;
1050 werner 519
 
1065 werner 520
 
1050 werner 521
            }
1065 werner 522
            if (cell.deadtrees==BarkBeetleCell::StormDamage) {
523
                // the pixel acts as a source
524
                cell.setInfested(true);
1066 werner 525
                cell.outbreakYear = mYear; // this outbreak starts in the current year
526
                stats.infestedStorm++;
1065 werner 527
            }
1070 werner 528
            if (cell.infested) {
529
                // record infestation for the resource unit
530
                mRUGrid[mGrid.cellCenterPoint(QPoint(x,y))].infested++;
531
            }
1050 werner 532
        }
1010 werner 533
}
534
 
1050 werner 535
 
1010 werner 536
void BarkBeetleModule::barkbeetleSpread()
537
{
538
    DebugTimer t("BBSpread");
1057 werner 539
 
540
    double ant_years = qMax(nrandom(params.outbreakDurationMin, params.outbreakDurationMax), 1.);
1010 werner 541
    for (int generation=1;generation<=stats.maxGenerations;++generation) {
542
 
543
        GridRunner<BarkBeetleCell> runner(mGrid, mGrid.rectangle());
544
        GridRunner<BarkBeetleCell> targeter(mGrid, mGrid.rectangle());
545
        while (BarkBeetleCell *b=runner.next()) {
546
            if (!b->infested)
547
                continue;
548
            QPoint start_index = runner.currentIndex();
1024 werner 549
            BarkBeetleRUCell &bbru=mRUGrid.valueAt(runner.currentCoord());
1010 werner 550
            if (generation>bbru.generations)
551
                continue;
552
 
1085 werner 553
            // the number of packages is increased if there is a developed sisterbrood *and* one filial generation
554
            // (Wermelinger and Seiffert, 1999, Wermelinger 2004). If more than one generation develops, we assume
555
            // that the effect of sister broods is reduced.
1082 werner 556
            int n_packets = params.cohortsPerGeneration;
1085 werner 557
            if (bbru.generations<2. && bbru.add_sister)
1082 werner 558
                n_packets = params.cohortsPerSisterbrood;
1037 werner 559
 
1055 werner 560
            // antagonists:
561
            double t_ob = mYear - b->outbreakYear;
1057 werner 562
            double p_antagonist_mort = mOutbreakDurationFormula.calculate(limit(t_ob / ant_years, 0., 1.));
1055 werner 563
            n_packets = qRound(n_packets * (1. - p_antagonist_mort));
564
 
1010 werner 565
            stats.NCohortsSpread += n_packets;
566
 
567
            // mark this cell as "dead" (as the beetles have killed the host trees and now move on)
1013 werner 568
            b->finishedSpread(mIteration>0 ? mIteration+1 : generation);
1024 werner 569
            // mark the resource unit, that some killing is required
570
            bbru.killed_trees = true;
1157 werner 571
            stats.NAreaKilled++;
1037 werner 572
            bbru.killed_pixels++;
573
            bbru.host_pixels--;
1010 werner 574
 
575
            BarkBeetleCell *nb8[8];
576
            for (int i=0;i<n_packets;++i) {
577
                // estimate distance and direction of spread
578
                double rho = mKernelPDF.get(); // distance (m)
579
                double phi = nrandom(0, 2*M_PI); // direction (degree)
580
                // calculate the pixel
581
 
582
                QPoint pos = start_index + QPoint(qRound( rho * sin(phi) / cHeightSize ),  qRound( rho * cos(phi) / cHeightSize ) );
583
                // don't spread to the initial start pixel
584
                if (start_index == pos)
585
                    continue;
586
                targeter.setPosition(pos);
587
                if (!targeter.isValid())
588
                    continue;
589
 
1064 werner 590
 
591
                // effect of windthrown trees or "fangbaume"
592
                if (targeter.current()->isNeutralized())
1066 werner 593
                    if (drandom() < params.deadTreeSelectivity)
594
                        continue;
1064 werner 595
 
1010 werner 596
                BarkBeetleCell *target=0;
597
                if (targeter.current()->isPotentialHost()) {
598
                    // found a potential host at the immediate target pixel
599
                    target = targeter.current();
600
                } else {
601
                    // get elements of the moore-neighborhood
602
                    // and look for a potential host
603
                    targeter.neighbors8(nb8);
1164 werner 604
                    int idx = irandom(0,8);
1010 werner 605
                    for (int j=0;j<8;j++) {
606
                        BarkBeetleCell *nb = nb8[ (idx+j) % 8 ];
607
                        if (nb && nb->isPotentialHost()) {
608
                            target = nb;
609
                            break;
610
                        }
611
                    }
612
                }
613
 
614
                // attack the target pixel if a target could be identified
615
                if (target) {
616
                    target->n++;
1055 werner 617
                    target->packageOutbreakYear += b->outbreakYear;
1010 werner 618
                }
619
            }
620
        }  // while
621
 
622
        // now evaluate whether the landed beetles are able to infest the target trees
623
        runner.reset();
624
        while (BarkBeetleCell *b=runner.next()) {
625
            if (b->n > 0) {
626
                stats.NCohortsLanded+=b->n;
1021 werner 627
                stats.NPixelsLanded++;
1010 werner 628
                // the cell is attacked by n packages. Calculate the probability that the beetles win.
629
                // the probability is derived from an expression with the parameter "tree_stress"
630
                double p_col = limit(mColonizeProbability.calculate(b->tree_stress), 0., 1.);
631
                // the attack happens 'n' times, therefore the probability is higher
632
                double p_ncol = 1. - pow(1.-p_col, b->n);
1013 werner 633
                b->p_colonize = std::max(b->p_colonize, float(p_ncol));
1010 werner 634
                if (drandom() < p_ncol) {
635
                    // attack successful - the pixel gets infested
1081 werner 636
                    b->outbreakYear = b->n>0 ? b->packageOutbreakYear / float(b->n) : mYear; // b->n always >0, but just to silence compiler warning ;)
1010 werner 637
                    b->setInfested(true);
638
                    stats.NInfested++;
1055 werner 639
                } else {
640
                    b->n = 0; // reset the counter
641
                    b->packageOutbreakYear = 0.f;
1010 werner 642
                }
643
 
644
            }
645
        }
1042 werner 646
 
1037 werner 647
    } // for(generations)
1039 werner 648
 
1010 werner 649
}
650
 
1024 werner 651
void BarkBeetleModule::barkbeetleKill()
652
{
653
    int n_killed=0;
1157 werner 654
    double basal_area=0.;
655
    double volume=0.;
1024 werner 656
    for (BarkBeetleRUCell *rucell=mRUGrid.begin(); rucell!=mRUGrid.end(); ++rucell)
657
        if (rucell->killed_trees) {
658
            // there are killed pixels within the resource unit....
659
            QVector<Tree> &tv = GlobalSettings::instance()->model()->RUgrid().constValueAtIndex(mRUGrid.indexOf(rucell))->trees();
660
            for (QVector<Tree>::const_iterator t=tv.constBegin(); t!=tv.constEnd(); ++t) {
661
                if (!t->isDead() && t->dbh()>params.minDbh && t->species()->id()==QStringLiteral("piab")) {
662
                    // check if on killed pixel?
1157 werner 663
                    BarkBeetleCell &bbc = mGrid.valueAt(t->position());
664
                    if (bbc.killed) {
1024 werner 665
                        // yes: kill the tree:
666
                        Tree *tree = const_cast<Tree*>(&(*t));
667
                        n_killed++;
668
                        basal_area+=tree->basalArea();
1157 werner 669
                        volume+=tree->volume();
670
                        bbc.sum_volume_killed += tree->volume();
671
 
672
                        if (!mSimulate) { // remove tree only if not in simulation mode
673
                            tree->setDeathReasonBarkBeetle();
1029 werner 674
                            tree->removeDisturbance(0., 1., // 0% of the stem to soil, 100% to snag (keeps standing)
675
                                                    0., 1.,   // 100% of branches to snags
1202 werner 676
                                                    1.);      // 100% of foliage to soil
1157 werner 677
                        }
1010 werner 678
 
1024 werner 679
                    }
680
                }
681
            }
682
 
683
        }
684
    stats.NTreesKilled = n_killed;
685
    stats.BasalAreaKilled = basal_area;
1157 werner 686
    stats.VolumeKilled = volume;
1024 werner 687
 
1065 werner 688
    // reset the effect of wind-damaged trees and "fangbaueme" -> year begin
689
//    for (BarkBeetleCell *c=mGrid.begin(); c!=mGrid.end(); ++c)
690
//        c->deadtrees = BarkBeetleCell::NoDeadTrees;
1064 werner 691
 
1024 werner 692
}
693
 
694
 
961 werner 695
//*********************************************************************************
696
//************************************ BarkBeetleLayers ***************************
697
//*********************************************************************************
698
 
699
 
700
double BarkBeetleLayers::value(const BarkBeetleCell &data, const int param_index) const
701
{
702
    switch(param_index){
1036 werner 703
    case 0: return data.n; // grid value on pixel
1009 werner 704
    case 1: return data.dbh; // diameter of host
1010 werner 705
    case 2: return data.infested?1.:0.; // infested yes/no
1085 werner 706
    case 3: return data.killed?1.:0.; // pixel has been killed in the (last) year
707
    case 4: if (data.isHost()) { // dead
1036 werner 708
                if (data.infested)
709
                    return data.max_iteration+1; // infested right now (will be dead soon next year)
710
                else
711
                    return data.killedYear; // iteration when killed
712
            }
713
            return -1; // no host
1085 werner 714
    case 5: return data.p_colonize; // probability of kill
715
    case 6: return double(data.deadtrees); // availabilitiy of deadwood (spruce)
716
    case 7: return data.backgroundInfestationProbability;
717
    case 8: return GlobalSettings::instance()->currentYear() - data.outbreakYear;
1157 werner 718
    case 9: return data.n_events; // number of events on a specific pixel
719
    case 10: return data.sum_volume_killed; // total sum of trees killed for a pixel
1007 werner 720
    default: throw IException(QString("invalid variable index for a BarkBeetleCell: %1").arg(param_index));
961 werner 721
    }
722
}
723
 
724
 
1014 werner 725
const QVector<LayeredGridBase::LayerElement> &BarkBeetleLayers::names()
961 werner 726
{
1014 werner 727
    if (mNames.isEmpty())
728
        mNames = QVector<LayeredGridBase::LayerElement>()
729
                << LayeredGridBase::LayerElement(QLatin1Literal("value"), QLatin1Literal("grid value of the pixel"), GridViewRainbow)
730
                << LayeredGridBase::LayerElement(QLatin1Literal("dbh"), QLatin1Literal("diameter of thickest spruce tree on the 10m pixel"), GridViewRainbow)
731
                << LayeredGridBase::LayerElement(QLatin1Literal("infested"), QLatin1Literal("infested pixels (1) are colonized by beetles."), GridViewHeat)
1086 werner 732
                << LayeredGridBase::LayerElement(QLatin1Literal("killed"), QLatin1Literal("1 for pixels that have been killed (0 otherwise) in the current year (last execution of the module)."), GridViewRainbow)
1082 werner 733
                << LayeredGridBase::LayerElement(QLatin1Literal("dead"), QLatin1Literal("iteration at which the treees on the pixel were killed (0: alive, -1: no host trees). \nNewly infested pixels are included (max iteration + 1)."), GridViewRainbow)
1044 werner 734
                << LayeredGridBase::LayerElement(QLatin1Literal("p_killed"), QLatin1Literal("highest probability (within one year) that a pixel is colonized/killed (integrates the number of arriving beetles and the defense state) 0..1"), GridViewHeat)
1066 werner 735
                << LayeredGridBase::LayerElement(QLatin1Literal("deadwood"), QLatin1Literal("10: trees killed by storm, 8: trap trees, 5: active vicinity of 5/8, 0: no dead trees"), GridViewRainbow)
1055 werner 736
                << LayeredGridBase::LayerElement(QLatin1Literal("outbreakProbability"), QLatin1Literal("background infestation probability (p that outbreak starts at each 10m pixel per year) (does not include the interannual climate sensitivity)"), GridViewGray)
1157 werner 737
                << LayeredGridBase::LayerElement(QLatin1Literal("outbreakAge"), QLatin1Literal("age of the outbreak that led to the infestation of the pixel."), GridViewGray)
738
                << LayeredGridBase::LayerElement(QLatin1Literal("nEvents"), QLatin1Literal("number of events (total since start of simulation) that killed trees on a pixel."), GridViewReds)
739
                << LayeredGridBase::LayerElement(QLatin1Literal("sumVolume"), QLatin1Literal("running sum of damages trees (volume, m3)."), GridViewReds);
1014 werner 740
    return mNames;
1008 werner 741
 
961 werner 742
}
962 werner 743
 
744
bool BarkBeetleLayers::onClick(const QPointF &world_coord) const
745
{
746
    qDebug() << "received click" << world_coord;
747
    return true; // handled the click
748
}
1008 werner 749
 
750
 
751
double BarkBeetleRULayers::value(const BarkBeetleRUCell &data, const int index) const
752
{
753
    switch(index){
754
    case 0: return data.generations; // number of generation
755
    default: throw IException(QString("invalid variable index for a BarkBeetleRUCell: %1").arg(index));
756
    }
757
 
758
}
759
 
1014 werner 760
const QVector<LayeredGridBase::LayerElement> &BarkBeetleRULayers::names()
1008 werner 761
{
1014 werner 762
    if (mNames.isEmpty())
763
        mNames = QVector<LayeredGridBase::LayerElement>()
1054 werner 764
                << LayeredGridBase::LayerElement(QLatin1Literal("generations"), QLatin1Literal("total number of bark beetle generations"), GridViewHeat);
1014 werner 765
    return mNames;
1008 werner 766
}
767
 
768
bool BarkBeetleRULayers::onClick(const QPointF &world_coord) const
769
{
770
    qDebug() << "received click" << world_coord;
771
    return true; // handled the click
772
 
773
}
1039 werner 774
 
775
 
1042 werner 776