Subversion Repositories public iLand

Rev

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

Rev Author Line No. Line
1
 
671 werner 2
/********************************************************************************************
3
**    iLand - an individual based forest landscape and disturbance model
4
**    http://iland.boku.ac.at
5
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
6
**
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
9
**    the Free Software Foundation, either version 3 of the License, or
10
**    (at your option) any later version.
11
**
12
**    This program is distributed in the hope that it will be useful,
13
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
**    GNU General Public License for more details.
16
**
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/>.
19
********************************************************************************************/
20
 
237 werner 21
#include "global.h"
22
#include "watercycle.h"
23
#include "climate.h"
24
#include "resourceunit.h"
25
#include "species.h"
239 werner 26
#include "model.h"
808 werner 27
#include "debugtimer.h"
646 werner 28
#include "modules.h"
237 werner 29
 
697 werner 30
/** @class WaterCycle
31
  @ingroup core
32
  simulates the water cycle on a ResourceUnit.
33
  The WaterCycle is simulated with a daily time step on the spatial level of a ResourceUnit. Related are
34
  the snow module (SnowPack), and Canopy module that simulates the interception (and evaporation) of precipitation and the
35
  transpiration from the canopy.
36
  The WaterCycle covers the "soil water bucket". Main entry function is run().
37
 
38
  See http://iland.boku.ac.at/water+cycle
39
  */
40
 
237 werner 41
WaterCycle::WaterCycle()
42
{
266 werner 43
    mSoilDepth = 0;
496 werner 44
    mLastYear = -1;
237 werner 45
}
46
 
239 werner 47
void WaterCycle::setup(const ResourceUnit *ru)
237 werner 48
{
49
    mRU = ru;
50
    // get values...
266 werner 51
    mFieldCapacity = 0.; // on top
281 werner 52
    const XmlHelper &xml=GlobalSettings::instance()->settings();
53
    mSoilDepth = xml.valueDouble("model.site.soilDepth", 0.) * 10; // convert from cm to mm
338 werner 54
    double pct_sand = xml.valueDouble("model.site.pctSand");
55
    double pct_silt = xml.valueDouble("model.site.pctSilt");
56
    double pct_clay = xml.valueDouble("model.site.pctClay");
572 werner 57
    if (fabs(100. - (pct_sand + pct_silt + pct_clay)) > 0.01)
338 werner 58
        throw IException(QString("Setup Watercycle: soil composition percentages do not sum up to 100. Sand: %1, Silt: %2 Clay: %3").arg(pct_sand).arg(pct_silt).arg(pct_clay));
59
 
60
    // calculate soil characteristics based on empirical functions (Schwalm & Ek, 2004)
61
    // note: the variables are percentages [0..100]
802 werner 62
    mPsi_sat = -exp((1.54 - 0.0095*pct_sand + 0.0063*pct_silt) * log(10)) * 0.000098; // Eq. 83
338 werner 63
    mPsi_koeff_b = -( 3.1 + 0.157*pct_clay - 0.003*pct_sand );  // Eq. 84
802 werner 64
    mTheta_sat = 0.01 * (50.5 - 0.142*pct_sand - 0.037*pct_clay); // Eq. 78
240 werner 65
    mCanopy.setup();
339 werner 66
 
67
    mPermanentWiltingPoint = heightFromPsi(-4000); // maximum psi is set to a constant of -4MPa
379 werner 68
    if (xml.valueBool("model.settings.waterUseSoilSaturation",false)==false) {
69
        mFieldCapacity = heightFromPsi(-15);
70
    } else {
71
        // =-EXP((1.54-0.0095* pctSand +0.0063* pctSilt)*LN(10))*0.000098
72
        double psi_sat = -exp((1.54-0.0095 * pct_sand + 0.0063*pct_silt)*log(10.))*0.000098;
73
        mFieldCapacity = heightFromPsi(psi_sat);
431 werner 74
        if (logLevelDebug()) qDebug() << "psi: saturation " << psi_sat << "field capacity:" << mFieldCapacity;
379 werner 75
    }
76
 
266 werner 77
    mContent = mFieldCapacity; // start with full water content (in the middle of winter)
802 werner 78
    if (logLevelDebug()) qDebug() << "setup of water: Psi_sat (kPa)" << mPsi_sat << "Theta_sat" << mTheta_sat << "coeff. b" << mPsi_koeff_b;
566 werner 79
    mCanopyConductance = 0.;
496 werner 80
    mLastYear = -1;
621 werner 81
 
82
    // canopy settings
83
    mCanopy.mNeedleFactor = xml.valueDouble("model.settings.interceptionStorageNeedle", 4.);
84
    mCanopy.mDecidousFactor = xml.valueDouble("model.settings.interceptionStorageBroadleaf", 2.);
85
    mSnowPack.mSnowTemperature = xml.valueDouble("model.settings.snowMeltTemperature", 0.);
1157 werner 86
 
87
    mTotalET = mTotalExcess = mSnowRad = 0.;
88
    mSnowDays = 0;
237 werner 89
}
90
 
