LandscapeDNDC 1.37.0
Loading...
Searching...
No Matches
ldndc::PhysiologyPNET Class Reference

Vegetation model PNET. More...

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

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

References AllocateYr(), get_na_max_pnet(), N_uptake(), TransformPhysiology(), and Update().

Referenced by solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AllocateYr()

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

AllocateYr

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

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

References AllocateYr(), and GROWMAX.

Referenced by AllocateMo(), AllocateYr(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_na_max_pnet()

size_t ldndc::PhysiologyPNET::get_na_max_pnet ( )
private

get_na_max_pnet

454{
455 size_t nb_ageclasses_total( 0.0);
456 for ( PlantIterator vt = m_veg->begin(); vt != m_veg->end(); ++vt)
457 {
458 nb_ageclasses_total += (*vt)->nb_ageclasses();
459 }
460 return (int)ceil(sqrt((double)nb_ageclasses_total));
461}

References get_na_max_pnet().

Referenced by AllocateMo(), get_na_max_pnet(), TransformPhysiology(), and YearlyReset().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ N_uptake()

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

N_uptake

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

References KM_NH4, KM_NO3, N_uptake(), TMAX_NUP, and TMIN_NUP.

Referenced by AllocateMo(), N_uptake(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Phenology()

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

Phenology

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

References Phenology().

Referenced by Phenology(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ 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).

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

References Photosyn().

Referenced by Photosyn(), and solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ 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).

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

Referenced by solve().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ TransformPhysiology()

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

TransformPhysiology

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

Referenced by AllocateMo(), solve(), and TransformPhysiology().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Update()

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

Update

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

References Update().

Referenced by AllocateMo(), solve(), and Update().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ update_nitrogen_concentration()

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

update_nitrogen_concentration

516{
517 double const nFolOpt( m_pf.optimum_nitrogen_content_foliage( p));
518
519 FolNCon = 0.0;
520 double FolNConOpt( 0.0);
521 if (p->mFol > 0.0)
522 {
523 FolNCon += (p->ncFol * cbm::PRO_IN_GG * p->mFol / p->mFol);
524 FolNConOpt += (nFolOpt / p->mFol * cbm::PRO_IN_GG * p->mFol / p->mFol);
525 }
526
527 FolNCon = std::min( FolNCon, FolNConOpt);
528}

References update_nitrogen_concentration().

Referenced by solve(), and update_nitrogen_concentration().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ YearlyReset()

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

Resets:

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

References get_na_max_pnet(), and YearlyReset().

Referenced by solve(), and YearlyReset().

Here is the call graph for this function:
Here is the caller graph for this function:

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

Referenced by AllocateYr().

◆ KM_NH4

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

??

Referenced by N_uptake().

◆ KM_NO3

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

??

Referenced by N_uptake().

◆ 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

??

Referenced by TransformPhysiology().

◆ TMAX_NUP

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

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

Referenced by N_uptake().

◆ 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)

Referenced by N_uptake().