Subversion Repositories public iLand

Rev

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

Rev Author Line No. Line
1
 
671 werner 2
/********************************************************************************************
3
**    iLand - an individual based forest landscape and disturbance model
4
**    http://iland.boku.ac.at
5
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
6
**
7
**    This program is free software: you can redistribute it and/or modify
8
**    it under the terms of the GNU General Public License as published by
9
**    the Free Software Foundation, either version 3 of the License, or
10
**    (at your option) any later version.
11
**
12
**    This program is distributed in the hope that it will be useful,
13
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
**    GNU General Public License for more details.
16
**
17
**    You should have received a copy of the GNU General Public License
18
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
********************************************************************************************/
20
 
534 werner 21
/** @class ResourceUnit
22
  ResourceUnit is the spatial unit that encapsulates a forest stand and links to several environmental components
23
  (Climate, Soil, Water, ...).
697 werner 24
  @ingroup core
25
  A resource unit has a size of (currently) 100x100m. Many processes in iLand operate on the level of a ResourceUnit.
26
  Each resource unit has the same Climate and other properties (e.g. available nitrogen).
27
  Proceses on this level are, inter alia, NPP Production (see Production3PG), water calculations (WaterCycle), the modeling
28
  of dead trees (Snag) and soil processes (Soil).
534 werner 29
 
30
  */
31
#include <QtCore>
32
#include "global.h"
33
 
34
#include "resourceunit.h"
35
#include "resourceunitspecies.h"
36
#include "speciesset.h"
37
#include "species.h"
38
#include "production3pg.h"
39
#include "model.h"
40
#include "climate.h"
41
#include "watercycle.h"
42
#include "snag.h"
43
#include "soil.h"
44
#include "helper.h"
45
 
46
ResourceUnit::~ResourceUnit()
47
{
48
    if (mWater)
49
        delete mWater;
50
    mWater = 0;
51
    if (mSnag)
52
        delete mSnag;
53
    if (mSoil)
54
        delete mSoil;
55
 
738 werner 56
    qDeleteAll(mRUSpecies);
57
 
1159 werner 58
    if (mSaplings)
59
        delete[] mSaplings;
60
 
534 werner 61
    mSnag = 0;
62
    mSoil = 0;
1159 werner 63
    mSaplings = 0;
534 werner 64
}
65
 
66
ResourceUnit::ResourceUnit(const int index)
67
{
68
    qDeleteAll(mRUSpecies);
69
    mSpeciesSet = 0;
70
    mClimate = 0;
71
    mPixelCount=0;
72
    mStockedArea = 0;
73
    mStockedPixelCount = 0;
1157 werner 74
    mStockableArea = 0;
1024 werner 75
    mAggregatedWLA = 0.;
76
    mAggregatedLA = 0.;
77
    mAggregatedLR = 0.;
78
    mEffectiveArea = 0.;
79
    mLRI_modification = 0.;
534 werner 80
    mIndex = index;
81
    mSaplingHeightMap = 0;
82
    mEffectiveArea_perWLA = 0.;
83
    mWater = new WaterCycle();
84
    mSnag = 0;
85
    mSoil = 0;
1159 werner 86
    mSaplings = 0;
569 werner 87
    mID = 0;
534 werner 88
}
89
 
