Subversion Repositories public iLand

Rev

Rev 680 | Rev 720 | 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"
626 werner 27
#include "helper.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]
62
    mPsi_ref = -exp((1.54 - 0.0095*pct_sand + 0.0063*pct_silt) * log(10)) * 0.000098; // Eq. 83
63
    mPsi_koeff_b = -( 3.1 + 0.157*pct_clay - 0.003*pct_sand );  // Eq. 84
64
    mRho_ref = 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)
431 werner 78
    if (logLevelDebug()) qDebug() << "setup of water: Psi_ref (kPa)" << mPsi_ref << "Rho_ref" << mRho_ref << "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.);
237 werner 86
}
87
 
331 werner 88
/** function to calculate the water pressure [saugspannung] for a given amount of water.
338 werner 89
    returns water potential in kPa.
90
  see http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance */
266 werner 91
inline double WaterCycle::psiFromHeight(const double mm) const
92
{
93
    // psi_x = psi_ref * ( rho_x / rho_ref) ^ b
94
    if (mm<0.001)
95
        return -100000000;
96
    double psi_x = mPsi_ref * pow((mm / mSoilDepth / mRho_ref),mPsi_koeff_b);
338 werner 97
    return psi_x; // pis
266 werner 98
}
99
 
331 werner 100
/// calculate the height of the water column for a given pressure
338 werner 101
/// return water amount in mm
102
/// see http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance
266 werner 103
inline double WaterCycle::heightFromPsi(const double psi_kpa) const
104
{
105
    // rho_x = rho_ref * (psi_x / psi_ref)^(1/b)
106
    double h = mSoilDepth * mRho_ref * pow(psi_kpa / mPsi_ref, 1./mPsi_koeff_b);
107
    return h;
108
}
109
 
331 werner 110
/// get canopy characteristics of the resource unit.
111
/// It is important, that species-statistics are valid when this function is called (LAI)!
237 werner 112
void WaterCycle::getStandValues()
113
{
246 werner 114
    mLAINeedle=mLAIBroadleaved=0.;
237 werner 115
    mCanopyConductance=0.;
553 werner 116
    const double ground_vegetationCC = 0.02;
237 werner 117
    double lai;
455 werner 118
    foreach(ResourceUnitSpecies *rus, mRU->ruSpecies()) {
119
        lai = rus->constStatistics().leafAreaIndex();
120
        if (rus->species()->isConiferous())
237 werner 121
            mLAINeedle+=lai;
122
        else
123
            mLAIBroadleaved+=lai;
455 werner 124
        mCanopyConductance += rus->species()->canopyConductance() * lai; // weigh with LAI
237 werner 125
    }
126
    double total_lai = mLAIBroadleaved+mLAINeedle;
502 werner 127
 
128
    // handle cases with LAI < 1 (use generic "ground cover characteristics" instead)
129
    if (total_lai<1.) {
553 werner 130
        mCanopyConductance+=(ground_vegetationCC)*(1. - total_lai);
502 werner 131
        total_lai = 1.;
237 werner 132
    }
502 werner 133
    mCanopyConductance /= total_lai;
134
 
368 werner 135
    if (total_lai < Model::settings().laiThresholdForClosedStands) {
136
        // following Landsberg and Waring: when LAI is < 3 (default for laiThresholdForClosedStands), a linear "ramp" from 0 to 3 is assumed
299 werner 137
        // http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance
368 werner 138
        mCanopyConductance *= total_lai / Model::settings().laiThresholdForClosedStands;
237 werner 139
    }
431 werner 140
    if (logLevelInfo()) qDebug() << "WaterCycle:getStandValues: LAI needle" << mLAINeedle << "LAI Broadl:"<< mLAIBroadleaved << "weighted avg. Conductance (m/2):" << mCanopyConductance;
237 werner 141
}
142
 
502 werner 143
/// calculate responses for ground vegetation, i.e. for "unstocked" areas.
144
/// this duplicates calculations done in Species.
145
/// @return Minimum of vpd and soilwater response for default
146
inline double WaterCycle::calculateBaseSoilAtmosphereResponse(const double psi_kpa, const double vpd_kpa)
147
{
148
    // constant parameters used for ground vegetation:
503 werner 149
    const double mPsiMin = 1.5; // MPa
502 werner 150
    const double mRespVpdExponent = -0.6;
151
    // see SpeciesResponse::soilAtmosphereResponses()
152
    double water_resp;
153
    // see Species::soilwaterResponse:
154
    const double psi_mpa = psi_kpa / 1000.; // convert to MPa
155
    water_resp = limit( 1. - psi_mpa / mPsiMin, 0., 1.);
156
    // see species::vpdResponse
157
 
158
    double vpd_resp;
159
    vpd_resp =  exp(mRespVpdExponent * vpd_kpa);
160
    return qMin(water_resp, vpd_resp);
161
}
162
 
