somdynam.cpp 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554
  1. ///////////////////////////////////////////////////////////////////////////////////////
  2. /// \file somdynam.cpp
  3. /// \brief Soil organic matter dynamics
  4. ///
  5. /// \author Ben Smith (LPJ SOM dynamics, CENTURY), David Wårlind (CENTURY)
  6. /// $Date: 2013-10-10 10:20:33 +0200 (Thu, 10 Oct 2013) $
  7. ///
  8. ///////////////////////////////////////////////////////////////////////////////////////
  9. // WHAT SHOULD THIS FILE CONTAIN?
  10. // Module source code files should contain, in this order:
  11. // (1) a "#include" directive naming the framework header file. The framework header
  12. // file should define all classes used as arguments to functions in the present
  13. // module. It may also include declarations of global functions, constants and
  14. // types, accessible throughout the model code;
  15. // (2) other #includes, including header files for other modules accessed by the
  16. // present one;
  17. // (3) type definitions, constants and file scope global variables for use within
  18. // the present module only;
  19. // (4) declarations of functions defined in this file, if needed;
  20. // (5) definitions of all functions. Functions that are to be accessible to other
  21. // modules or to the calling framework should be declared in the module header
  22. // file.
  23. //
  24. // PORTING MODULES BETWEEN FRAMEWORKS:
  25. // Modules should be structured so as to be fully portable between models (frameworks).
  26. // When porting between frameworks, the only change required should normally be in the
  27. // "#include" directive referring to the framework header file.
  28. #include "config.h"
  29. #include "somdynam.h"
  30. #include "driver.h"
  31. #include <assert.h>
  32. #include <bitset>
  33. #include <vector>
  34. ///////////////////////////////////////////////////////////////////////////////////////
  35. // FILE SCOPE GLOBAL CONSTANTS
  36. // Turnover times (in years, approximate) for litter and SOM fractions at 10 deg C with
  37. // ample moisture (Meentemeyer 1978; Foley 1995)
  38. static const double TAU_LITTER=2.85; // Thonicke, Sitch, pers comm, 26/11/01
  39. static const double TAU_SOILFAST=33.0;
  40. static const double TAU_SOILSLOW=1000.0;
  41. static const double TILLAGE_FACTOR = 33.0 / 17.0;
  42. // (Inverse of "tillage factor" in Chatskikh et al. 2009, Value selected by T.Pugh)
  43. static const double FASTFRAC=0.985;
  44. // fraction of litter decomposition entering fast SOM pool
  45. static const double ATMFRAC=0.7;
  46. // fraction of litter decomposition entering atmosphere
  47. // Corresponds to the amount of soil available nitrogen where SOM C:N ratio reach
  48. // their minimum (nitrogen saturation) (Parton et al 1993, Fig. 4)
  49. // Comment: NMASS_SAT is too high when considering BNF - Zaehle
  50. static const double NMASS_SAT = 0.002 * 0.05;
  51. // Corresponds to the nitrogen concentration in litter where SOM C:N ratio reach
  52. // their minimum (nitrogen saturation) (Parton et al 1993, Fig. 4)
  53. static const double NCONC_SAT = 0.02;
  54. ///////////////////////////////////////////////////////////////////////////////////////
  55. // FILE SCOPE GLOBAL VARIABLES
  56. // Exponential decay constants for litter and SOM fractions
  57. // Values set from turnover times (constants above) on first call to decayrates
  58. static double k_litter10;
  59. static double k_soilfast10;
  60. static double k_soilslow10;
  61. static bool firsttime=true;
  62. // indicates whether function decayrates has been called before
  63. ///////////////////////////////////////////////////////////////////////////////////////
  64. // SETCONSTANTS
  65. // Internal function (do not call directly from framework)
  66. void setconstants() {
  67. // DESCRIPTION
  68. // Calculate exponential decay constants (annual basis) for litter and
  69. // SOM fractions first time function decayrates is called
  70. k_litter10=1.0/TAU_LITTER;
  71. k_soilfast10=1.0/TAU_SOILFAST;
  72. k_soilslow10=1.0/TAU_SOILSLOW;
  73. firsttime=false;
  74. }
  75. ///////////////////////////////////////////////////////////////////////////////////////
  76. // DECAYRATES
  77. // Internal function (do not call directly from framework)
  78. // used by som_dynamic_lpj()
  79. void decayrates(double wcont,double gtemp_soil,double& k_soilfast,double& k_soilslow,
  80. double& fr_litter,double& fr_soilfast,double& fr_soilslow, bool tillage) {
  81. // DESCRIPTION
  82. // Calculation of fractional decay amounts for litter and fast and slow SOM
  83. // fractions given current soil moisture and temperature
  84. // INPUT PARAMETERS
  85. // wcont = water content of upper soil layer (fraction of AWC)
  86. // gtemp_soil = respiration temperature response incorporating damping of Q10
  87. // response due to temperature acclimation (Eqn 11, Lloyd & Taylor
  88. // 1994)
  89. // OUTPUT PARAMETERS
  90. // k_soilfast = adjusted daily decay constant for fast SOM fraction
  91. // k_soilslow = adjusted daily decay constant for slow SOM fraction
  92. // fr_litter = litter fraction remaining following today's decomposition
  93. // fr_soilfast = fast SOM fraction remaining following today's decomposition
  94. // fr_soilslow = slow SOM fraction remaining following today's decomposition
  95. double moist_response; // moisture modifier of decomposition rate
  96. // On first call only: set exponential decay constants
  97. if (firsttime) setconstants();
  98. // Calculate response of soil respiration rate to moisture content of upper soil layer
  99. // Foley 1995 Eqn 19
  100. moist_response=0.25+0.75*wcont;
  101. // Calculate litter and SOM fractions remaining following today's decomposition
  102. // (Sitch et al 2000 Eqn 71) adjusting exponential decay constants by moisture and
  103. // temperature responses and converting from annual to daily basis
  104. // NB: Temperature response (gtemp; Lloyd & Taylor 1994) set by framework
  105. k_soilfast=k_soilfast10*gtemp_soil*moist_response/(double)date.year_length();
  106. if (tillage) {
  107. k_soilfast *= TILLAGE_FACTOR; // Increased HR for crops (tillage)
  108. }
  109. k_soilslow=k_soilslow10*gtemp_soil*moist_response/(double)date.year_length();
  110. fr_litter=exp(-k_litter10*gtemp_soil*moist_response/(double)date.year_length());
  111. fr_soilfast=exp(-k_soilfast);
  112. fr_soilslow=exp(-k_soilslow);
  113. }
  114. ///////////////////////////////////////////////////////////////////////////////////////
  115. // DECAYRATES
  116. // Should be called by framework on last day of simulation year, following call to
  117. // som_dynamics, once annual litter production and vegetation PFT composition are close
  118. // to their long term equilibrium (typically 500-1000 simulation years).
  119. // NB: should be called ONCE ONLY during simulation for a particular grid cell
  120. void equilsom_lpj(Soil& soil) {
  121. // DESCRIPTION
  122. // Analytically solves differential flux equations for fast and slow SOM pools
  123. // assuming annual litter inputs close to long term equilibrium
  124. // INPUT PARAMETER (class defined in framework header file)
  125. // soil = current soil status
  126. double nyear;
  127. // number of years over which decay constants and litter inputs averaged
  128. nyear=soil.soiltype.solvesom_end-soil.soiltype.solvesom_begin+1;
  129. soil.decomp_litter_mean/=nyear;
  130. soil.k_soilfast_mean/=nyear;
  131. soil.k_soilslow_mean/=nyear;
  132. soil.cpool_fast=(1.0-ATMFRAC)*FASTFRAC*soil.decomp_litter_mean/
  133. soil.k_soilfast_mean;
  134. soil.cpool_slow=(1.0-ATMFRAC)*(1.0-FASTFRAC)*soil.decomp_litter_mean/
  135. soil.k_soilslow_mean;
  136. }
  137. ///////////////////////////////////////////////////////////////////////////////////////
  138. // SOM DYNAMICS
  139. // To be called each simulation day for each modelled area or patch, following update
  140. // of soil temperature and soil water.
  141. void som_dynamics_lpj(Patch& patch, bool tillage) {
  142. // DESCRIPTION
  143. // Calculation of soil decomposition and transfer of C between litter and soil
  144. // organic matter pools.
  145. double k_soilfast; // adjusted daily decay constant for fast SOM fraction
  146. double k_soilslow; // adjusted daily decay constant for slow SOM fraction
  147. double fr_litter;
  148. // litter fraction remaining following one day's/one month's decomposition
  149. double fr_soilfast;
  150. // fast SOM fraction remaining following one day's/one month's decomposition
  151. double fr_soilslow;
  152. // slow SOM fraction remaining following one day's/one month's decomposition
  153. double decomp_litter; // litter decomposition today/this month (kgC/m2)
  154. double cflux; // accumulated C flux to atmosphere today/this month (kgC/m2)
  155. int p;
  156. // Obtain reference to Soil object
  157. Soil& soil=patch.soil;
  158. // Calculate decay constants and rates given today's soil moisture and
  159. // temperature
  160. decayrates(soil.wcont[0],soil.gtemp,k_soilfast,k_soilslow,fr_litter,
  161. fr_soilfast,fr_soilslow, tillage);
  162. // From year soil.solvesom_begin, update running means for later solution
  163. // (at year soil.solvesom_end) of equilibrium SOM pool sizes
  164. if (patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.soiltype.solvesom_begin) {
  165. soil.k_soilfast_mean += k_soilfast;
  166. soil.k_soilslow_mean += k_soilslow;
  167. }
  168. // Reduce litter and SOM pools, sum C flux to atmosphere from decomposition
  169. // and transfer correct proportions of litter decomposition to fast and slow
  170. // SOM pools
  171. // Reduce individual litter pools and calculate total litter decomposition
  172. // for today/this month
  173. decomp_litter=0.0;
  174. // Loop through PFTs
  175. for (p=0;p<npft;p++) { //NB. also inactive pft's
  176. // For this PFT ...
  177. decomp_litter+=(patch.pft[p].litter_leaf+
  178. patch.pft[p].litter_root+
  179. patch.pft[p].litter_sap+
  180. patch.pft[p].litter_heart+
  181. patch.pft[p].litter_repr)*(1.0-fr_litter);
  182. patch.pft[p].litter_leaf*=fr_litter;
  183. patch.pft[p].litter_root*=fr_litter;
  184. patch.pft[p].litter_sap*=fr_litter;
  185. patch.pft[p].litter_heart*=fr_litter;
  186. patch.pft[p].litter_repr*=fr_litter;
  187. }
  188. if (patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.soiltype.solvesom_begin)
  189. soil.decomp_litter_mean += decomp_litter;
  190. // Partition litter decomposition among fast and slow SOM pools
  191. // and flux to atmosphere
  192. // flux to atmosphere
  193. cflux=decomp_litter*ATMFRAC;
  194. // remaining decomposition - goes to ...
  195. decomp_litter-=cflux;
  196. // ... fast SOM pool ...
  197. soil.cpool_fast+=decomp_litter*FASTFRAC;
  198. // ... and slow SOM pool
  199. soil.cpool_slow+=decomp_litter*(1.0-FASTFRAC);
  200. // Increment C flux to atmosphere by SOM decomposition
  201. cflux+=soil.cpool_fast*(1.0-fr_soilfast)+soil.cpool_slow*(1.0-fr_soilslow);
  202. // Reduce SOM pools
  203. soil.cpool_fast*=fr_soilfast;
  204. soil.cpool_slow*=fr_soilslow;
  205. // Updated soil fluxes
  206. patch.fluxes.report_flux(Fluxes::SOILC, cflux);
  207. // Solve SOM pool sizes at end of year given by soil.solvesom_end
  208. if (patch.stand.get_gridcell().getsimulationyear(date.year) == soil.soiltype.solvesom_end && date.islastmonth && date.islastday)
  209. soil.decomp_litter_mean += decomp_litter;
  210. }
  211. /////////////////////////////////////////////////
  212. // CENTURY SOM DYNAMICS
  213. /// Data type representing a selection of SOM pools
  214. /** A selection of SOM pools is represented by a bitset,
  215. * the selected pools have their corresponding bits switched on.
  216. */
  217. typedef std::bitset<NSOMPOOL> SomPoolSelection;
  218. /// Reduce decay rates to keep the daily nitrogen balance in the soil
  219. /** Only a selected subset of the SOM pools (as specified by the caller),
  220. * are considered for reducion of decay rates.
  221. * Usually the first time is enough (decay rate reduction of litter).
  222. */
  223. void reduce_decay_rates(double decay_reduction[NSOMPOOL], double net_min_pool[NSOMPOOL], const SomPoolSelection& selected, double neg_nmass_avail) {
  224. int neg_min_pool[NSOMPOOL] = {0}; // Keeping track on which pools that are negative
  225. // Add up immobilization for considered pools
  226. double tot_neg_min = 0.0;
  227. for (int p = 0; p < NSOMPOOL; p++) {
  228. if (selected[p] && net_min_pool[p] < 0.0) {
  229. tot_neg_min += net_min_pool[p];
  230. neg_min_pool[p] = 1;
  231. }
  232. }
  233. // Calculate decay reduction
  234. double decay_red = 0.0;
  235. if (tot_neg_min < neg_nmass_avail) {
  236. // Enough to reduce decay rates for these pools to achieve a
  237. // net positive mineralization
  238. decay_red = 1.0 - (tot_neg_min - neg_nmass_avail) / tot_neg_min;
  239. }
  240. else {
  241. // Need to stop these pools from decaying to be able to get
  242. // a net positive mineralization
  243. decay_red = 1.0;
  244. }
  245. // Reduce decay rate for considered pools
  246. for (int p = 0; p < NSOMPOOL; p++) {
  247. if (selected[p]) {
  248. decay_reduction[p] = decay_red * neg_min_pool[p];
  249. }
  250. }
  251. }
  252. /// Set N:C ratios for SOM pools
  253. /** Set N:C ratios for slow, passive, humus and soil microbial pools
  254. * based on mineral nitrogen pool or litter nitrogen fraction (Parton et al 1993, Fig 4)
  255. */
  256. void setntoc(Soil& soil, double fac, pooltype pool, double cton_max, double cton_min,
  257. double fmin, double fmax) {
  258. if (fac <= fmin)
  259. soil.sompool[pool].ntoc = 1.0 / cton_max;
  260. else if (fac >= fmax)
  261. soil.sompool[pool].ntoc = 1.0 / cton_min;
  262. else {
  263. soil.sompool[pool].ntoc = 1.0 / (cton_min + (cton_max - cton_min) *
  264. (fmax - fac) / (fmax - fmin));
  265. }
  266. }
  267. /// Calculates CENTURY instantaneous decay rates
  268. /** Calculates CENTURY instantaneous decay rates given soil temperature,
  269. * water content of upper soil layer
  270. */
  271. void decayrates(Soil& soil, double temp_soil, double wcont_soil, bool tillage) {
  272. // Maximum exponential decay constants for each SOM pool (daily basis)
  273. // (Parton et al 2010, Figure 2)
  274. // plus Kirschbaum et al 2001 coarse woody debris decay
  275. const double K_MAX[] = {9.5e-3, 1.9e-2, 4.2e-2, 4.8e-4, 2.7e-2, 3.8e-2, 1.1e-2, 2.2e-3, 7.0e-2, 1.7e-3, 1.9e-6};
  276. // pools SURFSTRUCT,SOILSTRUCT,SOILMICRO,SURFHUMUS,SURFMICRO,SURFMETA,SURFFWD,SURFCWD,SOILMETA,SLOWSOM,PASSIVESOM
  277. // Modifier for effect of soil texture
  278. // Eqn 5, Parton et al 1993:
  279. const double texture_mod = 1.0 - 0.75 * (soil.soiltype.clay_frac + soil.soiltype.silt_frac);
  280. // Calculate decomposition temperature modifier (in range 0-1)
  281. // [A(T_soil), Eqn A9, Comins & McMurtrie 1993; ET, Friend et al 1997; abiotic
  282. // effect of soil temperature, Parton et al 1993, Fig 2)
  283. double temp_mod = 0.0;
  284. if (temp_soil > 0.0) {
  285. temp_mod = max(0.0,
  286. 0.0326 + 0.00351 * pow(temp_soil, 1.652) - pow(temp_soil / 41.748, 7.19));
  287. }
  288. // Calculate decomposition moisture modifier (in range 0-1)
  289. // Friend et al 1997, Eqn 53
  290. // (Parton et al 1993, Fig 2)
  291. // Water Filled Pore Spaces (wfps)
  292. // water holding capacity at wilting point (wp) and saturation capacity (wsats)
  293. // is calculated with the help of Cosby et al 1984;
  294. const double wfps = (wcont_soil * soil.soiltype.awc[0] + soil.soiltype.wp[0]) * 100.0 / soil.soiltype.wsats[0];
  295. double moist_mod;
  296. if (wfps < 60.0)
  297. moist_mod = exp((wfps - 60.0) * (wfps - 60.0) / -800.0);
  298. else
  299. moist_mod = 0.000371 * wfps * wfps - 0.0748 * wfps + 4.13;
  300. for (int p = 0; p < NSOMPOOL-1; p++) {
  301. // Calculate decay constant
  302. // (dC_I/dt / C_I; Parton et al 1993, Eqns 2-4)
  303. double k = K_MAX[p] * temp_mod * moist_mod;
  304. // Include effect of recalcitrance effect of lignin
  305. // Parton et al 1993 Eqn 2
  306. // Kirschbaum et al 2001 changed the exponential term
  307. // from 3 to 5.
  308. if (p == SURFSTRUCT || p == SOILSTRUCT || p == SURFFWD || p == SURFCWD) {
  309. k *= exp(-5.0 * soil.sompool[p].ligcfrac);
  310. }
  311. else if (p == SOILMICRO) {
  312. k *= texture_mod;
  313. }
  314. // Increased HR for crops (tillage)
  315. if (tillage && (p == SURFMICRO || p == SURFHUMUS || p == SOILMICRO || p == SLOWSOM)) {
  316. k *= TILLAGE_FACTOR;
  317. }
  318. // Calculate fraction of carbon pool remaining after today's decomposition
  319. soil.sompool[p].fracremain = exp(-k);
  320. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  321. soil.sompool[p].mfracremain_mean[date.month] += soil.sompool[p].fracremain / (double)date.ndaymonth[date.month];
  322. }
  323. }
  324. }
  325. /// Transfers specified fraction (frac) of today's decomposition
  326. /** Transfers specified fraction (frac) of today's decomposition in donor pool type
  327. * to receiver pool, transferring fraction respfrac of this to the accumulated CO2
  328. * flux respsum (representing total microbial respiration today)
  329. */
  330. void transferdecomp(Soil& soil, pooltype donor, pooltype receiver,
  331. double frac, double respfrac, double& respsum, double& nmin_actual,
  332. double& nimmob, double& net_min) {
  333. // decrement in donor carbon pool and nitrogen pools
  334. double cdec = soil.sompool[donor].cdec * frac;
  335. double ndec = soil.sompool[donor].ndec * frac;
  336. // associated nitrogen increment in receiver pool (Friend et al 1997, Eqn 49)
  337. double ninc = cdec * (1.0 - respfrac) * soil.sompool[receiver].ntoc;
  338. // if increase in receiver nitrogen greater than decrease in donor nitrogen,
  339. // balance must be immobilisation from mineral nitrogen pool
  340. // otherwise balance is nitrogen mineralisation
  341. if (ninc > ndec) {
  342. nimmob += ninc - ndec;
  343. net_min += ndec - ninc;
  344. }
  345. else {
  346. nmin_actual += ndec - ninc;
  347. net_min += ndec - ninc;
  348. }
  349. // "Transfer" carbon and nitrogen to receiver
  350. soil.sompool[receiver].delta_cmass += cdec * (1.0 - respfrac);
  351. soil.sompool[receiver].delta_nmass += ninc;
  352. // Transfer microbial respiration
  353. respsum += cdec * respfrac;
  354. if ((donor == SOILSTRUCT || donor == SOILMETA || donor == SURFHUMUS) && (receiver == SLOWSOM || receiver == SOILMICRO)) {
  355. soil.litter2soilc += cdec * (1.0 - respfrac);
  356. soil.litter2soiln += ninc;
  357. }
  358. }
  359. /// Fluxes between the CENTURY pools, and CO2 release to the atmosphere
  360. /** Daily or monthly fluxes between the ten CENTURY pools, and CO2 release to the atmosphere
  361. * Parton et al 1993, Fig 1; Comins & McMurtrie 1993, Appendix A
  362. *
  363. * \param ifequilsom Whether the function is called during calculation om SOM pool equilibrium,
  364. * \see equilsom(). During this stage, somfluxes shouldn't calculate decayrates
  365. * itself, and shouldn't produce output like fluxes etc.
  366. */
  367. void somfluxes(Patch& patch, bool ifequilsom, bool tillage) {
  368. double respsum, littrespsum;
  369. double leachsum_cmass, leachsum_nmass;
  370. double nmin_actual; // actual (not net) nitrogen mineralisation
  371. double nimmob; // nitrogen immobilisation
  372. double resp_nmin;
  373. const double EPS = 1.0e-16;
  374. Soil& soil = patch.soil;
  375. if (date.day == 0) {
  376. soil.anmin = 0.0;
  377. soil.animmob = 0.0;
  378. }
  379. // ecev3 - check
  380. if (soil.nmass_avail < -EPS) {
  381. dprintf("ERROR! Year %d day %d Stand %d: Negative soil.nmass_avail in somfluxes: %.15f\n", patch.stand.get_gridcell().simulationyear, date.day, patch.stand.id, soil.nmass_avail);
  382. dprintf("%.15f\n", soil.ninput);
  383. }
  384. // Warning if soil available nitrogen is negative (if happens once or so no problem, but if it propagates through time then it is)
  385. if (ifnlim) {
  386. assert(soil.nmass_avail > -EPS);
  387. }
  388. // Set N:C ratios for humus, soil microbial, passive and slow pool based on estimated mineral nitrogen pool
  389. // (Parton et al 1993, Fig 4)
  390. // ForCent (Parton 2010) values
  391. setntoc(soil, soil.nmass_avail, SLOWSOM, 30.0, 15.0, 0.0, NMASS_SAT);
  392. setntoc(soil, soil.nmass_avail, SOILMICRO, 15.0, 6.0, 0.0, NMASS_SAT);
  393. setntoc(soil, soil.nmass_avail, SURFHUMUS, 30.0, 15.0, 0.0, NMASS_SAT);
  394. if (!ifequilsom) {
  395. // Calculate potential fraction remaining following decay today for all pools
  396. // (assumes no nitrogen limitation)
  397. decayrates(soil, soil.temp, soil.wcont[0], tillage);
  398. }
  399. // Calculate decomposition in all pools assuming these decay rates
  400. // Save delta carbon and nitrogen mass
  401. bool net_mineralization = false;
  402. int times = 0;
  403. double decay_reduction[NSOMPOOL] = {0.0};
  404. double init_negative_nmass, init_ntoc_reduction;
  405. double ntoc_reduction = 0.8;
  406. // If necessary, the decay rates in the pools will be reduced in groups, one group
  407. // is reduced after each iteration in the loop below. The groups are defined by
  408. // how the pools feed into each other.
  409. SomPoolSelection reduction_groups[4];
  410. reduction_groups[0].set(SURFSTRUCT).set(SURFMETA).set(SURFFWD).set(SURFCWD).set(SOILSTRUCT).set(SOILMETA);
  411. reduction_groups[1].set(SURFMICRO);
  412. reduction_groups[2].set(SURFHUMUS);
  413. reduction_groups[3].set(SOILMICRO).set(SLOWSOM).set(PASSIVESOM);
  414. // If mineralization together with soil available nitrogen is negative then decay rates are decreased
  415. // The SOM system have five try to get a positive result, after that all pools decay rate has been
  416. // affected by nitrogen limitation
  417. while (!net_mineralization && times < 5) {
  418. respsum = 0.0;
  419. nmin_actual = 0.0;
  420. nimmob = 0.0;
  421. leachsum_cmass = 0.0;
  422. leachsum_nmass = 0.0;
  423. soil.litter2soilc = 0.0;
  424. soil.litter2soiln = 0.0;
  425. // Calculate decomposition in all pools assuming these decay rates
  426. for (int p = 0; p < NSOMPOOL; p++) {
  427. soil.sompool[p].cdec = soil.sompool[p].cmass * (1.0 - soil.sompool[p].fracremain) * (1.0 - decay_reduction[p]);
  428. soil.sompool[p].ndec = soil.sompool[p].nmass * (1.0 - soil.sompool[p].fracremain) * (1.0 - decay_reduction[p]);
  429. soil.sompool[p].delta_cmass = 0.0;
  430. soil.sompool[p].delta_nmass = 0.0;
  431. soil.sompool[p].delta_cmass -= soil.sompool[p].cdec;
  432. soil.sompool[p].delta_nmass -= soil.sompool[p].ndec;
  433. }
  434. double net_min[NSOMPOOL] = {0};
  435. // Partition potential decomposition among receiver pools
  436. // Donor pool SURFACE STRUCTURAL
  437. transferdecomp(soil, SURFSTRUCT, SURFMICRO, 1.0 - soil.sompool[SURFSTRUCT].ligcfrac,
  438. 0.6, respsum, nmin_actual, nimmob, net_min[SURFSTRUCT]);
  439. transferdecomp(soil, SURFSTRUCT, SURFHUMUS, soil.sompool[SURFSTRUCT].ligcfrac, 0.3,
  440. respsum, nmin_actual, nimmob, net_min[SURFSTRUCT]);
  441. // Donor pool SURFACE METABOLIC
  442. transferdecomp(soil, SURFMETA, SURFMICRO, 1.0, 0.6, respsum, nmin_actual, nimmob, net_min[SURFMETA]);
  443. // Donor pool SOIL STRUCTURAL
  444. transferdecomp(soil, SOILSTRUCT, SOILMICRO, 1.0 - soil.sompool[SOILSTRUCT].ligcfrac,
  445. 0.55, respsum, nmin_actual, nimmob, net_min[SOILSTRUCT]);
  446. transferdecomp(soil, SOILSTRUCT, SLOWSOM, soil.sompool[SOILSTRUCT].ligcfrac, 0.3,
  447. respsum, nmin_actual, nimmob, net_min[SOILSTRUCT]);
  448. // Donor pool SOIL METABOLIC
  449. transferdecomp(soil, SOILMETA, SOILMICRO, 1.0, 0.55, respsum, nmin_actual, nimmob, net_min[SOILMETA]);
  450. // Donor pool SURFACE FINE WOODY DEBRIS
  451. transferdecomp(soil, SURFFWD, SURFMICRO, 1.0 - soil.sompool[SURFFWD].ligcfrac,
  452. 0.76, respsum, nmin_actual, nimmob, net_min[SURFFWD]);
  453. transferdecomp(soil, SURFFWD, SURFHUMUS, soil.sompool[SURFFWD].ligcfrac, 0.4,
  454. respsum, nmin_actual, nimmob, net_min[SURFFWD]);
  455. // Donor pool SURFACE COARSE WOODY DEBRIS
  456. transferdecomp(soil, SURFCWD, SURFMICRO, 1.0 - soil.sompool[SURFCWD].ligcfrac,
  457. 0.9, respsum, nmin_actual, nimmob, net_min[SURFCWD]);
  458. transferdecomp(soil, SURFCWD, SURFHUMUS, soil.sompool[SURFCWD].ligcfrac, 0.5,
  459. respsum, nmin_actual, nimmob, net_min[SURFCWD]);
  460. // Litter respiration
  461. littrespsum = respsum;
  462. // Donor pool SURFACE MICROBE
  463. transferdecomp(soil, SURFMICRO, SURFHUMUS, 1.0, 0.6, respsum, nmin_actual, nimmob, net_min[SURFMICRO]);
  464. // Donor pool SURFACE HUMUS
  465. transferdecomp(soil, SURFHUMUS, SLOWSOM, 1.0, 0.6, respsum, nmin_actual, nimmob, net_min[SURFHUMUS]);
  466. // Donor pool SLOW SOM
  467. // First work out partitioning coefficients (Fig 1, Parton et al 1993)
  468. double csp = max(0.0, 0.003 - 0.009 * soil.soiltype.clay_frac);
  469. double respfrac = 0.55;
  470. double csa = 1.0 - csp - respfrac;
  471. transferdecomp(soil, SLOWSOM, SOILMICRO, csa, 0.0, respsum, nmin_actual, nimmob, net_min[SLOWSOM]);
  472. transferdecomp(soil, SLOWSOM, PASSIVESOM, csp, 0.0, respsum, nmin_actual, nimmob, net_min[SLOWSOM]);
  473. // Account for respiration flux
  474. // Nitrogen associated with this respiration is mineralised (Parton et al 1993, p 791)
  475. respsum += respfrac * soil.sompool[SLOWSOM].cdec;
  476. if (!negligible(soil.sompool[SLOWSOM].cmass)) {
  477. resp_nmin = respfrac * soil.sompool[SLOWSOM].cdec * soil.sompool[SLOWSOM].nmass / soil.sompool[SLOWSOM].cmass;
  478. nmin_actual += resp_nmin;
  479. net_min[SLOWSOM] += resp_nmin;
  480. }
  481. // Donor pool SOIL MICROBE
  482. // Fraction lost to microbial respiration (F_t, Parton et al 1993 Eqn 7)
  483. respfrac = max(0.0, 0.85 - 0.68 * (soil.soiltype.clay_frac + soil.soiltype.silt_frac));
  484. // Fraction entering passive SOM pool (Parton et al 1993, Eqn 9)
  485. double cap = 0.003 + 0.032 * soil.soiltype.clay_frac;
  486. transferdecomp(soil, SOILMICRO, PASSIVESOM, cap, 0.0, respsum, nmin_actual, nimmob, net_min[SOILMICRO]);
  487. // Fraction entering slow SOM pool
  488. csp = 1.0 - respfrac - soil.orgleachfrac - cap;
  489. transferdecomp(soil, SOILMICRO, SLOWSOM, csp, 0.0, respsum, nmin_actual, nimmob, net_min[SOILMICRO]);
  490. // Account for respiration flux
  491. // nitrogen associated with this respiration is mineralised (Parton et al 1993, p 791)
  492. respsum += respfrac * soil.sompool[SOILMICRO].cdec;
  493. // Account for organic carbon leaching loss
  494. leachsum_cmass = soil.orgleachfrac * soil.sompool[SOILMICRO].cdec;
  495. if (!negligible(soil.sompool[SOILMICRO].cmass)) {
  496. resp_nmin = respfrac * soil.sompool[SOILMICRO].cdec * soil.sompool[SOILMICRO].nmass / soil.sompool[SOILMICRO].cmass;
  497. nmin_actual += resp_nmin;
  498. net_min[SOILMICRO] += resp_nmin;
  499. // Account for organic nitrogen leaching loss
  500. leachsum_nmass = soil.orgleachfrac * soil.sompool[SOILMICRO].cdec * soil.sompool[SOILMICRO].nmass / soil.sompool[SOILMICRO].cmass;
  501. }
  502. // Donor pool PASSIVE SOM
  503. transferdecomp(soil, PASSIVESOM, SOILMICRO, 1.0, 0.55, respsum, nmin_actual, nimmob, net_min[PASSIVESOM]);
  504. // Total net mineralization
  505. double tot_net_min = nmin_actual - nimmob;
  506. // Estimate daily soil mineral nitrogen pool after decomposition
  507. // (negative value = immobilisation)
  508. if (tot_net_min + soil.nmass_avail + EPS >= 0.0) {
  509. net_mineralization = true;
  510. }
  511. else if (!ifnlim) {
  512. // Free Nitrogen years. Not minding immobilisation higher than nmass_avail
  513. if (patch.stand.get_gridcell().getsimulationyear(date.year) > freenyears) {
  514. // Immobilization larger than soil available nitrogen -> reduce targeted N concentration in SOM pool with flexible N:C ratios
  515. if (times == 0) {
  516. // initial reduction
  517. init_negative_nmass = tot_net_min + soil.nmass_avail;
  518. init_ntoc_reduction = ntoc_reduction;
  519. }
  520. else {
  521. // trying to match needed N:C reduction
  522. ntoc_reduction = min(init_ntoc_reduction, pow(init_ntoc_reduction, 1.0 / (1.0 - (tot_net_min + soil.nmass_avail) / init_negative_nmass) + 1.0));
  523. }
  524. soil.sompool[SLOWSOM].ntoc *= ntoc_reduction;
  525. soil.sompool[SOILMICRO].ntoc *= ntoc_reduction;
  526. soil.sompool[SURFHUMUS].ntoc *= ntoc_reduction;
  527. net_mineralization = false;
  528. }
  529. else {
  530. net_mineralization = true;
  531. }
  532. }
  533. else {
  534. // Immobilization larger than soil available nitrogen -> reduce decay rates
  535. if (times < 4) {
  536. reduce_decay_rates(decay_reduction, net_min, reduction_groups[times], tot_net_min + 0.9 * soil.nmass_avail);
  537. }
  538. net_mineralization = false;
  539. }
  540. times++;
  541. }
  542. // Update pool sizes
  543. for (int p = 0; p < NSOMPOOL-1; p++) {
  544. soil.sompool[p].cmass += soil.sompool[p].delta_cmass;
  545. soil.sompool[p].nmass += soil.sompool[p].delta_nmass;
  546. }
  547. if (!ifequilsom) {
  548. // Updated soil fluxes
  549. patch.fluxes.report_flux(Fluxes::SOILC, respsum);
  550. patch.fluxes.report_flux(Fluxes::LITTERC, littrespsum);
  551. // Transfer organic leaching to pool
  552. soil.sompool[LEACHED].cmass += leachsum_cmass;
  553. soil.sompool[LEACHED].nmass += leachsum_nmass;
  554. // CRESCENDO Organic carbon and nitrogen leaching
  555. patch.fluxes.report_flux(Fluxes::LEACHC, leachsum_cmass);
  556. patch.fluxes.report_flux(Fluxes::LEACHN, leachsum_nmass);
  557. #ifdef CRESCENDO_FACE
  558. patch.fluxes.report_flux(Fluxes::DLEACHN, leachsum_nmass);
  559. #endif
  560. // Sum annual organic nitrogen leaching
  561. soil.aorgNleach += leachsum_nmass;
  562. // Sum annual organic carbon leaching
  563. soil.aorgCleach += leachsum_cmass;
  564. // Sum annuals
  565. soil.anmin += nmin_actual;
  566. soil.animmob += nimmob;
  567. // report net nitrogen mineralisation
  568. patch.fluxes.report_flux(Fluxes::NMIN, nmin_actual - nimmob);
  569. #ifdef CRESCENDO_FACE
  570. // report daily gross and net nitrogen mineralisation
  571. patch.fluxes.report_flux(Fluxes::DGNMIN, nmin_actual);
  572. patch.fluxes.report_flux(Fluxes::DNMIN, nmin_actual - nimmob);
  573. #endif
  574. // CRESCENDO Carbon and nitrogen flux from litter to soil
  575. soil.patch.fluxes.report_flux(Fluxes::LITTERSOILC, soil.litter2soilc);
  576. soil.patch.fluxes.report_flux(Fluxes::LITTERSOILN, soil.litter2soiln);
  577. }
  578. // Adding mineral nitrogen to soil available pool
  579. soil.nmass_avail += nmin_actual - nimmob;
  580. // Estimate of N flux from soil (simple CLM-CN approach)
  581. double nflux = nmin_actual - nimmob > 0.0 ? (nmin_actual - nimmob) * 0.01 : 0.0;
  582. soil.nmass_avail -= nflux;
  583. if (!ifequilsom) {
  584. patch.fluxes.report_flux(Fluxes::N_SOIL, nflux);
  585. #ifdef CRESCENDO_FACE
  586. patch.fluxes.report_flux(Fluxes::DN_SOIL, nflux);
  587. #endif
  588. }
  589. // If no nitrogen limitation or during free nitrogen years set soil
  590. // available nitrogen to its saturation level.
  591. if (patch.stand.get_gridcell().getsimulationyear(date.year) <= freenyears)
  592. soil.nmass_avail = NMASS_SAT;
  593. }
  594. /// Litter lignin to N ratio (for leaf and root litter)
  595. /** Specific lignin fractions for leaf and root
  596. * are specified in transfer_litter()
  597. */
  598. double lignin_to_n_ratio(double cmass_litter, double nmass_litter, double LIGCFRAC, double cton_avr) {
  599. if (!negligible(nmass_litter) && ifnlim) {
  600. return max(0.0, LIGCFRAC * cmass_litter / nmass_litter);
  601. }
  602. else {
  603. return max(0.0, LIGCFRAC * cton_avr / (1.0 - nrelocfrac));
  604. }
  605. }
  606. /// Metabolic litter fraction (for leaf and root litter)
  607. /** Fm, Parton et al 1993, Eqn 1:
  608. * NB: incorrect/out-of-date intercept and slope given in Eqn 1; values used in
  609. * code of CENTURY 4.0 used instead (also correct in Parton et al. 1993, figure 1)
  610. *
  611. * \param lton Litter lignin:N ratio
  612. */
  613. double metabolic_litter_fraction(double lton) {
  614. return max(0.0, 0.85 - lton * 0.013);
  615. }
  616. /// Transfers litter from growth, mortality and fire
  617. /** Called monthly to transfer last year's litter from vegetation
  618. * (turnover, mortality and fire) to soil litter pools.
  619. * Alternatively, with daily allocation and harvest/turnover,
  620. * the litter produced a certain day.
  621. */
  622. void transfer_litter(Patch& patch) {
  623. // Transfer last year's litter to SOM pools
  624. // Leaf and fine root litter on first day of year (first day of july in SH)
  625. // Woody litter transfers a portion first day of every month
  626. // OR this year's litter on harvest/turnover day for stands with daily allocation
  627. if (!(date.dayofmonth == 0 || patch.is_litter_day))
  628. return;
  629. Soil& soil = patch.soil;
  630. double lat = patch.get_climate().lat;
  631. double EPS = -1.0e-16;
  632. // Leaf, root and wood litter lignin fractions
  633. // Leaf and root fractions: Comins & McMurtrie 1993; Friend et al 1997
  634. // Not sure of wood fraction
  635. const double LIGCFRAC_LEAF = 0.2;
  636. const double LIGCFRAC_ROOT = 0.16;
  637. const double LIGCFRAC_WOOD = 0.3;
  638. double ligcmass_new, ligcmass_old;
  639. // Fire
  640. double litterme[NSOMPOOL];
  641. litterme[SURFSTRUCT] = soil.sompool[SURFSTRUCT].cmass * soil.sompool[SURFSTRUCT].litterme;
  642. litterme[SURFMETA] = soil.sompool[SURFMETA].cmass * soil.sompool[SURFMETA].litterme;
  643. litterme[SURFFWD] = soil.sompool[SURFFWD].cmass * soil.sompool[SURFFWD].litterme;
  644. litterme[SURFCWD] = soil.sompool[SURFCWD].cmass * soil.sompool[SURFCWD].litterme;
  645. double fireresist[NSOMPOOL];
  646. fireresist[SURFSTRUCT] = soil.sompool[SURFSTRUCT].cmass * soil.sompool[SURFSTRUCT].fireresist;
  647. fireresist[SURFMETA] = soil.sompool[SURFMETA].cmass * soil.sompool[SURFMETA].fireresist;
  648. fireresist[SURFFWD] = soil.sompool[SURFFWD].cmass * soil.sompool[SURFFWD].fireresist;
  649. fireresist[SURFCWD] = soil.sompool[SURFCWD].cmass * soil.sompool[SURFCWD].fireresist;
  650. double leaf_littter = 0.0;
  651. double root_littter = 0.0;
  652. double wood_littter = 0.0;
  653. bool drop_leaf_root_litter = (lat >= 0.0 && date.month == 0) || (lat < 0.0 && date.month == 6) || patch.is_litter_day;
  654. patch.pft.firstobj();
  655. while (patch.pft.isobj) {
  656. Patchpft& pft=patch.pft.getobj();
  657. // For stands with yearly growth, drop leaf and root litter on first day of the year for northern hemisphere
  658. // and first day of the second half of the year for southern hemisphere.
  659. // For stands with daily growth, harvest and/or turnover, do this when patch.is_litter_day is true.
  660. if (drop_leaf_root_litter) {
  661. // LEAF
  662. leaf_littter += pft.litter_leaf;
  663. // Calculate inputs to surface structural and metabolic litter
  664. // Leaf litter lignin:N ratio
  665. double leaf_lton = lignin_to_n_ratio(pft.litter_leaf, pft.nmass_litter_leaf, LIGCFRAC_LEAF, pft.pft.cton_leaf_avr);
  666. // Metabolic litter fraction for leaf litter
  667. double fm = metabolic_litter_fraction(leaf_lton);
  668. ligcmass_old = soil.sompool[SURFSTRUCT].cmass * soil.sompool[SURFSTRUCT].ligcfrac;
  669. // Add to pools
  670. soil.sompool[SURFSTRUCT].cmass += pft.litter_leaf * (1.0 - fm);
  671. soil.sompool[SURFSTRUCT].nmass += pft.nmass_litter_leaf * (1.0 - fm);
  672. soil.sompool[SURFMETA].cmass += pft.litter_leaf * fm;
  673. soil.sompool[SURFMETA].nmass += pft.nmass_litter_leaf * fm;
  674. patch.fluxes.report_flux(Fluxes::VEGLITTERC, pft.litter_leaf);
  675. patch.fluxes.report_flux(Fluxes::VEGLITTERN, pft.nmass_litter_leaf);
  676. #ifdef CRESCENDO_FACE
  677. patch.fluxes.report_flux(Fluxes::DLEAFLITC, pft.litter_leaf);
  678. patch.fluxes.report_flux(Fluxes::DLEAFLITN, pft.nmass_litter_leaf);
  679. #endif
  680. // Save litter input for equilsom()
  681. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  682. soil.litterSolveSOM.add_litter(pft.litter_leaf * (1.0 - fm), pft.nmass_litter_leaf * (1.0 - fm), SURFSTRUCT);
  683. soil.litterSolveSOM.add_litter(pft.litter_leaf * fm, pft.nmass_litter_leaf * fm, SURFMETA);
  684. }
  685. // Fire
  686. litterme[SURFSTRUCT] += pft.litter_leaf * (1.0 - fm) * pft.pft.litterme;
  687. fireresist[SURFSTRUCT] += pft.litter_leaf * (1.0 - fm) * pft.pft.fireresist;
  688. litterme[SURFMETA] += pft.litter_leaf * fm * pft.pft.litterme;
  689. fireresist[SURFMETA] += pft.litter_leaf * fm * pft.pft.fireresist;
  690. // NB: reproduction litter cannot contain nitrogen!!
  691. ligcmass_new = pft.litter_leaf * (1.0 - fm) * LIGCFRAC_LEAF;
  692. if (negligible(soil.sompool[SURFSTRUCT].cmass)) {
  693. soil.sompool[SURFSTRUCT].ligcfrac = 0.0;
  694. }
  695. else {
  696. soil.sompool[SURFSTRUCT].ligcfrac = (ligcmass_new + ligcmass_old)/
  697. soil.sompool[SURFSTRUCT].cmass;
  698. }
  699. // ROOT
  700. root_littter += pft.litter_root;
  701. // Calculate inputs to soil structural and metabolic litter
  702. // Root litter lignin:N ratio
  703. double root_lton = lignin_to_n_ratio(pft.litter_root, pft.nmass_litter_root, LIGCFRAC_ROOT, pft.pft.cton_root_avr);
  704. // Metabolic litter fraction for root litter
  705. fm = metabolic_litter_fraction(root_lton);
  706. ligcmass_new = pft.litter_root * (1.0 - fm) * LIGCFRAC_ROOT;
  707. ligcmass_old = soil.sompool[SOILSTRUCT].cmass * soil.sompool[SOILSTRUCT].ligcfrac;
  708. // Add to pools and update lignin fraction in structural pool
  709. soil.sompool[SOILSTRUCT].cmass += pft.litter_root * (1.0 - fm);
  710. soil.sompool[SOILSTRUCT].nmass += pft.nmass_litter_root * (1.0 - fm);
  711. soil.sompool[SOILMETA].cmass += pft.litter_root * fm;
  712. soil.sompool[SOILMETA].nmass += pft.nmass_litter_root * fm;
  713. patch.fluxes.report_flux(Fluxes::VEGLITTERC, pft.litter_root);
  714. patch.fluxes.report_flux(Fluxes::VEGLITTERN, pft.nmass_litter_root);
  715. #ifdef CRESCENDO_FACE
  716. patch.fluxes.report_flux(Fluxes::DROOTLITC, pft.litter_root);
  717. patch.fluxes.report_flux(Fluxes::DROOTLITN, pft.nmass_litter_root);
  718. #endif
  719. // Save litter input for equilsom()
  720. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  721. soil.litterSolveSOM.add_litter(pft.litter_root * (1.0 - fm), pft.nmass_litter_root * (1.0 - fm), SOILSTRUCT);
  722. soil.litterSolveSOM.add_litter(pft.litter_root * fm, pft.nmass_litter_root * fm, SOILMETA);
  723. }
  724. if (negligible(soil.sompool[SOILSTRUCT].cmass)) {
  725. soil.sompool[SOILSTRUCT].ligcfrac = 0.0;
  726. }
  727. else {
  728. soil.sompool[SOILSTRUCT].ligcfrac = (ligcmass_new + ligcmass_old) /
  729. soil.sompool[SOILSTRUCT].cmass;
  730. }
  731. // Remove association with vegetation
  732. pft.litter_leaf = 0.0;
  733. pft.litter_root = 0.0;
  734. pft.nmass_litter_leaf = 0.0;
  735. pft.nmass_litter_root = 0.0;
  736. }
  737. // WOOD
  738. if (pft.pft.lifeform == TREE && date.dayofmonth == 0) {
  739. // Woody debris enters two woody litter pools as described in
  740. // Kirschbaum and Paul (2002).
  741. if (date.month == 0) {
  742. pft.litter_sap_year = pft.litter_sap;
  743. pft.nmass_litter_sap_year = pft.nmass_litter_sap;
  744. }
  745. // Monthly fraction of last years litter
  746. double litter_sap = pft.litter_sap_year / 12.0;
  747. double nmass_litter_sap = pft.nmass_litter_sap_year / 12.0;
  748. pft.litter_sap -= pft.litter_sap_year / 12.0;
  749. pft.nmass_litter_sap -= pft.nmass_litter_sap_year / 12.0;
  750. soil.sompool[SURFFWD].nmass += nmass_litter_sap;
  751. patch.fluxes.report_flux(Fluxes::VEGLITTERN, nmass_litter_sap);
  752. #ifdef CRESCENDO_FACE
  753. patch.fluxes.report_flux(Fluxes::DWOODLITN, nmass_litter_sap);
  754. #endif
  755. if (!negligible(litter_sap)) {
  756. // Fine woody debris
  757. wood_littter += litter_sap;
  758. // ecev3 - check
  759. if (litter_sap < EPS) {
  760. dprintf("ERROR! Year %d day %d Stand %d: Tiny litter_sap in transfer_litter: %.15f\n", date.year, date.day, soil.patch.stand.id, litter_sap);
  761. }
  762. assert(litter_sap >= EPS);
  763. ligcmass_new = litter_sap * LIGCFRAC_WOOD;
  764. ligcmass_old = soil.sompool[SURFFWD].cmass * soil.sompool[SURFFWD].ligcfrac;
  765. // Add to structural pool and update lignin fraction in pool
  766. soil.sompool[SURFFWD].cmass += litter_sap;
  767. patch.fluxes.report_flux(Fluxes::VEGLITTERC, litter_sap);
  768. #ifdef CRESCENDO_FACE
  769. patch.fluxes.report_flux(Fluxes::DWOODLITC, litter_sap);
  770. #endif
  771. // Save litter input for equilsom()
  772. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  773. soil.litterSolveSOM.add_litter(litter_sap, nmass_litter_sap, SURFFWD);
  774. }
  775. if (negligible(soil.sompool[SURFFWD].cmass)) {
  776. soil.sompool[SURFFWD].ligcfrac = 0.0;
  777. }
  778. else {
  779. double ligcfrac = (ligcmass_new + ligcmass_old) /
  780. soil.sompool[SURFFWD].cmass;
  781. soil.sompool[SURFFWD].ligcfrac = ligcfrac;
  782. }
  783. // Fire
  784. litterme[SURFFWD] += litter_sap * pft.pft.litterme;
  785. fireresist[SURFFWD] += litter_sap * pft.pft.fireresist;
  786. }
  787. if (date.month == 0) {
  788. pft.litter_heart_year = pft.litter_heart;
  789. pft.nmass_litter_heart_year = pft.nmass_litter_heart;
  790. }
  791. // Monthly fraction of last years litter
  792. double litter_heart = pft.litter_heart_year / 12.0;
  793. double nmass_litter_heart = pft.nmass_litter_heart_year / 12.0;
  794. pft.litter_heart -= pft.litter_heart_year / 12.0;
  795. pft.nmass_litter_heart -= pft.nmass_litter_heart_year / 12.0;
  796. soil.sompool[SURFCWD].nmass += nmass_litter_heart;
  797. patch.fluxes.report_flux(Fluxes::VEGLITTERN, nmass_litter_heart);
  798. #ifdef CRESCENDO_FACE
  799. patch.fluxes.report_flux(Fluxes::DWOODLITN, nmass_litter_heart);
  800. #endif
  801. if (!negligible(litter_heart)) {
  802. // Coarse woody debris
  803. wood_littter += litter_heart;
  804. // ecev3 - check
  805. if (litter_heart < EPS) {
  806. dprintf("ERROR! Year %d day %d Stand %d: Tiny litter_heart in transfer_litter: %.15f\n", date.year, date.day, soil.patch.stand.id, litter_heart);
  807. }
  808. assert(litter_heart >= EPS);
  809. ligcmass_new = litter_heart * LIGCFRAC_WOOD;
  810. ligcmass_old = soil.sompool[SURFCWD].cmass * soil.sompool[SURFCWD].ligcfrac;
  811. // Add to structural pool and update lignin fraction in pool
  812. soil.sompool[SURFCWD].cmass += litter_heart;
  813. patch.fluxes.report_flux(Fluxes::VEGLITTERC, litter_heart);
  814. #ifdef CRESCENDO_FACE
  815. patch.fluxes.report_flux(Fluxes::DWOODLITC, litter_heart);
  816. #endif
  817. // Save litter input for equilsom()
  818. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  819. soil.litterSolveSOM.add_litter(litter_heart, nmass_litter_heart, SURFCWD);
  820. }
  821. if (negligible(soil.sompool[SURFCWD].cmass)) {
  822. soil.sompool[SURFCWD].ligcfrac = 0.0;
  823. }
  824. else {
  825. double ligcfrac = (ligcmass_new + ligcmass_old) /
  826. soil.sompool[SURFCWD].cmass;
  827. soil.sompool[SURFCWD].ligcfrac = ligcfrac;
  828. }
  829. // Fire
  830. litterme[SURFCWD] += litter_heart * pft.pft.litterme;
  831. fireresist[SURFCWD] += litter_heart * pft.pft.fireresist;
  832. }
  833. }
  834. // Remove association with vegetation
  835. if (date.islastmonth) {
  836. pft.litter_sap = 0.0;
  837. pft.litter_heart = 0.0;
  838. pft.nmass_litter_sap = 0.0;
  839. pft.nmass_litter_heart = 0.0;
  840. }
  841. patch.pft.nextobj();
  842. }
  843. // FIRE
  844. if (soil.sompool[SURFSTRUCT].cmass > 0.0) {
  845. soil.sompool[SURFSTRUCT].litterme = litterme[SURFSTRUCT] / soil.sompool[SURFSTRUCT].cmass;
  846. soil.sompool[SURFSTRUCT].fireresist = fireresist[SURFSTRUCT] / soil.sompool[SURFSTRUCT].cmass;
  847. }
  848. if (soil.sompool[SURFMETA].cmass > 0.0) {
  849. soil.sompool[SURFMETA].litterme = litterme[SURFMETA] / soil.sompool[SURFMETA].cmass;
  850. soil.sompool[SURFMETA].fireresist = fireresist[SURFMETA] / soil.sompool[SURFMETA].cmass;
  851. }
  852. if (soil.sompool[SURFFWD].cmass > 0.0) {
  853. soil.sompool[SURFFWD].litterme = litterme[SURFFWD] / soil.sompool[SURFFWD].cmass;
  854. soil.sompool[SURFFWD].fireresist = fireresist[SURFFWD] / soil.sompool[SURFFWD].cmass;
  855. }
  856. if (soil.sompool[SURFCWD].cmass > 0.0) {
  857. soil.sompool[SURFCWD].litterme = litterme[SURFCWD] / soil.sompool[SURFCWD].cmass;
  858. soil.sompool[SURFCWD].fireresist = fireresist[SURFCWD] / soil.sompool[SURFCWD].cmass;
  859. }
  860. // Calculate total litter carbon and nitrogen mass for set N:C ratio of surface microbial pool
  861. double litter_cmass = soil.sompool[SURFSTRUCT].cmass + soil.sompool[SURFMETA].cmass +
  862. soil.sompool[SURFFWD].cmass + soil.sompool[SURFCWD].cmass;
  863. double litter_nmass = soil.sompool[SURFSTRUCT].nmass + soil.sompool[SURFMETA].nmass +
  864. soil.sompool[SURFFWD].nmass + soil.sompool[SURFCWD].nmass;
  865. // Set N:C ratio of surface microbial pool based on N:C ratio of litter from all PFTs
  866. // Parton et al 1993 Fig 4. Dry mass litter == cmass litter * 2
  867. if (!negligible(litter_cmass)) {
  868. setntoc(soil, litter_nmass / (litter_cmass * 2.0), SURFMICRO, 20.0, 10.0, 0.0, NCONC_SAT);
  869. }
  870. // Add litter to solvesom array every month
  871. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  872. if (date.dayofmonth == 0) {
  873. soil.solvesom.push_back(soil.litterSolveSOM);
  874. soil.litterSolveSOM.clear();
  875. }
  876. }
  877. }
  878. /// LEACHING
  879. /** Leaching fractions for both organic and mineral leaching
  880. * Should be called every day in both daily and monthly mode
  881. */
  882. void leaching(Soil& soil) {
  883. double minleachfrac;
  884. const double EPS = 1.0e-16;
  885. // ecev3 - check
  886. if (soil.dperc < -EPS) {
  887. dprintf("ERROR! Year %d day %d Stand %d: Negative soil.dperc in leaching: %.15f\n", date.year, date.day, soil.patch.stand.id, soil.dperc);
  888. }
  889. // Warning if soil available nitrogen is negative (if happens once or so no problem, but if it propagates through time then it is)
  890. if (ifnlim) {
  891. assert(soil.dperc > -EPS);
  892. }
  893. if (!negligible(soil.dperc)) {
  894. // Leaching from available nitrogen mineral pool
  895. // in proportion to amount of water drainage
  896. minleachfrac = soil.dperc / (soil.dperc + soil.soiltype.awc[0] * soil.wcont[0] + soil.soiltype.awc[1] * soil.wcont[1]);
  897. // ecev3 - check
  898. if (minleachfrac < -EPS) {
  899. dprintf("ERROR! Year %d day %d Stand %d: Negative soil.minleachfrac in leaching: %.15f\n", date.year, date.day, soil.patch.stand.id, minleachfrac);
  900. }
  901. if (ifnlim) {
  902. assert(minleachfrac > -EPS);
  903. }
  904. // Leaching from decayed organic carbon/nitrogen
  905. // using Parton et al. eqn. 8; CENTURY 5 parameter update; from equation: C Leached=microbial_C*[OMLECH(1)+OMLECH(2)*sand_fraction]*[1.0f-(OMLECH(3)-water_leaching)/OMLECH(3)],
  906. // reorganised as: leachfrac = [water_leaching/OMLECH(3)]*[OMLECH(1)+OMLECH(2)*sand_fraction]
  907. // water_leaching was originally in cm/month
  908. double OMLECH_1 = 0.03;
  909. double OMLECH_2 = 0.12;
  910. double OMLECH_3 = 1.9; // saturation point in leaching equation (cm H20/month)
  911. double cmpermonth_to_mmperday = 10.0 * 12.0 / 365.0;
  912. soil.orgleachfrac = min(1.0, soil.dperc / (OMLECH_3 * cmpermonth_to_mmperday)) * (OMLECH_1 + OMLECH_2 * soil.soiltype.sand_frac);
  913. // ecev3 - check
  914. if (soil.orgleachfrac < -EPS) {
  915. dprintf("ERROR! Year %d day %d Stand %d: Negative soil.orgleachfrac in leaching: %.15f\n", date.year, date.day, soil.patch.stand.id, soil.orgleachfrac);
  916. }
  917. if (ifnlim) {
  918. assert(soil.orgleachfrac > -EPS);
  919. }
  920. }
  921. else {
  922. minleachfrac = 0.0;
  923. soil.orgleachfrac = 0.0;
  924. }
  925. // Leaching of soil mineral nitrogen
  926. // Allowed on days with residual nitrogen following vegetation uptake
  927. // in proportion to amount of water drainage
  928. if (soil.nmass_avail > 0.0) {
  929. double leaching = soil.nmass_avail * minleachfrac;
  930. soil.nmass_avail -= leaching;
  931. soil.aminleach += leaching;
  932. soil.sompool[LEACHED].nmass += leaching;
  933. soil.patch.fluxes.report_flux(Fluxes::LEACHN, leaching);
  934. #ifdef CRESCENDO_FACE
  935. soil.patch.fluxes.report_flux(Fluxes::DLEACHN, leaching);
  936. #endif
  937. }
  938. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  939. soil.morgleach_mean[date.month] += soil.orgleachfrac / (double)date.ndaymonth[date.month];
  940. soil.mminleach_mean[date.month] += minleachfrac / (double)date.ndaymonth[date.month];
  941. }
  942. }
  943. /// Nitrogen addition to the soil
  944. /** Daily nitrogen addition to the soil
  945. * from deposition and fixation
  946. */
  947. void soilnadd(Patch& patch) {
  948. Soil& soil = patch.soil;
  949. double nflux = 0.01 * soil.ninput;
  950. soil.ninput -= nflux;
  951. patch.fluxes.report_flux(Fluxes::N_SOIL, nflux);
  952. #ifdef CRESCENDO_FACE
  953. patch.fluxes.report_flux(Fluxes::DN_SOIL, nflux);
  954. #endif
  955. // Nitrogen deposition and fertilization input to the soil (calculated in snow_ninput())
  956. soil.nmass_avail += soil.ninput;
  957. // Nitrogen fixation
  958. // If soil available nitrogen is above the value for minimum SOM C:N ratio, then
  959. // nitrogen fixation is reduced (nitrogen rich soils)
  960. if (soil.nmass_avail < NMASS_SAT) {
  961. double daily_nfix = soil.anfix_calc / date.year_length();
  962. if (soil.nmass_avail + daily_nfix < NMASS_SAT) {
  963. soil.nmass_avail += daily_nfix;
  964. soil.anfix += daily_nfix;
  965. }
  966. else {
  967. daily_nfix = NMASS_SAT - soil.nmass_avail;
  968. soil.anfix += daily_nfix;
  969. soil.nmass_avail = NMASS_SAT;
  970. }
  971. patch.fluxes.report_flux(Fluxes::NFIX, daily_nfix);
  972. #ifdef CRESCENDO_FACE
  973. patch.fluxes.report_flux(Fluxes::DNFIX, daily_nfix);
  974. #endif
  975. }
  976. // Calculate nitrogen fixation (Cleveland et al. 1999)
  977. // by using five year average aaet
  978. if (date.islastmonth && date.islastday) {
  979. // Add this year's AET to aaet_5 which keeps track of the last 5 years
  980. patch.aaet_5.add(patch.aaet+patch.aevap+patch.aintercep);
  981. // Calculate estimated nitrogen fixation (aaet should be in cm/yr, eqn is in nitrogen/ha/yr)
  982. double cmtomm = 0.1;
  983. double hatom2 = 0.0001;
  984. soil.anfix_calc = max((nfix_a * patch.aaet_5.mean() * cmtomm + nfix_b) * hatom2, 0.0);
  985. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  986. soil.anfix_mean += soil.anfix;
  987. }
  988. }
  989. }
  990. /// Vegetation nitrogen uptake
  991. /** Daily vegetation uptake of mineral nitrogen
  992. * Partitioned among individuals according to todays nitrogen demand
  993. * and individuals root area
  994. */
  995. void vegetation_n_uptake(Patch& patch) {
  996. // Daily nitrogen demand given by:
  997. // For individual:
  998. // (1) ndemand = leafndemand + rootndemand + sapndemand;
  999. // where
  1000. // leafndemand is leaf demand based on vmax
  1001. // rootndemand is based on optimal leaf C:N ratio
  1002. // sapndemand is based on optimal leaf C:N ratio
  1003. //
  1004. // Actual nitrogen uptake for each day and individual given by:
  1005. // (2) nuptake = ndemand * fnuptake
  1006. // where fnuptake is individual uptake capacity calculated in fnuptake in canexch.cpp
  1007. double nuptake_day, orignmass;
  1008. Vegetation& vegetation=patch.vegetation;
  1009. Soil& soil = patch.soil;
  1010. orignmass = soil.nmass_avail;
  1011. // Loop through individuals
  1012. vegetation.firstobj();
  1013. while (vegetation.isobj) {
  1014. Individual& indiv = vegetation.getobj();
  1015. if (date.day == 0)
  1016. indiv.anuptake = 0.0;
  1017. nuptake_day = indiv.ndemand * indiv.fnuptake;
  1018. indiv.anuptake += nuptake_day;
  1019. indiv.nmass_leaf += indiv.leaffndemand * nuptake_day;
  1020. indiv.nmass_root += indiv.rootfndemand * nuptake_day;
  1021. indiv.nmass_sap += indiv.sapfndemand * nuptake_day;
  1022. if (indiv.pft.phenology == CROPGREEN && ifnlim)
  1023. indiv.cropindiv->nmass_agpool += indiv.storefndemand * nuptake_day;
  1024. else
  1025. indiv.nstore_longterm += indiv.storefndemand * nuptake_day;
  1026. soil.nmass_avail -= nuptake_day;
  1027. indiv.report_flux(Fluxes::NUP, nuptake_day);
  1028. #ifdef CRESCENDO_FACE
  1029. indiv.report_flux(Fluxes::DNUP, nuptake_day);
  1030. indiv.report_flux(Fluxes::DNGL, indiv.leaffndemand * nuptake_day);
  1031. indiv.report_flux(Fluxes::DNGW, indiv.sapfndemand * nuptake_day);
  1032. indiv.report_flux(Fluxes::DNGR, indiv.rootfndemand * nuptake_day);
  1033. #endif
  1034. if (!negligible(indiv.phen))
  1035. indiv.cton_leaf_aavr += min(indiv.cton_leaf(),indiv.pft.cton_leaf_max);
  1036. vegetation.nextobj();
  1037. }
  1038. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr && !negligible(orignmass)) {
  1039. soil.fnuptake_mean[date.month] += (1.0 - soil.nmass_avail / orignmass) / (double)date.ndaymonth[date.month];
  1040. }
  1041. }
  1042. /// Add litter to equilsom()
  1043. void add_litter(Soil& soil, int year, int pool) {
  1044. // If LUC have occurred in between solvesomcent_beginyr and solvesomcent_endyr then array might be too smalll
  1045. if (year >= (int)soil.solvesom.size())
  1046. year = year%(int)soil.solvesom.size();
  1047. // Add to litter pools
  1048. soil.sompool[pool].cmass += soil.solvesom[year].get_clitter(pool);
  1049. soil.sompool[pool].nmass += soil.solvesom[year].get_nlitter(pool);
  1050. }
  1051. /// Iteratively solving differential flux equations for century SOM pools
  1052. /** Iteratively solving differential flux equations for century SOM pools
  1053. * assuming annual litter inputs, nitrogen uptake and leaching is close
  1054. * to long term equilibrium
  1055. */
  1056. void equilsom(Soil& soil) {
  1057. if (!(soil.patch.stand.get_gridcell().getsimulationyear(date.year) == soil.solvesomcent_endyr && date.islastmonth && date.islastday))
  1058. return;
  1059. // Number of years to run SOM pools, value chosen to get cold climates to equilibrium
  1060. const int EQUILSOM_YEARS = 40000;
  1061. Patch& patch = soil.patch;
  1062. const Climate& climate = soil.patch.get_climate();
  1063. // Save nmass_avail status
  1064. double save_nmass_avail = soil.nmass_avail;
  1065. // Number of years with mean input data
  1066. int nyear = soil.solvesomcent_endyr - soil.solvesomcent_beginyr + 1;
  1067. for (int m = 0; m < 12; m++) {
  1068. // Monthly average decay rates
  1069. for (int p = 0; p < NSOMPOOL-1; p++) {
  1070. soil.sompool[p].mfracremain_mean[m] = pow(soil.sompool[p].mfracremain_mean[m] / (double)nyear, (double)date.ndaymonth[m]);
  1071. }
  1072. // Monthly average mineral nitrogen uptake
  1073. soil.fnuptake_mean[m] /= (double)nyear;
  1074. // Monthly average organic carbon and nitrogen leaching
  1075. soil.morgleach_mean[m] /= (double)nyear;
  1076. // Monthly average mineral nitrogen leaching
  1077. soil.mminleach_mean[m] /= (double)nyear;
  1078. }
  1079. // Annual average nitrogen fixation
  1080. soil.anfix_mean /= (double)nyear;
  1081. // Spin SOM pools with saved litter input, nitrogen addition and fractions of
  1082. // nitrogen uptake and leaching for EQUILSOM_YEARS years with monthly timesteps
  1083. for (int yr = 0; yr < EQUILSOM_YEARS; yr++) {
  1084. // Which year in saved data set
  1085. int savedyear = yr%nyear;
  1086. // Monthly time steps
  1087. for (int m = 0; m < 12; m++) {
  1088. // Transfer yearly mean litter on first day of year
  1089. add_litter(soil, savedyear*12+m, SURFSTRUCT);
  1090. add_litter(soil, savedyear*12+m, SURFMETA);
  1091. add_litter(soil, savedyear*12+m, SOILSTRUCT);
  1092. add_litter(soil, savedyear*12+m, SOILMETA);
  1093. add_litter(soil, savedyear*12+m, SURFFWD);
  1094. add_litter(soil, savedyear*12+m, SURFCWD);
  1095. // Calculate total litter carbon and nitrogen mass for set N:C ratio of surface microbial pool
  1096. double litter_cmass = soil.sompool[SURFSTRUCT].cmass + soil.sompool[SURFMETA].cmass +
  1097. soil.sompool[SURFFWD].cmass + soil.sompool[SURFCWD].cmass;
  1098. double litter_nmass = soil.sompool[SURFSTRUCT].nmass + soil.sompool[SURFMETA].nmass +
  1099. soil.sompool[SURFFWD].nmass + soil.sompool[SURFCWD].nmass;
  1100. // Set N:C ratio of surface microbial pool based on N:C ratio of litter from all PFTs
  1101. if (!negligible(litter_cmass)) {
  1102. setntoc(soil, litter_nmass / (litter_cmass * 2.0), SURFMICRO, 20.0, 10.0, 0.0, NCONC_SAT);
  1103. }
  1104. // Monthly nitrogen uptake
  1105. soil.nmass_avail *= (1.0 - soil.fnuptake_mean[m]);
  1106. // Monthly mineral nitrogen leaching
  1107. soil.nmass_avail *= (1.0 - soil.mminleach_mean[m]);
  1108. // Monthly nitrogen addition to the system
  1109. soil.nmass_avail += (climate.andep + soil.anfix_mean) / 12.0;
  1110. // Monthly decomposition and fluxes between SOM pools
  1111. // Set this months decay rates
  1112. for (int p = 0; p < NSOMPOOL-1; p++) {
  1113. soil.sompool[p].fracremain = soil.sompool[p].mfracremain_mean[m];
  1114. }
  1115. // Set this months organic nitrogen leaching fraction
  1116. soil.orgleachfrac = soil.morgleach_mean[m];
  1117. // Monthly decomposition and fluxes between SOM pools
  1118. // and nitrogen flux from soil
  1119. somfluxes(patch, true, false);
  1120. }
  1121. }
  1122. // Reset nmass_avail status
  1123. soil.nmass_avail = save_nmass_avail;
  1124. // Reset variables for next equilsom()
  1125. for (int m = 0; m < 12; m++) {
  1126. for (int p = 0; p < NSOMPOOL-1; p++) {
  1127. soil.sompool[p].mfracremain_mean[m] = 0.0;
  1128. }
  1129. soil.fnuptake_mean[m] = 0.0;
  1130. soil.morgleach_mean[m] = 0.0;
  1131. soil.mminleach_mean[m] = 0.0;
  1132. }
  1133. soil.anfix_mean = 0.0;
  1134. soil.solvesom.clear();
  1135. std::vector<LitterSolveSOM>().swap(soil.solvesom);
  1136. }
  1137. /// SOM CENTURY DYNAMICS
  1138. /** To be called each simulation day for each modelled patch, following update
  1139. * of soil temperature and soil water.
  1140. * Transfers litter, performes nitrogen uptake and addition, leaching and decomposition.
  1141. */
  1142. void som_dynamics_century(Patch& patch, bool tillage) {
  1143. // Transfer litter first day of every month or other harvest/turnover day
  1144. transfer_litter(patch);
  1145. // Daily nitrogen uptake
  1146. vegetation_n_uptake(patch);
  1147. // Daily mineral and organic nitrogen leaching
  1148. leaching(patch.soil);
  1149. // Daily nitrogen addition to the soil
  1150. soilnadd(patch);
  1151. // Daily or monthly decomposition and fluxes between SOM pools
  1152. somfluxes(patch, false, tillage);
  1153. // Solve SOM pool sizes at end of year given by soil.solvesomcent_endyr
  1154. equilsom(patch.soil);
  1155. }
  1156. /// Choose between CENTURY or standard LPJ SOM dynamics
  1157. /**
  1158. */
  1159. void som_dynamics(Patch& patch) {
  1160. bool tillage = iftillage && patch.stand.landcover == CROPLAND;
  1161. if (ifcentury) som_dynamics_century(patch, tillage);
  1162. else som_dynamics_lpj(patch, tillage);
  1163. }
  1164. ///////////////////////////////////////////////////////////////////////////////////////
  1165. // REFERENCES
  1166. //
  1167. // Chatskikh, D., Hansen, S., Olesen, J.E. & Petersen, B.M. 2009. A simplified modelling approach
  1168. // for quantifying tillage effects on soil carbon stocks. Eur.J.Soil.Sci., 60:924-934.
  1169. // CENTURY Soil Organic Matter Model Version 5; Century User's Guide and Reference.
  1170. // http://www.nrel.colostate.edu/projects/century5/reference/index.htm; accessed Oct.7, 2016.
  1171. // Comins, H. N. & McMurtrie, R. E. 1993. Long-Term Response of Nutrient-Limited
  1172. // Forests to CO2 Enrichment - Equilibrium Behavior of Plant-Soil Models.
  1173. // Ecological Applications, 3, 666-681.
  1174. // Cosby, B. J., Hornberger, C. M., Clapp, R. B., & Ginn, T. R. 1984 A statistical exploration
  1175. // of the relationships of soil moisture characteristic to the physical properties of soil.
  1176. // Water Resources Research, 20: 682-690.
  1177. // Cleveland C C (1999) Global patterns of terrestrial biological nitrogen (N2) fixation
  1178. // in natural ecosystems. GBC 13: 623-645
  1179. // Foley J A 1995 An equilibrium model of the terrestrial carbon budget
  1180. // Tellus (1995), 47B, 310-319
  1181. // Friend, A. D., Stevens, A. K., Knox, R. G. & Cannell, M. G. R. 1997. A
  1182. // process-based, terrestrial biosphere model of ecosystem dynamics
  1183. // (Hybrid v3.0). Ecological Modelling, 95, 249-287.
  1184. // Kirschbaum, M. U. F. and K. I. Paul (2002). "Modelling C and N dynamics in forest soils
  1185. // with a modified version of the CENTURY model." Soil Biology & Biochemistry 34(3): 341-354.
  1186. // Meentemeyer, V. (1978) Macroclimate and lignin control of litter decomposition
  1187. // rates. Ecology 59: 465-472.
  1188. // Parton, W. J., Scurlock, J. M. O., Ojima, D. S., Gilmanov, T. G., Scholes, R. J., Schimel, D. S.,
  1189. // Kirchner, T., Menaut, J. C., Seastedt, T., Moya, E. G., Kamnalrut, A. & Kinyamario, J. I. 1993.
  1190. // Observations and Modeling of Biomass and Soil Organic-Matter Dynamics for the Grassland Biome
  1191. // Worldwide. Global Biogeochemical Cycles, 7, 785-809.
  1192. // Parton, W. J., Hanson, P. J., Swanston, C., Torn, M., Trumbore, S. E., Riley, W. & Kelly, R. 2010.
  1193. // ForCent model development and testing using the Enriched Background Isotope Study experiment.
  1194. // Journal of Geophysical Research-Biogeosciences, 115.