/// <summary>Implements the operator -.</summary> /// <param name="state">The soil state.</param> /// <param name="estimate">The estimate to subtract from the soil state.</param> /// <returns>The result of the operator.</returns> public static SoilState operator -(SoilState state, Estimate estimate) { SoilState NewState = new SoilState(state.Parent); foreach (ZoneWaterAndN Z in state.Zones) { ZoneWaterAndN NewZ = new ZoneWaterAndN(); NewZ.Name = Z.Name; NewZ.Water = Z.Water; NewZ.NO3N = Z.NO3N; NewZ.NH4N = Z.NH4N; NewState.Zones.Add(NewZ); } foreach (CropUptakes C in estimate.Values) { foreach (ZoneWaterAndN Z in C.Zones) { foreach (ZoneWaterAndN NewZ in NewState.Zones) { if (Z.Name == NewZ.Name) { NewZ.Water = MathUtilities.Subtract(NewZ.Water, Z.Water); NewZ.NO3N = MathUtilities.Subtract(NewZ.NO3N, Z.NO3N); NewZ.NH4N = MathUtilities.Subtract(NewZ.NH4N, Z.NH4N); } } } } return(NewState); }
/// <summary>Implements the operator *.</summary> /// <param name="zone">The zone</param> /// <param name="value">The value.</param> /// <returns>The result of the operator.</returns> public static ZoneWaterAndN operator *(ZoneWaterAndN zone, double value) { ZoneWaterAndN NewZ = new ZoneWaterAndN(zone.Zone); NewZ.Water = MathUtilities.Multiply_Value(zone.Water, value); NewZ.NO3N = MathUtilities.Multiply_Value(zone.NO3N, value); NewZ.NH4N = MathUtilities.Multiply_Value(zone.NH4N, value); return(NewZ); }
/// <summary>Implements the operator *.</summary> /// <param name="zone">The zone</param> /// <param name="value">The value.</param> /// <returns>The result of the operator.</returns> public static ZoneWaterAndN operator *(ZoneWaterAndN zone, double value) { ZoneWaterAndN NewZ = new ZoneWaterAndN(); NewZ.Name = zone.Name; NewZ.Water = MathUtilities.Multiply_Value(zone.Water, value); NewZ.NO3N = MathUtilities.Multiply_Value(zone.NO3N, value); NewZ.NH4N = MathUtilities.Multiply_Value(zone.NH4N, value); return NewZ; }
/// <summary> /// Constructor. Copy state from another instance. /// </summary> /// <param name="from">The instance to copy from.</param> public ZoneWaterAndN(ZoneWaterAndN from) { NO3Solute = from.NO3Solute; NH4Solute = from.NH4Solute; soilInZone = from.soilInZone; Zone = from.Zone; Water = from.Water; NO3N = from.NO3N; NH4N = from.NH4N; }
/// <summary>Implements the operator +.</summary> /// <param name="zone1">Zone 1</param> /// <param name="zone2">Zone 2</param> /// <returns>The result of the operator.</returns> /// <exception cref="System.Exception">Cannot add zones with different names</exception> public static ZoneWaterAndN operator +(ZoneWaterAndN zone1, ZoneWaterAndN zone2) { if (zone1.Name != zone2.Name) throw new Exception("Cannot add zones with different names"); ZoneWaterAndN NewZ = new ZoneWaterAndN(); NewZ.Name = zone1.Name; NewZ.Water = MathUtilities.Add(zone1.Water, zone2.Water); NewZ.NO3N = MathUtilities.Add(zone1.NO3N, zone2.NO3N); NewZ.NH4N = MathUtilities.Add(zone1.NH4N, zone2.NH4N); return NewZ; }
/// <summary>Initialises this instance.</summary> public void Initialise() { foreach (Zone Z in Apsim.Children(this.Parent, typeof(Zone))) { ZoneWaterAndN NewZ = new ZoneWaterAndN(); NewZ.Name = Z.Name; Soil soil = Apsim.Child(Z, typeof(Soil)) as Soil; NewZ.Water = soil.Water; NewZ.NO3N = soil.NO3N; NewZ.NH4N = soil.NH4N; Zones.Add(NewZ); } }
/// <summary>Implements the operator -.</summary> /// <param name="ZWN1">Zone 1</param> /// <param name="ZWN2">Zone 2</param> /// <returns>The result of the operator.</returns> /// <exception cref="System.Exception">Cannot subtract zones with different names</exception> public static ZoneWaterAndN operator -(ZoneWaterAndN ZWN1, ZoneWaterAndN ZWN2) { if (ZWN1.Zone.Name != ZWN2.Zone.Name) { throw new Exception("Cannot subtract zones with different names"); } ZoneWaterAndN NewZ = new ZoneWaterAndN(ZWN1.Zone); NewZ.Water = MathUtilities.Subtract(ZWN1.Water, ZWN2.Water); NewZ.NO3N = MathUtilities.Subtract(ZWN1.NO3N, ZWN2.NO3N); NewZ.NH4N = MathUtilities.Subtract(ZWN1.NH4N, ZWN2.NH4N); return(NewZ); }
/// <summary>Implements the operator -.</summary> /// <param name="zone1">Zone 1</param> /// <param name="zone2">Zone 2</param> /// <returns>The result of the operator.</returns> /// <exception cref="System.Exception">Cannot subtract zones with different names</exception> public static ZoneWaterAndN operator -(ZoneWaterAndN zone1, ZoneWaterAndN zone2) { if (zone1.Name != zone2.Name) { throw new Exception("Cannot subtract zones with different names"); } ZoneWaterAndN NewZ = new ZoneWaterAndN(); NewZ.Name = zone1.Name; NewZ.Water = MathUtilities.Subtract(zone1.Water, zone2.Water); NewZ.NO3N = MathUtilities.Subtract(zone1.NO3N, zone2.NO3N); NewZ.NH4N = MathUtilities.Subtract(zone1.NH4N, zone2.NH4N); return(NewZ); }
/// <summary>Implements the operator +.</summary> /// <param name="ZWN1">Zone 1</param> /// <param name="ZWN2">Zone 2</param> /// <returns>The result of the operator.</returns> /// <exception cref="System.Exception">Cannot add zones with different names</exception> public static ZoneWaterAndN operator +(ZoneWaterAndN ZWN1, ZoneWaterAndN ZWN2) { if (ZWN1.Zone.Name != ZWN2.Zone.Name) { throw new Exception("Cannot add zones with different names"); } ZoneWaterAndN NewZ = new ZoneWaterAndN(ZWN1); NewZ.Water = MathUtilities.Add(ZWN1.Water, ZWN2.Water); NewZ.NO3N = MathUtilities.Add(ZWN1.NO3N, ZWN2.NO3N); NewZ.NH4N = MathUtilities.Add(ZWN1.NH4N, ZWN2.NH4N); NewZ.PlantAvailableNO3N = MathUtilities.Add(ZWN1.PlantAvailableNO3N, ZWN2.PlantAvailableNO3N); NewZ.PlantAvailableNH4N = MathUtilities.Add(ZWN1.PlantAvailableNH4N, ZWN2.PlantAvailableNH4N); return(NewZ); }
/// <summary>Initialises this instance.</summary> public void Initialise() { foreach (Zone Z in Apsim.ChildrenRecursively(this.Parent, typeof(Zone))) { Soil soil = Apsim.Child(Z, typeof(Soil)) as Soil; if (soil != null) { ZoneWaterAndN NewZ = new ZoneWaterAndN(Z); NewZ.Water = soil.Water; NewZ.NO3N = soil.NO3N; NewZ.NH4N = soil.NH4N; Zones.Add(NewZ); } } }
/// <summary>Initialises this instance.</summary> public void Initialise(List <IModel> zones) { foreach (Zone Z in zones) { Soil soil = Apsim.Child(Z, typeof(Soil)) as Soil; if (soil != null) { ZoneWaterAndN NewZ = new ZoneWaterAndN(Z); NewZ.Water = soil.Water; NewZ.NO3N = soil.NO3N; NewZ.NH4N = soil.NH4N; Zones.Add(NewZ); } } }
/// <summary>Implements the operator *.</summary> /// <param name="E">The estimate</param> /// <param name="value">The value to multiply the estimate by.</param> /// <returns>The resulting estimate</returns> public static Estimate operator *(Estimate E, double value) { Estimate NewE = new Estimate(E.Parent); foreach (CropUptakes U in E.Values) { CropUptakes NewU = new CropUptakes(); NewE.Values.Add(NewU); foreach (ZoneWaterAndN Z in U.Zones) { ZoneWaterAndN NewZ = Z * value; NewU.Zones.Add(NewZ); } } return(NewE); }
/// <summary> /// General soil arbitration method (water or nutrients) based upon Runge-Kutta method /// </summary> /// <param name="arbitrationType">Water or Nitrogen</param> private void DoArbitration(Estimate.CalcType arbitrationType) { SoilState InitialSoilState = new SoilState(this.Parent); InitialSoilState.Initialise(zones); Estimate UptakeEstimate1 = new Estimate(this.Parent, arbitrationType, InitialSoilState, uptakeModels); Estimate UptakeEstimate2 = new Estimate(this.Parent, arbitrationType, InitialSoilState - UptakeEstimate1 * 0.5, uptakeModels); Estimate UptakeEstimate3 = new Estimate(this.Parent, arbitrationType, InitialSoilState - UptakeEstimate2 * 0.5, uptakeModels); Estimate UptakeEstimate4 = new Estimate(this.Parent, arbitrationType, InitialSoilState - UptakeEstimate3, uptakeModels); List <ZoneWaterAndN> listOfZoneUptakes = new List <ZoneWaterAndN>(); List <CropUptakes> ActualUptakes = new List <CropUptakes>(); foreach (CropUptakes U in UptakeEstimate1.Values) { CropUptakes CU = new CropUptakes(); CU.Crop = U.Crop; foreach (ZoneWaterAndN ZU in U.Zones) { ZoneWaterAndN NewZone = UptakeEstimate1.UptakeZone(CU.Crop, ZU.Zone.Name) * (1.0 / 6.0) + UptakeEstimate2.UptakeZone(CU.Crop, ZU.Zone.Name) * (1.0 / 3.0) + UptakeEstimate3.UptakeZone(CU.Crop, ZU.Zone.Name) * (1.0 / 3.0) + UptakeEstimate4.UptakeZone(CU.Crop, ZU.Zone.Name) * (1.0 / 6.0); CU.Zones.Add(NewZone); listOfZoneUptakes.Add(NewZone); } ActualUptakes.Add(CU); } ScaleWaterAndNIfNecessary(InitialSoilState.Zones, listOfZoneUptakes); foreach (CropUptakes Uptake in ActualUptakes) { if (arbitrationType == Estimate.CalcType.Water) { Uptake.Crop.SetActualWaterUptake(Uptake.Zones); } else { Uptake.Crop.SetActualNitrogenUptakes(Uptake.Zones); } } }
/// <summary> /// General soil arbitration method (water or nutrients) based upon Runge-Kutta method /// </summary> /// <param name="arbitrationType">Water or Nitrogen</param> private void DoArbitration(Estimate.CalcType arbitrationType) { SoilState InitialSoilState = new SoilState(this.Parent); InitialSoilState.Initialise(zones); Estimate UptakeEstimate1 = new Estimate(this.Parent, arbitrationType, InitialSoilState, uptakeModels); Estimate UptakeEstimate2 = new Estimate(this.Parent, arbitrationType, InitialSoilState - UptakeEstimate1 * 0.5, uptakeModels); Estimate UptakeEstimate3 = new Estimate(this.Parent, arbitrationType, InitialSoilState - UptakeEstimate2 * 0.5, uptakeModels); Estimate UptakeEstimate4 = new Estimate(this.Parent, arbitrationType, InitialSoilState - UptakeEstimate3, uptakeModels); List <ZoneWaterAndN> listOfZoneWaterAndNs = new List <ZoneWaterAndN>(); List <CropUptakes> UptakesFinal = new List <CropUptakes>(); foreach (CropUptakes U in UptakeEstimate1.Values) { CropUptakes CWU = new CropUptakes(); CWU.Crop = U.Crop; foreach (ZoneWaterAndN ZW1 in U.Zones) { ZoneWaterAndN NewZ = UptakeEstimate1.UptakeZone(CWU.Crop, ZW1.Zone.Name) * (1.0 / 6.0) + UptakeEstimate2.UptakeZone(CWU.Crop, ZW1.Zone.Name) * (1.0 / 3.0) + UptakeEstimate3.UptakeZone(CWU.Crop, ZW1.Zone.Name) * (1.0 / 3.0) + UptakeEstimate4.UptakeZone(CWU.Crop, ZW1.Zone.Name) * (1.0 / 6.0); CWU.Zones.Add(NewZ); listOfZoneWaterAndNs.Add(NewZ); } UptakesFinal.Add(CWU); } ScaleWaterAndNIfNecessary(InitialSoilState.Zones, listOfZoneWaterAndNs); foreach (CropUptakes Uptake in UptakesFinal) { if (arbitrationType == Estimate.CalcType.Water) { Uptake.Crop.SetSWUptake(Uptake.Zones); } else { Uptake.Crop.SetNUptake(Uptake.Zones); } } }
/// <summary>Implements the operator -.</summary> /// <param name="state">The soil state.</param> /// <param name="estimate">The estimate to subtract from the soil state.</param> /// <returns>The result of the operator.</returns> public static SoilState operator -(SoilState state, Estimate estimate) { SoilState NewState = new SoilState(state.Parent); foreach (ZoneWaterAndN Z in state.Zones) { ZoneWaterAndN NewZ = new ZoneWaterAndN(); NewZ.Name = Z.Name; NewZ.Water = Z.Water; NewZ.NO3N = Z.NO3N; NewZ.NH4N = Z.NH4N; NewState.Zones.Add(NewZ); } foreach (CropUptakes C in estimate.Values) foreach (ZoneWaterAndN Z in C.Zones) foreach (ZoneWaterAndN NewZ in NewState.Zones) if (Z.Name == NewZ.Name) { NewZ.Water = MathUtilities.Subtract(NewZ.Water, Z.Water); NewZ.NO3N = MathUtilities.Subtract(NewZ.NO3N, Z.NO3N); NewZ.NH4N = MathUtilities.Subtract(NewZ.NH4N, Z.NH4N); } return NewState; }
/// <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">Soil information</param> private void PlantAvailableSoilNAlternativeWup(ZoneWaterAndN myZone) { double layerFrac; // the fraction of layer within the root zone double swuFac; // the soil water factor double potAvailableN; // potential available N for (int layer = 0; layer <= roots.BottomLayer; layer++) { layerFrac = FractionLayerWithRoots(layer); swuFac = MathUtilities.Divide(mySoilWaterUptake[layer], myZone.Water[layer], 0.0); // get NH4 available potAvailableN = myZone.NH4N[layer] * layerFrac; mySoilNH4Available[layer] = potAvailableN * Math.Min(1.0, swuFac * kuNH4); // get NO3 available potAvailableN = myZone.NO3N[layer] * layerFrac; mySoilNO3Available[layer] = potAvailableN * Math.Min(1.0, swuFac * kuNO3); } // check for maximum uptake potAvailableN = mySoilNH4Available.Sum() + mySoilNO3Available.Sum(); if (potAvailableN > MaximumNUptake) { double upFraction = MaximumNUptake / potAvailableN; for (int layer = 0; layer <= roots.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 is a basic method, used as default in old AgPasture, all N in the root zone is available</remarks> /// <param name="myZone">Soil information</param> private void PlantAvailableSoilNBasicAgPasture(ZoneWaterAndN myZone) { double layerFrac; // the fraction of layer within the root zone for (int layer = 0; layer <= roots.BottomLayer; layer++) { layerFrac = FractionLayerWithRoots(layer); mySoilNH4Available[layer] = myZone.NH4N[layer] * layerFrac; mySoilNO3Available[layer] = myZone.NO3N[layer] * layerFrac; } }
/// <summary>Performs the nitrogen uptake calculations.</summary> private void DoNitrogenCalculations() { if (myNUptakeSource.ToLower() == "sward") { // Pack the soil information ZoneWaterAndN myZone = new ZoneWaterAndN(); myZone.Name = this.Parent.Name; myZone.Water = mySoil.Water; myZone.NO3N = mySoil.NO3N; myZone.NH4N = mySoil.NH4N; // Get the N amount available in the soil GetSoilAvailableN(myZone); foreach (PastureSpecies species in mySpecies) { // Get the N amount fixed through symbiosis species.EvaluateNitrogenFixation(); // Evaluate the use of N remobilised and get N amount demanded from soil species.EvaluateSoilNitrogenDemand(); // Get N amount taken up from the soil species.EvaluateSoilNitrogenUptake(); for (int layer = 0; layer < RootFrontier; layer++) { swardSoilNH4Uptake[layer] += species.SoilNH4Uptake[layer]; swardSoilNO3Uptake[layer] += species.SoilNO3Uptake[layer]; } // Evaluate whether remobilisation of luxury N is needed species.EvaluateNLuxuryRemobilisation(); } // Send delta N to the soil model DoSoilNitrogenUptake(); } else { //N uptake is controlled at species level, get sward totals for (int layer = 0; layer < RootFrontier; layer++) { foreach (PastureSpecies species in mySpecies) { swardSoilNH4Available[layer] += species.SoilNH4Available[layer]; swardSoilNO3Available[layer] += species.SoilNO3Available[layer]; swardSoilNH4Uptake[layer] += species.SoilNH4Uptake[layer]; swardSoilNO3Uptake[layer] += species.SoilNO3Uptake[layer]; } } // Send delta N to the soil model DoSoilNitrogenUptake(); } }
/// <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 stauts 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">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 <= roots.BottomLayer; layer++) { layerFrac = FractionLayerWithRoots(layer); rldFac = Math.Min(1.0, MathUtilities.Divide(RLD[layer], ReferenceRLD, 1.0)); if (myZone.Water[layer] >= mySoil.SoilWater.DULmm[layer]) swFac = 1.0; else if (myZone.Water[layer] <= mySoil.SoilWater.LL15mm[layer]) swFac = 0.0; else { double waterRatio = (myZone.Water[layer] - mySoil.SoilWater.LL15mm[layer]) / (mySoil.SoilWater.DULmm[layer] - mySoil.SoilWater.LL15mm[layer]); swFac = 1.0 - Math.Pow(1.0 - waterRatio, ExponentSoilMoisture); } // get NH4 available potAvailableN = myZone.NH4N[layer] * layerFrac; mySoilNH4Available[layer] = potAvailableN * Math.Min(1.0, swFac * rldFac * kuNH4); // get NO3 available potAvailableN = myZone.NO3N[layer] * layerFrac; mySoilNO3Available[layer] = potAvailableN * Math.Min(1.0, swFac * rldFac * kuNO3); } // check for maximum uptake potAvailableN = mySoilNH4Available.Sum() + mySoilNO3Available.Sum(); if (potAvailableN > MaximumNUptake) { double upFraction = MaximumNUptake / potAvailableN; for (int layer = 0; layer <= roots.BottomLayer; layer++) { mySoilNH4Available[layer] *= upFraction; mySoilNO3Available[layer] *= upFraction; } } }
/// <summary> /// Calculate the potential sw uptake for today. Should return null if crop is not in the ground. /// </summary> public List<Soils.Arbitrator.ZoneWaterAndN> GetNUptakes(SoilState soilstate) { if (Plant.IsAlive) { foreach (ZoneWaterAndN zone in soilstate.Zones) { ZoneWaterAndN UptakeDemands = new ZoneWaterAndN(); if (Plant.Phenology != null) { if (Plant.Phenology.Emerged == true) { DoPotentialNutrientUptake(ref N, zone); //Work out how much N the uptaking organs (roots) would take up in the absence of competition //Pack results into uptake structure UptakeDemands.NO3N = PotentialNO3NUptake; UptakeDemands.NH4N = PotentialNH4NUptake; } else //Uptakes are zero { UptakeDemands.NO3N = new double[zone.NO3N.Length]; for (int i = 0; i < UptakeDemands.NO3N.Length; i++) { UptakeDemands.NO3N[i] = 0; } UptakeDemands.NH4N = new double[zone.NH4N.Length]; for (int i = 0; i < UptakeDemands.NH4N.Length; i++) { UptakeDemands.NH4N[i] = 0; } } } else { DoPotentialNutrientUptake(ref N, zone); //Work out how much N the uptaking organs (roots) would take up in the absence of competition //Pack results into uptake structure UptakeDemands.NO3N = PotentialNO3NUptake; UptakeDemands.NH4N = PotentialNH4NUptake; } UptakeDemands.Name = zone.Name; UptakeDemands.Water = new double[UptakeDemands.NO3N.Length]; List<ZoneWaterAndN> zones = new List<ZoneWaterAndN>(); zones.Add(UptakeDemands); return zones; } } return null; }
/// <summary>Does the uptake from the specified zone.</summary> /// <param name="BAT">The bat.</param> /// <param name="MyZone">The zone.</param> public virtual void DoPotentialNutrientUptake(ref BiomassArbitrationType BAT, ZoneWaterAndN MyZone) { PotentialNO3NUptake = new double[MyZone.NO3N.Length]; PotentialNH4NUptake = new double[MyZone.NH4N.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.Length; i++) { double[] organNO3Supply; double[] organNH4Supply; Organs[i].CalcNSupply(MyZone, out organNO3Supply, out organNH4Supply); if (organNO3Supply != null) { PotentialNO3NUptake = MathUtilities.Add(PotentialNO3NUptake, organNO3Supply); //Add uptake supply from each organ to the plants total to tell the Soil arbitrator BAT.UptakeSupply[i] = MathUtilities.Sum(organNO3Supply) * kgha2gsm; //Populate uptakeSupply for each organ for internal allocation routines } if (organNH4Supply != null) { PotentialNH4NUptake = MathUtilities.Add(PotentialNH4NUptake, organNH4Supply); BAT.UptakeSupply[i] += MathUtilities.Sum(organNH4Supply) * kgha2gsm; } } //Calculate plant level supply totals. BAT.TotalUptakeSupply = MathUtilities.Sum(BAT.UptakeSupply); BAT.TotalPlantSupply = BAT.TotalReallocationSupply + BAT.TotalUptakeSupply + BAT.TotalFixationSupply + BAT.TotalRetranslocationSupply; //If NUsupply is greater than uptake (total demand - reallocatio nsupply) reduce the PotentialUptakes that we pass to the soil arbitrator if (BAT.TotalUptakeSupply > (BAT.TotalPlantDemand - BAT.TotalReallocation)) { double ratio = Math.Min(1.0, (BAT.TotalPlantDemand - BAT.TotalReallocation) / BAT.TotalUptakeSupply); PotentialNO3NUptake = MathUtilities.Multiply_Value(PotentialNO3NUptake, ratio); PotentialNH4NUptake = MathUtilities.Multiply_Value(PotentialNH4NUptake, ratio); } }
/// <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">Soil information</param> /// <returns>Amount of available water (mm)</returns> private double[] PlantAvailableSoilWaterAlternativeKS(ZoneWaterAndN myZone) { double[] result = new double[nLayers]; double condFac = 0.0; double rldFac = 0.0; double swFac = 0.0; SoilCrop soilCropData = (SoilCrop)mySoil.Crop(Name); for (int layer = 0; layer <= roots.BottomLayer; layer++) { condFac = 1.0 - Math.Pow(10, -mySoil.KS[layer] / ReferenceKSuptake); rldFac = 1.0 - Math.Pow(10, -RLD[layer] / ReferenceRLD); if (mySoil.SoilWater.SWmm[layer] >= mySoil.SoilWater.DULmm[layer]) swFac = 1.0; else if (mySoil.SoilWater.SWmm[layer] <= mySoil.SoilWater.LL15mm[layer]) swFac = 0.0; else { double waterRatio = (myZone.Water[layer] - mySoil.SoilWater.LL15mm[layer]) / (mySoil.SoilWater.DULmm[layer] - mySoil.SoilWater.LL15mm[layer]); swFac = 1.0 - Math.Pow(1.0 - waterRatio, ExponentSoilMoisture); } // Theoretical total available water result[layer] = Math.Max(0.0, myZone.Water[layer] - soilCropData.LL[layer]) * mySoil.Thickness[layer]; // Actual available water result[layer] *= FractionLayerWithRoots(layer) * rldFac * condFac * swFac; } return result; }
/// <summary>Placeholder for SoilArbitrator</summary> /// <param name="soilstate">soil state</param> /// <returns></returns> public List<ZoneWaterAndN> GetNUptakes(SoilState soilstate) { if (IsAlive) { List<ZoneWaterAndN> Uptakes = new List<ZoneWaterAndN>(); ZoneWaterAndN Uptake = new ZoneWaterAndN(); ZoneWaterAndN MyZone = new ZoneWaterAndN(); foreach (ZoneWaterAndN Z in soilstate.Zones) if (Z.Name == this.Parent.Name) MyZone = Z; double[] NO3N = MyZone.NO3N; double[] NH4N = MyZone.NH4N; double[] NO3NUp = new double[NO3N.Length]; double[] NH4NUp = new double[NH4N.Length]; Root.CalculateNUptake(NO3N, NH4N, ref NO3NUp, ref NH4NUp); Uptake.NO3N = NO3NUp; Uptake.NH4N = NH4NUp; Uptake.Water = new double[NO3N.Length]; Uptake.Name = this.Parent.Name; Uptakes.Add(Uptake); return Uptakes; } else return null; }
/// <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>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">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 <= roots.BottomLayer; layer++) { layerFrac = FractionLayerWithRoots(layer); bdFac = 100.0 / (mySoil.Thickness[layer] * mySoil.BD[layer]); if (myZone.Water[layer] >= mySoil.SoilWater.DULmm[layer]) swFac = 1.0; else if (myZone.Water[layer] <= mySoil.SoilWater.LL15mm[layer]) swFac = 0.0; else { double waterRatio = (myZone.Water[layer] - mySoil.SoilWater.LL15mm[layer]) / (mySoil.SoilWater.DULmm[layer] - mySoil.SoilWater.LL15mm[layer]); swFac = 1.0 - Math.Pow(1.0 - waterRatio, ExponentSoilMoisture); } // get NH4 available potAvailableN = Math.Pow(myZone.NH4N[layer] * layerFrac, 2.0) * swFac * bdFac * KNH4; mySoilNH4Available[layer] = Math.Min(myZone.NH4N[layer] * layerFrac, potAvailableN); // get NO3 available potAvailableN = Math.Pow(myZone.NO3N[layer] * layerFrac, 2.0) * swFac * bdFac * KNO3; mySoilNO3Available[layer] = Math.Min(myZone.NO3N[layer] * layerFrac, potAvailableN); } // check for maximum uptake potAvailableN = mySoilNH4Available.Sum() + mySoilNO3Available.Sum(); if (potAvailableN > MaximumNUptake) { double upFraction = MaximumNUptake / potAvailableN; for (int layer = 0; layer <= roots.BottomLayer; layer++) { mySoilNH4Available[layer] *= upFraction; mySoilNO3Available[layer] *= upFraction; } } }
/// <summary>Placeholder for SoilArbitrator</summary> /// <param name="soilstate">soil state</param> /// <returns></returns> public List<ZoneWaterAndN> GetNUptakes(SoilState soilstate) { ZoneWaterAndN MyZone = new ZoneWaterAndN(); foreach (ZoneWaterAndN Z in soilstate.Zones) if (Z.Name == this.Parent.Name) MyZone = Z; double[] PotNO3Uptake = new double[Soil.NO3N.Length]; double[] PotNH4Uptake = new double[Soil.NH4N.Length]; NO3Uptake = new double[Soil.NO3N.Length]; NH4Uptake = new double[Soil.NH4N.Length]; SoilCrop soilCrop = Soil.Crop(this.Name) as SoilCrop; for (int j = 0; j < Soil.SoilWater.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 < Soil.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(); Uptake.Name = this.Parent.Name; Uptake.NO3N = NO3Uptake; Uptake.NH4N = NH4Uptake; Uptake.Water = new double[NO3Uptake.Length]; Uptakes.Add(Uptake); return Uptakes; }
/// <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> GetSWUptakes(SoilState soilstate) { ZoneWaterAndN MyZone = new ZoneWaterAndN(); foreach (ZoneWaterAndN Z in soilstate.Zones) if (Z.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.SoilWater.LL15mm.Length; j++) PotSWUptake[j] = Math.Max(0.0, RootProportion(j, RootDepth) * soilCrop.KL[j] * (MyZone.Water[j] - Soil.SoilWater.LL15mm[j])); double TotPotSWUptake = MathUtilities.Sum(PotSWUptake); for (int j = 0; j < Soil.SoilWater.LL15mm.Length; j++) SWUptake[j] = PotSWUptake[j] * Math.Min(1.0, PotentialEP / TotPotSWUptake); List<ZoneWaterAndN> Uptakes = new List<ZoneWaterAndN>(); ZoneWaterAndN Uptake = new ZoneWaterAndN(); Uptake.Name = this.Parent.Name; Uptake.Water = SWUptake; Uptake.NO3N = new double[SWUptake.Length]; Uptake.NH4N = new double[SWUptake.Length]; Uptakes.Add(Uptake); return Uptakes; }
/// <summary>Performs the water uptake calculations.</summary> private void DoWaterCalculations() { if (myWaterUptakeSource == "sward") { // Pack the soil information ZoneWaterAndN myZone = new ZoneWaterAndN(); myZone.Name = this.Parent.Name; myZone.Water = mySoil.Water; myZone.NO3N = mySoil.NO3N; myZone.NH4N = mySoil.NH4N; // Get the amount of soil available water GetSoilAvailableWater(myZone); // Water demand computed by MicroClimate // Get the amount of water taken up GetSoilWaterUptake(); // Send the delta water to soil water module DoSoilWaterUptake(); } else { //water uptake is controlled at species level, get sward totals for (int layer = 0; layer <= RootFrontier; layer++) { foreach (PastureSpecies species in mySpecies) { swardSoilWaterAvailable[layer] += species.SoilAvailableWater[layer]; swardSoilWaterUptake[layer] += species.WaterUptake[layer]; } } // Send the delta water to soil water module DoSoilWaterUptake(); } }
/// <summary>Finds out the amount of plant available water in the soil, consider all species.</summary> /// <param name="myZone">Soil information</param> private void GetSoilAvailableWater(ZoneWaterAndN myZone) { double totalPlantWater; double totalSoilWater; double maxPlantWater = 0.0; double waterFraction = 1.0; double layerFraction = 1.0; // Get the water available as seen by each species foreach (PastureSpecies species in mySpecies) species.EvaluateSoilWaterAvailable(myZone); // Evaluate the available water for whole sward and adjust availability for each species if needed for (int layer = 0; layer <= RootFrontier; layer++) { // Get total as seen by each species totalPlantWater = 0.0; if (layer == RootFrontier) layerFraction = 0.0; foreach (PastureSpecies species in mySpecies) { totalPlantWater += species.SoilAvailableWater[layer]; maxPlantWater = Math.Max(maxPlantWater, species.SoilAvailableWater[layer]); if (layer == RootFrontier) layerFraction = Math.Max(layerFraction, species.FractionLayerWithRoots(layer)); } // Get total in the soil totalSoilWater = Math.Max(0.0, myZone.Water[layer] - mySoil.SoilWater.LL15mm[layer]) * layerFraction; totalSoilWater = Math.Max(totalSoilWater, maxPlantWater); //allows for a plant to uptake below LL15 // Sward total is the minimum of the two totals swardSoilWaterAvailable[layer] = Math.Min(totalPlantWater, totalSoilWater); if (totalPlantWater > totalSoilWater) { // adjust the water available for each species waterFraction = MathUtilities.Divide(totalSoilWater, totalPlantWater, 0.0); foreach (PastureSpecies species in mySpecies) species.UpdateAvailableWater(waterFraction); } } }
/// <summary>Finds out the amount of plant available nitrogen (NH4 and NO3) in the soil, consider all species.</summary> /// <param name="myZone">Soil information</param> private void GetSoilAvailableN(ZoneWaterAndN myZone) { double totalSoilNH4; double totalSoilNO3; double totalPlantNH4; double totalPlantNO3; double nh4Fraction = 1.0; double no3Fraction = 1.0; double layerFraction = 1.0; // Get the N available as seen by each species foreach (PastureSpecies species in mySpecies) species.EvaluateSoilNitrogenAvailable(myZone); // Evaluate the available N for whole sward and adjust availability for each species if needed for (int layer = 0; layer <= RootFrontier; layer++) { // Get total as seen by each species totalPlantNH4 = 0.0; totalPlantNO3 = 0.0; if (layer == RootFrontier) layerFraction = 0.0; foreach (PastureSpecies species in mySpecies) { totalPlantNH4 += species.SoilNH4Available[layer]; totalPlantNO3 += species.SoilNO3Available[layer]; if (layer == RootFrontier) layerFraction = Math.Max(layerFraction, species.FractionLayerWithRoots(layer)); } // Get total in the soil totalSoilNH4 = myZone.NH4N[layer] * layerFraction; totalSoilNO3 = myZone.NO3N[layer] * layerFraction; // Sward total is the minimum of the two totals swardSoilNH4Available[layer] = Math.Min(totalPlantNH4, totalSoilNH4); swardSoilNO3Available[layer] = Math.Min(totalPlantNO3, totalSoilNO3); if ((totalPlantNH4 > totalSoilNH4) || (totalPlantNO3 > totalSoilNO3)) { // adjust the N available for each species nh4Fraction = Math.Min(1.0, MathUtilities.Divide(totalSoilNH4, totalPlantNH4, 0.0)); no3Fraction = Math.Min(1.0, MathUtilities.Divide(totalSoilNO3, totalPlantNO3, 0.0)); foreach (PastureSpecies species in mySpecies) { species.UpdateAvailableNitrogen(nh4Fraction, no3Fraction); } } } }
/// <summary>Estimates the amount of plant available water in each soil layer of the root zone.</summary> /// <remarks> /// This is an alternative method, kl representing a soil limiting factor for water extraction (clayey soils have lower values) /// this is further modiied by soil water content (a reduction for dry soil). A plant related factor is defined based on root /// length density (limiting conditions when RLD is below ReferenceRLD) /// </remarks> /// <param name="myZone">Soil information</param> /// <returns>Amount of available water (mm)</returns> private double[] PlantAvailableSoilWaterAlternativeKL(ZoneWaterAndN myZone) { double[] result = new double[nLayers]; SoilCrop soilCropData = (SoilCrop)mySoil.Crop(Name); double rldFac; double swFac; for (int layer = 0; layer <= roots.BottomLayer; layer++) { rldFac = Math.Min(1.0, RLD[layer] / ReferenceRLD); if (mySoil.SoilWater.SWmm[layer] >= mySoil.SoilWater.DULmm[layer]) swFac = 1.0; else if (mySoil.SoilWater.SWmm[layer] <= mySoil.SoilWater.LL15mm[layer]) swFac = 0.0; else { double waterRatio = (myZone.Water[layer] - mySoil.SoilWater.LL15mm[layer]) / (mySoil.SoilWater.DULmm[layer] - mySoil.SoilWater.LL15mm[layer]); swFac = 1.0 - Math.Pow(1.0 - waterRatio, ExponentSoilMoisture); } result[layer] = Math.Max(0.0, myZone.Water[layer] - (soilCropData.LL[layer] * mySoil.Thickness[layer])); result[layer] *= FractionLayerWithRoots(layer) * Math.Min(1.0, soilCropData.KL[layer] * swFac * rldFac); } return result; }
/// <summary>Sets the amount of water taken up by this plant (mm).</summary> /// <remarks>The model can only handle one root zone at present.</remarks> /// <param name="zones">Water uptake from each layer (mm), by zone</param> public void SetSWUptake(List<ZoneWaterAndN> zones) { // Get the zone this plant is in ZoneWaterAndN MyZone = new ZoneWaterAndN(); Zone parentZone = Apsim.Parent(this, typeof (Zone)) as Zone; foreach (ZoneWaterAndN Z in zones) if (Z.Name == parentZone.Name) MyZone = Z; // Get the water uptake from each layer for (int layer = 0; layer < nLayers; layer++) mySoilWaterUptake[layer] = MyZone.Water[layer]; }
/// <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">Soil information</param> /// <returns>Amount of available water (mm)</returns> private double[] PlantAvailableSoilWaterDefault(ZoneWaterAndN myZone) { double[] result = new double[nLayers]; SoilCrop soilCropData = (SoilCrop)mySoil.Crop(Name); for (int layer = 0; layer <= roots.BottomLayer; layer++) { result[layer] = Math.Max(0.0, myZone.Water[layer] - (soilCropData.LL[layer] * mySoil.Thickness[layer])); result[layer] *= FractionLayerWithRoots(layer) * soilCropData.KL[layer]; } return result; }
/// <summary>Performs the nitrogen uptake calculations.</summary> internal void DoNitrogenCalculations() { if (MyNitrogenUptakeSource == "species") { // this module will compute nitrogen uptake // Pack the soil information ZoneWaterAndN myZone = new ZoneWaterAndN(); myZone.Name = this.Parent.Name; myZone.Water = mySoil.Water; myZone.NO3N = mySoil.NO3N; myZone.NH4N = mySoil.NH4N; // Get the N amount available in the soil EvaluateSoilNitrogenAvailable(myZone); // Get the N amount fixed through symbiosis EvaluateNitrogenFixation(); // Evaluate the use of N remobilised and get N amount demanded from soil EvaluateSoilNitrogenDemand(); // Get N amount take up from the soil EvaluateSoilNitrogenUptake(); // Evaluate whether remobilisation of luxury N is needed EvaluateNLuxuryRemobilisation(); // Send delta N to the soil model DoSoilNitrogenUptake(); } else if (MyNitrogenUptakeSource == "SoilArbitrator") { // Nitrogen uptake was computed by the resource arbitrator // Evaluate whether remobilisation of luxury N is needed EvaluateNLuxuryRemobilisation(); // Send delta N to the soil model DoSoilNitrogenUptake(); } else if (MyNitrogenUptakeSource == "Arbitrator") { // Nitrogen uptake was computed by the resource arbitrator // gather the uptake values if (MyNitrogenUptakeSource == "Arbitrator") { for (int layer = 0; layer <= roots.BottomLayer; layer++) { mySoilNH4Uptake[layer] = uptakeNitrogen[layer] * (1.0 - uptakeNitrogenPropNO3[layer]); mySoilNO3Uptake[layer] = uptakeNitrogen[layer] * uptakeNitrogenPropNO3[layer]; } } // Evaluate whether remobilisation of luxury N is needed EvaluateNLuxuryRemobilisation(); } else { // N uptake is computed by another module (e.g. SWIM) and supplied by OnNitrogenUptakesCalculated throw new NotImplementedException(); } }
/// <summary>Placeholder for SoilArbitrator</summary> /// <param name="soilstate">Soil state</param> /// <returns></returns> public List<ZoneWaterAndN> GetSWUptakes(SoilState soilstate) { if (IsAlive) { List<ZoneWaterAndN> Uptakes = new List<ZoneWaterAndN>(); ZoneWaterAndN Uptake = new ZoneWaterAndN(); ZoneWaterAndN MyZone = new ZoneWaterAndN(); foreach (ZoneWaterAndN Z in soilstate.Zones) if (Z.Name == this.Parent.Name) MyZone = Z; double[] SW = MyZone.Water; OnPrepare(null, null); //DEAN!!! Uptake.Name = this.Parent.Name; Uptake.Water = Root.CalculateWaterUptake(TopsSWDemand, SW); Uptake.NO3N = new double[SW.Length]; Uptake.NH4N = new double[SW.Length]; Uptakes.Add(Uptake); return Uptakes; } else return null; }
/// <summary>Performs the water uptake calculations.</summary> internal void DoWaterCalculations() { if (MyWaterUptakeSource == "species") { // this module will compute water uptake // Pack the soil information ZoneWaterAndN myZone = new ZoneWaterAndN(); myZone.Name = this.Parent.Name; myZone.Water = mySoil.Water; myZone.NO3N = mySoil.NO3N; myZone.NH4N = mySoil.NH4N; // Get the amount of soil available water EvaluateSoilWaterAvailable(myZone); // Get the amount of water taken up EvaluateSoilWaterUptake(); // Send the delta water to soil water module DoSoilWaterUptake(); } else if ((MyWaterUptakeSource == "SoilArbitrator") || (MyWaterUptakeSource == "Arbitrator")) { // water uptake has been calculated by a resource arbitrator DoSoilWaterUptake(); } else { // water uptake is computed by another module (e.g. SWIM) and supplied by OnWaterUptakesCalculated throw new NotImplementedException(); } }
/// <summary> /// Calculate the potential sw uptake for today /// </summary> public List<ZoneWaterAndN> GetSWUptakes(SoilState soilstate) { if (Plant.IsAlive) { // Get all water supplies. double waterSupply = 0; List<double[]> supplies = new List<double[]>(); List<string> zoneNames = new List<string>(); foreach (ZoneWaterAndN zone in soilstate.Zones) { foreach (IArbitration o in Organs) { double[] organSupply = o.WaterSupply(zone); if (organSupply != null) { supplies.Add(organSupply); zoneNames.Add(zone.Name); waterSupply += MathUtilities.Sum(organSupply); } } } // Calculate total water demand. double waterDemand = 0; foreach (IArbitration o in Organs) waterDemand += o.WaterDemand; // 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> zones = new List<ZoneWaterAndN>(); for (int i = 0; i < supplies.Count; i++) { // Just send uptake from my zone ZoneWaterAndN uptake = new ZoneWaterAndN(); uptake.Name = zoneNames[i]; uptake.Water = MathUtilities.Multiply_Value(supplies[i], fractionUsed); uptake.NO3N = new double[uptake.Water.Length]; uptake.NH4N = new double[uptake.Water.Length]; zones.Add(uptake); } return zones; } else return null; }
/// <summary>Finds out the amount of plant available nitrogen (NH4 and NO3) in the soil.</summary> /// <param name="myZone">Soil information</param> internal void EvaluateSoilNitrogenAvailable(ZoneWaterAndN myZone) { if (myNitrogenAvailableMethod == PlantAvailableNitrogenMethod.BasicAgPasture) PlantAvailableSoilNBasicAgPasture(myZone); else if (myNitrogenAvailableMethod == PlantAvailableNitrogenMethod.DefaultAPSIM) PlantAvailableSoilNDefaultAPSIM(myZone); else if (myNitrogenAvailableMethod == PlantAvailableNitrogenMethod.AlternativeRLD) PlantAvailableSoilNAlternativeRLD(myZone); else if (myNitrogenAvailableMethod == PlantAvailableNitrogenMethod.AlternativeWup) PlantAvailableSoilNAlternativeWup(myZone); }
/// <summary>Gets the potential plant N uptake for each layer (mm).</summary> /// <remarks>The model can only handle one root zone at present.</remarks> /// <param name="soilstate">Soil state (current N contents)</param> /// <returns>Potential N uptake (kg/ha)</returns> public List<ZoneWaterAndN> GetNUptakes(SoilState soilstate) { if (IsAlive) { // Get the zone this plant is in ZoneWaterAndN myZone = new ZoneWaterAndN(); Zone parentZone = Apsim.Parent(this, typeof (Zone)) as Zone; foreach (ZoneWaterAndN Z in soilstate.Zones) if (Z.Name == parentZone.Name) myZone = Z; // Get the N amount available in the soil EvaluateSoilNitrogenAvailable(myZone); // Get the N amount fixed through symbiosis EvaluateNitrogenFixation(); // Evaluate the use of N remobilised and get N amount demanded from soil EvaluateSoilNitrogenDemand(); // Get N amount take up from the soil EvaluateSoilNitrogenUptake(); //Pack results into uptake structure ZoneWaterAndN myUptakeDemand = new ZoneWaterAndN(); myUptakeDemand.Name = myZone.Name; myUptakeDemand.NH4N = mySoilNH4Uptake; myUptakeDemand.NO3N = mySoilNO3Uptake; myUptakeDemand.Water = new double[nLayers]; List<ZoneWaterAndN> zones = new List<ZoneWaterAndN>(); zones.Add(myUptakeDemand); return zones; } else return null; }
/// <summary>Finds out the amount of plant available water in the soil.</summary> /// <param name="myZone">Soil information</param> internal void EvaluateSoilWaterAvailable(ZoneWaterAndN myZone) { if (myWaterAvailableMethod == PlantAvailableWaterMethod.Default) mySoilWaterAvailable = PlantAvailableSoilWaterDefault(myZone); else if (myWaterAvailableMethod == PlantAvailableWaterMethod.AlternativeKL) mySoilWaterAvailable = PlantAvailableSoilWaterAlternativeKL(myZone); else if (myWaterAvailableMethod == PlantAvailableWaterMethod.AlternativeKS) mySoilWaterAvailable = PlantAvailableSoilWaterAlternativeKS(myZone); }
/// <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> GetSWUptakes(Soils.Arbitrator.SoilState soilstate) { double SWDemand = 0; // Tree water demand (L) double PotSWSupply = 0; // Total water supply (L) foreach (ZoneInfo ZI in ZoneInfoList) { Soils.SoilWater S = Apsim.Find(ZI.zone, typeof(Soils.SoilWater)) as Soils.SoilWater; SWDemand += S.Eo * (1 / (1 - ZI.Shade / 100) - 1) * ZI.zone.Area * 10000; } 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++) { double[] LL15mm = MathUtilities.Multiply(ThisSoil.LL15,ThisSoil.Thickness); Uptake.Water[i] = (SW[i] - LL15mm[i]) * ZI.RLD[i]; PotSWSupply += Uptake.Water[i] * ZI.zone.Area * 10000; } Uptakes.Add(Uptake); } } } // 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; foreach (ZoneWaterAndN Z in Uptakes) Z.Water = MathUtilities.Multiply_Value(Z.Water, F); return Uptakes; }
/// <summary>Gets the potential plant water uptake for each layer (mm).</summary> /// <remarks>The model can only handle one root zone at present.</remarks> /// <param name="soilstate">Soil state (current water content)</param> /// <returns>Potential water uptake (mm)</returns> public List<ZoneWaterAndN> GetSWUptakes(SoilState soilstate) { if (IsAlive) { // Get the zone this plant is in ZoneWaterAndN myZone = new ZoneWaterAndN(); Zone parentZone = Apsim.Parent(this, typeof (Zone)) as Zone; foreach (ZoneWaterAndN zone in soilstate.Zones) if (zone.Name == parentZone.Name) myZone = zone; // Get the amount of water available for this plant EvaluateSoilWaterAvailable(myZone); // Get the amount of water potentially taken up by this plant EvaluateSoilWaterUptake(); // Pack potential uptake data for this plant ZoneWaterAndN myUptakeDemand = new ZoneWaterAndN(); myUptakeDemand.Name = myZone.Name; myUptakeDemand.Water = mySoilWaterUptake; myUptakeDemand.NO3N = new double[nLayers]; myUptakeDemand.NH4N = new double[nLayers]; List<ZoneWaterAndN> zones = new List<ZoneWaterAndN>(); zones.Add(myUptakeDemand); return zones; } else return null; }