LandscapeDNDC 1.37.0
ldndc::PhysiologyPNET Class Reference

Vegetation model PNET. More...

#include <models/physiology/pnet/pnet.h>

Inherits ldndc::MBE_LegacyModel.

Public Member Functions

lerr_t solve ()
 
lerr_t sleep ()
 

Private Member Functions

void update_nitrogen_concentration (MoBiLE_Plant *)
 
void YearlyReset (MoBiLE_Plant *)
 
void Phenology (MoBiLE_Plant *, int, double)
 
void Photosyn (MoBiLE_Plant *)
 
void AllocateMo (MoBiLE_Plant *)
 
void AllocateYr (MoBiLE_Plant *)
 
void N_uptake (MoBiLE_Plant *, double &)
 
void Update (MoBiLE_Plant *, double, double)
 
void TransformPhysiology (MoBiLE_Plant *)
 
size_t get_na_max_pnet ()
 

Static Private Attributes

static const double TMIN_NUP = 6.0
 
static const double TMAX_NUP = 25.0
 
static const double KM_NO3 = 1.0
 
static const double KM_NH4 = 1.0
 
static const double SRATEMAX = 0.01
 
static const double C = 0.02
 
static const double NUPTAIR = 0.0
 
static const double GROWMAX = 0.95
 

Detailed Description

Vegetation model PNET.

Author
Ruediger Grote

Member Function Documentation

◆ AllocateMo()

void ldndc::PhysiologyPNET::AllocateMo ( MoBiLE_Plant *  )
private

AllocateMo

◆ AllocateYr()

void ldndc::PhysiologyPNET::AllocateYr ( MoBiLE_Plant *  p)
private

AllocateYr

NOTE: the shift in carbon between compartments disturbs the nitrogen balance at this day!!!

871{
872 double fratio( (*p)->QWODFOLMIN() / (1.0 + (*p)->QWODFOLMIN()));
873 double TotalC( WoodC + BudC + FolMass * cbm::CCDM);
874 WoodC = TotalC * fratio;
875 BudC = TotalC - WoodC - FolMass * cbm::CCDM;
876
877 double mSapSum( 0.0);
878 double mBudSum( 0.0);
879// for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
880// {
881 mSapSum += p->mSap;
882 mBudSum += p->mBud;
883// }
884
885 // check for consistency of bud biomass
886 transfDWbs = 0.0;
887 transfDWsb = 0.0;
888 double scale_ay( cbm::KG_IN_G / cbm::CCDM); // gC in kgDW
889// for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
890// {
891 // shift from buds to sapwood tissue
892 double freeBudDW( 0.0);
893 double requBudDW(0.0);
894 if (mBudSum > 0.0)
895 {
896 double fm_vt( p->mBud / mBudSum);
897 freeBudDW = std::max(0.0, p->mBud - BudC * scale_ay * fm_vt);
898 requBudDW = std::max(0.0, BudC * scale_ay * fm_vt - p->mBud);
899 }
900 else
901 {
902 requBudDW = BudC * scale_ay / double( m_veg->size());
903 }
904
905 transfDWbs += freeBudDW;
906
907 // shift from sapwood to buds
908 double freeSapDW( 0.0);
909 if (mSapSum > 0.0)
910 {
911 freeSapDW = std::max(0.0, p->mSap - WoodC * scale_ay * p->mSap / mSapSum);
912 }
913 transfDWsb += std::min(freeSapDW, requBudDW);
914// }
915
916 // new bud and wood compartments
917 BudC += (transfDWsb - transfDWbs) / scale_ay;
918 WoodC += (transfDWbs - transfDWsb) / scale_ay;
919 if (BudC < 0.0) BudC = 0.0;
920 if (WoodC < 0.0) WoodC = 0.0;
921
922 // determination of maximum foliage growth next year
923 double FolMassMaxOld( FolMassMax);
924 FolMassMax = FolMass + BudC / cbm::CCDM;
925
926 // limitation of maximum foliage growth
927 double vegRelGrow( 0.0);
928// for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
929// {
930 vegRelGrow += GROWMAX * p->f_area;
931// }
932
933 // limitation of maximum foliage growth by maximum biomass (rg 03.02.11); including height and density impact (rg 19.10.11)
934 double FolMassOpt( 0.0);
935// for ( TreeIterator w = m_veg->groupbegin< species::wood >();
936// w != m_veg->groupend< species::wood >(); ++w)
937// {
938 FolMassOpt += m_pf.get_optimum_foliage_biomass_trees( p) * cbm::G_IN_KG;
939// }
940
941 // 15.05.11 rg maximum density effect considered
942 if ( FolMassMax > std::min(FolMassOpt, FolMassMaxOld * (1.0 + vegRelGrow * LightEffMin) * 1.0/p->f_area))
943 {
944 double excessDW( FolMassMax - std::min(FolMassOpt, FolMassMaxOld * (1.0 + vegRelGrow * LightEffMin)/p->f_area));
945 BudC -= (excessDW * cbm::CCDM);
946 FolMassMax = std::min(FolMassOpt, FolMassMaxOld * (1.0 + vegRelGrow * LightEffMin)/p->f_area);
947 transfDWbs += excessDW;
948 }
949
950 // determination of minimum foliage in the next year
951 if (FolReten > 0.0)
952 {
953 FolMassMin = FolMassMax * (1.0 - 1.0 / FolReten);
954 }
955 else
956 {
957 FolMassMin = 0.0;
958 }
959
960 // stand death
961 if (FolMassMax < 0.0)
962 {
963// for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
964// {
965 p->mCor += p->mSap;
966 p->mSap = 0.0;
967 p->mBud = 0.0;
968 p->mFrt = 0.0;
969// }
970 }
971}
static const double GROWMAX
Definition: pnet.h:75

◆ get_na_max_pnet()

size_t ldndc::PhysiologyPNET::get_na_max_pnet ( )
private

get_na_max_pnet

467{
468 size_t nb_ageclasses_total( 0.0);
469 for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
470 {
471 nb_ageclasses_total += (*vt)->nb_ageclasses();
472 }
473 return (int)ceil(sqrt((double)nb_ageclasses_total));
474}

◆ N_uptake()

void ldndc::PhysiologyPNET::N_uptake ( MoBiLE_Plant *  p,
double &  _n_uptake 
)
private

N_uptake