331 werner 91
/** function to calculate the water pressure [saugspannung] for a given amount of water.
338 werner 92
    returns water potential in kPa.
93
  see http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance */
266 werner 94
inline double WaterCycle::psiFromHeight(const double mm) const
95
{
96
    // psi_x = psi_ref * ( rho_x / rho_ref) ^ b
97
    if (mm<0.001)
98
        return -100000000;
802 werner 99
    double psi_x = mPsi_sat * pow((mm / mSoilDepth / mTheta_sat),mPsi_koeff_b);
338 werner 100
    return psi_x; // pis
266 werner 101
}
102
 
331 werner 103
/// calculate the height of the water column for a given pressure
338 werner 104
/// return water amount in mm
105
/// see http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance
266 werner 106
inline double WaterCycle::heightFromPsi(const double psi_kpa) const
107
{
108
    // rho_x = rho_ref * (psi_x / psi_ref)^(1/b)
802 werner 109
    double h = mSoilDepth * mTheta_sat * pow(psi_kpa / mPsi_sat, 1./mPsi_koeff_b);
266 werner 110
    return h;
111
}
112
 
331 werner 113
/// get canopy characteristics of the resource unit.
114
/// It is important, that species-statistics are valid when this function is called (LAI)!
237 werner 115
void WaterCycle::getStandValues()
116
{
246 werner 117
    mLAINeedle=mLAIBroadleaved=0.;
237 werner 118
    mCanopyConductance=0.;
553 werner 119
    const double ground_vegetationCC = 0.02;
237 werner 120
    double lai;
720 werner 121
    foreach(const ResourceUnitSpecies *rus, mRU->ruSpecies()) {
455 werner 122
        lai = rus->constStatistics().leafAreaIndex();
123
        if (rus->species()->isConiferous())
237 werner 124
            mLAINeedle+=lai;
125
        else
126
            mLAIBroadleaved+=lai;
455 werner 127
        mCanopyConductance += rus->species()->canopyConductance() * lai; // weigh with LAI
237 werner 128
    }
129
    double total_lai = mLAIBroadleaved+mLAINeedle;
502 werner 130
 
131
    // handle cases with LAI < 1 (use generic "ground cover characteristics" instead)
723 werner 132
    /* The LAI used here is derived from the "stockable" area (and not the stocked area).
133
       If the stand has gaps, the available trees are "thinned" across the whole area. Otherwise (when stocked area is used)
134
       the LAI would overestimate the transpiring canopy. However, the current solution overestimates e.g. the interception.
135
       If the "thinned out" LAI is below one, the rest (i.e. the gaps) are thought to be covered by ground vegetation.
136
    */
502 werner 137
    if (total_lai<1.) {
553 werner 138
        mCanopyConductance+=(ground_vegetationCC)*(1. - total_lai);
502 werner 139
        total_lai = 1.;
237 werner 140
    }
502 werner 141
    mCanopyConductance /= total_lai;
142
 
368 werner 143
    if (total_lai < Model::settings().laiThresholdForClosedStands) {
144
        // following Landsberg and Waring: when LAI is < 3 (default for laiThresholdForClosedStands), a linear "ramp" from 0 to 3 is assumed
299 werner 145
        // http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance
368 werner 146
        mCanopyConductance *= total_lai / Model::settings().laiThresholdForClosedStands;
237 werner 147
    }
431 werner 148
    if (logLevelInfo()) qDebug() << "WaterCycle:getStandValues: LAI needle" << mLAINeedle << "LAI Broadl:"<< mLAIBroadleaved << "weighted avg. Conductance (m/2):" << mCanopyConductance;
237 werner 149
}
150
 
