private void StoreWaterVariablesForNitrogenUptake(ZoneWaterAndN zoneWater) { ZoneState myZone = root.Zones.Find(z => z.Name == zoneWater.Zone.Name); if (myZone != null) { //store Water variables for N Uptake calculation //Old sorghum doesn't do actualUptake of Water until end of day myZone.StartWater = new double[myZone.soil.Thickness.Length]; myZone.AvailableSW = new double[myZone.soil.Thickness.Length]; myZone.PotentialAvailableSW = new double[myZone.soil.Thickness.Length]; myZone.Supply = new double[myZone.soil.Thickness.Length]; var soilCrop = Soil.Crop(Plant.Name); double[] kl = soilCrop.KL; if (root.Depth != myZone.Depth) { myZone.Depth += 0; } var currentLayer = myZone.soil.LayerIndexOfDepth(myZone.Depth); var currentLayerProportion = myZone.soil.ProportionThroughLayer(currentLayer, myZone.Depth); for (int layer = 0; layer <= currentLayer; ++layer) { myZone.StartWater[layer] = myZone.soil.Water[layer]; myZone.AvailableSW[layer] = Math.Max(myZone.soil.Water[layer] - myZone.soil.LL15mm[layer], 0); myZone.PotentialAvailableSW[layer] = myZone.soil.DULmm[layer] - myZone.soil.LL15mm[layer]; if (layer == currentLayer) { myZone.AvailableSW[layer] *= currentLayerProportion; myZone.PotentialAvailableSW[layer] *= currentLayerProportion; } var proportion = root.rootProportionInLayer(layer, myZone); myZone.Supply[layer] = Math.Max(myZone.AvailableSW[layer] * kl[layer] * proportion, 0.0); } var totalAvail = myZone.AvailableSW.Sum(); var totalAvailPot = myZone.PotentialAvailableSW.Sum(); var totalSupply = myZone.Supply.Sum(); WatSupply = totalSupply; // Set reporting variables. Avail = myZone.AvailableSW; PotAvail = myZone.PotentialAvailableSW; TotalAvail = myZone.AvailableSW.Sum(); TotalPotAvail = myZone.PotentialAvailableSW.Sum(); //used for SWDef PhenologyStress table lookup SWAvailRatio = MathUtilities.Bound(MathUtilities.Divide(totalAvail, totalAvailPot, 1.0), 0.0, 10.0); //used for SWDef ExpansionStress table lookup SDRatio = MathUtilities.Bound(MathUtilities.Divide(totalSupply, WDemand, 1.0), 0.0, 10); //used for SwDefPhoto Stress PhotoStress = MathUtilities.Bound(MathUtilities.Divide(totalSupply, WDemand, 1.0), 0.0, 1.0); } }
/// <summary>Gets the nitrogen supply from the specified zone.</summary> /// <param name="zone">The zone.</param> /// <param name="NO3Supply">The returned NO3 supply</param> /// <param name="NH4Supply">The returned NH4 supply</param> public void CalculateNitrogenSupply(ZoneWaterAndN zone, ref double[] NO3Supply, ref double[] NH4Supply) { ZoneState myZone = Zones.Find(z => z.Name == zone.Zone.Name); if (myZone != null) { if (RWC == null || RWC.Length != myZone.soil.Thickness.Length) { RWC = new double[myZone.soil.Thickness.Length]; } double NO3Uptake = 0; double NH4Uptake = 0; for (int layer = 0; layer < myZone.soil.Thickness.Length; layer++) { if (myZone.LayerLive[layer].Wt > 0) { RWC[layer] = (myZone.soil.Water[layer] - myZone.soil.SoilWater.LL15mm[layer]) / (myZone.soil.SoilWater.DULmm[layer] - myZone.soil.SoilWater.LL15mm[layer]); RWC[layer] = Math.Max(0.0, Math.Min(RWC[layer], 1.0)); double SWAF = NUptakeSWFactor.Value(layer); double kno3 = KNO3.Value(layer); double NO3ppm = zone.NO3N[layer] * (100.0 / (myZone.soil.BD[layer] * myZone.soil.Thickness[layer])); NO3Supply[layer] = Math.Min(zone.NO3N[layer] * kno3 * NO3ppm * SWAF, (MaxDailyNUptake.Value() - NO3Uptake)); NO3Uptake += NO3Supply[layer]; double knh4 = KNH4.Value(layer); double NH4ppm = zone.NH4N[layer] * (100.0 / (myZone.soil.BD[layer] * myZone.soil.Thickness[layer])); NH4Supply[layer] = Math.Min(zone.NH4N[layer] * knh4 * NH4ppm * SWAF, (MaxDailyNUptake.Value() - NH4Uptake)); NH4Uptake += NH4Supply[layer]; } } } }
private void CalculateNitrogenSupply(ZoneState myZone, ZoneWaterAndN zone) { myZone.MassFlow = new double[myZone.soil.Thickness.Length]; myZone.Diffusion = new double[myZone.soil.Thickness.Length]; int currentLayer = Soils.Soil.LayerIndexOfDepth(myZone.Depth, myZone.soil.Thickness); for (int layer = 0; layer <= currentLayer; layer++) { var swdep = myZone.StartWater[layer]; //mm var flow = myZone.WaterUptake[layer]; //NO3N is in kg/ha - old sorghum used g/m^2 var no3conc = zone.NO3N[layer] * kgha2gsm / swdep; var no3massFlow = no3conc * (-flow); myZone.MassFlow[layer] = no3massFlow; //diffusion var swAvailFrac = myZone.AvailableSW[layer] / myZone.PotentialAvailableSW[layer]; //old sorghum stores N03 in g/ms not kg/ha var no3Diffusion = MathUtilities.Bound(swAvailFrac, 0.0, 1.0) * (zone.NO3N[layer] * kgha2gsm); if (layer == currentLayer) { var proportion = Soils.Soil.ProportionThroughLayer(currentLayer, myZone.Depth, myZone.soil.Thickness); no3Diffusion *= proportion; } myZone.Diffusion[layer] = no3Diffusion; //NH4Supply[layer] = no3massFlow; //onyl 2 fields passed in for returning data. //actual uptake needs to distinguish between massflow and diffusion //sorghum calcs don't use nh4 - so using that temporarily } }
/// <summary>Gets or sets the water supply.</summary> /// <param name="zone">The zone.</param> public double[] CalculateWaterSupply(ZoneWaterAndN zone) { ZoneState myZone = Zones.Find(z => z.Name == zone.Zone.Name); if (myZone == null) { return(null); } if (myZone.soil.Weirdo != null) { return(new double[myZone.soil.Thickness.Length]); //With Weirdo, water extraction is not done through the arbitrator because the time step is different. } else { double[] kl = myZone.soil.KL(Plant.Name); double[] ll = myZone.soil.LL(Plant.Name); double[] supply = new double[myZone.soil.Thickness.Length]; LayerMidPointDepth = Soil.ToMidPoints(myZone.soil.Thickness); for (int layer = 0; layer < myZone.soil.Thickness.Length; layer++) { if (layer <= Soil.LayerIndexOfDepth(myZone.Depth, myZone.soil.Thickness)) { supply[layer] = Math.Max(0.0, kl[layer] * klModifier.Value(layer) * (zone.Water[layer] - ll[layer] * myZone.soil.Thickness[layer]) * Soil.ProportionThroughLayer(layer, myZone.Depth, myZone.soil.Thickness)); } } return(supply); } }
/// <summary>Estimates the amount of plant available water in each soil layer of the root zone.</summary> /// <remarks> /// This is an alternative method, which does not use kl. A factor based on Ksat is used instead. This is further modified /// by soil water content and a plant related factor, defined based on root length density. All three factors are normalised /// (using ReferenceKSat and ReferenceRLD for KSat and root and DUL for soil water content). The effect of all factors are /// assumed to vary between zero and one following exponential functions, such that the effect is 90% at the reference value. /// </remarks> /// <param name="myZone">The soil information</param> /// <returns>The amount of available water in each layer (mm)</returns> internal double[] PlantAvailableSoilWaterAlternativeKS(ZoneWaterAndN myZone) { double[] result = new double[nLayers]; SoilCrop soilCropData = (SoilCrop)mySoil.Crop(mySpeciesName); for (int layer = 0; layer <= BottomLayer; layer++) { double condFac = 1.0 - Math.Pow(10.0, -mySoil.KS[layer] / myReferenceKSuptake); double rldFac = 1.0 - Math.Pow(10.0, -RootLengthDensity[layer] / myReferenceRLD); double swFac; if (mySoil.SoilWater.SWmm[layer] >= mySoil.DULmm[layer]) { swFac = 1.0; } else if (mySoil.SoilWater.SWmm[layer] <= mySoil.LL15mm[layer]) { swFac = 0.0; } else { double waterRatio = (myZone.Water[layer] - mySoil.LL15mm[layer]) / (mySoil.DULmm[layer] - mySoil.LL15mm[layer]); swFac = 1.0 - Math.Pow(1.0 - waterRatio, myExponentSoilMoisture); } // Total available water result[layer] = Math.Max(0.0, myZone.Water[layer] - soilCropData.LL[layer]) * mySoil.Thickness[layer]; // Actual plant available water result[layer] *= FractionLayerWithRoots(layer) * Math.Min(1.0, rldFac * condFac * swFac); } return(result); }
private void CalculateNitrogenSupply(ZoneState myZone, ZoneWaterAndN zone) { myZone.MassFlow = new double[myZone.soil.Thickness.Length]; myZone.Diffusion = new double[myZone.soil.Thickness.Length]; int currentLayer = myZone.soil.LayerIndexOfDepth(myZone.Depth); for (int layer = 0; layer <= currentLayer; layer++) { var swdep = myZone.StartWater[layer]; //mm var dltSwdep = myZone.WaterUptake[layer]; //NO3N is in kg/ha - old sorghum used g/m^2 var no3conc = MathUtilities.Divide(zone.NO3N[layer] * kgha2gsm, swdep, 0); var no3massFlow = no3conc * (-dltSwdep); myZone.MassFlow[layer] = Math.Min(no3massFlow, zone.NO3N[layer] * kgha2gsm); //diffusion var swAvailFrac = MathUtilities.Divide(myZone.AvailableSW[layer], myZone.PotentialAvailableSW[layer], 0); //old sorghum stores N03 in g/ms not kg/ha var no3Diffusion = MathUtilities.Bound(swAvailFrac, 0.0, 1.0) * (zone.NO3N[layer] * kgha2gsm); myZone.Diffusion[layer] = Math.Min(no3Diffusion, zone.NO3N[layer] * kgha2gsm) * myZone.RootProportions[layer]; //NH4Supply[layer] = no3massFlow; //onyl 2 fields passed in for returning data. //actual uptake needs to distinguish between massflow and diffusion } }
private void StoreWaterVariablesForNitrogenUptake(ZoneWaterAndN zoneWater) { ZoneState myZone = root.Zones.Find(z => z.Name == zoneWater.Zone.Name); if (myZone != null) { var soilPhysical = myZone.Soil.FindChild <Soils.IPhysical>(); var waterBalance = myZone.Soil.FindChild <ISoilWater>(); //store Water variables for N Uptake calculation //Old sorghum doesn't do actualUptake of Water until end of day myZone.StartWater = new double[soilPhysical.Thickness.Length]; myZone.AvailableSW = new double[soilPhysical.Thickness.Length]; myZone.PotentialAvailableSW = new double[soilPhysical.Thickness.Length]; myZone.Supply = new double[soilPhysical.Thickness.Length]; var soilCrop = Soil.FindDescendant <SoilCrop>(plant.Name + "Soil"); if (soilCrop == null) { throw new Exception($"Cannot find a soil crop parameterisation called {plant.Name + "Soil"}"); } double[] kl = soilCrop.KL; double[] llDep = MathUtilities.Multiply(soilCrop.LL, soilPhysical.Thickness); if (root.Depth != myZone.Depth) { myZone.Depth += 0; // wtf?? } var currentLayer = SoilUtilities.LayerIndexOfDepth(myZone.Physical.Thickness, myZone.Depth); for (int layer = 0; layer <= currentLayer; ++layer) { myZone.StartWater[layer] = waterBalance.SWmm[layer]; myZone.AvailableSW[layer] = Math.Max(waterBalance.SWmm[layer] - llDep[layer] * myZone.LLModifier[layer], 0) * myZone.RootProportions[layer]; myZone.PotentialAvailableSW[layer] = Math.Max(soilPhysical.DULmm[layer] - llDep[layer] * myZone.LLModifier[layer], 0) * myZone.RootProportions[layer]; var proportion = myZone.RootProportions[layer]; myZone.Supply[layer] = Math.Max(myZone.AvailableSW[layer] * kl[layer] * proportion, 0.0); } var totalAvail = myZone.AvailableSW.Sum(); var totalAvailPot = myZone.PotentialAvailableSW.Sum(); var totalSupply = myZone.Supply.Sum(); WatSupply = totalSupply; // Set reporting variables. //Avail = myZone.AvailableSW; //PotAvail = myZone.PotentialAvailableSW; //used for SWDef PhenologyStress table lookup SWAvailRatio = MathUtilities.Bound(MathUtilities.Divide(totalAvail, totalAvailPot, 1.0), 0.0, 10.0); //used for SWDef ExpansionStress table lookup SDRatio = MathUtilities.Bound(MathUtilities.Divide(totalSupply, WDemand, 1.0), 0.0, 10); //used for SwDefPhoto Stress //PhotoStress = MathUtilities.Bound(MathUtilities.Divide(totalSupply, WDemand, 1.0), 0.0, 1.0); } }
/// <summary>Estimates the amount of plant available nitrogen in each soil layer of the root zone.</summary> /// <remarks> /// This method considers soil water as the main factor controlling N availability/uptake. /// Availability is given by the proportion of water taken up in each layer, further modified by uptake factors /// Uptake is caped for a maximum value plants can take in one day. /// </remarks> /// <param name="myZone">The soil information</param> /// <param name="mySoilWaterUptake">Soil water uptake</param> private void PlantAvailableSoilNAlternativeWup(ZoneWaterAndN myZone, double[] mySoilWaterUptake) { double layerFrac; // the fraction of layer within the root zone double potAvailableN; // potential available N for (int layer = 0; layer <= BottomLayer; layer++) { layerFrac = FractionLayerWithRoots(layer); double swuFac = MathUtilities.Divide(mySoilWaterUptake[layer], myZone.Water[layer], 0.0); // get NH4 available potAvailableN = myZone.PlantAvailableNH4N[layer] * layerFrac; mySoilNH4Available[layer] = potAvailableN * Math.Min(1.0, swuFac * myKuNH4); // get NO3 available potAvailableN = myZone.PlantAvailableNO3N[layer] * layerFrac; mySoilNO3Available[layer] = potAvailableN * Math.Min(1.0, swuFac * myKuNO3); } // check for maximum uptake potAvailableN = mySoilNH4Available.Sum() + mySoilNO3Available.Sum(); if (potAvailableN > myMaximumNUptake) { double upFraction = myMaximumNUptake / potAvailableN; for (int layer = 0; layer <= BottomLayer; layer++) { mySoilNH4Available[layer] *= upFraction; mySoilNO3Available[layer] *= upFraction; } } }
/// <summary>Finds out the amount of plant available water in the soil.</summary> /// <param name="myZone">The soil information</param> internal void EvaluateSoilWaterAvailability(ZoneWaterAndN myZone) { for (int layer = 0; layer <= BottomLayer; layer++) { mySoilWaterAvailable[layer] = Math.Max(0.0, myZone.Water[layer] - soilCropData.LLmm[layer]); mySoilWaterAvailable[layer] *= FractionLayerWithRoots(layer) * soilCropData.KL[layer] * KLModiferDueToDamage(layer); } }
/// <summary>Finds out the amount of plant available nitrogen (NH4 and NO3) in the soil.</summary> /// <param name="myZone">The soil information</param> /// <param name="mySoilWaterUptake">Soil water uptake</param> internal void EvaluateSoilNitrogenAvailable(ZoneWaterAndN myZone, double[] mySoilWaterUptake) { double layerFrac; // the fraction of layer within the root zone double swFac; // the soil water factor double bdFac; // the soil density factor double potAvailableN; // potential available N var thickness = soilPhysical.Thickness; var bd = soilPhysical.BD; var water = myZone.Water; var nh4 = myZone.NH4N; var no3 = myZone.NO3N; double depthOfTopOfLayer = 0; for (int layer = 0; layer <= BottomLayer; layer++) { layerFrac = (Depth - depthOfTopOfLayer) / thickness[layer]; layerFrac = Math.Min(1.0, Math.Max(0.0, layerFrac)); bdFac = 100.0 / (thickness[layer] * bd[layer]); if (water[layer] >= dulMM[layer]) { swFac = 1.0; } else if (water[layer] <= ll15MM[layer]) { swFac = 0.0; } else { double waterRatio = (water[layer] - ll15MM[layer]) / (dulMM[layer] - ll15MM[layer]); waterRatio = MathUtilities.Bound(waterRatio, 0.0, 1.0); swFac = 1.0 - Math.Pow(1.0 - waterRatio, ExponentSoilMoisture); } // get NH4 available potAvailableN = nh4[layer] * layerFrac * swFac * bdFac * KNH4; mySoilNH4Available[layer] = Math.Min(nh4[layer] * layerFrac, potAvailableN); // get NO3 available potAvailableN = no3[layer] * layerFrac * swFac * bdFac * KNO3; mySoilNO3Available[layer] = Math.Min(no3[layer] * layerFrac, potAvailableN); depthOfTopOfLayer += thickness[layer]; } // check for maximum uptake potAvailableN = mySoilNH4Available.Sum() + mySoilNO3Available.Sum(); if (potAvailableN > MaximumNUptake) { double upFraction = MaximumNUptake / potAvailableN; for (int layer = 0; layer <= BottomLayer; layer++) { mySoilNH4Available[layer] *= upFraction; mySoilNO3Available[layer] *= upFraction; } } }
/// <summary>The method used to arbitrate N allocations</summary> public List <ZoneWaterAndN> GetUptakeEstimates(SoilState soilstate, IArbitration[] Organs) { // Get all water supplies. double waterSupply = 0; //NOTE: This is in L, not mm, to arbitrate water demands for spatial simulations. List <double[]> supplies = new List <double[]>(); List <ZoneWaterAndN> zones = new List <ZoneWaterAndN>(); foreach (ZoneWaterAndN zone in soilstate.Zones) { foreach (IOrgan o in Organs) { if (o is IWaterNitrogenUptake) { double[] organSupply = (o as IWaterNitrogenUptake).CalculateWaterSupply(zone); if (organSupply != null) { supplies.Add(organSupply); zones.Add(zone); waterSupply += MathUtilities.Sum(organSupply) * zone.Zone.Area; } } } } // Calculate total water demand. double waterDemand = 0; //NOTE: This is in L, not mm, to arbitrate water demands for spatial simulations. foreach (IHasWaterDemand WD in WaterDemands) { waterDemand += WD.CalculateWaterDemand() * plant.Zone.Area; } // Calculate demand / supply ratio. double fractionUsed = 0; if (waterSupply > 0) { fractionUsed = Math.Min(1.0, waterDemand / waterSupply); } // Apply demand supply ratio to each zone and create a ZoneWaterAndN structure // to return to caller. List <ZoneWaterAndN> ZWNs = new List <ZoneWaterAndN>(); for (int i = 0; i < supplies.Count; i++) { // Just send uptake from my zone ZoneWaterAndN uptake = new ZoneWaterAndN(zones[i]); uptake.Water = MathUtilities.Multiply_Value(supplies[i], fractionUsed); uptake.NO3N = new double[uptake.Water.Length]; uptake.NH4N = new double[uptake.Water.Length]; uptake.PlantAvailableNO3N = new double[uptake.Water.Length]; uptake.PlantAvailableNH4N = new double[uptake.Water.Length]; ZWNs.Add(uptake); } return(ZWNs); }
/// <summary>Calculate Nitrogen UptakeEstimates</summary> public List <ZoneWaterAndN> GetUptakeEstimates(SoilState soilstate, IArbitration[] Organs) { var N = Arbitrator.N; double NSupply = 0;//NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations. for (int i = 0; i < Organs.Count(); i++) { N.UptakeSupply[i] = 0; } List <ZoneWaterAndN> zones = new List <ZoneWaterAndN>(); foreach (ZoneWaterAndN zone in soilstate.Zones) { ZoneWaterAndN UptakeDemands = new ZoneWaterAndN(zone); UptakeDemands.NO3N = new double[zone.NO3N.Length]; UptakeDemands.NH4N = new double[zone.NH4N.Length]; UptakeDemands.Water = new double[UptakeDemands.NO3N.Length]; //Get Nuptake supply from each organ and set the PotentialUptake parameters that are passed to the soil arbitrator for (int i = 0; i < Organs.Count(); i++) { if (Organs[i] is IWaterNitrogenUptake) { double[] organNO3Supply = new double[zone.NO3N.Length]; double[] organNH4Supply = new double[zone.NH4N.Length]; (Organs[i] as IWaterNitrogenUptake).CalculateNitrogenSupply(zone, ref organNO3Supply, ref organNH4Supply); UptakeDemands.NO3N = MathUtilities.Add(UptakeDemands.NO3N, organNO3Supply); //Add uptake supply from each organ to the plants total to tell the Soil arbitrator UptakeDemands.NH4N = MathUtilities.Add(UptakeDemands.NH4N, organNH4Supply); double organSupply = organNH4Supply.Sum() + organNO3Supply.Sum(); N.UptakeSupply[i] += organSupply * kgha2gsm * zone.Zone.Area / this.zone.Area; NSupply += organSupply * zone.Zone.Area; } } zones.Add(UptakeDemands); } double NDemand = (N.TotalPlantDemand - N.TotalReallocation) / kgha2gsm * zone.Area; //NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations. if (NDemand < 0) { NDemand = 0; //NSupply should be zero if Reallocation can meet all demand (including small rounding errors which can make this -ve) } if (NSupply > NDemand) { //Reduce the PotentialUptakes that we pass to the soil arbitrator double ratio = Math.Min(1.0, NDemand / NSupply); foreach (ZoneWaterAndN UptakeDemands in zones) { UptakeDemands.NO3N = MathUtilities.Multiply_Value(UptakeDemands.NO3N, ratio); UptakeDemands.NH4N = MathUtilities.Multiply_Value(UptakeDemands.NH4N, ratio); } } return(zones); }
/// <summary> /// Calculate the potential N uptake for today. Should return null if crop is not in the ground. /// </summary> public virtual List <Soils.Arbitrator.ZoneWaterAndN> GetNitrogenUptakeEstimates(SoilState soilstate) { if (Plant.IsEmerged) { double NSupply = 0;//NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations. for (int i = 0; i < Organs.Count; i++) { N.UptakeSupply[i] = 0; } List <ZoneWaterAndN> zones = new List <ZoneWaterAndN>(); foreach (ZoneWaterAndN zone in soilstate.Zones) { ZoneWaterAndN UptakeDemands = new ZoneWaterAndN(zone); UptakeDemands.NO3N = new double[zone.NO3N.Length]; UptakeDemands.NH4N = new double[zone.NH4N.Length]; UptakeDemands.PlantAvailableNO3N = new double[zone.NO3N.Length]; UptakeDemands.PlantAvailableNH4N = new double[zone.NO3N.Length]; UptakeDemands.Water = new double[UptakeDemands.NO3N.Length]; //Get Nuptake supply from each organ and set the PotentialUptake parameters that are passed to the soil arbitrator for (int i = 0; i < Organs.Count; i++) { if (Organs[i] is IWaterNitrogenUptake) { double[] organNO3Supply = new double[zone.NO3N.Length]; double[] organNH4Supply = new double[zone.NH4N.Length]; (Organs[i] as IWaterNitrogenUptake).CalculateNitrogenSupply(zone, ref organNO3Supply, ref organNH4Supply); UptakeDemands.NO3N = MathUtilities.Add(UptakeDemands.NO3N, organNO3Supply); //Add uptake supply from each organ to the plants total to tell the Soil arbitrator UptakeDemands.NH4N = MathUtilities.Add(UptakeDemands.NH4N, organNH4Supply); N.UptakeSupply[i] += (MathUtilities.Sum(organNH4Supply) + MathUtilities.Sum(organNO3Supply)) * kgha2gsm * zone.Zone.Area / Plant.Zone.Area; NSupply += (MathUtilities.Sum(organNH4Supply) + MathUtilities.Sum(organNO3Supply)) * zone.Zone.Area; } } zones.Add(UptakeDemands); } double NDemand = (N.TotalPlantDemand - N.TotalReallocation) / kgha2gsm * Plant.Zone.Area; //NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations. if (NSupply > NDemand) { //Reduce the PotentialUptakes that we pass to the soil arbitrator double ratio = Math.Min(1.0, NDemand / NSupply); foreach (ZoneWaterAndN UptakeDemands in zones) { UptakeDemands.NO3N = MathUtilities.Multiply_Value(UptakeDemands.NO3N, ratio); UptakeDemands.NH4N = MathUtilities.Multiply_Value(UptakeDemands.NH4N, ratio); } } return(zones); } return(null); }
/// <summary>Finds out the amount of plant available water in the soil.</summary> /// <param name="myZone">The soil information</param> internal double[] EvaluateSoilWaterAvailable(ZoneWaterAndN myZone) { double[] result = new double[nLayers]; for (int layer = 0; layer <= BottomLayer; layer++) { result[layer] = Math.Max(0.0, myZone.Water[layer] - (soilCropData.LL[layer] * soilPhysical.Thickness[layer])); result[layer] *= FractionLayerWithRoots(layer) * soilCropData.KL[layer] * KLModiferDueToDamage(layer); } return(result); }
/// <summary>Estimates the amount of plant available nitrogen in each soil layer of the root zone.</summary> /// <remarks>This is a basic method, used as default in old AgPasture, all N in the root zone is available</remarks> /// <param name="myZone">The soil information</param> private void PlantAvailableSoilNBasicAgPasture(ZoneWaterAndN myZone) { double layerFrac; // the fraction of layer within the root zone for (int layer = 0; layer <= BottomLayer; layer++) { layerFrac = FractionLayerWithRoots(layer); mySoilNH4Available[layer] = myZone.PlantAvailableNH4N[layer] * layerFrac; mySoilNO3Available[layer] = myZone.PlantAvailableNO3N[layer] * layerFrac; } }
/// <summary>Estimates the amount of plant available water in each soil layer of the root zone.</summary> /// <remarks>This is the default APSIM method, with kl representing the daily rate for water extraction</remarks> /// <param name="myZone">The soil information</param> /// <returns>The amount of available water in each layer (mm)</returns> internal double[] PlantAvailableSoilWaterDefault(ZoneWaterAndN myZone) { double[] result = new double[nLayers]; SoilCrop soilCropData = (SoilCrop)mySoil.Crop(mySpeciesName); for (int layer = 0; layer <= BottomLayer; layer++) { result[layer] = Math.Max(0.0, myZone.Water[layer] - (soilCropData.LL[layer] * mySoil.Thickness[layer])); result[layer] *= FractionLayerWithRoots(layer) * soilCropData.KL[layer] * KLModiferDueToDamage(layer); } return(result); }
/// <summary>Estimates the amount of plant available nitrogen in each soil layer of the root zone.</summary> /// <remarks> /// This method approximates the default approach in APSIM plants (method 3 in Plant1 models) /// Soil water status and uptake coefficient control the availability, which is a square function of N content. /// Uptake is capped for a maximum value plants can take in one day. /// </remarks> /// <param name="myZone">The soil information</param> private void PlantAvailableSoilNDefaultAPSIM(ZoneWaterAndN myZone) { double layerFrac; // the fraction of layer within the root zone double swFac; // the soil water factor double bdFac; // the soil density factor double potAvailableN; // potential available N for (int layer = 0; layer <= BottomLayer; layer++) { layerFrac = FractionLayerWithRoots(layer); bdFac = 100.0 / (mySoil.Thickness[layer] * mySoil.BD[layer]); if (myZone.Water[layer] >= mySoil.DULmm[layer]) { swFac = 1.0; } else if (myZone.Water[layer] <= mySoil.LL15mm[layer]) { swFac = 0.0; } else { double waterRatio = (myZone.Water[layer] - mySoil.LL15mm[layer]) / (mySoil.DULmm[layer] - mySoil.LL15mm[layer]); waterRatio = MathUtilities.Bound(waterRatio, 0.0, 1.0); swFac = 1.0 - Math.Pow(1.0 - waterRatio, myExponentSoilMoisture); } // get NH4 available potAvailableN = Math.Pow(myZone.PlantAvailableNH4N[layer] * layerFrac, 2.0) * swFac * bdFac * myKNH4; mySoilNH4Available[layer] = Math.Min(myZone.PlantAvailableNH4N[layer] * layerFrac, potAvailableN); // get NO3 available potAvailableN = Math.Pow(myZone.PlantAvailableNO3N[layer] * layerFrac, 2.0) * swFac * bdFac * myKNO3; mySoilNO3Available[layer] = Math.Min(myZone.PlantAvailableNO3N[layer] * layerFrac, potAvailableN); } // check for maximum uptake potAvailableN = mySoilNH4Available.Sum() + mySoilNO3Available.Sum(); if (potAvailableN > myMaximumNUptake) { double upFraction = myMaximumNUptake / potAvailableN; for (int layer = 0; layer <= BottomLayer; layer++) { mySoilNH4Available[layer] *= upFraction; mySoilNO3Available[layer] *= upFraction; } } }
/// <summary>Estimates the amount of plant available nitrogen in each soil layer of the root zone.</summary> /// <remarks> /// This method considers soil water status and root length density to define factors controlling N availability. /// Soil water status is used to define a factor that varies from zero at LL, below which no uptake can happen, /// to one at DUL, above which no restrictions to uptake exist. /// Root length density is used to define a factor varying from zero if there are no roots to one when root length /// density is equal to a ReferenceRLD, above which there are no restrictions for uptake. /// Factors for each N form can also alter the amount available. /// Uptake is caped for a maximum value plants can take in one day. /// </remarks> /// <param name="myZone">The soil information</param> private void PlantAvailableSoilNAlternativeRLD(ZoneWaterAndN myZone) { double layerFrac; // the fraction of layer within the root zone double swFac; // the soil water factor double rldFac; // the root density factor double potAvailableN; // potential available N for (int layer = 0; layer <= BottomLayer; layer++) { layerFrac = FractionLayerWithRoots(layer); rldFac = Math.Min(1.0, MathUtilities.Divide(RootLengthDensity[layer], myReferenceRLD, 1.0)); if (myZone.Water[layer] >= mySoil.DULmm[layer]) { swFac = 1.0; } else if (myZone.Water[layer] <= mySoil.LL15mm[layer]) { swFac = 0.0; } else { double waterRatio = (myZone.Water[layer] - mySoil.LL15mm[layer]) / (mySoil.DULmm[layer] - mySoil.LL15mm[layer]); swFac = 1.0 - Math.Pow(1.0 - waterRatio, myExponentSoilMoisture); } // get NH4 available potAvailableN = myZone.PlantAvailableNH4N[layer] * layerFrac; mySoilNH4Available[layer] = potAvailableN * Math.Min(1.0, swFac * rldFac * myKuNH4); // get NO3 available potAvailableN = myZone.PlantAvailableNO3N[layer] * layerFrac; mySoilNO3Available[layer] = potAvailableN * Math.Min(1.0, swFac * rldFac * myKuNO3); } // check for maximum uptake potAvailableN = mySoilNH4Available.Sum() + mySoilNO3Available.Sum(); if (potAvailableN > myMaximumNUptake) { double upFraction = myMaximumNUptake / potAvailableN; for (int layer = 0; layer <= BottomLayer; layer++) { mySoilNH4Available[layer] *= upFraction; mySoilNO3Available[layer] *= upFraction; } } }
/// <summary>Finds out the amount of plant available nitrogen (NH4 and NO3) in the soil.</summary> /// <remarks> /// N availability is considered only within the root zone, and is affected by moisture (dry soils /// having less N available) and N concentration (low concentration leads to reduced availability). /// The effect of soil moisture is a curve starting at LL, where it is zero, reaching one at DUL. /// An exponent bends the pattern between these two values, making it concave if the exponent is /// greater than one, with the derivative being zero at DUL; /// The effect of concentration is a simple linear function starting at zero when there is no N in /// the soil, and reaching its maximum (one) at a concentration defined by the 'kNxx' parameter. /// This (1/kNxx) represents the critical concentration (in ppm), below which N availability is /// limited (e.g. a KNO3 = 0.02 means no limitations if the NO3 concentration is above 50 ppm). /// </remarks> /// <param name="myZone">The soil information from the zone that contains the roots.</param> internal void EvaluateSoilNitrogenAvailability(ZoneWaterAndN myZone) { var thickness = soilPhysical.Thickness; var bd = soilPhysical.BD; var dulMM = soilPhysical.DULmm; var llMM = soilCropData.LLmm; var swMM = myZone.Water; var nh4 = myZone.NH4N; var no3 = myZone.NO3N; double depthAtTopOfLayer = 0; for (int layer = 0; layer <= BottomLayer; layer++) { // get the fraction of this layer that is within the root zone double layerFraction = MathUtilities.Bound((Depth - depthAtTopOfLayer) / thickness[layer], 0.0, 1.0); // get the soil moisture factor (less N available in drier soil) double rwc = MathUtilities.Bound((swMM[layer] - llMM[layer]) / (dulMM[layer] - llMM[layer]), 0.0, 1.0); double moistureFactor = 1.0 - Math.Pow(1.0 - rwc, ExponentSoilMoisture); // get NH4 available double nh4ppm = nh4[layer] * 100.0 / (thickness[layer] * bd[layer]); double concentrationFactor = Math.Min(1.0, nh4ppm * KNH4); mySoilNH4Available[layer] = nh4[layer] * layerFraction *Math.Min(0.999999, moistureFactor *concentrationFactor); // get NO3 available double no3ppm = no3[layer] * 100.0 / (thickness[layer] * bd[layer]); concentrationFactor = Math.Min(1.0, no3ppm * KNO3); mySoilNO3Available[layer] = no3[layer] * layerFraction *Math.Min(0.999999, moistureFactor *concentrationFactor); depthAtTopOfLayer += thickness[layer]; } // check totals, reduce available N if greater than maximum uptake double potentialAvailableN = mySoilNH4Available.Sum() + mySoilNO3Available.Sum(); if (potentialAvailableN > MaximumNUptake) { double upFraction = MaximumNUptake / potentialAvailableN; for (int layer = 0; layer <= BottomLayer; layer++) { mySoilNH4Available[layer] *= upFraction; mySoilNO3Available[layer] *= upFraction; } } }
/// <summary>Gets the nitrogen supply from the specified zone.</summary> /// <param name="zone">The zone.</param> /// <param name="NO3Supply">The returned NO3 supply</param> /// <param name="NH4Supply">The returned NH4 supply</param> public void CalculateNitrogenSupply(ZoneWaterAndN zone, ref double[] NO3Supply, ref double[] NH4Supply) { ZoneState myZone = Zones.Find(z => z.Name == zone.Zone.Name); if (myZone != null) { if (RWC == null || RWC.Length != myZone.soil.Thickness.Length) { RWC = new double[myZone.soil.Thickness.Length]; } double NO3Uptake = 0; double NH4Uptake = 0; double[] thickness = myZone.soil.Thickness; double[] water = myZone.soil.Water; double[] ll15mm = myZone.soil.LL15mm; double[] dulmm = myZone.soil.DULmm; double[] bd = myZone.soil.BD; double accuDepth = 0; for (int layer = 0; layer < thickness.Length; layer++) { accuDepth += thickness[layer]; if (myZone.LayerLive[layer].Wt > 0) { double factorRootDepth = Math.Max(0, Math.Min(1, 1 - (accuDepth - Depth) / thickness[layer])); RWC[layer] = (water[layer] - ll15mm[layer]) / (dulmm[layer] - ll15mm[layer]); RWC[layer] = Math.Max(0.0, Math.Min(RWC[layer], 1.0)); double SWAF = nUptakeSWFactor.Value(layer); double kno3 = this.kno3.Value(layer); double NO3ppm = zone.NO3N[layer] * (100.0 / (bd[layer] * thickness[layer])); NO3Supply[layer] = Math.Min(zone.NO3N[layer] * kno3 * NO3ppm * SWAF * factorRootDepth, (maxDailyNUptake.Value() - NO3Uptake)); NO3Uptake += NO3Supply[layer]; double knh4 = this.knh4.Value(layer); double NH4ppm = zone.NH4N[layer] * (100.0 / (bd[layer] * thickness[layer])); NH4Supply[layer] = Math.Min(zone.NH4N[layer] * knh4 * NH4ppm * SWAF * factorRootDepth, (maxDailyNUptake.Value() - NH4Uptake)); NH4Uptake += NH4Supply[layer]; } } } }
/// <summary>Finds out the amount of plant available water in the soil.</summary> /// <param name="myZone">The soil information</param> internal double[] EvaluateSoilWaterAvailable(ZoneWaterAndN myZone) { if (myWaterAvailableMethod == PastureSpecies.PlantAvailableWaterMethod.DefaultAPSIM) { return(PlantAvailableSoilWaterDefault(myZone)); } else if (myWaterAvailableMethod == PastureSpecies.PlantAvailableWaterMethod.AlternativeKL) { return(PlantAvailableSoilWaterAlternativeKL(myZone)); } else if (myWaterAvailableMethod == PastureSpecies.PlantAvailableWaterMethod.AlternativeKS) { return(PlantAvailableSoilWaterAlternativeKS(myZone)); } else { throw new Exception("Invalid water uptake method found"); } }
/// <summary>Finds out the amount of plant available nitrogen (NH4 and NO3) in the soil.</summary> /// <param name="myZone">The soil information</param> /// <param name="mySoilWaterUptake">Soil water uptake</param> internal void EvaluateSoilNitrogenAvailable(ZoneWaterAndN myZone, double[] mySoilWaterUptake) { if (myNitrogenAvailableMethod == PastureSpecies.PlantAvailableNitrogenMethod.BasicAgPasture) { PlantAvailableSoilNBasicAgPasture(myZone); } else if (myNitrogenAvailableMethod == PastureSpecies.PlantAvailableNitrogenMethod.DefaultAPSIM) { PlantAvailableSoilNDefaultAPSIM(myZone); } else if (myNitrogenAvailableMethod == PastureSpecies.PlantAvailableNitrogenMethod.AlternativeRLD) { PlantAvailableSoilNAlternativeRLD(myZone); } else if (myNitrogenAvailableMethod == PastureSpecies.PlantAvailableNitrogenMethod.AlternativeWup) { PlantAvailableSoilNAlternativeWup(myZone, mySoilWaterUptake); } }
/// <summary>Calculate the potential sw uptake for today</summary> /// <param name="soilstate"></param> /// <returns>list of uptakes</returns> /// <exception cref="ApsimXException">Could not find root zone in Zone + this.Parent.Name + for SimpleTree</exception> public List <ZoneWaterAndN> GetWaterUptakeEstimates(SoilState soilstate) { ZoneWaterAndN MyZone = new ZoneWaterAndN(this.Parent as Zone); foreach (ZoneWaterAndN Z in soilstate.Zones) { if (Z.Zone.Name == this.Parent.Name) { MyZone = Z; } } double[] PotSWUptake = new double[Soil.LL15.Length]; SWUptake = new double[Soil.LL15.Length]; SoilCrop soilCrop = Soil.Crop(this.Name) as SoilCrop; for (int j = 0; j < Soil.LL15mm.Length; j++) { PotSWUptake[j] = Math.Max(0.0, RootProportion(j, RootDepth) * soilCrop.KL[j] * (MyZone.Water[j] - Soil.LL15mm[j])); } double TotPotSWUptake = MathUtilities.Sum(PotSWUptake); for (int j = 0; j < Soil.LL15mm.Length; j++) { SWUptake[j] = PotSWUptake[j] * Math.Min(1.0, PotentialEP / TotPotSWUptake); } List <ZoneWaterAndN> Uptakes = new List <ZoneWaterAndN>(); ZoneWaterAndN Uptake = new ZoneWaterAndN(this.Parent as Zone); Uptake.Water = SWUptake; Uptake.NO3N = new double[SWUptake.Length]; Uptake.NH4N = new double[SWUptake.Length]; Uptake.NH4N = new double[SWUptake.Length]; Uptake.PlantAvailableNO3N = new double[SWUptake.Length]; Uptake.PlantAvailableNH4N = new double[SWUptake.Length]; Uptakes.Add(Uptake); return(Uptakes); }
/// <summary>Placeholder for SoilArbitrator</summary> /// <param name="soilstate">soil state</param> /// <returns></returns> public List <ZoneWaterAndN> GetNitrogenUptakeEstimates(SoilState soilstate) { ZoneWaterAndN MyZone = new ZoneWaterAndN(this.Parent as Zone); foreach (ZoneWaterAndN Z in soilstate.Zones) { if (Z.Zone.Name == this.Parent.Name) { MyZone = Z; } } double[] PotNO3Uptake = new double[MyZone.NO3N.Length]; double[] PotNH4Uptake = new double[MyZone.NH4N.Length]; NO3Uptake = new double[MyZone.NO3N.Length]; NH4Uptake = new double[MyZone.NH4N.Length]; var soilCrop = Soil.Crop(Name); for (int j = 0; j < Soil.LL15mm.Length; j++) { PotNO3Uptake[j] = Math.Max(0.0, RootProportion(j, RootDepth) * soilCrop.KL[j] * MyZone.NO3N[j]); PotNH4Uptake[j] = Math.Max(0.0, RootProportion(j, RootDepth) * soilCrop.KL[j] * MyZone.NH4N[j]); } double TotPotNUptake = MathUtilities.Sum(PotNO3Uptake) + MathUtilities.Sum(PotNH4Uptake); for (int j = 0; j < MyZone.NO3N.Length; j++) { NO3Uptake[j] = PotNO3Uptake[j] * Math.Min(1.0, NDemand / TotPotNUptake); NH4Uptake[j] = PotNH4Uptake[j] * Math.Min(1.0, NDemand / TotPotNUptake); } List <ZoneWaterAndN> Uptakes = new List <ZoneWaterAndN>(); ZoneWaterAndN Uptake = new ZoneWaterAndN(this.Parent as Zone); Uptake.NO3N = NO3Uptake; Uptake.NH4N = NH4Uptake; Uptake.PlantAvailableNO3N = new double[NO3Uptake.Length]; Uptake.PlantAvailableNH4N = new double[NO3Uptake.Length]; Uptake.Water = new double[NO3Uptake.Length]; Uptakes.Add(Uptake); return(Uptakes); }
private void StoreWaterVariablesForNitrogenUptake(ZoneWaterAndN zoneWater) { ZoneState myZone = root.Zones.Find(z => z.Name == zoneWater.Zone.Name); if (myZone != null) { //store Water variables for N Uptake calculation //Old sorghum doesn't do actualUptake of Water until end of day myZone.StartWater = new double[myZone.soil.Thickness.Length]; myZone.AvailableSW = new double[myZone.soil.Thickness.Length]; myZone.PotentialAvailableSW = new double[myZone.soil.Thickness.Length]; for(int layer = 0; layer < myZone.soil.Thickness.Length; ++layer) { myZone.StartWater[layer] = myZone.soil.Water[layer]; myZone.AvailableSW[layer] = myZone.soil.Water[layer] - myZone.soil.LL15mm[layer]; myZone.PotentialAvailableSW[layer] = myZone.soil.DULmm[layer] - myZone.soil.LL15mm[layer]; } } }
/// <summary> /// Returns soil Nitrogen uptake from each zone by the static tree model /// </summary> /// <param name="soilstate"></param> /// <returns></returns> public List <Soils.Arbitrator.ZoneWaterAndN> GetNUptakes(Soils.Arbitrator.SoilState soilstate) { List <ZoneWaterAndN> Uptakes = new List <ZoneWaterAndN>(); foreach (ZoneWaterAndN Z in soilstate.Zones) { foreach (ZoneInfo ZI in ZoneInfoList) { if (Z.Name == ZI.zone.Name) { ZoneWaterAndN Uptake = new ZoneWaterAndN(); //Find the soil for this zone Zone ThisZone = new Zone(); Soils.Soil ThisSoil = new Soils.Soil(); foreach (Zone SearchZ in Apsim.ChildrenRecursively(Parent, typeof(Zone))) { if (SearchZ.Name == Z.Name) { ThisSoil = Apsim.Find(SearchZ, typeof(Soils.Soil)) as Soils.Soil; } } Uptake.Name = Z.Name; double[] SW = Z.Water; Uptake.NO3N = new double[SW.Length]; Uptake.NH4N = new double[SW.Length]; Uptake.Water = new double[SW.Length]; //for (int i = 0; i <= SW.Length-1; i++) // Uptake.NO3N[i] = Z.NO3N[i] * ZI.RLD[i]; Uptakes.Add(Uptake); } } } return(Uptakes); }
/// <summary>Gets or sets the water supply.</summary> /// <param name="zone">The zone.</param> public double[] CalculateWaterSupply(ZoneWaterAndN zone) { ZoneState myZone = Zones.Find(z => z.Name == zone.Zone.Name); if (myZone == null) { return(null); } SoilCrop crop = myZone.soil.Crop(Plant.Name) as SoilCrop; double[] supply = new double[myZone.soil.Thickness.Length]; double[] layerMidPoints = Soil.ToMidPoints(myZone.soil.Thickness); for (int layer = 0; layer < myZone.soil.Thickness.Length; layer++) { if (layer <= Soil.LayerIndexOfDepth(myZone.Depth, myZone.soil.Thickness)) { supply[layer] = Math.Max(0.0, crop.KL[layer] * KLModifier.ValueForX(layerMidPoints[layer]) * (zone.Water[layer] - crop.LL[layer] * myZone.soil.Thickness[layer]) * Soil.ProportionThroughLayer(layer, myZone.Depth, myZone.soil.Thickness)); } } return(supply); }
/// <summary> /// Returns soil Nitrogen uptake from each zone by the static tree model /// </summary> /// <param name="soilstate"></param> /// <returns></returns> public List <Soils.Arbitrator.ZoneWaterAndN> GetNitrogenUptakeEstimates(Soils.Arbitrator.SoilState soilstate) { Zone treeZone = ZoneList.FirstOrDefault() as Zone; List <ZoneWaterAndN> Uptakes = new List <ZoneWaterAndN>(); double PotNO3Supply = 0; // Total N supply (kg) double NDemandkg = GetNDemandToday() * 10 * treeZone.Area; foreach (ZoneWaterAndN Z in soilstate.Zones) { foreach (Zone ZI in ZoneList) { if (Z.Zone.Name == ZI.Name) { ZoneWaterAndN Uptake = new ZoneWaterAndN(ZI); //Find the soil for this zone Soils.Soil ThisSoil = null; Soils.IPhysical soilPhysical = null; foreach (Zone SearchZ in forestryZones) { if (SearchZ.Name == Z.Zone.Name) { ThisSoil = SearchZ.FindInScope <Soils.Soil>(); soilPhysical = ThisSoil.FindChild <Soils.IPhysical>(); break; } } double[] SW = Z.Water; Uptake.NO3N = new double[SW.Length]; Uptake.NH4N = new double[SW.Length]; Uptake.Water = new double[SW.Length]; double[] LL15mm = MathUtilities.Multiply(soilPhysical.LL15, soilPhysical.Thickness); double[] BD = soilPhysical.BD; double[] RLD = GetRLD(ZI); for (int i = 0; i <= SW.Length - 1; i++) { Uptake.NO3N[i] = PotentialNO3Uptake(soilPhysical.Thickness[i], Z.NO3N[i], Z.Water[i], RLD[i], RootRadius, BD[i], Kd); Uptake.NO3N[i] *= 10; // convert from g/m2 to kg/ha PotNO3Supply += Uptake.NO3N[i] * ZI.Area; } Uptakes.Add(Uptake); break; } } } // Now scale back uptakes if demand > supply double F = 0; // Uptake scaling factor if (PotNO3Supply > 0) { F = NDemandkg / PotNO3Supply; if (F > 1) { F = 1; } } else { F = 1; } NStress = Math.Min(1, Math.Max(0, PotNO3Supply / NDemandkg)); List <double> uptakeList = new List <double>(); foreach (ZoneWaterAndN Z in Uptakes) { Z.NO3N = MathUtilities.Multiply_Value(Z.NO3N, F); uptakeList.Add(Z.TotalNO3N); } NUptake = uptakeList.ToArray(); return(Uptakes); }
/// <summary> /// Returns soil water uptake from each zone by the static tree model /// </summary> /// <param name="soilstate"></param> /// <returns></returns> public List <Soils.Arbitrator.ZoneWaterAndN> GetWaterUptakeEstimates(Soils.Arbitrator.SoilState soilstate) { double Etz = treeZoneWater.Eo; //Eo of Tree Zone SWDemand = 0; foreach (Zone ZI in ZoneList) { SWDemand += Etz * (GetShade(ZI) / 100) * (ZI.Area * 10000); // 100 converts from %, 10000 converts from ha to m2 } IndividualTreeWaterDemand = SWDemand / NumberOfTrees; List <ZoneWaterAndN> Uptakes = new List <ZoneWaterAndN>(); double PotSWSupply = 0; // Total water supply (L) foreach (ZoneWaterAndN Z in soilstate.Zones) { foreach (Zone ZI in ZoneList) { if (Z.Zone.Name == ZI.Name) { ZoneWaterAndN Uptake = new ZoneWaterAndN(ZI); //Find the soil for this zone Soils.Soil ThisSoil = null; Soils.IPhysical soilPhysical = null; foreach (Zone SearchZ in forestryZones) { if (SearchZ.Name == Z.Zone.Name) { ThisSoil = SearchZ.FindInScope <Soils.Soil>(); soilPhysical = ThisSoil.FindChild <Soils.IPhysical>(); break; } } double[] SW = Z.Water; Uptake.NO3N = new double[SW.Length]; Uptake.NH4N = new double[SW.Length]; Uptake.Water = new double[SW.Length]; double[] LL15mm = MathUtilities.Multiply(soilPhysical.LL15, soilPhysical.Thickness); double[] RLD = GetRLD(ZI); for (int i = 0; i <= SW.Length - 1; i++) { Uptake.Water[i] = Math.Max(SW[i] - LL15mm[i], 0.0) * BaseKL * RLD[i]; PotSWSupply += Uptake.Water[i] * ZI.Area * 10000; } Uptakes.Add(Uptake); break; } } } // Now scale back uptakes if supply > demand double F = 0; // Uptake scaling factor if (PotSWSupply > 0) { F = SWDemand / PotSWSupply; if (F > 1) { F = 1; } } else { F = 1; } WaterStress = Math.Min(1, Math.Max(0, PotSWSupply / SWDemand)); List <double> uptakeList = new List <double>(); foreach (ZoneWaterAndN Z in Uptakes) { Z.Water = MathUtilities.Multiply_Value(Z.Water, F); uptakeList.Add(Z.TotalWater); } WaterUptake = uptakeList.ToArray(); return(Uptakes); }
/// <summary>Gets the nitrogen supply from the specified zone.</summary> /// <param name="zone">The zone.</param> /// <param name="NO3Supply">The returned NO3 supply</param> /// <param name="NH4Supply">The returned NH4 supply</param> public void CalcNSupply(ZoneWaterAndN zone, out double[] NO3Supply, out double[] NH4Supply) { NO3Supply = null; NH4Supply = null; }