988{
989 bool physio(false);
990
991 // nitrogen demand
992 double nopt( 0.0);
993 double nact( 0.0);
994
995 double nFolOpt_vt( m_pf.optimum_nitrogen_content_foliage( p));
996 double nBudOpt_vt( m_pf.optimum_nitrogen_content_buds( p));
997
998 nopt += (nFolOpt_vt + p->mFrt * (*p)->NC_FINEROOTS_MAX()
999 + p->mSap * (*p)->NCSAPOPT() + nBudOpt_vt); // rg 11.06.10 (end of changes)
1000
1001 nact += (p->mFol * p->ncFol + p->mFrt * p->ncFrt
1002 + p->mSap * p->ncSap + p->mBud * p->ncBud);
1003
1004 double n_demand( cbm::bound_min(0.0, nopt - nact));
1005
1006 if (physio == false)
1007 {
1008 // standard DNDC procedure
1009
1010 // total nitrogen availability
1011 double n_avail( 0.0);
1012 for ( size_t sl = 0; sl < sl_.soil_layer_cnt(); ++sl)
1013 {
1014 if (sc_.depth_sl[sl] <= p->rooting_depth)
1015 {
1016 n_avail += (*p)->EXPL_NO3() * no3_sl[sl] +
1017 (*p)->EXPL_NH4() * nh4_sl[sl];
1018 // + (*p)->EXPL_DON() * don_sl[sl];
1019 }
1020 }
1021
1022 // nitrogen uptake
1023 double n_fact( 0.0);
1024 double const transpiration = wc_.accumulated_wateruptake_sl.sum() - accumulated_transpiration_old;
1025 accumulated_transpiration_old = wc_.accumulated_wateruptake_sl.sum();
1026 if (( n_avail > 0.0) && ( transpiration > 0.0))
1027 {
1028 n_fact = std::max(0.0, std::min(1.0, n_demand / n_avail));
1029 }
1030
1031 for ( size_t sl = 0; sl < sl_.soil_layer_cnt(); ++sl)
1032 {
1033 n_avail = no3_sl[sl] + nh4_sl[sl]; // don_sl[sl];
1034
1035 double layer_ava_no3( 0.0);
1036 double layer_ava_nh4( 0.0);
1037 if ( cbm::flt_less_equal( sc_.depth_sl[sl], p->rooting_depth) &&
1038 cbm::flt_greater_zero( n_avail) &&
1039 cbm::flt_greater( wc_.wc_sl[sl], wc_.wc_wp_sl[sl]))
1040 {
1041 layer_ava_no3 = (*p)->EXPL_NO3() * no3_sl[sl] * n_fact;
1042 layer_ava_nh4 = (*p)->EXPL_NH4() * nh4_sl[sl] * n_fact;
1043 }
1044
1045// for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
1046// {
1047 double const mfrt_veg_sl( ph_.mfrt_sl(m_veg, sl));
1048 if ( cbm::flt_greater_zero( mfrt_veg_sl))
1049 {
1050 double fm_vt( p->fFrt_sl[sl] * p->mFrt / mfrt_veg_sl);
1051 _n_uptake += layer_ava_no3 * fm_vt;
1052 _n_uptake += layer_ava_nh4 * fm_vt;
1053
1054 ph_.accumulated_no3_uptake_sl[sl] += layer_ava_no3 * fm_vt;
1055 ph_.accumulated_nh4_uptake_sl[sl] += layer_ava_nh4 * fm_vt;
1056 }
1057// }
1058 }
1059 }
1060 else
1061 {
1062 // physiological calculation (Arjan)
1063
1064 double no3_mg,nh4_mg,usNH4,usNO3 /*don_mg*/;
1065 double fact_t,fact_no3,fact_nh4 /*fact_don*/;
1066 double maxUptake_NO3,maxUptake_NH4,uptakeN /*maxUptake_DON*/;
1067
1068// for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
1069// {
1070 for ( size_t sl = 0; sl < sl_.soil_layer_cnt(); ++sl)
1071 {
1072 double no3_uptake_sl( 0.0);
1073 double nh4_uptake_sl( 0.0);
1074
1075 if ( cbm::flt_greater_zero( n_demand))
1076 {
1077 // temperature dependency of root uptake
1078 fact_t = m_pf.nitrogen_uptake_temperature_dependency(
1079 TMIN_NUP,
1080 TMAX_NUP,
1081 mc_.nd_temp_sl[sl]);
1082
1083 // nitrogen availability factor for nh4 and no3
1084 no3_mg = no3_sl[sl] * cbm::MG_IN_KG / (wc_.wc_sl[sl]* cbm::MM_IN_M * sc_.h_sl[sl]);
1085 nh4_mg = nh4_sl[sl] * cbm::MG_IN_KG / (wc_.wc_sl[sl]* cbm::MM_IN_M * sc_.h_sl[sl]);
1086 //don_mg = don_sl[sl] * cbm::MG_IN_KG / (wc_.wc_sl[sl]* cbm::MM_IN_M * sc_.h_sl[sl]);
1087 fact_no3 = no3_mg / (KM_NO3 + no3_mg);
1088 fact_nh4 = nh4_mg / (KM_NH4 + nh4_mg);
1089 //fact_don = don_mg / (KM_DON + don_mg);
1090
1091 // maximum specific uptake for nitrogen (rg 01.06.11 - no change in case CFCROPT = 0.0)
1092 usNH4 = (*p)->US_NH4() * (1.0-(*p)->CFCROPT()) + (*p)->US_NH4MYC() * (*p)->CFCROPT();
1093 usNO3 = (*p)->US_NO3() * (1.0-(*p)->CFCROPT()) + (*p)->US_NO3MYC() * (*p)->CFCROPT();
1094 //usDON = (*p)->US_DON() * (1.0-(*p)->CFCROPT()) + (*p)->US_DONMYC() * (*p)->CFCROPT();
1095
1096
1097 // maximum uptake rate
1098 maxUptake_NO3 = usNO3 * p->fFrt_sl[sl] * p->mFrt * fact_no3 * fact_t;
1099 maxUptake_NH4 = usNH4 * p->fFrt_sl[sl] * p->mFrt * fact_nh4 * fact_t;
1100 //maxUptake_DON = usDON * p->fFrt_sl[sl] * p->mFrt * fact_don * fact_t;
1101 uptakeN = std::min(n_demand, maxUptake_NO3 + maxUptake_NH4 /* + maxUptake_DON */ );
1102
1103 // soil layer specific uptake
1104 if ( uptakeN > 0.0)
1105 {
1106 no3_uptake_sl = std::min( no3_sl[sl], uptakeN * maxUptake_NO3 / (maxUptake_NO3 + maxUptake_NH4/* + maxUptake_DON */));
1107 nh4_uptake_sl = std::min( nh4_sl[sl], uptakeN * maxUptake_NH4 / (maxUptake_NO3 + maxUptake_NH4/* + maxUptake_DON */));
1108 //ph_.nd_don_uptake_sl[sl] = std::min( don_sl[sl], uptakeN * maxUptake_DON / (maxUptake_NO3 + maxUptake_NH4/* + maxUptake_DON */));
1109
1110 }
1111
1112 // updating N demand
1113 n_demand -= no3_uptake_sl + nh4_uptake_sl;
1114 }
1115
1116 // species specific uptake
1117 _n_uptake += no3_uptake_sl;
1118 _n_uptake += nh4_uptake_sl;
1119
1120 ph_.accumulated_no3_uptake_sl[sl] += no3_uptake_sl;
1121 ph_.accumulated_nh4_uptake_sl[sl] += nh4_uptake_sl;
1122
1123 nh4_sl[sl] -= nh4_uptake_sl;
1124 no3_sl[sl] -= no3_uptake_sl;
1125 }
1126// }
1127 }
1128}
static const double TMIN_NUP
Definition: pnet.h:39
static const double TMAX_NUP
Definition: pnet.h:42
static const double KM_NO3
Definition: pnet.h:45
static const double KM_NH4
Definition: pnet.h:48

◆ Phenology()

void ldndc::PhysiologyPNET::Phenology ( MoBiLE_Plant *  p,
int  _GrowthPhase,
double  _DWater 
)
private

Phenology

