public void Process() { // + Purpose // This routine performs the soil C and N balance, daily. // - Assesses potential decomposition of surface residues (adjust decompostion if needed, accounts for mineralisation/immobilisation of N) // - Calculates hydrolysis of urea, denitrification, transformations on soil organic matter (including N mineralisation/immobilition) and nitrification. int nLayers = g.dlayer.Length; // number of layers in the soil double[,] dlt_fom_n = new double[3, nLayers]; // fom N mineralised in each fraction (kg/ha) if (g.is_pond_active) { // dsg 190508, If there is a pond, the POND module will decompose residues - not SoilNitrogen // dsg 110708 Get the biom & hum C decomposed in the pond and add to soil - on advice of MEP // increment the hum and biom C pools in top soil layer hum_c[0] += g.pond_hum_C; // humic material from breakdown of residues in pond biom_c[0] += g.pond_biom_C; // biom material from breakdown of residues in pond // reset the N amounts of N in hum and biom pools hum_n[0] = MathUtilities.Divide(hum_c[0], g.hum_cn, 0.0); biom_n[0] = MathUtilities.Divide(biom_c[0], g.biom_cn, 0.0); } else { // Decompose residues // assess the potential decomposition of surface residues and calculate actual mineralisation/immobilisation DecomposeResidues(); // update C content in hum and biom pools for (int layer = 0; layer < nLayers; layer++) { hum_c[layer] += SumDoubleArray(dlt_c_res_2_hum[layer]); biom_c[layer] += SumDoubleArray(dlt_c_res_2_biom[layer]); } // update N content in hum and biom pools as well as the mineral N for (int layer = 0; layer < nLayers; layer++) { hum_n[layer] = MathUtilities.Divide(hum_c[layer], g.hum_cn, 0.0); biom_n[layer] = MathUtilities.Divide(biom_c[layer], g.biom_cn, 0.0); // update soil mineral N _nh4[layer] += dlt_nh4_decomp[layer]; _no3[layer] += dlt_no3_decomp[layer]; } } // now take each layer in turn and compute N processes for (int layer = 0; layer < nLayers; layer++) { // urea hydrolysis dlt_urea_hydrolised[layer] = UreaHydrolysis(layer); _nh4[layer] += dlt_urea_hydrolised[layer]; _urea[layer] -= dlt_urea_hydrolised[layer]; // nitrate-N denitrification switch (g.n2o_approach) { case 1: dlt_no3_dnit[layer] = Denitrification_NEMIS(layer); //n2o_atm[layer] is calculated in Nitrification_NEMIS break; case 2: dlt_no3_dnit[layer] = Denitrification_WNMM(layer); //n2o_atm[layer] is calculated in Nitrification_WNMM break; case 3: dlt_no3_dnit[layer] = Denitrification_CENT(layer); //n2o_atm[layer] is calculated in Nitrification_CENT break; case 0: default: dlt_no3_dnit[layer] = Denitrification(layer); break; } _no3[layer] -= dlt_no3_dnit[layer]; // N2O loss to atmosphere - due to denitrification n2o_atm[layer] = 0.0; double N2N2O = Denitrification_Nratio(layer); n2o_atm[layer] = dlt_no3_dnit[layer] / (N2N2O + 1.0); // Calculate transformations of soil organic matter (C and N) // humic pool mineralisation MineraliseHumus(layer); // microbial biomass pool mineralisation MineraliseBiomass(layer); // mineralisation of fresh organic matter pools // need to be revisited - create FOM pools as array //for (int fract = 0; fract < 3; fract++) //{ // MinFom(layer, fract); // dlt_c_fom_2_biom[fract][layer] = dlt_fc_biom[fract]; // dlt_c_fom_2_hum[fract][layer] = dlt_fc_hum[fract]; // dlt_c_fom_2_atm[fract][layer] = dlt_fc_atm[fract]; // dlt_fom_n[fract, layer] = dlt_f_n[fract]; //} double[] dlt_f_n; double[] dlt_fc_biom; double[] dlt_fc_hum; double[] dlt_fc_atm; FOMdecompData MineralisedFOM = new FOMdecompData(); if (g.useNewProcesses) { MineralisedFOM = MineraliseFOM1(layer); for (int fract = 0; fract < 3; fract++) { dlt_c_fom_2_hum[fract][layer] = MineralisedFOM.dlt_c_hum[fract]; dlt_c_fom_2_biom[fract][layer] = MineralisedFOM.dlt_c_biom[fract]; dlt_c_fom_2_atm[fract][layer] = MineralisedFOM.dlt_c_atm[fract]; dlt_fom_n[fract, layer] = MineralisedFOM.dlt_fom_n[fract]; } dlt_n_fom_2_min[layer] = MineralisedFOM.dlt_n_min; } else { MineraliseFOM(layer, out dlt_fc_biom, out dlt_fc_hum, out dlt_fc_atm, out dlt_f_n, out dlt_n_fom_2_min[layer]); for (int fract = 0; fract < 3; fract++) { dlt_c_fom_2_biom[fract][layer] = dlt_fc_biom[fract]; dlt_c_fom_2_hum[fract][layer] = dlt_fc_hum[fract]; dlt_c_fom_2_atm[fract][layer] = dlt_fc_atm[fract]; dlt_fom_n[fract, layer] = dlt_f_n[fract]; } } // update pools C an N contents hum_c[layer] += dlt_c_biom_2_hum[layer] - dlt_c_hum_2_biom[layer] - dlt_c_hum_2_atm[layer] + dlt_c_fom_2_hum[0][layer] + dlt_c_fom_2_hum[1][layer] + dlt_c_fom_2_hum[2][layer]; hum_n[layer] = MathUtilities.Divide(hum_c[layer], g.hum_cn, 0.0); biom_c[layer] += dlt_c_hum_2_biom[layer] - dlt_c_biom_2_hum[layer] - dlt_c_biom_2_atm[layer] + dlt_c_fom_2_biom[0][layer] + dlt_c_fom_2_biom[1][layer] + dlt_c_fom_2_biom[2][layer]; biom_n[layer] = MathUtilities.Divide(biom_c[layer], g.biom_cn, 0.0); fom_c_pool1[layer] -= (dlt_c_fom_2_hum[0][layer] + dlt_c_fom_2_biom[0][layer] + dlt_c_fom_2_atm[0][layer]); fom_c_pool2[layer] -= (dlt_c_fom_2_hum[1][layer] + dlt_c_fom_2_biom[1][layer] + dlt_c_fom_2_atm[1][layer]); fom_c_pool3[layer] -= (dlt_c_fom_2_hum[2][layer] + dlt_c_fom_2_biom[2][layer] + dlt_c_fom_2_atm[2][layer]); fom_n_pool1[layer] -= dlt_fom_n[0, layer]; fom_n_pool2[layer] -= dlt_fom_n[1, layer]; fom_n_pool3[layer] -= dlt_fom_n[2, layer]; // dsg these 3 dlts are calculated for the benefit of soilp which needs to 'get' them dlt_fom_c_pool1[layer] = dlt_c_fom_2_hum[0][layer] + dlt_c_fom_2_biom[0][layer] + dlt_c_fom_2_atm[0][layer]; dlt_fom_c_pool2[layer] = dlt_c_fom_2_hum[1][layer] + dlt_c_fom_2_biom[1][layer] + dlt_c_fom_2_atm[1][layer]; dlt_fom_c_pool3[layer] = dlt_c_fom_2_hum[2][layer] + dlt_c_fom_2_biom[2][layer] + dlt_c_fom_2_atm[2][layer]; // add up fom in each layer in each of the pools //double fom_c = fom_c_pool1[layer] + fom_c_pool2[layer] + fom_c_pool3[layer]; //fom_n[layer] = fom_n_pool1[layer] + fom_n_pool2[layer] + fom_n_pool3[layer]; // update soil mineral N after mineralisation/immobilisation // starts with nh4 _nh4[layer] += dlt_n_hum_2_min[layer] + dlt_n_biom_2_min[layer] + dlt_n_fom_2_min[layer]; // check whether there is enough NH4 to be immobilised nh4_deficit_immob = new double[g.dlayer.Length]; if (_nh4[layer] < g.nh4_min[layer]) { nh4_deficit_immob[layer] = g.nh4_min[layer] - _nh4[layer]; _nh4[layer] = g.nh4_min[layer]; } // now change no3 _no3[layer] -= nh4_deficit_immob[layer]; if (_no3[layer] < g.no3_min[layer] - g.EPSILON) { // note: tests for adequate mineral N for immobilisation have been made so this no3 should not go below no3_min throw new Exception("N immobilisation resulted in mineral N in layer(" + (layer + 1).ToString() + ") to go below minimum"); } // NITRIFICATION switch (g.n2o_approach) { case 1: dlt_nitrification[layer] = Nitrification(layer); //using default APSIM process for NEMIS dlt_nh4_dnit[layer] = N2OLostInNitrification_ApsimSoilNitrogen(layer); break; case 2: dlt_nitrification[layer] = Nitrification_WNMM(layer); // dlt_nh4_dnit[layer] & n2o_atm[layer] are calculated in Nitrification_WNMM break; case 3: dlt_nitrification[layer] = Nitrification_CENT(layer); // dlt_nh4_dnit[layer] & n2o_atm[layer] are calculated in Nitrification_CENT break; case 0: default: // nitrification of ammonium-N (total) dlt_nitrification[layer] = Nitrification(layer); // denitrification loss during nitrification (- n2o_atm ) dlt_nh4_dnit[layer] = N2OLostInNitrification_ApsimSoilNitrogen(layer); // N2O loss to atmosphere from nitrification n2o_atm[layer] += dlt_nh4_dnit[layer]; break; } // effective or net nitrification effective_nitrification[layer] = dlt_nitrification[layer] - dlt_nh4_dnit[layer]; // update soil mineral N _no3[layer] += effective_nitrification[layer]; _nh4[layer] -= dlt_nitrification[layer]; // check some of the values if (Math.Abs(_urea[layer]) < g.EPSILON) _urea[layer] = 0.0; if (Math.Abs(_nh4[layer]) < g.EPSILON) _nh4[layer] = 0.0; if (Math.Abs(_no3[layer]) < g.EPSILON) _no3[layer] = 0.0; if (_urea[layer] < g.urea_min[layer] || _urea[layer] > 9000.0) throw new Exception("Value for urea(layer) is out of range"); if (_nh4[layer] < g.nh4_min[layer] || _nh4[layer] > 9000.0) throw new Exception("Value for NH4(layer) is out of range"); if (_no3[layer] < g.no3_min[layer] || _no3[layer] > 9000.0) throw new Exception("Value for NO3(layer) is out of range"); // net N tansformations nh4_transform_net[layer] = dlt_nh4_decomp[layer] + dlt_n_fom_2_min[layer] + dlt_n_biom_2_min[layer] + dlt_n_hum_2_min[layer] - dlt_nitrification[layer] + dlt_urea_hydrolised[layer] + nh4_deficit_immob[layer]; no3_transform_net[layer] = dlt_no3_decomp[layer] - dlt_no3_dnit[layer] + effective_nitrification[layer] - nh4_deficit_immob[layer]; // net deltas dlt_nh4_net[layer] = _nh4[layer] - nh4_yesterday[layer]; dlt_no3_net[layer] = _no3[layer] - no3_yesterday[layer]; // store these values so they may be used tomorrow nh4_yesterday[layer] = _nh4[layer]; no3_yesterday[layer] = _no3[layer]; } }
private FOMdecompData MineraliseFOM1(int layer) { // + Purpose // Calculate the daily transformation of the soil fresh organic matter pools, mineralisation (+ve) or immobilisation (-ve) double[] dlt_c_hum = new double[3]; // dlt_c from fom to humus double[] dlt_c_biom = new double[3]; // dlt_c from fom to biomass double[] dlt_c_atm = new double[3]; // dlt_c from fom to atmosphere double[] dlt_fom_n = new double[3]; // dlt_n from fom pools to OM double dlt_n_min = 0.0; // dlt_n from fom to mineral // dsg 200508 use different values for some constants when anaerobic conditions dominate // index = 1 for aerobic conditions, 2 for anaerobic conditions int index = (!g.is_pond_active) ? 1 : 2; // get total available mineral N (kg/ha) double nitTot = Math.Max(0.0, (_no3[layer] - g.no3_min[layer]) + (_nh4[layer] - g.nh4_min[layer])); // fresh organic carbon (kg/ha) double fomC = fom_c_pool1[layer] + fom_c_pool2[layer] + fom_c_pool3[layer]; // fresh organic nitrogen (kg/ha) double fomN = fom_n_pool1[layer] + fom_n_pool2[layer] + fom_n_pool3[layer]; // ratio of C in fresh OM to N available for decay double cnr = MathUtilities.Divide(fomC, fomN + nitTot, 0.0); // calculate the C:N ratio factor - Bound to [0, 1] double cnrf = Math.Max(0.0, Math.Min(1.0, Math.Exp(-g.cnrf_coeff * (cnr - g.cnrf_optcn) / g.cnrf_optcn))); if (g.useNewProcesses) cnrf = CNratioFactor(layer, index, g.CNFactorMinerFOM_OptCN, g.CNFactorMinerFOM_RateCN); // get the soil temperature factor double tf = (g.SoilCNParameterSet == "rothc") ? RothcTF(layer, index) : TF(layer, index); if (g.useNewSTFFunction) if (g.useSingleMinerFactors) { tf = SoilTempFactor(layer, index, g.TempFactorData_MinerSOM); } else { if (g.useFactorsByFOMpool) { } else { tf = SoilTempFactor(layer, index, g.TempFactorData_MinerFOM); } } // get the soil water factor double wf = WF(layer, index); if (g.useNewSWFFunction) if (g.useSingleMinerFactors) { wf = SoilMoistFactor(layer, index, g.MoistFactorData_MinerSOM); } else { if (g.useFactorsByFOMpool) { } else { wf = SoilMoistFactor(layer, index, g.MoistFactorData_MinerFOM); } } // calculate gross amount of C & N released due to mineralisation of the fresh organic matter. if (fomC >= g.fom_min) { double dlt_n_min_fom = 0.0; // amount of fresh organic N mineralised across fpools (kg/ha) double dlt_c_min_fom = 0.0; // total C mineralised (kg/ha) summed across fpools double[] dlt_n_min_tot = new double[3]; // amount of fresh organic N mineralised in each pool (kg/ha) double[] dlt_c_min_tot = new double[3]; // amount of C mineralised (kg/ha) from each pool // C:N ratio of fom double fom_cn = MathUtilities.Divide(fomC, fomN, 0.0); // get the decomposition of carbohydrate-like, cellulose-like and lignin-like fractions (fpools) in turn. for (int fractn = 0; fractn < 3; fractn++) { // get the max decomposition rate for each fpool double decomp_rate = FractRDFom(fractn)[index - 1] * cnrf * tf * wf; // calculate the gross amount of fresh organic carbon mineralised (kg/ha) double gross_c_decomp = decomp_rate * FractFomC(fractn)[layer]; // calculate the gross amount of N released from fresh organic matter (kg/ha) double gross_n_decomp = decomp_rate * FractFomN(fractn)[layer]; dlt_n_min_fom += gross_n_decomp; dlt_c_min_tot[fractn] = gross_c_decomp; dlt_n_min_tot[fractn] = gross_n_decomp; dlt_c_min_fom += gross_c_decomp; } // calculate potential transfers of C mineralised to biomass double dlt_c_biom_tot = dlt_c_min_fom * g.ef_fom * g.fr_fom_biom; // calculate potential transfers of C mineralised to humus double dlt_c_hum_tot = dlt_c_min_fom * g.ef_fom * (1.0 - g.fr_fom_biom); // test whether there is adequate N available to meet immobilisation demand double n_demand = MathUtilities.Divide(dlt_c_biom_tot, g.biom_cn, 0.0) + MathUtilities.Divide(dlt_c_hum_tot, g.hum_cn, 0.0); double n_avail = nitTot + dlt_n_min_fom; // factor to reduce mineralisation rates if insufficient N to meet immobilisation demand double Navail_factor = 1.0; if (n_demand > n_avail) Navail_factor = Math.Max(0.0, Math.Min(1.0, MathUtilities.Divide(nitTot, n_demand - dlt_n_min_fom, 0.0))); // now adjust carbon transformations etc. and similarly for npools for (int fractn = 0; fractn < 3; fractn++) { dlt_c_hum[fractn] = dlt_c_min_tot[fractn] * g.ef_fom * (1.0 - g.fr_fom_biom) * Navail_factor; dlt_c_biom[fractn] = dlt_c_min_tot[fractn] * g.ef_fom * g.fr_fom_biom * Navail_factor; dlt_c_atm[fractn] = dlt_c_min_tot[fractn] * (1.0 - g.ef_fom) * Navail_factor; dlt_fom_n[fractn] = dlt_n_min_tot[fractn] * Navail_factor; dlt_c_hum[fractn] = MathUtilities.RoundToZero(dlt_c_hum[fractn]); dlt_c_biom[fractn] = MathUtilities.RoundToZero(dlt_c_biom[fractn]); dlt_c_atm[fractn] = MathUtilities.RoundToZero(dlt_c_atm[fractn]); dlt_fom_n[fractn] = MathUtilities.RoundToZero(dlt_fom_n[fractn]); } dlt_n_min = (dlt_n_min_fom - n_demand) * Navail_factor; } FOMdecompData Result = new FOMdecompData(); Result.dlt_c_hum = dlt_c_hum; Result.dlt_c_biom = dlt_c_biom; Result.dlt_c_atm = dlt_c_atm; Result.dlt_fom_n = dlt_fom_n; Result.dlt_n_min = dlt_n_min; return Result; }