somdynam.cpp 58 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619
  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. // or depending on vegetation type (ifpftlitterfall == 1)
  626. // Woody litter transfers a portion first day of every month
  627. // OR this year's litter on harvest/turnover day for stands with daily allocation
  628. if ((!(date.dayofmonth == 0 || patch.is_litter_day)) && !ifpftlitterfall)
  629. return;
  630. Soil& soil = patch.soil;
  631. double lat = patch.get_climate().lat;
  632. double EPS = -1.0e-16;
  633. // Leaf, root and wood litter lignin fractions
  634. // Leaf and root fractions: Comins & McMurtrie 1993; Friend et al 1997
  635. // Not sure of wood fraction
  636. const double LIGCFRAC_LEAF = 0.2;
  637. const double LIGCFRAC_ROOT = 0.16;
  638. const double LIGCFRAC_WOOD = 0.3;
  639. double ligcmass_new, ligcmass_old;
  640. // Fire
  641. double litterme[NSOMPOOL];
  642. litterme[SURFSTRUCT] = soil.sompool[SURFSTRUCT].cmass * soil.sompool[SURFSTRUCT].litterme;
  643. litterme[SURFMETA] = soil.sompool[SURFMETA].cmass * soil.sompool[SURFMETA].litterme;
  644. litterme[SURFFWD] = soil.sompool[SURFFWD].cmass * soil.sompool[SURFFWD].litterme;
  645. litterme[SURFCWD] = soil.sompool[SURFCWD].cmass * soil.sompool[SURFCWD].litterme;
  646. double fireresist[NSOMPOOL];
  647. fireresist[SURFSTRUCT] = soil.sompool[SURFSTRUCT].cmass * soil.sompool[SURFSTRUCT].fireresist;
  648. fireresist[SURFMETA] = soil.sompool[SURFMETA].cmass * soil.sompool[SURFMETA].fireresist;
  649. fireresist[SURFFWD] = soil.sompool[SURFFWD].cmass * soil.sompool[SURFFWD].fireresist;
  650. fireresist[SURFCWD] = soil.sompool[SURFCWD].cmass * soil.sompool[SURFCWD].fireresist;
  651. bool drop_leaf_root_litter = (lat >= 0.0 && date.month == 0) || (lat < 0.0 && date.month == 6) || patch.is_litter_day || ifpftlitterfall;
  652. patch.pft.firstobj();
  653. while (patch.pft.isobj) {
  654. Patchpft& pft = patch.pft.getobj();
  655. // For stands with yearly growth, drop leaf and root litter on first day of the year for northern hemisphere
  656. // and first day of the second half of the year for southern hemisphere or depending on vegetation type
  657. // For stands with daily growth, harvest and/or turnover, do this when patch.is_litter_day is true.
  658. if (drop_leaf_root_litter) {
  659. // LEAF
  660. double litter_leaf, nmass_litter_leaf;
  661. // Is a litter day, drop all leaf litter (crop)
  662. if (patch.is_litter_day) {
  663. litter_leaf = pft.litter_leaf;
  664. nmass_litter_leaf = pft.nmass_litter_leaf;
  665. pft.litter_leaf = 0.0;
  666. pft.nmass_litter_leaf = 0.0;
  667. }
  668. else if (!ifpftlitterfall) {
  669. litter_leaf = pft.litter_leaf;
  670. nmass_litter_leaf = pft.nmass_litter_leaf;
  671. pft.litter_leaf = 0.0;
  672. pft.nmass_litter_leaf = 0.0;
  673. }
  674. // ecev3 - For summergreens drop leaf litter over all days during Jan in NH and July SH
  675. else if (pft.pft.phenology == SUMMERGREEN) {
  676. if ((lat >= 0.0 && date.month == 0) || (lat < 0.0 && date.month == 6)) {
  677. litter_leaf = pft.litter_leaf / (date.ndaymonth[date.month] - date.dayofmonth);
  678. nmass_litter_leaf = pft.nmass_litter_leaf / (date.ndaymonth[date.month] - date.dayofmonth);
  679. pft.litter_leaf -= litter_leaf;
  680. pft.nmass_litter_leaf -= nmass_litter_leaf;
  681. }
  682. else {
  683. litter_leaf = 0.0;
  684. nmass_litter_leaf = 0.0;
  685. }
  686. }
  687. // Rest drops leaf litter every day
  688. else {
  689. litter_leaf = pft.litter_leaf / (date.year_length() - date.day);
  690. nmass_litter_leaf = pft.nmass_litter_leaf / (date.year_length() - date.day);
  691. pft.litter_leaf -= litter_leaf;
  692. pft.nmass_litter_leaf -= nmass_litter_leaf;
  693. }
  694. // Calculate inputs to surface structural and metabolic litter
  695. // Leaf litter lignin:N ratio
  696. double leaf_lton = lignin_to_n_ratio(litter_leaf, nmass_litter_leaf, LIGCFRAC_LEAF, pft.pft.cton_leaf_avr);
  697. // Metabolic litter fraction for leaf litter
  698. double fm = metabolic_litter_fraction(leaf_lton);
  699. ligcmass_old = soil.sompool[SURFSTRUCT].cmass * soil.sompool[SURFSTRUCT].ligcfrac;
  700. // Add to pools
  701. soil.sompool[SURFSTRUCT].cmass += litter_leaf * (1.0 - fm);
  702. soil.sompool[SURFSTRUCT].nmass += nmass_litter_leaf * (1.0 - fm);
  703. soil.sompool[SURFMETA].cmass += litter_leaf * fm;
  704. soil.sompool[SURFMETA].nmass += nmass_litter_leaf * fm;
  705. patch.fluxes.report_flux(Fluxes::VEGLITTERC, litter_leaf);
  706. patch.fluxes.report_flux(Fluxes::VEGLITTERN, nmass_litter_leaf);
  707. #ifdef CRESCENDO_FACE
  708. patch.fluxes.report_flux(Fluxes::DLEAFLITC, litter_leaf);
  709. patch.fluxes.report_flux(Fluxes::DLEAFLITN, nmass_litter_leaf);
  710. #endif
  711. // Save litter input for equilsom()
  712. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  713. soil.litterSolveSOM.add_litter(litter_leaf * (1.0 - fm), nmass_litter_leaf * (1.0 - fm), SURFSTRUCT);
  714. soil.litterSolveSOM.add_litter(litter_leaf * fm, nmass_litter_leaf * fm, SURFMETA);
  715. }
  716. // Fire
  717. litterme[SURFSTRUCT] += litter_leaf * (1.0 - fm) * pft.pft.litterme;
  718. fireresist[SURFSTRUCT] += litter_leaf * (1.0 - fm) * pft.pft.fireresist;
  719. litterme[SURFMETA] += litter_leaf * fm * pft.pft.litterme;
  720. fireresist[SURFMETA] += litter_leaf * fm * pft.pft.fireresist;
  721. // NB: reproduction litter cannot contain nitrogen!!
  722. ligcmass_new = litter_leaf * (1.0 - fm) * LIGCFRAC_LEAF;
  723. if (negligible(soil.sompool[SURFSTRUCT].cmass)) {
  724. soil.sompool[SURFSTRUCT].ligcfrac = 0.0;
  725. }
  726. else {
  727. soil.sompool[SURFSTRUCT].ligcfrac = (ligcmass_new + ligcmass_old) /
  728. soil.sompool[SURFSTRUCT].cmass;
  729. }
  730. // ROOT
  731. double litter_root, nmass_litter_root;
  732. // Is a litter day, drop all root litter (crop)
  733. if (patch.is_litter_day) {
  734. litter_root = pft.litter_root;
  735. nmass_litter_root = pft.nmass_litter_root;
  736. pft.litter_root = 0.0;
  737. pft.nmass_litter_root = 0.0;
  738. }
  739. else if (!ifpftlitterfall) {
  740. litter_root = pft.litter_root;
  741. nmass_litter_root = pft.nmass_litter_root;
  742. pft.litter_root = 0.0;
  743. pft.nmass_litter_root = 0.0;
  744. }
  745. // ecev3 - For summergreens drop root litter over all days during Jan in NH and July SH
  746. else if (pft.pft.phenology == SUMMERGREEN) {
  747. if ((lat >= 0.0 && date.month == 0) || (lat < 0.0 && date.month == 6)) {
  748. litter_root = pft.litter_root / (date.ndaymonth[date.month] - date.dayofmonth);
  749. nmass_litter_root = pft.nmass_litter_root / (date.ndaymonth[date.month] - date.dayofmonth);
  750. pft.litter_root -= litter_root;
  751. pft.nmass_litter_root -= nmass_litter_root;
  752. }
  753. else {
  754. litter_root = 0.0;
  755. nmass_litter_root = 0.0;
  756. }
  757. }
  758. // Rest drops root litter every day
  759. else {
  760. litter_root = pft.litter_root / (date.year_length() - date.day);
  761. nmass_litter_root = pft.nmass_litter_root / (date.year_length() - date.day);
  762. pft.litter_root -= litter_root;
  763. pft.nmass_litter_root -= nmass_litter_root;
  764. }
  765. // Calculate inputs to soil structural and metabolic litter
  766. // Root litter lignin:N ratio
  767. double root_lton = lignin_to_n_ratio(litter_root, nmass_litter_root, LIGCFRAC_ROOT, pft.pft.cton_root_avr);
  768. // Metabolic litter fraction for root litter
  769. fm = metabolic_litter_fraction(root_lton);
  770. ligcmass_new = litter_root * (1.0 - fm) * LIGCFRAC_ROOT;
  771. ligcmass_old = soil.sompool[SOILSTRUCT].cmass * soil.sompool[SOILSTRUCT].ligcfrac;
  772. // Add to pools and update lignin fraction in structural pool
  773. soil.sompool[SOILSTRUCT].cmass += litter_root * (1.0 - fm);
  774. soil.sompool[SOILSTRUCT].nmass += nmass_litter_root * (1.0 - fm);
  775. soil.sompool[SOILMETA].cmass += litter_root * fm;
  776. soil.sompool[SOILMETA].nmass += nmass_litter_root * fm;
  777. patch.fluxes.report_flux(Fluxes::VEGLITTERC, litter_root);
  778. patch.fluxes.report_flux(Fluxes::VEGLITTERN, nmass_litter_root);
  779. #ifdef CRESCENDO_FACE
  780. patch.fluxes.report_flux(Fluxes::DROOTLITC, litter_root);
  781. patch.fluxes.report_flux(Fluxes::DROOTLITN, nmass_litter_root);
  782. #endif
  783. // Save litter input for equilsom()
  784. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  785. soil.litterSolveSOM.add_litter(litter_root * (1.0 - fm), nmass_litter_root * (1.0 - fm), SOILSTRUCT);
  786. soil.litterSolveSOM.add_litter(litter_root * fm, nmass_litter_root * fm, SOILMETA);
  787. }
  788. if (negligible(soil.sompool[SOILSTRUCT].cmass)) {
  789. soil.sompool[SOILSTRUCT].ligcfrac = 0.0;
  790. }
  791. else {
  792. soil.sompool[SOILSTRUCT].ligcfrac = (ligcmass_new + ligcmass_old) /
  793. soil.sompool[SOILSTRUCT].cmass;
  794. }
  795. }
  796. // WOOD
  797. if (pft.pft.lifeform == TREE && date.dayofmonth == 0) {
  798. // Woody debris enters two woody litter pools as described in
  799. // Kirschbaum and Paul (2002).
  800. if (date.month == 0) {
  801. pft.litter_sap_year = pft.litter_sap;
  802. pft.nmass_litter_sap_year = pft.nmass_litter_sap;
  803. }
  804. // Monthly fraction of last years litter
  805. double litter_sap = pft.litter_sap_year / 12.0;
  806. double nmass_litter_sap = pft.nmass_litter_sap_year / 12.0;
  807. pft.litter_sap -= pft.litter_sap_year / 12.0;
  808. pft.nmass_litter_sap -= pft.nmass_litter_sap_year / 12.0;
  809. soil.sompool[SURFFWD].nmass += nmass_litter_sap;
  810. patch.fluxes.report_flux(Fluxes::VEGLITTERN, nmass_litter_sap);
  811. #ifdef CRESCENDO_FACE
  812. patch.fluxes.report_flux(Fluxes::DWOODLITN, nmass_litter_sap);
  813. #endif
  814. if (!negligible(litter_sap)) {
  815. // Fine woody debris
  816. // ecev3 - check
  817. if (litter_sap < EPS) {
  818. 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);
  819. }
  820. assert(litter_sap >= EPS);
  821. ligcmass_new = litter_sap * LIGCFRAC_WOOD;
  822. ligcmass_old = soil.sompool[SURFFWD].cmass * soil.sompool[SURFFWD].ligcfrac;
  823. // Add to structural pool and update lignin fraction in pool
  824. soil.sompool[SURFFWD].cmass += litter_sap;
  825. patch.fluxes.report_flux(Fluxes::VEGLITTERC, litter_sap);
  826. #ifdef CRESCENDO_FACE
  827. patch.fluxes.report_flux(Fluxes::DWOODLITC, litter_sap);
  828. #endif
  829. // Save litter input for equilsom()
  830. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  831. soil.litterSolveSOM.add_litter(litter_sap, nmass_litter_sap, SURFFWD);
  832. }
  833. if (negligible(soil.sompool[SURFFWD].cmass)) {
  834. soil.sompool[SURFFWD].ligcfrac = 0.0;
  835. }
  836. else {
  837. double ligcfrac = (ligcmass_new + ligcmass_old) /
  838. soil.sompool[SURFFWD].cmass;
  839. soil.sompool[SURFFWD].ligcfrac = ligcfrac;
  840. }
  841. // Fire
  842. litterme[SURFFWD] += litter_sap * pft.pft.litterme;
  843. fireresist[SURFFWD] += litter_sap * pft.pft.fireresist;
  844. }
  845. if (date.month == 0) {
  846. pft.litter_heart_year = pft.litter_heart;
  847. pft.nmass_litter_heart_year = pft.nmass_litter_heart;
  848. }
  849. // Monthly fraction of last years litter
  850. double litter_heart = pft.litter_heart_year / 12.0;
  851. double nmass_litter_heart = pft.nmass_litter_heart_year / 12.0;
  852. pft.litter_heart -= pft.litter_heart_year / 12.0;
  853. pft.nmass_litter_heart -= pft.nmass_litter_heart_year / 12.0;
  854. soil.sompool[SURFCWD].nmass += nmass_litter_heart;
  855. patch.fluxes.report_flux(Fluxes::VEGLITTERN, nmass_litter_heart);
  856. #ifdef CRESCENDO_FACE
  857. patch.fluxes.report_flux(Fluxes::DWOODLITN, nmass_litter_heart);
  858. #endif
  859. if (!negligible(litter_heart)) {
  860. // Coarse woody debris
  861. // ecev3 - check
  862. if (litter_heart < EPS) {
  863. 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);
  864. }
  865. assert(litter_heart >= EPS);
  866. ligcmass_new = litter_heart * LIGCFRAC_WOOD;
  867. ligcmass_old = soil.sompool[SURFCWD].cmass * soil.sompool[SURFCWD].ligcfrac;
  868. // Add to structural pool and update lignin fraction in pool
  869. soil.sompool[SURFCWD].cmass += litter_heart;
  870. patch.fluxes.report_flux(Fluxes::VEGLITTERC, litter_heart);
  871. #ifdef CRESCENDO_FACE
  872. patch.fluxes.report_flux(Fluxes::DWOODLITC, litter_heart);
  873. #endif
  874. // Save litter input for equilsom()
  875. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  876. soil.litterSolveSOM.add_litter(litter_heart, nmass_litter_heart, SURFCWD);
  877. }
  878. if (negligible(soil.sompool[SURFCWD].cmass)) {
  879. soil.sompool[SURFCWD].ligcfrac = 0.0;
  880. }
  881. else {
  882. double ligcfrac = (ligcmass_new + ligcmass_old) /
  883. soil.sompool[SURFCWD].cmass;
  884. soil.sompool[SURFCWD].ligcfrac = ligcfrac;
  885. }
  886. // Fire
  887. litterme[SURFCWD] += litter_heart * pft.pft.litterme;
  888. fireresist[SURFCWD] += litter_heart * pft.pft.fireresist;
  889. }
  890. }
  891. // Remove association with vegetation
  892. if (date.islastmonth) {
  893. pft.litter_sap = 0.0;
  894. pft.litter_heart = 0.0;
  895. pft.nmass_litter_sap = 0.0;
  896. pft.nmass_litter_heart = 0.0;
  897. }
  898. patch.pft.nextobj();
  899. }
  900. // FIRE
  901. if (soil.sompool[SURFSTRUCT].cmass > 0.0) {
  902. soil.sompool[SURFSTRUCT].litterme = litterme[SURFSTRUCT] / soil.sompool[SURFSTRUCT].cmass;
  903. soil.sompool[SURFSTRUCT].fireresist = fireresist[SURFSTRUCT] / soil.sompool[SURFSTRUCT].cmass;
  904. }
  905. if (soil.sompool[SURFMETA].cmass > 0.0) {
  906. soil.sompool[SURFMETA].litterme = litterme[SURFMETA] / soil.sompool[SURFMETA].cmass;
  907. soil.sompool[SURFMETA].fireresist = fireresist[SURFMETA] / soil.sompool[SURFMETA].cmass;
  908. }
  909. if (soil.sompool[SURFFWD].cmass > 0.0) {
  910. soil.sompool[SURFFWD].litterme = litterme[SURFFWD] / soil.sompool[SURFFWD].cmass;
  911. soil.sompool[SURFFWD].fireresist = fireresist[SURFFWD] / soil.sompool[SURFFWD].cmass;
  912. }
  913. if (soil.sompool[SURFCWD].cmass > 0.0) {
  914. soil.sompool[SURFCWD].litterme = litterme[SURFCWD] / soil.sompool[SURFCWD].cmass;
  915. soil.sompool[SURFCWD].fireresist = fireresist[SURFCWD] / soil.sompool[SURFCWD].cmass;
  916. }
  917. // Calculate total litter carbon and nitrogen mass for set N:C ratio of surface microbial pool
  918. double litter_cmass = soil.sompool[SURFSTRUCT].cmass + soil.sompool[SURFMETA].cmass +
  919. soil.sompool[SURFFWD].cmass + soil.sompool[SURFCWD].cmass;
  920. double litter_nmass = soil.sompool[SURFSTRUCT].nmass + soil.sompool[SURFMETA].nmass +
  921. soil.sompool[SURFFWD].nmass + soil.sompool[SURFCWD].nmass;
  922. // Set N:C ratio of surface microbial pool based on N:C ratio of litter from all PFTs
  923. // Parton et al 1993 Fig 4. Dry mass litter == cmass litter * 2
  924. if (!negligible(litter_cmass)) {
  925. setntoc(soil, litter_nmass / (litter_cmass * 2.0), SURFMICRO, 20.0, 10.0, 0.0, NCONC_SAT);
  926. }
  927. // Add litter to solvesom array every month
  928. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  929. if (date.dayofmonth == 0) {
  930. soil.solvesom.push_back(soil.litterSolveSOM);
  931. soil.litterSolveSOM.clear();
  932. }
  933. }
  934. }
  935. /// LEACHING
  936. /** Leaching fractions for both organic and mineral leaching
  937. * Should be called every day in both daily and monthly mode
  938. */
  939. void leaching(Soil& soil) {
  940. double minleachfrac;
  941. const double EPS = 1.0e-16;
  942. // ecev3 - check
  943. if (soil.dperc < -EPS) {
  944. 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);
  945. }
  946. // Warning if soil available nitrogen is negative (if happens once or so no problem, but if it propagates through time then it is)
  947. if (ifnlim) {
  948. assert(soil.dperc > -EPS);
  949. }
  950. if (!negligible(soil.dperc)) {
  951. // Leaching from available nitrogen mineral pool
  952. // in proportion to amount of water drainage
  953. minleachfrac = soil.dperc / (soil.dperc + soil.soiltype.awc[0] * soil.wcont[0] + soil.soiltype.awc[1] * soil.wcont[1]);
  954. // ecev3 - check
  955. if (minleachfrac < -EPS) {
  956. dprintf("ERROR! Year %d day %d Stand %d: Negative soil.minleachfrac in leaching: %.15f\n", date.year, date.day, soil.patch.stand.id, minleachfrac);
  957. }
  958. if (ifnlim) {
  959. assert(minleachfrac > -EPS);
  960. }
  961. // Leaching from decayed organic carbon/nitrogen
  962. // 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)],
  963. // reorganised as: leachfrac = [water_leaching/OMLECH(3)]*[OMLECH(1)+OMLECH(2)*sand_fraction]
  964. // water_leaching was originally in cm/month
  965. double OMLECH_1 = 0.03;
  966. double OMLECH_2 = 0.12;
  967. double OMLECH_3 = 1.9; // saturation point in leaching equation (cm H20/month)
  968. double cmpermonth_to_mmperday = 10.0 * 12.0 / 365.0;
  969. soil.orgleachfrac = min(1.0, soil.dperc / (OMLECH_3 * cmpermonth_to_mmperday)) * (OMLECH_1 + OMLECH_2 * soil.soiltype.sand_frac);
  970. // ecev3 - check
  971. if (soil.orgleachfrac < -EPS) {
  972. 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);
  973. }
  974. if (ifnlim) {
  975. assert(soil.orgleachfrac > -EPS);
  976. }
  977. }
  978. else {
  979. minleachfrac = 0.0;
  980. soil.orgleachfrac = 0.0;
  981. }
  982. // Leaching of soil mineral nitrogen
  983. // Allowed on days with residual nitrogen following vegetation uptake
  984. // in proportion to amount of water drainage
  985. if (soil.nmass_avail > 0.0) {
  986. double leaching = soil.nmass_avail * minleachfrac;
  987. soil.nmass_avail -= leaching;
  988. soil.aminleach += leaching;
  989. soil.sompool[LEACHED].nmass += leaching;
  990. soil.patch.fluxes.report_flux(Fluxes::LEACHN, leaching);
  991. #ifdef CRESCENDO_FACE
  992. soil.patch.fluxes.report_flux(Fluxes::DLEACHN, leaching);
  993. #endif
  994. }
  995. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  996. soil.morgleach_mean[date.month] += soil.orgleachfrac / (double)date.ndaymonth[date.month];
  997. soil.mminleach_mean[date.month] += minleachfrac / (double)date.ndaymonth[date.month];
  998. }
  999. }
  1000. /// Nitrogen addition to the soil
  1001. /** Daily nitrogen addition to the soil
  1002. * from deposition and fixation
  1003. */
  1004. void soilnadd(Patch& patch) {
  1005. Soil& soil = patch.soil;
  1006. double nflux = 0.01 * soil.ninput;
  1007. soil.ninput -= nflux;
  1008. patch.fluxes.report_flux(Fluxes::N_SOIL, nflux);
  1009. #ifdef CRESCENDO_FACE
  1010. patch.fluxes.report_flux(Fluxes::DN_SOIL, nflux);
  1011. #endif
  1012. // Nitrogen deposition and fertilization input to the soil (calculated in snow_ninput())
  1013. soil.nmass_avail += soil.ninput;
  1014. // Nitrogen fixation
  1015. // If soil available nitrogen is above the value for minimum SOM C:N ratio, then
  1016. // nitrogen fixation is reduced (nitrogen rich soils)
  1017. if (soil.nmass_avail < NMASS_SAT) {
  1018. double daily_nfix = soil.anfix_calc / date.year_length();
  1019. if (soil.nmass_avail + daily_nfix < NMASS_SAT) {
  1020. soil.nmass_avail += daily_nfix;
  1021. soil.anfix += daily_nfix;
  1022. }
  1023. else {
  1024. daily_nfix = NMASS_SAT - soil.nmass_avail;
  1025. soil.anfix += daily_nfix;
  1026. soil.nmass_avail = NMASS_SAT;
  1027. }
  1028. patch.fluxes.report_flux(Fluxes::NFIX, daily_nfix);
  1029. #ifdef CRESCENDO_FACE
  1030. patch.fluxes.report_flux(Fluxes::DNFIX, daily_nfix);
  1031. #endif
  1032. }
  1033. // Calculate nitrogen fixation (Cleveland et al. 1999)
  1034. // by using five year average aaet
  1035. if (date.islastmonth && date.islastday) {
  1036. // Add this year's AET to aaet_5 which keeps track of the last 5 years
  1037. patch.aaet_5.add(patch.aaet+patch.aevap+patch.aintercep);
  1038. // Calculate estimated nitrogen fixation (aaet should be in cm/yr, eqn is in nitrogen/ha/yr)
  1039. double cmtomm = 0.1;
  1040. double hatom2 = 0.0001;
  1041. soil.anfix_calc = max((nfix_a * patch.aaet_5.mean() * cmtomm + nfix_b) * hatom2, 0.0);
  1042. if (soil.patch.stand.get_gridcell().getsimulationyear(date.year) >= soil.solvesomcent_beginyr && soil.patch.stand.get_gridcell().getsimulationyear(date.year) <= soil.solvesomcent_endyr) {
  1043. soil.anfix_mean += soil.anfix;
  1044. }
  1045. }
  1046. }
  1047. /// Vegetation nitrogen uptake
  1048. /** Daily vegetation uptake of mineral nitrogen
  1049. * Partitioned among individuals according to todays nitrogen demand
  1050. * and individuals root area
  1051. */
  1052. void vegetation_n_uptake(Patch& patch) {
  1053. // Daily nitrogen demand given by:
  1054. // For individual:
  1055. // (1) ndemand = leafndemand + rootndemand + sapndemand;
  1056. // where
  1057. // leafndemand is leaf demand based on vmax
  1058. // rootndemand is based on optimal leaf C:N ratio
  1059. // sapndemand is based on optimal leaf C:N ratio
  1060. //
  1061. // Actual nitrogen uptake for each day and individual given by:
  1062. // (2) nuptake = ndemand * fnuptake
  1063. // where fnuptake is individual uptake capacity calculated in fnuptake in canexch.cpp
  1064. double nuptake_day, orignmass;
  1065. Vegetation& vegetation=patch.vegetation;
  1066. Soil& soil = patch.soil;
  1067. orignmass = soil.nmass_avail;
  1068. // Loop through individuals
  1069. vegetation.firstobj();
  1070. while (vegetation.isobj) {
  1071. Individual& indiv = vegetation.getobj();
  1072. if (date.day == 0)
  1073. indiv.anuptake = 0.0;
  1074. nuptake_day = indiv.ndemand * indiv.fnuptake;
  1075. indiv.anuptake += nuptake_day;
  1076. indiv.nmass_leaf += indiv.leaffndemand * nuptake_day;
  1077. indiv.nmass_root += indiv.rootfndemand * nuptake_day;
  1078. indiv.nmass_sap += indiv.sapfndemand * nuptake_day;
  1079. if (indiv.pft.phenology == CROPGREEN && ifnlim)
  1080. indiv.cropindiv->nmass_agpool += indiv.storefndemand * nuptake_day;
  1081. else
  1082. indiv.nstore_longterm += indiv.storefndemand * nuptake_day;
  1083. soil.nmass_avail -= nuptake_day;
  1084. indiv.report_flux(Fluxes::NUP, nuptake_day);
  1085. #ifdef CRESCENDO_FACE
  1086. indiv.report_flux(Fluxes::DNUP, nuptake_day);
  1087. indiv.report_flux(Fluxes::DNGL, indiv.leaffndemand * nuptake_day);
  1088. indiv.report_flux(Fluxes::DNGW, indiv.sapfndemand * nuptake_day);
  1089. indiv.report_flux(Fluxes::DNGR, indiv.rootfndemand * nuptake_day);
  1090. #endif
  1091. if (!negligible(indiv.phen))
  1092. indiv.cton_leaf_aavr += min(indiv.cton_leaf(),indiv.pft.cton_leaf_max);
  1093. vegetation.nextobj();
  1094. }
  1095. 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)) {
  1096. soil.fnuptake_mean[date.month] += (1.0 - soil.nmass_avail / orignmass) / (double)date.ndaymonth[date.month];
  1097. }
  1098. }
  1099. /// Add litter to equilsom()
  1100. void add_litter(Soil& soil, int year, int pool) {
  1101. // If LUC have occurred in between solvesomcent_beginyr and solvesomcent_endyr then array might be too smalll
  1102. if (year >= (int)soil.solvesom.size())
  1103. year = year%(int)soil.solvesom.size();
  1104. // Add to litter pools
  1105. soil.sompool[pool].cmass += soil.solvesom[year].get_clitter(pool);
  1106. soil.sompool[pool].nmass += soil.solvesom[year].get_nlitter(pool);
  1107. }
  1108. /// Iteratively solving differential flux equations for century SOM pools
  1109. /** Iteratively solving differential flux equations for century SOM pools
  1110. * assuming annual litter inputs, nitrogen uptake and leaching is close
  1111. * to long term equilibrium
  1112. */
  1113. void equilsom(Soil& soil) {
  1114. if (!(soil.patch.stand.get_gridcell().getsimulationyear(date.year) == soil.solvesomcent_endyr && date.islastmonth && date.islastday))
  1115. return;
  1116. // Number of years to run SOM pools, value chosen to get cold climates to equilibrium
  1117. const int EQUILSOM_YEARS = 40000;
  1118. Patch& patch = soil.patch;
  1119. const Climate& climate = soil.patch.get_climate();
  1120. // Save nmass_avail status
  1121. double save_nmass_avail = soil.nmass_avail;
  1122. // Number of years with mean input data
  1123. int nyear = soil.solvesomcent_endyr - soil.solvesomcent_beginyr + 1;
  1124. for (int m = 0; m < 12; m++) {
  1125. // Monthly average decay rates
  1126. for (int p = 0; p < NSOMPOOL-1; p++) {
  1127. soil.sompool[p].mfracremain_mean[m] = pow(soil.sompool[p].mfracremain_mean[m] / (double)nyear, (double)date.ndaymonth[m]);
  1128. }
  1129. // Monthly average mineral nitrogen uptake
  1130. soil.fnuptake_mean[m] /= (double)nyear;
  1131. // Monthly average organic carbon and nitrogen leaching
  1132. soil.morgleach_mean[m] /= (double)nyear;
  1133. // Monthly average mineral nitrogen leaching
  1134. soil.mminleach_mean[m] /= (double)nyear;
  1135. }
  1136. // Annual average nitrogen fixation
  1137. soil.anfix_mean /= (double)nyear;
  1138. // Spin SOM pools with saved litter input, nitrogen addition and fractions of
  1139. // nitrogen uptake and leaching for EQUILSOM_YEARS years with monthly timesteps
  1140. for (int yr = 0; yr < EQUILSOM_YEARS; yr++) {
  1141. // Which year in saved data set
  1142. int savedyear = yr%nyear;
  1143. // Monthly time steps
  1144. for (int m = 0; m < 12; m++) {
  1145. // Transfer yearly mean litter on first day of year
  1146. add_litter(soil, savedyear*12+m, SURFSTRUCT);
  1147. add_litter(soil, savedyear*12+m, SURFMETA);
  1148. add_litter(soil, savedyear*12+m, SOILSTRUCT);
  1149. add_litter(soil, savedyear*12+m, SOILMETA);
  1150. add_litter(soil, savedyear*12+m, SURFFWD);
  1151. add_litter(soil, savedyear*12+m, SURFCWD);
  1152. // Calculate total litter carbon and nitrogen mass for set N:C ratio of surface microbial pool
  1153. double litter_cmass = soil.sompool[SURFSTRUCT].cmass + soil.sompool[SURFMETA].cmass +
  1154. soil.sompool[SURFFWD].cmass + soil.sompool[SURFCWD].cmass;
  1155. double litter_nmass = soil.sompool[SURFSTRUCT].nmass + soil.sompool[SURFMETA].nmass +
  1156. soil.sompool[SURFFWD].nmass + soil.sompool[SURFCWD].nmass;
  1157. // Set N:C ratio of surface microbial pool based on N:C ratio of litter from all PFTs
  1158. if (!negligible(litter_cmass)) {
  1159. setntoc(soil, litter_nmass / (litter_cmass * 2.0), SURFMICRO, 20.0, 10.0, 0.0, NCONC_SAT);
  1160. }
  1161. // Monthly nitrogen uptake
  1162. soil.nmass_avail *= (1.0 - soil.fnuptake_mean[m]);
  1163. // Monthly mineral nitrogen leaching
  1164. soil.nmass_avail *= (1.0 - soil.mminleach_mean[m]);
  1165. // Monthly nitrogen addition to the system
  1166. soil.nmass_avail += (climate.andep + soil.anfix_mean) / 12.0;
  1167. // Monthly decomposition and fluxes between SOM pools
  1168. // Set this months decay rates
  1169. for (int p = 0; p < NSOMPOOL-1; p++) {
  1170. soil.sompool[p].fracremain = soil.sompool[p].mfracremain_mean[m];
  1171. }
  1172. // Set this months organic nitrogen leaching fraction
  1173. soil.orgleachfrac = soil.morgleach_mean[m];
  1174. // Monthly decomposition and fluxes between SOM pools
  1175. // and nitrogen flux from soil
  1176. somfluxes(patch, true, false);
  1177. }
  1178. }
  1179. // Reset nmass_avail status
  1180. soil.nmass_avail = save_nmass_avail;
  1181. // Reset variables for next equilsom()
  1182. for (int m = 0; m < 12; m++) {
  1183. for (int p = 0; p < NSOMPOOL-1; p++) {
  1184. soil.sompool[p].mfracremain_mean[m] = 0.0;
  1185. }
  1186. soil.fnuptake_mean[m] = 0.0;
  1187. soil.morgleach_mean[m] = 0.0;
  1188. soil.mminleach_mean[m] = 0.0;
  1189. }
  1190. soil.anfix_mean = 0.0;
  1191. soil.solvesom.clear();
  1192. std::vector<LitterSolveSOM>().swap(soil.solvesom);
  1193. }
  1194. /// SOM CENTURY DYNAMICS
  1195. /** To be called each simulation day for each modelled patch, following update
  1196. * of soil temperature and soil water.
  1197. * Transfers litter, performes nitrogen uptake and addition, leaching and decomposition.
  1198. */
  1199. void som_dynamics_century(Patch& patch, bool tillage) {
  1200. // Transfer litter first day of every month or other harvest/turnover day
  1201. transfer_litter(patch);
  1202. // Daily nitrogen uptake
  1203. vegetation_n_uptake(patch);
  1204. // Daily mineral and organic nitrogen leaching
  1205. leaching(patch.soil);
  1206. // Daily nitrogen addition to the soil
  1207. soilnadd(patch);
  1208. // Daily or monthly decomposition and fluxes between SOM pools
  1209. somfluxes(patch, false, tillage);
  1210. // Solve SOM pool sizes at end of year given by soil.solvesomcent_endyr
  1211. equilsom(patch.soil);
  1212. }
  1213. /// Choose between CENTURY or standard LPJ SOM dynamics
  1214. /**
  1215. */
  1216. void som_dynamics(Patch& patch) {
  1217. bool tillage = iftillage && patch.stand.landcover == CROPLAND;
  1218. if (ifcentury) som_dynamics_century(patch, tillage);
  1219. else som_dynamics_lpj(patch, tillage);
  1220. }
  1221. ///////////////////////////////////////////////////////////////////////////////////////
  1222. // REFERENCES
  1223. //
  1224. // Chatskikh, D., Hansen, S., Olesen, J.E. & Petersen, B.M. 2009. A simplified modelling approach
  1225. // for quantifying tillage effects on soil carbon stocks. Eur.J.Soil.Sci., 60:924-934.
  1226. // CENTURY Soil Organic Matter Model Version 5; Century User's Guide and Reference.
  1227. // http://www.nrel.colostate.edu/projects/century5/reference/index.htm; accessed Oct.7, 2016.
  1228. // Comins, H. N. & McMurtrie, R. E. 1993. Long-Term Response of Nutrient-Limited
  1229. // Forests to CO2 Enrichment - Equilibrium Behavior of Plant-Soil Models.
  1230. // Ecological Applications, 3, 666-681.
  1231. // Cosby, B. J., Hornberger, C. M., Clapp, R. B., & Ginn, T. R. 1984 A statistical exploration
  1232. // of the relationships of soil moisture characteristic to the physical properties of soil.
  1233. // Water Resources Research, 20: 682-690.
  1234. // Cleveland C C (1999) Global patterns of terrestrial biological nitrogen (N2) fixation
  1235. // in natural ecosystems. GBC 13: 623-645
  1236. // Foley J A 1995 An equilibrium model of the terrestrial carbon budget
  1237. // Tellus (1995), 47B, 310-319
  1238. // Friend, A. D., Stevens, A. K., Knox, R. G. & Cannell, M. G. R. 1997. A
  1239. // process-based, terrestrial biosphere model of ecosystem dynamics
  1240. // (Hybrid v3.0). Ecological Modelling, 95, 249-287.
  1241. // Kirschbaum, M. U. F. and K. I. Paul (2002). "Modelling C and N dynamics in forest soils
  1242. // with a modified version of the CENTURY model." Soil Biology & Biochemistry 34(3): 341-354.
  1243. // Meentemeyer, V. (1978) Macroclimate and lignin control of litter decomposition
  1244. // rates. Ecology 59: 465-472.
  1245. // Parton, W. J., Scurlock, J. M. O., Ojima, D. S., Gilmanov, T. G., Scholes, R. J., Schimel, D. S.,
  1246. // Kirchner, T., Menaut, J. C., Seastedt, T., Moya, E. G., Kamnalrut, A. & Kinyamario, J. I. 1993.
  1247. // Observations and Modeling of Biomass and Soil Organic-Matter Dynamics for the Grassland Biome
  1248. // Worldwide. Global Biogeochemical Cycles, 7, 785-809.
  1249. // Parton, W. J., Hanson, P. J., Swanston, C., Torn, M., Trumbore, S. E., Riley, W. & Kelly, R. 2010.
  1250. // ForCent model development and testing using the Enriched Background Isotope Study experiment.
  1251. // Journal of Geophysical Research-Biogeosciences, 115.