Subversion Repositories public iLand

Rev

Rev 1221 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/********************************************************************************************
**    iLand - an individual based forest landscape and disturbance model
**    http://iland.boku.ac.at
**    Copyright (C) 2009-  Werner Rammer, Rupert Seidl
**
**    This program is free software: you can redistribute it and/or modify
**    it under the terms of the GNU General Public License as published by
**    the Free Software Foundation, either version 3 of the License, or
**    (at your option) any later version.
**
**    This program is distributed in the hope that it will be useful,
**    but WITHOUT ANY WARRANTY; without even the implied warranty of
**    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**    GNU General Public License for more details.
**
**    You should have received a copy of the GNU General Public License
**    along with this program.  If not, see <http://www.gnu.org/licenses/>.
********************************************************************************************/


#include "bbgenerations.h"

#include "resourceunit.h"
#include "climate.h"
/** @class BBGenerations
    @ingroup beetlemodule
    BBGenerations calculates potential bark beetle generations based on climate data (including bark temperature).
  */

BBGenerations::BBGenerations()
{
}

/**
 * @brief BBGenerations::calculateGenerations
 * @param ru
 * @return the number of filial generation (i.e, main generations) + 0.5 if a sister brood develops also for the last generation
 */

double BBGenerations::calculateGenerations(const ResourceUnit *ru)
{
    calculateBarkTemperature(ru);

    // start at the 1. of April, and wait for 140.3 degree days (with a threhsold of 8.3 degrees)
    const ClimateDay *clim = ru->climate()->day(4-1,1-1); // 0-based indices
    const ClimateDay *last_day = ru->climate()->day(10-1,31-1); // 0-based indices -> Oct 31
    const ClimateDay * day_too_short = ru->climate()->dayOfYear(ru->climate()->sun().dayShorter14_5hrs()); // the first doy where the day is shorter than 14.5 hours

    double dd=0.;
    while (dd<140.3 && clim<last_day) {
        dd+=std::max(clim->max_temperature-8.3, 0.);
        ++clim;
    }
    // now wait for a decent warm day with tmax > 16.5 degrees
    while (clim->max_temperature<=16.5 && clim<last_day)
        ++clim;

    mGenerations.clear();
    // start with the first generation....
    BBGeneration bbgen;
    bbgen.start_day = ru->climate()->whichDayOfYear(clim);
    bbgen.is_sister_brood = false;
    bbgen.gen = 1; // the first generation
    mGenerations.append(bbgen);

    // process the generations
    int i=0;
    while (i<mGenerations.count()) {
        BBGeneration bb = mGenerations[i]; // deliberately make a copy and not a ref
        const ClimateDay *c = ru->climate()->dayOfYear(bb.start_day);
        int doy = bb.start_day;
        double base_temp = mEffectiveBarkTemp[doy];

        double t_sum=0.;
        bool added_sister_brood = false;
        while (c < last_day) {
            t_sum = (mEffectiveBarkTemp[doy]-base_temp) / 557.;
            if (t_sum>=1.) {
                if (c<day_too_short) {
                    // start a new parental generation (a full cycle), only if the current
                    // generation is also a filial generation.
                    if (bb.is_sister_brood)
                        mGenerations.append(BBGeneration(doy, true, bb.gen)); // sisterbrood: (true), keep counter of filial generation
                    else
                        mGenerations.append(BBGeneration(doy, false, bb.gen+1)); // new filial brood (false), start a new gen

                }
                break;
            } else if (t_sum>0.5 && !added_sister_brood) {
                // start a sister brood, *if* the maximum air temperature is high enough, and if the
                // length of the day > 14.5 hours
                if (c->max_temperature>16.5 && c<day_too_short) {
                    mGenerations.append(BBGeneration(doy, true, bb.gen)); // add a sister brood generation (true), keep gen. of originating brood
                    added_sister_brood = true;
                }
            }
            ++c; ++doy;
        }
        mGenerations[i].value = std::min(t_sum, 1.);
        ++i; // now process the next generation
    }


    mNSisterBroods = 0; mNFilialBroods = 0;

    for (i=0;i<mGenerations.count();++i) {
        if (mGenerations[i].value>0.6 && mGenerations[i].is_sister_brood==true)
            mNSisterBroods = std::max( mGenerations[i].gen, mNSisterBroods );

        if (mGenerations[i].value>0.6 && mGenerations[i].is_sister_brood==false)
            mNFilialBroods = mGenerations[i].gen;
    }

    // return the number of filial broods, and increase by 0.5 if a sister brood of the last filial generation has successfully developed.
    return mNFilialBroods + ( hasSisterBrood() ? 0.5 : 0.);

//    // now accumulate the generations
//    double filial_generations = 0.;
//    double sister_generations = 0.;
//    // accumulate possible number of offspring
//    const double offspring_factor = 25.; // assuming sex-ratio of 1:1 and 50 offspring per female (see p. 59)
//    int n=0;
//    int total_offspring = 0;
//    for (i=0;i<mGenerations.count();++i) {
//        if (mGenerations[i].value>0.6)  {
//            ++n;
//            total_offspring+= pow(offspring_factor, mGenerations[i].gen-1)*2.*offspring_factor;
//        }
//        if (mGenerations[i].value>0.6 && mGenerations[i].is_sister_brood==true)
//            sister_generations+=mGenerations[i].value;
//        if (mGenerations[i].value>0.6 && mGenerations[i].is_sister_brood==false)
//            filial_generations+=mGenerations[i].value;

//    }
//    if (logLevelDebug())
//        qDebug() << "rid" <<ru->id() << "filial/sister:" << filial_generations << sister_generations << "offspring:" << total_offspring << "started generations:" << n;
//    mNSisterBroods = sister_generations;
//    mNFilialBroods = filial_generations;

//    return filial_generations + (sister_generations>0?0.5:0);
}


/**
 * Calculate the bark temperatures for this year and a given resource unit.
 * Input: climate data (tmax (C), tmean (C), radiation (MJ/m2))
 * the LAI to estimate the radiation on the ground (Wh/m2)
 * Output: calculates for each day of the year the "effective" bark-temperature and saves a cumulative sum
 * Source: Schopf et al 2004: Risikoabschaetzung von Borkenkaefermassenkalamitaeten im Nationalpark Kalkalpen
 */

void BBGenerations::calculateBarkTemperature(const ResourceUnit *ru)
{
    // estimate the fraction of light on the ground (multiplier from 0..1)
    const double k = 0.5; // constant for the beer lambert function
    double ground_light_fraction = exp(-k * ru->leafAreaIndex() );

    mFrostDaysEarly=0;
    mFrostDaysLate=0;

    for (int i=0;i<ru->climate()->daysOfYear();++i) {
        const ClimateDay *clim = ru->climate()->dayOfYear(i);
        // radiation: MJ/m2/day -> the regression uses Wh/m2/day -> conversion-factor: 1/0.0036
        double rad_wh = clim->radiation*ground_light_fraction/0.0036;
        // calc. maximum bark temperature
        double bt_max=1.656 + 0.002955*rad_wh + 0.534*clim->max_temperature + 0.01884 * clim->max_temperature*clim->max_temperature;
        double diff_bt=0.;


        if (bt_max>=30.4)
            diff_bt=std::max(-310.667 + 9.603 * bt_max, 0.); // degree * hours

        // mean bark temperature
        double bt_mean=-0.173+0.0008518*rad_wh+1.054*clim->mean_temp();

        // effective:
        double bt_sum = std::max(bt_mean - 8.3, 0.)*24.; // degree * hours

        // corrected for days with very high bark temperature (>30.4 degrees)
        double bt_sum_eff = (bt_sum - diff_bt) / 24.; // degree * days

        mEffectiveBarkTemp[i] = (i>0? mEffectiveBarkTemp[i-1] : 0.) + bt_sum_eff;

        // frost days:
        if (clim->min_temperature < -15.) {
            if (i < ru->climate()->sun().longestDay())
                mFrostDaysEarly++;
            else
                mFrostDaysLate++;
        }

    }

}