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!!!

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

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

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

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

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

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

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

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

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

References AllocateMo(), AllocateYr(), N_uptake(), Phenology(), Photosyn(), ldndc::potential_transpiration_soil_atmosphere_limited(), 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

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

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

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

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

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

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