90
void ResourceUnit::setup()
91
{
92
    mWater->setup(this);
93
 
94
    if (mSnag)
95
        delete mSnag;
96
    mSnag=0;
97
    if (mSoil)
98
        delete mSoil;
99
    mSoil=0;
100
    if (Model::settings().carbonCycleEnabled) {
591 werner 101
        mSoil = new Soil(this);
534 werner 102
        mSnag = new Snag;
103
        mSnag->setup(this);
104
        const XmlHelper &xml=GlobalSettings::instance()->settings();
105
 
106
        // setup contents of the soil of the RU; use values for C and N (kg/ha)
107
        mSoil->setInitialState(CNPool(xml.valueDouble("model.site.youngLabileC", -1),
108
                                      xml.valueDouble("model.site.youngLabileN", -1),
109
                                      xml.valueDouble("model.site.youngLabileDecompRate", -1)),
110
                               CNPool(xml.valueDouble("model.site.youngRefractoryC", -1),
111
                                      xml.valueDouble("model.site.youngRefractoryN", -1),
112
                                      xml.valueDouble("model.site.youngRefractoryDecompRate", -1)),
113
                               CNPair(xml.valueDouble("model.site.somC", -1), xml.valueDouble("model.site.somN", -1)));
114
    }
115
 
1159 werner 116
    if (mSaplings)
117
        delete mSaplings;
118
    if (Model::settings().regenerationEnabled) {
119
        mSaplings = new SaplingCell[cPxPerHectare];
120
    }
121
 
534 werner 122
    // setup variables
123
    mUnitVariables.nitrogenAvailable = GlobalSettings::instance()->settings().valueDouble("model.site.availableNitrogen", 40);
124
 
895 werner 125
    // if dynamic coupling of soil nitrogen is enabled, a starting value for available N is calculated
534 werner 126
    if (mSoil && Model::settings().useDynamicAvailableNitrogen && Model::settings().carbonCycleEnabled) {
127
        mSoil->setClimateFactor(1.);
128
        mSoil->calculateYear();
895 werner 129
        mUnitVariables.nitrogenAvailable = soil()->availableNitrogen();
534 werner 130
    }
664 werner 131
    mHasDeadTrees = false;
534 werner 132
    mAverageAging = 0.;
133
 
134
}
135
void ResourceUnit::setBoundingBox(const QRectF &bb)
136
{
137
    mBoundingBox = bb;
1118 werner 138
    mCornerOffset = GlobalSettings::instance()->model()->grid()->indexAt(bb.topLeft());
534 werner 139
}
140
 
141
/// set species and setup the species-per-RU-data
142
void ResourceUnit::setSpeciesSet(SpeciesSet *set)
143
{
144
    mSpeciesSet = set;
145
    qDeleteAll(mRUSpecies);
146
 
147
    //mRUSpecies.resize(set->count()); // ensure that the vector space is not relocated
148
    for (int i=0;i<set->count();i++) {
149
        Species *s = const_cast<Species*>(mSpeciesSet->species(i));
150
        if (!s)
151
            throw IException("ResourceUnit::setSpeciesSet: invalid index!");
152
 
153
        ResourceUnitSpecies *rus = new ResourceUnitSpecies();
154
        mRUSpecies.push_back(rus);
155
        rus->setup(s, this);
156
        /* be careful: setup() is called with a pointer somewhere to the content of the mRUSpecies container.
157
           If the container memory is relocated (QVector), the pointer gets invalid!!!
158
           Therefore, a resize() is called before the loop (no resize()-operations during the loop)! */
159
        //mRUSpecies[i].setup(s,this); // setup this element
160
 
161
    }
162
}
163
 
164
ResourceUnitSpecies &ResourceUnit::resourceUnitSpecies(const Species *species)
165
{
166
    return *mRUSpecies[species->index()];
167
}
168
 
1040 werner 169
const ResourceUnitSpecies *ResourceUnit::constResourceUnitSpecies(const Species *species) const
170
{
171
    return mRUSpecies[species->index()];
172
}
173
 
534 werner 174
Tree &ResourceUnit::newTree()
175
{
176
    // start simple: just append to the vector...
177
    if (mTrees.isEmpty())
178
        mTrees.reserve(100); // reserve a junk of memory for trees
179
 
180
    mTrees.append(Tree());
181
    return mTrees.back();
182
}
183
int ResourceUnit::newTreeIndex()
184
{
734 werner 185
    newTree();
186
    return mTrees.count()-1; // return index of the last tree
534 werner 187
}
188
 