502 werner 151
/// calculate responses for ground vegetation, i.e. for "unstocked" areas.
152
/// this duplicates calculations done in Species.
153
/// @return Minimum of vpd and soilwater response for default
154
inline double WaterCycle::calculateBaseSoilAtmosphereResponse(const double psi_kpa, const double vpd_kpa)
155
{
156
    // constant parameters used for ground vegetation:
503 werner 157
    const double mPsiMin = 1.5; // MPa
502 werner 158
    const double mRespVpdExponent = -0.6;
159
    // see SpeciesResponse::soilAtmosphereResponses()
160
    double water_resp;
161
    // see Species::soilwaterResponse:
162
    const double psi_mpa = psi_kpa / 1000.; // convert to MPa
163
    water_resp = limit( 1. - psi_mpa / mPsiMin, 0., 1.);
164
    // see species::vpdResponse
165
 
166
    double vpd_resp;
167
    vpd_resp =  exp(mRespVpdExponent * vpd_kpa);
168
    return qMin(water_resp, vpd_resp);
169
}
170
 
367 werner 171
/// calculate combined VPD and soilwaterresponse for all species
172
/// on the RU. This is used for the calc. of the transpiration.
173
inline double WaterCycle::calculateSoilAtmosphereResponse(const double psi_kpa, const double vpd_kpa)
174
{
175
    double min_response;
502 werner 176
    double total_response = 0; // LAI weighted minimum response for all speices on the RU
177
    double total_lai_factor = 0.;
720 werner 178
    foreach(const ResourceUnitSpecies *rus, mRU->ruSpecies()) {
179
        if (rus->LAIfactor()>0.) {
502 werner 180
            // retrieve the minimum of VPD / soil water response for that species
455 werner 181
            rus->speciesResponse()->soilAtmosphereResponses(psi_kpa, vpd_kpa, min_response);
182
            total_response += min_response * rus->LAIfactor();
502 werner 183
            total_lai_factor += rus->LAIfactor();
367 werner 184
        }
185
    }
455 werner 186
 
502 werner 187
    if (total_lai_factor<1.) {
188
        // the LAI is below 1: the rest is considered as "ground vegetation"
189
        total_response += calculateBaseSoilAtmosphereResponse(psi_kpa, vpd_kpa) * (1. - total_lai_factor);
190
    }
191
 
377 werner 192
    // add an aging factor to the total response (averageAging: leaf area weighted mean aging value):
193
    // conceptually: response = min(vpd_response, water_response)*aging
503 werner 194
    if (total_lai_factor==1.)
195
        total_response *= mRU->averageAging(); // no ground cover: use aging value for all LA
196
    else if (total_lai_factor>0. && mRU->averageAging()>0.)
197
        total_response *= (1.-total_lai_factor)*1. + (total_lai_factor * mRU->averageAging()); // between 0..1: a part of the LAI is "ground cover" (aging=1)
502 werner 198
 
720 werner 199
    DBGMODE(
200
          if (mRU->averageAging()>1. || mRU->averageAging()<0. || total_response<0 || total_response>1.)
201
             qDebug() << "water cycle: average aging invalid. aging:" << mRU->averageAging() << "total response" << total_response << "total lai factor:" << total_lai_factor;
484 werner 202
    );
482 werner 203
 
204
    //DBG_IF(mRU->averageAging()>1. || mRU->averageAging()<0.,"water cycle", "average aging invalid!" );
367 werner 205
    return total_response;
206
}
207
 