367 werner 163
/// calculate combined VPD and soilwaterresponse for all species
164
/// on the RU. This is used for the calc. of the transpiration.
165
inline double WaterCycle::calculateSoilAtmosphereResponse(const double psi_kpa, const double vpd_kpa)
166
{
167
    double min_response;
502 werner 168
    double total_response = 0; // LAI weighted minimum response for all speices on the RU
169
    double total_lai_factor = 0.;
455 werner 170
    foreach(ResourceUnitSpecies *rus, mRU->ruSpecies()) {
171
        if (rus->LAIfactor()>0) {
502 werner 172
            // retrieve the minimum of VPD / soil water response for that species
455 werner 173
            rus->speciesResponse()->soilAtmosphereResponses(psi_kpa, vpd_kpa, min_response);
174
            total_response += min_response * rus->LAIfactor();
502 werner 175
            total_lai_factor += rus->LAIfactor();
367 werner 176
        }
177
    }
455 werner 178
 
502 werner 179
    if (total_lai_factor<1.) {
180
        // the LAI is below 1: the rest is considered as "ground vegetation"
181
        total_response += calculateBaseSoilAtmosphereResponse(psi_kpa, vpd_kpa) * (1. - total_lai_factor);
182
    }
183
 
377 werner 184
    // add an aging factor to the total response (averageAging: leaf area weighted mean aging value):
185
    // conceptually: response = min(vpd_response, water_response)*aging
503 werner 186
    if (total_lai_factor==1.)
187
        total_response *= mRU->averageAging(); // no ground cover: use aging value for all LA
188
    else if (total_lai_factor>0. && mRU->averageAging()>0.)
189
        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 190
 
504 werner 191
    DBGMODE( if (mRU->averageAging()>1. || mRU->averageAging()<0. || total_response<0 || total_response>1.)
192
        qDebug() << "water cycle: average aging invalid. aging:" << mRU->averageAging() << "total response" << total_response;
484 werner 193
    );
482 werner 194
 
195
    //DBG_IF(mRU->averageAging()>1. || mRU->averageAging()<0.,"water cycle", "average aging invalid!" );
367 werner 196
    return total_response;
197
}
198
 
199
 