557{
558 if ( _GrowthPhase == 1)
559 {
560 if ( _DWater >= ((*p)->H2OREF_A()*0.5))
561 {
562 p->growing_degree_days += cbm::bound_min( 0.0, mc_.nd_airtemperature);
563 }
564
565 if (( p->growing_degree_days >= (*p)->GDDFOLSTART()) && ( lclock_ref().yearday() >= year_start_) && ( _DWater >= (*p)->H2OREF_A()*0.5))
566 {
567 p->dvsFlushOld = p->dvsFlush;
568 p->dvsFlush = cbm::bound(0.0, (p->growing_degree_days - (*p)->GDDFOLSTART()) / ((*p)->GDDFOLEND() - (*p)->GDDFOLSTART()), 1.0);
569 double const delta_dvsFlush( p->dvsFlush - p->dvsFlushOld);
570
571 if ( p->dvsFlushOld < 0.99)
572 {
573 mature = false;
574 }
575
576 double gFolC( std::min(BudCStart * delta_dvsFlush, BudC));
577 double const OldFolMass( FolMass);
578 FolMass += (gFolC / cbm::CCDM);
579 FolProdCMo = ((FolMass - OldFolMass) * cbm::CCDM);
580 FolGRespMo = (FolProdCMo * (*p)->FYIELD());
581 }
582 else
583 {
584 FolProdCMo = 0.0;
585 FolGRespMo = 0.0;
586 }
587 }
588 else if ( _GrowthPhase == 2)
589 {
590 FolLitSum = 0.0;
591
592 if (( PosCBalMass < FolMass) && mature
593 && (( lclock_ref().yearday() > (unsigned int)((*p)->SENESCSTART()+year_start_-1)) || ( lclock_ref().yearday() < year_start_))
594 && ( lclock_ref().yearday() != year_end_))
595 {
596 // rg 28.03.12 stand density already considered in annual update of foliage values
597 double const FolMassNew( std::max( PosCBalMass, FolMassMin));
598 if (FolMassNew < FolMass)
599 {
600 FolLitSum = FolMass - FolMassNew;
601 }
602
603 if ( FolMassNew > 0.000001)
604 {
605 FolMass = std::min( FolMass, FolMassNew);
606 }
607 else
608 {
609 FolMass = 0.0;
610 }
611 }
612
613 // This function accounts for foliage not lost during the year because carbon exchange was never negative
614 if ( lclock_ref().yearday() == year_end_)
615 {
616 FolLitSum = std::max( 0.0, FolMass - FolMassMin);
617 FolMass = std::min( FolMass, FolMassMin);
618 }
619 }
620 else
621 {
622 KLOGFATAL( "unknown growth phase");
623 }
624}

◆ Photosyn()

void ldndc::PhysiologyPNET::Photosyn ( MoBiLE_Plant *  p)
private

Photosyn

NOTE: This module is written for photosynthesis responses to monthly average climate values and should be used with care if applied on an daily time step. In PNET-N-DNDC the available light is reduced by: PAR_f = par / (Lai * 0.1 + 1.0); and the light response is changed to: LightEff = (1.0 - pow(2.666, (-Il / HALFSAT[p]))); If the Lai-impact on LightEff had not been limited to Lai 15, the responses would be equal at Lai app. 22. As it is, the photosynthesis in PNET is always larger (app. twice as high).

CHANGES:

  • The assumption of equal foliage biomass distribution is replaced by the explicit foliage distribution calculated in the vegetation structure module.
  • Also specific leaf area is taken form the annual calculation and distributed into the calculated layers. Thereby replacing the internally parameterized values.

AMAXA and AMAXB are specific for the PnET module. The parameters define the photosynthesis rate in dependence on nitrogen, assuming a linear relationship. AMAXA is a mere hypothetical photosynthesis rate at nitrogen content = 0. AMAXB is the increase of photosynthesis with increase of nitrogen percentage.

For parametrization, there are three different sources that have defined general values for broadleaved trees. These are Aber et al. 1995 (AMAXA: -46, AMAXB: 71.9, defined from investigations on Quercus rubra). Reich et al. 1995 (AMAXA 0.31*; AMAXB 5.45, defined from oak, maple and birch leaves). Kattge et al. 2009 (AMAXA: 5.73; AMAXB: 29.81 * 10.0 / SLAMIN, defined from a literature review with many different species).

The general practice now is to take the AMAXA from the newest and most elaborated dataset (Kattge et al. = 5.73) and adjust AMAXB according to the specific leaf area value at the top of the canopy. As long as there is no indication of SLAMIN, a standard of 18.1 is used (resulting from the assumption SLAMIN = 16.5).

663{
664 double Amax(0.0); // Foliage nitrogen-related photosynthesis rate (umol m-2 s-1)
665 double BaseFolResp(0.0);
666 double GrossAmax(0.0);
667 double LightEff(0.0);
668 double LayerResp(0.0);
669 double DTemp(0.0);
670 double Il(0.0); // Light extinction coefficient (-)
671 double LayerGrossPsnRate(0.0);
672 double LayerNetPsn(0.0);
673 double LayerGrossPsn_i(0.0);
674 double fFol_fl(0.0);
675 double laiEst(0.0); // estimate of LAI based on evenly distributed foliage biomass
676 double sla(0.0); // Specific foliage area (m2 kg-1)
677 double FolMass_fl(0.0); // total foliage in one layer (gDW m-2)
678
679 double ten9 = 1000000000.0;
680
681 double const Tnight( (mc_.nd_minimumairtemperature + mc_.nd_airtemperature) * 0.5);
682 double const Tday( (mc_.nd_maximumairtemperature + mc_.nd_airtemperature) * 0.5);
683
684
685 // relative temperature impact on photosynthesis, integrated over time
686 DTemp = (((*p)->PSNTMAX() - Tday) * (Tday - (*p)->PSNTMIN())) / ( pow((((*p)->PSNTMAX() - (*p)->PSNTMIN()) / 2.0), 2));
687
688 // considering frost effects empirically
689 if (mc_.nd_minimumairtemperature < 6.0 && DTemp > 0.0 && p->growing_degree_days >= (*p)->GDDFOLEND())
690 {
691 DTemp *= (1.0 - ((6.0 - mc_.nd_minimumairtemperature) / 6.0) * (1.0 / 30.0));
692 }
693
694 double const DayLength = ldndc::meteo::daylength( latitude, lclock_ref().yearday()) * cbm::SEC_IN_HR;
695 double const NightLength = cbm::SEC_IN_DAY - DayLength;
696
697 DTemp = std::max( 0.0, DTemp);
698
699 // relative vapour pressure deficit impact on photosynthesis
700 Dvpd = 1.0 - (*p)->DVPD1() * ( pow( mc_.nd_watervaporsaturationdeficit,(*p)->DVPD2()));
701
702 // Photosynthesis response to foliage N
703 Amax = std::max( 0.0, (*p)->AMAXA() + (*p)->AMAXB() * FolNCon);
704
705 // respiration in relation to photosynthesis
706 BaseFolResp = (*p)->BASEFOLRESPFRAC() * Amax;
707
708 // Gross photosynthesis
709 GrossAmax = (Amax * (*p)->AMAXFRAC() + BaseFolResp) * Dvpd * DTemp * DayLength * 12.0 / ten9;
710 if (GrossAmax < 0.0) GrossAmax = 0.0;
711 GrossAmax *= fFrost;
712
713 // Light respiration
714 DayResp = (BaseFolResp * (pow((*p)->RESPQ10(),((Tday - (*p)->PSNTOPT()) / 10.0)))
715 * DayLength * 12.0) / ten9;
716
717 // Dark respiration
718 NightResp = (BaseFolResp * (pow((*p)->RESPQ10(),((Tnight - (*p)->PSNTOPT()) / 10.0)))
719 * NightLength * 12.0) / ten9;
720
721 if ( FolMass > 0.0)
722 {
723 PosCBalMass = FolMass;
724
725 // initializations
726 laiEst = 0.0;
727 double fiCum( 0.0);
728 CanopyNetPsn = 0.0;
729 CanopyGrossPsn = 0.0;
730 size_t flMin( 0);
731
732 size_t fl_cnt( m_veg->canopy_layers_used());
733 for ( int fl = (int)(fl_cnt-1); fl != 0; --fl)
734 {
735 LayerGrossPsn[fl] = 0.0;
736
737 FolMass_fl = 0.0; // rg 25.11.10: prevent array mismatch by using FolMass for scaling
738 for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
739 {
740 FolMass_fl += p->fFol_fl[fl] * FolMass / double( m_veg->size());
741 }
742 if ( FolMass_fl > 0.0)
743 {
744 flMin = fl;
745 }
746 }
747
748 // number of steps calculated within each canopy layer
749 size_t ifl( cbm::bound_min( (size_t)1, (size_t)( 50.0 / double( fl_cnt - flMin))));
750
751
752 // calculations per canopy layer
753 for ( int fl = (int)(fl_cnt-1); fl >= (int)flMin; --fl)
754 {
755 // foliage mass and specific leaf area for each layer
756 /* using indicators from outside the module instead equal mass distribution */
757 fFol_fl = 0.0; sla = 0.0;
758 for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
759 {
760 fFol_fl += p->fFol_fl[fl];
761 sla += p->sla_fl[fl] / double( m_veg->size());
762 }
763
764 double fi(( fFol_fl * FolMass / double( m_veg->size())) / double(ifl));
765
766 // photosysnthesis and respiration in each layer fraction
767 for ( size_t i = 0; i < ifl; i++)
768 {
769 // cumlative leaf biomass and area
770 fiCum += fi;
771 laiEst += (fi * sla * cbm::KG_IN_G);
772
773 // photosynthesis
774
775 // photosynthetic active radiation (umol PAR m-2 s-1)
776 double const par( mc_.nd_shortwaveradiation_in * cbm::FPAR *
777 cbm::UMOL_IN_W * cbm::HR_IN_DAY
778 / ldndc::meteo::daylength( latitude, lclock_ref().yearday()));
779 Il = par * exp(-(*p)->EXT() * laiEst);
780 LightEff = std::max(0.0, (1.0 - exp(-Il * log(2.0) / (*p)->HALFSAT())));
781 LayerGrossPsnRate = GrossAmax * LightEff * DWater;
782 LayerGrossPsn_i = LayerGrossPsnRate * fi;
783 LayerGrossPsn[fl] += LayerGrossPsn_i;
784 LayerResp = (DayResp + NightResp) * fi;
785 LayerNetPsn = LayerGrossPsn_i - LayerResp;
786 CanopyNetPsn += LayerNetPsn;
787 CanopyGrossPsn += LayerGrossPsn_i;
788
789 if ((LayerNetPsn <= 0.0) && cbm::flt_equal( PosCBalMass, FolMass))
790 {
791 PosCBalMass = fiCum;
792 }
793
794 if (LightEff < LightEffMin)
795 {
796 LightEffMin = LightEff;
797 }
798 }
799 }
800
801 if ( DTemp > 0.0 && p->growing_degree_days > (*p)->GDDFOLEND() && lclock_ref().yearday() < (*p)->SENESCSTART()+year_start_-1)
802 {
803 PosCBalMassTot += ( PosCBalMass);
804 PosCBalMassIx += 1.0;
805 }
806 } // End if FolMass > 0.0
807 else
808 {
809 PosCBalMass = 0.0;
810 CanopyNetPsn = 0.0;
811 CanopyGrossPsn = 0.0;
812 DayResp = 0.0;
813 NightResp = 0.0;
814 }
815
816 CanopyGrossPsnMo = CanopyGrossPsn;
817 NetPsnMo = (CanopyGrossPsnMo - (DayResp + NightResp) * FolMass);
818}