189
/// remove dead trees from tree list
190
/// reduce size of vector if lots of space is free
191
/// tests showed that this way of cleanup is very fast,
192
/// because no memory allocations are performed (simple memmove())
193
/// when trees are moved.
194
void ResourceUnit::cleanTreeList()
195
{
664 werner 196
    if (!mHasDeadTrees)
197
        return;
198
 
534 werner 199
    QVector<Tree>::iterator last=mTrees.end()-1;
200
    QVector<Tree>::iterator current = mTrees.begin();
201
    while (last>=current && (*last).isDead())
202
        --last;
203
 
204
    while (current<last) {
205
        if ((*current).isDead()) {
206
            *current = *last; // copy data!
207
            --last; //
208
            while (last>=current && (*last).isDead())
209
                --last;
210
        }
211
        ++current;
212
    }
213
    ++last; // last points now to the first dead tree
214
 
215
    // free ressources
216
    if (last!=mTrees.end()) {
217
        mTrees.erase(last, mTrees.end());
218
        if (mTrees.capacity()>100) {
219
            if (mTrees.count() / double(mTrees.capacity()) < 0.2) {
220
                //int target_size = mTrees.count()*2;
221
                //qDebug() << "reduce size from "<<mTrees.capacity() << "to" << target_size;
222
                //mTrees.reserve(qMax(target_size, 100));
664 werner 223
                if (logLevelDebug())
224
                    qDebug() << "reduce tree storage of RU" << index() << " from " << mTrees.capacity() << "to" << mTrees.count();
534 werner 225
                mTrees.squeeze();
226
            }
227
        }
228
    }
664 werner 229
    mHasDeadTrees = false; // reset flag
534 werner 230
}
231
 
232
void ResourceUnit::newYear()
233
{
234
    mAggregatedWLA = 0.;
235
    mAggregatedLA = 0.;
236
    mAggregatedLR = 0.;
237
    mEffectiveArea = 0.;
238
    mPixelCount = mStockedPixelCount = 0;
239
    snagNewYear();
609 werner 240
    if (mSoil)
241
        mSoil->newYear();
534 werner 242
    // clear statistics global and per species...
243
    QList<ResourceUnitSpecies*>::const_iterator i;
244
    QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd();
245
    mStatistics.clear();
246
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
247
        (*i)->statisticsDead().clear();
248
        (*i)->statisticsMgmt().clear();
662 werner 249
        (*i)->changeSapling().newYear();
534 werner 250
    }
251
 
252
}
253
 
254
/** production() is the "stand-level" part of the biomass production (3PG).
255
    - The amount of radiation intercepted by the stand is calculated
256
    - the water cycle is calculated
257
    - statistics for each species are cleared
258
    - The 3PG production for each species and ressource unit is called (calculates species-responses and NPP production)
259
    see also: http://iland.boku.ac.at/individual+tree+light+availability */
260
void ResourceUnit::production()
261
{
262
 
1107 werner 263
    if (mAggregatedWLA==0. || mPixelCount==0) {
936 werner 264
        // clear statistics of resourceunitspecies
265
        for ( QList<ResourceUnitSpecies*>::const_iterator i=mRUSpecies.constBegin(); i!=mRUSpecies.constEnd(); ++i)
266
            (*i)->statistics().clear();
267
        mEffectiveArea = 0.;
268
        mStockedArea = 0.;
534 werner 269
        return;
270
    }
271
 
272
    // the pixel counters are filled during the height-grid-calculations
273
    mStockedArea = 100. * mStockedPixelCount; // m2 (1 height grid pixel = 10x10m)
1107 werner 274
    if (leafAreaIndex()<3.) {
275
        // estimate stocked area based on crown projections
276
        double crown_area = 0.;
277
        for (int i=0;i<mTrees.count();++i)
278
            crown_area += mTrees.at(i).isDead() ? 0. : mTrees.at(i).stamp()->reader()->crownArea();
534 werner 279
 
1157 werner 280
        if (logLevelDebug())
281
            qDebug() << "crown area: lai" << leafAreaIndex() << "stocked area (pixels)" << mStockedArea << " area (crown)" << crown_area;
282
        if (leafAreaIndex()<1.) {
283
            mStockedArea = std::min(crown_area, mStockedArea);
1107 werner 284
        } else {
285
 
1157 werner 286
            double px_frac = (leafAreaIndex()-1.)/2.; // 0 at LAI=1, 1 at LAI=3
287
            mStockedArea = mStockedArea * px_frac + std::min(crown_area, mStockedArea) * (1. - px_frac);
1107 werner 288
        }
289
        if (mStockedArea==0.)
290
            return;
291
    }
292
 
534 werner 293
    // calculate the leaf area index (LAI)
294
    double LAI = mAggregatedLA / mStockedArea;
295
    // calculate the intercepted radiation fraction using the law of Beer Lambert
296
    const double k = Model::settings().lightExtinctionCoefficient;
297
    double interception_fraction = 1. - exp(-k * LAI);
298
    mEffectiveArea = mStockedArea * interception_fraction; // m2
299
 
300
    // calculate the total weighted leaf area on this RU:
301
    mLRI_modification = interception_fraction *  mStockedArea / mAggregatedWLA; // p_WLA
302
    if (mLRI_modification == 0.)
303
        qDebug() << "lri modifaction==0!";
304
 
611 werner 305
    if (logLevelDebug()) {
534 werner 306
    DBGMODE(qDebug() << QString("production: LAI: %1 (intercepted fraction: %2, stocked area: %4). LRI-Multiplier: %3")
307
            .arg(LAI)
308
            .arg(interception_fraction)
309
            .arg(mLRI_modification)
310
            .arg(mStockedArea);
311
    );
611 werner 312
    }
534 werner 313
 
314
    // calculate LAI fractions
315
    QList<ResourceUnitSpecies*>::const_iterator i;
316
    QList<ResourceUnitSpecies*>::const_iterator iend = mRUSpecies.constEnd();
317
    double ru_lai = leafAreaIndex();
318
    if (ru_lai < 1.)
319
        ru_lai = 1.;
320
    // note: LAIFactors are only 1 if sum of LAI is > 1. (see WaterCycle)
321
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
720 werner 322
        double lai_factor = (*i)->statistics().leafAreaIndex() / ru_lai;
1157 werner 323
 
324
        //DBGMODE(
325
        if (lai_factor > 1.) {
326
                        const ResourceUnitSpecies* rus=*i;
327
                        qDebug() << "LAI factor > 1: species ru-index:" << rus->species()->name() << rus->ru()->index();
328
                    }
329
        //);
720 werner 330
        (*i)->setLAIfactor( lai_factor );
534 werner 331
    }
