/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }