Subversion Repositories public iLand

Rev

Rev 1220 | Go to most recent revision | 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 "establishment.h"

#include "climate.h"
#include "species.h"
#include "resourceunit.h"
#include "resourceunitspecies.h"
#include "seeddispersal.h"
#include "model.h"
#include "grasscover.h"
#include "helper.h"
#include "debugtimer.h"
#include "watercycle.h"

/** @class Establishment
    Establishment deals with the establishment process of saplings.
    http://iland.boku.ac.at/establishment
    Prerequisites for establishment are:
    the availability of seeds: derived from the seed-maps per Species (@sa SeedDispersal)
    the quality of the abiotic environment (TACA-model): calculations are performend here, based on climate and species responses
    the quality of the biotic environment, mainly light: based on the LIF-values

  */

Establishment::Establishment()
{
    mPAbiotic = 0.;
}

Establishment::Establishment(const Climate *climate, const ResourceUnitSpecies *rus)
{
    setup(climate, rus);
}

void Establishment::setup(const Climate *climate, const ResourceUnitSpecies *rus)
{
    mClimate = climate;
    mRUS = rus;
    mPAbiotic = 0.;
    mPxDensity = 0.;
    mNumberEstablished = 0;
    if (climate==0)
        throw IException("Establishment::setup: no valid climate for a resource unit.");
    if (rus==0 || rus->species()==0 || rus->ru()==0)
        throw IException("Establishment::setup: important variable is null (are the species properly set up?).");
}



void Establishment::clear()
{
    mPAbiotic = 0.;
    mNumberEstablished = 0;
    mPxDensity = 0.;
    mTACA_min_temp=mTACA_chill=mTACA_gdd=mTACA_frostfree=false;
    mTACA_frostAfterBuds=0;
    mSumLIFvalue = 0.;
    mLIFcount = 0;
    mWaterLimitation = 0.;

}


double Establishment::calculateWaterLimitation(const int veg_period_start, const int veg_period_end)
{
    // return 1 if effect is disabled
    if (mRUS->species()->establishmentParameters().psi_min >= 0.)
        return 1.;

    double psi_min = mRUS->species()->establishmentParameters().psi_min;
    const WaterCycle *water = mRUS->ru()->waterCycle();
    int days = mRUS->ru()->climate()->daysOfYear();

    // two week (14 days) running average of actual psi-values on the resource unit
    static const int nwindow = 14;
    double psi_buffer[nwindow];
    for (int i=0;i<nwindow;++i)
        psi_buffer[i] = 0.;
    double current_sum = 0.;

    int i_buffer = 0;
    double min_average = 9999999.;
    double current_avg = 0.;
    for (int day=0;day<days;++day) {
        // running average: remove oldest item, add new item in a ringbuffer
        current_sum -= psi_buffer[i_buffer];
        psi_buffer[i_buffer] = water->psi_kPa(day);
        current_sum += psi_buffer[i_buffer];

        if (day>=veg_period_start && day<=veg_period_end) {
            current_avg = day>0? current_sum / std::min(day, nwindow) : current_sum;
            min_average = std::min(min_average, current_avg);
        }

        // move to next value in the buffer
        i_buffer = ++i_buffer % nwindow;
    }

    if (min_average > 1000.)
        return 1.; // invalid vegetation period?

    // calculate the response of the species to this value of psi (see also Species::soilwaterResponse())
    const double psi_mpa = min_average / 1000.; // convert to MPa
    double result = limit( (psi_mpa - psi_min) / (-0.015 -  psi_min) , 0., 1.);

    return result;

}



/** Calculate the abiotic environemnt for seedling for a given species and a given resource unit.
 The model is closely based on the TACA approach of Nitschke and Innes (2008), Ecol. Model 210, 263-277
 more details: http://iland.boku.ac.at/establishment#abiotic_environment
 a model mockup in R: script_establishment.r

 */