332
 
333
    // soil water model - this determines soil water contents needed for response calculations
334
    {
335
    mWater->run();
336
    }
337
 
338
    // invoke species specific calculation (3PG)
339
    for (i=mRUSpecies.constBegin(); i!=iend; ++i) {
1157 werner 340
        //DBGMODE(
341
        if ((*i)->LAIfactor() > 1.) {
342
                    const ResourceUnitSpecies* rus=*i;
343
                    qDebug() << "LAI factor > 1: species ru-index value:" << rus->species()->name() << rus->ru()->index() << rus->LAIfactor();
344
                    }
345
        //);
534 werner 346
        (*i)->calculate(); // CALCULATE 3PG
347
        if (logLevelInfo() &&  (*i)->LAIfactor()>0)
348
            qDebug() << "ru" << mIndex << "species" << (*i)->species()->id() << "LAIfraction" << (*i)->LAIfactor() << "raw_gpp_m2"
349
                     << (*i)->prod3PG().GPPperArea() << "area:" << productiveArea() << "gpp:"
350
                     << productiveArea()*(*i)->prod3PG().GPPperArea()
351
                     << "aging(lastyear):" << averageAging() << "f_env,yr:" << (*i)->prod3PG().fEnvYear();
352
    }
353
}
354
 
355
void ResourceUnit::calculateInterceptedArea()
356
{
357
    if (mAggregatedLR==0) {
358
        mEffectiveArea_perWLA = 0.;
359
        return;
360
    }
361
    Q_ASSERT(mAggregatedLR>0.);
362
    mEffectiveArea_perWLA = mEffectiveArea / mAggregatedLR;
363
    if (logLevelDebug()) qDebug() << "RU: aggregated lightresponse:" << mAggregatedLR  << "eff.area./wla:" << mEffectiveArea_perWLA;
364
}
365
 
366
// function is called immediately before the growth of individuals
367
void ResourceUnit::beforeGrow()
368
{
369
    mAverageAging = 0.;
370
}
371
 
372
// function is called after finishing the indivdual growth / mortality.
373
void ResourceUnit::afterGrow()
374
{
375
    mAverageAging = leafArea()>0.?mAverageAging/leafArea():0; // calculate aging value (calls to addAverageAging() by individual trees)
376
    if (mAverageAging>0. && mAverageAging<0.00001)
377
        qDebug() << "ru" << mIndex << "aging <0.00001";
378
    if (mAverageAging<0. || mAverageAging>1.)
379
        qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex();
380
}
381
 
