Subversion Repositories public iLand

Rev

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

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