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 |