private double IntergrateElevationsForAccumulation(double lowerElevation, double upperElevation, LinearPerPartFunction accumulationLookup) { var lowerLoad = accumulationLookup.f(lowerElevation); var upperLoad = accumulationLookup.f(upperElevation); var elevationPoints = accumulationLookup.Select(p => p.Key).Where(p => p > lowerElevation && p < upperElevation).ToArray(); var loads = elevationPoints.Select(p => accumulationLookup.f(p)).ToList(); //change here, get all loads, not sum var areas = elevationPoints.Select(p => Areal.AreaForHeightLookup(p) * 0.0001).ToList(); // get areas as well loads.Insert(0, lowerLoad); loads.Add(upperLoad); areas.Insert(0, Areal.AreaForHeightLookup(lowerElevation) * 0.0001); areas.Add(Areal.AreaForHeightLookup(upperElevation) * 0.0001); var lowerElevationPoints = accumulationLookup.Where(p => p.Key < lowerElevation); var previousElevationPoint = lowerElevationPoints.Any() ? lowerElevationPoints.Last().Key : lowerElevation; var previousLoad = accumulationLookup.f(previousElevationPoint); var previousArea = Areal.AreaForHeightLookup(previousElevationPoint) * 0.0001; var totalLoad = 0d; var totalAreaBetween = 0d; // start at the second element for (var i = 0; i < loads.Count; i++) { double pArea; double pLoad; if (i == 0) { pArea = previousArea; pLoad = previousLoad; } else { pArea = areas[i - 1]; pLoad = loads[i - 1]; } var areaBetween = areas[i] - pArea; totalLoad += areaBetween * (loads[i] + pLoad) / 2; totalAreaBetween += areaBetween; } // if we're accumulating across no area then our rate is 0 if (totalAreaBetween <= 0) { return(0); } return(totalLoad / totalAreaBetween); }
/// <summary> /// Initialises the Zones using the "InitialLeafDryMatter" lookup tables /// /// The precise logic used here depends on the nature of the <see cref="Zones"/> collection /// </summary> private void InitialiseMultipleZones() { // Get the min and max heights of the storage var minHeight = FloodplainElevation; var maxHeight = Areal.MaxElevation; // Build a collection of all the unique points when the leaf matter changes // Include the Min and Max elevations as valid points var uniqueHeights = new List <double> { minHeight, maxHeight }; foreach (var height in InitialLeafDryMatterReadilyDegradable.ToUnsortedArray().Select(p => p.Key)) { if (height > minHeight) { uniqueHeights.Add(height); } } foreach (var height in InitialLeafDryMatterNonReadilyDegradable.ToUnsortedArray().Select(p => p.Key)) { if (height > minHeight) { uniqueHeights.Add(height); } } // Add the current elevation uniqueHeights.Add(Elevation); // Process in increasing order var increasingHeights = uniqueHeights.OrderBy(h => h).ToArray(); // Due to the odd nature of the Zones collection, we shall sort the Zones into Dry and Wet var increasingWetZoneArray = new List <FloodplainData>(); var tempDryZoneArray = new List <FloodplainData>(); var previousCumulativeArea = 0.0; // Create the first zone from the lowest height to the next lowest height var previousHeight = increasingHeights[0]; for (var i = 1; i < increasingHeights.Length; i++) { var height = increasingHeights[i]; // Avoid duplicates if (height.EqualWithTolerance(previousHeight)) { continue; } // Anything at or below the current storage level is considered Wet var isWet = height <= Elevation; var area = Fac * Areal.AreaForHeightLookup(height); // Get the initial leaf matter settings var leafDryMatterNonReadilyDegradable = IntergrateElevationsForAccumulation( previousHeight, height, InitialLeafDryMatterNonReadilyDegradable); var leafDryMatterReadilyDegradable = IntergrateElevationsForAccumulation( previousHeight, height, InitialLeafDryMatterReadilyDegradable); // Create the zone var newZone = new FloodplainData(isWet) { CumulativeAreaM2 = area, // Set the area this zone covers ZoneAreaM2 = area - previousCumulativeArea, ElevationM = height, LeafDryMatterNonReadilyDegradable = leafDryMatterNonReadilyDegradable, LeafDryMatterReadilyDegradable = leafDryMatterReadilyDegradable, }; // Keep track of the last area previousCumulativeArea = area; previousHeight = height; // Sort the zone into wet and dry if (newZone.Dry) { tempDryZoneArray.Add(newZone); } else { increasingWetZoneArray.Add(newZone); } } // The Zones collection has a very specific state. // This state has been carried over (presumably) from the FORTRAN implemenation. // The rules are... // 1. Dry zones are grouped together and come first in the collection // 2. Dry zones are ordered by DECREASING area // 3. Wet zones are grouped together and come last in the collection // 4. Wet zones are ordered by INCREASING area tempDryZoneArray.Reverse(); Zones.AddRange(tempDryZoneArray); Zones.AddRange(increasingWetZoneArray); // Also set the PreviousArea to the current Area so the Zones arenconsidered "stable" on the first timestep PreviousArea = Areal.Area; }