LandscapeDNDC  1.37.0
ldndc::PhysiologyPNET Class Reference

Vegetation model PNET. More...

Inherits ldndc::MBE_LegacyModel.

Public Member Functions

lerr_t solve ()
 
lerr_t sleep ()
 

Private Member Functions

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

Static Private Attributes

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

Detailed Description

Vegetation model PNET.

Author
Ruediger Grote

Member Function Documentation

◆ AllocateMo()

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

AllocateMo

Referenced by Photosyn().

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

References ldndc::PhysiologyPlaMox::m_pf.

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

◆ get_na_max_pnet()

size_t ldndc::PhysiologyPNET::get_na_max_pnet ( )
private

get_na_max_pnet

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

◆ N_uptake()

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

N_uptake

References ldndc::PhysiologyPlaMox::m_pf.

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

◆ Phenology()

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

Phenology

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

◆ Photosyn()

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

Photosyn

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

CHANGES:

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

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

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

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

References AllocateMo().

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

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

References ldndc::potential_wood_transpiration().

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

◆ TransformPhysiology()

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

TransformPhysiology

References ldndc::PhysiologyPlaMox::m_pf, and ldndc::sap_wood_fraction().

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

◆ Update()

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

Update

References ldndc::PhysiologyPlaMox::m_pf.

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

◆ update_nitrogen_concentration()

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

update_nitrogen_concentration

References ldndc::PhysiologyPlaMox::m_pf.

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

◆ YearlyReset()

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

Resets:

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

Member Data Documentation

◆ C

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

parameter from frost hardiness function

◆ GROWMAX

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

fraction of uncovered area that can be reoccuppied in one year

◆ KM_NH4

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

??

◆ KM_NO3

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

??

◆ NUPTAIR

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

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

◆ SRATEMAX

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

??

◆ TMAX_NUP

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

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

◆ TMIN_NUP

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

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