Example #1
0
 /// <summary>
 /// Calculate the potential sw uptake for today
 /// </summary>
 public List <ZoneWaterAndN> GetWaterUptakeEstimates(SoilState soilstate)
 {
     if (plant.IsAlive)
     {
         return(waterUptakeMethod.GetUptakeEstimates(soilstate, Organs.ToArray()));
     }
     return(null);
 }
        /// <summary>The method used to arbitrate N allocations</summary>
        public List <ZoneWaterAndN> GetUptakeEstimates(SoilState soilstate, IArbitration[] Organs)
        {
            // Get all water supplies.
            double waterSupply = 0;  //NOTE: This is in L, not mm, to arbitrate water demands for spatial simulations.

            List <double[]>      supplies = new List <double[]>();
            List <ZoneWaterAndN> zones    = new List <ZoneWaterAndN>();

            foreach (ZoneWaterAndN zone in soilstate.Zones)
            {
                foreach (IOrgan o in Organs)
                {
                    if (o is IWaterNitrogenUptake)
                    {
                        double[] organSupply = (o as IWaterNitrogenUptake).CalculateWaterSupply(zone);
                        if (organSupply != null)
                        {
                            supplies.Add(organSupply);
                            zones.Add(zone);
                            waterSupply += MathUtilities.Sum(organSupply) * zone.Zone.Area;
                        }
                    }
                }
            }

            // Calculate total water demand.
            double waterDemand = 0; //NOTE: This is in L, not mm, to arbitrate water demands for spatial simulations.

            foreach (IHasWaterDemand WD in WaterDemands)
            {
                waterDemand += WD.CalculateWaterDemand() * plant.Zone.Area;
            }

            // Calculate demand / supply ratio.
            double fractionUsed = 0;

            if (waterSupply > 0)
            {
                fractionUsed = Math.Min(1.0, waterDemand / waterSupply);
            }

            // Apply demand supply ratio to each zone and create a ZoneWaterAndN structure
            // to return to caller.
            List <ZoneWaterAndN> ZWNs = new List <ZoneWaterAndN>();

            for (int i = 0; i < supplies.Count; i++)
            {
                // Just send uptake from my zone
                ZoneWaterAndN uptake = new ZoneWaterAndN(zones[i]);
                uptake.Water = MathUtilities.Multiply_Value(supplies[i], fractionUsed);
                uptake.NO3N  = new double[uptake.Water.Length];
                uptake.NH4N  = new double[uptake.Water.Length];
                uptake.PlantAvailableNO3N = new double[uptake.Water.Length];
                uptake.PlantAvailableNH4N = new double[uptake.Water.Length];
                ZWNs.Add(uptake);
            }
            return(ZWNs);
        }
        /// <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);
        }
Example #4
0
        /// <summary>
        /// Calculate the potential sw uptake for today. Should return null if crop is not in the ground.
        /// </summary>
        public 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.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);
                            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);
        }
Example #5
0
        /// <summary>Placeholder for SoilArbitrator</summary>
        /// <param name="soilstate">soil state</param>
        /// <returns></returns>
        public List <ZoneWaterAndN> GetNitrogenUptakeEstimates(SoilState soilstate)
        {
            ZoneWaterAndN MyZone = new ZoneWaterAndN(this.Parent as Zone);

            foreach (ZoneWaterAndN Z in soilstate.Zones)
            {
                if (Z.Zone.Name == this.Parent.Name)
                {
                    MyZone = Z;
                }
            }

            double[] PotNO3Uptake = new double[MyZone.NO3N.Length];
            double[] PotNH4Uptake = new double[MyZone.NH4N.Length];
            NO3Uptake = new double[MyZone.NO3N.Length];
            NH4Uptake = new double[MyZone.NH4N.Length];

            var soilCrop = Soil.Crop(Name);

            for (int j = 0; j < Soil.LL15mm.Length; j++)
            {
                PotNO3Uptake[j] = Math.Max(0.0, RootProportion(j, RootDepth) * soilCrop.KL[j] * MyZone.NO3N[j]);
                PotNH4Uptake[j] = Math.Max(0.0, RootProportion(j, RootDepth) * soilCrop.KL[j] * MyZone.NH4N[j]);
            }
            double TotPotNUptake = MathUtilities.Sum(PotNO3Uptake) + MathUtilities.Sum(PotNH4Uptake);

            for (int j = 0; j < MyZone.NO3N.Length; j++)
            {
                NO3Uptake[j] = PotNO3Uptake[j] * Math.Min(1.0, NDemand / TotPotNUptake);
                NH4Uptake[j] = PotNH4Uptake[j] * Math.Min(1.0, NDemand / TotPotNUptake);
            }
            List <ZoneWaterAndN> Uptakes = new List <ZoneWaterAndN>();
            ZoneWaterAndN        Uptake  = new ZoneWaterAndN(this.Parent as Zone);

            Uptake.NO3N = NO3Uptake;
            Uptake.NH4N = NH4Uptake;
            Uptake.PlantAvailableNO3N = new double[NO3Uptake.Length];
            Uptake.PlantAvailableNH4N = new double[NO3Uptake.Length];
            Uptake.Water = new double[NO3Uptake.Length];
            Uptakes.Add(Uptake);
            return(Uptakes);
        }
Example #6
0
        /// <summary>Calculate the potential sw uptake for today</summary>
        /// <param name="soilstate"></param>
        /// <returns>list of uptakes</returns>
        /// <exception cref="ApsimXException">Could not find root zone in Zone  + this.Parent.Name +  for SimpleTree</exception>
        public List <ZoneWaterAndN> GetWaterUptakeEstimates(SoilState soilstate)
        {
            ZoneWaterAndN MyZone = new ZoneWaterAndN(this.Parent as Zone);

            foreach (ZoneWaterAndN Z in soilstate.Zones)
            {
                if (Z.Zone.Name == this.Parent.Name)
                {
                    MyZone = Z;
                }
            }


            double[] PotSWUptake = new double[Soil.LL15.Length];
            SWUptake = new double[Soil.LL15.Length];

            SoilCrop soilCrop = Soil.Crop(this.Name) as SoilCrop;

            for (int j = 0; j < Soil.LL15mm.Length; j++)
            {
                PotSWUptake[j] = Math.Max(0.0, RootProportion(j, RootDepth) * soilCrop.KL[j] * (MyZone.Water[j] - Soil.LL15mm[j]));
            }

            double TotPotSWUptake = MathUtilities.Sum(PotSWUptake);

            for (int j = 0; j < Soil.LL15mm.Length; j++)
            {
                SWUptake[j] = PotSWUptake[j] * Math.Min(1.0, PotentialEP / TotPotSWUptake);
            }

            List <ZoneWaterAndN> Uptakes = new List <ZoneWaterAndN>();
            ZoneWaterAndN        Uptake  = new ZoneWaterAndN(this.Parent as Zone);

            Uptake.Water = SWUptake;
            Uptake.NO3N  = new double[SWUptake.Length];
            Uptake.NH4N  = new double[SWUptake.Length];
            Uptake.NH4N  = new double[SWUptake.Length];
            Uptake.PlantAvailableNO3N = new double[SWUptake.Length];
            Uptake.PlantAvailableNH4N = new double[SWUptake.Length];
            Uptakes.Add(Uptake);
            return(Uptakes);
        }
Example #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)
     {
         return(nitrogenUptakeMethod.GetUptakeEstimates(soilstate, Organs.ToArray()));
     }
     return(null);
 }
        /// <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);
        }
Example #9
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);
        }