Rev 210 | Rev 213 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 210 | Rev 211 | ||
---|---|---|---|
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 | #include <QtCore>
|
2 | #include <QtCore>
|
3 | #include <QtSql>
|
3 | #include <QtSql>
|
4 | #include "global.h"
|
4 | #include "global.h"
|
5 | #include "xmlhelper.h"
|
5 | #include "xmlhelper.h"
|
6 | #include "speciesset.h"
|
6 | #include "speciesset.h"
|
7 | #include "species.h"
|
7 | #include "species.h"
|
8 | 8 | ||
9 | SpeciesSet::SpeciesSet() |
9 | SpeciesSet::SpeciesSet() |
10 | {
|
10 | {
|
11 | mSetupQuery = 0; |
11 | mSetupQuery = 0; |
12 | }
|
12 | }
|
13 | 13 | ||
14 | SpeciesSet::~SpeciesSet() |
14 | SpeciesSet::~SpeciesSet() |
15 | {
|
15 | {
|
16 | clear(); |
16 | clear(); |
17 | }
|
17 | }
|
18 | 18 | ||
19 | void SpeciesSet::clear() |
19 | void SpeciesSet::clear() |
20 | {
|
20 | {
|
21 | qDeleteAll(mSpecies.values()); |
21 | qDeleteAll(mSpecies.values()); |
22 | mSpecies.clear(); |
22 | mSpecies.clear(); |
23 | mActiveSpecies.clear(); |
23 | mActiveSpecies.clear(); |
24 | }
|
24 | }
|
25 | 25 | ||
26 | const Species *SpeciesSet::species(const int &index) |
26 | const Species *SpeciesSet::species(const int &index) |
27 | {
|
27 | {
|
28 | foreach(Species *s, mSpecies) |
28 | foreach(Species *s, mSpecies) |
29 | if (s->index() == index) |
29 | if (s->index() == index) |
30 | return s; |
30 | return s; |
31 | return NULL; |
31 | return NULL; |
32 | }
|
32 | }
|
33 | 33 | ||
34 | /** loads active species from a database table and creates/setups the species.
|
34 | /** loads active species from a database table and creates/setups the species.
|
35 | The function uses the global database-connection.
|
35 | The function uses the global database-connection.
|
36 | */
|
36 | */
|
37 | int SpeciesSet::setup() |
37 | int SpeciesSet::setup() |
38 | {
|
38 | {
|
- | 39 | // setup phenology
|
|
- | 40 | setupPhenology(); |
|
- | 41 | ||
39 | const XmlHelper &xml = GlobalSettings::instance()->settings(); |
42 | const XmlHelper &xml = GlobalSettings::instance()->settings(); |
40 | QString tableName = xml.value("model.species.source", "species"); |
43 | QString tableName = xml.value("model.species.source", "species"); |
41 | QString readerFile = xml.value("model.species.reader", "reader.bin"); |
44 | QString readerFile = xml.value("model.species.reader", "reader.bin"); |
42 | readerFile = GlobalSettings::instance()->path(readerFile, "lip"); |
45 | readerFile = GlobalSettings::instance()->path(readerFile, "lip"); |
43 | mReaderStamp.load(readerFile); |
46 | mReaderStamp.load(readerFile); |
44 | 47 | ||
45 | QSqlQuery query(GlobalSettings::instance()->dbin()); |
48 | QSqlQuery query(GlobalSettings::instance()->dbin()); |
46 | mSetupQuery = &query; |
49 | mSetupQuery = &query; |
47 | QString sql = QString("select * from %1").arg(tableName); |
50 | QString sql = QString("select * from %1").arg(tableName); |
48 | query.exec(sql); |
51 | query.exec(sql); |
49 | clear(); |
52 | clear(); |
50 | qDebug() << "attempting to load a species set from" << tableName; |
53 | qDebug() << "attempting to load a species set from" << tableName; |
51 | while (query.next()) { |
54 | while (query.next()) { |
52 | if (var("active").toInt()==0) |
55 | if (var("active").toInt()==0) |
53 | continue; |
56 | continue; |
54 | 57 | ||
55 | Species *s = new Species(this); // create |
58 | Species *s = new Species(this); // create |
56 | // call setup routine (which calls SpeciesSet::var() to retrieve values
|
59 | // call setup routine (which calls SpeciesSet::var() to retrieve values
|
57 | s->setup(); |
60 | s->setup(); |
58 | 61 | ||
59 | mSpecies.insert(s->id(), s); // store |
62 | mSpecies.insert(s->id(), s); // store |
60 | if (s->active()) |
63 | if (s->active()) |
61 | mActiveSpecies.append(s); |
64 | mActiveSpecies.append(s); |
62 | } // while query.next() |
65 | } // while query.next() |
63 | qDebug() << "loaded" << mSpecies.count() << "active species:"; |
66 | qDebug() << "loaded" << mSpecies.count() << "active species:"; |
64 | qDebug() << mSpecies.keys(); |
67 | qDebug() << mSpecies.keys(); |
65 | 68 | ||
66 | mSetupQuery = 0; |
69 | mSetupQuery = 0; |
67 | 70 | ||
68 | // setup nitrogen response
|
71 | // setup nitrogen response
|
69 | XmlHelper resp(xml.node("model.species.nitrogenResponseClasses")); |
72 | XmlHelper resp(xml.node("model.species.nitrogenResponseClasses")); |
70 | if (!resp.isValid()) |
73 | if (!resp.isValid()) |
71 | throw IException("model.species.nitrogenResponseClasses not present!"); |
74 | throw IException("model.species.nitrogenResponseClasses not present!"); |
72 | mNitrogen_1a = resp.valueDouble("class_1_a"); |
75 | mNitrogen_1a = resp.valueDouble("class_1_a"); |
73 | mNitrogen_1b = resp.valueDouble("class_1_b"); |
76 | mNitrogen_1b = resp.valueDouble("class_1_b"); |
74 | mNitrogen_2a = resp.valueDouble("class_2_a"); |
77 | mNitrogen_2a = resp.valueDouble("class_2_a"); |
75 | mNitrogen_2b = resp.valueDouble("class_2_b"); |
78 | mNitrogen_2b = resp.valueDouble("class_2_b"); |
76 | mNitrogen_3a = resp.valueDouble("class_3_a"); |
79 | mNitrogen_3a = resp.valueDouble("class_3_a"); |
77 | mNitrogen_3b = resp.valueDouble("class_3_b"); |
80 | mNitrogen_3b = resp.valueDouble("class_3_b"); |
78 | if (mNitrogen_1a*mNitrogen_1b*mNitrogen_2a*mNitrogen_2b*mNitrogen_3a*mNitrogen_3b == 0) |
81 | if (mNitrogen_1a*mNitrogen_1b*mNitrogen_2a*mNitrogen_2b*mNitrogen_3a*mNitrogen_3b == 0) |
79 | throw IException("at least one parameter of model.species.nitrogenResponseClasses is not valid (value=0)!"); |
82 | throw IException("at least one parameter of model.species.nitrogenResponseClasses is not valid (value=0)!"); |
80 | 83 | ||
81 | // setup CO2 response
|
84 | // setup CO2 response
|
82 | XmlHelper co2(xml.node("model.species.CO2Response")); |
85 | XmlHelper co2(xml.node("model.species.CO2Response")); |
83 | mCO2base = co2.valueDouble("baseConcentration"); |
86 | mCO2base = co2.valueDouble("baseConcentration"); |
84 | mCO2comp = co2.valueDouble("compensationPoint"); |
87 | mCO2comp = co2.valueDouble("compensationPoint"); |
85 | mCO2beta0 = co2.valueDouble("beta0"); |
88 | mCO2beta0 = co2.valueDouble("beta0"); |
86 | mCO2p0 = co2.valueDouble("p0"); |
89 | mCO2p0 = co2.valueDouble("p0"); |
87 | if (mCO2base*mCO2comp*(mCO2base-mCO2comp)*mCO2beta0*mCO2p0==0) |
90 | if (mCO2base*mCO2comp*(mCO2base-mCO2comp)*mCO2beta0*mCO2p0==0) |
88 | throw IException("at least one parameter of model.species.CO2Response is not valid!"); |
91 | throw IException("at least one parameter of model.species.CO2Response is not valid!"); |
89 | 92 | ||
90 | return mSpecies.count(); |
93 | return mSpecies.count(); |
91 | 94 | ||
92 | }
|
95 | }
|
- | 96 | ||
- | 97 | void SpeciesSet::setupPhenology() |
|
- | 98 | {
|
|
- | 99 | mPhenology.clear(); |
|
- | 100 | mPhenology.push_back(Phenology()); // id=0 |
|
- | 101 | XmlHelper xml(GlobalSettings::instance()->settings().node("model.species.phenology")); |
|
- | 102 | int i=0; |
|
- | 103 | do { |
|
- | 104 | QDomElement n = xml.node(QString("type[%1]").arg(i)); |
|
- | 105 | if (n.isNull()) |
|
- | 106 | break; |
|
- | 107 | i++; |
|
- | 108 | int id; |
|
- | 109 | id = n.attribute("id", "-1").toInt(); |
|
- | 110 | if (id<0) throw IException(QString("Error setting up phenology: id invalid\ndump: %1").arg(xml.dump(""))); |
|
- | 111 | xml.setCurrentNode(QString("type[%1]").arg(i)); |
|
- | 112 | Phenology item( id,
|
|
- | 113 | xml.valueDouble(".vpdMin",0.5), // use relative access to node (".x") |
|
- | 114 | xml.valueDouble(".vpdMax", 5), |
|
- | 115 | xml.valueDouble(".minDayLength",10), |
|
- | 116 | xml.valueDouble(".maxDayLength",11), |
|
- | 117 | xml.valueDouble(".tempMin", 2), |
|
- | 118 | xml.valueDouble(".tempMax", 9) ); |
|
- | 119 | ||
- | 120 | mPhenology.push_back(item); |
|
- | 121 | } while(true); |
|
- | 122 | ||
- | 123 | }
|
|
- | 124 | ||
93 | /** retrieves variables from the datasource available during the setup of species.
|
125 | /** retrieves variables from the datasource available during the setup of species.
|
94 | */
|
126 | */
|
95 | QVariant SpeciesSet::var(const QString& varName) |
127 | QVariant SpeciesSet::var(const QString& varName) |
96 | {
|
128 | {
|
97 | Q_ASSERT(mSetupQuery!=0); |
129 | Q_ASSERT(mSetupQuery!=0); |
98 | 130 | ||
99 | int idx = mSetupQuery->record().indexOf(varName); |
131 | int idx = mSetupQuery->record().indexOf(varName); |
100 | if (idx>=0) |
132 | if (idx>=0) |
101 | return mSetupQuery->value(idx); |
133 | return mSetupQuery->value(idx); |
102 | throw IException(QString("SpeciesSet: variable not set: %1").arg(varName)); |
134 | throw IException(QString("SpeciesSet: variable not set: %1").arg(varName)); |
103 | //throw IException(QString("load species parameter: field %1 not found!").arg(varName));
|
135 | //throw IException(QString("load species parameter: field %1 not found!").arg(varName));
|
104 | // lookup in defaults
|
136 | // lookup in defaults
|
105 | //qDebug() << "variable" << varName << "not found - using default.";
|
137 | //qDebug() << "variable" << varName << "not found - using default.";
|
106 | //return GlobalSettings::instance()->settingDefaultValue(varName);
|
138 | //return GlobalSettings::instance()->settingDefaultValue(varName);
|
107 | }
|
139 | }
|
108 | 140 | ||
109 | inline double SpeciesSet::nitrogenResponse(const double &availableNitrogen, const double &NA, const double &NB) const |
141 | inline double SpeciesSet::nitrogenResponse(const double &availableNitrogen, const double &NA, const double &NB) const |
110 | {
|
142 | {
|
111 | if (availableNitrogen<=NB) |
143 | if (availableNitrogen<=NB) |
112 | return 0; |
144 | return 0; |
113 | double x = 1. - exp(NA * (availableNitrogen-NB)); |
145 | double x = 1. - exp(NA * (availableNitrogen-NB)); |
114 | return x; |
146 | return x; |
115 | }
|
147 | }
|
116 | 148 | ||
117 | /// calculate nitrogen response for a given amount of available nitrogen and a respone class
|
149 | /// calculate nitrogen response for a given amount of available nitrogen and a respone class
|
118 | /// for fractional values, the response value is interpolated between the fixedly defined classes (1,2,3)
|
150 | /// for fractional values, the response value is interpolated between the fixedly defined classes (1,2,3)
|
119 | double SpeciesSet::nitrogenResponse(const double availableNitrogen, const double &responseClass) const |
151 | double SpeciesSet::nitrogenResponse(const double availableNitrogen, const double &responseClass) const |
120 | {
|
152 | {
|
121 | double value1, value2, value3; |
153 | double value1, value2, value3; |
122 | if (responseClass>2.) { |
154 | if (responseClass>2.) { |
123 | if (responseClass==3.) |
155 | if (responseClass==3.) |
124 | return nitrogenResponse(availableNitrogen, mNitrogen_3a, mNitrogen_3b); |
156 | return nitrogenResponse(availableNitrogen, mNitrogen_3a, mNitrogen_3b); |
125 | else { |
157 | else { |
126 | // interpolate between 2 and 3
|
158 | // interpolate between 2 and 3
|
127 | value2 = nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b); |
159 | value2 = nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b); |
128 | value3 = nitrogenResponse(availableNitrogen, mNitrogen_3a, mNitrogen_3b); |
160 | value3 = nitrogenResponse(availableNitrogen, mNitrogen_3a, mNitrogen_3b); |
129 | return value2 + (responseClass-2)*(value3-value2); |
161 | return value2 + (responseClass-2)*(value3-value2); |
130 | }
|
162 | }
|
131 | }
|
163 | }
|
132 | if (responseClass==2) |
164 | if (responseClass==2) |
133 | return nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b); |
165 | return nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b); |
134 | if (responseClass==1) |
166 | if (responseClass==1) |
135 | return nitrogenResponse(availableNitrogen, mNitrogen_1a, mNitrogen_1b); |
167 | return nitrogenResponse(availableNitrogen, mNitrogen_1a, mNitrogen_1b); |
136 | // last ressort: interpolate between 1 and 2
|
168 | // last ressort: interpolate between 1 and 2
|
137 | value1 = nitrogenResponse(availableNitrogen, mNitrogen_1a, mNitrogen_1b); |
169 | value1 = nitrogenResponse(availableNitrogen, mNitrogen_1a, mNitrogen_1b); |
138 | value2 = nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b); |
170 | value2 = nitrogenResponse(availableNitrogen, mNitrogen_2a, mNitrogen_2b); |
139 | return value1 + (responseClass-1)*(value2-value1); |
171 | return value1 + (responseClass-1)*(value2-value1); |
140 | }
|
172 | }
|
141 | 173 | ||
142 | /** calculation for the CO2 response for the ambientCO2 for the water- and nitrogen responses given.
|
174 | /** calculation for the CO2 response for the ambientCO2 for the water- and nitrogen responses given.
|
143 | The calculation follows Friedlingsstein 1995 (see also links to equations in code)
|
175 | The calculation follows Friedlingsstein 1995 (see also links to equations in code)
|
144 | */
|
176 | */
|
145 | double SpeciesSet::co2Response(const double ambientCO2, const double nitrogenResponse, const double soilWaterResponse) const |
177 | double SpeciesSet::co2Response(const double ambientCO2, const double nitrogenResponse, const double soilWaterResponse) const |
146 | {
|
178 | {
|
147 | if (nitrogenResponse==0) |
179 | if (nitrogenResponse==0) |
148 | return 0.; |
180 | return 0.; |
149 | 181 | ||
150 | double co2_water = 2. - soilWaterResponse; |
182 | double co2_water = 2. - soilWaterResponse; |
151 | double beta = mCO2beta0 * co2_water * nitrogenResponse; |
183 | double beta = mCO2beta0 * co2_water * nitrogenResponse; |
152 | 184 | ||
153 | double r =1. + M_LN2 * beta; // NPP increase for a doubling of atmospheric CO2 (Eq. 17) |
185 | double r =1. + M_LN2 * beta; // NPP increase for a doubling of atmospheric CO2 (Eq. 17) |
154 | 186 | ||
155 | // fertilization function (cf. Farquhar, 1980) based on Michaelis-Menten expressions
|
187 | // fertilization function (cf. Farquhar, 1980) based on Michaelis-Menten expressions
|
156 | double deltaC = mCO2base - mCO2comp; |
188 | double deltaC = mCO2base - mCO2comp; |
157 | double K2 = ((2*mCO2base - mCO2comp) - r*deltaC ) / ((r-1.)*deltaC*(2*mCO2base - mCO2comp)); // Eq. 16 |
189 | double K2 = ((2*mCO2base - mCO2comp) - r*deltaC ) / ((r-1.)*deltaC*(2*mCO2base - mCO2comp)); // Eq. 16 |
158 | double K1 = (1. + K2*deltaC) / deltaC; |
190 | double K1 = (1. + K2*deltaC) / deltaC; |
159 | 191 | ||
160 | double response = mCO2p0 * K1*(ambientCO2 - mCO2comp) / (1 + K2*(ambientCO2-mCO2comp)); // Eq. 16 |
192 | double response = mCO2p0 * K1*(ambientCO2 - mCO2comp) / (1 + K2*(ambientCO2-mCO2comp)); // Eq. 16 |
161 | return response; |
193 | return response; |
162 | 194 | ||
- | 195 | }
|
|
- | 196 | ||
- | 197 | /** return the phenology of the group... */
|
|
- | 198 | const Phenology &SpeciesSet::phenology(const int phenologyGroup) |
|
- | 199 | {
|
|
- | 200 | const Phenology &p = mPhenology.at(phenologyGroup); |
|
- | 201 | if (p.id() == phenologyGroup) |
|
- | 202 | return p; |
|
- | 203 | // search...
|
|
- | 204 | for (int i=0;i<mPhenology.count();i++) |
|
- | 205 | if (mPhenology[i].id()==phenologyGroup) |
|
- | 206 | return mPhenology[i]; |
|
- | 207 | throw IException(QString("Error at SpeciesSEt::phenology(): invalid group: %1").arg(phenologyGroup)); |
|
163 | }
|
208 | }
|
164 | 209 |