/// <summary> /// Using the depth of soil water and current crop kl value, calculate a relative layer source strength for each crop. /// </summary> /// <param name="RootZones">The rootzones to process in current paddock</param> /// <param name="SWDep">An array containing depth of soil water per layer (mm/mm)</param> /// <param name="NumLayers">Number of layers in the soil profile</param> /// <returns>A 2D array containing the relative source strength for each crop and layer.</returns> private double[,] CalcRelSWLayerStrength(IEnumerable <DataRow> RootZones, double[] SWDep, int NumLayers) { RootSystemZoneType zone = new RootSystemZoneType(); double[,] RelSWLayerStrength = new double[RootZones.Count(), NumLayers]; for (int i = 0; i < RootZones.Count(); i++) //crops { double TotalSource = 0; zone = (RootSystemZoneType)RootZones.ToArray()[i].ItemArray[4]; int DeepestRoot = CalcMaxRootLayer(zone.RootDepth, zone.dlayer); for (int j = 0; j < NumLayers; j++) { if (j <= DeepestRoot) { TotalSource += zone.kl[j] * SWDep[j]; } } for (int j = 0; j < NumLayers; j++) { if (j <= DeepestRoot && zone.kl[j] > 0) { RelSWLayerStrength[i, j] = zone.kl[j] * SWDep[j] / TotalSource; } else { RelSWLayerStrength[i, j] = 0; } } } return(RelSWLayerStrength); }
/// <summary> /// For each crop, calculate the maximum water uptake for each layer. /// </summary> /// <param name="CropSWDemand">An array holding the SW demand for each crop.</param> /// <param name="RelSWLayerStrength">The relative source strength for each crop and layer</param> /// <param name="NumLayers">Number of layers in the soil profile</param> /// <param name="RootZones">The rootzones to process in current paddock</param> /// <param name="dlayer">Layer depth array</param> /// <returns>A 2D array containing the maximum uptake for each crop and layer</returns> private double[,] CalcCropSWLayerUptake(double[] CropSWDemand, double[,] RelSWLayerStrength, int NumLayers, IEnumerable <DataRow> RootZones, double[] dlayer) { double[,] CropSWLayerUptake = new double[CropSWDemand.Length, NumLayers]; for (int i = 0; i < CropSWDemand.Length; i++) { RootSystemZoneType Zone = (RootSystemZoneType)RootZones.ToArray()[i].ItemArray[4]; for (int j = 0; j < NumLayers; j++) { CropSWLayerUptake[i, j] = CropSWDemand[i] * RelSWLayerStrength[i, j] * RootProportion(j, Zone.RootDepth, dlayer); } } return(CropSWLayerUptake); }
/// <summary> /// Calculate relative strength of crop kl for each layer. /// Will be 1 for a single crop. /// </summary> /// <param name="RootZones">The rootzones to process in current paddock</param> /// <returns>A 2D array containing the relative kl strength of each crop and layer.</returns> private double[,] CalcRelKLStrength(IEnumerable <DataRow> RootZones, double[] CropSWDemand) { double[][] KLArray = new double[RootZones.Count()][]; int[] LowestRootLayer = new int[RootZones.Count()]; for (int i = 0; i < KLArray.GetLength(0); i++) //extract the kl array from each zone { RootSystemZoneType zone = (RootSystemZoneType)RootZones.ToArray()[i].ItemArray[4]; KLArray[i] = zone.kl; for (int j = 0; j < KLArray[i].Length; j++) { if (CropSWDemand[i] == 0) { KLArray[i][j] = 0; } } LowestRootLayer[i] = CalcMaxRootLayer(zone.RootDepth, zone.dlayer); } //calculate relative demand strength for each layer double[,] RelKLStrength = new double[KLArray[0].Length, KLArray.Length]; for (int i = 0; i < KLArray[0].Length; i++) //layer { double KLSum = 0; for (int j = 0; j < KLArray.Length; j++) //for the current layer, sum the kl's of each crop in the layer { if (i <= LowestRootLayer[j]) { KLSum += KLArray[j][i]; } } for (int j = 0; j < KLArray.Length; j++) //use those summed kl's to calculate the relative kl strength for each crop in the layer { if (i <= LowestRootLayer[j] && KLArray[j][i] > 0) { RelKLStrength[i, j] = KLArray[j][i] / KLSum; } else { RelKLStrength[i, j] = 0; } } } return(RelKLStrength); }
/// <summary> /// Calculate the amount of water available to each crop on a per layer basis. /// As crops will have different lower limits, they can have a different supply. /// </summary> /// <param name="RootZones">The rootzones to process in current paddock</param> /// <param name="SWDep">An array containing depth of soil water per layer (mm/mm)</param> /// <param name="NumLayers">Number of layers in the soil profile</param> /// <returns>A 2D array containing the SW supply available for each crop and layer.</returns> private double[,] CalcSWSupply(IEnumerable <DataRow> RootZones, double[] SWDep, int NumLayers) { RootSystemZoneType zone = new RootSystemZoneType(); double[,] SWSupply = new double[RootZones.Count(), NumLayers]; for (int i = 0; i < RootZones.Count(); i++) //crops { zone = (RootSystemZoneType)RootZones.ToArray()[i].ItemArray[4]; for (int j = 0; j < NumLayers; j++) { SWSupply[i, j] = zone.kl[j] * (SWDep[j] - zone.ll[j] * zone.dlayer[j]) * zone.ZoneArea; if (SWSupply[i, j] < 0) { SWSupply[i, j] = 0; //can be < 0 if another crop with a lower LL has extracted below what this one can. } } } return(SWSupply); }
public void OnProcess() { //set up data table int NumLayers = 0; AllRootSystems.Rows.Clear(); foreach (Paddock p in paddock.ChildPaddocks) { foreach (Component c in p.Crops) { string PlantStatus; if (!c.Get("plant_status", out PlantStatus)) { throw new Exception("Could not find plant_status for crop :" + c.Name); } if (PlantStatus != "out") //if crop is not in ground, we don't care about it { if (c.GetObject("RootSystem", ref RootData)) //crop has a RootData structre { Dictionary <string, double> SWStrength = CalcSWSourceStrength(RootData); foreach (RootSystemZoneType zone in RootData.Zone) //add each zone to the table { AllRootSystems.Rows.Add(zone.ZoneName, c.Name, RootData.SWDemand, SWStrength, zone, true); } NumLayers = RootData.Zone[0].kl.Length; } else //crop does not have RootData structure, so make one. { Dictionary <string, double> SWStrength = new Dictionary <string, double>(); RootData = new RootSystemType(); RootData.Zone = new RootSystemZoneType[1]; RootData.Zone[0] = new RootSystemZoneType(); RootData.Zone[0].ZoneName = p.Name; RootData.Zone[0].ZoneArea = 1; if (!c.Get("sw_demand", out RootData.SWDemand)) { throw new Exception("Could not get sw_demand for crop " + c.Name); } if (!c.Get("root_depth", out RootData.Zone[0].RootDepth)) { throw new Exception("Could not get root_depth for crop " + c.Name); } if (!c.Get("ll", out RootData.Zone[0].ll)) { throw new Exception("Could not get ll for crop " + c.Name); } if (!c.Get("kl", out RootData.Zone[0].kl)) { throw new Exception("Could not get kl for crop " + c.Name); } SoilWat = (Component)p.LinkByType("SoilWat"); SWStrength.Add(p.Name, 1); if (!SoilWat.Get("dlayer", out RootData.Zone[0].dlayer)) { throw new Exception("Could not get dlayer for paddock " + p.Name); } AllRootSystems.Rows.Add(RootData.Zone[0].ZoneName, c.Name, RootData.SWDemand, SWStrength, RootData.Zone[0], true); NumLayers = RootData.Zone[0].kl.Length; } } } } //use LINQ to extract the paddocks for processing IEnumerable <string> paddockNames = AllRootSystems.AsEnumerable().Select <DataRow, string>(name => (string)name.ItemArray[0]).Distinct(); //do water allocation for each paddock foreach (string PaddockName in paddockNames) { IEnumerable <DataRow> RootZones = AllRootSystems.AsEnumerable().Where(row => row.ItemArray[0].Equals(PaddockName)); Paddock p = (Paddock)paddock.LinkByName(PaddockName); Component fieldProps = (Component)p.LinkByName("FieldProps"); double fieldArea; if (fieldProps == null || !fieldProps.Get("fieldArea", out fieldArea)) { throw new Exception("Could not find FieldProps component in field " + PaddockName); } Component Soil = (Component)p.LinkByType("SoilWat"); double[] SWDep; double[] dlayer; Soil.Get("dlayer", out dlayer); Soil.Get("sw_dep", out SWDep); double[] CropSWDemand = new double[RootZones.Count()]; for (int i = 0; i < RootZones.Count(); i++) //get demand for all crops in paddock using relative SW strength { Dictionary <string, double> PaddockSWDemands = (Dictionary <string, double>)RootZones.ToArray()[i].ItemArray[3]; CropSWDemand[i] = PaddockSWDemands[p.Name] * (double)RootZones.ToArray()[i].ItemArray[2]; } double[,] RelKLStrength = CalcRelKLStrength(RootZones, CropSWDemand); double[,] RelSWLayerStrength = CalcRelSWLayerStrength(RootZones, SWDep, NumLayers); double[,] SWSupply = CalcSWSupply(RootZones, SWDep, NumLayers); double[,] LayerUptake = new double[RootZones.Count(), NumLayers]; double[] LastCropSWDemand; double[,] LastSWSupply; int count = 0; do { count++; LastCropSWDemand = CropSWDemand; LastSWSupply = SWSupply; for (int i = 0; i < RootZones.Count(); i++) //get as much water as possible for the layer using relative kl strengths { RootSystemZoneType Zone = (RootSystemZoneType)RootZones.ToArray()[i].ItemArray[4]; for (int j = 0; j < NumLayers; j++) { if (MathUtility.Sum(CropSWDemand) < MathUtility.Sum(SWSupply)) { LayerUptake[i, j] = CropSWDemand[i] * RelSWLayerStrength[i, j]; } else { LayerUptake[i, j] = SWSupply[i, j] * RelKLStrength[j, i] * RootProportion(j, Zone.RootDepth, dlayer); } if (LayerUptake[i, j] < 0) { throw new Exception("Layer uptake should not be negative"); } } } DenseMatrix Uptake = DenseMatrix.OfArray(LayerUptake); Paddock CurrentPaddock; Component CurrentCrop; for (int i = 0; i < RootZones.Count(); i++) //subtract taken water from the supply and demand { CurrentPaddock = (Paddock)p.LinkByName((string)RootZones.ToArray()[i].ItemArray[0]); CurrentCrop = (Component)CurrentPaddock.LinkByName((string)RootZones.ToArray()[i].ItemArray[1]); CropSWDemand[i] -= Uptake.Row(i).Sum(); if (CurrentCrop != null && CurrentCrop.Name.ToLower().Equals("maize")) { CurrentCrop.Set("arb_water_uptake", Uptake.Row(i).ToArray()); } for (int j = 0; j < NumLayers; j++) { SWSupply[i, j] -= LayerUptake[i, j]; } } //subtract from soil water for (int j = 0; j < Uptake.ColumnCount; j++) { SWDep[j] -= Uptake.Column(j).Sum() / fieldArea; } Soil.Set("sw_dep", SWDep); } while (MathUtility.Sum(LastCropSWDemand) != MathUtility.Sum(CropSWDemand) && MathUtility.Sum(LastSWSupply) != MathUtility.Sum(SWSupply)); } }