Subversion Repositories public iLand

Rev

Rev 707 | Rev 779 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

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