382
void ResourceUnit::yearEnd()
383
{
384
    // calculate statistics for all tree species of the ressource unit
385
    int c = mRUSpecies.count();
386
    for (int i=0;i<c; i++) {
387
        mRUSpecies[i]->statisticsDead().calculate(); // calculate the dead trees
388
        mRUSpecies[i]->statisticsMgmt().calculate(); // stats of removed trees
389
        mRUSpecies[i]->updateGWL(); // get sum of dead trees (died + removed)
390
        mRUSpecies[i]->statistics().calculate(); // calculate the living (and add removed volume to gwl)
391
        mStatistics.add(mRUSpecies[i]->statistics());
392
    }
393
    mStatistics.calculate(); // aggreagte on stand level
394
 
1157 werner 395
    // update carbon flows
396
    if (soil() && GlobalSettings::instance()->model()->settings().carbonCycleEnabled) {
397
        double area_factor = stockableArea() / cRUArea; //conversion factor
398
        mUnitVariables.carbonUptake = statistics().npp() * biomassCFraction;
399
        mUnitVariables.carbonUptake += statistics().nppSaplings() * biomassCFraction;
400
 
401
        double to_atm = snag()->fluxToAtmosphere().C / area_factor; // from snags, kgC/ha
402
        to_atm += soil()->fluxToAtmosphere().C *cRUArea/10.; // soil: t/ha -> t/m2 -> kg/ha
403
        mUnitVariables.carbonToAtm = to_atm;
404
 
405
        double to_dist = snag()->fluxToDisturbance().C / area_factor;
406
        to_dist += soil()->fluxToDisturbance().C * cRUArea/10.;
407
        double to_harvest = snag()->fluxToExtern().C / area_factor;
408
 
409
        mUnitVariables.NEP = mUnitVariables.carbonUptake - to_atm - to_dist - to_harvest; // kgC/ha
410
 
411
        // incremental values....
412
        mUnitVariables.cumCarbonUptake += mUnitVariables.carbonUptake;
413
        mUnitVariables.cumCarbonToAtm += mUnitVariables.carbonToAtm;
414
        mUnitVariables.cumNEP += mUnitVariables.NEP;
415
 
416
    }
417
 
534 werner 418
}
419
 
420
void ResourceUnit::addTreeAgingForAllTrees()
421
{
422
    mAverageAging = 0.;
423
    foreach(const Tree &t, mTrees) {
424
        addTreeAging(t.leafArea(), t.species()->aging(t.height(), t.age()));
425
    }
426
 
427
}
428
 
429
/// refresh of tree based statistics.
430
/// WARNING: this function is only called once (during startup).
431
/// see function "yearEnd()" above!!!
432
void ResourceUnit::createStandStatistics()
433
{
434
    // clear statistics (ru-level and ru-species level)
435
    mStatistics.clear();
436
    for (int i=0;i<mRUSpecies.count();i++) {
437
        mRUSpecies[i]->statistics().clear();
438
        mRUSpecies[i]->statisticsDead().clear();
439
        mRUSpecies[i]->statisticsMgmt().clear();
440
    }
441
 
442
    // add all trees to the statistics objects of the species
443
    foreach(const Tree &t, mTrees) {
444
        if (!t.isDead())
445
            resourceUnitSpecies(t.species()).statistics().add(&t, 0);
446
    }
447
    // summarize statistics for the whole resource unit
448
    for (int i=0;i<mRUSpecies.count();i++) {
449
        mRUSpecies[i]->statistics().calculate();
450
        mStatistics.add(mRUSpecies[i]->statistics());
451
    }
452
    mStatistics.calculate();
575 werner 453
    mAverageAging = mStatistics.leafAreaIndex()>0.?mAverageAging / (mStatistics.leafAreaIndex()*stockableArea()):0.;
534 werner 454
    if (mAverageAging<0. || mAverageAging>1.)
455
        qDebug() << "Average aging invalid: (RU, LAI):" << index() << mStatistics.leafAreaIndex();
456
}
457
 
720 werner 458
/** recreate statistics. This is necessary after events that changed the structure
459
    of the stand *after* the growth of trees (where stand statistics are updated).
460
    An example is after disturbances.  */