◆ sleep()

lerr_t ldndc::PhysiologyPNET::sleep ( )
inline
Note
it is sadly wrong to assume that you can put me to sleep during times when multiple species exist: in general, i cannot handle the resulting slot layout. if you do, i also have no way of knowing .. so beware!
105{ return LDNDC_ERR_OK; }

◆ solve()

lerr_t ldndc::PhysiologyPNET::solve ( )
Note

species loops are in general incorrect: however, we only allow one single species with this module. if the species is not alive we return immediately.

@fixme The following two variables need to re-initialized after land use change. However, currently only arrays can be re-initialized in the treedyn module. Therefore, pnet can not be run for re-planting within the year after harvest (possible work-around: harvest at 31.12. of the year).

211{
212 if ( m_veg->size() == 0)
213 {
214 return LDNDC_ERR_OK;
215 }
216 else if ( m_veg->size() > 1)
217 {
218 KLOGERROR( "can't handle more than one species :( bye bye.");
219 return LDNDC_ERR_FAIL;
220 }
221
222 step_init();
223
224 for ( TreeIterator w = m_veg->groupbegin< species::wood >();
225 w != m_veg->groupend< species::wood >(); ++w)
226 {
227 MoBiLE_Plant *p = *w;
228
229 // variables init
230 FolLitSum = 0.0; // litterfall (gDW m-2)
231
232 tra = 0.0; // transpiration during a single time step (mm)
233 uptNAir = 0.0; // uptake of nitrogen by canopy processes (kg N m-2)
234
235 Dvpd = 0.0; // Gradient on vapour pressure deficit (?)
236 DayResp = 0.0; // Respiration during daytime (?)
237 NightResp = 0.0; // Respiration during nighttime (?)
238 CanopyNetPsn = 0.0; // Canopy net photosynthesis (?)
239 CanopyGrossPsn = 0.0; // Canopy gross photosynthesis (?)
240 FolProdCMo = 0.0; // foliage carbon production over the integration timestep (day or month) (?)
241 FolGRespMo = 0.0; // foliage growth respiration over the integration timestep (day or month) (?)
242 NetPsnMo = 0.0; // net photosynthesis over the integration timestep (day or month) (?)
243 CanopyGrossPsnMo = 0.0; // canopy gross photosynthesis over the integration timestep (day or month) (?)
244 RootCAddMo = 0.0; // fine root growth over the integration timestep (day or month)
245
246 // - allocation and mass variables
247 qwodfolact = 0.0; // relation between wood and foliage biomass at the start of the year
248 transfDWbs = 0.0; // annual mass transfer from reserves (buds) to (sap)wood (gC m-2)
249 transfDWsb = 0.0; // annual mass transfer from (sap)wood to reserves (buds) (gC m-2)
250 WoodC = 0.0; // living wood carbon (gC m-2)
251 BudC = 0.0; // reserve carbon for future foliage (gC m-2)
252 RootC = 0.0; // fine root carbon (gC m-2)
253 FolMass = 0.0; // foliage biomass (gDW m-2)
254 FolMassMax = 0.0; // maximum summer foliar biomass (gDW m-2) (not really a parameter)
255 FolMassMin = 0.0; // minimum summer foliar biomass (gDW m-2) (not really a parameter)
256 FolReten = 0.0; // foliage retention time (yr) (not really a parameter)
257
258 WoodMRespMo = 0.0; // Daily maintanance respiration of the wood
259 WoodGRespMo = 0.0; // Daily growth respiration of the wood
260 RootMRespMo = 0.0; // Daily maintanance root respiration
261 RootGRespMo = 0.0; // Daily root growth respiration
262 WoodProdCMo = 0.0; // Daily wood production
263 RootProdCMo = 0.0; // Daily carbon loss form the root system other than respiration;
264
265 for ( size_t fl = 0; fl < m_setup.canopylayers(); ++fl)
266 {
267 LayerGrossPsn[fl] = 0.0;
268 }
269
270 // other variables
271 fFrost = 1.0;
272 mature = false;
273
274 // - at the beginning of each year
275 YearlyReset( p);
276
277 static double const scale( cbm::G_IN_KG * cbm::CCDM);
278
279 // - at the beginning of each year
280 if (( lclock_ref().cycles() == 0u) || ( lclock_ref().yearday() == year_start_))
281 {
282 LightEffMin = 1.0;
283 PosCBalMass = 0.0;
284 PosCBalMassTot = 0.0;
285 PosCBalMassIx = 0.0;
286
287 // PnET specific re-initialization of sapwood
288 double const mSapIni( p->mSap);
289 double const mCorIni( p->mCor);
290 p->mSap = std::min( mSapIni + mCorIni, w->QWODFOLMIN() * ( p->mFol + p->mBud));
291 p->mCor += (mSapIni - p->mSap);
292
293 if ((p->mFol + p->mBud) > 0.0)
294 {
295 qwodfolact = p->mSap / (p->mFol + p->mBud);
296 }
297 else
298 {
299 qwodfolact = 0.0;
300 }
301
302 if (p->mCor > 0.0)
303 {
304 p->ncCor = (mCorIni * p->ncCor + (mSapIni - p->mSap) * p->ncSap) / p->mCor;
305 }
306 else
307 {
308 p->ncCor = 0.0;
309 }
310
311 // carbon pools in PnET initialized from biomass in different compartments
319 WoodCStart = (qwodfolact * p->mBud * scale); // virtual carbon pool for this year wood growth
320 BudCStart = (p->mBudStart * scale); // carbon for this year foliage growth
321 }
322
323 // transformation of units
324 RootC = (p->mFrt * scale);
325 WoodC = (p->mSap * scale);
326 BudC = (p->mBud * scale);
327 FolMass = (p->mFol * cbm::G_IN_KG);
328 FolMassMax = (p->mFolMax * cbm::G_IN_KG);
329 FolMassMin = (p->mFolMin * cbm::G_IN_KG);
330
331 if (( FolMassMax - FolMassMin) > 0.0)
332 {
333 FolReten = FolMassMax / (FolMassMax - FolMassMin);
334 }
335
336 balance_check(p, 1);
337
338 // variables from the start of the time step
339 mFolOld_vt[p->slot] = p->mFol;
340 mFrtOld_vt[p->slot] = p->mFrt;
341 mBudOld_vt[p->slot] = p->mBud;
342 mSapOld_vt[p->slot] = p->mSap;
343 mCorOld_vt[p->slot] = p->mCor;
344
345 // consideration of nitrogen supply for photosynthesis
347
348 transfDWbs = 0.0;
349 transfDWsb = 0.0;
350 mature = true;
351
352 // average relative water availability (drought) during an integration step (mm)
353 DroughtStress droughtstress;
354 droughtstress.fh2o_ref = w->H2OREF_A();
355 (*w)->f_h2o = m_state->get_fh2o( *w, m_sipar->CP_WP(), (*w)->f_area);
356 DWater = droughtstress.linear_threshold( (*w)->f_h2o);
357
358 Phenology( p, 1, DWater);
359
360 Photosyn( p);
361
362 AllocateMo( p);
363
364 Phenology( p, 2, DWater);
365
366 if ( lclock_ref().yearday() == year_end_)
367 {
368 AllocateYr( p);
369 PosCBalMass = FolMassMin * (*w)->f_area;
370 }
371
373
374 double uptNSoil( 0.0);
375 N_uptake( p, uptNSoil);
376
377 Update( p, uptNSoil, uptNAir);
378
379 wc_.accumulated_potentialtranspiration += potential_wood_transpiration(
380 sl_,
381 mc_.nd_watervaporsaturationdeficit,
382 cbm::sum( p->d_carbonuptake_fl, p->nb_foliagelayers()),
383 p->f_area,
384 w->WUECMAX(),
385 w->WUECMIN(),
386 sc_.h_sl,
387 wc_.wc_sl,
388 wc_.wc_wp_sl,
389 wc_.wc_fc_sl);
390
391 balance_check( p, 1);
392 }
393
394 lerr_t rc_vs = CalcVegStructure();
395 if ( rc_vs)
396 {
397 KLOGERROR("Simulation of vegetation structure not successful");
398 return LDNDC_ERR_FAIL;
399 }
400
401 step_out();
402
403 return LDNDC_ERR_OK;
404}
void TransformPhysiology(MoBiLE_Plant *)
Definition: pnet.cpp:1141
void AllocateMo(MoBiLE_Plant *)
void YearlyReset(MoBiLE_Plant *)
Definition: pnet.cpp:414
void N_uptake(MoBiLE_Plant *, double &)
Definition: pnet.cpp:986
void Update(MoBiLE_Plant *, double, double)
Definition: pnet.cpp:1399
void Phenology(MoBiLE_Plant *, int, double)
Definition: pnet.cpp:553
void AllocateYr(MoBiLE_Plant *)
Definition: pnet.cpp:870
void update_nitrogen_concentration(MoBiLE_Plant *)
Definition: pnet.cpp:528
void Photosyn(MoBiLE_Plant *)
Definition: pnet.cpp:662
double potential_wood_transpiration(soillayers::input_class_soillayers_t const &, double _vpd, double _carbon_uptake, double _f_area, double _wuecmax, double _wuecmin, lvector_t< double > _h_sl, lvector_t< double > _wc, lvector_t< double > _wc_min, lvector_t< double > _wc_max)
Returns potential transpiration in [m] for trees.
Definition: ld_transpiration.cpp:144

