/// <summary> /// calculate how the dlt's (C and N) are partitioned amongst patches /// </summary> /// <param name="callingModelType">Type of calling model.</param> /// <param name="incomingDelta">The dlt to be partioned amongst patches</param> /// <param name="soluteName">The solute or pool that is changing</param> /// <param name="partitionApproach">The type of partition to be used</param> /// <returns>The values of dlt partitioned for each existing patch</returns> private double[][] PartitionDelta(double[] incomingDelta, string soluteName, SoluteSetterType callingModelType, PartitionApproachEnum partitionApproach) { int numberLayers = incomingDelta.Length; // 1. initialise the result array double[][] Result = new double[patches.Count][]; for (int k = 0; k < patches.Count; k++) { Result[k] = new double[numberLayers]; } try { // 1.5 If the calling model is a plant and the solute is NO3 or NH4 then use the 'PlantAvailable' solutes instead. if (callingModelType == SoluteSetterType.Plant && (soluteName == "NO3" || soluteName == "NH4")) { soluteName = "PlantAvailable" + soluteName; } // 2- gather how much solute is already in the soil double[][] existingSoluteAmount = new double[patches.Count][]; for (int k = 0; k < patches.Count; k++) { existingSoluteAmount[k] = patches[k].GetSoluteKgHa(soluteName); } // 3- calculate partition weighting factors, done for each layer based on existing solute amount double[] partitionWeight; double[] thisLayerPatchSolute; double thisLayersTotalSolute; for (int layer = 0; layer < numberLayers; layer++) { if (Math.Abs(incomingDelta[layer]) > epsilon) { // 3.1- zero and initialise the variables partitionWeight = new double[patches.Count]; thisLayerPatchSolute = new double[patches.Count]; // 3.2- get the solute amounts for each patch in this layer if (partitionApproach == PartitionApproachEnum.BasedOnLayerConcentration || (partitionApproach == PartitionApproachEnum.BasedOnConcentrationAndDelta && incomingDelta[layer] < epsilon)) { for (int k = 0; k < patches.Count; k++) { thisLayerPatchSolute[k] = existingSoluteAmount[k][layer] * patches[k].RelativeArea; } } else if (partitionApproach == PartitionApproachEnum.BasedOnSoilConcentration || (partitionApproach == PartitionApproachEnum.BasedOnConcentrationAndDelta && incomingDelta[layer] >= epsilon)) { for (int k = 0; k < patches.Count; k++) { double layerUsed = 0.0; for (int z = layer; z >= 0; z--) // goes backwards till soil surface (but may stop before that) { thisLayerPatchSolute[k] += existingSoluteAmount[k][z]; layerUsed += soilPhysical.Thickness[z]; if ((LayerForNPartition > epsilon) && (layerUsed >= LayerForNPartition)) { // stop if thickness reaches a defined value z = -1; } } thisLayerPatchSolute[k] *= patches[k].RelativeArea; } } // 3.3- get the total solute amount for this layer thisLayersTotalSolute = MathUtilities.Sum(thisLayerPatchSolute); // 3.4- Check whether the existing solute is greater than the incoming delta if (MathUtilities.IsLessThan(thisLayersTotalSolute + incomingDelta[layer], 0)) { throw new Exception($"Attempt to change {soluteName}[{layer + 1}] to a negative value"); } // 3.5- Compute the partition weights for each patch for (int k = 0; k < patches.Count; k++) { partitionWeight[k] = 0.0; if (thisLayersTotalSolute >= epsilon) { partitionWeight[k] = MathUtilities.Divide(thisLayerPatchSolute[k], thisLayersTotalSolute, 0.0); } } // 4- Compute the partitioned values for each patch for (int k = 0; k < patches.Count; k++) { Result[k][layer] = (incomingDelta[layer] * partitionWeight[k]) / patches[k].RelativeArea; } } else { // there is no incoming solute for this layer for (int k = 0; k < patches.Count; k++) { Result[k][layer] = 0.0; } } } } catch (Exception e) { throw new Exception($"Problems with partitioning {soluteName} - {e}"); } return(Result); }
/// <summary> /// calculate how the dlt's (C and N) are partitioned amongst patches /// </summary> /// <param name="incomingDelta">The dlt to be partioned amongst patches</param> /// <param name="SoluteName">The solute or pool that is changing</param> /// <param name="PartitionType">The type of partition to be used</param> /// <returns>The values of dlt partitioned for each existing patch</returns> private double[][] partitionDelta(double[] incomingDelta, string SoluteName, PartitionApproachEnum PartitionType) { int nPatches = Patch.Count; // 1. initialise the result array double[][] Result = new double[nPatches][]; for (int k = 0; k < nPatches; k++) { Result[k] = new double[nLayers]; } try { if (senderModule.Equals("Plant", StringComparison.InvariantCultureIgnoreCase)) { for (int k = 0; k < nPatches; k++) { Patch[k].CalcTotalMineralNInRootZone(); } } // 2- gather how much solute is already in the soil double[][] existingSoluteAmount = new double[nPatches][]; for (int k = 0; k < nPatches; k++) { switch (SoluteName.ToUpper()) { case "UREA": existingSoluteAmount[k] = Patch[k].urea; break; case "NH4": if (senderModule.Equals("Plant", StringComparison.InvariantCultureIgnoreCase)) { existingSoluteAmount[k] = Patch[k].nh4AvailableToPlants; } else { existingSoluteAmount[k] = Patch[k].nh4; } break; case "NO3": if (senderModule.Equals("Plant", StringComparison.InvariantCultureIgnoreCase)) { existingSoluteAmount[k] = Patch[k].no3AvailableToPlants; } else { existingSoluteAmount[k] = Patch[k].no3; } break; default: throw new Exception(" The solute " + SoluteName + " is not recognised by SoilNitrogen - solute partition"); } } // 3- calculate partition weighting factors, done for each layer based on existing solute amount double[] partitionWeight; double[] thisLayerPatchSolute; double thisLayersTotalSolute; for (int layer = 0; layer < (nLayers); layer++) { if (Math.Abs(incomingDelta[layer]) > epsilon) { // 3.1- zero and initialise the variables partitionWeight = new double[nPatches]; thisLayerPatchSolute = new double[nPatches]; // 3.2- get the solute amounts for each patch in this layer if ((PartitionType == PartitionApproachEnum.BasedOnLayerConcentration) || (PartitionType == PartitionApproachEnum.BasedOnConcentrationAndDelta && incomingDelta[layer] < epsilon)) { for (int k = 0; k < nPatches; k++) { thisLayerPatchSolute[k] = existingSoluteAmount[k][layer] * Patch[k].RelativeArea; } } else if ((PartitionType == PartitionApproachEnum.BasedOnSoilConcentration) || (PartitionType == PartitionApproachEnum.BasedOnConcentrationAndDelta && incomingDelta[layer] >= epsilon)) { for (int k = 0; k < nPatches; k++) { double layerUsed = 0.0; for (int z = layer; z >= 0; z--) // goes backwards till soil surface (but may stop before that) { thisLayerPatchSolute[k] += existingSoluteAmount[k][z]; layerUsed += dlayer[z]; if ((LayerForNPartition > epsilon) && (layerUsed >= LayerForNPartition)) { // stop if thickness reaches a defined value z = -1; } } thisLayerPatchSolute[k] *= Patch[k].RelativeArea; } } // 3.3- get the total solute amount for this layer thisLayersTotalSolute = SumDoubleArray(thisLayerPatchSolute); // 3.4- Check whether the existing solute is greater than the incoming delta if (MathUtilities.IsLessThan(thisLayersTotalSolute + incomingDelta[layer], 0)) { string myMessage = "attempt to change " + SoluteName + "[" + (layer + 1) + "] to a negative value"; throw new Exception(myMessage); } // 3.5- Compute the partition weights for each patch for (int k = 0; k < nPatches; k++) { partitionWeight[k] = 0.0; if (thisLayersTotalSolute >= epsilon) { partitionWeight[k] = MathUtilities.Divide(thisLayerPatchSolute[k], thisLayersTotalSolute, 0.0); } } // 4- Compute the partitioned values for each patch for (int k = 0; k < nPatches; k++) { Result[k][layer] = (incomingDelta[layer] * partitionWeight[k]) / Patch[k].RelativeArea; } } else { // there is no incoming solute for this layer for (int k = 0; k < nPatches; k++) { Result[k][layer] = 0.0; } } } } catch (Exception e) { throw new Exception(" problems with partitioning " + SoluteName + " - " + e.ToString()); } return(Result); }