canexch.cpp 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253
  1. ///////////////////////////////////////////////////////////////////////////////////////
  2. /// \file canexch.cpp
  3. /// \brief The canopy exchange module
  4. ///
  5. /// Vegetation-atmosphere exchange of H2O and CO2 via
  6. /// production, respiration and evapotranspiration.
  7. ///
  8. /// \author Ben Smith
  9. /// $Date: 2013-10-10 10:20:33 +0200 (Thu, 10 Oct 2013) $
  10. ///
  11. ///////////////////////////////////////////////////////////////////////////////////////
  12. // WHAT SHOULD THIS FILE CONTAIN?
  13. // Module source code files should contain, in this order:
  14. // (1) a "#include" directive naming the framework header file. The framework header
  15. // file should define all classes used as arguments to functions in the present
  16. // module. It may also include declarations of global functions, constants and
  17. // types, accessible throughout the model code;
  18. // (2) other #includes, including header files for other modules accessed by the
  19. // present one;
  20. // (3) type definitions, constants and file scope global variables for use within
  21. // the present module only;
  22. // (4) declarations of functions defined in this file, if needed;
  23. // (5) definitions of all functions. Functions that are to be accessible to other
  24. // modules or to the calling framework should be declared in the module header
  25. // file.
  26. //
  27. // PORTING MODULES BETWEEN FRAMEWORKS:
  28. // Modules should be structured so as to be fully portable between models (frameworks).
  29. // When porting between frameworks, the only change required should normally be in the
  30. // "#include" directive referring to the framework header file.
  31. #include "config.h"
  32. #include "canexch.h"
  33. #include "driver.h"
  34. #include "q10.h"
  35. #include "bvoc.h"
  36. #include "ncompete.h"
  37. #include <assert.h>
  38. // Anonymous namespace for variables with file scope
  39. namespace {
  40. /// leaf nitrogen (kgN/kgC) not associated with photosynthesis
  41. /** (value given by Haxeltine & Prentice 1996) */
  42. const double N0 = 7.15 * 0.001;
  43. // Lookup tables for parameters with Q10 temperature responses
  44. /// lookup table for Q10 temperature response of CO2/O2 specificity ratio
  45. LookupQ10 lookup_tau(0.57, 2600.0);
  46. /// lookup table for Q10 temperature response of Michaelis constant for O2
  47. LookupQ10 lookup_ko(1.2, 3.0e4);
  48. /// lookup table for Q10 temperature response of Michaelis constant for CO2
  49. LookupQ10 lookup_kc(2.1, 30.0);
  50. }
  51. ///////////////////////////////////////////////////////////////////////////////////////
  52. // INTERCEPTION
  53. /// Daily loss of water and energy through evaporation of rain or snow intercepted by the vegetation canopy
  54. /** Gerten et al. (2004) Eq 2-4.
  55. */
  56. void interception(Patch& patch,Climate& climate) {
  57. // Calculates daily loss of water and energy through evaporation of rainfall
  58. // intercepted by the vegetation canopy
  59. double scap; // canopy storage capacity (mm)
  60. double fwet; // fraction of day that canopy is wet (Kergoat 1996)
  61. double pet; // potential evapotranspiration (mm)
  62. pet=climate.eet*PRIESTLEY_TAYLOR;
  63. // Retrieve Vegetation object
  64. Vegetation& vegetation=patch.vegetation;
  65. patch.intercep=0.0;
  66. if (date.day == 0) {
  67. for (int d = 0; d < date.year_length(); d++) {
  68. patch.dintercep[d] = 0.0;
  69. }
  70. }
  71. // Loop through individuals ...
  72. vegetation.firstobj();
  73. while (vegetation.isobj) {
  74. Individual& indiv=vegetation.getobj();
  75. // For this individual ...
  76. if (!negligible(pet)) {
  77. if (indiv.alive) {
  78. // Storage capacity for precipitation by canopy (point scale)
  79. scap=climate.prec*min(indiv.lai_indiv_today()*indiv.pft.intc,0.999);
  80. // Fraction of day that canopy remains wet
  81. fwet=min(scap/pet,patch.fpc_rescale);
  82. // Calculate interception by this individual, and increment patch total
  83. indiv.intercep=fwet*pet*indiv.fpc;
  84. patch.intercep+=indiv.intercep;
  85. }
  86. else {
  87. indiv.intercep=0.0;
  88. }
  89. }
  90. else {
  91. indiv.intercep=0.0;
  92. patch.intercep=0.0;
  93. }
  94. // ... on to next individual
  95. vegetation.nextobj();
  96. }
  97. // Calculate net EET for vegetated parts of patch (deducting loss to interception)
  98. patch.eet_net_veg=max(climate.eet-patch.intercep,0.0);
  99. // Interception accounting for patch
  100. patch.aintercep+=patch.intercep;
  101. patch.mintercep[date.month]+=patch.intercep;
  102. patch.dintercep[date.day] = patch.intercep;
  103. }
  104. ///////////////////////////////////////////////////////////////////////////////////////
  105. // FPAR
  106. // Internal function - not intended to be called by framework
  107. void fpar(Patch& patch) {
  108. // DESCRIPTION
  109. // Calculates daily fraction of incoming PAR (FPAR) taken up by individuals in a
  110. // particular patch over their projective areas, given current leaf phenological
  111. // status. Calculates PAR and FPAR at top of grass canopy (individual and cohort
  112. // modes). Calculates fpar assuming leaf-on (phen=1) for all vegetation.
  113. //
  114. // Note: In order to compensate for the additional computational cost of
  115. // calculating fpar_leafon in cohort/individual mode, the grain of the
  116. // integration of FPAR through the canopy has been increased from 1 to 2 m
  117. //
  118. // NEW ASSUMPTIONS CONCERNING FPC AND FPAR (Ben Smith 2002-02-20)
  119. // FPAR = average individual fraction of PAR absorbed on patch basis today,
  120. // including effect of current leaf phenology (this differs from previous
  121. // versions of LPJ-GUESS in which FPAR was on an FPC basis)
  122. // FPC = PFT population (population mode), cohort (cohort mode) or individual
  123. // (individual mode) fractional projective cover as a fraction of patch area
  124. // (in population mode, corresponds to LPJF variable fpc_grid). Updated
  125. // annually based on leaf-out LAI (see function allometry in growth module).
  126. // (FPC was previously equal to summed crown area as a fraction of patch
  127. // area in cohort/individual mode)
  128. //
  129. // Population mode: FPAR on patch (grid cell) area basis assumed to be equal to fpc
  130. // under full leaf cover; i.e.
  131. // (1) fpar = fpc*phen
  132. // (2) fpar_leafon = fpc
  133. //
  134. // Individual and cohort modes: FPAR calculated assuming trees shade themselves
  135. // and all individuals below them according to the Lambert-Beer law (Prentice
  136. // et al 1993, Eqn 27; Monsi & Saeki 1953):
  137. // (3) fpar = integral [0-tree height] exp ( -k * plai(z) )
  138. // where
  139. // k = extinction coefficient;
  140. // plai(z) = summed leaf-area index for leaves of all individuals, above
  141. // canopy depth z, taking account of current phenological status
  142. const double VSTEP=2.0; // width of vertical layers for canopy-area integration (m)
  143. const double PHEN_GROWINGSEASON=0.5;
  144. // minimum expected vegetation leaf-on fraction for growing season
  145. double plai; // cumulative leaf-area index (LAI) for patch (m2 leaf/m2 ground)
  146. double plai_leafon;
  147. // cumulative LAI for patch assuming full leaf cover for all individuals
  148. double plai_layer; // summed LAI by layer for patch
  149. double plai_leafon_layer;
  150. // summed LAI by layer for patch assuming full leaf cover for all individuals
  151. double plai_grass; // summed LAI for grasses
  152. double plai_leafon_grass;
  153. // summed LAI for grasses assuming full leaf cover for all individuals
  154. double flai; // fraction of total grass LAI represented by a particular grass
  155. double fpar_layer_top; // FPAR by layer
  156. double fpar_leafon_layer_top;
  157. // FPAR by layer assuming full leaf cover for all individuals
  158. double fpar_layer_bottom;
  159. double fpar_leafon_layer_bottom;
  160. double fpar_grass; // FPAR at top of grass canopy
  161. double fpar_leafon_grass;
  162. // FPAR at top of grass canopy assuming full leaf cover for all individuals
  163. double fpar_ff; // FPAR at forest floor (beneath grass canopy)
  164. double fpar_leafon_ff;
  165. // FPAR at forest floor assuming full leaf cover for all individuals
  166. double frac;
  167. // vertical fraction of layer occupied by crown cylinder(s) of a particular
  168. // individual or cohort
  169. double atoh; // term in calculating LAI sum for a given layer
  170. double height_veg; // maximum vegetation height (m)
  171. int toplayer; // number of vertical layers of width VSTEP in vegetation (minus 1)
  172. int layer; // layer number (0=lowest)
  173. double lowbound; // lower bound of current layer (m)
  174. double highbound; // upper bound of current layer (m)
  175. double fpar_min; // minimum FPAR required for grass growth
  176. double par_grass; // PAR reaching top of grass canopy (J/m2/day)
  177. double phen_veg; // LAI-weighted mean fractional leaf-out for vegetation
  178. //variables needed for "S�kes" FPAR scheme
  179. double fpar_uptake_layer;
  180. double fpar_uptake_leafon_layer;
  181. // Obtain reference to Vegetation object
  182. Vegetation& vegetation=patch.vegetation;
  183. // And to Climate object
  184. const Climate& climate = patch.get_climate();
  185. if (vegmode==POPULATION) {
  186. // POPULATION MODE
  187. // Loop through individuals
  188. vegetation.firstobj();
  189. while (vegetation.isobj) {
  190. Individual& indiv=vegetation.getobj();
  191. // For this individual ...
  192. indiv.fpar=indiv.fpc_today(); // Eqn 1
  193. indiv.fpar_leafon=indiv.fpc * indiv.growingseason(); // Eqn 2
  194. vegetation.nextobj(); // ... on to next individual
  195. }
  196. }
  197. else {
  198. // INDIVIDUAL OR COHORT MODE
  199. // Initialise individual FPAR, find maximum height of vegetation, calculate
  200. // individual LAI given current phenology, calculate summed LAI for grasses
  201. plai=0.0;
  202. plai_leafon=0.0;
  203. plai_grass=0.0;
  204. plai_leafon_grass=0.0;
  205. phen_veg=0.0;
  206. height_veg=0.0;
  207. // Loop through individuals
  208. vegetation.firstobj();
  209. while (vegetation.isobj) {
  210. Individual& indiv=vegetation.getobj();
  211. // For this individual ...
  212. if (indiv.growingseason()) {
  213. indiv.fpar=0.0;
  214. indiv.fpar_leafon=0.0;
  215. if (indiv.height>height_veg) height_veg=indiv.height;
  216. plai_leafon+=indiv.lai;
  217. if (indiv.pft.lifeform==GRASS) {
  218. plai_leafon_grass+=indiv.lai;
  219. plai_grass+=indiv.lai_today();
  220. }
  221. // Accumulate LAI-weighted sum of individual leaf-out fractions
  222. phen_veg+=indiv.lai_today();
  223. }
  224. vegetation.nextobj(); // ... on to next individual
  225. }
  226. // Calculate LAI-weighted mean leaf-out fraction for vegetation
  227. // guess2008 - bugfix - was: if (!negligible(plai))
  228. if (!negligible(plai_leafon))
  229. phen_veg/=plai_leafon;
  230. else
  231. phen_veg=1.0;
  232. // Calculate number of layers (minus 1) from ground surface to top of canopy
  233. toplayer=(int)(height_veg/VSTEP-0.0001);
  234. // Calculate FPAR by integration from the top of the canopy (Eqn 2)
  235. plai=0.0;
  236. plai_leafon=0.0;
  237. // Set FPAR for bottom of layer above (initially 1 at top of canopy)
  238. fpar_layer_bottom=1.0;
  239. fpar_leafon_layer_bottom=1.0;
  240. for (layer=toplayer;layer>=0;layer--) {
  241. lowbound=(double)layer*VSTEP;
  242. highbound=lowbound+VSTEP;
  243. // FPAR at top of this layer = FPAR at bottom of layer above
  244. fpar_layer_top=fpar_layer_bottom;
  245. fpar_leafon_layer_top=fpar_leafon_layer_bottom;
  246. plai_layer=0.0;
  247. plai_leafon_layer=0.0;
  248. // Loop through individuals
  249. vegetation.firstobj();
  250. while (vegetation.isobj) {
  251. Individual& indiv=vegetation.getobj();
  252. // For this individual ...
  253. if (indiv.pft.lifeform==TREE) {
  254. if (indiv.height>lowbound && indiv.boleht<highbound &&
  255. !negligible(indiv.height-indiv.boleht)) {
  256. // Calculate vertical fraction of current layer occupied by
  257. // crown cylinders of this cohort
  258. frac=1.0;
  259. if (indiv.height<highbound)
  260. frac-=(highbound-indiv.height)/VSTEP;
  261. if (indiv.boleht>lowbound)
  262. frac-=(indiv.boleht-lowbound)/VSTEP;
  263. // Calculate summed LAI of this cohort in this layer
  264. atoh=indiv.lai/(indiv.height-indiv.boleht);
  265. indiv.lai_leafon_layer=atoh*frac*VSTEP;
  266. plai_layer+=indiv.lai_leafon_layer*indiv.phen;
  267. plai_leafon_layer+=indiv.lai_leafon_layer;
  268. }
  269. else {
  270. indiv.lai_layer=0.0;
  271. indiv.lai_leafon_layer=0.0;
  272. }
  273. }
  274. // ... on to next individual
  275. vegetation.nextobj();
  276. }
  277. // Update cumulative LAI for this layer and above
  278. plai+=plai_layer;
  279. plai_leafon+=plai_leafon_layer;
  280. // Calculate FPAR at bottom of this layer
  281. // Eqn 27, Prentice et al 1993
  282. fpar_layer_bottom = lambertbeer(plai);
  283. fpar_leafon_layer_bottom = lambertbeer(plai_leafon);
  284. // Total PAR uptake in this layer
  285. fpar_uptake_layer=fpar_layer_top-fpar_layer_bottom;
  286. fpar_uptake_leafon_layer=fpar_leafon_layer_top-fpar_leafon_layer_bottom;
  287. // Partition PAR for this layer among trees,
  288. vegetation.firstobj();
  289. while (vegetation.isobj) {
  290. Individual& indiv=vegetation.getobj();
  291. // For this individual ...
  292. if (indiv.pft.lifeform==TREE) {
  293. if (!negligible(plai_leafon_layer))
  294. // FPAR partitioned according to the relative amount
  295. // of leaf area in this layer for this individual
  296. indiv.fpar_leafon+=fpar_uptake_leafon_layer*
  297. indiv.lai_leafon_layer/plai_leafon_layer;
  298. if (!negligible(plai_layer))
  299. indiv.fpar+=fpar_uptake_layer*
  300. (indiv.lai_leafon_layer*indiv.phen)/plai_layer;
  301. }
  302. // ... on to next individual
  303. vegetation.nextobj();
  304. }
  305. }
  306. // FPAR reaching grass canopy
  307. fpar_grass = lambertbeer(plai);
  308. fpar_leafon_grass = lambertbeer(plai_leafon);
  309. // Add grass LAI to calculate PAR reaching forest floor
  310. // BLARP: Order changed Ben 050301 to overcome optimisation bug in pgCC
  311. //plai+=plai_grass;
  312. fpar_ff = lambertbeer(plai+plai_grass);
  313. plai+=plai_grass;
  314. // Save this
  315. patch.fpar_ff=fpar_ff;
  316. plai_leafon+=plai_leafon_grass;
  317. fpar_leafon_ff = lambertbeer(plai_leafon);
  318. // FPAR for grass PFTs is difference between relative PAR at top of grass canopy
  319. // canopy and at forest floor, or lower if FPAR at forest floor below threshold
  320. // for grass growth. PAR reaching the grass canopy is partitioned among grasses
  321. // in proportion to their LAI (a somewhat simplified assumption)
  322. // Loop through individuals
  323. double fpar_tree_total=0.0;
  324. vegetation.firstobj();
  325. while (vegetation.isobj) {
  326. Individual& indiv=vegetation.getobj();
  327. // For this individual ...
  328. if (indiv.pft.lifeform==GRASS) {
  329. // Calculate minimum FPAR for growth of this grass
  330. // Fraction of total grass LAI represented by this grass
  331. if (!negligible(plai_grass))
  332. flai=indiv.lai_today()/plai_grass;
  333. else
  334. flai=1.0;
  335. if (!negligible(climate.par))
  336. fpar_min=min(indiv.pft.parff_min/climate.par,1.0);
  337. else
  338. fpar_min=1.0;
  339. indiv.fpar=max(0.0,fpar_grass*flai-max(fpar_ff*flai,fpar_min));
  340. // Repeat assuming full leaf cover for all individuals
  341. if (!negligible(plai_leafon_grass))
  342. flai=indiv.lai/plai_leafon_grass;
  343. else
  344. flai=1.0;
  345. indiv.fpar_leafon=max(0.0,fpar_leafon_grass*flai-
  346. max(fpar_leafon_ff*flai,fpar_min));
  347. }
  348. if (indiv.pft.lifeform==TREE) fpar_tree_total+=indiv.fpar;
  349. vegetation.nextobj();
  350. }
  351. // Save grass canopy FPAR and update mean growing season grass canopy PAR
  352. // Growing season defined here as days when mean vegetation leaf-on fraction
  353. // exceeds 50% and we're in the light half of the year (daylength >= 11).
  354. //
  355. // The daylength condition was added because sites with evergreens can have
  356. // a mean vegetation leaf-on fraction over 50% even during polar night.
  357. // 11 hours was chosen because some sites never reach exactly 12 hours, the
  358. // exact limit shouldn't matter much.
  359. patch.fpar_grass=fpar_grass;
  360. par_grass=fpar_grass*climate.par;
  361. if (date.day==0) {
  362. patch.par_grass_mean=0.0;
  363. patch.nday_growingseason=0;
  364. }
  365. if (phen_veg>PHEN_GROWINGSEASON && patch.get_climate().daylength >= 11.0) {
  366. patch.par_grass_mean+=par_grass;
  367. patch.nday_growingseason++;
  368. }
  369. // Convert from sum to mean on last day of year
  370. if (date.islastday && date.islastmonth && patch.nday_growingseason) {
  371. patch.par_grass_mean/=(double)patch.nday_growingseason;
  372. }
  373. }
  374. }
  375. double alphaa(const Pft& pft) {
  376. if (!ECEARTH) {
  377. // trunk
  378. if (pft.phenology == CROPGREEN)
  379. return ifnlim ? ALPHAA_CROP_NLIM : ALPHAA_CROP;
  380. else
  381. return ifnlim ? ALPHAA_NLIM : ALPHAA;
  382. }
  383. else {
  384. // EC-Earth - increased _NLIM values
  385. if (pft.phenology == CROPGREEN)
  386. return ifnlim ? ALPHAA_CROP_NLIM_ECE : ALPHAA_CROP;
  387. else
  388. return ifnlim ? ALPHAA_NLIM_ECE : ALPHAA;
  389. }
  390. }
  391. /// Non-water stressed rubisco capacity, with or without nitrogen limitation
  392. void vmax(double b, double c1, double c2, double apar, double tscal,
  393. double daylength, double temp, double nactive, bool ifnlimvmax, double& vm, double& vmaxnlim, double& nactive_opt) {
  394. // Calculation of non-water-stressed rubisco capacity assuming leaf nitrogen not
  395. // limiting (Eqn 11, Haxeltine & Prentice 1996a)
  396. // Calculation of sigma is based on Eqn 12 (same source)
  397. double s = 24.0 / daylength * b;
  398. double sigma = sqrt(max(0., 1. - (c2 - s) / (c2 - THETA * s)));
  399. vm = 1 / b * CMASS * CQ * c1 / c2 * tscal * apar *
  400. (2. * THETA * s * (1. - sigma) - s + c2 * sigma);
  401. // Calculate nitrogen-limited Vmax for current leaf nitrogen
  402. // Haxeltine & Prentice 1996b Eqn 28
  403. const double M = 25.0; // corresponds to parameter p in Eqn 28, Haxeltine & Prentice 1996b
  404. // Conversion factor in calculation of leaf nitrogen: includes conversion of:
  405. // - Vm from gC/m2/day to umolC/m2/sec
  406. // - nitrogen from mg/m2 to kg/m2
  407. double CN = 1.0 / (3600 * daylength * CMASS);
  408. double tfac = exp(-0.0693 * (temp - 25.0));
  409. double vm_max = nactive / (M * CN * tfac);
  410. // Calculate optimal leaf nitrogen based on [potential] Vmax (Eqn 28 Haxeltine & Prentice 1996b)
  411. nactive_opt = M * vm * CN * tfac;
  412. if (vm > vm_max && ifnlimvmax) {
  413. vmaxnlim = vm_max / vm; // Save vmax nitrogen limitation
  414. vm = vm_max;
  415. }
  416. else {
  417. vmaxnlim = 1.0;
  418. }
  419. }
  420. /// Total daily gross photosynthesis
  421. /** Calculation of total daily gross photosynthesis and leaf-level net daytime
  422. * photosynthesis given degree of stomatal closure (as parameter lambda).
  423. * Includes implicit scaling from leaf to plant projective area basis.
  424. * Adapted from Farquhar & von Caemmerer (1982) photosynthesis model, as simplified
  425. * by Collatz et al (1991), Collatz et al (1992), Haxeltine & Prentice (1996a,b)
  426. * and Sitch et al. (2000).
  427. *
  428. * To calculate vmax call w/ daily averages of temperature and par.
  429. * Vmax is to be calculated daily and only with lambda == lambda_max.
  430. * lambda values greater than lambda_max are forbidden.
  431. * In sub-daily mode daylength should be 24 h, to obtain values in daily units.
  432. *
  433. * INPUT PARAMETERS
  434. *
  435. * \param co2 atmospheric ambient CO2 concentration (ppmv)
  436. * \param temp mean air temperature today (deg C)
  437. * \param par total daily photosynthetically-active radiation today (J/m2/day)
  438. * \param daylength day length, must equal 24 in diurnal mode (h)
  439. * \param fpar fraction of PAR absorbed by foliage
  440. * \param lambda ratio of intercellular to ambient partial pressure of CO2
  441. * \param pft Pft object containing the following public members:
  442. * - pathway biochemical pathway for photosynthesis (C3 or C4)
  443. * - pstemp_min approximate low temperature limit for photosynthesis (deg C)
  444. * - pstemp_low approximate lower range of temperature optimum for
  445. * photosynthesis (deg C)
  446. * - pstemp_high approximate upper range of temperature optimum for photosynthesis
  447. * (deg C)
  448. * - pstemp_max maximum temperature limit for photosynthesis (deg C)
  449. * - lambda_max non-water-stressed ratio of intercellular to ambient CO2 pp
  450. * \param nactive nitrogen available for photosynthesis
  451. * \param ifnlimvmax whether nitrogen should limit Vmax
  452. * \param vm pre-calculated value of Vmax for this stand for this day if
  453. * available, otherwise calculated
  454. *
  455. * OUTPUT PARAMETERS
  456. *
  457. * \param result see documentation of PhotosynthesisResult struct
  458. */
  459. void photosynthesis(double co2, double temp, double par, double daylength,
  460. double fpar, double lambda, const Pft& pft,
  461. double nactive, bool ifnlimvmax,
  462. PhotosynthesisResult& result, double vm) {
  463. // NOTE: This function is identical to LPJF subroutine "photosynthesis" except for
  464. // the formulation of low-temperature inhibition coefficient tscal (tstress; LPJF).
  465. // The function adopted here draws down metabolic activity in approximately the
  466. // temperature range pstemp_min-pstemp_low but does not affect photosynthesis
  467. // at high temperatures.
  468. // HISTORY
  469. // Ben Smith 18/1/2001: Tested in comparison to LPJF subroutine "photosynthesis":
  470. // function showed identical behaviour except at temperatures >= c. 35 deg C where
  471. // LPJF temperature inhibition function results in lower photosynthesis.
  472. // Make sure that only two alternative modes are possible:
  473. // * daily non-water stressed (forces Vmax calculation)
  474. // * with pre-calculated Vmax (sub-daily and water-stressed)
  475. assert(vm >= 0 || lambda == pft.lambda_max);
  476. assert(lambda <= pft.lambda_max);
  477. const double PATMOS = 1e5; // atmospheric pressure (Pa)
  478. // No photosynthesis during polar night, outside of temperature range or no RuBisCO activity
  479. if (negligible(daylength) || negligible(fpar) || temp > pft.pstemp_max || temp < pft.pstemp_min || !vm) {
  480. result.clear();
  481. return;
  482. }
  483. // Scale fractional PAR absorption at plant projective area level (FPAR) to
  484. // fractional absorption at leaf level (APAR)
  485. // Eqn 4, Haxeltine & Prentice 1996a
  486. double apar = par * fpar * alphaa(pft);
  487. double b, c1, c2;
  488. // Calculate temperature-inhibition coefficient
  489. // This function (tscal) is mathematically identical to function tstress in LPJF.
  490. // In contrast to earlier versions of modular LPJ and LPJ-GUESS, it includes both
  491. // high- and low-temperature inhibition.
  492. double k1 = (pft.pstemp_min+pft.pstemp_low) / 2.0;
  493. double tscal = (1. - .01*exp(4.6/(pft.pstemp_max-pft.pstemp_high)*(temp-pft.pstemp_high)))/
  494. (1.0+exp((k1-temp)/(k1-pft.pstemp_min)*4.6));
  495. if (pft.pathway == C3) { // C3 photosynthesis
  496. // Calculate CO2 compensation point (partial pressure)
  497. // Eqn 8, Haxeltine & Prentice 1996a
  498. double gammastar = PO2 / 2.0 / lookup_tau[temp];
  499. // Intercellular partial pressure of CO2 given stomatal opening (Pa)
  500. // Eqn 7, Haxeltine & Prentice 1996a
  501. double pi_co2 = lambda * co2 * PATMOS * CO2_CONV;
  502. // Calculation of C1_C3, Eqn 4, Haxeltine & Prentice 1996a
  503. // High-temperature inhibition modelled by suppression of LUE by decreased
  504. // relative affinity of rubisco for CO2 with increasing temperature (Table 3.7,
  505. // Larcher 1983)
  506. // Notes: - there is an error in Eqn 4, Haxeltine & Prentice 1996a (missing
  507. // 2.0* in denominator) which is fixed here (see Eqn A2, Collatz
  508. // et al 1991)
  509. // - the explicit low temperature inhibition function has been removed
  510. // and replaced by a temperature-dependent upper limit on V_m, see
  511. // below
  512. // - the reduction in maximum photosynthesis due to leaf age (phi_c)
  513. // has been removed
  514. // - alpha_a, accounting for reduction in PAR utilisation efficiency
  515. // from the leaf to ecosystem level, appears in the calculation of
  516. // apar (above) instead of here
  517. // - C_mass, the atomic weight of carbon, appears in the calculation
  518. // of V_m instead of here
  519. c1 = (pi_co2 - gammastar) / (pi_co2 + 2.0 * gammastar) * ALPHA_C3;
  520. // Calculation of C2_C3, Eqn 6, Haxeltine & Prentice 1996a
  521. c2 = (pi_co2 - gammastar) / (pi_co2 + lookup_kc[temp] * (1.0 + PO2/lookup_ko[temp]));
  522. b = BC3;
  523. }
  524. else { // C4 photosynthesis
  525. // Calculation of C1_C4 given actual pi (lambda)
  526. // C1_C4 incorporates term accounting for effect of intercellular CO2
  527. // concentration on photosynthesis (Eqn 14, 16, Haxeltine & Prentice 1996a)
  528. c1 = min(lambda/LAMBDA_SC4, 1.0) * ALPHA_C4;
  529. c2 = 1;
  530. b = BC4;
  531. }
  532. if (vm < 0) {
  533. // Calculation of non-water-stressed rubisco capacity (Eqn 11, Haxeltine & Prentice 1996a)
  534. vmax(b, c1, c2, apar, tscal, daylength, temp, nactive, ifnlimvmax, result.vm, result.vmaxnlim, result.nactive_opt);
  535. }
  536. else {
  537. result.vm = vm; // reuse existing Vmax
  538. }
  539. // Calculation of daily leaf respiration
  540. // Eqn 10, Haxeltine & Prentice 1996a
  541. result.rd_g = result.vm * b;
  542. // PAR-limited photosynthesis rate (gC/m2/h)
  543. // Eqn 3, Haxeltine & Prentice 1996a
  544. result.je = c1 * tscal * apar * CMASS * CQ / daylength;
  545. // Rubisco-activity limited photosynthesis rate (gC/m2/h)
  546. // Eqn 5, Haxeltine & Prentice 1996a
  547. double jc = c2 * result.vm / 24.0;
  548. // Calculation of daily gross photosynthesis
  549. // Eqn 2, Haxeltine & Prentice 1996a
  550. // Notes: - there is an error in Eqn 2, Haxeltine & Prentice 1996a (missing
  551. // theta in 4*theta*je*jc term) which is fixed here
  552. result.agd_g = (result.je + jc - sqrt((result.je + jc) * (result.je + jc) - 4.0 * THETA * result.je * jc)) /
  553. (2.0 * THETA) * daylength;
  554. // Leaf-level net daytime photosynthesis (gC/m2/day)
  555. // Based on Eqn 19, Haxeltine & Prentice 1996a
  556. double adt = result.agd_g - daylength / 24.0 * result.rd_g;
  557. // Convert to CO2 diffusion units (mm/m2/day) using ideal gas law
  558. result.adtmm = adt / CMASS * 8.314 * (temp + K2degC) / PATMOS * 1e3;
  559. }
  560. /// Calculate value for canopy conductance component associated with photosynthesis (mm/s)
  561. /** Eqn 21, Haxeltine & Prentice 1996
  562. * includes conversion of daylight from hours to seconds
  563. */
  564. inline double gpterm(double adtmm, double co2, double lambda, double daylength) {
  565. if (adtmm <= 0) {
  566. return 0;
  567. }
  568. return 1.6 / CO2_CONV / 3600 * adtmm / co2 / (1 - lambda) / daylength;
  569. }
  570. /// Pre-calculate Vmax and no-stress assimilation and canopy conductance
  571. /**
  572. * Vmax is calculated on a daily scale (w/ daily averages of temperature and par)
  573. * Subdaily values calculated if needed
  574. */
  575. void photosynthesis_nostress(Patch& patch, Climate& climate) {
  576. // If this is the first patch, calculate no-stress assimilation for
  577. // each Standpft, assuming FPAR=1. This is then later used in
  578. // forest_floor_conditions.
  579. if (!patch.id) {
  580. for (int p=0; p<npft; p++) {
  581. Standpft& spft = patch.stand.pft[p];
  582. if (spft.active) {
  583. // Call photosynthesis assuming stomates fully open (lambda = lambda_max)
  584. photosynthesis(climate.co2, climate.temp, climate.par, climate.daylength,
  585. 1.0, spft.pft.lambda_max, spft.pft, 1.0, false, spft.photosynthesis, -1);
  586. }
  587. }
  588. }
  589. // Pre-calculation of no-stress assimilation for each individual
  590. Vegetation& vegetation = patch.vegetation;
  591. vegetation.firstobj();
  592. while (vegetation.isobj) {
  593. Individual& indiv = vegetation.getobj();
  594. Pft& pft = indiv.pft;
  595. // Individual photosynthesis with no nitrogen limitation
  596. photosynthesis(climate.co2, climate.temp, climate.par, climate.daylength,
  597. indiv.fpar, pft.lambda_max, pft,
  598. 1.0, false,
  599. indiv.photosynthesis,
  600. -1);
  601. indiv.gpterm = gpterm(indiv.photosynthesis.adtmm, climate.co2, pft.lambda_max, climate.daylength);
  602. if (date.diurnal()) {
  603. indiv.gpterms.assign(date.subdaily, 0);
  604. PhotosynthesisResult res;
  605. indiv.phots.assign(date.subdaily, res);
  606. for (int i=0; i<date.subdaily; i++) {
  607. PhotosynthesisResult& result = indiv.phots[i];
  608. photosynthesis(climate.co2, climate.temps[i], climate.pars[i], 24,
  609. indiv.fpar, pft.lambda_max, pft,
  610. 1.0, false,
  611. result,
  612. indiv.photosynthesis.vm);
  613. indiv.gpterms[i] = gpterm(result.adtmm, climate.co2, pft.lambda_max, 24);
  614. }
  615. }
  616. vegetation.nextobj();
  617. }
  618. }
  619. /// Calculates individual fnuptake based on surface of fine root
  620. /** Calculates individual fraction nitrogen uptake based on surface of fine root
  621. * Roots are cone formed with height == radius.
  622. * V = PI * r^3 / 3
  623. * A = (2^1/2 + 1) * PI * r^2
  624. * -> A = const * cmass_root^2/3
  625. */
  626. double nitrogen_uptake_strength(const Individual& indiv) {
  627. return pow(max(0.0, indiv.cmass_root_today()) * indiv.pft.nupscoeff * indiv.cton_status / indiv.densindiv, 2.0 / 3.0) * indiv.densindiv;
  628. }
  629. /// Individual nitrogen uptake fraction
  630. /** Determining individual nitrogen uptake as a fraction of its nitrogen demand.
  631. *
  632. * \see ncompete
  633. *
  634. * Function nitrogen_uptake_strength() determines how good individuals are at
  635. * acquiring nitrogen.
  636. */
  637. void fnuptake(Vegetation& vegetation, double nmass_avail) {
  638. // Create vector describing the individuals to ncompete()
  639. std::vector<NCompetingIndividual> individuals(vegetation.nobj);
  640. for (unsigned int i = 0; i < vegetation.nobj; i++) {
  641. individuals[i].ndemand = vegetation[i].ndemand;
  642. individuals[i].strength = nitrogen_uptake_strength(vegetation[i]);
  643. }
  644. // Let ncompete() do the actual distribution
  645. ncompete(individuals, nmass_avail);
  646. // Get the results, nitrogen uptake fraction for each individual
  647. for (unsigned int i = 0; i < vegetation.nobj; i++) {
  648. vegetation[i].fnuptake = individuals[i].fnuptake;
  649. }
  650. }
  651. /// Use nitrogen storage to limit stress
  652. /** Retranslocated nitrogen from last year is used to
  653. * limit nitrogen stress in leaves, roots, and sap wood
  654. */
  655. void nstore_usage(Vegetation& vegetation) {
  656. vegetation.firstobj();
  657. while (vegetation.isobj) {
  658. Individual& indiv=vegetation.getobj();
  659. // individual excess nitrogen demand after uptake
  660. double excess_ndemand = (indiv.leafndemand + indiv.rootndemand) * (1.0 - indiv.fnuptake)
  661. + indiv.leafndemand_store + indiv.rootndemand_store;
  662. // if individual is in need of using its labile nitrogen storage
  663. if (!negligible(excess_ndemand)) {
  664. // if labile nitrogen storage is larger than excess nitrogen demand
  665. if (excess_ndemand <= indiv.nstore_labile) {
  666. // leaf nitrogen demand
  667. double leaf_ndemand = indiv.leafndemand * (1.0 - indiv.fnuptake) + indiv.leafndemand_store;
  668. indiv.nmass_leaf += leaf_ndemand;
  669. indiv.nstore_labile -= leaf_ndemand;
  670. // root nitrogen demand
  671. double root_ndemand = indiv.rootndemand * (1.0 - indiv.fnuptake) + indiv.rootndemand_store;
  672. indiv.nmass_root += root_ndemand;
  673. indiv.nstore_labile -= root_ndemand;
  674. #ifdef CRESCENDO_FACE
  675. indiv.report_flux(Fluxes::DNGL, leaf_ndemand);
  676. indiv.report_flux(Fluxes::DNGR, root_ndemand);
  677. #endif
  678. indiv.nstress = false;
  679. }
  680. else {
  681. if (!negligible(indiv.nstore_labile)) {
  682. // calculate total nitrogen mass
  683. double tot_nmass = indiv.nmass_leaf + indiv.nmass_root + indiv.fnuptake * (indiv.leafndemand + indiv.rootndemand) + indiv.nstore_labile;
  684. // new leaf C:N ratio
  685. double cton_leaf = (indiv.cmass_leaf_today() + indiv.cmass_root_today() * (indiv.pft.cton_leaf_avr / indiv.pft.cton_root_avr)) / tot_nmass;
  686. // nitrogen added to leaf from storage
  687. double labile_nto_leaf = indiv.cmass_leaf_today() / cton_leaf - (indiv.nmass_leaf + indiv.fnuptake * indiv.leafndemand);
  688. // new leaf nitrogen
  689. indiv.nmass_leaf += labile_nto_leaf;
  690. // new root nitrogen
  691. indiv.nmass_root += indiv.nstore_labile - labile_nto_leaf;
  692. #ifdef CRESCENDO_FACE
  693. indiv.report_flux(Fluxes::DNGL, labile_nto_leaf);
  694. indiv.report_flux(Fluxes::DNGR, indiv.nstore_labile - labile_nto_leaf);
  695. #endif
  696. indiv.nstore_labile = 0.0;
  697. }
  698. // nitrogen stressed photosynthesis is allowed only when nitrogen limitation is turned on
  699. indiv.nstress = ifnlim;
  700. }
  701. }
  702. else
  703. // photosynthesis will not be nitrogen stresses
  704. indiv.nstress = false;
  705. vegetation.nextobj();
  706. }
  707. }
  708. /// Nitrogen demand
  709. /** Determines nitrogen demand based on vmax for leaves.
  710. * Roots and sap wood nitrogen concentration follows leaf
  711. * nitrogen concentration.
  712. * Also determines individual nitrogen uptake capability
  713. */
  714. void ndemand(Patch& patch, Vegetation& vegetation) {
  715. Gridcell& gridcell = patch.stand.get_gridcell();
  716. Soil& soil = patch.soil;
  717. /// daily nitrogen demand for patch (kgN/m2)
  718. patch.ndemand = 0.0;
  719. // Scalar to soil temperature (Eqn A9, Comins & McMurtrie 1993) for nitrogen uptake
  720. double temp_scale = soil.temp > 0.0 ? max(0.0, 0.0326 + 0.00351 * pow(soil.temp, 1.652) - pow(soil.temp / 41.748, 7.19)) : 0.0;
  721. /// Rate of nitrogen uptake not associated with Michaelis-Menten Kinetics (Zaehle and Friend 2010)
  722. double kNmin = 0.05;
  723. vegetation.firstobj();
  724. while (vegetation.isobj) {
  725. Individual& indiv = vegetation.getobj();
  726. // Rescaler of nitrogen uptake
  727. indiv.fnuptake = 1.0;
  728. // Starts with no nitrogen stress
  729. indiv.nstress = false;
  730. // Optimal leaf nitrogen content
  731. double leafoptn;
  732. // Optimal leaf C:N ratio
  733. double cton_leaf_opt;
  734. // Calculate optimal leaf nitrogen content and demand
  735. if (!negligible(indiv.phen) || !negligible(indiv.cmass_leaf_today())) {
  736. indiv.nday_leafon++;
  737. // Added a scalar depending on individual lai to slow down light optimization of newly shaded leafs
  738. // Peltoniemi et al. 2012
  739. indiv.nextin = exp(0.12 * min(10.0*indiv.phen, indiv.lai_indiv_today()));
  740. // Calculate optimal leaf nitrogen associated with photosynthesis and none photosynthetic
  741. // active nitrogen (Haxeltine et al. 1996 eqn 27/28)
  742. leafoptn = indiv.photosynthesis.nactive_opt * indiv.nextin + N0 * indiv.cmass_leaf_today();
  743. // Can not have higher nitrogen concentration than minimum leaf C:N ratio
  744. if (indiv.cmass_leaf_today() / leafoptn < indiv.pft.cton_leaf_min) {
  745. leafoptn = indiv.cmass_leaf_today() / indiv.pft.cton_leaf_min;
  746. }
  747. // Can not have lower nitrogen concentration than maximum leaf C:N ratio
  748. else if (indiv.cmass_leaf_today() / leafoptn > indiv.pft.cton_leaf_max) {
  749. leafoptn = indiv.cmass_leaf_today() / indiv.pft.cton_leaf_max;
  750. }
  751. // Updating annual optimal leaf C:N ratio
  752. indiv.cton_leaf_aopt = min(indiv.cmass_leaf_today() / leafoptn, indiv.cton_leaf_aopt);
  753. // Leaf nitrogen demand
  754. indiv.leafndemand = max(leafoptn - indiv.nmass_leaf, 0.0);
  755. // Setting daily optimal leaf C:N ratio
  756. if (indiv.leafndemand) {
  757. cton_leaf_opt = indiv.cmass_leaf_today() / leafoptn;
  758. }
  759. else {
  760. cton_leaf_opt = max(indiv.pft.cton_leaf_min, indiv.cton_leaf());
  761. }
  762. }
  763. else {
  764. indiv.leafndemand = 0.0;
  765. cton_leaf_opt = indiv.cton_leaf();
  766. }
  767. // Nitrogen demand
  768. // Root nitrogen demand
  769. indiv.rootndemand = max(0.0, indiv.cmass_root_today() / (cton_leaf_opt * indiv.pft.cton_root_avr / indiv.pft.cton_leaf_avr) - indiv.nmass_root);
  770. // Sap wood nitrogen demand. Demand is ramped up throughout the year.
  771. if (indiv.pft.lifeform == TREE) {
  772. indiv.sapndemand = max(0.0, indiv.cmass_sap / (cton_leaf_opt * indiv.pft.cton_sap_avr / indiv.pft.cton_leaf_avr) - indiv.nmass_sap) * ((1.0 + (double)date.day) / (double)date.year_length());
  773. }
  774. // Labile nitrogen storage demand
  775. indiv.storendemand = indiv.ndemand_storage(cton_leaf_opt);
  776. //TODO HO demand
  777. indiv.hondemand = 0.0;
  778. // Total nitrogen demand
  779. double ndemand_tot = indiv.leafndemand + indiv.rootndemand + indiv.sapndemand + indiv.storendemand + indiv.hondemand;
  780. // Calculate scalars to possible nitrogen uptake
  781. // Current plant mobile nitrogen concentration
  782. double ntoc = !negligible(indiv.cmass_leaf_today() + indiv.cmass_root_today()) ? (indiv.nmass_leaf + indiv.nmass_root) / (indiv.cmass_leaf_today() + indiv.cmass_root_today()) : 0.0;
  783. // Scale to maximum nitrogen concentrations
  784. indiv.cton_status = max(0.0, (ntoc - 1.0 / indiv.pft.cton_leaf_min) / (1.0 / indiv.pft.cton_leaf_avr - 1.0 / indiv.pft.cton_leaf_min));
  785. // Nitrogen availablilty scalar due to saturating Michaelis-Menten kinetics
  786. double nmin_scale = kNmin + soil.nmass_avail / (soil.nmass_avail + gridcell.pft[indiv.pft.id].Km);
  787. // Maximum available soil mineral nitrogen for this individual is base on its root area.
  788. // This is considered to be related to FPC which is proportional to crown area which is approx
  789. // 4 times smaller than the root area
  790. double max_indiv_avail = min(1.0, indiv.fpc * 4.0) * soil.nmass_avail;
  791. // Maximum nitrogen uptake due to all scalars (times 2 because considering both NO3- and NH4+ uptake)
  792. // and soil available nitrogen within individual projectived coverage
  793. double maxnup = min(2.0 * indiv.pft.nuptoroot * nmin_scale * temp_scale * indiv.cton_status * indiv.cmass_root_today(), max_indiv_avail);
  794. // Nitrogen demand limitation due to maximum nitrogen uptake capacity
  795. double fractomax = ndemand_tot > 0.0 ? min(maxnup/ndemand_tot,1.0) : 0.0;
  796. // Root and leaf demand from storage pools
  797. indiv.leafndemand_store = indiv.leafndemand * (1.0 - fractomax);
  798. indiv.rootndemand_store = indiv.rootndemand * (1.0 - fractomax);
  799. // Nitrogen demand after adjustment to maximum uptake capacity
  800. indiv.leafndemand *= fractomax;
  801. indiv.rootndemand *= fractomax;
  802. indiv.sapndemand *= fractomax;
  803. indiv.storendemand *= fractomax;
  804. // Sum total nitrogen demand individual is capable of taking up
  805. indiv.ndemand = indiv.leafndemand + indiv.rootndemand + indiv.sapndemand + indiv.storendemand;
  806. // Negative nitrogen demand not allowed
  807. if (indiv.ndemand <= 0.0) {
  808. indiv.ndemand = 0.0;
  809. // Compartments fraction of total nitrogen demand
  810. indiv.leaffndemand = 0.0;
  811. indiv.rootfndemand = 0.0;
  812. indiv.sapfndemand = 0.0;
  813. indiv.storefndemand = 0.0;
  814. }
  815. else {
  816. // Compartments fraction of total nitrogen demand
  817. indiv.leaffndemand = indiv.leafndemand / indiv.ndemand;
  818. indiv.rootfndemand = indiv.rootndemand / indiv.ndemand;
  819. indiv.sapfndemand = indiv.sapndemand / indiv.ndemand;
  820. indiv.storefndemand = max(0.0, 1.0 - (indiv.leaffndemand + indiv.rootfndemand + indiv.sapfndemand));
  821. }
  822. // Sum total patch nitrogen demand
  823. patch.ndemand += indiv.ndemand;
  824. vegetation.nextobj();
  825. }
  826. }
  827. /// Recalculation of photosynthesis under vmax nitrogen stress
  828. /** If nitrogen supply is not able to meet demand it will lead
  829. * to down-regulation of vmax resulting in lower photosynthesis
  830. */
  831. void vmax_nitrogen_stress(Patch& patch, Climate& climate, Vegetation& vegetation) {
  832. // Supply function for nitrogen and determination of nitrogen stress leading
  833. // to down-regulation of vmax.
  834. // Nitrogen within projective cover of all individuals
  835. double tot_nmass_avail = patch.soil.nmass_avail * min(1.0, patch.fpc_total);
  836. if (patch.stand.landcover == CROPLAND && ifnlim) { // Also for other landcovers ??
  837. // Take soil wcont into account
  838. tot_nmass_avail *= patch.soil.wcont[0] * 0.9 + patch.soil.wcont[1] * 0.1;
  839. }
  840. // Calculate individual uptake fraction of nitrogen demand
  841. if (patch.ndemand > tot_nmass_avail) {
  842. // Determine individual nitrogen uptake fractions
  843. fnuptake(vegetation, tot_nmass_avail);
  844. }
  845. // Resolve nitrogen stress with longterm stored nitrogen
  846. nstore_usage(vegetation);
  847. // Calculate leaf nitrogen associated with photosynthesis, nitrogen limited photosynthesis,
  848. // and annual otimal leaf nitrogen content and nitrogen limitation on vmax
  849. vegetation.firstobj();
  850. while (vegetation.isobj) {
  851. Individual& indiv = vegetation.getobj();
  852. Pft& pft = indiv.pft;
  853. // Calculate leaf nitrogen associated with photosynthesis (Haxeltine et al. 1996 eqn 27/28)
  854. // Added difference between needleleaved and broadleaved mentioned in Friend et al. 1997
  855. // Should be done on FPC basis, but is not as it does not matter mathematically
  856. // Needs to be calculated for each individual due to possible water stress
  857. // Todays leaf nitrogen after uptake
  858. double nmass_leaf = indiv.nmass_leaf + indiv.leafndemand * indiv.fnuptake;
  859. if (indiv.phen > 0.0) {
  860. indiv.nactive = max(0.0, nmass_leaf - N0 * indiv.cmass_leaf_today());
  861. }
  862. else {
  863. indiv.nactive = 0.0;
  864. }
  865. // Individuals photosynthesis is nitrogen stressed
  866. if (indiv.nstress) {
  867. // Individual photosynthesis
  868. photosynthesis(climate.co2, climate.temp, climate.par, climate.daylength,
  869. indiv.fpar, pft.lambda_max, pft,
  870. indiv.nactive / indiv.nextin, true,
  871. indiv.photosynthesis,
  872. -1);
  873. indiv.gpterm = gpterm(indiv.photosynthesis.adtmm, climate.co2, pft.lambda_max, climate.daylength);
  874. if (date.diurnal()) {
  875. for (int i=0; i<date.subdaily; i++) {
  876. PhotosynthesisResult& result = indiv.phots[i];
  877. photosynthesis(climate.co2, climate.temps[i], climate.pars[i], 24,
  878. indiv.fpar, pft.lambda_max, pft,
  879. indiv.nactive / indiv.nextin, true,
  880. result,
  881. indiv.photosynthesis.vm);
  882. indiv.gpterms[i] = gpterm(result.adtmm, climate.co2, pft.lambda_max, 24);
  883. }
  884. }
  885. }
  886. // Sum annual average nitrogen limitation on vmax
  887. if (indiv.phen)
  888. indiv.avmaxnlim += indiv.photosynthesis.vmaxnlim;
  889. // On last day of year determined average nitrogen limitation
  890. // based on days with leaf on
  891. if (date.islastday && date.islastmonth) {
  892. if (!negligible(indiv.nday_leafon)) {
  893. indiv.avmaxnlim /= (double)indiv.nday_leafon;
  894. }
  895. else {
  896. indiv.avmaxnlim = 0.0;
  897. }
  898. }
  899. vegetation.nextobj();
  900. }
  901. }
  902. /// Transpirative demand
  903. /** Two alternative parameterisations of aet_monteith are available:
  904. * AET_MONTEITH_HYPERBOLIC and AET_MONTEITH_EXPONENTIAL
  905. * \see canexch.h
  906. */
  907. void wdemand(Patch& patch, Climate& climate, Vegetation& vegetation, const Day& day) {
  908. // Determination of transpirative demand based on a Monteith parameterisation of
  909. // boundary layer dynamics, i.e. demand = f(EET, conductance)
  910. double gp_patch = 0.0;
  911. // non-water-stressed canopy conductance for patch, patch vegetated area
  912. // basis (mm/s)
  913. double gp_leafon_patch = 0.0;
  914. // non-water-stressed canopy conductance assuming full leaf cover, patch
  915. // vegetated area basis (mm/s)
  916. vegetation.firstobj();
  917. while (vegetation.isobj) {
  918. Individual& indiv = vegetation.getobj();
  919. Pft& pft = indiv.pft;
  920. // Calculate non-water-stressed canopy conductance assuming full leaf cover
  921. // - include canopy-conductance component not linked to
  922. // photosynthesis (diffusion through leaf cuticle etc); this is
  923. // assumed to be proportional to leaf-on fraction
  924. // Call photosynthesis for individual assuming stomates fully open
  925. // (lambda = lambda_max)
  926. if (indiv.growingseason()) {
  927. PhotosynthesisResult leafon_photosynthesis;
  928. // Call photosynthesis first with fpar_leafon to get gp_leafon below.
  929. // Should hopefully not be needed in future, demand_leafon only used
  930. // by raingreen phenology.
  931. double temp = date.diurnal() ? climate.temps[day.period] : climate.temp;
  932. double par = date.diurnal() ? climate.pars[day.period] : climate.par;
  933. double daylength = date.diurnal() ? 24 : climate.daylength;
  934. // No nitrogen limitation when calculating gp_leafon
  935. photosynthesis(climate.co2, temp, par, daylength,
  936. indiv.fpar_leafon, pft.lambda_max, pft,
  937. 1.0, false,
  938. leafon_photosynthesis,
  939. -1);
  940. double gp_leafon = gpterm(leafon_photosynthesis.adtmm, climate.co2, pft.lambda_max, daylength) + pft.gmin * indiv.fpc;
  941. // Increment patch sums of non-water-stressed gp by individual value
  942. gp_patch += (date.diurnal() ? indiv.gpterms[day.period] : indiv.gpterm) + pft.gmin * indiv.fpc_today();
  943. gp_leafon_patch += gp_leafon;
  944. }
  945. vegetation.nextobj();
  946. }
  947. // Calculate transpirational demand on patch vegetated area basis
  948. // Eqn 23, Haxeltine & Prentice 1996
  949. if (!negligible(gp_patch) && !negligible(patch.fpc_total)) {
  950. gp_patch /= patch.fpc_total;
  951. patch.wdemand = aet_monteith(patch.eet_net_veg, gp_patch);
  952. }
  953. else
  954. patch.wdemand = 0.0;
  955. patch.wdemand_day += patch.wdemand;
  956. if (day.isend) {
  957. patch.wdemand_day /= date.subdaily;
  958. }
  959. if (!negligible(gp_leafon_patch) && !negligible(patch.fpc_total)) {
  960. gp_leafon_patch /= patch.fpc_total;
  961. patch.wdemand_leafon = aet_monteith(patch.eet_net_veg, gp_leafon_patch);
  962. }
  963. else
  964. patch.wdemand_leafon = 0.0;
  965. }
  966. /// Plant water uptake
  967. /**
  968. * Returns plant water uptake (point scale, or mean for patch) as a fraction of
  969. * maximum possible (daily basis).
  970. *
  971. * Supports alternative parameterisations of plant water uptake:
  972. *
  973. * WCONT = uptake rate coupled to water content and vertical
  974. * root distribution (as in earlier versions of LPJ-GUESS and LPJF)
  975. * ROOTDIST = uptake rate independent of water content (to wilting point)
  976. * but with fractional uptake from different layers according
  977. * to prescribed root distribution
  978. * SMART = uptake rate independent of water content (to wilting point),
  979. * fractional uptake from different layers according to layer
  980. * water content for trees, according to prescribed root
  981. * distribution for grasses
  982. * SPECIESSPECIFIC = uptake rate is species specific, with more drought
  983. * tolerance species (lower species_drought_tolerance values)
  984. * having greater relative uptake rates.
  985. */
  986. inline double water_uptake(double wcont[NSOILLAYER], double awc[NSOILLAYER],
  987. double rootdist[NSOILLAYER], double emax, double fpc_rescale,
  988. double fwuptake[NSOILLAYER], bool ifsmart, double species_drought_tolerance) {
  989. // INPUT PARAMETERS:
  990. // wcont = water content of soil layers as fraction between wilting point
  991. // (0) and available water holding capacity (1)
  992. // awc = available water holding capacity of each soil layer (mm)
  993. // rootdist = plant root distribution (fraction in each soil layer)
  994. // emax = maximum evapotranspiration rate (mm/day)
  995. // fpc_rescale = scaling factor for foliar projective cover (complement of patch
  996. // summed FPC overlap)
  997. // ifsmart = whether plants can freely adapt root profile to distribution of
  998. // available water among layers (required for "smart" mode)
  999. // species_drought_tolerance = used only if the SPECIESSPECIFIC option is specified.
  1000. // OUTPUT PARAMETER:
  1001. // fwuptake = fraction of total uptake originating from each layer
  1002. double wr;
  1003. int s;
  1004. switch (wateruptake) {
  1005. case WR_WCONT:
  1006. // LPJ "standard" formulation with linear scaling of uptake to water content
  1007. // and weighting by plant root profiles
  1008. wr = 0.0;
  1009. for (s=0; s<NSOILLAYER; s++) {
  1010. fwuptake[s] = rootdist[s] * wcont[s] * fpc_rescale;
  1011. wr += fwuptake[s];
  1012. }
  1013. break;
  1014. // guess2008 - drought/water uptake changes - new option
  1015. case WR_SPECIESSPECIFIC:
  1016. // Uptake rate is species specific, with more drought tolerance species (lower species_drought_tolerance
  1017. // values) having greater relative uptake rates.
  1018. // Reduces to WCONT if species_drought_tolerance = 0.5
  1019. wr = 0.0;
  1020. for (s=0; s<NSOILLAYER; s++) {
  1021. double max_rel_uptake = pow(wcont[s], 2.0 * 0.1); // Upper limit. Limits C3 grass uptake
  1022. fwuptake[s] = rootdist[s] * min(pow(wcont[s], 2.0 * species_drought_tolerance), max_rel_uptake) * fpc_rescale;
  1023. wr += fwuptake[s];
  1024. }
  1025. break;
  1026. case WR_ROOTDIST:
  1027. // Uptake rate independent of water content (to wilting point) but with fractional
  1028. // uptake from different layers according to prescribed root distribution
  1029. wr = 0.0;
  1030. for (s=0; s<NSOILLAYER; s++) {
  1031. fwuptake[s] = min(wcont[s] * awc[s] * fpc_rescale, emax * rootdist[s]) / emax;
  1032. wr += fwuptake[s];
  1033. }
  1034. break;
  1035. case WR_SMART:
  1036. {
  1037. // Uptake rate independent of water content (to wilting point), fractional uptake
  1038. // from different layers according to layer water content for trees, and according
  1039. // to prescribed root distribution for grasses
  1040. double wcsum = 0.0;
  1041. double wcfrac;
  1042. for (s=0; s<NSOILLAYER; s++) wcsum += wcont[s];
  1043. wr = 0.0;
  1044. if (negligible(wcsum))
  1045. for (s=0; s<NSOILLAYER; s++) fwuptake[s] = 0.0;
  1046. else {
  1047. for (s=0; s<NSOILLAYER; s++) {
  1048. wcfrac = wcont[s] / wcsum;
  1049. if (ifsmart)
  1050. fwuptake[s] = min(wcont[s] * awc[s] * wcfrac * fpc_rescale, emax * wcfrac) / emax;
  1051. else
  1052. fwuptake[s] = min(wcont[s] * awc[s] * fpc_rescale, emax * rootdist[s]) / emax;
  1053. wr += fwuptake[s];
  1054. }
  1055. }
  1056. }
  1057. break;
  1058. default:
  1059. // Should never happen
  1060. fail("Unsupported wateruptake type");
  1061. }
  1062. if (!negligible(wr))
  1063. for (s=0; s<NSOILLAYER; s++)
  1064. fwuptake[s] /= wr;
  1065. return wr;
  1066. }
  1067. /// Plant water uptake for irrigated crops
  1068. /**
  1069. * Returns plant water uptake (point scale, or mean for patch) as a fraction of
  1070. * maximum possible (daily basis), after adding required water to obtain maximum
  1071. * water uptake.
  1072. * Irrigation water is added to the soil in hydrology_lpjf
  1073. *
  1074. * Only ROOTDIST currently supported plant water uptake parameterisation:
  1075. *
  1076. * ROOTDIST = uptake rate independent of water content (to wilting point)
  1077. * but with fractional uptake from different layers according
  1078. * to prescribed root distribution
  1079. */
  1080. double irrigated_water_uptake(Patch& patch, Pft& pft, const Day& day) {
  1081. Patchpft& ppft = patch.pft[pft.id];
  1082. double* awc = patch.soil.soiltype.awc;
  1083. double wcont_cp[NSOILLAYER];
  1084. for (int i=0;i<NSOILLAYER;i++) {
  1085. wcont_cp[i] = patch.soil.wcont[i];
  1086. }
  1087. if (day.isstart) {
  1088. ppft.water_deficit_d = 0.0;
  1089. if (date.day == 0) {
  1090. ppft.water_deficit_y = 0.0;
  1091. }
  1092. }
  1093. if (patch.soil.wcont[0]<0.9 && ppft.phen > 0.0) {
  1094. double wcont_0_opt = 0.0;
  1095. double wr_opt = min(1.0, patch.wdemand / ppft.phen / pft.emax);
  1096. if (wateruptake == WR_ROOTDIST) {
  1097. wcont_0_opt = (wr_opt * pft.emax - min(patch.soil.wcont[1] * awc[1] * patch.fpc_rescale, pft.emax * pft.rootdist[1])) / awc[0] / patch.fpc_rescale;
  1098. if (wcont_0_opt * awc[0] * patch.fpc_rescale > pft.emax * pft.rootdist[0]) {
  1099. wcont_0_opt = pft.emax * pft.rootdist[0] / awc[0] / patch.fpc_rescale;
  1100. }
  1101. }
  1102. else {
  1103. fail("Irrigation soil water only balanced for WR_ROOTDIST currently !\n");
  1104. }
  1105. if (wcont_0_opt > patch.soil.wcont[0]) {
  1106. ppft.water_deficit_d += (wcont_0_opt-patch.soil.wcont[0]) * awc[0];
  1107. wcont_cp[0] = wcont_0_opt;
  1108. }
  1109. if (day.isend) {
  1110. ppft.water_deficit_d /= date.subdaily;
  1111. ppft.water_deficit_y += ppft.water_deficit_d;
  1112. }
  1113. }
  1114. return water_uptake(wcont_cp, awc, pft.rootdist, pft.emax, patch.fpc_rescale,
  1115. ppft.fwuptake, pft.lifeform == TREE, pft.drought_tolerance);
  1116. };
  1117. /// Actual evapotranspiration and water stress
  1118. /** Soil water supply at the roots available to meet the transpirational demand
  1119. * Fundamentally, water stress = supply < demand
  1120. */
  1121. void aet_water_stress(Patch& patch, Vegetation& vegetation, const Day& day) {
  1122. // Supply function for evapotranspiration and determination of water stress leading
  1123. // to down-regulation of stomatal conductance. Actual evapotranspiration determined
  1124. // as smaller of supply and transpirative demand (see function demand).
  1125. // Base value for actual canopy conductance calculated here for water-stressed
  1126. // individuals and used to derive actual photosynthesis in function npp (below)
  1127. // Calculate common point supply for each PFT in this patch
  1128. for (int p=0; p<npft; p++) {
  1129. Standpft& spft = patch.stand.pft[p];
  1130. if (!spft.active)
  1131. continue;
  1132. // Retrieve next patch PFT
  1133. Patchpft& ppft = patch.pft[p];
  1134. // Retrieve PFT
  1135. Pft& pft = ppft.pft;
  1136. if (day.isstart || spft.irrigated && pft.id == patch.stand.pftid) {
  1137. // Calculate effective water supply from plant roots
  1138. // Rescale available water by patch FPC if exceeds 1
  1139. // (this then represents the average amount of water available over an
  1140. // individual's FPC, assuming individuals are equal in competition for water)
  1141. double wr;
  1142. if (spft.irrigated && pft.id == patch.stand.pftid) {
  1143. wr = irrigated_water_uptake(patch, pft, day);
  1144. } else {
  1145. wr = water_uptake(patch.soil.wcont, patch.soil.soiltype.awc,
  1146. pft.rootdist, pft.emax, patch.fpc_rescale, ppft.fwuptake,
  1147. pft.lifeform == TREE, pft.drought_tolerance);
  1148. }
  1149. // Calculate supply (Eqn 24, Haxeltine & Prentice 1996)
  1150. if (patch.stand.landcover!=CROPLAND || ppft.cropphen->growingseason)
  1151. ppft.wsupply_leafon = pft.emax * wr;
  1152. else
  1153. ppft.wsupply_leafon = 0.0;
  1154. ppft.wsupply = ppft.wsupply_leafon * ppft.phen;
  1155. }
  1156. ppft.wstress = ppft.wsupply < patch.wdemand && !negligible(ppft.phen) && !(pft.phenology==CROPGREEN && !largerthanzero(patch.wdemand-ppft.wsupply, -10));
  1157. // Calculate water-stressed canopy conductance on FPC basis assuming
  1158. // FPAR=1 and deducting canopy conductance component not associated
  1159. // with CO2 uptake; valid for all individuals of this PFT in this patch
  1160. // today.
  1161. // Eqn 25, Haxeltine & Prentice 1996
  1162. // Fix, valid for monocultures, for faulty equation, manifesting itself in problems with crops in high scenario CO2-levels.
  1163. // No fix for natural vegetation yet.
  1164. double gmin = pft.phenology==CROPGREEN ? ppft.phen * pft.gmin : pft.gmin;
  1165. ppft.gcbase = ppft.wstress ? max(gc_monteith(ppft.wsupply, patch.eet_net_veg)-
  1166. gmin * ppft.wsupply / patch.wdemand, 0.0) : 0;
  1167. if (!date.diurnal()) {
  1168. ppft.wstress_day = ppft.wstress;
  1169. ppft.gcbase_day = ppft.gcbase;
  1170. }
  1171. else if (day.isend) {
  1172. ppft.wstress_day = ppft.wsupply < patch.wdemand_day && !negligible(ppft.phen) && !(pft.phenology==CROPGREEN && !largerthanzero(patch.wdemand-ppft.wsupply, -10));
  1173. ppft.gcbase_day = ppft.wstress_day ? max(gc_monteith(ppft.wsupply,
  1174. patch.eet_net_veg) - gmin * ppft.wsupply / patch.wdemand_day, 0.0) : 0;
  1175. }
  1176. }
  1177. // Calculate / transfer supply to individuals
  1178. vegetation.firstobj();
  1179. while (vegetation.isobj) {
  1180. Individual& indiv = vegetation.getobj();
  1181. Patchpft& ppft = patch.pft[indiv.pft.id];
  1182. if (day.isstart) {
  1183. indiv.aet = 0;
  1184. if (date.day == 0)
  1185. indiv.aaet = 0.0;
  1186. }
  1187. indiv.wstress = ppft.wstress;
  1188. if (indiv.alive) {
  1189. if (indiv.wstress) {
  1190. indiv.aet += ppft.wsupply;
  1191. }
  1192. else {
  1193. indiv.aet += negligible(indiv.phen) ? 0.0 : patch.wdemand;
  1194. }
  1195. }
  1196. if (day.isend) {
  1197. indiv.aet *= indiv.fpc / date.subdaily;
  1198. }
  1199. if (day.isend) {
  1200. indiv.aaet += indiv.aet;
  1201. }
  1202. vegetation.nextobj();
  1203. }
  1204. }
  1205. /// Water scalar
  1206. void water_scalar(Patch& patch, Vegetation& vegetation, const Day& day) {
  1207. // Derivation of daily and annual versions of water scalar (wscal, omega)
  1208. // Daily version is used to determine leaf onset and abscission for raingreen PFTs.
  1209. // Annual version determines relative allocation to roots versus leaves for
  1210. // subsequent year
  1211. for (int p=0; p<npft; p++) {
  1212. // Retrieve next patch PFT
  1213. Patchpft& ppft = patch.pft[p];
  1214. if (!patch.stand.pft[p].active)
  1215. continue;
  1216. if (day.isstart) {
  1217. ppft.wscal = 0;
  1218. if (date.day == 0) {
  1219. ppft.wscal_mean = 0;
  1220. if (ppft.pft.phenology==CROPGREEN || ppft.pft.isintercropgrass)
  1221. ppft.cropphen->growingdays_y=0;
  1222. }
  1223. }
  1224. // Calculate patch PFT water scalar value
  1225. if (!negligible(patch.wdemand_leafon)) {
  1226. ppft.wscal += min(1.0, ppft.wsupply_leafon/patch.wdemand_leafon);
  1227. }
  1228. else {
  1229. ppft.wscal += 1.0;
  1230. }
  1231. if (day.isend) {
  1232. ppft.wscal /= (double)date.subdaily;
  1233. if (patch.stand.landcover!=CROPLAND //natural, urban, pasture, forest and peatland stands
  1234. || ppft.pft.phenology==ANY && ppft.pft.id==patch.stand.pftid) { //normal grass growth
  1235. ppft.wscal_mean += ppft.wscal;
  1236. // Convert from sum to mean on last day of year
  1237. if (date.islastday && date.islastmonth) {
  1238. ppft.wscal_mean /= (double)date.year_length();
  1239. }
  1240. }
  1241. else if (ppft.cropphen->growingseason // true crops and cover-crop grass
  1242. || ppft.pft.phenology == CROPGREEN && date.day == ppft.cropphen->hdate
  1243. || ppft.pft.isintercropgrass && date.day == patch.pft[patch.stand.pftid].cropphen->eicdate) {
  1244. ppft.cropphen->growingdays_y++;
  1245. ppft.wscal_mean = ppft.wscal_mean + (ppft.wscal - ppft.wscal_mean) / ppft.cropphen->growingdays_y;
  1246. }
  1247. }
  1248. }
  1249. }
  1250. ///////////////////////////////////////////////////////////////////////////////////////
  1251. // ASSIMILATION_WSTRESS
  1252. // Internal function (do not call directly from framework)
  1253. void assimilation_wstress(const Pft& pft, double co2, double temp, double par,
  1254. double daylength, double fpar, double fpc, double gcbase,
  1255. double vmax, PhotosynthesisResult& phot_result, double& lambda,
  1256. double nactive, bool ifnlimvmax) {
  1257. // DESCRIPTION
  1258. // Calculation of net C-assimilation under water-stressed conditions
  1259. // (demand>supply; see function canopy_exchange). Utilises a numerical
  1260. // iteration procedure to find the level of stomatal aperture (characterised by
  1261. // lambda, the ratio of leaf intercellular to ambient CO2 concentration) which
  1262. // satisfies simulataneously a canopy-conductance based and light-based
  1263. // formulation of photosynthesis (Eqns 2, 18 and 19, Haxeltine & Prentice (1996)).
  1264. // Numerical method is a tailored implementation of the bisection method,
  1265. // assuming root (f(lambda)=0) bracketed by f(0.02)<0 and
  1266. // f(lambda_max)>0 (Press et al 1986)
  1267. // The bisection method terminates when we're close enough to a root
  1268. // (absolute value of f(lambda) < EPS), or after a maximum number of
  1269. // iterations.
  1270. // Note that the function sometimes doesn't search for a lambda,
  1271. // and returns zero assimilation (for instance if there is no
  1272. // root within the valid interval, or if daylength is zero).
  1273. // So if zero assimilation is returned, the returned lambda should
  1274. // not be used!
  1275. // OUTPUT PARAMETER
  1276. // phot_result = result of photosynthesis for the found lambda
  1277. // lambda = the lambda found by the bisection method (see above)
  1278. // Set lambda to something for cases where we don't actually search for
  1279. // a proper lambda. This value shouldn't be used (see documentation
  1280. // above), but we'll set it to something anyway so we don't return
  1281. // random garbage.
  1282. lambda = -1;
  1283. if (negligible(fpc) || negligible(fpar) || negligible(gcbase * daylength * 3600)) {
  1284. // Return zero assimilation
  1285. phot_result.clear();
  1286. return;
  1287. }
  1288. // Canopy conductance component associated with photosynthesis on a
  1289. // daily basis (mm / m2 / day)
  1290. double gcphot = gcbase * daylength * 3600 / 1.6 * co2 * CO2_CONV;
  1291. // At this point the function f(x) = g(x) - h(x) can be calculated as:
  1292. //
  1293. // g(x) = phot_result.adtmm / fpc (after a call to photosynthesis with lambda x)
  1294. // h(x) = gcphot * (1 - x)
  1295. // Evaluate f(lambda_max) to see if there's a root
  1296. // in the interval we're searching
  1297. photosynthesis(co2, temp, par, daylength, fpar, pft.lambda_max, pft, nactive, ifnlimvmax, phot_result, vmax);
  1298. double f_lambda_max = phot_result.adtmm / fpc - gcphot * (1 - pft.lambda_max);
  1299. if (f_lambda_max <= 0) {
  1300. // Return zero assimilation
  1301. phot_result.clear();
  1302. return;
  1303. }
  1304. const double EPS = 0.1; // minimum precision of solution in bisection method
  1305. double xmid;
  1306. // Implement numerical solution
  1307. double x1 = 0.02; // minimum bracket of root
  1308. double x2 = pft.lambda_max; // maximum bracket of root
  1309. double rtbis = x1; // root of the bisection
  1310. double dx = x2 - x1;
  1311. const int MAXTRIES = 6; // maximum number of iterations towards a solution
  1312. int b = 0; // number of tries so far towards solution
  1313. double fmid = EPS + 1.0;
  1314. while (fabs(fmid) > EPS && b <= MAXTRIES) {
  1315. b++;
  1316. dx *= 0.5;
  1317. xmid = rtbis + dx; // current guess for lambda
  1318. // Call function photosynthesis to calculate alternative value
  1319. // for total daytime photosynthesis according to Eqns 2 & 19,
  1320. // Haxeltine & Prentice (1996), and current guess for lambda
  1321. photosynthesis(co2, temp, par, daylength, fpar, xmid, pft, nactive, ifnlimvmax, phot_result, vmax);
  1322. // Evaluate fmid at the point lambda=xmid
  1323. // fmid will be an increasing function of xmid, with a solution
  1324. // (fmid=0) between x1 and x2
  1325. // Second term is total daytime photosynthesis (mm/m2/day) implied by
  1326. // canopy conductance and current guess for lambda (xmid)
  1327. // Eqn 18, Haxeltine & Prentice 1996
  1328. fmid = phot_result.adtmm / fpc - gcphot * (1 - xmid);
  1329. if (fmid < 0) {
  1330. rtbis = xmid;
  1331. }
  1332. }
  1333. // bvoc
  1334. lambda=xmid;
  1335. }
  1336. ///////////////////////////////////////////////////////////////////////////////////////
  1337. // AUTOTROPHIC RESPIRATION
  1338. // Internal function (do not call directly from framework)
  1339. void respiration(double gtemp_air, double gtemp_soil, lifeformtype lifeform,
  1340. double respcoeff, double cton_sap, double cton_root,
  1341. double cmass_sap, double cmass_root_today, double assim, double& resp,
  1342. double& resp_root, double& resp_sap, double& resp_growth) {
  1343. // DESCRIPTION
  1344. // Calculation of daily maintenance and growth respiration for individual with
  1345. // specified life form, phenological state, tissue C:N ratios and daily net
  1346. // assimilation, given current air and soil temperatures.
  1347. // Sitch et al. (2000), Lloyd & Taylor (1994), Sprugel et al (1996).
  1348. // NOTE: leaf respiration is not calculated here, but included in the calculation
  1349. // of net assimilation (function production above) as a proportion of rubisco
  1350. // capacity (Vmax).
  1351. // INPUT PARAMETERS
  1352. // gtemp_air = respiration temperature response incorporating damping of Q10
  1353. // response due to temperature acclimation (Eqn 11, Lloyd & Taylor
  1354. // 1994); Eqn B2 below
  1355. // gtemp_soil = as gtemp_air given soil temperature
  1356. // lifeform = PFT life form class (TREE or GRASS)
  1357. // respcoeff = PFT respiration coefficient
  1358. // cton_sap = PFT sapwood C:N ratio
  1359. // cton_root = PFT root C:N ratio
  1360. // phen = vegetation phenological state (fraction of potential leaf cover)
  1361. // cmass_sap = sapwood C biomass on grid cell area basis (kgC/m2)
  1362. // cmass_root = fine root C biomass on grid cell area basis (kgC/m2)
  1363. // assim = net assimilation on grid cell area basis (kgC/m2/day)
  1364. // OUTPUT PARAMETER
  1365. // resp = sum of maintenance and growth respiration on grid cell area basis
  1366. // (kgC/m2/day)
  1367. // guess2008 - following a comment by Annett Wolf, the following parameter value was changed:
  1368. // const double K=0.0548; // OLD value
  1369. const double K=0.095218; // NEW parameter value in respiration equations
  1370. // See the comment after Eqn (4) below.
  1371. // double resp_sap; // sapwood respiration (kg/m2/day)
  1372. // double resp_root; // root respiration (kg/m2/day)
  1373. // double resp_growth; // growth respiration (kg/m2/day)
  1374. // Calculation of maintenance respiration components for each living tissue:
  1375. //
  1376. // Based on the relations
  1377. //
  1378. // (A) Tissue respiration response to temperature
  1379. // (Sprugel et al. 1996, Eqn 7)
  1380. //
  1381. // (A1) Rm = 7.4e-7 * N * f(T)
  1382. // (A2) f(T) = EXP (beta * T)
  1383. //
  1384. // where Rm = tissue maintenance respiration rate in mol C/sec
  1385. // N = tissue nitrogen in mol N
  1386. // f(T) = temperature response function
  1387. // beta = ln Q10 / 10
  1388. // Q10 = change in respiration rate with a 10 K change
  1389. // in temperature
  1390. // T = tissue absolute temperature in K
  1391. //
  1392. // (B) Temperature response of soil respiration across ecosystems
  1393. // incorporating damping of Q10 response due to temperature acclimation
  1394. // (Lloyd & Taylor 1994, Eqn 11)
  1395. //
  1396. // (B1) R = R10 * g(T)
  1397. // (B2) g(T) = EXP [308.56 * (1 / 56.02 - 1 / (T - 227.13))]
  1398. //
  1399. // where R = respiration rate
  1400. // R10 = respiration rate at 10 K
  1401. // g(T) = temperature response function at 10 deg C
  1402. // T = soil absolute temperature in K
  1403. //
  1404. // Mathematical derivation:
  1405. //
  1406. // For a tissue with C:N mass ratio cton, and C mass, c_mass, N concentration
  1407. // in mol given by
  1408. // (1) N = c_mass / cton / atomic_mass_N
  1409. // Tissue respiration in gC/day given by
  1410. // (2) R = Rm * atomic_mass_C * seconds_per_day
  1411. // From (A1), (1) and (2),
  1412. // (3) R = 7.4e-7 * c_mass / cton / atomic_mass_N * atomic_mass_C
  1413. // * seconds_per_day * f(T)
  1414. // Let
  1415. // (4) k = 7.4e-7 * atomic_mass_C / atomic_mass_N * seconds_per_day
  1416. // = 0.0548
  1417. // guess2008 - there is an ERROR here, spotted by Annett Wolf
  1418. // If we calculate the respiration at 20 degC using g(T) and compare it to
  1419. // Sprugel's eqn 3, for 1 mole tissue N, say, we do NOT get the same result with this
  1420. // k value. This is because g(T) = 1 at 10 degC, not 20 degC. Changing k from 0.0548
  1421. // to 0.095218 gives exactly the same results as Sprugel at 20 degC. The scaling factor
  1422. // 7.4e-7 used here is taken from Sprugel's eqn. (7), but they used f(T), not g(T), and
  1423. // these are defined on different bases.
  1424. // from (3), (4)
  1425. // (5) R = k * c_mass / cton * f(T)
  1426. // substituting ecosystem temperature response function g(T) for f(T) (Eqn B2),
  1427. // (6) R = k * c_mass / cton * g(T)
  1428. // incorporate PFT-specific respiration coefficient to model acclimation
  1429. // of respiration rates to average (temperature) conditions for PFT (Ryan 1991)
  1430. // (7) R_pft = respcoeff_pft * k * c_mass / cton * g(T)
  1431. if (lifeform == TREE) {
  1432. // Sapwood respiration (Eqn 7)
  1433. resp_sap = respcoeff * K * cmass_sap / cton_sap * gtemp_air;
  1434. // Root respiration (Eqn 7)
  1435. // Assumed that root phenology follows leaf phenology
  1436. resp_root = respcoeff * K * cmass_root_today / cton_root * gtemp_soil;
  1437. // Growth respiration = 0.25 ( GPP - maintenance respiration)
  1438. resp_growth = (assim - resp_sap - resp_root) * 0.25;
  1439. // guess2008 - disallow negative growth respiration
  1440. // (following a comment (060823) from Annett Wolf)
  1441. if(resp_growth < 0.0) resp_growth = 0.0;
  1442. // Total respiration is sum of maintenance and growth respiration
  1443. resp = resp_sap + resp_root + resp_growth;
  1444. }
  1445. else if (lifeform == GRASS) {
  1446. // Root respiration
  1447. resp_root = respcoeff * K * cmass_root_today / cton_root * gtemp_soil;
  1448. resp_sap = 0.0;
  1449. // Growth respiration (see above)
  1450. resp_growth = (assim - resp_root) * 0.25;
  1451. // guess2008 - disallow negative growth respiration
  1452. // (following a comment (060823) from Annett Wolf)
  1453. if(resp_growth < 0.0) resp_growth = 0.0;
  1454. // Total respiration (see above)
  1455. resp = resp_root + resp_growth;
  1456. }
  1457. else fail ("Canopy exchange function respiration: unknown life form");
  1458. }
  1459. /// Net Primary Productivity
  1460. /** Includes BVOC calculations \see bvoc.cpp
  1461. */
  1462. void npp(Patch& patch, Climate& climate, Vegetation& vegetation, const Day& day) {
  1463. // Determination of daily NPP. Leaf level net assimilation calculated for non-
  1464. // water-stressed individuals (i.e. with fully-open stomata) using base value
  1465. // from function demand (above); for water-stressed individuals using base value
  1466. // for canopy conductance by a simultaneous solution of light-based and canopy
  1467. // conductance-based equations for net daily photosynthesis (see function
  1468. // assimilation wstress above). The latter uses the PFT-specific base value for
  1469. // conductance from function aet_water_stress (above).
  1470. // Plant respiration obtained by a call to function respiration (above).
  1471. double par, temp, assim, resp, resp_leaf, resp_root, resp_sap, resp_growth, lambda, rad, gtemp;
  1472. double hours = 24; // diurnal "daylength" to convert to daily units
  1473. if (date.diurnal()) {
  1474. par = climate.pars[day.period];
  1475. temp = climate.temps[day.period];
  1476. rad = climate.rads[day.period];
  1477. gtemp = climate.gtemps[day.period];
  1478. }
  1479. else {
  1480. par = climate.par;
  1481. temp = climate.temp;
  1482. hours = climate.daylength;
  1483. rad = climate.rad;
  1484. gtemp = climate.gtemp;
  1485. }
  1486. vegetation.firstobj();
  1487. while (vegetation.isobj) {
  1488. Individual& indiv = vegetation.getobj();
  1489. // For this individual ...
  1490. // Retrieve PFT and patch PFT
  1491. Pft& pft = indiv.pft;
  1492. Patchpft& ppft = patch.pft[pft.id];
  1493. //Don't do calculations for crops outside their growingseason
  1494. if (!indiv.growingseason()) {
  1495. indiv.dnpp=0.0;
  1496. vegetation.nextobj();
  1497. continue;
  1498. }
  1499. PhotosynthesisResult phot = date.diurnal() ? indiv.phots[day.period] : indiv.photosynthesis;
  1500. if (indiv.wstress) {
  1501. // Water stress - derive assimilation by simultaneous solution
  1502. // of light- and conductance-based equations of photosynthesis
  1503. assimilation_wstress(pft, climate.co2, temp, par, hours, indiv.fpar, indiv.fpc,
  1504. ppft.gcbase, phot.vm, phot, lambda,
  1505. indiv.nactive / indiv.nextin, ifnlim);
  1506. }
  1507. else {
  1508. lambda = indiv.pft.lambda_max;
  1509. }
  1510. //assim = phot.net_assimilation();
  1511. assim = phot.agd_g * 1e-3;
  1512. resp_leaf = phot.rd_g * 1e-3;
  1513. if (ifbvoc) {
  1514. PhotosynthesisResult phot_nostress = date.diurnal() ? indiv.phots[day.period] : indiv.photosynthesis;
  1515. bvoc(temp, hours, rad, climate, patch, indiv, pft, phot_nostress, phot.adtmm, day);
  1516. }
  1517. // Calculate autotrophic respiration
  1518. double cmass_root;
  1519. if (indiv.cropindiv && indiv.cropindiv->isintercropgrass && indiv.phen == 0.0)
  1520. cmass_root = 0.0;
  1521. else
  1522. cmass_root = indiv.cmass_root_today();
  1523. // Static root and sap wood C:N ratio if no N limitation
  1524. // to not let N affect respiration for C only version of model
  1525. double cton_sap, cton_root;
  1526. if (ifnlim) {
  1527. cton_sap = indiv.cton_sap();
  1528. cton_root = indiv.cton_root();
  1529. }
  1530. else {
  1531. cton_sap = pft.cton_sap_avr;
  1532. cton_root = pft.cton_root_avr;
  1533. }
  1534. respiration(gtemp, patch.soil.gtemp, indiv.pft.lifeform,
  1535. indiv.pft.respcoeff, cton_sap, cton_root,
  1536. indiv.cmass_sap, cmass_root, assim, resp, resp_root, resp_sap, resp_growth);
  1537. resp += resp_leaf;
  1538. // Convert to averages for this period for accounting purposes
  1539. assim /= date.subdaily;
  1540. resp /= date.subdaily;
  1541. // Update accumulated annual NPP and daily vegetation-atmosphere flux
  1542. indiv.dnpp = assim - resp;
  1543. indiv.anpp += indiv.dnpp;
  1544. indiv.mgpp[date.month] += assim;
  1545. indiv.mlambda_gpp[date.month] += lambda * assim;
  1546. indiv.report_flux(Fluxes::NPP, indiv.dnpp);
  1547. indiv.report_flux(Fluxes::GPP, assim);
  1548. indiv.report_flux(Fluxes::RA, resp);
  1549. indiv.report_flux(Fluxes::RALEAF, resp_leaf);
  1550. indiv.report_flux(Fluxes::RASTEM, resp_sap);
  1551. indiv.report_flux(Fluxes::RAROOT, resp_root);
  1552. indiv.report_flux(Fluxes::RAGROWTH, resp_growth);
  1553. if (indiv.pft.lifeform == TREE) {
  1554. indiv.report_flux(Fluxes::GPPTREE, assim);
  1555. indiv.report_flux(Fluxes::RATREE, resp);
  1556. }
  1557. else if (indiv.pft.landcover != CROPLAND) {
  1558. indiv.report_flux(Fluxes::GPPGRASS, assim);
  1559. indiv.report_flux(Fluxes::RAGRASS, resp);
  1560. }
  1561. #ifdef CRESCENDO_FACE
  1562. indiv.report_flux(Fluxes::DRALEAF, resp_leaf);
  1563. indiv.report_flux(Fluxes::DRASTEM, resp_sap);
  1564. indiv.report_flux(Fluxes::DRAROOT, resp_root);
  1565. indiv.report_flux(Fluxes::DRAGROWTH, resp_growth);
  1566. #endif
  1567. if (indiv.lai_today() > indiv.mlai_max[date.month])
  1568. indiv.mlai_max[date.month] = indiv.lai_today();
  1569. if (day.isend) {
  1570. indiv.mlai[date.month] += indiv.lai_today() / (double)date.ndaymonth[date.month];
  1571. }
  1572. vegetation.nextobj();
  1573. }
  1574. }
  1575. /// Leaf senescence for crops Eqs. 8,9,13 and 14 in Olin 2015
  1576. void leaf_senescence(Vegetation& vegetation) {
  1577. if (!(vegetation.patch.stand.is_true_crop_stand() && ifnlim)) {
  1578. return;
  1579. }
  1580. vegetation.firstobj();
  1581. while (vegetation.isobj) {
  1582. Individual& indiv = vegetation.getobj();
  1583. // Age dependent N retranslocation, Sec. 2.1.3 Olin 2015
  1584. if (indiv.patchpft().cropphen->dev_stage > 1.0) {
  1585. const double senNr = 0.1;
  1586. double senN = senNr * (indiv.nmass_leaf-indiv.cmass_leaf_today() / (indiv.pft.cton_leaf_max));
  1587. // Senescence is not done during spinup
  1588. if (vegetation.patch.stand.get_gridcell().getsimulationyear(date.year) > nyear_spinup && senN > 0) {
  1589. indiv.nmass_leaf -= senN;
  1590. indiv.cropindiv->nmass_agpool += senN;
  1591. }
  1592. }
  1593. double r = 0.0;
  1594. // N dependant C mass loss, with an inertia of 1/10, Eq. 13 Olin 2015
  1595. if (indiv.cmass_leaf_today() > 0.0) {
  1596. double Ln = indiv.lai_nitrogen_today();
  1597. double Lnld = indiv.lai_today();
  1598. r = (Lnld - min(Lnld, Ln))/indiv.pft.sla/10.0;
  1599. }
  1600. // No senescence during the initial growing period
  1601. if (indiv.patchpft().cropphen->fphu < 0.05) {
  1602. indiv.daily_cmass_leafloss = 0.0;
  1603. } else {
  1604. indiv.daily_cmass_leafloss = max(0.0, r);
  1605. }
  1606. indiv.daily_nmass_leafloss = 0.0;
  1607. vegetation.nextobj();
  1608. }
  1609. }
  1610. /// Forest-floor conditions
  1611. /** Called in cohort/individual mode (not population mode) to quantify growth
  1612. * conditions at the forest floor for each PFT
  1613. * Calculates net assimilation at top of grass canopy (or at soil surface if
  1614. * there is none).
  1615. */
  1616. void forest_floor_conditions(Patch& patch) {
  1617. const Climate& climate = patch.get_climate();
  1618. double lambda; // not used here
  1619. PhotosynthesisResult phot;
  1620. for (int p=0; p<npft; p++) {
  1621. Patchpft& ppft = patch.pft[p];
  1622. Standpft& spft = patch.stand.pft[p];
  1623. if (!spft.active) {
  1624. continue;
  1625. }
  1626. Pft& pft = spft.pft;
  1627. // Initialise net photosynthesis sum on first day of year
  1628. if (date.day == 0) {
  1629. ppft.anetps_ff = 0.0;
  1630. }
  1631. if (patch.stand.landcover != CROPLAND || pft.phenology != CROPGREEN && ppft.cropphen->growingseason) {
  1632. double assim = 0;
  1633. if (ppft.wstress_day) {
  1634. assimilation_wstress(pft, climate.co2, climate.temp, climate.par,
  1635. climate.daylength, patch.fpar_grass * ppft.phen, 1., ppft.gcbase_day,
  1636. spft.photosynthesis.vm, phot, lambda, 1.0, false);
  1637. assim = phot.net_assimilation();
  1638. }
  1639. else {
  1640. assim = spft.photosynthesis.net_assimilation() * ppft.phen * patch.fpar_grass;
  1641. }
  1642. // Accumulate annual value
  1643. ppft.anetps_ff += assim;
  1644. }
  1645. if (date.islastmonth && date.islastday) {
  1646. // Avoid negative ppft.anetps_ff
  1647. ppft.anetps_ff = max(0.0, ppft.anetps_ff);
  1648. if (ppft.anetps_ff > spft.anetps_ff_max) {
  1649. spft.anetps_ff_max = ppft.anetps_ff;
  1650. }
  1651. }
  1652. }
  1653. }
  1654. /// Initiate required variables for the module
  1655. void init_canexch(Patch& patch, Climate& climate, Vegetation& vegetation) {
  1656. if (date.day == 0) {
  1657. vegetation.firstobj();
  1658. while (vegetation.isobj) {
  1659. Individual& indiv = vegetation.getobj();
  1660. indiv.anpp = 0.0;
  1661. indiv.leafndemand = 0.0;
  1662. indiv.rootndemand = 0.0;
  1663. indiv.sapndemand = 0.0;
  1664. indiv.storendemand = 0.0;
  1665. indiv.hondemand = 0.0;
  1666. indiv.nday_leafon = 0;
  1667. indiv.avmaxnlim = 1.0;
  1668. indiv.cton_leaf_aavr = 0.0;
  1669. if (!negligible(indiv.cmass_leaf) && !negligible(indiv.nmass_leaf))
  1670. indiv.cton_leaf_aopt = indiv.cmass_leaf / indiv.nmass_leaf;
  1671. else
  1672. indiv.cton_leaf_aopt = indiv.pft.cton_leaf_max;
  1673. for (int m=0; m<12; m++) {
  1674. indiv.mlai[m] = 0.0;
  1675. indiv.mlai_max[m] = 0.0;
  1676. indiv.mgpp[m] = 0.0;
  1677. indiv.mlambda_gpp[m] = 0.0;
  1678. }
  1679. vegetation.nextobj();
  1680. }
  1681. }
  1682. patch.wdemand_day = 0;
  1683. }
  1684. /// Canopy exchange
  1685. /** Vegetation-atmosphere exchange of CO2 and water including calculations
  1686. * of actual evapotranspiration (AET), canopy conductance, carbon assimilation
  1687. * and autotrophic respiration.
  1688. * Should be called each simulation day for each modelled area or patch,
  1689. * following update of leaf phenology and soil temperature and prior to update
  1690. * of soil water.
  1691. */
  1692. void canopy_exchange(Patch& patch, Climate& climate) {
  1693. // NEW ASSUMPTIONS CONCERNING FPC AND FPAR (Ben Smith 2002-02-20)
  1694. // FPAR = average individual fraction of PAR absorbed on patch basis today,
  1695. // including effect of current leaf phenology (this differs from previous
  1696. // versions of LPJ-GUESS in which FPAR was on an FPC basis)
  1697. // FPC = PFT population (population mode), cohort (cohort mode) or individual
  1698. // (individual mode) fractional projective cover as a fraction of patch area
  1699. // (in population mode, corresponds to LPJF variable fpc_grid). Updated
  1700. // annually based on leaf-out LAI (see function allometry in growth module).
  1701. // (FPC was previously equal to summed crown area as a fraction of patch
  1702. // area in cohort/individual mode)
  1703. // Retrieve Vegetation and Climate objects for this patch
  1704. Vegetation& vegetation = patch.vegetation;
  1705. // Initial no-stress canopy exchange processes
  1706. init_canexch(patch, climate, vegetation);
  1707. // Canopy exchange processes
  1708. fpar(patch);
  1709. // Calculates no-stress daily values of photosynthesis and gpterm
  1710. photosynthesis_nostress(patch, climate);
  1711. // Nitrogen demand
  1712. ndemand(patch, vegetation);
  1713. // Nitrogen stress
  1714. vmax_nitrogen_stress(patch, climate, vegetation);
  1715. // Only these processes are affected in diurnal mode
  1716. for (Day day; day.period != date.subdaily; day.next()) {
  1717. wdemand(patch, climate, vegetation, day);
  1718. aet_water_stress(patch, vegetation, day);
  1719. water_scalar(patch, vegetation, day);
  1720. npp(patch, climate, vegetation, day);
  1721. }
  1722. leaf_senescence(vegetation);
  1723. // Forest-floor conditions
  1724. forest_floor_conditions(patch);
  1725. // Total potential evapotranspiration for patch (mm, patch basis)
  1726. // is a sum of: (1) potential transpirative demand of the vegetation;
  1727. // (2) evaporation of canopy-intercepted precipitation; and
  1728. // (3) evaporation from the soil surface of non-vegetated parts of patch -
  1729. // currently with boleht at 0, a patch has only two surfaces - vegetated
  1730. // and non-vegetated.
  1731. // This value is only diagnostic, it is not to be used in further calculations.
  1732. // Correct value should use daily value of patch.demand_leafon.
  1733. double pet_patch = patch.wdemand_day * patch.fpc_total + patch.intercep +
  1734. climate.eet * PRIESTLEY_TAYLOR * max(1.0-patch.fpc_total, 0.0);
  1735. patch.apet += pet_patch;
  1736. patch.mpet[date.month] += pet_patch;
  1737. patch.dpet[date.day] += pet_patch;
  1738. }
  1739. ///////////////////////////////////////////////////////////////////////////////////////
  1740. // REFERENCES
  1741. //
  1742. // LPJF refers to the original FORTRAN implementation of LPJ as described by Sitch
  1743. // et al 2001
  1744. // Collatz, GJ, Ball, JT, Grivet C & Berry, JA 1991 Physiological and
  1745. // environmental regulation of stomatal conductance, photosynthesis and
  1746. // transpiration: a model that includes a laminar boundary layer. Agricultural
  1747. // and Forest Meteorology 54: 107-136
  1748. // Collatz, GJ, Ribas-Carbo, M & Berry, JA 1992 Coupled photosynthesis-stomatal
  1749. // conductance models for leaves of C4 plants. Australian Journal of Plant
  1750. // Physiology 19: 519-538
  1751. // Comins, H. N. & McMurtrie, R. E. 1993. Long-Term Response of Nutrient-Limited
  1752. // Forests to CO2 Enrichment - Equilibrium Behavior of Plant-Soil Models.
  1753. // Ecological Applications, 3, 666-681.
  1754. // Farquhar GD & von Caemmerer 1982 Modelling of photosynthetic response to
  1755. // environmental conditions. In: Lange, OL, Nobel PS, Osmond CB, Ziegler H
  1756. // (eds) Physiological Plant Ecology II: Water Relations and Carbon
  1757. // Assimilation, Vol 12B. Springer, Berlin, pp 549-587.
  1758. // Friend, A. D., Stevens, A. K., Knox, R. G. & Cannell, M. G. R. 1997. A
  1759. // process-based, terrestrial biosphere model of ecosystem dynamics
  1760. // (Hybrid v3.0). Ecological Modelling, 95, 249-287.
  1761. // Gerten, D., Schaphoff, S., Haberlandt, U., Lucht, W. & Sitch, S. 2004.
  1762. // Terrestrial vegetation and water balance - hydrological evaluation of a
  1763. // dynamic global vegetation model. Journal of Hydrology 286: 249-270.
  1764. // Haxeltine A & Prentice IC 1996a BIOME3: an equilibrium terrestrial biosphere
  1765. // model based on ecophysiological constraints, resource availability, and
  1766. // competition among plant functional types. Global Biogeochemical Cycles 10:
  1767. // 693-709
  1768. // Haxeltine A & Prentice IC 1996b A general model for the light-use efficiency
  1769. // of primary production. Functional Ecology 10: 551-561
  1770. // Huntingford, C & Monteith, JL 1998. The behaviour of a mixed-layer model of the
  1771. // convective boundary layer coupled to a big leaf model of surface energy
  1772. // partitioning. Boundary Layer Meteorology 88: 87-101
  1773. // Lloyd, J & Taylor JA 1994 On the temperature dependence of soil respiration
  1774. // Functional Ecology 8: 315-323
  1775. // Monsi M & Saeki T 1953 Ueber den Lichtfaktor in den Pflanzengesellschaften und
  1776. // seine Bedeutung fuer die Stoffproduktion. Japanese Journal of Botany 14: 22-52
  1777. // Monteith, JL, 1995. Accomodation between transpiring vegetation and the convective
  1778. // boundary layer. Journal of Hydrology 166: 251-263.
  1779. // S. Olin, G. Schurgers, M. Lindeskog, D. Wårlind, B. Smith, P. Bodin, J. Holmér, and A. Arneth. 2015
  1780. // Biogeosciences Discuss., 12, 1047-1111. The impact of atmospheric CO2 and N management on yields
  1781. // and tissue C:N in the main wheat regions of Western Europe
  1782. // Peltoniemi, MS, Duursma, RA & Medlyn, BE. 2012. Co-optimal distribution of leaf
  1783. // nitrogen and hydraulic conductance in plant canopies. Tree Physiology, 32, 510-519.
  1784. // Prentice, IC, Sykes, MT & Cramer W (1993) A simulation model for the transient
  1785. // effects of climate change on forest landscapes. Ecological Modelling 65: 51-70.
  1786. // Press, WH, Teukolsky, SA, Vetterling, WT & Flannery, BT. 1986. Numerical
  1787. // Recipes in FORTRAN, 2nd ed. Cambridge University Press, Cambridge
  1788. // Sitch, S, Prentice IC, Smith, B & Other LPJ Consortium Members (2000) LPJ - a
  1789. // coupled model of vegetation dynamics and the terrestrial carbon cycle. In:
  1790. // Sitch, S. The Role of Vegetation Dynamics in the Control of Atmospheric CO2
  1791. // Content, PhD Thesis, Lund University, Lund, Sweden.
  1792. // Sprugel, DG, Ryan MG, Renee Brooks, J, Vogt, KA & Martin, TA (1996) Respiration
  1793. // from the organ level to the stand. In: Smith, WK & Hinckley, TM (eds),
  1794. // Physiological Ecology of Coniferous Forests.
  1795. // Zaehle, S. & Friend, A. D. 2010. Carbon and nitrogen cycle dynamics in the O-CN
  1796. // land surface model: 1. Model description, site-scale evaluation, and sensitivity
  1797. // to parameter estimates. Global Biogeochemical Cycles, 24.