237 werner 200
/// Main Water Cycle function. This function triggers all water related tasks for
201
/// one simulation year.
299 werner 202
/// @sa http://iland.boku.ac.at/water+cycle
237 werner 203
void WaterCycle::run()
204
{
496 werner 205
    // necessary?
206
    if (GlobalSettings::instance()->currentYear() == mLastYear)
207
        return;
626 werner 208
    DebugTimer tw("water:run");
646 werner 209
    WaterCycleData add_data;
626 werner 210
 
237 werner 211
    // preparations (once a year)
367 werner 212
    getStandValues(); // fetch canopy characteristics from iLand (including weighted average for mCanopyConductance)
237 werner 213
    mCanopy.setStandParameters(mLAINeedle,
214
                               mLAIBroadleaved,
215
                               mCanopyConductance);
216
 
217
    // main loop over all days of the year
239 werner 218
    double prec_mm, prec_after_interception, prec_to_soil, et, excess;
338 werner 219
    const Climate *climate = mRU->climate();
220
    const ClimateDay *day = climate->begin();
221
    const ClimateDay *end = climate->end();
237 werner 222
    int doy=0;
223
    double total_excess = 0.;
224
    for (; day<end; ++day, ++doy) {
225
        // (1) precipitation of the day
226
        prec_mm = day->preciptitation;
227
        // (2) interception by the crown
228
        prec_after_interception = mCanopy.flow(prec_mm, day->temperature);
229
        // (3) storage in the snow pack
230
        prec_to_soil = mSnowPack.flow(prec_after_interception, day->temperature);
646 werner 231
        // save extra data (used by e.g. fire module)
232
        add_data.water_to_ground[doy] = prec_to_soil;
233
        add_data.snow_cover[doy] = mSnowPack.snowPack();
237 werner 234
        // (4) add rest to soil
235
        mContent += prec_to_soil;
266 werner 236
 
237
        excess = 0.;
238
        if (mContent>mFieldCapacity) {
239
            // excess water runoff
240
            excess = mContent - mFieldCapacity;
241
            total_excess += excess;
242
            mContent = mFieldCapacity;
243
        }
244
 
367 werner 245
        double current_psi = psiFromHeight(mContent);
246
        mPsi[doy] = current_psi;
553 werner 247
 
335 werner 248
        // (5) transpiration of the vegetation (and of water intercepted in canopy)
367 werner 249
        // calculate the LAI-weighted response values for soil water and vpd:
680 werner 250
        double interception_before_transpiration = mCanopy.interception();
367 werner 251
        double combined_response = calculateSoilAtmosphereResponse( current_psi, day->vpd);
252
        et = mCanopy.evapotranspiration3PG(day, climate->daylength_h(doy), combined_response);
680 werner 253
        // if there is some flow from intercepted water to the ground -> add to "water_to_the_ground"
254
        if (mCanopy.interception() < interception_before_transpiration)
255
            add_data.water_to_ground[doy]+= interception_before_transpiration - mCanopy.interception();
238 werner 256
 
241 werner 257
        mContent -= et; // reduce content (transpiration)
338 werner 258
        // add intercepted water (that is *not* evaporated) again to the soil (or add to snow if temp too low -> call to snowpack)
259
        mContent += mSnowPack.add(mCanopy.interception(),day->temperature);
241 werner 260
 
338 werner 261
        // do not remove water below the PWP (fixed value)
266 werner 262
        if (mContent<mPermanentWiltingPoint) {
271 werner 263
            et -= mPermanentWiltingPoint - mContent; // reduce et (for bookkeeping)
266 werner 264
            mContent = mPermanentWiltingPoint;
237 werner 265
        }
266 werner 266
 
546 werner 267
 
271 werner 268
        //DBGMODE(
239 werner 269
            if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dWaterCycle)) {
240 werner 270
                DebugList &out = GlobalSettings::instance()->debugList(day->id(), GlobalSettings::dWaterCycle);
239 werner 271
                // climatic variables
605 werner 272
                out << day->id() << mRU->index() << mRU->id() << day->temperature << day->vpd << day->preciptitation << day->radiation;
368 werner 273
                out << combined_response; // combined response of all species on RU (min(water, vpd))
239 werner 274
                // fluxes
271 werner 275
                out << prec_after_interception << prec_to_soil << et << mCanopy.evaporationCanopy()
540 werner 276
                        << mContent << mPsi[doy] << excess;
239 werner 277
                // other states
278
                out << mSnowPack.snowPack();
364 werner 279
                //special sanity check:
280
                if (prec_to_soil>0. && mCanopy.interception()>0.)
281
                    if (mSnowPack.snowPack()==0. && day->preciptitation==0)
282
                        qDebug() << "watercontent increase without precipititaion";
239 werner 283
 
284
            }
271 werner 285
        //); // DBGMODE()
239 werner 286
 
237 werner 287
    }
646 werner 288
    // call external modules
289
    GlobalSettings::instance()->model()->modules()->calculateWater(mRU, &add_data);
496 werner 290
    mLastYear = GlobalSettings::instance()->currentYear();
291
 
237 werner 292
}
293
 
294
 