208
 
237 werner 209
/// Main Water Cycle function. This function triggers all water related tasks for
210
/// one simulation year.
299 werner 211
/// @sa http://iland.boku.ac.at/water+cycle
237 werner 212
void WaterCycle::run()
213
{
496 werner 214
    // necessary?
215
    if (GlobalSettings::instance()->currentYear() == mLastYear)
216
        return;
626 werner 217
    DebugTimer tw("water:run");
646 werner 218
    WaterCycleData add_data;
626 werner 219
 
237 werner 220
    // preparations (once a year)
367 werner 221
    getStandValues(); // fetch canopy characteristics from iLand (including weighted average for mCanopyConductance)
237 werner 222
    mCanopy.setStandParameters(mLAINeedle,
223
                               mLAIBroadleaved,
224
                               mCanopyConductance);
225
 
226
    // main loop over all days of the year
239 werner 227
    double prec_mm, prec_after_interception, prec_to_soil, et, excess;
338 werner 228
    const Climate *climate = mRU->climate();
229
    const ClimateDay *day = climate->begin();
230
    const ClimateDay *end = climate->end();
237 werner 231
    int doy=0;
1157 werner 232
    mTotalExcess = 0.;
233
    mTotalET = 0.;
234
    mSnowRad = 0.;
235
    mSnowDays = 0;
237 werner 236
    for (; day<end; ++day, ++doy) {
237
        // (1) precipitation of the day
238
        prec_mm = day->preciptitation;
239
        // (2) interception by the crown
777 werner 240
        prec_after_interception = mCanopy.flow(prec_mm);
237 werner 241
        // (3) storage in the snow pack
242
        prec_to_soil = mSnowPack.flow(prec_after_interception, day->temperature);
646 werner 243
        // save extra data (used by e.g. fire module)
244
        add_data.water_to_ground[doy] = prec_to_soil;
245
        add_data.snow_cover[doy] = mSnowPack.snowPack();
1157 werner 246
        if (mSnowPack.snowPack()>0.) {
247
            mSnowRad += day->radiation;
248
            mSnowDays++;
249
        }
250
 
237 werner 251
        // (4) add rest to soil
252
        mContent += prec_to_soil;
266 werner 253
 
254
        excess = 0.;
255
        if (mContent>mFieldCapacity) {
256
            // excess water runoff
257
            excess = mContent - mFieldCapacity;
1157 werner 258
            mTotalExcess += excess;
266 werner 259
            mContent = mFieldCapacity;
260
        }
261
 
367 werner 262
        double current_psi = psiFromHeight(mContent);
263
        mPsi[doy] = current_psi;
553 werner 264
 
335 werner 265
        // (5) transpiration of the vegetation (and of water intercepted in canopy)
367 werner 266
        // calculate the LAI-weighted response values for soil water and vpd:
680 werner 267
        double interception_before_transpiration = mCanopy.interception();
367 werner 268
        double combined_response = calculateSoilAtmosphereResponse( current_psi, day->vpd);
269
        et = mCanopy.evapotranspiration3PG(day, climate->daylength_h(doy), combined_response);
680 werner 270
        // if there is some flow from intercepted water to the ground -> add to "water_to_the_ground"
271
        if (mCanopy.interception() < interception_before_transpiration)
272
            add_data.water_to_ground[doy]+= interception_before_transpiration - mCanopy.interception();
238 werner 273
 
241 werner 274
        mContent -= et; // reduce content (transpiration)
338 werner 275
        // add intercepted water (that is *not* evaporated) again to the soil (or add to snow if temp too low -> call to snowpack)
276
        mContent += mSnowPack.add(mCanopy.interception(),day->temperature);
241 werner 277
 
338 werner 278
        // do not remove water below the PWP (fixed value)
266 werner 279
        if (mContent<mPermanentWiltingPoint) {
271 werner 280
            et -= mPermanentWiltingPoint - mContent; // reduce et (for bookkeeping)
266 werner 281
            mContent = mPermanentWiltingPoint;
237 werner 282
        }
266 werner 283
 
1157 werner 284
        mTotalET += et;
546 werner 285
 
271 werner 286
        //DBGMODE(
239 werner 287
            if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dWaterCycle)) {
240 werner 288
                DebugList &out = GlobalSettings::instance()->debugList(day->id(), GlobalSettings::dWaterCycle);
239 werner 289
                // climatic variables
605 werner 290
                out << day->id() << mRU->index() << mRU->id() << day->temperature << day->vpd << day->preciptitation << day->radiation;
368 werner 291
                out << combined_response; // combined response of all species on RU (min(water, vpd))
239 werner 292
                // fluxes
271 werner 293
                out << prec_after_interception << prec_to_soil << et << mCanopy.evaporationCanopy()
540 werner 294
                        << mContent << mPsi[doy] << excess;
239 werner 295
                // other states
296
                out << mSnowPack.snowPack();
364 werner 297
                //special sanity check:
298
                if (prec_to_soil>0. && mCanopy.interception()>0.)
299
                    if (mSnowPack.snowPack()==0. && day->preciptitation==0)
300
                        qDebug() << "watercontent increase without precipititaion";
239 werner 301
 
302
            }