References ldndc::potential_wood_transpiration().

Here is the call graph for this function:

◆ TransformPhysiology()

void ldndc::PhysiologyPNET::TransformPhysiology ( MoBiLE_Plant *  p)
private

TransformPhysiology

1142{
1143 double mSum(0.0),maintResp(0.0),fResp(0.0);
1144 double dcTot(0.0),dcSapC(0.0),dcBudC(0.0),dcFrtC(0.0),dBudY(0.0),dSapY(0.0);
1145 double fm_vt(0.0),fwood(0.0);
1146
1147 // distribution of net carbon gain into wood and foliage reserves for next year
1148 /* root growth is already calculated as a fraction of gross gain, so it has to
1149 be substracted first */
1150 dcTot = (CanopyGrossPsnMo
1151 - (CanopyGrossPsnMo - NetPsnMo) - WoodMRespMo - RootMRespMo
1152 - FolGRespMo - WoodGRespMo - RootGRespMo);
1153
1154 // Reduction of respiration if total growth is negative
1155 /* This has been newly introduced to account for the large biomass increases
1156 particular in Eucalyptus plantation under severe drought */
1157// double massLoss = 0.0;
1158 if (dcTot < 0.0)
1159 {
1160 maintResp = WoodMRespMo + RootMRespMo + (CanopyGrossPsnMo - NetPsnMo);
1161 if (maintResp > 0.0)
1162 {
1163 fResp = (maintResp + dcTot) / maintResp;
1164 }
1165
1166 if (fResp < 0.0)
1167 {
1168 fResp = 0.0;
1169// massLoss -= dcTot;
1170 }
1171
1172 WoodMRespMo *= fResp;
1173 RootMRespMo *= fResp;
1174
1175 NetPsnMo = CanopyGrossPsnMo - fResp * (CanopyGrossPsnMo - NetPsnMo);
1176 dcTot = (CanopyGrossPsnMo
1177 - (CanopyGrossPsnMo - NetPsnMo) - WoodMRespMo - RootMRespMo
1178 - FolGRespMo - WoodGRespMo - RootGRespMo);
1179
1180 // if we have negative growth carbon is taken from sap wood
1181 // here we make sure that negative carbon growth is not exceeding available carbon
1182 if (dcTot < 0.0 && std::abs(dcTot) > p->mSap)
1183 {
1184 FolGRespMo = 0.0;
1185 WoodMRespMo = 0.0;
1186 WoodGRespMo = 0.0;
1187 RootMRespMo = 0.0;
1188 RootGRespMo = 0.0;
1189
1190 dcTot = (CanopyGrossPsnMo
1191 - (CanopyGrossPsnMo - NetPsnMo) - WoodMRespMo - RootMRespMo
1192 - FolGRespMo - WoodGRespMo - RootGRespMo);
1193 }
1194 }
1195
1196 // wood and reserve change
1197 /* If growth is negative, it is taken only from free available tissue. This is
1198 assumed to be the whole labile reserve pool and 20 percent of woody tissue. */
1199 if (dcTot > 0.0 && p->dvsFlushOld >= 1.0) // positive gain, after flushing
1200 {
1201 fwood = (*p)->QWODFOLMIN() / (1.0 + (*p)->QWODFOLMIN());
1202 }
1203 else if (BudC + WoodC * 0.2 > 0.0 && p->dvsFlushOld >= 1.0) // negative gain, after flushing
1204 {
1205 fwood = WoodC * 0.2 / (BudC + WoodC * 0.2);
1206 }
1207 else // before flushing
1208 {
1209 fwood = 1.0;
1210 }
1211
1212 dcFrtC = std::max(0.0, std::min(dcTot, RootCAddMo)); // root growth has priority, but is limited by availability
1213 dcSapC = (dcTot-dcFrtC) * fwood;
1214 dcBudC = (dcTot-dcFrtC) * (1.0 - fwood);
1215
1216 mSum = 0.0;
1217// for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
1218// {
1219 mSum += (p->mFol + p->mFrt + p->mSap + p->mBud);
1220 p->mFolMax = 0.0;
1221 p->mFolMin = 0.0;
1222// }
1223
1224// for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
1225// {
1226 // biomass fraction (and unit conversion)
1227 if (mSum > 0.0)
1228 {
1229 fm_vt = ((p->mFol + p->mFrt + p->mSap + p->mBud) / mSum);
1230
1231 if (fm_vt > 1.0)
1232 {
1233 fm_vt = 1.0;
1234 }
1235
1236 fm_vt *= cbm::KG_IN_G;
1237 }
1238 else
1239 {
1240 fm_vt = 0.0;
1241 }
1242
1243 // growth
1244 p->d_dcFol = FolProdCMo * fm_vt; // shift from buds to foliage
1245 mBudLoss_vt[p->slot] = p->d_dcFol / cbm::CCDM;
1246 p->d_dcFrt = dcFrtC * fm_vt; // growth from assimilated carbon
1247 p->d_dcSap = dcSapC * fm_vt;
1248 p->d_dcBud = dcBudC * fm_vt;
1249
1250 // respiration
1251 double const sap_wood_frac( sap_wood_fraction( p->lai_pot, p->qsfa, p->height_at_canopy_start,
1252 p->height_max, p->rooting_depth));
1253 double const dc_sum( std::max( 0.0, p->d_dcFol)
1254 + std::max( 0.0, p->d_dcFrt)
1255 + std::max( 0.0, p->d_dcSap)
1256 + std::max( 0.0, p->d_dcBud));
1257 double const dc_below( std::max( 0.0, p->d_dcFrt) + std::max( 0.0, p->d_dcSap * sap_wood_frac));
1258
1259 p->d_rFol = (CanopyGrossPsnMo - NetPsnMo) * fm_vt;
1260 p->d_rFrt = RootMRespMo * fm_vt;
1261 p->d_rSap = WoodMRespMo * fm_vt;
1262 p->d_rSapBelow = (p->d_rSap * sap_wood_frac);
1263 p->d_rBud = 0.0;
1264 p->d_rRes = p->d_rFol + p->d_rFrt + p->d_rSap + p->d_rBud;
1265 p->d_rTra = 0.0;
1266 p->d_rGro = (WoodGRespMo + RootGRespMo + FolGRespMo) * fm_vt;
1267 p->d_rGroBelow = ((dc_sum > 0.0) ? p->d_rGro * dc_below / dc_sum : 0.0);
1268
1269 // senescence
1270 p->d_sFol = FolLitSum * fm_vt;
1271 for ( size_t sl = 0; sl < sl_.soil_layer_cnt(); ++sl)
1272 {
1273 p->d_sFrt_sl[sl] += RootProdCMo / cbm::CCDM * fm_vt * p->fFrt_sl[sl];
1274 }
1275
1276 mSapLoss_vt[p->slot] = 0.0;
1277 if ((( p->mSap * 0.99999)) > ((p->mFol + p->mBud) * (*p)->QWODFOLMIN()))
1278 {
1279 mSapLoss_vt[p->slot] = std::min( SRATEMAX * p->mSap, p->mSap - (p->mFol + p->mBud) * (*p)->QWODFOLMIN());
1280 if (mSapLoss_vt[p->slot] < 0.0)
1281 {
1282 mSapLoss_vt[p->slot] = 0.0;
1283 }
1284 }
1285 p->d_sWoodAbove = mSapLoss_vt[p->slot] * p->f_branch;
1286
1287 // senescence due to annual shift between biomasses
1288 dBudY = transfDWsb * fm_vt;
1289 dSapY = transfDWbs * fm_vt;
1290
1291 // rg 19.11.10 security
1292 if (dBudY < 0.0)
1293 {
1294 dBudY = std::max(-1 * (p->mCor + mSapLoss_vt[p->slot] * (1.0-p->f_branch)), transfDWsb * fm_vt);
1295 }
1296 if (dSapY < 0.0)
1297 {
1298 dSapY = std::max(-1 * (p->mSap + (p->d_dcSap / cbm::CCDM - mSapLoss_vt[p->slot])), transfDWbs * fm_vt);
1299 }
1300
1301 // biomass update
1302 p->mFol = FolMass * fm_vt;
1303 p->mFrt += ((p->d_dcFrt / cbm::CCDM - cbm::sum( p->d_sFrt_sl, sl_.soil_layer_cnt())));
1304 p->mSap += ((p->d_dcSap / cbm::CCDM - mSapLoss_vt[p->slot]) - dBudY);
1305 p->mBud += ((p->d_dcBud / cbm::CCDM - mBudLoss_vt[p->slot]) + dBudY - dSapY);
1306 p->mCor += (mSapLoss_vt[p->slot] * (1.0-p->f_branch) + dSapY);
1307
1308 // foliage biomass in age classes // rg 08.06.10 (new)
1309 p->mFol_na[0] += p->d_dcFol;
1310 if ( get_na_max_pnet() > 0)
1311 {
1312 p->mFol_na[get_na_max_pnet()-1] -= p->d_sFol;
1313 for ( size_t na = 0; na < get_na_max_pnet(); ++na)
1314 {
1315 if ( p->mFol_na[na] < 0.0)
1316 {
1317 p->mFol_na[na] = 0.0;
1318 }
1319 }
1320 }
1321
1322 // control values for next year
1323 p->mBudStart = p->mBud;
1324 p->mFolMax += (FolMassMax * fm_vt);
1325 p->mFolMin += (FolMassMin * fm_vt);
1326
1327 /* Note that if one of the following equations applies, the carbon balance is not closed any more */
1328 if ( p->mFrt < 0.0)
1329 {
1330 if (( p->mFrt > 1e-7) || (-1 * p->mFrt > 1e-7))
1331 {
1332 KLOGWARN(" mFrt-C-leak in PNET [day=",lclock_ref().now().to_string(),",mFrt=",p->mFrt,"]");
1333 }
1334 p->mFrt = 0.0;
1335 }
1336
1337 if ( p->mSap < 0.0)
1338 {
1339 if (p->mSap > 1e-7 || -1 * p->mSap > 1e-7)
1340 {
1341 KLOGWARN(" mSap-C-leak in PNET [day=",lclock_ref().now().to_string(),",mSap=",p->mSap,"]");
1342 }
1343 p->mSap = 0.0;
1344 }
1345
1346 if ( p->mBud < 0.0)
1347 {
1348 if (p->mBud > 1e-7 || -1 * p->mBud > 1e-7)
1349 {
1350 KLOGWARN(" mBud-C-leak in PNET [day=",lclock_ref().now().to_string(),",mBud=",p->mBud,"]");
1351 }
1352 p->mBud = 0.0;
1353 }
1354
1355 if (( p->growing_degree_days >= (*p)->GDDFOLSTART()) && ( p->dEmerg == 0))
1356 {
1357 p->dEmerg = lclock_ref().yearday();
1358 }
1359
1360
1361 if ( mature
1362 && ( p->f_area > 0.0)
1363 && (lclock_ref().yearday() > (unsigned int)((*p)->SENESCSTART()+year_start_-1)
1364 || lclock_ref().yearday() < year_start_)
1365 && lclock_ref().yearday() != lclock_ref().days_in_year())
1366 {
1367 // rg 08.06.10 (no senescence at the last day of the year)
1368 p->dvsMort = 1.0 - (FolMass / p->f_area - FolMassMin) / ( std::max(FolMass / p->f_area, FolMassMax) - FolMassMin);
1369 }
1370 else
1371 {
1372 p->dvsMort = 0.0;
1373 }
1374
1375
1376 if (p->dvsMort < 0.0)
1377 {
1378 p->dvsMort = 0.0;
1379 }
1380 else if (p->dvsMort > 1.0)
1381 {
1382 p->dvsMort = 1.0;
1383 }
1384
1385 size_t fl_cnt( p->nb_foliagelayers());
1386 m_pf.update_foliage_layer_biomass_and_lai( p, fl_cnt, 0.001);
1387
1388 // fraction of photosynthesis is used with smaller-timestep models
1389 for ( size_t fl = 0; fl < fl_cnt; ++fl)
1390 {
1391 p->d_carbonuptake_fl[fl] = LayerGrossPsn[fl] * cbm::KG_IN_G;
1392 }
1393// }
1394}
static const double SRATEMAX
Definition: pnet.h:51
size_t get_na_max_pnet()
Definition: pnet.cpp:466
double LDNDC_API sap_wood_fraction(double _lai_max, double _qsfa, double _height_min, double _height_max, double _depth)
sap_wood_fraction The calculation of the fraction of sapwood in coarse roots is based on the idealize...
Definition: ld_sapwoodfraction.cpp:15