void Establishment::calculateAbioticEnvironment()
{
    //DebugTimer t("est_abiotic"); t.setSilent();
    // make sure that required calculations (e.g. watercycle are already performed)
    const_cast<ResourceUnitSpecies*>(mRUS)->calculate(true); // calculate the 3pg module and run the water cycle (this is done only if that did not happen up to now); true: call comes from regeneration

    const EstablishmentParameters &p = mRUS->species()->establishmentParameters();
    const Phenology &pheno = mClimate->phenology(mRUS->species()->phenologyClass());

    mTACA_min_temp = true; // minimum temperature threshold
    mTACA_chill = false;  // (total) chilling requirement
    mTACA_gdd = false;   // gdd-thresholds
    mTACA_frostfree = false; // frost free days in vegetation period
    mTACA_frostAfterBuds = 0; // frost days after bud birst


    const ClimateDay *day = mClimate->begin();
    int doy = 0;
    double GDD=0.;
    double GDD_BudBirst = 0.;
    int chill_days = pheno.chillingDaysLastYear(); // chilling days of the last autumn
    int frost_free = 0;
    mTACA_frostAfterBuds = 0;
    bool chill_ok = false;
    bool buds_are_birst = false;
    int veg_period_end = pheno.vegetationPeriodEnd();
    if (veg_period_end >= 365)
        veg_period_end = mClimate->sun().dayShorter10_5hrs();

    for (; day!=mClimate->end(); ++day, ++doy) {
        // minimum temperature: if temp too low -> set prob. to zero
        if (day->min_temperature < p.min_temp)
            mTACA_min_temp = false;

        // count frost free days
        if (day->min_temperature > 0.)
            frost_free++;

        // chilling requirement, GDD, bud birst
        if (day->temperature>=-5. && day->temperature<5.)
            chill_days++;
        if (chill_days>p.chill_requirement)
            chill_ok=true;
        // GDDs above the base temperature are counted if beginning from the day where the chilling requirements are met
        // up to a fixed day ending the veg period
        if (doy<=veg_period_end) {
            // accumulate growing degree days
            if (chill_ok && day->temperature > p.GDD_baseTemperature) {
                GDD += day->temperature - p.GDD_baseTemperature;
                GDD_BudBirst += day->temperature - p.GDD_baseTemperature;
            }
            // if day-frost occurs, the GDD counter for bud birst is reset
            if (day->temperature <= 0.)
                GDD_BudBirst = 0.;

            if (GDD_BudBirst > p.bud_birst)
                buds_are_birst = true;

            if (doy<veg_period_end && buds_are_birst && day->min_temperature <= 0.)
                mTACA_frostAfterBuds++;
        }
    }
    // chilling requirement
    if (chill_ok)
        mTACA_chill = true;

    // GDD requirements
    if (GDD>p.GDD_min && GDD<p.GDD_max)
        mTACA_gdd = true;

    // frost free days in the vegetation period
    if (frost_free > p.frost_free)
        mTACA_frostfree = true;

    // if all requirements are met:
    if (mTACA_chill && mTACA_min_temp && mTACA_gdd && mTACA_frostfree) {
        // negative effect of frost events after bud birst
        double frost_effect = 1.;
        if (mTACA_frostAfterBuds>0)
            frost_effect = pow(p.frost_tolerance, sqrt(double(mTACA_frostAfterBuds)));
        // negative effect due to water limitation on establishment [1: no effect]
        mWaterLimitation = calculateWaterLimitation(pheno.vegetationPeriodStart(), pheno.vegetationPeriodLength());
        // combine drought and frost effect multiplicatively
        mPAbiotic = frost_effect * mWaterLimitation;
    } else {
        mPAbiotic = 0.; // if any of the requirements is not met
    }

}

void Establishment::writeDebugOutputs()
{
    if (GlobalSettings::instance()->isDebugEnabled(GlobalSettings::dEstablishment)) {
        DebugList &out = GlobalSettings::instance()->debugList(mRUS->ru()->index(), GlobalSettings::dEstablishment);
        // establishment details
        out << mRUS->species()->id() << mRUS->ru()->index() << mRUS->ru()->id();
        out << avgSeedDensity();
        out << TACAminTemp() << TACAchill() << TACAfrostFree() << TACgdd();
        out << TACAfrostDaysAfterBudBirst() << waterLimitation() << abioticEnvironment();
        out << mRUS->prod3PG().fEnvYear() << mRUS->constSaplingStat().newSaplings();

        //out << mSaplingStat.livingSaplings() << mSaplingStat.averageHeight() << mSaplingStat.averageAge() << mSaplingStat.averageDeltaHPot() << mSaplingStat.averageDeltaHRealized();
        //out << mSaplingStat.newSaplings() << mSaplingStat.diedSaplings() << mSaplingStat.recruitedSaplings() << mSpecies->saplingGrowthParameters().referenceRatio;
    }
    //); // DBGMODE()


    if ( logLevelDebug() )
        qDebug() << "establishment of RU" << mRUS->ru()->index() << "species" << mRUS->species()->id()
                 << "seeds density:" << avgSeedDensity()
                 << "abiotic environment:" << abioticEnvironment()
                 << "f_env,yr:" << mRUS->prod3PG().fEnvYear()
                 << "N(established):" << numberEstablished();

}