Subversion Repositories public iLand

Rev

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

Rev Author Line No. Line
1
 
91 Werner 2
#include <QtCore>
3
#include <QtSql>
4
#include "global.h"
393 werner 5
#include "globalsettings.h"
102 Werner 6
#include "xmlhelper.h"
90 Werner 7
#include "speciesset.h"
91 Werner 8
#include "species.h"
387 werner 9
#include "model.h"
10
#include "seeddispersal.h"
393 werner 11
#include "modelsettings.h"
90 Werner 12
 
13
SpeciesSet::SpeciesSet()
14
{
91 Werner 15
    mSetupQuery = 0;
90 Werner 16
}
91 Werner 17
 
18
SpeciesSet::~SpeciesSet()
19
{
20
   clear();
21
}
22
 
23
void SpeciesSet::clear()
24
{
25
    qDeleteAll(mSpecies.values());
387 werner 26
    qDeleteAll(mSeedDispersal);
91 Werner 27
    mSpecies.clear();
179 werner 28
    mActiveSpecies.clear();
91 Werner 29
}
30
 
111 Werner 31
const Species *SpeciesSet::species(const int &index)
32
{
33
    foreach(Species *s, mSpecies)
34
        if (s->index() == index)
35
            return s;
36
    return NULL;
37
}
91 Werner 38
 
39
/** loads active species from a database table and creates/setups the species.
40
    The function uses the global database-connection.
41
  */
102 Werner 42
int SpeciesSet::setup()
91 Werner 43
{
102 Werner 44
    const XmlHelper &xml = GlobalSettings::instance()->settings();
191 werner 45
    QString tableName = xml.value("model.species.source", "species");
318 werner 46
    mName = tableName;
191 werner 47
    QString readerFile = xml.value("model.species.reader", "reader.bin");
102 Werner 48
    readerFile = GlobalSettings::instance()->path(readerFile, "lip");
49
    mReaderStamp.load(readerFile);
50
 
91 Werner 51
    QSqlQuery query(GlobalSettings::instance()->dbin());
52
    mSetupQuery = &query;
53
    QString sql = QString("select * from %1").arg(tableName);
54
    query.exec(sql);
270 werner 55
    if (query.lastError().isValid()){
56
        throw IException(QString("Error loading species set: %1 \n %2").arg(sql, query.lastError().text()) );
57
    }
58
 
91 Werner 59
    clear();
60
    qDebug() << "attempting to load a species set from" << tableName;
61
    while (query.next()) {
62
        if (var("active").toInt()==0)
63
            continue;
64
 
65
        Species *s = new Species(this); // create
99 Werner 66
        // call setup routine (which calls SpeciesSet::var() to retrieve values
91 Werner 67
        s->setup();
68
 
69
        mSpecies.insert(s->id(), s); // store
179 werner 70
        if (s->active())
71
            mActiveSpecies.append(s);
91 Werner 72
    } // while query.next()
73
    qDebug() << "loaded" << mSpecies.count() << "active species:";
74
    qDebug() << mSpecies.keys();
75
 
76
    mSetupQuery = 0;
209 werner 77
 
78
    // setup nitrogen response
79
    XmlHelper resp(xml.node("model.species.nitrogenResponseClasses"));
80
    if (!resp.isValid())
81
        throw IException("model.species.nitrogenResponseClasses not present!");
82
    mNitrogen_1a = resp.valueDouble("class_1_a");
83
    mNitrogen_1b = resp.valueDouble("class_1_b");
84
    mNitrogen_2a = resp.valueDouble("class_2_a");
85
    mNitrogen_2b = resp.valueDouble("class_2_b");
86
    mNitrogen_3a = resp.valueDouble("class_3_a");
87
    mNitrogen_3b = resp.valueDouble("class_3_b");
88
    if (mNitrogen_1a*mNitrogen_1b*mNitrogen_2a*mNitrogen_2b*mNitrogen_3a*mNitrogen_3b == 0)
89
        throw IException("at least one parameter of model.species.nitrogenResponseClasses is not valid (value=0)!");
90
 
91
    // setup CO2 response
92
    XmlHelper co2(xml.node("model.species.CO2Response"));
93
    mCO2base = co2.valueDouble("baseConcentration");
94
    mCO2comp = co2.valueDouble("compensationPoint");
95
    mCO2beta0 = co2.valueDouble("beta0");
96
    mCO2p0 = co2.valueDouble("p0");
97
    if (mCO2base*mCO2comp*(mCO2base-mCO2comp)*mCO2beta0*mCO2p0==0)
98
        throw IException("at least one parameter of model.species.CO2Response is not valid!");
99
 
274 werner 100
    // setup Light responses
101
    XmlHelper light(xml.node("model.species.lightResponse"));
102
    mLightResponseTolerant.setAndParse(light.value("shadeTolerant"));
103
    mLightResponseIntolerant.setAndParse(light.value("shadeIntolerant"));
428 werner 104
    mLightResponseTolerant.linearize(0., 1.);
105
    mLightResponseIntolerant.linearize(0., 1.);
274 werner 106
    if (mLightResponseTolerant.expression().isEmpty() || mLightResponseIntolerant.expression().isEmpty())
107
        throw IException("at least one parameter of model.species.lightResponse is empty!");
425 werner 108
    // lri-correction
109
    mLRICorrection.setAndParse(light.value("LRImodifier","1"));
428 werner 110
    // x: LRI, y: relative heigth
111
    mLRICorrection.linearize2d(0., 1., 0., 1.);
391 werner 112
    return mSpecies.count();
387 werner 113
 
391 werner 114
}
115
 