References ldndc::sap_wood_fraction().

Here is the call graph for this function:

◆ Update()

void ldndc::PhysiologyPNET::Update ( MoBiLE_Plant *  _vt,
double  _upt1_vt,
double  _upt2_vt 
)
private

Update

1402{
1403 // component demand
1404 double nFolOpt_vt( m_pf.optimum_nitrogen_content_foliage( _vt));
1405 double nBudOpt_vt( m_pf.optimum_nitrogen_content_buds( _vt));
1406
1407 // optimum nitrogen contents
1408 double ncFolOpt( (_vt->mFol > 0.0) ? nFolOpt_vt/_vt->mFol : 0.0);
1409 double ncBudOpt( (_vt->mBud > 0.0) ? nBudOpt_vt/_vt->mBud : 0.0);
1410
1411
1412 // component demand of compartments
1413 double demFol( std::max(0.0, (ncFolOpt - _vt->ncFol) * mFolOld_vt[_vt]
1414 + ncFolOpt * (_vt->mFol - mFolOld_vt[_vt])));
1415 double demFrt( std::max(0.0, ((*_vt)->NC_FINEROOTS_MAX() - _vt->ncFrt) * mFrtOld_vt[_vt]
1416 + (*_vt)->NC_FINEROOTS_MAX() * (_vt->mFrt - mFrtOld_vt[_vt])));
1417 double demSap( std::max(0.0, ((*_vt)->NCSAPOPT() - _vt->ncSap) * mSapOld_vt[_vt]
1418 + (*_vt)->NCSAPOPT() * (_vt->mSap - mSapOld_vt[_vt])));
1419 double demBud( std::max(0.0, (ncBudOpt - _vt->ncBud) * mBudOld_vt[_vt]
1420 + ncBudOpt * (_vt->mBud - mBudOld_vt[_vt])));
1421 double demTot = (demFol + demFrt + demSap + demBud);
1422
1423
1424 // accounting for biomass shifts at the end of a year
1425 double transfBud( (transfDWbs > 0.0) ?
1426 std::max(0.0,
1427 (mBudOld_vt[_vt] - mBudLoss_vt[_vt->slot] + _vt->d_dcBud/cbm::CCDM
1428 - _vt->mBud) * _vt->ncBud)
1429 : 0.0);
1430
1431 double transfSap( (transfDWsb > 0.0) ?
1432 std::max(0.0,
1433 (mSapOld_vt[_vt] - mSapLoss_vt[_vt->slot] + _vt->d_dcSap/cbm::CCDM
1434 - _vt->mSap) * _vt->ncSap)
1435 : 0.0);
1436
1437
1438 // element losses // rg 08.12.10 simplified
1439 // the minimum function is needed to guarantee mass conservation.
1440 // there is no more nitrogen loss allowed than present at the beginning of the time step
1441 double xlossFol( std::min(_vt->d_sFol, mFolOld_vt[_vt]) * _vt->ncFol);
1442 double xlossFrt( std::min(cbm::sum(_vt->d_sFrt_sl, sl_.soil_layer_cnt()), mFrtOld_vt[_vt]) * _vt->ncFrt);
1443 double xlossBud( std::min(mBudLoss_vt[_vt->slot], mBudOld_vt[_vt]) * _vt->ncBud);
1444 double xlossSap( std::min(mSapLoss_vt[_vt->slot], mSapOld_vt[_vt]) * _vt->ncSap);
1445
1446
1447 // nitrogen uptake
1448 double xdist_up( _upt1_vt + _upt2_vt);
1449
1450
1451 // nitrogen retention
1452 // note: elements are principally neither retranslocated from sapwood
1453 // when turned into corewood nor from fine roots (Gordon and Jackson 2000)
1454 _vt->d_n_retention = ( xlossFol * (*_vt)->FRET_N());
1455
1456
1457 // total nitrogen availability for distribution
1458 double xdistTot( xdist_up + _vt->d_n_retention);
1459
1460
1461 // correct nitrogen retention in case exceeding demand
1462 if (demTot < xdistTot)
1463 {
1464 // reduced nitrogen retention
1465 double const xret_reduced( std::max(demTot - xdist_up, 0.0));
1466 _vt->d_n_retention = xret_reduced;
1467 xdistTot = (xdist_up + xret_reduced);
1468 }
1469
1470
1471 // components from uptake and retranslocation to be distributed between plant tissues
1472 // by internal translocation from sapwood to corewood
1473 double xdistCor( xlossSap * (1.0 - _vt->f_branch));
1474
1475
1476 // by internal translocation from buds to foliage
1477 double xlitBud( 0.0);
1478 double xfolBud( xlossBud);
1479 if (xlossBud < demFol)
1480 {
1481 demFol -= xlossBud;
1482 demTot -= xlossBud;
1483 }
1484 else
1485 {
1486 xlitBud = xlossBud;
1487 xfolBud = 0.0;
1488 }
1489
1490
1491 // nitrogen distribution to components
1492 double xdistFol( 0.0);
1493 double xdistFrt( 0.0);
1494 double xdistSap( 0.0);
1495 double xdistBud( 0.0);
1496 double xrest( 0.0);
1497
1498 if (demTot > xdistTot) // any available element is taken up
1499 {
1500 xdistFol = xdistTot * demFol / demTot;
1501 xdistFrt = xdistTot * demFrt / demTot;
1502 xdistSap = xdistTot * demSap / demTot;
1503 xdistBud = xdistTot * demBud / demTot;
1504 xrest = 0.0;
1505 }
1506 else if (xdistTot > 0.0) // every element demand is saturated
1507 {
1508 xdistFol = demFol;
1509 xdistFrt = demFrt;
1510 xdistSap = demSap;
1511 xdistBud = demBud;
1512 xrest = xdistTot - demTot;
1513 }
1514
1515
1516 // not needed elements are distributed to litter
1517 if ( xrest > 0.0)
1518 {
1519 double const m_tot( mFolOld_vt[_vt]+mBudOld_vt[_vt]+mFrtOld_vt[_vt]+mSapOld_vt[_vt]);
1520 if (cbm::flt_equal_zero( m_tot))
1521 {
1522 KLOGWARN("No more living biomass for species ", _vt->name());
1523 }
1524 else
1525 {
1526 _vt->d_nLitFol += (xrest * mFolOld_vt[_vt] / m_tot);
1527 _vt->d_nLitWoodAbove += (xrest * mSapOld_vt[_vt] / m_tot);
1528 _vt->d_nLitBud += (xrest * mBudOld_vt[_vt] / m_tot);
1529 for ( size_t sl = 0; sl < sl_.soil_layer_cnt(); ++sl)
1530 {
1531 _vt->d_nLitFrt_sl[sl] += (xrest * mFrtOld_vt[_vt] / m_tot * _vt->fFrt_sl[sl]);
1532 }
1533 }
1534 }
1535
1536 _vt->d_nLitBud += xlitBud;
1537 _vt->d_nLitFol += (xlossFol - _vt->d_n_retention);
1538 _vt->d_nLitWoodAbove += (xlossSap * _vt->f_branch);
1539 for ( size_t sl = 0; sl < sl_.soil_layer_cnt(); ++sl)
1540 {
1541 _vt->d_nLitFrt_sl[sl] += xlossFrt * _vt->fFrt_sl[sl];
1542 }
1543
1544 // new concentrations
1545 if (_vt->mFol > 1.0e-9)
1546 {
1547 _vt->ncFol = std::max(0., mFolOld_vt[_vt] * _vt->ncFol + xdistFol + xfolBud - xlossFol) / _vt->mFol;
1548 }
1549 else
1550 {
1551 _vt->ncFol = 0.0;
1552 }
1553
1554 if (_vt->mBud > 1.0e-9)
1555 {
1556 _vt->ncBud = std::max(0., mBudOld_vt[_vt] * _vt->ncBud + xdistBud - xlossBud - transfBud + transfSap) / _vt->mBud;
1557 }
1558 else
1559 {
1560 _vt->ncBud = 0.0;
1561 }
1562
1563 if (_vt->mFrt > 1.0e-9)
1564 {
1565 _vt->ncFrt = std::max(0., mFrtOld_vt[_vt] * _vt->ncFrt + xdistFrt - xlossFrt) / _vt->mFrt;
1566 }
1567 else
1568 {
1569 _vt->ncFrt = 0.0;
1570 }
1571
1572 if (_vt->mCor > 1.0e-9)
1573 {
1574 _vt->ncCor = std::max(0., mCorOld_vt[_vt] * _vt->ncCor + xdistCor + transfBud) / _vt->mCor;
1575 }
1576 else
1577 {
1578 _vt->ncCor = 0.0;
1579 }
1580
1581 if (_vt->mSap > 1.0e-9)
1582 {
1583 _vt->ncSap = std::max(0., mSapOld_vt[_vt] * _vt->ncSap + xdistSap - xlossSap - transfSap) / _vt->mSap;
1584 /*
1585 // additional nitrogen allocation from core wood to reach minimum nitrogen concentration
1586 if (_vt->ncSap < cMin && _vt->mSap > 0.0 && _vt->mCor > 0.0){ // rg 11.06.10 (avoid divide by zero problem)
1587 transfer = Min(_vt->mCor * _vt->ncCor, _vt->mSap * (cMin - _vt->ncSap));
1588 _vt->ncSap = (_vt->mSap * _vt->ncSap + transfer) / _vt->mSap;
1589 _vt->ncCor = (_vt->mCor * _vt->ncCor - transfer) / _vt->mCor;
1590 }
1591 */
1592 }
1593 else
1594 {
1595 _vt->ncSap = 0.0;
1596 }
1597}

