Exemplo n.º 1
0
        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);
            }
        }
Exemplo n.º 2
0
        /// <summary>Creates an initial sample.</summary>
        /// <param name="soil">The soil.</param>
        private static void CreateInitialSample(Soil soil)
        {
            var soilOrganicMatter = soil.Children.Find(child => child is Organic) as Organic;
            var analysis          = soil.Children.Find(child => child is Chemical) as Chemical;
            var initial           = soil.Children.Find(child => child is Sample) as Sample;
            var soilPhysical      = soil.FindChild <Soils.IPhysical>();

            if (initial == null)
            {
                initial = new Sample()
                {
                    Thickness = soilPhysical.Thickness, Parent = soil
                };
                soil.Children.Add(initial);
            }
            initial.Name = "Initial";

            if (analysis.NO3N != null)
            {
                initial.NO3 = SoilUtilities.ppm2kgha(soilPhysical.Thickness, soilPhysical.BD, analysis.NO3N);
            }
            if (analysis.NH4N != null)
            {
                initial.NH4 = SoilUtilities.ppm2kgha(soilPhysical.Thickness, soilPhysical.BD, analysis.NH4N);
            }

            initial.OC  = MergeArrays(initial.OC, soilOrganicMatter.Carbon);
            initial.PH  = MergeArrays(initial.PH, analysis.PH);
            initial.ESP = MergeArrays(initial.ESP, analysis.ESP);
            initial.EC  = MergeArrays(initial.EC, analysis.EC);
            initial.CL  = MergeArrays(initial.CL, analysis.CL);

            soilOrganicMatter.Carbon = null;
            //soil.Children.Remove(analysis);
        }