116
void SpeciesSet::setupRegeneration()
117
{
118
    foreach(Species *s, mActiveSpecies) {
119
        SeedDispersal *sd = new SeedDispersal(s);
120
        sd->setup(); // setup memory for the seed map (grid)
121
        s->setSeedDispersal(sd); // establish the link between species and the map
387 werner 122
    }
391 werner 123
    qDebug() << "Setup of seed dispersal maps finished.";
124
}
91 Werner 125
 
475 werner 126
Species *nc_seed_distribution(Species *species)
127
{
128
    species->setRandomGenerator();
479 werner 129
    species->seedDispersal()->execute();
475 werner 130
    return species;
131
}
391 werner 132
void SpeciesSet::regeneration()
133
{
393 werner 134
    if (!GlobalSettings::instance()->model()->settings().regenerationEnabled)
135
        return;
475 werner 136
    ThreadRunner runner(mActiveSpecies); // initialize a thread runner object with all active species
137
    runner.run(nc_seed_distribution);
391 werner 138
 
475 werner 139
    if (logLevelDebug())
140
        qDebug() << "seed dispersal finished.";
91 Werner 141
}
211 werner 142
 
391 werner 143
/** newYear is called by Model::runYear at the beginning of a year before any growth occurs.
144
  This is used for various initializations, e.g. to clear seed dispersal maps
145
  */
146
void SpeciesSet::newYear()
147
{
393 werner 148
    if (!GlobalSettings::instance()->model()->settings().regenerationEnabled)
149
        return;
391 werner 150
    foreach(Species *s, mActiveSpecies) {
415 werner 151
        s->newYear();
391 werner 152
    }
153
}
211 werner 154
 
91 Werner 155
/** retrieves variables from the datasource available during the setup of species.
156
  */
157
QVariant SpeciesSet::var(const QString& varName)
158
{
94 Werner 159
    Q_ASSERT(mSetupQuery!=0);
91 Werner 160
 
161
    int idx = mSetupQuery->record().indexOf(varName);
162
    if (idx>=0)
163
        return mSetupQuery->value(idx);
125 Werner 164
    throw IException(QString("SpeciesSet: variable not set: %1").arg(varName));
120 Werner 165
    //throw IException(QString("load species parameter: field %1 not found!").arg(varName));
91 Werner 166
    // lookup in defaults
119 Werner 167
    //qDebug() << "variable" << varName << "not found - using default.";
168
    //return GlobalSettings::instance()->settingDefaultValue(varName);
91 Werner 169
}
209 werner 170
 
