Subversion Repositories public iLand

Rev

Rev 697 | Rev 723 | 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;
720 werner 118
    foreach(const ResourceUnitSpecies *rus, mRU->ruSpecies()) {
455 werner 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.;
720 werner 170
    foreach(const 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
 
720 werner 191
    DBGMODE(
192
          if (mRU->averageAging()>1. || mRU->averageAging()<0. || total_response<0 || total_response>1.)
193
             qDebug() << "water cycle: average aging invalid. aging:" << mRU->averageAging() << "total response" << total_response << "total lai factor:" << total_lai_factor;
484 werner 194
    );
482 werner 195
 
196
    //DBG_IF(mRU->averageAging()>1. || mRU->averageAging()<0.,"water cycle", "average aging invalid!" );
367 werner 197
    return total_response;
198
}
199
 
200
 
237 werner 201
/// Main Water Cycle function. This function triggers all water related tasks for
202
/// one simulation year.
299 werner 203
/// @sa http://iland.boku.ac.at/water+cycle
237 werner 204
void WaterCycle::run()
205
{
496 werner 206
    // necessary?
207
    if (GlobalSettings::instance()->currentYear() == mLastYear)
208
        return;
626 werner 209
    DebugTimer tw("water:run");
646 werner 210
    WaterCycleData add_data;
626 werner 211
 
237 werner 212
    // preparations (once a year)
367 werner 213
    getStandValues(); // fetch canopy characteristics from iLand (including weighted average for mCanopyConductance)
237 werner 214
    mCanopy.setStandParameters(mLAINeedle,
215
                               mLAIBroadleaved,
216
                               mCanopyConductance);
217
 
218
    // main loop over all days of the year
239 werner 219
    double prec_mm, prec_after_interception, prec_to_soil, et, excess;
338 werner 220
    const Climate *climate = mRU->climate();
221
    const ClimateDay *day = climate->begin();
222
    const ClimateDay *end = climate->end();
237 werner 223
    int doy=0;
224
    double total_excess = 0.;
225
    for (; day<end; ++day, ++doy) {
226
        // (1) precipitation of the day
227
        prec_mm = day->preciptitation;
228
        // (2) interception by the crown
229
        prec_after_interception = mCanopy.flow(prec_mm, day->temperature);
230
        // (3) storage in the snow pack
231
        prec_to_soil = mSnowPack.flow(prec_after_interception, day->temperature);
646 werner 232
        // save extra data (used by e.g. fire module)
233
        add_data.water_to_ground[doy] = prec_to_soil;
234
        add_data.snow_cover[doy] = mSnowPack.snowPack();
237 werner 235
        // (4) add rest to soil
236
        mContent += prec_to_soil;
266 werner 237
 
238
        excess = 0.;
239
        if (mContent>mFieldCapacity) {
240
            // excess water runoff
241
            excess = mContent - mFieldCapacity;
242
            total_excess += excess;
243
            mContent = mFieldCapacity;
244
        }
245
 
367 werner 246
        double current_psi = psiFromHeight(mContent);
247
        mPsi[doy] = current_psi;
553 werner 248
 
335 werner 249
        // (5) transpiration of the vegetation (and of water intercepted in canopy)
367 werner 250
        // calculate the LAI-weighted response values for soil water and vpd:
680 werner 251
        double interception_before_transpiration = mCanopy.interception();
367 werner 252
        double combined_response = calculateSoilAtmosphereResponse( current_psi, day->vpd);
253
        et = mCanopy.evapotranspiration3PG(day, climate->daylength_h(doy), combined_response);
680 werner 254
        // if there is some flow from intercepted water to the ground -> add to "water_to_the_ground"
255
        if (mCanopy.interception() < interception_before_transpiration)
256
            add_data.water_to_ground[doy]+= interception_before_transpiration - mCanopy.interception();
238 werner 257
 
241 werner 258
        mContent -= et; // reduce content (transpiration)
338 werner 259
        // add intercepted water (that is *not* evaporated) again to the soil (or add to snow if temp too low -> call to snowpack)
260
        mContent += mSnowPack.add(mCanopy.interception(),day->temperature);
241 werner 261
 
338 werner 262
        // do not remove water below the PWP (fixed value)
266 werner 263
        if (mContent<mPermanentWiltingPoint) {
271 werner 264
            et -= mPermanentWiltingPoint - mContent; // reduce et (for bookkeeping)
266 werner 265
            mContent = mPermanentWiltingPoint;
237 werner 266
        }
266 werner 267
 
546 werner 268
 
271 werner 269
        //DBGMODE(
239 werner 270
            if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dWaterCycle)) {
240 werner 271
                DebugList &out = GlobalSettings::instance()->debugList(day->id(), GlobalSettings::dWaterCycle);
239 werner 272
                // climatic variables
605 werner 273
                out << day->id() << mRU->index() << mRU->id() << day->temperature << day->vpd << day->preciptitation << day->radiation;
368 werner 274
                out << combined_response; // combined response of all species on RU (min(water, vpd))
239 werner 275
                // fluxes
271 werner 276
                out << prec_after_interception << prec_to_soil << et << mCanopy.evaporationCanopy()
540 werner 277
                        << mContent << mPsi[doy] << excess;
239 werner 278
                // other states
279
                out << mSnowPack.snowPack();
364 werner 280
                //special sanity check:
281
                if (prec_to_soil>0. && mCanopy.interception()>0.)
282
                    if (mSnowPack.snowPack()==0. && day->preciptitation==0)
283
                        qDebug() << "watercontent increase without precipititaion";
239 werner 284
 
285
            }
271 werner 286
        //); // DBGMODE()
239 werner 287
 
237 werner 288
    }