271 werner 303
        //); // DBGMODE()
239 werner 304
 
237 werner 305
    }
646 werner 306
    // call external modules
307
    GlobalSettings::instance()->model()->modules()->calculateWater(mRU, &add_data);
496 werner 308
    mLastYear = GlobalSettings::instance()->currentYear();
309
 
237 werner 310
}
311
 
312
 
313
namespace Water {
314
 
315
/** calculates the input/output of water that is stored in the snow pack.
316
  The approach is similar to Picus 1.3 and ForestBGC (Running, 1988).
317
  Returns the amount of water that exits the snowpack (precipitation, snow melt) */
318
double SnowPack::flow(const double &preciptitation_mm, const double &temperature)
319
{
621 werner 320
    if (temperature>mSnowTemperature) {
237 werner 321
        if (mSnowPack==0.)
322
            return preciptitation_mm; // no change
323
        else {
324
            // snow melts
945 werner 325
            const double melting_coefficient = 0.7; // mm/C
621 werner 326
            double melt = qMin( (temperature-mSnowTemperature) * melting_coefficient, mSnowPack);
240 werner 327
            mSnowPack -=melt;
237 werner 328
            return preciptitation_mm + melt;
329
        }
330
    } else {
331
        // snow:
332
        mSnowPack += preciptitation_mm;
333
        return 0.; // no output.
334
    }
335
 
336
}
337
 
338
 
334 werner 339
inline double SnowPack::add(const double &preciptitation_mm, const double &temperature)
340
{
945 werner 341
    // do nothing for temps > 0 C
621 werner 342
    if (temperature>mSnowTemperature)
334 werner 343
        return preciptitation_mm;
344
 
945 werner 345
    // temps < 0 C: add to snow
334 werner 346
    mSnowPack += preciptitation_mm;
347
    return 0.;
348
}
349
 
237 werner 350
/** Interception in crown canopy.
351
    Calculates the amount of preciptitation that does not reach the ground and
352
    is stored in the canopy. The approach is adopted from Picus 1.3.
299 werner 353
    Returns the amount of precipitation (mm) that surpasses the canopy layer.
354
    @sa http://iland.boku.ac.at/water+cycle#precipitation_and_interception */
777 werner 355
double Canopy::flow(const double &preciptitation_mm)
237 werner 356
{
357
    // sanity checks
358
    mInterception = 0.;
271 werner 359
    mEvaporation = 0.;
237 werner 360
    if (!mLAI)
361
        return preciptitation_mm;
362
    if (!preciptitation_mm)
363
        return 0.;
364
    double max_interception_mm=0.; // maximum interception based on the current foliage
945 werner 365
    double max_storage_mm=0.; // maximum storage in canopy (current LAI)
366
    double max_storage_potentital = 0.; // storage capacity at very high LAI
237 werner 367
 
368
    if (mLAINeedle>0.) {
369
        // (1) calculate maximum fraction of thru-flow the crown (based on precipitation)
370
        double max_flow_needle = 0.9 * sqrt(1.03 - exp(-0.055*preciptitation_mm));
371
        max_interception_mm += preciptitation_mm *  (1. - max_flow_needle * mLAINeedle/mLAI);
372
        // (2) calculate maximum storage potential based on the current LAI
945 werner 373
        //     by weighing the needle/decidious storage capacity
374
        max_storage_potentital += mNeedleFactor * mLAINeedle/mLAI;
237 werner 375
    }
376
 
377
    if (mLAIBroadleaved>0.) {
378
        // (1) calculate maximum fraction of thru-flow the crown (based on precipitation)
299 werner 379
        double max_flow_broad = 0.9 * pow(1.22 - exp(-0.055*preciptitation_mm), 0.35);
946 werner 380
        max_interception_mm += preciptitation_mm *  (1. - max_flow_broad) * mLAIBroadleaved/mLAI;
237 werner 381
        // (2) calculate maximum storage potential based on the current LAI
945 werner 382
        max_storage_potentital += mDecidousFactor * mLAIBroadleaved/mLAI;
237 werner 383
    }
384
 
945 werner 385
    // the extent to which the maximum stoarge capacity is exploited, depends on LAI:
386
    max_storage_mm = max_storage_potentital * (1. - exp(-0.5 * mLAI));
387
 
237 werner 388
    // (3) calculate actual interception and store for evaporation calculation
389
    mInterception = qMin( max_storage_mm, max_interception_mm );
390
 
335 werner 391
    // (4) limit interception with amount of precipitation
945 werner 392
    mInterception = qMin( mInterception, preciptitation_mm );
335 werner 393
 
394
    // (5) reduce preciptitaion by the amount that is intercepted by the canopy
237 werner 395
    return preciptitation_mm - mInterception;
396
 
397
}
398
 
239 werner 399
/// sets up the canopy. fetch some global parameter values...
400
void Canopy::setup()
237 werner 401
{
240 werner 402
    mAirDensity = Model::settings().airDensity; // kg / m3
237 werner 403
}
404
 
405
void Canopy::setStandParameters(const double LAIneedle, const double LAIbroadleave, const double maxCanopyConductance)
406
{
407
    mLAINeedle = LAIneedle;
408
    mLAIBroadleaved=LAIbroadleave;
409
    mLAI=LAIneedle+LAIbroadleave;
239 werner 410
    mAvgMaxCanopyConductance = maxCanopyConductance;
553 werner 411
 
412
    // clear aggregation containers
562 werner 413
    for (int i=0;i<12;++i) mET0[i]=0.;
553 werner 414
 
237 werner 415
}
416
 
417
 
418
 
256 werner 419
/** calculate the daily evaporation/transpiration using the Penman-Monteith-Equation.
420
   This version is based on 3PG. See the Visual Basic Code in 3PGjs.xls.
421
   Returns the total sum of evaporation+transpiration in mm of the day. */
367 werner 422
double Canopy::evapotranspiration3PG(const ClimateDay *climate, const double daylength_h, const double combined_response)
256 werner 423
{
424
    double vpd_mbar = climate->vpd * 10.; // convert from kPa to mbar
945 werner 425
    double temperature = climate->temperature; // average temperature of the day (degree C)
256 werner 426
    double daylength = daylength_h * 3600.; // daylength in seconds (convert from length in hours)
427
    double rad = climate->radiation / daylength * 1000000; //convert from MJ/m2 (day sum) to average radiation flow W/m2 [MJ=MWs -> /s * 1,000,000
428
 
561 werner 429
    // the radiation: based on linear empirical function
430
    const double qa = -90.;
431
    const double qb = 0.8;
432
    double net_rad = qa + qb*rad;
433
 
256 werner 434
    //: Landsberg original: const double e20 = 2.2;  //rate of change of saturated VP with T at 20C
965 werner 435
    const double VPDconv = 0.000622; //convert VPD to saturation deficit = 18/29/1000 = molecular weight of H2O/molecular weight of air
256 werner 436
    const double latent_heat = 2460000.; // Latent heat of vaporization. Energy required per unit mass of water vaporized [J kg-1]
437
 
368 werner 438
    double gBL  = Model::settings().boundaryLayerConductance; // boundary layer conductance
439
 
440
    // canopy conductance.
441
    // The species traits are weighted by LAI on the RU.
442
    // maximum canopy conductance: see getStandValues()
443
    // current response: see calculateSoilAtmosphereResponse(). This is basically a weighted average of min(water_response, vpd_response) for each species
367 werner 444
    double gC = mAvgMaxCanopyConductance * combined_response;
256 werner 445
 
367 werner 446
 
256 werner 447
    double defTerm = mAirDensity * latent_heat * (vpd_mbar * VPDconv) * gBL;
448
 
561 werner 449
    //  with temperature-dependent  slope of  vapor pressure saturation curve
450
    // (following  Allen et al. (1998),  http://www.fao.org/docrep/x0490e/x0490e07.htm#atmospheric%20parameters)
451
    // svp_slope in mbar.
621 werner 452
    //double svp_slope = 4098. * (6.1078 * exp(17.269 * temperature / (temperature + 237.3))) / ((237.3+temperature)*(237.3+temperature));
561 werner 453
 
621 werner 454
    // alternatively: very simple variant (following here the original 3PG code). This
455
    // keeps yields +- same results for summer, but slightly lower values in winter (2011/03/16)
456
    double svp_slope = 2.2;
457
 
256 werner 458
    double div = (1. + svp_slope + gBL / gC);
561 werner 459
    double Etransp = (svp_slope * net_rad + defTerm) / div;
335 werner 460
    double canopy_transpiration = Etransp / latent_heat * daylength;
256 werner 461
 
562 werner 462
    // calculate reference evapotranspiration
463
    // see Adair et al 2008
464
    const double psychrometric_const = 0.0672718682328237; // kPa/degC
465
    const double windspeed = 2.; // m/s
466
    double net_rad_mj_day = net_rad*daylength/1000000.; // convert W/m2 again to MJ/m2*day
467
    double et0_day = 0.408*svp_slope*net_rad_mj_day  + psychrometric_const*900./(temperature+273.)*windspeed*climate->vpd;
468
    double et0_div = svp_slope+psychrometric_const*(1.+0.34*windspeed);
469
    et0_day = et0_day / et0_div;
470
    mET0[climate->month-1] += et0_day;
553 werner 471
 
256 werner 472
    if (mInterception>0.) {
473
        // we assume that for evaporation from leaf surface gBL/gC -> 0
562 werner 474
        double div_evap = 1. + svp_slope;
620 werner 475
        double evap_canopy_potential = (svp_slope*net_rad + defTerm) / div_evap / latent_heat * daylength;
476
        // reduce the amount of transpiration on a wet day based on the approach of
477
        // Wigmosta et al (1994). see http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance
478
 
479
        double ratio_T_E = canopy_transpiration / evap_canopy_potential;
480
        double evap_canopy = qMin(evap_canopy_potential, mInterception);
481
 
482
        // for interception -> 0, the canopy transpiration is unchanged
483
        canopy_transpiration = (evap_canopy_potential - evap_canopy) * ratio_T_E;
484
 
562 werner 485
        mInterception -= evap_canopy; // reduce interception
486
        mEvaporation = evap_canopy; // evaporation from intercepted water
620 werner 487
 
256 werner 488
    }
335 werner 489
    return canopy_transpiration;
256 werner 490
}
491
 
237 werner 492
} // end namespace