1157 werner 461
void ResourceUnit::recreateStandStatistics(bool recalculate_stats)
720 werner 462
{
463
    for (int i=0;i<mRUSpecies.count();i++) {
464
        mRUSpecies[i]->statistics().clear();
465
    }
466
    foreach(const Tree &t, mTrees) {
467
        resourceUnitSpecies(t.species()).statistics().add(&t, 0);
468
    }
1157 werner 469
 
470
    if (recalculate_stats) {
471
        for (int i=0;i<mRUSpecies.count();i++) {
472
            mRUSpecies[i]->statistics().calculate();
473
        }
937 werner 474
    }
720 werner 475
}
476
 
824 werner 477
void ResourceUnit::setSaplingHeightMap(float *map_pointer)
478
{
479
    // set to zero
480
    mSaplingHeightMap=map_pointer;
481
    if (!mSaplingHeightMap)
482
        return;
483
    for (int i=0;i<cPxPerRU*cPxPerRU;i++)
484
        mSaplingHeightMap[i]=0.f;
485
    // flag all trees in the resource unit
486
    for (QVector<Tree>::const_iterator i=mTrees.cbegin(); i!= mTrees.cend(); ++i) {
487
        setMaxSaplingHeightAt(i->positionIndex(), 10.f);
488
    }
489
}
490
 
534 werner 491
void ResourceUnit::setMaxSaplingHeightAt(const QPoint &position, const float height)
492
{
493
    Q_ASSERT(mSaplingHeightMap);
1118 werner 494
    int pixel_index = cPxPerRU*(position.x()-mCornerOffset.x())+(position.y()-mCornerOffset.y());
534 werner 495
    if (pixel_index<0 || pixel_index>=cPxPerRU*cPxPerRU) {
1118 werner 496
        qDebug() << "setSaplingHeightAt-Error for position" << position << "for RU at" << boundingBox() << "with corner" << mCornerOffset;
534 werner 497
    } else {
498
        if (mSaplingHeightMap[pixel_index]<height)
499
            mSaplingHeightMap[pixel_index]=height;
500
    }
501
}
502
 
503
/// clear all saplings of all species on a given position (after recruitment)
504
void ResourceUnit::clearSaplings(const QPoint &position)
505
{
506
    foreach(ResourceUnitSpecies* rus, mRUSpecies)
507
        rus->clearSaplings(position);
508
 
509
}
510
 
662 werner 511
/// kill all saplings within a given rect
512
void ResourceUnit::clearSaplings(const QRectF pixel_rect, const bool remove_from_soil)
513
{
514
    foreach(ResourceUnitSpecies* rus, mRUSpecies) {
515
        rus->changeSapling().clearSaplings(pixel_rect, remove_from_soil);
516
    }
517
 
518
}
519
 
1157 werner 520
double ResourceUnit::saplingHeightForInit(const QPoint &position) const
600 werner 521
{
522
    double maxh = 0.;
523
    foreach(ResourceUnitSpecies* rus, mRUSpecies)
524
        maxh = qMax(maxh, rus->sapling().heightAt(position));
525
    return maxh;
526
}
534 werner 527
 
528
void ResourceUnit::calculateCarbonCycle()
529
{
530
    if (!snag())
531
        return;
532
 
533
    // (1) calculate the snag dynamics
534
    // because all carbon/nitrogen-flows from trees to the soil are routed through the snag-layer,
535
    // all soil inputs (litter + deadwood) are collected in the Snag-object.
536
    snag()->calculateYear();
537
    soil()->setClimateFactor( snag()->climateFactor() ); // the climate factor is only calculated once
538
    soil()->setSoilInput( snag()->labileFlux(), snag()->refractoryFlux());
539
    soil()->calculateYear(); // update the ICBM/2N model
540
    // use available nitrogen?
541
    if (Model::settings().useDynamicAvailableNitrogen)
542
        mUnitVariables.nitrogenAvailable = soil()->availableNitrogen();
543
 
544
    // debug output
545
    if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dCarbonCycle) && !snag()->isEmpty()) {
546
        DebugList &out = GlobalSettings::instance()->debugList(index(), GlobalSettings::dCarbonCycle);
605 werner 547
        out << index() << id(); // resource unit index and id
534 werner 548
        out << snag()->debugList(); // snag debug outs
549
        out << soil()->debugList(); // ICBM/2N debug outs
550
    }
551
 
552
}
600 werner 553
 
554