646 werner 289
    // call external modules
290
    GlobalSettings::instance()->model()->modules()->calculateWater(mRU, &add_data);
496 werner 291
    mLastYear = GlobalSettings::instance()->currentYear();
292
 
237 werner 293
}
294
 
295
 
296
namespace Water {
297
 
298
/** calculates the input/output of water that is stored in the snow pack.
299
  The approach is similar to Picus 1.3 and ForestBGC (Running, 1988).
300
  Returns the amount of water that exits the snowpack (precipitation, snow melt) */
301
double SnowPack::flow(const double &preciptitation_mm, const double &temperature)
302
{
621 werner 303
    if (temperature>mSnowTemperature) {
237 werner 304
        if (mSnowPack==0.)
305
            return preciptitation_mm; // no change
306
        else {
307
            // snow melts
308
            const double melting_coefficient = 0.7; // mm/°C
621 werner 309
            double melt = qMin( (temperature-mSnowTemperature) * melting_coefficient, mSnowPack);
240 werner 310
            mSnowPack -=melt;
237 werner 311
            return preciptitation_mm + melt;
312
        }
313
    } else {
314
        // snow:
315
        mSnowPack += preciptitation_mm;
316
        return 0.; // no output.
317
    }
318
 
319
}
320
 
321
 
334 werner 322
inline double SnowPack::add(const double &preciptitation_mm, const double &temperature)
323
{
324
    // do nothing for temps > 0°
621 werner 325
    if (temperature>mSnowTemperature)
334 werner 326
        return preciptitation_mm;
327
 
328
    // temps < 0°: add to snow
329
    mSnowPack += preciptitation_mm;
330
    return 0.;
331
}
332
 
237 werner 333
/** Interception in crown canopy.
334
    Calculates the amount of preciptitation that does not reach the ground and
335
    is stored in the canopy. The approach is adopted from Picus 1.3.
299 werner 336
    Returns the amount of precipitation (mm) that surpasses the canopy layer.
337
    @sa http://iland.boku.ac.at/water+cycle#precipitation_and_interception */
237 werner 338
double Canopy::flow(const double &preciptitation_mm, const double &temperature)
339
{
340
    // sanity checks
341
    mInterception = 0.;
271 werner 342
    mEvaporation = 0.;
237 werner 343
    if (!mLAI)
344
        return preciptitation_mm;
345
    if (!preciptitation_mm)
346
        return 0.;
347
    double max_interception_mm=0.; // maximum interception based on the current foliage
348
    double max_storage_mm=0.; // maximum storage in canopy
349
 
350
    if (mLAINeedle>0.) {
351
        // (1) calculate maximum fraction of thru-flow the crown (based on precipitation)
352
        double max_flow_needle = 0.9 * sqrt(1.03 - exp(-0.055*preciptitation_mm));
353
        max_interception_mm += preciptitation_mm *  (1. - max_flow_needle * mLAINeedle/mLAI);
354
        // (2) calculate maximum storage potential based on the current LAI
621 werner 355
        double max_storage_needle = mNeedleFactor * (1. - exp(-0.55*mLAINeedle) );
237 werner 356
        max_storage_mm += max_storage_needle;
357
    }
358
 
359
    if (mLAIBroadleaved>0.) {
360
        // (1) calculate maximum fraction of thru-flow the crown (based on precipitation)
299 werner 361
        double max_flow_broad = 0.9 * pow(1.22 - exp(-0.055*preciptitation_mm), 0.35);
237 werner 362
        max_interception_mm += preciptitation_mm *  (1. - max_flow_broad * mLAIBroadleaved/mLAI);
363
        // (2) calculate maximum storage potential based on the current LAI
621 werner 364
        double max_storage_broad = mDecidousFactor * (1. - exp(-0.5*mLAIBroadleaved) );
237 werner 365
        max_storage_mm += max_storage_broad;
366
    }
367
 
368
    // (3) calculate actual interception and store for evaporation calculation
369
    mInterception = qMin( max_storage_mm, max_interception_mm );
370
 
335 werner 371
    // (4) limit interception with amount of precipitation
372
    mInterception = qMin( mInterception, preciptitation_mm);
373
 
374
    // (5) reduce preciptitaion by the amount that is intercepted by the canopy
237 werner 375
    return preciptitation_mm - mInterception;
376
 
377
}
378
 
239 werner 379
/// sets up the canopy. fetch some global parameter values...
380
void Canopy::setup()
237 werner 381
{
240 werner 382
    mAirDensity = Model::settings().airDensity; // kg / m3
237 werner 383
}
384
 
385
void Canopy::setStandParameters(const double LAIneedle, const double LAIbroadleave, const double maxCanopyConductance)
386
{
387
    mLAINeedle = LAIneedle;
388
    mLAIBroadleaved=LAIbroadleave;
389
    mLAI=LAIneedle+LAIbroadleave;
239 werner 390
    mAvgMaxCanopyConductance = maxCanopyConductance;
553 werner 391
 
392
    // clear aggregation containers
562 werner 393
    for (int i=0;i<12;++i) mET0[i]=0.;
553 werner 394
 
237 werner 395
}
396
 
397
 
398
 
256 werner 399
/** calculate the daily evaporation/transpiration using the Penman-Monteith-Equation.
400
   This version is based on 3PG. See the Visual Basic Code in 3PGjs.xls.
401
   Returns the total sum of evaporation+transpiration in mm of the day. */
367 werner 402
double Canopy::evapotranspiration3PG(const ClimateDay *climate, const double daylength_h, const double combined_response)
256 werner 403
{
404
    double vpd_mbar = climate->vpd * 10.; // convert from kPa to mbar
405
    double temperature = climate->temperature; // average temperature of the day (°C)
406
    double daylength = daylength_h * 3600.; // daylength in seconds (convert from length in hours)
407
    double rad = climate->radiation / daylength * 1000000; //convert from MJ/m2 (day sum) to average radiation flow W/m2 [MJ=MWs -> /s * 1,000,000
408
 
561 werner 409
    // the radiation: based on linear empirical function
410
    const double qa = -90.;
411
    const double qb = 0.8;
412
    double net_rad = qa + qb*rad;
413
 
256 werner 414
    //: Landsberg original: const double e20 = 2.2;  //rate of change of saturated VP with T at 20C
415
    const double VPDconv = 0.000622; //convert VPD to saturation deficit = 18/29/1000
416
    const double latent_heat = 2460000.; // Latent heat of vaporization. Energy required per unit mass of water vaporized [J kg-1]
417
 
368 werner 418
    double gBL  = Model::settings().boundaryLayerConductance; // boundary layer conductance
419
 
420
    // canopy conductance.
421
    // The species traits are weighted by LAI on the RU.
422
    // maximum canopy conductance: see getStandValues()
423
    // current response: see calculateSoilAtmosphereResponse(). This is basically a weighted average of min(water_response, vpd_response) for each species
367 werner 424
    double gC = mAvgMaxCanopyConductance * combined_response;
256 werner 425
 
367 werner 426
 
256 werner 427
    double defTerm = mAirDensity * latent_heat * (vpd_mbar * VPDconv) * gBL;
428
 
561 werner 429
    //  with temperature-dependent  slope of  vapor pressure saturation curve
430
    // (following  Allen et al. (1998),  http://www.fao.org/docrep/x0490e/x0490e07.htm#atmospheric%20parameters)
431
    // svp_slope in mbar.
621 werner 432
    //double svp_slope = 4098. * (6.1078 * exp(17.269 * temperature / (temperature + 237.3))) / ((237.3+temperature)*(237.3+temperature));
561 werner 433
 
621 werner 434
    // alternatively: very simple variant (following here the original 3PG code). This
435
    // keeps yields +- same results for summer, but slightly lower values in winter (2011/03/16)
436
    double svp_slope = 2.2;
437
 
256 werner 438
    double div = (1. + svp_slope + gBL / gC);
561 werner 439
    double Etransp = (svp_slope * net_rad + defTerm) / div;
335 werner 440
    double canopy_transpiration = Etransp / latent_heat * daylength;
256 werner 441
 
562 werner 442
    // calculate reference evapotranspiration
443
    // see Adair et al 2008
444
    const double psychrometric_const = 0.0672718682328237; // kPa/degC
445
    const double windspeed = 2.; // m/s
446
    double net_rad_mj_day = net_rad*daylength/1000000.; // convert W/m2 again to MJ/m2*day
447
    double et0_day = 0.408*svp_slope*net_rad_mj_day  + psychrometric_const*900./(temperature+273.)*windspeed*climate->vpd;
448
    double et0_div = svp_slope+psychrometric_const*(1.+0.34*windspeed);
449
    et0_day = et0_day / et0_div;
450
    mET0[climate->month-1] += et0_day;
553 werner 451
 
256 werner 452
    if (mInterception>0.) {
453
        // we assume that for evaporation from leaf surface gBL/gC -> 0
562 werner 454
        double div_evap = 1. + svp_slope;
620 werner 455
        double evap_canopy_potential = (svp_slope*net_rad + defTerm) / div_evap / latent_heat * daylength;
456
        // reduce the amount of transpiration on a wet day based on the approach of
457
        // Wigmosta et al (1994). see http://iland.boku.ac.at/water+cycle#transpiration_and_canopy_conductance
458
 
459
        double ratio_T_E = canopy_transpiration / evap_canopy_potential;
460
        double evap_canopy = qMin(evap_canopy_potential, mInterception);
461
 
462
        // for interception -> 0, the canopy transpiration is unchanged
463
        canopy_transpiration = (evap_canopy_potential - evap_canopy) * ratio_T_E;
464
 
562 werner 465
        mInterception -= evap_canopy; // reduce interception
466
        mEvaporation = evap_canopy; // evaporation from intercepted water
620 werner 467
 
256 werner 468
    }
335 werner 469
    return canopy_transpiration;
256 werner 470
}
471
 
237 werner 472
} // end namespace