Exemplo n.º 3
0
        private void CalculateNitrogenSupply(ZoneState myZone, ZoneWaterAndN zone)
        {
            var soilPhysical = myZone.Soil.FindChild <Soils.IPhysical>();

            myZone.MassFlow  = new double[soilPhysical.Thickness.Length];
            myZone.Diffusion = new double[soilPhysical.Thickness.Length];

            int currentLayer = SoilUtilities.LayerIndexOfDepth(myZone.Physical.Thickness, 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
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Growth depth of roots in this zone
        /// </summary>
        public void GrowRootDepth()
        {
            // Do Root Front Advance
            int RootLayer            = SoilUtilities.LayerIndexOfDepth(Physical.Thickness, Depth);
            var rootfrontvelocity    = rootFrontVelocity.Value(RootLayer);
            var rootDepthWaterStress = root.RootDepthStressFactor.Value(RootLayer);

            double MaxDepth;

            double[] xf = null;
            if (!IsWeirdoPresent)
            {
                var soilCrop = Soil.FindDescendant <SoilCrop>(plant.Name + "Soil");
                if (soilCrop == null)
                {
                    throw new Exception($"Cannot find a soil crop parameterisation called {plant.Name}Soil");
                }

                xf = soilCrop.XF;

                Depth    = Depth + rootfrontvelocity * xf[RootLayer] * rootDepthWaterStress;;
                MaxDepth = 0;
                // Limit root depth for impeded layers
                for (int i = 0; i < Physical.Thickness.Length; i++)
                {
                    if (xf[i] > 0)
                    {
                        MaxDepth += Physical.Thickness[i];
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else
            {
                Depth    = Depth + rootfrontvelocity;
                MaxDepth = Physical.Thickness.Sum();
            }

            // Limit root depth for the crop specific maximum depth
            MaxDepth = Math.Min(maximumRootDepth.Value(), MaxDepth);
            Depth    = Math.Min(Depth, MaxDepth);

            //RootFront - needed by sorghum
            if (root.RootFrontCalcSwitch?.Value() == 1)
            {
                var dltRootFront = rootfrontvelocity * rootDepthWaterStress * xf[RootLayer];

                double maxFront = Math.Sqrt(Math.Pow(Depth, 2) + Math.Pow(LeftDist, 2));
                dltRootFront = Math.Min(dltRootFront, maxFront - RootFront);
                RootFront   += dltRootFront;
            }
            else
            {
                RootFront = Depth;
            }
            root.RootShape.CalcRootProportionInLayers(this);
        }
Exemplo n.º 5
0
 /// <summary>
 /// Calculate Root Activity Values for water and nitrogen
 /// </summary>
 public double[] CalculateRootActivityValues()
 {
     double[] RAw = new double[Physical.Thickness.Length];
     for (int layer = 0; layer < Physical.Thickness.Length; layer++)
     {
         if (layer <= SoilUtilities.LayerIndexOfDepth(Physical.Thickness, Depth))
         {
             if (LayerLive[layer].Wt > 0)
             {
                 RAw[layer] = -WaterUptake[layer] / LayerLive[layer].Wt
                              * Physical.Thickness[layer]
                              * SoilUtilities.ProportionThroughLayer(Physical.Thickness, layer, Depth);
                 RAw[layer] = Math.Max(RAw[layer], 1e-20);  // Make sure small numbers to avoid lack of info for partitioning
             }
             else if (layer > 0)
             {
                 RAw[layer] = RAw[layer - 1];
             }
             else
             {
                 RAw[layer] = 0;
             }
         }
     }
     return(RAw);
 }
Exemplo n.º 6
0
        /// <summary>Sets the soil.</summary>
        /// <param name="soil">The soil.</param>
        public void SetSoil(Soil soil)
        {
            XmlDocument soilDoc = new XmlDocument();

            soilDoc.LoadXml(SoilUtilities.ToXML(soil));
            XmlNode paddockNode = XmlUtilities.Find(simulationXML, "Paddock");

            paddockNode.AppendChild(paddockNode.OwnerDocument.ImportNode(soilDoc.DocumentElement, true));
        }
Exemplo n.º 7
0
        /// <summary>
        /// Growth depth of roots in this zone
        /// </summary>
        public void GrowRootDepth()
        {
            // Do Root Front Advance
            int RootLayer         = SoilUtilities.LayerIndexOfDepth(Physical.Thickness, Depth);
            var rootfrontvelocity = rootFrontVelocity.Value(RootLayer);

            double MaxDepth;

            double[] xf = null;
            if (!IsWeirdoPresent)
            {
                var soilCrop = Soil.FindDescendant <SoilCrop>(plant.Name + "Soil");
                if (soilCrop == null)
                {
                    throw new Exception($"Cannot find a soil crop parameterisation called {plant.Name}Soil");
                }

                xf = soilCrop.XF;

                Depth    = Depth + rootfrontvelocity * xf[RootLayer];
                MaxDepth = 0;
                // Limit root depth for impeded layers
                for (int i = 0; i < Physical.Thickness.Length; i++)
                {
                    if (xf[i] > 0)
                    {
                        MaxDepth += Physical.Thickness[i];
                    }
                    else
                    {
                        break;
                    }
                }
            }
            else
            {
                Depth    = Depth + rootfrontvelocity;
                MaxDepth = Physical.Thickness.Sum();
            }

            // Limit root depth for the crop specific maximum depth
            MaxDepth = Math.Min(maximumRootDepth.Value(), MaxDepth);
            Depth    = Math.Min(Depth, MaxDepth);

            RootFront = Depth;
            root.RootShape.CalcRootProportionInLayers(this);
            root.RootShape.CalcRootVolumeProportionInLayers(this);
        }
Exemplo n.º 8
0
        /// <summary>Apply fertiliser.</summary>
        /// <param name="Amount">The amount.</param>
        /// <param name="Type">The type.</param>
        /// <param name="Depth">The depth.</param>
        /// <param name="doOutput">If true, output will be written to the summary.</param>
        public void Apply(double Amount, Types Type, double Depth = 0.0, bool doOutput = true)
        {
            if (Amount > 0)
            {
                // find the layer that the fertilizer is to be added to.
                int layer = SoilUtilities.LayerIndexOfDepth(soilPhysical.Thickness, Depth);

                FertiliserType fertiliserType = Definitions.FirstOrDefault(f => f.Name == Type.ToString());
                if (fertiliserType == null)
                {
                    throw new ApsimXException(this, "Cannot find fertiliser type '" + Type + "'");
                }

                // We find the current amount of N in each form, add to it as needed,
                // then set the new value. An alternative approach could call AddKgHaDelta
                // rather than SetKgHa
                if (fertiliserType.FractionNO3 != 0)
                {
                    var values = NO3.kgha;
                    values[layer] += Amount * fertiliserType.FractionNO3;
                    NO3.SetKgHa(SoluteSetterType.Fertiliser, values);
                    NitrogenApplied += Amount * fertiliserType.FractionNO3;
                }
                if (fertiliserType.FractionNH4 != 0)
                {
                    var values = NH4.kgha;
                    values[layer] += Amount * fertiliserType.FractionNH4;
                    NH4.SetKgHa(SoluteSetterType.Fertiliser, values);
                    NitrogenApplied += Amount * fertiliserType.FractionNH4;
                }
                if (fertiliserType.FractionUrea != 0)
                {
                    var values = Urea.kgha;
                    values[layer] += Amount * fertiliserType.FractionUrea;
                    Urea.SetKgHa(SoluteSetterType.Fertiliser, values);
                    NitrogenApplied += Amount * fertiliserType.FractionUrea;
                }
                if (doOutput)
                {
                    Summary.WriteMessage(this, string.Format("{0} kg/ha of {1} added at depth {2} layer {3}", Amount, Type, Depth, layer + 1));
                }

                Fertilised?.Invoke(this, new FertiliserApplicationType()
                {
                    Amount = Amount, Depth = Depth, FertiliserType = Type
                });
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Retrieves soil types from ApSoil
        /// Configures any missing crop ll, kl values
        /// </summary>
        /// <param name="simulation"></param>
        /// <param name="apsoilService"></param>
        /// <returns></returns>
        public static void DoSoils(AusFarmSpec simulation)
        {
            APSOIL.Service apsoilService = null;
            Soil           soil;

            for (int i = 0; i < simulation.OnFarmSoilTypes.Count; i++)
            {
                FarmSoilType soilType = simulation.OnFarmSoilTypes[i];
                soil = soilType.SoilDescr;
                if (soil == null)
                {
                    // Look for a <SoilName> and if found go get the soil from the Apsoil web service.
                    if (apsoilService == null)
                    {
                        apsoilService = new APSOIL.Service();
                    }
                    string soilXml = apsoilService.SoilXML(soilType.SoilPath);
                    if (soilXml == string.Empty)
                    {
                        throw new Exception("Cannot find soil: " + soilType.SoilPath);
                    }

                    soil = SoilUtilities.FromXML(soilXml);
                }

                // Other crop types not listed here will have their ll, kll, xf values calculated later

                // Remove any initwater nodes.
                soil.InitialWater = null;

                foreach (Sample sample in soil.Samples)
                {
                    CheckSample(soil, sample);
                }

                // get rid of <soiltype> from the soil
                // this is necessary because NPD uses this field and puts in really long
                // descriptive classifications. Soiln2 bombs with an FString internal error.
                soil.SoilType = "";

                // Set the soil name to 'soil'
                //soil.Name = "Soil";
                soilType.SoilDescr = soil;  //store the changed description
            }
        }
Exemplo n.º 10
0
        // POST: api/ApsoilDB
        public void Post([FromBody] string value)
        {
            List <string> eachSoilsXML = DatabaseManager.GetEachSoilsXMLfromDB();
            List <Soil>   soilObjects  = new List <Soil>();
            Soil          soil;

            foreach (string xml in eachSoilsXML)
            {
                soil = SoilUtilities.FromXML(xml);
                if (SoilInAustralia(soil))
                {
                    soilObjects.Add(soil);
                }
            }
            AllSoilsInDataTables soilsInDataTables = new AllSoilsInDataTables();

            soilsInDataTables.AddAllSoilsIntoDataTables(soilObjects);
            DatabaseManager.CleanOutDBTables();
            DatabaseManager.InsertDataTablesIntoDB(soilsInDataTables);
        }
Exemplo n.º 11
0
        /// <summary>Calculates the root area for a layer of soil</summary>
        public void CalcRootProportionInLayers(ZoneState zone)
        {
            var physical = zone.Soil.FindChild <Soils.IPhysical>();

            zone.RootArea = (zone.RightDist + zone.LeftDist) * zone.Depth / 1e6;
            for (int layer = 0; layer < physical.Thickness.Length; layer++)
            {
                double prop;
                double top = layer == 0 ? 0 : MathUtilities.Sum(physical.Thickness, 0, layer - 1);

                if (zone.Depth < top)
                {
                    prop = 0;
                }
                else
                {
                    prop = SoilUtilities.ProportionThroughLayer(physical.Thickness, layer, zone.Depth);
                }
                zone.RootProportions[layer] = prop;
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Return the plant available water CAPACITY. Units: mm/mm
        /// </summary>
        public double[] PAWCCrop(string CropName)
        {
            var soilCrop = Soil.FindDescendant <SoilCrop>(CropName + "Soil");

            if (soilCrop == null)
            {
                throw new Exception($"Cannot find a soil crop parameterisation called {CropName}Soil");
            }

            if (soilCrop != null)
            {
                return(SoilUtilities.CalcPAWC(SoilPhysical.Thickness,
                                              soilCrop.LL,
                                              SoilPhysical.DUL,
                                              soilCrop.XF));
            }
            else
            {
                return(new double[0]);
            }
        }
Exemplo n.º 13
0
        /// <summary>Fills in KL for crop.</summary>
        /// <param name="crop">The crop.</param>
        private static void FillInKLForCrop(SoilCrop crop)
        {
            if (crop.Name == null)
            {
                throw new Exception("Crop has no name");
            }
            int i = StringUtilities.IndexOfCaseInsensitive(cropNames, crop.Name);

            if (i != -1)
            {
                var water = crop.Parent as Physical;

                double[] KLs = GetRowOfArray(defaultKLs, i);

                double[] cumThickness = SoilUtilities.ToCumThickness(water.Thickness);
                crop.KL = new double[water.Thickness.Length];
                for (int l = 0; l < water.Thickness.Length; l++)
                {
                    bool didInterpolate;
                    crop.KL[l] = MathUtilities.LinearInterpReal(cumThickness[l], defaultKLThickness, KLs, out didInterpolate);
                }
            }
        }
Exemplo n.º 14
0
        // --- Private methods ---------------------------------------------------------------

        /// <summary>
        /// Calculate the weighting factor hydraulic effectiveness used
        /// to weight the effect of soil moisture on runoff.
        /// </summary>
        /// <returns>Weighting factor for runoff</returns>
        private double[] RunoffWeightingFactor()
        {
            double cumRunoffWeightingFactor = 0.0;

            // Get sumulative depth (mm)
            double[] cumThickness = SoilUtilities.ToCumThickness(soilPhysical.Thickness);

            // Ensure hydro effective depth doesn't go below bottom of soil.
            hydrolEffectiveDepth = Math.Min(hydrolEffectiveDepth, MathUtilities.Sum(soilPhysical.Thickness));

            // Scaling factor for wf function to sum to 1
            double scaleFactor = 1.0 / (1.0 - Math.Exp(-4.16));

            // layer number that the effective depth occurs
            int hydrolEffectiveLayer = SoilUtilities.LayerIndexOfDepth(soilPhysical.Thickness, hydrolEffectiveDepth);

            double[] runoffWeightingFactor = new double[soilPhysical.Thickness.Length];
            for (int i = 0; i <= hydrolEffectiveLayer; i++)
            {
                double cumDepth = cumThickness[i];
                cumDepth = Math.Min(cumDepth, hydrolEffectiveDepth);

                // assume water content to hydrol_effective_depth affects runoff
                // sum of wf should = 1 - may need to be bounded? <dms 7-7-95>
                runoffWeightingFactor[i]  = scaleFactor * (1.0 - Math.Exp(-4.16 * MathUtilities.Divide(cumDepth, hydrolEffectiveDepth, 0.0)));
                runoffWeightingFactor[i]  = runoffWeightingFactor[i] - cumRunoffWeightingFactor;
                cumRunoffWeightingFactor += runoffWeightingFactor[i];
            }

            // Ensure total runoff weighting factor equals 1.
            if (!MathUtilities.FloatsAreEqual(MathUtilities.Sum(runoffWeightingFactor), 1.0))
            {
                throw new Exception("Internal error. Total runoff weighting factor must be equal to one.");
            }

            return(runoffWeightingFactor);
        }
Exemplo n.º 15
0
        /// <summary>Add urine to the soil.</summary>
        private void AddUrineToSoil()
        {
            if (DoUrineReturn == null)
            {
                // We will do the urine return.
                // find the layer that the fertilizer is to be added to.
                int layer = SoilUtilities.LayerIndexOfDepth(soilPhysical.Thickness, DepthUrineIsAdded);

                var ureaValues = Urea.kgha;
                ureaValues[layer] += AmountUrineNReturned;
                Urea.SetKgHa(SoluteSetterType.Fertiliser, ureaValues);
            }
            else
            {
                // Another model (e.g. urine patch) will do the urine return.
                DoUrineReturn.Invoke(this,
                                     new UrineReturnType()
                {
                    Amount   = AmountUrineNReturned,
                    Depth    = DepthUrineIsAdded,
                    GrazedDM = GrazedDM
                });
            }
        }
Exemplo n.º 16
0
        /// <summary>Sets the soil.</summary>
        /// <param name="soil">The soil.</param>
        public void SetSoil(Soil soil)
        {
            XmlDocument soilDoc = new XmlDocument();

            soilDoc.LoadXml(SoilUtilities.ToXML(soil));

            // Name soil crop models
            foreach (XmlNode soilcrop in XmlUtilities.FindAllRecursivelyByType(soilDoc.DocumentElement, "SoilCrop"))
            {
                XmlUtilities.SetValue(soilcrop, "Name", XmlUtilities.Attribute(soilcrop, "name") + "Soil");
            }

            // Name soil samples
            foreach (XmlNode sample in XmlUtilities.FindAllRecursivelyByType(soilDoc.DocumentElement, "Sample"))
            {
                XmlUtilities.SetValue(sample, "Name", XmlUtilities.Attribute(sample, "name"));
            }

            XmlNode paddockNode = XmlUtilities.Find(simulationXML, "Zone");

            XmlUtilities.EnsureNodeExists(soilDoc.DocumentElement, "SoilNitrogen");
            soilDoc.DocumentElement.RemoveChild(XmlUtilities.Find(soilDoc.DocumentElement, "SoilTemperature"));
            paddockNode.AppendChild(paddockNode.OwnerDocument.ImportNode(soilDoc.DocumentElement, true));
        }
Exemplo n.º 17
0
        /// <summary>Do all soil related settings.</summary>
        /// <param name="simulation">The specification to use</param>
        /// <param name="workingFolder">The folder where files shoud be created.</param>
        private static void DoSoil(APSIMSpecification simulation, string workingFolder)
        {
            Soil soil;

            if (simulation.Soil == null)
            {
                if (simulation.SoilPath.StartsWith("<Soil"))
                {
                    // Soil and Landscape grid
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(simulation.SoilPath);
                    soil = XmlUtilities.Deserialise(doc.DocumentElement, typeof(Soil)) as Soil;
                }
                else if (simulation.SoilPath.StartsWith("http"))
                {
                    // Soil and Landscape grid
                    string xml;
                    using (var client = new WebClient())
                    {
                        xml = client.DownloadString(simulation.SoilPath);
                    }
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(xml);
                    List <XmlNode> soils = XmlUtilities.ChildNodes(doc.DocumentElement, "Soil");
                    if (soils.Count == 0)
                    {
                        throw new Exception("Cannot find soil in Soil and Landscape Grid");
                    }
                    soil = XmlUtilities.Deserialise(soils[0], typeof(Soil)) as Soil;
                }
                else
                {
                    // Apsoil web service.
                    APSOIL.Service apsoilService = new APSOIL.Service();
                    string         soilXml       = apsoilService.SoilXML(simulation.SoilPath.Replace("\r\n", ""));
                    if (soilXml == string.Empty)
                    {
                        throw new Exception("Cannot find soil: " + simulation.SoilPath);
                    }
                    soil = SoilUtilities.FromXML(soilXml);
                }
            }
            else
            {
                // Just use the soil we already have
                soil = simulation.Soil;
            }

            // Make sure we have a soil crop parameterisation. If not then try creating one
            // based on wheat.
            Sow sowing = YieldProphetUtility.GetCropBeingSown(simulation.Management);

            string[] cropNames = soil.Water.Crops.Select(c => c.Name).ToArray();
            if (cropNames.Length == 0)
            {
                throw new Exception("Cannot find any crop parameterisations in soil: " + simulation.SoilPath);
            }

            if (sowing != null && !StringUtilities.Contains(cropNames, sowing.Crop))
            {
                SoilCrop wheat = soil.Water.Crops.Find(c => c.Name.Equals("wheat", StringComparison.InvariantCultureIgnoreCase));
                if (wheat == null)
                {
                    // Use the first crop instead.
                    wheat = soil.Water.Crops[0];
                }

                SoilCrop newSoilCrop = new SoilCrop();
                newSoilCrop.Name      = sowing.Crop;
                newSoilCrop.Thickness = wheat.Thickness;
                newSoilCrop.LL        = wheat.LL;
                newSoilCrop.KL        = wheat.KL;
                newSoilCrop.XF        = wheat.XF;
                soil.Water.Crops.Add(newSoilCrop);
            }

            // Remove any initwater nodes.
            soil.InitialWater = null;

            // Transfer the simulation samples to the soil
            if (simulation.Samples != null)
            {
                soil.Samples = simulation.Samples;
            }

            if (simulation.InitTotalWater != 0)
            {
                soil.InitialWater = new InitialWater();
                soil.InitialWater.PercentMethod = InitialWater.PercentMethodEnum.FilledFromTop;

                double pawc;
                if (sowing == null || sowing.Crop == null)
                {
                    pawc = MathUtilities.Sum(PAWC.OfSoilmm(soil));
                    soil.InitialWater.RelativeTo = "LL15";
                }
                else
                {
                    SoilCrop crop = soil.Water.Crops.Find(c => c.Name.Equals(sowing.Crop, StringComparison.InvariantCultureIgnoreCase));
                    pawc = MathUtilities.Sum(PAWC.OfCropmm(soil, crop));
                    soil.InitialWater.RelativeTo = crop.Name;
                }

                soil.InitialWater.FractionFull = Convert.ToDouble(simulation.InitTotalWater) / pawc;
            }

            if (simulation.InitTotalNitrogen != 0)
            {
                // Add in a sample.
                Sample nitrogenSample = new Sample();
                nitrogenSample.Name = "NitrogenSample";
                soil.Samples.Add(nitrogenSample);
                nitrogenSample.Thickness = new double[] { 150, 150, 3000 };
                nitrogenSample.NO3Units  = Nitrogen.NUnitsEnum.kgha;
                nitrogenSample.NH4Units  = Nitrogen.NUnitsEnum.kgha;
                nitrogenSample.NO3       = new double[] { 6.0, 2.1, 0.1 };
                nitrogenSample.NH4       = new double[] { 0.5, 0.1, 0.1 };
                nitrogenSample.OC        = new double[] { double.NaN, double.NaN, double.NaN };
                nitrogenSample.EC        = new double[] { double.NaN, double.NaN, double.NaN };
                nitrogenSample.PH        = new double[] { double.NaN, double.NaN, double.NaN };

                double Scale = Convert.ToDouble(simulation.InitTotalNitrogen) / MathUtilities.Sum(nitrogenSample.NO3);
                nitrogenSample.NO3 = MathUtilities.Multiply_Value(nitrogenSample.NO3, Scale);
            }

            // Add in soil temperature. Needed for Aflatoxin risk.
            soil.SoilTemperature = new SoilTemperature();
            soil.SoilTemperature.BoundaryLayerConductance = 15;
            soil.SoilTemperature.Thickness = new double[] { 2000 };
            soil.SoilTemperature.InitialSoilTemperature = new double[] { 22 };
            if (soil.Analysis.ParticleSizeClay == null)
            {
                soil.Analysis.ParticleSizeClay = MathUtilities.CreateArrayOfValues(60, soil.Analysis.Thickness.Length);
            }
            InFillMissingValues(soil.Analysis.ParticleSizeClay);

            foreach (Sample sample in soil.Samples)
            {
                CheckSample(soil, sample, sowing);
            }

            Defaults.FillInMissingValues(soil);

            // Correct the CONA / U parameters depending on LAT/LONG
            CorrectCONAU(simulation, soil, workingFolder);

            // get rid of <soiltype> from the soil
            // this is necessary because NPD uses this field and puts in really long
            // descriptive classifications. Soiln2 bombs with an FString internal error.
            soil.SoilType = null;

            // Set the soil name to 'soil'
            soil.Name = "Soil";

            simulation.Soil = soil;
        }
Exemplo n.º 18
0
        private void OnDoSoilWaterMovement(object sender, EventArgs e)
        {
            // Calculate lateral flow.
            LateralFlow = lateralFlowModel.Values;
            MathUtilities.Subtract(Water, LateralFlow);

            // Calculate runoff.
            Runoff = runoffModel.Value;

            // Calculate infiltration.
            Infiltration = PotentialInfiltration - Runoff;
            Water[0]     = Water[0] + Infiltration;

            // Allow irrigation to infiltrate.
            if (!irrigation.WillRunoff)
            {
                int irrigationLayer = SoilUtilities.FindLayerIndex(properties, Convert.ToInt32(irrigation.Depth));
                Water[irrigationLayer] = irrigation.IrrigationApplied;
                Infiltration          += irrigation.IrrigationApplied;

                // DeanH - haven't implemented solutes in irrigation water yet.
                // NO3[irrigationLayer] = irrigation.NO3;
                // NH4[irrigationLayer] = irrigation.NH4;
                // CL[irrigationLayer] = irrigation.Cl;
            }

            // Saturated flow.
            Flux = saturatedFlow.Values;

            // Add backed up water to runoff.
            Water[0] = Water[0] - saturatedFlow.backedUpSurface;

            // Now reduce the infiltration amount by what backed up.
            Infiltration = Infiltration - saturatedFlow.backedUpSurface;

            // Turn the proportion of the infiltration that backed up into runoff.
            Runoff = Runoff + saturatedFlow.backedUpSurface;

            // Should go to pond if one exists.
            //  pond = Math.Min(Runoff, max_pond);
            MoveDown(Water, Flux);

            double[] NO3 = soilNitrogen.NO3;
            double[] NH4 = soilNitrogen.NH4;

            // Calcualte solute movement down with water.
            double[] NO3Down = CalculateSoluteMovementDown(NO3, Water, Flux, SoluteFluxEfficiency);
            double[] NH4Down = CalculateSoluteMovementDown(NH4, Water, Flux, SoluteFluxEfficiency);
            MoveDown(NO3, NO3Down);
            MoveDown(NH4, NH4Down);

            double es = evaporationModel.Calculate();

            Water[0] = Water[0] - es;

            Flow = unsaturatedFlow.Values;
            MoveUp(Water, Flow);

            CheckForErrors();

            double waterTableDepth = waterTableModel.Value;

            double[] NO3Up = CalculateSoluteMovementUpDown(soilNitrogen.NO3, Water, Flow, SoluteFlowEfficiency);
            double[] NH4Up = CalculateSoluteMovementUpDown(soilNitrogen.NH4, Water, Flow, SoluteFlowEfficiency);
            MoveUp(NO3, NO3Up);
            MoveUp(NH4, NH4Up);

            // Send NitrogenChanged event.
            SendNitrogenChangedEvent(NO3, NH4);

            ResidueInterception = 0;
            CanopyInterception  = 0;
        }
Exemplo n.º 19
0
        /// <summary>Converts the paddock XML.</summary>
        /// <param name="paddock">The paddock.</param>
        /// <exception cref="System.Exception">Bad paddock name:  + name</exception>
        private static Paddock CreateSimulationSpec(XmlNode paddock, string baseFolder)
        {
            Paddock simulation = new Paddock();

            string name      = XmlUtilities.NameAttr(paddock);
            string delimiter = "^";
            int    posCaret  = name.IndexOf(delimiter);

            if (posCaret == -1)
            {
                throw new Exception("Bad paddock name: " + name);
            }

            string remainder = StringUtilities.SplitOffAfterDelimiter(ref name, delimiter);
            string growerName;
            string paddockName = StringUtilities.SplitOffAfterDelimiter(ref remainder, delimiter);

            if (paddockName == string.Empty)
            {
                growerName  = name;
                paddockName = remainder;
            }
            else
            {
                growerName = remainder;
            }

            // Give the paddock a name.
            string fullName = string.Format("{0}^{1}^{2}", GetDate(paddock, "SowDateFull").Year, growerName, paddockName);

            simulation.Name = fullName;

            // Set the report date.
            simulation.NowDate = GetDate(paddock.ParentNode, "TodayDateFull");
            if (simulation.NowDate == DateTime.MinValue)
            {
                simulation.NowDate = DateTime.Now;
            }

            // Store any rainfall data in the simulation.
            string rainfallSource = GetString(paddock, "RainfallSource");

            if (rainfallSource != "Weather station")
            {
                string rainFileName = GetString(paddock, "RainfallFilename");
                if (rainFileName != string.Empty)
                {
                    string fullFileName = Path.Combine(baseFolder, rainFileName);
                    if (!File.Exists(fullFileName))
                    {
                        throw new Exception("Cannot find file: " + fullFileName);
                    }
                    simulation.ObservedData           = ApsimTextFile.ToTable(fullFileName);
                    simulation.ObservedData.TableName = rainfallSource;
                    if (simulation.ObservedData.Rows.Count == 0)
                    {
                        simulation.ObservedData = null;
                    }
                }
            }
            // Set the reset dates
            simulation.SoilWaterSampleDate    = GetDate(paddock, "ResetDateFull");
            simulation.SoilNitrogenSampleDate = GetDate(paddock, "SoilNitrogenSampleDateFull");

            simulation.StationNumber = GetInteger(paddock, "StationNumber");
            simulation.StationName   = GetString(paddock, "StationName");

            // Create a sowing management
            Sow sowing = new Sow();

            simulation.Management.Add(sowing);
            sowing.Date          = GetDate(paddock, "SowDateFull");
            sowing.EmergenceDate = GetDate(paddock, "EmergenceDateFull");
            sowing.Crop          = GetString(paddock, "Crop");
            sowing.Cultivar      = GetString(paddock, "Cultivar");
            sowing.SkipRow       = GetString(paddock, "SkipRow");
            sowing.SowingDensity = GetInteger(paddock, "SowingDensity");
            sowing.MaxRootDepth  = GetInteger(paddock, "MaxRootDepth") * 10; // cm to mm
            sowing.BedWidth      = GetInteger(paddock, "BedWidth");
            sowing.BedRowSpacing = GetDouble(paddock, "BedRowSpacing");

            // Make sure we have a stubbletype
            simulation.StubbleType = GetString(paddock, "StubbleType");
            if (simulation.StubbleType == string.Empty || simulation.StubbleType == "None")
            {
                simulation.StubbleType = "Wheat";
            }
            simulation.StubbleMass = GetDouble(paddock, "StubbleMass");
            simulation.Slope       = GetDouble(paddock, "Slope");
            simulation.SlopeLength = GetDouble(paddock, "SlopeLength");
            simulation.UseEC       = GetBoolean(paddock, "UseEC");

            // Fertilise nodes.
            List <XmlNode> fertiliserNodes = XmlUtilities.ChildNodes(paddock, "Fertilise");

            for (int f = 0; f < fertiliserNodes.Count; f++)
            {
                Fertilise fertilise = new Fertilise();
                simulation.Management.Add(fertilise);
                fertilise.Date   = GetDate(fertiliserNodes[f], "FertDateFull");
                fertilise.Amount = GetDouble(fertiliserNodes[f], "FertAmount");
            }

            // Irrigate nodes.
            List <XmlNode> irrigateNodes = XmlUtilities.ChildNodes(paddock, "Irrigate");

            for (int i = 0; i < irrigateNodes.Count; i++)
            {
                Irrigate irrigate = new Irrigate();
                simulation.Management.Add(irrigate);
                irrigate.Date       = GetDate(irrigateNodes[i], "IrrigateDateFull");
                irrigate.Amount     = GetDouble(irrigateNodes[i], "IrrigateAmount");
                irrigate.Efficiency = GetDouble(irrigateNodes[i], "IrrigateEfficiency");
            }

            // Tillage nodes.
            foreach (XmlNode tillageNode in XmlUtilities.ChildNodes(paddock, "Tillage"))
            {
                Tillage tillage = new Tillage();
                simulation.Management.Add(tillage);
                tillage.Date = GetDate(tillageNode, "TillageDateFull");
                string disturbance = GetString(tillageNode, "Disturbance");
                if (disturbance == "Low")
                {
                    tillage.Disturbance = Tillage.DisturbanceEnum.Low;
                }
                else if (disturbance == "Medium")
                {
                    tillage.Disturbance = Tillage.DisturbanceEnum.Medium;
                }
                else
                {
                    tillage.Disturbance = Tillage.DisturbanceEnum.High;
                }
            }

            // Stubble removed nodes.
            foreach (XmlNode stubbleRemovedNode in XmlUtilities.ChildNodes(paddock, "StubbleRemoved"))
            {
                StubbleRemoved stubbleRemoved = new StubbleRemoved();
                simulation.Management.Add(stubbleRemoved);

                stubbleRemoved.Date    = GetDate(stubbleRemovedNode, "StubbleRemovedDateFull");
                stubbleRemoved.Percent = GetDouble(stubbleRemovedNode, "StubbleRemovedAmount");
            }

            // Look for a soil node.
            XmlNode soilNode = XmlUtilities.FindByType(paddock, "Soil");

            if (soilNode != null)
            {
                string testValue = XmlUtilities.Value(soilNode, "Water/Layer/Thickness");
                if (testValue != string.Empty)
                {
                    // old format.
                    soilNode = ConvertSoilNode.Upgrade(soilNode);
                }
            }

            // Fix up soil sample variables.
            Sample sample1 = new Sample
            {
                Name      = "Sample1",
                Thickness = GetArray(paddock, "Sample1Thickness"),
                SW        = GetArray(paddock, "SW")
            };

            if (sample1.SW == null)
            {
                // Really old way of doing samples - they are stored under soil.
                List <XmlNode> sampleNodes = XmlUtilities.ChildNodes(soilNode, "Sample");
                if (sampleNodes.Count > 0)
                {
                    sample1 = XmlUtilities.Deserialise(sampleNodes[0], typeof(Sample)) as Sample;
                }
            }
            else
            {
                sample1.NO3 = GetArray(paddock, "NO3");
                sample1.NH4 = GetArray(paddock, "NH4");
            }

            Sample sample2 = null;

            double[] sample2Thickness = GetArray(paddock, "Sample2Thickness");
            if (sample2Thickness == null)
            {
                // Really old way of doing samples - they are stored under soil.
                List <XmlNode> sampleNodes = XmlUtilities.ChildNodes(soilNode, "Sample");
                if (sampleNodes.Count > 1)
                {
                    sample2 = XmlUtilities.Deserialise(sampleNodes[1], typeof(Sample)) as Sample;
                }
            }
            else
            {
                sample2 = sample1;
                if (!MathUtilities.AreEqual(sample2Thickness, sample1.Thickness))
                {
                    sample2 = new Sample
                    {
                        Name = "Sample2"
                    };
                }
                sample2.OC      = GetArray(paddock, "OC");
                sample2.EC      = GetArray(paddock, "EC");
                sample2.PH      = GetArray(paddock, "PH");
                sample2.CL      = GetArray(paddock, "CL");
                sample2.OCUnits = SoilOrganicMatter.OCUnitsEnum.WalkleyBlack;
                sample2.PHUnits = Analysis.PHUnitsEnum.CaCl2;
            }

            // Make sure we have NH4 values.
            if (sample1.NH4 == null && sample1.NO3 != null)
            {
                string[] defaultValues = StringUtilities.CreateStringArray("0.1", sample1.NO3.Length);
                sample1.NH4 = MathUtilities.StringsToDoubles(defaultValues);
            }

            RemoveNullFieldsFromSample(sample1);
            if (sample2 != null)
            {
                RemoveNullFieldsFromSample(sample2);
            }


            // Fix up <WaterFormat>
            string waterFormatString = GetString(paddock, "WaterFormat");

            if (waterFormatString.Contains("Gravimetric"))
            {
                sample1.SWUnits = Sample.SWUnitsEnum.Gravimetric;
            }
            else
            {
                sample1.SWUnits = Sample.SWUnitsEnum.Volumetric;
            }

            if (MathUtilities.ValuesInArray(sample1.Thickness))
            {
                simulation.Samples.Add(sample1);
            }

            if (sample2 != null && MathUtilities.ValuesInArray(sample2.Thickness) && sample2 != sample1)
            {
                simulation.Samples.Add(sample2);
            }

            // Check for InitTotalWater & InitTotalNitrogen
            simulation.InitTotalWater    = GetDouble(paddock, "InitTotalWater");
            simulation.InitTotalNitrogen = GetDouble(paddock, "InitTotalNitrogen");

            // Check to see if we need to convert the soil structure.
            simulation.SoilPath = GetString(paddock, "SoilName");


            if (soilNode != null)
            {
                // See if there is a 'SWUnits' value. If found then copy it into
                // <WaterFormat>
                string waterFormat = XmlUtilities.Value(paddock, "WaterFormat");
                if (waterFormat == string.Empty)
                {
                    int sampleNumber = 0;
                    foreach (XmlNode soilSample in XmlUtilities.ChildNodes(soilNode, "Sample"))
                    {
                        string swUnits = XmlUtilities.Value(soilSample, "SWUnits");
                        if (swUnits != string.Empty)
                        {
                            XmlUtilities.SetValue(paddock, "WaterFormat", swUnits);
                        }

                        // Also make sure we don't have 2 samples with the same name.
                        string sampleName = "Sample" + (sampleNumber + 1).ToString();
                        XmlUtilities.SetAttribute(soilSample, "name", sampleName);
                        sampleNumber++;
                    }
                }
                simulation.Soil = SoilUtilities.FromXML(soilNode.OuterXml);
                if (simulation.Samples != null)
                {
                    simulation.Soil.Samples = simulation.Samples;
                }
            }
            return(simulation);
        }
Exemplo n.º 20
0
        /// <summary>
        /// Attach the model
        /// </summary>
        public void AttachData()
        {
            if (!(forestryModel.Parent is AgroforestrySystem))
            {
                throw new ApsimXException(forestryModel, "Error: TreeProxy must be a child of ForestrySystem.");
            }

            IEnumerable <Zone> zones = forestryModel.Parent.FindAllDescendants <Zone>();

            if (!zones.Any())
            {
                return;
            }

            // Setup tree heights.
            forestryViewer.SetupHeights(forestryModel.Dates, forestryModel.Heights, forestryModel.NDemands, forestryModel.ShadeModifiers);

            // Get the first soil. For now we're assuming all soils have the same structure.
            var physical = zones.First().FindInScope <Physical>();

            forestryViewer.SoilMidpoints = physical.DepthMidPoints;

            // Setup columns.
            List <string> colNames = new List <string>();

            colNames.Add("Parameter");
            colNames.Add("0");
            colNames.Add("0.5h");
            colNames.Add("1h");
            colNames.Add("1.5h");
            colNames.Add("2h");
            colNames.Add("2.5h");
            colNames.Add("3h");
            colNames.Add("4h");
            colNames.Add("5h");
            colNames.Add("6h");

            if (forestryModel.Table.Count == 0)
            {
                forestryModel.Table = new List <List <string> >();
                forestryModel.Table.Add(colNames);

                // Setup rows.
                List <string> rowNames = new List <string>();

                rowNames.Add("Shade (%)");
                rowNames.Add("Root Length Density (cm/cm3)");
                rowNames.Add("Depth (cm)");

                foreach (string s in SoilUtilities.ToDepthStrings(physical.Thickness))
                {
                    rowNames.Add(s);
                }

                forestryModel.Table.Add(rowNames);
                for (int i = 2; i < colNames.Count + 1; i++)
                {
                    forestryModel.Table.Add(Enumerable.Range(1, rowNames.Count).Select(x => "0").ToList());
                }

                for (int i = 2; i < forestryModel.Table.Count; i++)
                {
                    // Set Depth and RLD rows to empty strings.
                    forestryModel.Table[i][1] = string.Empty;
                    forestryModel.Table[i][2] = string.Empty;
                }
            }
            else
            {
                // add Zones not in the table
                IEnumerable <string> except = colNames.Except(forestryModel.Table[0]);
                foreach (string s in except)
                {
                    forestryModel.Table.Add(Enumerable.Range(1, forestryModel.Table[1].Count).Select(x => "0").ToList());
                }

                forestryModel.Table[0].AddRange(except);
                for (int i = 2; i < forestryModel.Table.Count; i++)
                {
                    // Set Depth and RLD rows to empty strings.
                    forestryModel.Table[i][2] = string.Empty;
                }

                // Remove Zones from table that don't exist in simulation.
                except = forestryModel.Table[0].Except(colNames);
                List <int> indexes = new List <int>();
                foreach (string s in except.ToArray())
                {
                    indexes.Add(forestryModel.Table[0].FindIndex(x => s == x));
                }

                indexes.Sort();
                indexes.Reverse();

                foreach (int i in indexes)
                {
                    forestryModel.Table[0].RemoveAt(i);
                    forestryModel.Table.RemoveAt(i + 1);
                }
            }
            forestryViewer.SpatialData = forestryModel.Table;
        }
Exemplo n.º 21
0
 private void OnPlantSowing(object sender, SowingParameters data)
 {
     SowLayer = SoilUtilities.LayerIndexOfDepth(soilPhysical.Thickness, plant.SowingData.Depth);
 }
Exemplo n.º 22
0
        private void OnDoSoilWaterMovement(object sender, EventArgs e)
        {
            // Calculate lateral flow.
            lateralFlowModel.Calculate();
            if (LateralFlow.Length > 0)
            {
                Water = MathUtilities.Subtract(Water, LateralFlow);
            }

            // Calculate runoff.
            Runoff = runoffModel.Value();

            // Calculate infiltration.
            Infiltration = PotentialInfiltration - Runoff;

            Water[0] = Water[0] + Infiltration + Runon;

            // Allow irrigation to infiltrate.
            foreach (var irrigation in irrigations)
            {
                if (irrigation.Amount > 0)
                {
                    int irrigationLayer = SoilUtilities.LayerIndexOfDepth(soilPhysical.Thickness, Convert.ToInt32(irrigation.Depth, CultureInfo.InvariantCulture));
                    Water[irrigationLayer] += irrigation.Amount;
                    if (irrigationLayer == 0)
                    {
                        Infiltration += irrigation.Amount;
                    }

                    if (no3 != null)
                    {
                        no3.kgha[irrigationLayer] += irrigation.NO3;
                    }

                    if (nh4 != null)
                    {
                        nh4.kgha[irrigationLayer] += irrigation.NH4;
                    }

                    if (cl != null)
                    {
                        cl.kgha[irrigationLayer] += irrigation.CL;
                    }
                }
            }

            // Saturated flow.
            Flux = saturatedFlow.Values;

            // Add backed up water to runoff.
            Water[0] = Water[0] - saturatedFlow.backedUpSurface;

            // Now reduce the infiltration amount by what backed up.
            Infiltration = Infiltration - saturatedFlow.backedUpSurface;

            // Turn the proportion of the infiltration that backed up into runoff.
            Runoff = Runoff + saturatedFlow.backedUpSurface;

            // Should go to pond if one exists.
            //  pond = Math.Min(Runoff, max_pond);
            MoveDown(Water, Flux);

            double[] no3Values  = no3.kgha;
            double[] ureaValues = urea.kgha;

            // Calcualte solute movement down with water.
            double[] no3Down = CalculateSoluteMovementDown(no3Values, Water, Flux, SoluteFluxEfficiency);
            MoveDown(no3Values, no3Down);
            double[] ureaDown = CalculateSoluteMovementDown(ureaValues, Water, Flux, SoluteFluxEfficiency);
            MoveDown(ureaValues, ureaDown);

            // Calculate evaporation and remove from top layer.
            double es = evaporationModel.Calculate();

            Water[0] = Water[0] - es;

            // Calculate unsaturated flow of water and apply.
            Flow = unsaturatedFlow.Values;
            MoveUp(Water, Flow);

            // Check for errors in water variables.
            //CheckForErrors();

            // Calculate water table depth.
            waterTableModel.Calculate();

            // Calculate and apply net solute movement.
            double[] no3Up = CalculateNetSoluteMovement(no3Values, Water, Flow, SoluteFlowEfficiency);
            MoveUp(no3Values, no3Up);
            double[] ureaUp = CalculateNetSoluteMovement(ureaValues, Water, Flow, SoluteFlowEfficiency);
            MoveUp(ureaValues, ureaUp);

            // Update flow output variables.
            FlowNO3  = MathUtilities.Subtract(no3Down, no3Up);
            FlowUrea = MathUtilities.Subtract(ureaDown, ureaUp);

            // Set solute state variables.
            no3.SetKgHa(SoluteSetterType.Soil, no3Values);
            urea.SetKgHa(SoluteSetterType.Soil, ureaValues);

            // Now that we've finished moving water, calculate volumetric water
            waterVolumetric = MathUtilities.Divide(Water, soilPhysical.Thickness);
        }