◆ update_nitrogen_concentration()

void ldndc::PhysiologyPNET::update_nitrogen_concentration ( MoBiLE_Plant *  p)
private

update_nitrogen_concentration

529{
530 double const nFolOpt( m_pf.optimum_nitrogen_content_foliage( p));
531
532 FolNCon = 0.0;
533 double FolNConOpt( 0.0);
534 if (p->mFol > 0.0)
535 {
536 FolNCon += (p->ncFol * cbm::PRO_IN_GG * p->mFol / p->mFol);
537 FolNConOpt += (nFolOpt / p->mFol * cbm::PRO_IN_GG * p->mFol / p->mFol);
538 }
539
540 FolNCon = std::min( FolNCon, FolNConOpt);
541}

◆ YearlyReset()

void ldndc::PhysiologyPNET::YearlyReset ( MoBiLE_Plant *  p)
private

Resets:

  • Growing degree days
415{
416 if ( lclock_ref().yearday() != year_start_)
417 {
418 return;
419 }
420
421 p->dEmerg = 0;
422 p->growing_degree_days = 0.0;
423 p->dvsFlush = 0.0;
424 p->dvsFlushOld = 0.0;
425 p->dvsWood = 0.0;
426
427 /* rg 24.02.11
428 * (re-initialization of biomass according foliage longvity based on the expected maximum foliage mFolMax)
429 * rg 20.01.12
430 * (shift from calculating only once to calculating every year start
431 * (account for new initialization after regeneration)
432 * rg 28.03.12
433 * (considering new nitrogen concentrations after adjustment of biomasses, including an adjustment factor)
434 */
435 if ( get_na_max_pnet() > 1)
436 {
437 double mFolNew = p->mFolMax * (1.0 - 1.0 / double(get_na_max_pnet()));
438 double mBudNew = mFolNew / double(get_na_max_pnet()-1);//assuming no flush of foliage
439 double mSapNew = p->mSap - (mFolNew + mBudNew - p->mFol - p->mBud);
440 double dnfol = (p->mFol - mFolNew) * p->ncFol;
441 double ncFolNew = (p->mFol * p->ncFol - dnfol) / mFolNew;
442 double ncBudNew = (p->mBud * p->ncBud + dnfol) / mBudNew;
443 double ncSapNew = (p->mSap * p->ncSap) / mSapNew;
444
445 p->ncFol = ncFolNew;
446 p->ncBud = ncBudNew;
447 p->ncSap = ncSapNew;
448 p->mFol = mFolNew;
449 p->mBud = mBudNew;
450 p->mSap = mSapNew;
451 p->mBudStart = p->mBud;
452 p->mFolMin = p->mFol;
453 }
454
455 double const mfol_va( p->mFolMax - p->mFolMin);
456 p->mFol_na[0] = 0.0;
457 for ( size_t na = 1; na < get_na_max_pnet(); ++na)
458 {
459 p->mFol_na[na] = mfol_va;
460 }
461}