171
inline double SpeciesSet::nitrogenResponse(const double &availableNitrogen, const double &NA, const double &NB) const
172
{
173
    if (availableNitrogen<=NB)
174
        return 0;
175
    double x = 1. - exp(NA * (availableNitrogen-NB));
176
    return x;
177
}
178
 
179
/// calculate nitrogen response for a given amount of available nitrogen and a respone class
180
/// for fractional values, the response value is interpolated between the fixedly defined classes (1,2,3)
181
double SpeciesSet::nitrogenResponse(const double availableNitrogen, const double &responseClass) const
182
{
183
    double value1, value2, value3;
184
    if (responseClass>2.) {
185
        if (responseClass==3.)
186
            return nitrogenResponse(availableNitrogen, mNitrogen_3a, mNitrogen_3b);
187
        else {
188
            // interpolate between 2 and 3
189
            value2 = nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b);
190
            value3 = nitrogenResponse(availableNitrogen, mNitrogen_3a, mNitrogen_3b);
191
            return value2 + (responseClass-2)*(value3-value2);
192
        }
193
    }
194
    if (responseClass==2)
195
        return nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b);
196
    if (responseClass==1)
197
        return nitrogenResponse(availableNitrogen, mNitrogen_1a, mNitrogen_1b);
198
    // last ressort: interpolate between 1 and 2
199
    value1 = nitrogenResponse(availableNitrogen, mNitrogen_1a, mNitrogen_1b);
200
    value2 = nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b);
201
    return value1 + (responseClass-1)*(value2-value1);
202
}
203
 
204
/** calculation for the CO2 response for the ambientCO2 for the water- and nitrogen responses given.
205
    The calculation follows Friedlingsstein 1995 (see also links to equations in code)
534 werner 206
    see also: http://iland.boku.ac.at/CO2+response
207
    @param ambientCO2 current CO2 concentration (ppm)
208
    @param nitrogenResponse (yearly) nitrogen response of the species
209
    @param soilWaterReponse soil water response (mean value for a month)
209 werner 210
*/
211
double SpeciesSet::co2Response(const double ambientCO2, const double nitrogenResponse, const double soilWaterResponse) const
212
{
210 werner 213
    if (nitrogenResponse==0)
214
        return 0.;
215
 
209 werner 216
    double co2_water = 2. - soilWaterResponse;
217
    double beta = mCO2beta0 * co2_water * nitrogenResponse;
218
 
219
    double r =1. +  M_LN2 * beta; // NPP increase for a doubling of atmospheric CO2 (Eq. 17)
220
 
221
    // fertilization function (cf. Farquhar, 1980) based on Michaelis-Menten expressions
222
    double deltaC = mCO2base - mCO2comp;
223
    double K2 = ((2*mCO2base - mCO2comp) - r*deltaC ) / ((r-1.)*deltaC*(2*mCO2base - mCO2comp)); // Eq. 16
224
    double K1 = (1. + K2*deltaC) / deltaC;
225
 
226
    double response = mCO2p0 * K1*(ambientCO2 - mCO2comp) / (1 + K2*(ambientCO2-mCO2comp)); // Eq. 16
227
    return response;
228
 
229
}
211 werner 230
 
274 werner 231
/** calculates the lightResponse based on a value for LRI and the species lightResponseClass.
232
    LightResponse is classified from 1 (very shade inolerant) and 5 (very shade tolerant) and interpolated for values between 1 and 5.
298 werner 233
    Returns a value between 0..1
234
    @sa http://iland.boku.ac.at/allocation#reserve_and_allocation_to_stem_growth */
470 werner 235
double SpeciesSet::lightResponse(const double lightResourceIndex, const double lightResponseClass) const
274 werner 236
{
237
    double low = mLightResponseIntolerant.calculate(lightResourceIndex);
238
    double high = mLightResponseTolerant.calculate(lightResourceIndex);
239
    double result = low + 0.25*(lightResponseClass-1.)*(high-low);
240
    return limit(result, 0., 1.);
214 werner 241
 
274 werner 242
}
243
 
244
 
245