/// <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> /// Return the %Wind Reduction for a given zone /// </summary> /// <param name="z">Zone</param> /// <param name="Today">The current date</param> /// <returns>%Wind Reduction</returns> public double GetWindReduction(Zone z, DateTime Today) { foreach (ZoneInfo zi in ZoneInfoList) if (zi.zone == z) { double UrelMin = Math.Max(0.0, 1.14 * 0.5 - 0.16); // 0.5 is porosity, will be dynamic in the future double Urel; double H; bool didInterp; double[] OADates = new double[dates.Count()]; double heightToday; for (int i = 0; i < dates.Count(); i++) OADates[i] = dates[i].ToOADate(); heightToday = MathUtilities.LinearInterpReal(Today.ToOADate(), OADates, heights, out didInterp); if (heightToday < 1000) Urel = 1; else { H = GetDistanceFromTrees(z) / (heightToday / 1000); if (H < 6) Urel = UrelMin + (1 - UrelMin) / 2 - H / 6 * (1 - UrelMin) / 2; else if (H < 6.1) Urel = UrelMin; else Urel = UrelMin + (1 - UrelMin) / (1 + 0.000928 * Math.Exp(Math.Pow(12.9372 * (H - 6), -0.26953))); } return Urel; } throw new ApsimXException(this, "Could not find zone called " + z.Name); }
/// <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> /// Return the %Shade for a given zone /// </summary> /// <param name="z">Zone</param> /// <returns>%Shade</returns> public double GetShade(Zone z) { foreach (ZoneInfo zi in ZoneInfoList) if (zi.zone == z) return zi.Shade; throw new ApsimXException(this, "Could not find a shade value for zone called " + z.Name); }
/// <summary> /// Return the %Wind Reduction for a given zone /// </summary> /// <param name="z">Zone</param> /// <returns>%Wind Reduction</returns> public double GetWindReduction(Zone z) { foreach (Zone zone in ZoneList) if (zone == z) { double UrelMin = Math.Max(0.0, 1.14 * 0.40 - 0.16); // 0.4 is porosity, will be dynamic in the future if (tree.heightToday < 1) Urel = 1; else { tree.H = GetDistanceFromTrees(z) / tree.heightToday; if (tree.H < 6) Urel = UrelMin + (1 - UrelMin) / 2 - tree.H / 6 * (1 - UrelMin) / 2; else if (tree.H < 6.1) Urel = UrelMin; else Urel = UrelMin + (1 - UrelMin) / (1 + 0.000928 * Math.Exp(12.9372 * Math.Pow((tree.H - 6), -0.26953))); } return Urel; } throw new ApsimXException(this, "Could not find zone called " + z.Name); }
/// <summary> /// Return the distance from the tree for a given zone. The tree is assumed to be in the first Zone. /// </summary> /// <param name="z">Zone</param> /// <returns>Distance from a static tree</returns> public double GetDistanceFromTrees(Zone z) { double D = 0; foreach (ZoneInfo zi in ZoneInfoList) { if (zi.zone is RectangularZone) D += (zi.zone as RectangularZone).Width; else if (zi.zone is CircularZone) D += (zi.zone as CircularZone).Width; else throw new ApsimXException(this, "Cannot calculate distance for trees for zone of given type."); if (zi.zone == z) return D; } throw new ApsimXException(this, "Could not find zone called " + z.Name); }
/// <summary> /// Passthrough for child nodes that need information from the tree. /// Saves having to query the simulation for the node location all the time. /// </summary> /// <param name="z">The zone.</param> /// <returns></returns> public double GetDistanceFromTrees(Zone z) { return tree.GetDistanceFromTrees(z); }
/// <summary> /// Return the %Radiation Reduction for a given zone /// </summary> /// <param name="z">Zone</param> /// <returns>%Radiation Reduction</returns> public double GetRadiationReduction(Zone z) { if (GetDistanceFromTrees(z) > 0.0) return 1.0-tree.GetShade(z)/100.0; else return 1.0; }
/// <summary> /// /// </summary> /// <param name="z"></param> /// <returns></returns> private double ZoneDistanceInTreeHeights(Zone z) { double treeHeight = GetHeightToday(); double distFromTree = GetDistanceFromTrees(z); return distFromTree / treeHeight; }
private void OnSimulationCommencing(object sender, EventArgs e) { ZoneList = Apsim.Children(this.Parent, typeof(Zone)); SetupTreeProperties(); //pre-fetch static information forestryZones = Apsim.ChildrenRecursively(Parent, typeof(Zone)); treeZone = ZoneList[0] as Zone; treeZoneWater = Apsim.Find(treeZone, typeof(Soils.SoilWater)) as Soils.SoilWater; TreeWaterUptake = new double[ZoneList.Count]; }
/// <summary> /// Return the width of the given zone. /// </summary> /// <param name="z">The width.</param> /// <returns></returns> private double GetZoneWidth(Zone z) { double D = 0; if (z is RectangularZone) D = (z as RectangularZone).Width; else if (z is CircularZone) D = (z as CircularZone).Width; else throw new ApsimXException(this, "Cannot calculate distance for trees for zone of given type."); return D; }
/// <summary> /// Return the %Shade for a given zone /// </summary> /// <param name="z"></param> public double GetShade(Zone z) { double distInTH = ZoneDistanceInTreeHeights(z); bool didInterp = false; return MathUtilities.LinearInterpReal(distInTH, shade.Keys.ToArray(), shade.Values.ToArray(), out didInterp); }
/// <summary> /// /// </summary> /// <param name="z"></param> /// <returns></returns> public double[] GetRLD(Zone z) { double distInTH = ZoneDistanceInTreeHeights(z); bool didInterp = false; DenseMatrix rldM = DenseMatrix.OfColumnArrays(rld.Values); double[] rldInterp = new double[rldM.RowCount]; for (int i=0;i< rldM.RowCount;i++) { rldInterp[i] = MathUtilities.LinearInterpReal(distInTH, rld.Keys.ToArray(), rldM.Row(i).ToArray(), out didInterp); } return rldInterp; }