Member Data Documentation

◆ C

double const ldndc::PhysiologyPNET::C = 0.02
staticprivate

parameter from frost hardiness function

◆ GROWMAX

double const ldndc::PhysiologyPNET::GROWMAX = 0.95
staticprivate

fraction of uncovered area that can be reoccuppied in one year

◆ KM_NH4

double const ldndc::PhysiologyPNET::KM_NH4 = 1.0
staticprivate

??

◆ KM_NO3

double const ldndc::PhysiologyPNET::KM_NO3 = 1.0
staticprivate

??

◆ NUPTAIR

double const ldndc::PhysiologyPNET::NUPTAIR = 0.0
staticprivate

fraction of nitrogen from total deposition that is taken up by the canopy

◆ SRATEMAX

double const ldndc::PhysiologyPNET::SRATEMAX = 0.01
staticprivate

??

◆ TMAX_NUP

double const ldndc::PhysiologyPNET::TMAX_NUP = 25.0
staticprivate

Optimum temperature where maximum N uptake occurs (Gessler et al. 1998)

◆ TMIN_NUP

double const ldndc::PhysiologyPNET::TMIN_NUP = 6.0
staticprivate

Minimum temperature where N uptake is possible (estimated from Gessler et al. 1998, corroborated by Alvarez-Uria and Koerner 2007)