Exemplo n.º 1
0
        /// <summary>Perform the movement of water.</summary>
        public void Calculate()
        {
            // Lateral flow does not move solutes. We should add this feature one day.
            if (InFlow != null)
            {
                if (OutFlow.Length != InFlow.Length)
                {
                    OutFlow = new double[InFlow.Length];
                }
                double[] SW  = MathUtilities.Add(soil.Water, InFlow);
                double[] DUL = MathUtilities.Multiply(soilPhysical.DUL, soilPhysical.Thickness);
                double[] SAT = MathUtilities.Multiply(soilPhysical.SAT, soilPhysical.Thickness);

                for (int layer = 0; layer < soilPhysical.Thickness.Length; layer++)
                {
                    // Calculate depth of water table (m)
                    double depthWaterTable = soilPhysical.Thickness[layer] * MathUtilities.Divide((SW[layer] - DUL[layer]), (SAT[layer] - DUL[layer]), 0.0);
                    depthWaterTable = Math.Max(0.0, depthWaterTable);  // water table depth in layer must be +ve

                    // Calculate out flow (mm)
                    double i, j;
                    i = soil.KLAT[layer] * depthWaterTable * (soil.DischargeWidth / UnitConversion.mm2m) * soil.Slope;
                    j = (soil.CatchmentArea * UnitConversion.sm2smm) * (Math.Pow((1.0 + Math.Pow(soil.Slope, 2)), 0.5));
                    OutFlow[layer] = MathUtilities.Divide(i, j, 0.0);

                    // Bound out flow to max flow
                    double max_flow = Math.Max(0.0, (SW[layer] - DUL[layer]));
                    OutFlow[layer] = MathUtilities.Bound(OutFlow[layer], 0.0, max_flow);
                }
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Scale the water and n values if the total uptake exceeds the amounts available.
        /// </summary>
        /// <param name="zones">List of zones to check.</param>
        /// <param name="uptakes">List of all potential uptakes</param>
        private static void ScaleWaterAndNIfNecessary(List <ZoneWaterAndN> zones, List <ZoneWaterAndN> uptakes)
        {
            foreach (ZoneWaterAndN uniqueZone in zones)
            {
                double[] totalWaterUptake = new double[uniqueZone.Water.Length];
                double[] totalNO3Uptake   = new double[uniqueZone.Water.Length];
                double[] totalNH4Uptake   = new double[uniqueZone.Water.Length];
                foreach (ZoneWaterAndN zone in uptakes)
                {
                    if (zone.Zone == uniqueZone.Zone)
                    {
                        totalWaterUptake = MathUtilities.Add(totalWaterUptake, zone.Water);
                        totalNO3Uptake   = MathUtilities.Add(totalNO3Uptake, zone.NO3N);
                        totalNH4Uptake   = MathUtilities.Add(totalNH4Uptake, zone.NH4N);
                    }
                }

                for (int i = 0; i < uniqueZone.Water.Length; i++)
                {
                    if (uniqueZone.Water[i] < totalWaterUptake[i] && totalWaterUptake[i] > 0)
                    {
                        // Scale water
                        double scale = uniqueZone.Water[i] / totalWaterUptake[i];
                        foreach (ZoneWaterAndN zone in uptakes)
                        {
                            if (zone.Zone == uniqueZone.Zone)
                            {
                                zone.Water[i] *= scale;
                            }
                        }
                    }

                    if (uniqueZone.NO3N[i] < totalNO3Uptake[i] && totalNO3Uptake[i] > 0)
                    {
                        // Scale NO3
                        double scale = uniqueZone.NO3N[i] / totalNO3Uptake[i];
                        foreach (ZoneWaterAndN zone in uptakes)
                        {
                            if (zone.Zone == uniqueZone.Zone)
                            {
                                zone.NO3N[i] *= scale;
                            }
                        }
                    }

                    if (uniqueZone.NH4N[i] < totalNH4Uptake[i] && totalNH4Uptake[i] > 0)
                    {
                        // Scale NH4
                        double scale = uniqueZone.NH4N[i] / totalNH4Uptake[i];
                        foreach (ZoneWaterAndN zone in uptakes)
                        {
                            if (zone.Zone == uniqueZone.Zone)
                            {
                                zone.NH4N[i] *= scale;
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Set the n uptake for today
        /// </summary>
        public void SetActualNitrogenUptakes(List <ZoneWaterAndN> info)
        {
            NO3Uptake      = info[0].NO3N;
            NH4Uptake      = info[0].NH4N;
            NitrogenUptake = MathUtilities.Add(NO3Uptake, NH4Uptake);

            NO3.SetKgHa(SoluteSetterType.Plant, MathUtilities.Subtract(NO3.kgha, NO3Uptake));
            NH4.SetKgHa(SoluteSetterType.Plant, MathUtilities.Subtract(NH4.kgha, NH4Uptake));
        }
Exemplo n.º 4
0
        public void FailingTest()
        {
            int a           = 2;
            int b           = 2;
            int expectedSum = 5;
            int actualSum   = MathUtilities.Add(a, b);

            Assert.AreEqual(expectedSum, actualSum);
        }
Exemplo n.º 5
0
        /// <summary>Calculate Nitrogen UptakeEstimates</summary>
        public List <ZoneWaterAndN> GetUptakeEstimates(SoilState soilstate, IArbitration[] Organs)
        {
            var    N       = Arbitrator.N;
            double NSupply = 0;//NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.

            for (int i = 0; i < Organs.Count(); i++)
            {
                N.UptakeSupply[i] = 0;
            }

            List <ZoneWaterAndN> zones = new List <ZoneWaterAndN>();

            foreach (ZoneWaterAndN zone in soilstate.Zones)
            {
                ZoneWaterAndN UptakeDemands = new ZoneWaterAndN(zone);

                UptakeDemands.NO3N  = new double[zone.NO3N.Length];
                UptakeDemands.NH4N  = new double[zone.NH4N.Length];
                UptakeDemands.Water = new double[UptakeDemands.NO3N.Length];

                //Get Nuptake supply from each organ and set the PotentialUptake parameters that are passed to the soil arbitrator
                for (int i = 0; i < Organs.Count(); i++)
                {
                    if (Organs[i] is IWaterNitrogenUptake)
                    {
                        double[] organNO3Supply = new double[zone.NO3N.Length];
                        double[] organNH4Supply = new double[zone.NH4N.Length];
                        (Organs[i] as IWaterNitrogenUptake).CalculateNitrogenSupply(zone, ref organNO3Supply, ref organNH4Supply);
                        UptakeDemands.NO3N = MathUtilities.Add(UptakeDemands.NO3N, organNO3Supply); //Add uptake supply from each organ to the plants total to tell the Soil arbitrator
                        UptakeDemands.NH4N = MathUtilities.Add(UptakeDemands.NH4N, organNH4Supply);
                        double organSupply = organNH4Supply.Sum() + organNO3Supply.Sum();
                        N.UptakeSupply[i] += organSupply * kgha2gsm * zone.Zone.Area / this.zone.Area;
                        NSupply           += organSupply * zone.Zone.Area;
                    }
                }
                zones.Add(UptakeDemands);
            }

            double NDemand = (N.TotalPlantDemand - N.TotalReallocation) / kgha2gsm * zone.Area; //NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.

            if (NDemand < 0)
            {
                NDemand = 0;               //NSupply should be zero if Reallocation can meet all demand (including small rounding errors which can make this -ve)
            }
            if (NSupply > NDemand)
            {
                //Reduce the PotentialUptakes that we pass to the soil arbitrator
                double ratio = Math.Min(1.0, NDemand / NSupply);
                foreach (ZoneWaterAndN UptakeDemands in zones)
                {
                    UptakeDemands.NO3N = MathUtilities.Multiply_Value(UptakeDemands.NO3N, ratio);
                    UptakeDemands.NH4N = MathUtilities.Multiply_Value(UptakeDemands.NH4N, ratio);
                }
            }
            return(zones);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Set the value of a solute by specifying a delta. Will throw if solute not found.
        /// </summary>
        /// <param name="name">Name of solute</param>
        /// <param name="delta">Delta values to be added to solute</param>
        public void Add(string name, double[] delta)
        {
            Solute foundSolute = solutes.Find(solute => solute.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));

            if (foundSolute == null)
            {
                throw new Exception("Cannot find solute: " + name);
            }
            foundSolute.Value = MathUtilities.Add(foundSolute.Value, delta);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Calculate the potential N uptake for today. Should return null if crop is not in the ground.
        /// </summary>
        public virtual List <Soils.Arbitrator.ZoneWaterAndN> GetNitrogenUptakeEstimates(SoilState soilstate)
        {
            if (Plant.IsEmerged)
            {
                double NSupply = 0;//NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.

                for (int i = 0; i < Organs.Count; i++)
                {
                    N.UptakeSupply[i] = 0;
                }

                List <ZoneWaterAndN> zones = new List <ZoneWaterAndN>();
                foreach (ZoneWaterAndN zone in soilstate.Zones)
                {
                    ZoneWaterAndN UptakeDemands = new ZoneWaterAndN(zone);

                    UptakeDemands.NO3N = new double[zone.NO3N.Length];
                    UptakeDemands.NH4N = new double[zone.NH4N.Length];
                    UptakeDemands.PlantAvailableNO3N = new double[zone.NO3N.Length];
                    UptakeDemands.PlantAvailableNH4N = new double[zone.NO3N.Length];
                    UptakeDemands.Water = new double[UptakeDemands.NO3N.Length];

                    //Get Nuptake supply from each organ and set the PotentialUptake parameters that are passed to the soil arbitrator
                    for (int i = 0; i < Organs.Count; i++)
                    {
                        if (Organs[i] is IWaterNitrogenUptake)
                        {
                            double[] organNO3Supply = new double[zone.NO3N.Length];
                            double[] organNH4Supply = new double[zone.NH4N.Length];
                            (Organs[i] as IWaterNitrogenUptake).CalculateNitrogenSupply(zone, ref organNO3Supply, ref organNH4Supply);
                            UptakeDemands.NO3N = MathUtilities.Add(UptakeDemands.NO3N, organNO3Supply); //Add uptake supply from each organ to the plants total to tell the Soil arbitrator
                            UptakeDemands.NH4N = MathUtilities.Add(UptakeDemands.NH4N, organNH4Supply);
                            N.UptakeSupply[i] += (MathUtilities.Sum(organNH4Supply) + MathUtilities.Sum(organNO3Supply)) * kgha2gsm * zone.Zone.Area / Plant.Zone.Area;
                            NSupply           += (MathUtilities.Sum(organNH4Supply) + MathUtilities.Sum(organNO3Supply)) * zone.Zone.Area;
                        }
                    }
                    zones.Add(UptakeDemands);
                }

                double NDemand = (N.TotalPlantDemand - N.TotalReallocation) / kgha2gsm * Plant.Zone.Area; //NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.

                if (NSupply > NDemand)
                {
                    //Reduce the PotentialUptakes that we pass to the soil arbitrator
                    double ratio = Math.Min(1.0, NDemand / NSupply);
                    foreach (ZoneWaterAndN UptakeDemands in zones)
                    {
                        UptakeDemands.NO3N = MathUtilities.Multiply_Value(UptakeDemands.NO3N, ratio);
                        UptakeDemands.NH4N = MathUtilities.Multiply_Value(UptakeDemands.NH4N, ratio);
                    }
                }
                return(zones);
            }
            return(null);
        }
Exemplo n.º 8
0
        /// <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.Zone);

            NewZ.Water = MathUtilities.Add(ZWN1.Water, ZWN2.Water);
            NewZ.NO3N  = MathUtilities.Add(ZWN1.NO3N, ZWN2.NO3N);
            NewZ.NH4N  = MathUtilities.Add(ZWN1.NH4N, ZWN2.NH4N);
            return(NewZ);
        }
Exemplo n.º 9
0
        /// <summary>Does the Nitrogen uptake.</summary>
        /// <param name="zonesFromSoilArbitrator">List of zones from soil arbitrator</param>
        public void DoNitrogenUptake(List <ZoneWaterAndN> zonesFromSoilArbitrator)
        {
            foreach (ZoneWaterAndN thisZone in zonesFromSoilArbitrator)
            {
                ZoneState zone = Zones.Find(z => z.Name == thisZone.Zone.Name);
                if (zone != null)
                {
                    zone.solutes.Subtract("NO3", SoluteManager.SoluteSetterType.Plant, thisZone.NO3N);
                    zone.solutes.Subtract("NH4", SoluteManager.SoluteSetterType.Plant, thisZone.NH4N);

                    zone.NitUptake = MathUtilities.Multiply_Value(MathUtilities.Add(thisZone.NO3N, thisZone.NH4N), -1);
                }
            }
        }
Exemplo n.º 10
0
Arquivo: Root.cs Projeto: ndb01/ApsimX
        /// <summary>Does the Nitrogen uptake.</summary>
        /// <param name="NO3NAmount">The NO3NAmount.</param>
        /// <param name="NH4NAmount">The NH4NAmount.</param>
        public override void DoNitrogenUptake(double[] NO3NAmount, double[] NH4NAmount)
        {
            // Send the delta water back to SoilN that we're going to uptake.
            NitrogenChangedType NitrogenUptake = new NitrogenChangedType();

            NitrogenUptake.DeltaNO3 = MathUtilities.Multiply_Value(NO3NAmount, -1.0);
            NitrogenUptake.DeltaNH4 = MathUtilities.Multiply_Value(NH4NAmount, -1.0);

            NitUptake = MathUtilities.Add(NitrogenUptake.DeltaNO3, NitrogenUptake.DeltaNH4);
            if (NitrogenChanged != null)
            {
                NitrogenChanged.Invoke(NitrogenUptake);
            }
        }
Exemplo n.º 11
0
        /// <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);
        }
Exemplo n.º 12
0
        // get curvature at interior points of (x,y)
        private static double[] curv(int n, double[] x, double[] y)
        {
            double[] s  = new double[n - 1];
            double[] yl = new double[n - 1];

            double[] ySub = MathUtilities.Subtract(y.Slice(3, n), y.Slice(1, n - 2));
            double[] xSub = MathUtilities.Subtract(x.Slice(3, n), x.Slice(1, n - 2));

            s  = MathUtilities.Divide(ySub, xSub);
            yl = MathUtilities.Add(y.Slice(1, n - 2),
                                   MathUtilities.Multiply(MathUtilities.Subtract(x.Slice(2, n - 1),
                                                                                 x.Slice(1, n - 2)),
                                                          s));
            double[] ySlice = y.Slice(2, n - 1);
            return(MathUtilities.Subtract_Value(MathUtilities.Divide(ySlice, yl), 1));
        }
Exemplo n.º 13
0
        // get curvature at interior points of (x,y)
        private static double[] curv(int n, double[] x, double[] y)
        {
            double[]        c  = new double[n - 2];
            double[]        s  = new double[n - 2];
            double[]        yl = new double[n - 2];
            Vector <double> xV = Vector <double> .Build.DenseOfArray(x);

            Vector <double> yV = Vector <double> .Build.DenseOfArray(y);

            s  = MathUtilities.Divide(MathUtilities.Subtract(yV.SubVector(2, n - 1).ToArray(), yV.SubVector(0, n - 3).ToArray()), MathUtilities.Subtract(xV.SubVector(2, n - 1).ToArray(), xV.SubVector(0, n - 3).ToArray()));
            yl = MathUtilities.Add(yV.SubVector(0, n - 3).ToArray(),
                                   MathUtilities.Multiply(MathUtilities.Subtract(xV.SubVector(1, n - 2).ToArray(),
                                                                                 xV.SubVector(0, n - 3).ToArray()),
                                                          s));
            return(MathUtilities.Subtract_Value(MathUtilities.Divide(yV.SubVector(1, n - 2).ToArray(), yl), 1));
        }
Exemplo n.º 14
0
 /// <summary>
 /// Called by solutes to get the value of a solute.
 /// </summary>
 /// <param name="name">The name of the solute to get.</param>
 /// <returns></returns>
 internal double[] GetSoluteKgha(string name)
 {
     double[] values = null;
     foreach (var patch in patches)
     {
         if (values == null)
         {
             values = patch.GetSoluteKgHaRelativeArea(name);
         }
         else
         {
             values = MathUtilities.Add(values, patch.GetSoluteKgHaRelativeArea(name));
         }
     }
     return(values);
 }
Exemplo n.º 15
0
        /// <summary>Does the Nitrogen uptake.</summary>
        /// <param name="zonesFromSoilArbitrator">List of zones from soil arbitrator</param>
        public void DoNitrogenUptake(List <ZoneWaterAndN> zonesFromSoilArbitrator)
        {
            foreach (ZoneWaterAndN thisZone in zonesFromSoilArbitrator)
            {
                ZoneState zone = Zones.Find(z => z.Name == thisZone.Zone.Name);
                if (zone != null)
                {
                    // Send the delta water back to SoilN that we're going to uptake.
                    NitrogenChangedType NitrogenUptake = new NitrogenChangedType();
                    NitrogenUptake.DeltaNO3 = MathUtilities.Multiply_Value(thisZone.NO3N, -1.0);
                    NitrogenUptake.DeltaNH4 = MathUtilities.Multiply_Value(thisZone.NH4N, -1.0);

                    zone.NitUptake = MathUtilities.Add(NitrogenUptake.DeltaNO3, NitrogenUptake.DeltaNH4);
                    zone.soil.SoilNitrogen.SetNitrogenChanged(NitrogenUptake);
                }
            }
        }
Exemplo n.º 16
0
        private static double[] odef(int n1, int n2, double[] aK, double[] hpK)
        {
            double[] u = new double[3];
            int      np;

            double[] da = new double[n2 - n1 + 2];
            double[] db = new double[n2 - n1 + 1];
            // Get z and dz/dq for flux q and phi from aphi(n1) to aphi(n2).
            // q is global to subroutine fluxtbl.
            np = n2 - n1 + 1;
            double[] daTemp = MathUtilities.Subtract_Value(aK.Slice(n1, n2), q);
            double[] dbTemp = MathUtilities.Subtract_Value(hpK.Slice(n1, n2 - 1), q);
            diags.AppendLine("n1: " + n1 + Environment.NewLine + "n2: " + n2);

            for (int idx = 1; idx < da.Length; idx++)
            {
                da[idx] = 1.0 / daTemp[idx];
            }

            for (int idx = 1; idx < db.Length; idx++)
            {
                db[idx] = 1.0 / dbTemp[idx];
            }

            diags.Append("da: ");
            for (int i = 1; i < da.Length; i++)
            {
                diags.Append(" " + da[i]);
            }
            diags.AppendLine();

            // apply Simpson's rule
            // u[] = sum((aphi(n1+1:n2)-aphi(n1:n2-1))*(da(1:np-1)+4*db+da(2:np))/6). Love the C# implementation. Note aphi is now sp.phic
            u[1] = MathUtilities.Sum(MathUtilities.Divide_Value(MathUtilities.Multiply
                                                                    (MathUtilities.Subtract(sp.phic.Slice(n1 + 1, n2), sp.phic.Slice(n1, n2 - 1)),
                                                                    MathUtilities.Add(MathUtilities.Add(MathUtilities.Multiply_Value(db, 4), da.Slice(1, np - 1)), da.Slice(2, np))), 6)); // this is madness!
            da   = MathUtilities.Multiply(da, da);
            db   = MathUtilities.Multiply(db, db);
            u[2] = MathUtilities.Sum(MathUtilities.Divide_Value(MathUtilities.Multiply
                                                                    (MathUtilities.Subtract(sp.phic.Slice(n1 + 1, n2), sp.phic.Slice(n1, n2 - 1)),
                                                                    MathUtilities.Add(MathUtilities.Add(MathUtilities.Multiply_Value(db, 4), da.Slice(1, np - 1)), da.Slice(2, np))), 6));
            return(u);
        }
Exemplo n.º 17
0
        private static double[] odef(int n1, int n2, double[] aK, double[] hpK)
        {
            double[] u = new double[2];
            int      np;

            double[] da = new double[n2 - n1 + 1];
            double[] db = new double[n2 - n1];
            // Get z and dz/dq for flux q and phi from aphi(n1) to aphi(n2).
            // q is global to subroutine fluxtbl.
            np = n2 - n1 + 1;
            Vector <double> akV = Vector <double> .Build.DenseOfArray(aK);

            Vector <double> hpKV = Vector <double> .Build.DenseOfArray(hpK);

            double[] daTemp = MathUtilities.Subtract_Value(akV.SubVector(n1, n2 - n1 + 1).ToArray(), q);
            double[] dbTemp = MathUtilities.Subtract_Value(akV.SubVector(n1, n2 - n1).ToArray(), q);
            for (int idx = 0; idx < da.Length; idx++)
            {
                da[idx] = 1.0 / daTemp[idx];
                if (idx < db.Length)
                {
                    db[idx] = 1.0 / dbTemp[idx];
                }
            }

            // apply Simpson's rule
            Vector <double> aphiV = Vector <double> .Build.DenseOfArray(aphi);

            Vector <double> daV = Vector <double> .Build.DenseOfArray(da);

            // sum((aphi(n1+1:n2)-aphi(n1:n2-1))*(da(1:np-1)+4*db+da(2:np))/6) for both of these stupid lines... find something better.
            Vector <double> t1 = aphiV.SubVector(n1, n2 - n1 + 1);
            Vector <double> t2 = aphiV.SubVector(n1 - 1, n2 - n1 + 1);
            Vector <double> t3 = daV.SubVector(0, np);
            Vector <double> t4 = daV.SubVector(1, np - 1);

            //u[0] = MathUtilities.Sum(MathUtilities.Divide_Value(MathUtilities.Multiply(aphiV.SubVector(n1, n2 - n1 + 1).Subtract(aphiV.SubVector(n1 - 1, n2 - n1)).ToArray(), MathUtilities.Add(MathUtilities.Multiply(daV.SubVector(0, np).Add(4).ToArray(), db), daV.SubVector(1, np + 1).ToArray())), 6)); // this is madness!
            u[0] = MathUtilities.Sum(MathUtilities.Divide_Value(MathUtilities.Multiply(aphiV.SubVector(n1, n2 - n1 + 1).Subtract(aphiV.SubVector(n1 - 1, n2 - n1)).ToArray(), MathUtilities.Add(MathUtilities.Multiply(daV.SubVector(0, np).Add(4).ToArray(), db), daV.SubVector(1, np + 1).ToArray())), 6)); // this is madness!
            da   = MathUtilities.Multiply(da, da);
            db   = MathUtilities.Multiply(db, db);
            u[1] = MathUtilities.Sum(MathUtilities.Divide_Value(MathUtilities.Multiply(aphiV.SubVector(n1, n2 - n1 + 1).Subtract(aphiV.SubVector(n1 - 1, n2 - n1)).ToArray(), MathUtilities.Add(MathUtilities.Multiply(daV.SubVector(0, np).Add(4).ToArray(), db), daV.SubVector(1, np + 1).ToArray())), 6)); // this is madness!
            return(u);
        }
Exemplo n.º 18
0
        /// <summary>
        /// Calculate the potential N uptake for today. Should return null if crop is not in the ground.
        /// </summary>
        public override List <Soils.Arbitrator.ZoneWaterAndN> GetNitrogenUptakeEstimates(SoilState soilstate)
        {
            if (Plant.IsEmerged)
            {
                var nSupply = 0.0;//NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.

                //this function is called 4 times as part of estimates
                //shouldn't set public variables in here

                var grainIndex = 0;
                var rootIndex  = 1;
                var leafIndex  = 2;
                var stemIndex  = 4;

                var nGreen  = stem.Live.N;
                var dmGreen = stem.Live.Wt;
                var dltDM   = stem.potentialDMAllocation.Structural;

                var rootDemand  = N.StructuralDemand[rootIndex] + N.MetabolicDemand[rootIndex];
                var stemDemand  = N.StructuralDemand[stemIndex] + N.MetabolicDemand[stemIndex];
                var leafDemand  = N.MetabolicDemand[leafIndex];
                var grainDemand = N.StructuralDemand[grainIndex] + N.MetabolicDemand[grainIndex];
                //have to correct the leaf demand calculation
                var leaf           = Organs[leafIndex] as SorghumLeaf;
                var leafAdjustment = leaf.calculateClassicDemandDelta();

                //double NDemand = (N.TotalPlantDemand - N.TotalReallocation) / kgha2gsm * Plant.Zone.Area; //NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.
                //old sorghum uses g/m^2 - need to convert after it is used to calculate actual diffusion
                // leaf adjustment is not needed here because it is an adjustment for structural demand - we only look at metabolic here.
                var nDemand = Math.Max(0, N.TotalMetabolicDemand - grainDemand); // to replicate calcNDemand in old sorghum

                for (int i = 0; i < Organs.Count; i++)
                {
                    N.UptakeSupply[i] = 0;
                }
                List <ZoneWaterAndN> zones = new List <ZoneWaterAndN>();
                foreach (ZoneWaterAndN zone in soilstate.Zones)
                {
                    ZoneWaterAndN UptakeDemands = new ZoneWaterAndN(zone.Zone);

                    UptakeDemands.NO3N = new double[zone.NO3N.Length];
                    UptakeDemands.NH4N = new double[zone.NH4N.Length];
                    UptakeDemands.PlantAvailableNO3N = new double[zone.NO3N.Length];
                    UptakeDemands.PlantAvailableNH4N = new double[zone.NO3N.Length];
                    UptakeDemands.Water = new double[UptakeDemands.NO3N.Length];

                    //only using Root to get Nitrogen from - temporary code for sorghum
                    var root = Organs[rootIndex] as Root;

                    //Get Nuptake supply from each organ and set the PotentialUptake parameters that are passed to the soil arbitrator

                    //at present these 2arrays arenot being used within the CalculateNitrogenSupply function
                    //sorghum uses Diffusion & Massflow variables currently
                    double[] organNO3Supply = new double[zone.NO3N.Length]; //kg/ha
                    double[] organNH4Supply = new double[zone.NH4N.Length];

                    ZoneState myZone = root.Zones.Find(z => z.Name == zone.Zone.Name);
                    if (myZone != null)
                    {
                        CalculateNitrogenSupply(myZone, zone);

                        //new code
                        double[] diffnAvailable = new double[myZone.Diffusion.Length];
                        for (var i = 0; i < myZone.Diffusion.Length; ++i)
                        {
                            diffnAvailable[i] = myZone.Diffusion[i] - myZone.MassFlow[i];
                        }
                        var totalMassFlow  = MathUtilities.Sum(myZone.MassFlow); //g/m^2
                        var totalDiffusion = MathUtilities.Sum(diffnAvailable);  //g/m^2

                        var potentialSupply   = totalMassFlow + totalDiffusion;
                        var dltt              = root.DltThermalTime.Value();
                        var actualDiffusion   = 0.0;
                        var actualMassFlow    = dltt > 0 ? totalMassFlow : 0.0;
                        var maxDiffusionConst = root.MaxDiffusion.Value();

                        if (totalMassFlow < nDemand && dltt > 0.0)
                        {
                            actualDiffusion = MathUtilities.Bound(nDemand - totalMassFlow, 0.0, totalDiffusion);
                            actualDiffusion = MathUtilities.Divide(actualDiffusion, maxDiffusionConst, 0.0);

                            var nsupplyFraction = root.NSupplyFraction.Value();
                            var maxRate         = root.MaxNUptakeRate.Value();

                            var maxUptakeRateFrac = Math.Min(1.0, (potentialSupply / root.NSupplyFraction.Value())) * root.MaxNUptakeRate.Value();
                            var maxUptake         = Math.Max(0, maxUptakeRateFrac * dltt - actualMassFlow);
                            actualDiffusion = Math.Min(actualDiffusion, maxUptake);
                        }

                        //adjust diffusion values proportionally
                        //make sure organNO3Supply is in kg/ha
                        for (int layer = 0; layer < organNO3Supply.Length; layer++)
                        {
                            var massFlowLayerFraction  = MathUtilities.Divide(myZone.MassFlow[layer], totalMassFlow, 0.0);
                            var diffusionLayerFraction = MathUtilities.Divide(diffnAvailable[layer], totalDiffusion, 0.0);
                            //organNH4Supply[layer] = massFlowLayerFraction * root.MassFlow[layer];
                            organNO3Supply[layer] = (massFlowLayerFraction * actualMassFlow +
                                                     diffusionLayerFraction * actualDiffusion) / kgha2gsm; //convert to kg/ha
                        }
                    }
                    //originalcode
                    UptakeDemands.NO3N = MathUtilities.Add(UptakeDemands.NO3N, organNO3Supply); //Add uptake supply from each organ to the plants total to tell the Soil arbitrator
                    if (UptakeDemands.NO3N.Any(n => MathUtilities.IsNegative(n)))
                    {
                        throw new Exception("-ve no3 uptake demand");
                    }
                    UptakeDemands.NH4N = MathUtilities.Add(UptakeDemands.NH4N, organNH4Supply);

                    N.UptakeSupply[rootIndex] += MathUtilities.Sum(organNO3Supply) * kgha2gsm * zone.Zone.Area / Plant.Zone.Area;  //g/^m
                    if (MathUtilities.IsNegative(N.UptakeSupply[rootIndex]))
                    {
                        throw new Exception($"-ve uptake supply for organ {(Organs[rootIndex] as IModel).Name}");
                    }
                    nSupply += MathUtilities.Sum(organNO3Supply) * zone.Zone.Area;
                    zones.Add(UptakeDemands);
                }

                var nDemandInKg = nDemand / kgha2gsm * Plant.Zone.Area; //NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.
                if (nSupply > nDemandInKg)
                {
                    //Reduce the PotentialUptakes that we pass to the soil arbitrator
                    double ratio = Math.Min(1.0, nDemandInKg / nSupply);
                    foreach (ZoneWaterAndN UptakeDemands in zones)
                    {
                        UptakeDemands.NO3N = MathUtilities.Multiply_Value(UptakeDemands.NO3N, ratio);
                        UptakeDemands.NH4N = MathUtilities.Multiply_Value(UptakeDemands.NH4N, ratio);
                    }
                }
                return(zones);
            }
            return(null);
        }
Exemplo n.º 19
0
        /// <summary>
        /// Solves the equation of continuity from time ts to tfin.
        /// </summary>
        /// <param name="sol">solute properties.</param>
        /// <param name="sdata">soil data.</param>
        /// <param name="ts">start time (h).</param>
        /// <param name="tfin">finish time.</param>
        /// <param name="qprec">precipitation (or water input) rate (fluxes are in cm/h).</param>
        /// <param name="qevap">potl evaporation rate from soil surface.</param>
        /// <param name="nsol">no. of solutes.</param>
        /// <param name="nex">no. of water extraction streams.</param>
        /// <param name="h0">surface head, equal to depth of surface pond.</param>
        /// <param name="S">degree of saturation ("effective satn") of layers.</param>
        /// <param name="evap">cumulative evaporation from soil surface (cm, not initialised).</param>
        /// <param name="runoff">cumulative runoff.</param>
        /// <param name="infil">cumulative net infiltration (time integral of flux across surface).</param>
        /// <param name="drn">cumulative net drainage (time integral of flux across bottom).</param>
        /// <param name="nsteps">cumulative no. of time steps for RE soln.</param>
        /// <param name="jt">layer soil type numbers for solute.</param>
        /// <param name="cin">solute concns in water input (user's units/cc).</param>
        /// <param name="c0">solute concns in surface pond.</param>
        /// <param name="sm">solute (mass) concns in layers.</param>
        /// <param name="soff">cumulative solute runoff (user's units).</param>
        /// <param name="sinfil">cumulative solute infiltration.</param>
        /// <param name="sdrn">cumulative solute drainage.</param>
        /// <param name="nssteps">cumulative no. of time steps for ADE soln.</param>
        /// <param name="wex">cumulative water extractions from layers.</param>
        /// <param name="sex">cumulative solute extractions from layers.</param>
        public static void Solve(SolProps sol, SoilData sdata, double ts, double tfin, double qprec, double qevap, int nsol, int nex,
                                 ref double h0, ref double[] S, ref double evap, ref double runoff, ref double infil, ref double drn, ref int nsteps, int[] jt, double[] cin,
                                 ref double[] c0, ref double[,] sm, ref double[] soff, ref double[] sinfil, ref double[] sdrn, ref int[] nssteps, ref double[,] wex, ref double[,,] sex)
        {
            // Since S.Length is one greater in Fortran to account for the 0 based index, this line is included
            // so that the +1 nomenclature can be kept below to avoid (further) confusion.
            int sLength = S.Length - 1;

            bool again, extraction, initpond, maxpond;
            int  i, iflux, ih0, iok, isatbot, itmp, ns, nsat, nsatlast, nsteps0;

            int[]  isat = new int[sLength + 1];
            double accel, dmax, dt, dwinfil, dwoff, fac, infili, qpme, qprec1, rsig, rsigdt, sig, t, ti, win, xtblbot;

            double[] dSdt  = new double[sLength + 1];
            double[] h     = new double[sLength + 1];
            double[] xtbl  = new double[sLength + 1];
            double[] thi   = new double[sLength + 1];
            double[] thf   = new double[sLength + 1];
            double[] qwex  = new double[sLength + 1];
            double[] qwexd = new double[sLength + 1];
            double[,] dwexs  = new double[sLength + 1, nex + 1];
            double[,] qwexs  = new double[sLength + 1, nex + 1];
            double[,] qwexsd = new double[sLength + 1, nex + 1];
            double[] aa      = new double[sLength + 1]; // these are 0 based
            double[] bb      = new double[sLength + 1];
            double[] cc      = new double[sLength + 1];
            double[] dd      = new double[sLength + 1];
            double[] dy      = new double[sLength + 1];
            double[] ee      = new double[sLength + 1];
            double[] q       = new double[sLength + 1];
            double[] qya     = new double[sLength + 1];
            double[] qyb     = new double[sLength + 1];
            double[] cav     = new double[nsol + 1];
            double[] sinfili = new double[nsol + 1];
            double[,] c;
            sd = sdata;
            c  = new double[nsol + 1, sd.n + 1];
            ISink sink = new SinkDripperDrain(); // Sink type can be changed here.

            /*
             * ! The saturation status of a layer is stored as 0 or 1 in isat since S may be
             * ! >1 (because of previous overshoot) when a layer desaturates. Fluxes at the
             * ! beginning of a time step and their partial derivs wrt S or h of upper and
             * ! lower layers or boundaries are stored in q, qya and qyb.
             */
            isatbot    = 0;
            xtblbot    = 0;
            dt         = 0.0;
            fac        = 0.0;
            initpond   = false;
            extraction = false;
            dwoff      = 0.0;
            ti         = 0.0;
            infili     = 0.0;

            if (nex > 0)
            {
                extraction = true;
            }

            if (sLength != sd.n)
            {
                Console.WriteLine("solve: Size of S differs from table data.");
                Environment.Exit(1);
            }

            //-----set up for boundary conditions
            if (botbc == "constant head") // !h at bottom bdry specified
            {
                if (hbot < sd.he[sd.n])
                {
                    isatbot = 0;
                    xtblbot = Sbot;
                }
                else
                {
                    isatbot = 1;
                    xtblbot = hbot - sd.he[sd.n];
                }
            }
            //-----end set up for boundary conditions
            //-----initialise
            t       = ts;
            nsteps0 = nsteps;
            nsat    = 0;
            //initialise saturated regions
            for (int x = 1; x < sLength; x++)
            {
                if (S[x] >= 1.0)
                {
                    isat[x] = 1;
                    h[x]    = sd.he[x];
                }
                else
                {
                    isat[x] = 0;
                    h[x]    = sd.he[x] - 1.0;
                }
            }

            if (nsol > 0)
            {
                //set solute info
                thi = MathUtilities.Multiply(sd.ths, S); //initial th
                dwexs.Populate2D(0);                     //initial water extracted from layers

                ti      = t;
                infili  = infil;
                sinfili = sinfil;
                double c0Temp = c0[1];
                if (h0 > 0 && cin.Any(x => x != c0Temp)) // count(c0 /= cin) > 0)
                {
                    initpond = true;                     //initial pond with different solute concn
                }
                else
                {
                    initpond = false;
                }
                c.Populate2D(0); //temp storage for soln concns
            }
            //-----end initialise
            //-----solve until tfin

            while (t < tfin)
            {
                //-----take next time step
                for (iflux = 1; iflux <= 2; iflux++) //sometimes need twice to adjust h at satn
                {
                    nsatlast = nsat;                 // for detecting onset of profile saturation
                    nsat     = isat.Sum();
                    sig      = 0.5;
                    if (nsat != 0)
                    {
                        sig = 1.0; //time weighting sigma
                    }
                    rsig = 1.0 / sig;
                    //-----get fluxes and derivs
                    //     get table entries
                    for (int x = 1; x < isat.Length; x++)
                    {
                        if (isat[x] == 0)
                        {
                            xtbl[x] = S[x];
                        }
                        else
                        {
                            xtbl[x] = h[x] - sd.he[x];
                        }
                    }

                    //get surface flux
                    qpme   = qprec - qevap;                  //input rate at saturation
                    qprec1 = qprec;                          //may change qprec1 to maintain pond if required
                    if (h[1] <= 0 && h0 <= 0 && nsat < sd.n) //no ponding
                    {
                        ns = 1;                              //start index for eqns
                        sd.GetQ(0, new [] { 0, 0, isat[1] }, new [] { 0, 0, xtbl[1] }, out q[0], out qya[0], out qyb[0]);
                        if (q[0] < qpme)
                        {
                            q[0]   = qpme;
                            qyb[0] = 0;
                        }
                        maxpond = false;
                    }
                    else //ponding
                    {
                        ns = 0;
                        sd.GetQ(0, new [] { 0, 1, isat[1] }, new [] { 0, h0 - sd.he[1], xtbl[1] }, out q[0], out qya[0], out qyb[0]);
                        if (h0 >= h0max && qpme > q[0])
                        {
                            maxpond = true;
                            ns      = 1;
                        }
                        else
                        {
                            maxpond = false;
                        }
                    }

                    //get profile fluxes
                    for (i = 1; i <= sd.n - 1; i++)
                    {
                        sd.GetQ(i, new [] { 0, isat[i], isat[i + 1] }, new [] { 0, xtbl[i], xtbl[i + 1] }, out q[i], out qya[i], out qyb[i]);
                    }

                    //get bottom flux
                    switch (botbc)
                    {
                    case "constant head":
                        sd.GetQ(sd.n, new [] { 0, isat[sd.n], isatbot }, new [] { 0, xtbl[sd.n], xtblbot }, out q[sd.n], out qya[sd.n], out qyb[sd.n]);
                        break;

                    case "0.0 flux":
                        q[sd.n]   = 0;
                        qya[sd.n] = 0;
                        break;

                    case "free drainage":
                        sd.GetK(sd.n, isat[sd.n], xtbl[sd.n], out q[sd.n], out qya[sd.n]);
                        break;

                    case "seepage":
                        if (h[sd.n] <= -0.5 * sd.dx[sd.n])
                        {
                            q[sd.n]   = 0;
                            qya[sd.n] = 0;
                        }
                        else
                        {
                            sd.GetQ(sd.n, new int[] { 0, isat[sd.n], 1 }, new double[] { 0, xtbl[sd.n], -sd.he[sd.n] }, out q[sd.n], out qya[sd.n], out qyb[sd.n]);
                        }
                        break;

                    default:
                        Console.Out.WriteLine("solve: illegal bottom boundary condn");
                        Environment.Exit(1);
                        break;
                    }

                    if (extraction) //get rate of extraction
                    {
                        sink.Wsinks(t, isat, xtbl, sd.he, ref qwexs, ref qwexsd);

                        for (int x = 1; x < qwexs.GetLength(1); x++)
                        {
                            qwex[x] = Matrix <double> .Build.DenseOfArray(qwexs).Column(x).Sum();

                            qwexd[x] = Matrix <double> .Build.DenseOfArray(qwexsd).Column(x).Sum();
                        }
                    }
                    again = false; //flag for recalcn of fluxes
                    //-----end get fluxes and derivs
                    //----estimate time step dt
                    dmax = 0;
                    dSdt = MathUtilities.CreateArrayOfValues(0, dSdt.Length);
                    for (int x = 1; x <= sd.n; x++)
                    {
                        if (isat[x] == 0)
                        {
                            dSdt[x] = Math.Abs(q[x] - q[x - 1] + (extraction ? qwex[x] : 0)) / (sd.ths[x] * sd.dx[x]);
                        }
                    }

                    dmax = MathUtilities.Max(dSdt); //Max derivative | dS / dt |
                    if (dmax > 0)
                    {
                        dt = dSmax / dmax;
                        // if pond going adjust dt
                        if (h0 > 0 && (q[0] - qpme) * dt > h0)
                        {
                            dt = (h0 - 0.5 * h0min) / (q[0] - qpme);
                        }
                    }
                    else //steady state flow
                    {
                        if (qpme >= q[sd.n]) //step to finish -but what if extraction varies with time ???
                        {
                            dt = tfin - t;
                        }
                        else
                        {
                            dt = -(h0 - 0.5 * h0min) / (qpme - q[sd.n]); //pond going so adjust dt
                        }
                    }

                    if (dt > dtmax)
                    {
                        dt = dtmax; //user's limit
                    }
                    // if initial step, improve h where S>= 1
                    if (nsteps == nsteps0 && nsat > 0 && iflux == 1)
                    {
                        again = true;
                        dt    = 1.0e-20 * (tfin - ts);
                    }
                    if (nsat == sd.n && nsatlast < sd.n && iflux == 1)
                    {
                        //profile has just become saturated so adjust h values
                        again = true;
                        dt    = 1.0e-20 * (tfin - ts);
                    }
                    if (t + 1.1 * dt > tfin) //step to finish
                    {
                        dt = tfin - t;
                        t  = tfin;
                    }
                    else
                    {
                        t = t + dt; //tentative update
                    }
                    //-----end estimate time step dt
                    //-----get and solve eqns
                    rsigdt = 1.0 / (sig * dt);
                    //aa, bb, cc and dd hold coeffs and rhs of tridiag eqn set
                    for (int x = ns; x <= sd.n - 1; x++)
                    {
                        aa[x + 1] = qya[x];
                        cc[x]     = -qyb[x];
                    }
                    if (extraction)
                    {
                        for (int x = 1; x <= sd.n; x++)
                        {
                            dd[x] = -(q[x - 1] - q[x] - qwex[x]) * rsig;
                        }
                    }
                    else
                    {
                        for (int x = 1; x <= sd.n; x++)
                        {
                            dd[x] = -(q[x - 1] - q[x]) * rsig;
                        }
                    }

                    iok  = 0;        //flag for time step test
                    itmp = 0;        //counter to abort if not getting solution
                    while (iok == 0) //keep reducing time step until all ok
                    {
                        itmp  = itmp + 1;
                        accel = 1.0 - 0.05 * Math.Min(10, Math.Max(0, itmp - 4)); //acceleration
                        if (itmp > 20)
                        {
                            Console.Out.WriteLine("solve: too many iterations of equation solution");
                            Environment.Exit(1);
                        }
                        if (ns < 1)
                        {
                            bb[0] = -qya[0] - rsigdt;
                            dd[0] = -(qpme - q[0]) * rsig;
                        }

                        for (int x = 1; x <= sd.n; x++)
                        {
                            bb[x] = qyb[x - 1] - qya[x];
                            if (isat[x] == 0)
                            {
                                bb[x] -= sd.ths[x] * sd.dx[x] * rsigdt;
                            }

                            if (extraction)
                            {
                                bb[x] -= qwexd[x];
                            }
                        }

                        Tri(ns, sd.n, aa, ref bb, cc, dd, ref ee, ref dy);
                        //dy contains dS or, for sat layers, h values
                        iok = 1;
                        if (!again)
                        {
                            //check if time step ok, if not then set fac to make it less
                            iok = 1;
                            for (i = 1; i <= sd.n; i++)
                            {
                                if (isat[i] == 0) //check change in S
                                {
                                    if (Math.Abs(dy[i]) > dSfac * dSmax)
                                    {
                                        fac = Math.Max(0.5, accel * Math.Abs(dSmax / dy[i]));
                                        iok = 0;
                                        break;
                                    }
                                    if (-dy[i] > dSmaxr * S[i])
                                    {
                                        fac = Math.Max(0.5, accel * dSmaxr * S[i] / (-dSfac * dy[i]));
                                        iok = 0;
                                        break;
                                    }
                                    if (S[i] < 1.0 && S[i] + dy[i] > Smax)
                                    {
                                        fac = accel * (0.5 * (1.0 + Smax) - S[i]) / dy[i];
                                        iok = 0;
                                        break;
                                    }
                                    if (S[i] >= 1.0 && dy[i] > 0.5 * (Smax - 1.0))
                                    {
                                        fac = 0.25 * (Smax - 1.0) / dy[i]; iok = 0;
                                        break;
                                    }
                                }
                            }
                            if (iok == 1 && ns < 1 && h0 < h0max && h0 + dy[0] > h0max + dh0max)
                            {
                                //start of runoff
                                fac = (h0max + 0.5 * dh0max - h0) / dy[0];
                                iok = 0;
                            }
                            if (iok == 1 && ns < 1 && h0 > 0.0 && h0 + dy[0] < h0min)
                            {
                                //pond going
                                fac = -(h0 - 0.5 * h0min) / dy[0];
                                iok = 0;
                            }
                            if (iok == 0) //reduce time step
                            {
                                t      = t - dt;
                                dt     = fac * dt;
                                t      = t + dt;
                                rsigdt = 1.0 / (sig * dt);
                                nless  = nless + 1; //count step size reductions
                            }
                            if (isat[1] != 0 && iflux == 1 && h[1] < 0.0 && h[1] + dy[1] > 0.0)
                            {
                                //incipient ponding - adjust state of saturated regions
                                t      = t - dt;
                                dt     = 1.0e-20 * (tfin - ts);
                                rsigdt = 1.0 / (sig * dt);
                                again  = true;
                                iok    = 0;
                            }
                        }
                    } //end while

                    //-----end get and solve eqns
                    //-----update unknowns
                    ih0 = 0;
                    if (!again)
                    {
                        dwoff = 0.0;
                        if (ns < 1)
                        {
                            h0 = h0 + dy[0];
                            if (h0 < 0.0 && dy[0] < 0.0)
                            {
                                ih0 = 1; //pond g1.0
                            }
                            evap = evap + qevap * dt;
                            //note that fluxes required are q at sigma of time step
                            dwinfil = (q[0] + sig * (qya[0] * dy[0] + qyb[0] * dy[1])) * dt;
                        }
                        else
                        {
                            dwinfil = (q[0] + sig * qyb[0] * dy[1]) * dt;
                            if (maxpond)
                            {
                                evap = evap + qevap * dt;
                                if (qprec > qprecmax) // set input to maintain pond
                                {
                                    qpme   = q[0] + sig * qyb[0] * dy[1];
                                    qprec1 = qpme + qevap;
                                    dwoff  = 0.0;
                                }
                                else
                                {
                                    dwoff = qpme * dt - dwinfil;
                                }
                                runoff = runoff + dwoff;
                            }
                            else
                            {
                                evap = evap + qprec1 * dt - dwinfil;
                            }
                        }
                        infil = infil + dwinfil;
                        if (nsol > 0)     //get surface solute balance
                        {
                            if (initpond) //pond concn != cin
                            {
                                if (h0 > 0.0)
                                {
                                    if (ns == 1) // if max pond depth
                                    {
                                        dy[0] = 0.0;
                                    }
                                    for (int x = 1; x < cav.Length; x++)
                                    {
                                        cav[x] = ((2.0 * h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (2.0 * h0 + dwoff + dwinfil);
                                    }
                                    for (int x = 1; x < c0.Length; x++)
                                    {
                                        c0[x] = 2.0 * cav[x] - c0[x]; //This needs to be tested from FORTRAN; no example in original code.
                                    }
                                }
                                else
                                {
                                    for (int x = 1; x < cav.Length; x++)
                                    {
                                        cav[x] = ((h0 - dy[0]) * c0[x] + qprec1 * dt * cin[x]) / (dwoff + dwinfil);
                                    }
                                    initpond = false; //pond gone
                                    c0       = cin;   // for output if any pond at end
                                }
                                soff   = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff));
                                sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cav, dwinfil));
                            }
                            else
                            {
                                soff   = MathUtilities.Add(soff, MathUtilities.Multiply_Value(cav, dwoff));
                                sinfil = MathUtilities.Add(sinfil, MathUtilities.Multiply_Value(cin, qprec1 * dt - dwoff));
                            }
                        }

                        // There's a condition based on botbc in the FORTRAN here, but both paths
                        // resolve to the same equation.
                        drn = drn + (q[sd.n] + sig * qya[sd.n] * dy[sd.n]) * dt;

                        if (extraction)
                        {
                            Matrix <double> dwexsM = Matrix <double> .Build.DenseOfArray(dwexs);

                            Matrix <double> qwexsM = Matrix <double> .Build.DenseOfArray(qwexs);

                            Matrix <double> qwexsdM = Matrix <double> .Build.DenseOfArray(qwexsd);

                            Matrix <double> wexM = Matrix <double> .Build.DenseOfArray(wex);

                            Vector <double> wexV;
                            Vector <double> dwexsV;
                            Vector <double> qwexsV;
                            Vector <double> qwexsdV;
                            if (nsol > 0)
                            {
                                //dwexs = dwexs + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt
                                for (i = 1; i <= nex; i++)
                                {
                                    dwexsV  = dwexsM.Column(i);
                                    qwexsV  = qwexsM.Column(i);
                                    qwexsdV = qwexsdM.Column(i);
                                    dwexsV  = dwexsV + (qwexsV + sig * qwexsdV * Vector <double> .Build.DenseOfArray(dy.Slice(1, sd.n))) * dt;
                                    dwexsM.Column(i, dwexsV);
                                }
                                dwexs = dwexsM.ToArray();
                            }

                            //wex = wex + (qwexs + sig * qwexsd * spread(dy(1:n), 2, nex)) * dt
                            if (wex.GetLength(0) > 1) // analog for if (present(wex))
                            {
                                for (i = 1; i <= nex; i++)
                                {
                                    qwexsV  = qwexsM.Column(i);
                                    qwexsdV = qwexsdM.Column(i);
                                    wexV    = wexM.Column(i);
                                    wexV    = wexV + (qwexsV + sig * qwexsdV * Vector <double> .Build.DenseOfArray(dy.Slice(1, sd.n))) * dt;
                                    wexM.Column(i, wexV);
                                }
                                wex = wexM.ToArray();
                            }
                        }
                    }

                    for (i = 1; i <= sd.n; i++)
                    {
                        if (isat[i] == 0)
                        {
                            if (!again)
                            {
                                S[i] = S[i] + dy[i];
                                if (S[i] > 1.0 && dy[i] > 0.0) //saturation of layer
                                {
                                    isat[i] = 1;
                                    h[i]    = sd.he[i];
                                }
                            }
                        }
                        else
                        {
                            h[i] = h[i] + dy[i];
                            if (i == 1 && ih0 != 0 && h[i] >= sd.he[i])
                            {
                                h[i] = sd.he[i] - 1.0; //pond gone
                            }
                            if (h[i] < sd.he[i])       //desaturation of layer
                            {
                                isat[i] = 0;
                                h[i]    = sd.he[i];
                            }
                        }
                    }
                    //-----end update unknowns
                    if (!again)
                    {
                        break;
                    }
                }

                if (dt <= dtmin)
                {
                    Console.WriteLine("solve: time step = " + dt);
                    Environment.Exit(1);
                }
                //-----end take next time step
                //remove negative h0 (optional)
                if (h0 < 0.0 && isat[1] == 0)
                {
                    infil = infil + h0;
                    S[1]  = S[1] + h0 / (sd.ths[1] * sd.dx[1]);
                    h0    = 0.0;
                }
                nsteps = nsteps + 1;
                //solve for solute transport if required
                if (nwsteps * (nsteps / nwsteps) == nsteps)
                {
                    // This is function getsolute() in FORTRAN. Inline here as it uses a huge number of vars and is only used once.
                    if (nsol > 0 && t > ti)
                    {
                        thf = MathUtilities.Multiply(sd.ths, S);                                        //final th before call
                        win = infil - infili;                                                           //water in at top over time interval
                        cav = MathUtilities.Divide_Value(MathUtilities.Subtract(sinfil, sinfili), win); //average concn in win
                        Solute(ti, t, thi, thf, dwexs, win, cav, sd.n, nsol, nex, sd.dx, jt, dsmmax, ref sm, ref sdrn, ref nssteps, ref c, ref sex, extraction, sol);
                        ti  = t;
                        thi = thf;
                        dwexs.Populate2D(0);
                        infili  = infil;
                        sinfili = sinfil; // for next interval
                    }
                }
            }
            //-----end solve until tfin
            //finalise solute transport if required
        }
Exemplo n.º 20
0
        /// <summary>Evaluates the specified sym1.</summary>
        /// <param name="sym1">The sym1.</param>
        /// <param name="opr">The opr.</param>
        /// <param name="sym2">The sym2.</param>
        /// <returns></returns>
        protected Symbol Evaluate(Symbol sym1, Symbol opr, Symbol sym2)
        {
            Symbol result;

            result.m_name   = sym1.m_name + opr.m_name + sym2.m_name;
            result.m_type   = ExpressionType.Result;
            result.m_value  = 0;
            result.m_values = null;
            switch (opr.m_name)
            {
            case "^":
                if (sym1.m_values != null)
                {
                    result.m_values = new double[sym1.m_values.Length];
                    for (int i = 0; i < sym1.m_values.Length; i++)
                    {
                        result.m_values[i] = Math.Pow(sym1.m_values[i], sym2.m_value);
                    }
                }
                else
                {
                    result.m_value = System.Math.Pow(sym1.m_value, sym2.m_value);
                }
                break;

            case "/":
            {
                if (sym1.m_values != null && sym2.m_values != null)
                {
                    result.m_values = MathUtilities.Divide(sym1.m_values, sym2.m_values);
                }
                else if (sym1.m_values != null)
                {
                    result.m_values = MathUtilities.Divide_Value(sym1.m_values, sym2.m_value);
                }
                else if (sym2.m_values != null)
                {
                    result.m_values = new double[sym2.m_values.Length];
                    for (int i = 0; i < result.m_values.Length; i++)
                    {
                        result.m_values[i] = MathUtilities.Divide(sym1.m_value, sym2.m_values[i], 0);
                    }
                }
                else
                {
                    if (sym2.m_value > 0)
                    {
                        result.m_value = sym1.m_value / sym2.m_value;
                    }
                    else
                    {
                        result.m_name = "Divide by Zero.";
                        result.m_type = ExpressionType.Error;
                    }
                }
                break;
            }

            case "*":
                if (sym1.m_values != null && sym2.m_values != null)
                {
                    result.m_values = MathUtilities.Multiply(sym1.m_values, sym2.m_values);
                }
                else if (sym1.m_values != null)
                {
                    result.m_values = MathUtilities.Multiply_Value(sym1.m_values, sym2.m_value);
                }
                else if (sym2.m_values != null)
                {
                    result.m_values = MathUtilities.Multiply_Value(sym2.m_values, sym1.m_value);
                }
                else
                {
                    result.m_value = sym1.m_value * sym2.m_value;
                }
                break;

            case "%":
                result.m_value = sym1.m_value % sym2.m_value;
                break;

            case "+":
                if (sym1.m_values != null && sym2.m_values != null)
                {
                    result.m_values = MathUtilities.Add(sym1.m_values, sym2.m_values);
                }
                else if (sym1.m_values != null)
                {
                    result.m_values = MathUtilities.AddValue(sym1.m_values, sym2.m_value);
                }
                else if (sym2.m_values != null)
                {
                    result.m_values = MathUtilities.AddValue(sym2.m_values, sym1.m_value);
                }
                else
                {
                    result.m_value = sym1.m_value + sym2.m_value;
                }
                break;

            case "-":
                if (sym1.m_values != null && sym2.m_values != null)
                {
                    result.m_values = MathUtilities.Subtract(sym1.m_values, sym2.m_values);
                }
                else if (sym1.m_values != null)
                {
                    result.m_values = MathUtilities.Subtract_Value(sym1.m_values, sym2.m_value);
                }
                else if (sym2.m_values != null)
                {
                    result.m_values = new double[sym2.m_values.Length];
                    for (int i = 0; i < result.m_values.Length; i++)
                    {
                        result.m_values[i] = sym1.m_value - sym2.m_values[i];
                    }
                }
                else
                {
                    result.m_value = sym1.m_value - sym2.m_value;
                }
                break;

            default:
                result.m_type = ExpressionType.Error;
                result.m_name = "Undefine operator: " + opr.m_name + ".";
                break;
            }
            return(result);
        }
Exemplo n.º 21
0
 public void AddKgHaDelta(SoluteSetterType callingModelType, double[] delta)
 {
     kgha = MathUtilities.Add(kgha, delta);
 }
Exemplo n.º 22
0
        /// <summary>The method used to arbitrate N allocations</summary>
        public List <ZoneWaterAndN> GetUptakeEstimates(SoilState soilstate, IArbitration[] Organs)
        {
            var N = Arbitrator.N;

            var nSupply = 0.0;//NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.

            //this function is called 4 times as part of estimates
            //shouldn't set public variables in here

            var grainIndex = 0;
            //Organs.ToList().FindIndex(o => (o as IModel).Name == "Grain")
            var rootIndex = 1;
            var leafIndex = 2;
            var stemIndex = 4;

            var rootDemand  = N.StructuralDemand[rootIndex] + N.MetabolicDemand[rootIndex];
            var stemDemand  = /*N.StructuralDemand[stemIndex] + */ N.MetabolicDemand[stemIndex];
            var leafDemand  = N.MetabolicDemand[leafIndex];
            var grainDemand = N.StructuralDemand[grainIndex] + N.MetabolicDemand[grainIndex];
            //have to correct the leaf demand calculation
            var leaf           = Organs[leafIndex] as SorghumLeaf;
            var leafAdjustment = leaf.calculateClassicDemandDelta();

            //double NDemand = (N.TotalPlantDemand - N.TotalReallocation) / kgha2gsm * Plant.Zone.Area; //NOTE: This is in kg, not kg/ha, to arbitrate N demands for spatial simulations.
            //old sorghum uses g/m^2 - need to convert after it is used to calculate actual diffusion
            // leaf adjustment is not needed here because it is an adjustment for structural demand - we only look at metabolic here.

            // dh - In old sorghum, root only has one type of NDemand - it doesn't have a structural/metabolic division.
            // In new apsim, root only uses structural, metabolic is always 0. Therefore, we have to include root's structural
            // NDemand in this calculation.

            // dh - In old sorghum, totalDemand is metabolic demand for all organs. However in new apsim, grain has no metabolic
            // demand, so we must include its structural demand in this calculation.
            double totalDemand         = N.TotalMetabolicDemand + N.StructuralDemand[rootIndex] + N.StructuralDemand[grainIndex];
            double nDemand             = Math.Max(0, totalDemand - grainDemand); // to replicate calcNDemand in old sorghum
            List <ZoneWaterAndN> zones = new List <ZoneWaterAndN>();

            foreach (ZoneWaterAndN zone in soilstate.Zones)
            {
                ZoneWaterAndN UptakeDemands = new ZoneWaterAndN(zone.Zone);

                UptakeDemands.NO3N = new double[zone.NO3N.Length];
                UptakeDemands.NH4N = new double[zone.NH4N.Length];
                UptakeDemands.PlantAvailableNO3N = new double[zone.NO3N.Length];
                UptakeDemands.PlantAvailableNH4N = new double[zone.NO3N.Length];
                UptakeDemands.Water = new double[UptakeDemands.NO3N.Length];

                //only using Root to get Nitrogen from - temporary code for sorghum
                var root = Organs[rootIndex] as Root;

                //Get Nuptake supply from each organ and set the PotentialUptake parameters that are passed to the soil arbitrator

                //at present these 2arrays arenot being used within the CalculateNitrogenSupply function
                //sorghum uses Diffusion & Massflow variables currently
                double[] organNO3Supply = new double[zone.NO3N.Length]; //kg/ha - dltNo3 in old apsim
                double[] organNH4Supply = new double[zone.NH4N.Length];

                ZoneState myZone = root.Zones.Find(z => z.Name == zone.Zone.Name);
                if (myZone != null)
                {
                    CalculateNitrogenSupply(myZone, zone);

                    //new code
                    double[] diffnAvailable = new double[myZone.Diffusion.Length];
                    for (var i = 0; i < myZone.Diffusion.Length; ++i)
                    {
                        diffnAvailable[i] = myZone.Diffusion[i] - myZone.MassFlow[i];
                    }
                    var totalMassFlow  = MathUtilities.Sum(myZone.MassFlow); //g/m^2
                    var totalDiffusion = MathUtilities.Sum(diffnAvailable);  //g/m^2

                    var potentialSupply = totalMassFlow + totalDiffusion;
                    var actualDiffusion = 0.0;
                    //var actualMassFlow = DltTT > 0 ? totalMassFlow : 0.0;
                    var maxDiffusionConst = root.MaxDiffusion.Value();

                    double nUptakeCease = NUptakeCease.Value();

                    if (TTFMFromFlowering.Value() > NUptakeCease.Value())
                    {
                        totalMassFlow = 0;
                    }
                    var actualMassFlow = totalMassFlow;

                    if (totalMassFlow < nDemand && TTFMFromFlowering.Value() < nUptakeCease) // fixme && ttElapsed < nUptakeCease
                    {
                        actualDiffusion = MathUtilities.Bound(nDemand - totalMassFlow, 0.0, totalDiffusion);
                        actualDiffusion = MathUtilities.Divide(actualDiffusion, maxDiffusionConst, 0.0);

                        var nsupplyFraction = root.NSupplyFraction.Value();
                        var maxRate         = root.MaxNUptakeRate.Value();

                        var maxUptakeRateFrac = Math.Min(1.0, (potentialSupply / root.NSupplyFraction.Value())) * root.MaxNUptakeRate.Value();
                        var maxUptake         = Math.Max(0, maxUptakeRateFrac * DltTT.Value() - actualMassFlow);
                        actualDiffusion = Math.Min(actualDiffusion, maxUptake);
                    }

                    NDiffusionSupply = actualDiffusion;
                    NMassFlowSupply  = actualMassFlow;

                    //adjust diffusion values proportionally
                    //make sure organNO3Supply is in kg/ha
                    for (int layer = 0; layer < organNO3Supply.Length; layer++)
                    {
                        var massFlowLayerFraction  = MathUtilities.Divide(myZone.MassFlow[layer], totalMassFlow, 0.0);
                        var diffusionLayerFraction = MathUtilities.Divide(diffnAvailable[layer], totalDiffusion, 0.0);
                        //organNH4Supply[layer] = massFlowLayerFraction * root.MassFlow[layer];
                        organNO3Supply[layer] = (massFlowLayerFraction * actualMassFlow +
                                                 diffusionLayerFraction * actualDiffusion) / kgha2gsm; //convert to kg/ha
                    }
                }
                //originalcode
                UptakeDemands.NO3N = MathUtilities.Add(UptakeDemands.NO3N, organNO3Supply); //Add uptake supply from each organ to the plants total to tell the Soil arbitrator
                if (UptakeDemands.NO3N.Any(n => MathUtilities.IsNegative(n)))
                {
                    throw new Exception("-ve no3 uptake demand");
                }
                UptakeDemands.NH4N = MathUtilities.Add(UptakeDemands.NH4N, organNH4Supply);

                N.UptakeSupply[rootIndex] += MathUtilities.Sum(organNO3Supply) * kgha2gsm * zone.Zone.Area / plant.Zone.Area;  //g/m2
                if (MathUtilities.IsNegative(N.UptakeSupply[rootIndex]))
                {
                    throw new Exception($"-ve uptake supply for organ {(Organs[rootIndex] as IModel).Name}");
                }
                nSupply += MathUtilities.Sum(organNO3Supply) * zone.Zone.Area;
                zones.Add(UptakeDemands);
            }

            return(zones);
        }