295
namespace Water {
296
 
297
/** calculates the input/output of water that is stored in the snow pack.
298
  The approach is similar to Picus 1.3 and ForestBGC (Running, 1988).
299
  Returns the amount of water that exits the snowpack (precipitation, snow melt) */
300
double SnowPack::flow(const double &preciptitation_mm, const double &temperature)
301
{
621 werner 302
    if (temperature>mSnowTemperature) {
237 werner 303
        if (mSnowPack==0.)
304
            return preciptitation_mm; // no change
305
        else {
306
            // snow melts
307
            const double melting_coefficient = 0.7; // mm/°C
621 werner 308
            double melt = qMin( (temperature-mSnowTemperature) * melting_coefficient, mSnowPack);
240 werner 309
            mSnowPack -=melt;
237 werner 310
            return preciptitation_mm + melt;
311
        }
312
    } else {
313
        // snow:
314
        mSnowPack += preciptitation_mm;
315
        return 0.; // no output.
316
    }
317
 
318
}
319
 
320
 
334 werner 321
inline double SnowPack::add(const double &preciptitation_mm, const double &temperature)
322
{
323
    // do nothing for temps > 0°
621 werner 324
    if (temperature>mSnowTemperature)
334 werner 325
        return preciptitation_mm;
326
 
327
    // temps < 0°: add to snow
328
    mSnowPack += preciptitation_mm;
329
    return 0.;
330
}
331
 
237 werner 332
/** Interception in crown canopy.
333
    Calculates the amount of preciptitation that does not reach the ground and
334
    is stored in the canopy. The approach is adopted from Picus 1.3.
299 werner 335
    Returns the amount of precipitation (mm) that surpasses the canopy layer.
336
    @sa http://iland.boku.ac.at/water+cycle#precipitation_and_interception */
237 werner 337
double Canopy::flow(const double &preciptitation_mm, const double &temperature)
338
{
339
    // sanity checks
340
    mInterception = 0.;
271 werner 341
    mEvaporation = 0.;
237 werner 342
    if (!mLAI)
343
        return preciptitation_mm;
344
    if (!preciptitation_mm)
345
        return 0.;
346
    double max_interception_mm=0.; // maximum interception based on the current foliage
347
    double max_storage_mm=0.; // maximum storage in canopy
348
 
349
    if (mLAINeedle>0.) {
350
        // (1) calculate maximum fraction of thru-flow the crown (based on precipitation)
351
        double max_flow_needle = 0.9 * sqrt(1.03 - exp(-0.055*preciptitation_mm));
352
        max_interception_mm += preciptitation_mm *  (1. - max_flow_needle * mLAINeedle/mLAI);
353
        // (2) calculate maximum storage potential based on the current LAI
621 werner 354
        double max_storage_needle = mNeedleFactor * (1. - exp(-0.55*mLAINeedle) );
237 werner 355
        max_storage_mm += max_storage_needle;
356
    }
357
 
358
    if (mLAIBroadleaved>0.) {
359
        // (1) calculate maximum fraction of thru-flow the crown (based on precipitation)
299 werner 360
        double max_flow_broad = 0.9 * pow(1.22 - exp(-0.055*preciptitation_mm), 0.35);
237 werner 361
        max_interception_mm += preciptitation_mm *  (1. - max_flow_broad * mLAIBroadleaved/mLAI);
362
        // (2) calculate maximum storage potential based on the current LAI
621 werner 363
        double max_storage_broad = mDecidousFactor * (1. - exp(-0.5*mLAIBroadleaved) );
237 werner 364
        max_storage_mm += max_storage_broad;
365
    }
366
 
367
    // (3) calculate actual interception and store for evaporation calculation
368
    mInterception = qMin( max_storage_mm, max_interception_mm );
369
 
335 werner 370
    // (4) limit interception with amount of precipitation
371
    mInterception = qMin( mInterception, preciptitation_mm);
372
 
373
    // (5) reduce preciptitaion by the amount that is intercepted by the canopy
237 werner 374
    return preciptitation_mm - mInterception;
375
 
376
}
377
 
239 werner 378
/// sets up the canopy. fetch some global parameter values...
379
void Canopy::setup()
237 werner 380
{
240 werner 381
    mAirDensity = Model::settings().airDensity; // kg / m3
237 werner 382
}
383
 
384
void Canopy::setStandParameters(const double LAIneedle, const double LAIbroadleave, const double maxCanopyConductance)
385
{
386
    mLAINeedle = LAIneedle;
387
    mLAIBroadleaved=LAIbroadleave;
388
    mLAI=LAIneedle+LAIbroadleave;
239 werner 389
    mAvgMaxCanopyConductance = maxCanopyConductance;
553 werner 390
 
391
    // clear aggregation containers
562 werner 392
    for (int i=0;i<12;++i) mET0[i]=0.;
553 werner 393
 
237 werner 394
}
395
 
396
 
397
 
256 werner 398
/** calculate the daily evaporation/transpiration using the Penman-Monteith-Equation.
399
   This version is based on 3PG. See the Visual Basic Code in 3PGjs.xls.
400
   Returns the total sum of evaporation+transpiration in mm of the day. */
367 werner 401
double Canopy::evapotranspiration3PG(const ClimateDay *climate, const double daylength_h, const double combined_response)
256 werner 402
{
403
    double vpd_mbar = climate->vpd * 10.; // convert from kPa to mbar
404
    double temperature = climate->temperature; // average temperature of the day (°C)
405
    double daylength = daylength_h * 3600.; // daylength in seconds (convert from length in hours)
406
    double rad = climate->radiation / daylength * 1000000; //convert from MJ/m2 (day sum) to average radiation flow W/m2 [MJ=MWs -> /s * 1,000,000
407
 
561 werner 408
    // the radiation: based on linear empirical function
409
    const double qa = -90.;
410
    const double qb = 0.8;
411
    double net_rad = qa + qb*rad;
412
 
256 werner 413
    //: Landsberg original: const double e20 = 2.2;  //rate of change of saturated VP with T at 20C
414
    const double VPDconv = 0.000622; //convert VPD to saturation deficit = 18/29/1000
415
    const double latent_heat = 2460000.; // Latent heat of vaporization. Energy required per unit mass of water vaporized [J kg-1]
416
 
368 werner 417
    double gBL  = Model::settings().boundaryLayerConductance; // boundary layer conductance
418
 
419
    // canopy conductance.
420
    // The species traits are weighted by LAI on the RU.
421
    // maximum canopy conductance: see getStandValues()
422
    // current response: see calculateSoilAtmosphereResponse(). This is basically a weighted average of min(water_response, vpd_response) for each species
367 werner 423
    double gC = mAvgMaxCanopyConductance * combined_response;
256 werner 424
 
367 werner 425
 
256 werner 426
    double defTerm = mAirDensity * latent_heat * (vpd_mbar * VPDconv) * gBL;
427
 
561 werner 428
    //  with temperature-dependent  slope of  vapor pressure saturation curve
429
    // (following  Allen et al. (1998),  http://www.fao.org/docrep/x0490e/x0490e07.htm#atmospheric%20parameters)
430
    // svp_slope in mbar.
621 werner 431
    //double svp_slope = 4098. * (6.1078 * exp(17.269 * temperature / (temperature + 237.3))) / ((237.3+temperature)*(237.3+temperature));
561 werner 432
 
621 werner 433
    // alternatively: very simple variant (following here the original 3PG code). This
434
    // keeps yields +- same results for summer, but slightly lower values in winter (2011/03/16)
435
    double svp_slope = 2.2;
436
 
256 werner 437
    double div = (1. + svp_slope + gBL / gC);
561 werner 438
    double Etransp = (svp_slope * net_rad + defTerm) / div;
335 werner 439
    double canopy_transpiration = Etransp / latent_heat * daylength;
256 werner 440
 
562 werner 441
    // calculate reference evapotranspiration
442
    // see Adair et al 2008
443
    const double psychrometric_const = 0.0672718682328237; // kPa/degC
444
    const double windspeed = 2.; // m/s
445
    double net_rad_mj_day = net_rad*daylength/1000000.; // convert W/m2 again to MJ/m2*day
446
    double et0_day = 0.408*svp_slope*net_rad_mj_day  + psychrometric_const*900./(temperature+273.)*windspeed*climate->vpd;
447
    double et0_div = svp_slope+psychrometric_const*(1.+0.34*windspeed);
448
    et0_day = et0_day / et0_div;
449
    mET0[climate->month-1] += et0_day;
553 werner 450
 
256 werner 451
    if (mInterception>0.) {
452
        // we assume that for evaporation from leaf surface gBL/gC -> 0
562 werner 453
        double div_evap = 1. + svp_slope;
620 werner 454
        double evap_canopy_potential = (svp_slope*net_rad + defTerm) / div_evap / latent_heat * daylength;
455
        // reduce the amount of transpiration on a wet day based on the approach of
456
        // Wigmosta et al (1994). see http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance
457
 
458
        double ratio_T_E = canopy_transpiration / evap_canopy_potential;
459
        double evap_canopy = qMin(evap_canopy_potential, mInterception);
460
 
461
        // for interception -> 0, the canopy transpiration is unchanged
462
        canopy_transpiration = (evap_canopy_potential - evap_canopy) * ratio_T_E;
463
 
562 werner 464
        mInterception -= evap_canopy; // reduce interception
465
        mEvaporation = evap_canopy; // evaporation from intercepted water
620 werner 466
 
256 werner 467
    }
335 werner 468
    return canopy_transpiration;
256 werner 469
}
470
 
237 werner 471
} // end namespace