/// <summary> /// Performs checks to determine whether a Facility has required parameters assigned, and that /// all parameters are valid. /// </summary> /// <param name="facility">The Facility to validate</param> /// <param name="category">The Hierarchy Category the facility will be validated against</param> /// <param name="message">A message indicating the first error found in an invalid facility, /// or "Valid Configuration" if the facility is valid.</param> /// <returns>True if the facility parameters are valid, otherwise false</returns> public static bool Validate(Facility facility, int category, out string message) { facility._validConfiguration = true; switch (category) { case 1: if (facility._configuration != FacilityConfiguration.A && facility._configuration != FacilityConfiguration.B) { message = "Only Configuration A or B may be used for Category 1"; facility._validConfiguration = false; return(false); } break; case 2: break; case 3: if (facility._configuration == FacilityConfiguration.E || facility._configuration == FacilityConfiguration.F) { message = "Configuration E and F may not be used for Category 3"; facility._validConfiguration = false; return(false); } break; case 4: if (facility._configuration == FacilityConfiguration.E || facility._configuration == FacilityConfiguration.F) { message = "Configuration E and F may not be used for Category 4"; facility._validConfiguration = false; return(false); } break; default: message = "Unknown configuration, unable to validate facility"; facility._validConfiguration = false; return(false); } Stack <string> invalidated = new Stack <string>(); switch (facility.Type) { case FacilityType.Basin: { switch (facility.Shape) { case FacilityShape.Rectangle: if (facility.BottomWidthFt < 0) { invalidated.Push("Bottom Width must be greater than or equal to zero."); } if (facility.SideSlopeRatio < 0) { invalidated.Push("Side Slope Ratio must be greater than or equal to zero."); } if (facility.FreeboardIn < 0) { invalidated.Push("Freeboard must be greater than or equal to zero."); } break; case FacilityShape.Amoeba: if (facility.BottomPerimeterLengthFt < Math.Sqrt(4 * facility.BottomAreaSqFt * Math.PI)) { invalidated.Push("Bottom Perimeter Length must be greater than or equal to the circumference of a circle with area equal to bottom area."); } if (facility.SideSlopeRatio < 0) { invalidated.Push("Side Slope Ratio must be greater than or equal to zero."); } if (facility.FreeboardIn < 0) { invalidated.Push("Freeboard must be greater than or equal to zero."); } break; case FacilityShape.UserDefined: if (facility.SurfaceAreaAtStorageDepth1SqFt < facility.BottomAreaSqFt) { invalidated.Push("Surface Area at Storage Depth 1 must be greater than or equal to the Bottom Area."); } if (facility.HasSecondaryOverflow && (facility.SurfaceAreaAtStorageDepth2SqFt < facility.SurfaceAreaAtStorageDepth1SqFt)) { invalidated.Push("Surface Area at Storage Depth 2 must be greater than or equal to the Surface Area at Storage Depth 1."); } break; default: break; } if (facility.BottomAreaSqFt < 0) { invalidated.Push("Bottom Area must be greater than or equal to zero."); } if (facility.StorageDepth1In <= 0) { invalidated.Push("Storage Depth 1 must be greater than zero."); } if (facility.HasRockStorage && (facility.RockStorageBottomAreaSqFt <= 0)) { invalidated.Push("Rock Storage Bottom Area must be greater than zero."); } if (facility.HasSecondaryOverflow && (facility.StorageDepth2In <= facility.StorageDepth1In)) { invalidated.Push("Storage Depth 2 must be greater than or equal to Storage Depth 1."); } break; } case FacilityType.PlanterFlat: { if (facility.BottomAreaSqFt <= 0) { invalidated.Push("Bottom Area must be greater than zero."); } if (facility.StorageDepth1In <= 0) { invalidated.Push("Storage Depth 1 must be greater than zero."); } if (facility.HasRockStorage && (facility.RockStorageBottomAreaSqFt < 0)) { invalidated.Push("Rock Storage Bottom Area must be greater than or equal to zero."); } if (facility.HasSecondaryOverflow && (facility.StorageDepth2In <= facility.StorageDepth1In)) { invalidated.Push("Storage Depth 2 must be greater than Storage Depth 1."); } break; } case FacilityType.PlanterSloped: case FacilityType.Swale: { if (facility is SlopedFacility) { SlopedFacility slopedFacility = facility as SlopedFacility; foreach (SlopedFacilitySegment segment in slopedFacility.Segments) { if (segment.SegmentLengthFt <= 0) { invalidated.Push("Segment " + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Segment Length must be greater than zero."); } if (segment.CheckDamLengthFt < 0) { invalidated.Push("Segment " + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Check Dam Length must be greater than or equal to zero."); } if (segment.SlopeRatio < 0) { invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Slope must be greater than or equal to zero."); } if (segment.BottomWidthFt < 0) { invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Bottom Width must be greater than or equal to zero."); } if (segment.SideSlopeRightRatio < 0) { invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Side Slope Ratio Right must be greater than or equal to zero."); } if (segment.SideSlopeLeftRatio < 0) { invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Side Slope Ratio Left must be greater than or equal to zero."); } if (segment.DownstreamDepthIn <= 0) { invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Downstream Depth must be greater than zero."); } if (segment.LandscapeWidthFt < segment.DownstreamTopWidthFt) { invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Landscape Width must contain facility segment at Downstream Depth."); } if (facility.HasRockStorage && (segment.RockStorageWidthFt <= 0)) { invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Rock Storage Width must be greater than zero."); } } if (facility.HasSecondaryOverflow && (facility.StorageDepth2In <= slopedFacility.Segments.Last().DownstreamDepthIn)) { invalidated.Push("Storage Depth 2 must be greater than the Downstream Depth of the final facility segment."); } } break; } default: break; } if (facility.GrowingMediumDepthIn <= 0) { invalidated.Push("Growing Medium Depth must be greater than zero."); } if (facility.HasRockStorage) { if (facility.RockStorageDepthIn <= 0) { invalidated.Push("Rock Storage Depth must be greater than zero."); } if (facility.RockVoidRatio <= 0 || facility.RockVoidRatio >= 1) { invalidated.Push("Rock Porosity must be between zero and one."); } } if ((facility.Configuration == FacilityConfiguration.F || facility.Configuration == FacilityConfiguration.C) && ((facility.StorageDepth3In < 0) || (facility.StorageDepth3In > facility._rockStorageDepthIn))) { invalidated.Push("Storage Depth 3 must be greater than or equal to zero and less than or equal to the Rock Storage Depth."); } if (facility.Catchment.DesignInfiltrationNativeInchesPerHour < 0) { invalidated.Push("Native Infiltration must be greater than or equal to zero."); } if (facility.Catchment.ImportedMediumInfiltrationInchesPerHour < 0) { invalidated.Push("Imported Medium Infiltration must be greater than or equal to zero."); } if (invalidated.Count() > 0) { message = string.Empty; while (invalidated.Count() > 0) { message += invalidated.Pop(); if (invalidated.Count != 0) { message += System.Environment.NewLine; } } facility._validConfiguration = false; return(false); } message = "Valid configuration."; return(facility._validConfiguration); }
/// <summary> /// Contains algorithims implementing the sizing calculations for various facility configurations /// </summary> /// <param name="facility"></param> /// <param name="category"></param> /// <param name="catchment"></param> /// <param name="inflowHydrograph"></param> /// <returns></returns> public static StormEventResults PerformCalculations(Facility facility, HierarchyCategory category, Catchment catchment, Hydrograph inflowHydrograph) { string message; Facility.Validate(facility, Convert.ToInt32(category), out message); if (!facility.IsValid) throw new ArgumentException("Unable to perform calculations: failed validation with message '" + message + "'"); double dt = inflowHydrograph.TimeStepMinutes; int timeSteps = inflowHydrograph.AsArray().Count(); double inflowFromRain; double inflowVolume; double inflowVolumeCummulative = 0; double[] surfaceInfiltrationCapacity = new double[timeSteps]; //Column E, aka Percolation Capacity double initialInfiltrationToAmendedSoil; double[] storedToBeInfiltrated = new double[timeSteps]; double graphLocator = 0; double potentialExtraInfiltration; double cumulativeInfiltrationVolume = 0; //Column J double additionalInfiltrationFromStorage; //Column K double[] totalInfiltrationCapacityToBelowGrade = new double[timeSteps]; //Column L double inflowToSurfaceStorageAfterInfiltration; //Column M double[] inflowMinusInfiltration = new double[timeSteps]; //Column N double surfaceStorageCumulativeVolume = 0; //Column O,P,Q,R combined. double[] flowOvertoppingToUnderdrain = new double[timeSteps]; //Column T (or Column W for type E) double[] infiltrationToBelowGrade = new double[timeSteps]; //Column Z double[] totalFlowToBelowGrade = new double[timeSteps]; //Column AA double[] inflowToRockStorage = new double[timeSteps]; //column AB double[] totalInfiltrationCapacityToNative = new double[timeSteps]; //Column AE double totalInfiltratedToNative = 0; double rockStorageCumulativeVolume = 0; //Column AJ double[] rockPercentCapacity = new double[timeSteps]; //Column AK/AL for A,B&E facilities; Column AR/AS for C&F; double excessRockCumulativeVolume = 0; //Column AM double underdrainStorageAreaCumulativeVolume = 0; //Column AR double[] aboveGradeStoragePercentCapacity = new double[timeSteps]; //Column AO for A,B facilities; Column S for C,D&F double[] aboveGradeSecondaryStoragePercentCapacity = new double[timeSteps]; //Column AO for E facilities double[] rockOverflowToEscapeRoute = new double[timeSteps]; //Column AP double[] overflowToEscapeRoute = new double[timeSteps]; //Column AQ for A,B,D,E,F facilities; Column AT for C&F double nativeInfiltrationRate = facility.NativeSoilInfiltrationCapacityCfs(); double growingMediumInfiltrationRate = facility.ImportedMediumInfiltrationCapacityCfs(); //The Lag Index is a property of the growing medium depth and infiltration rate that //corresponds to the number of time steps it will take for water to percolate through the //growing medium. The infiltration hydrograph to the rock medium is delayed by the lag index. //For facility configurations A, B, and E, no lag is applied when the native infiltration rate is less //than the growing medium infilration rate. double lagFactor = (facility.Configuration == FacilityConfiguration.A || facility.Configuration == FacilityConfiguration.B || facility.Configuration == FacilityConfiguration.E) && nativeInfiltrationRate < growingMediumInfiltrationRate ? 0 : facility.GrowingMediumPorespace; double lagTime = facility.GrowingMediumDepthIn / catchment.ImportedMediumInfiltrationInchesPerHour * 60 * lagFactor; int lag = (int)Math.Ceiling(lagTime / inflowHydrograph.TimeStepMinutes); // Rounding up is perfored in the current calculator. This may be unnecessary. for (int i = 0; i < timeSteps; i++) { inflowFromRain = inflowHydrograph.AsArray()[i]; inflowVolume = inflowFromRain * 600; inflowVolumeCummulative += inflowVolume; //Facility configurations A,B, and D have rock-influenced surface storage demand. When //the rock storage is full, infiltration rates in the growing medium can be limited by //infiltration rates of the native soil. if (facility.HasRockInfluencedSurfaceStorage && rockPercentCapacity[Math.Max(1, i - 1)] >= 1) surfaceInfiltrationCapacity[i] = Math.Min(growingMediumInfiltrationRate, nativeInfiltrationRate); else surfaceInfiltrationCapacity[i] = growingMediumInfiltrationRate; initialInfiltrationToAmendedSoil = Math.Min(inflowFromRain, surfaceInfiltrationCapacity[i]); storedToBeInfiltrated[i] = Math.Max(inflowFromRain - initialInfiltrationToAmendedSoil, 0) * 600; graphLocator = storedToBeInfiltrated[i] - storedToBeInfiltrated[Math.Max(1, i - 1)] < 0 ? 1 : graphLocator; potentialExtraInfiltration = (surfaceInfiltrationCapacity[i] - initialInfiltrationToAmendedSoil) * graphLocator; cumulativeInfiltrationVolume += (potentialExtraInfiltration * 600); // Existing spreadsheet rounds up storageToBeInfiltrated to nearest ten, // allowing more potentialExtraInfiltration to fill rock storage. This may be unnecessary... double storedToBeInfiltratedRoundedUp = Math.Ceiling(storedToBeInfiltrated.Sum()/10)*10; double cumulativeInfiltrationVolumeRounded = Math.Round(cumulativeInfiltrationVolume, 10); // Added 6/24/2015 to deal with binary storage of floating point numbers without changing to decimal data type additionalInfiltrationFromStorage = cumulativeInfiltrationVolumeRounded > storedToBeInfiltratedRoundedUp || cumulativeInfiltrationVolumeRounded > facility.SurfaceCapacityAtDepth1CuFt ? 0 : potentialExtraInfiltration; totalInfiltrationCapacityToBelowGrade[i] = initialInfiltrationToAmendedSoil + additionalInfiltrationFromStorage; inflowToSurfaceStorageAfterInfiltration = Math.Max(inflowFromRain - totalInfiltrationCapacityToBelowGrade[i], 0); inflowMinusInfiltration[i] = inflowFromRain - surfaceInfiltrationCapacity[i]; surfaceStorageCumulativeVolume += (inflowMinusInfiltration[i] * 600); surfaceStorageCumulativeVolume = Math.Max(surfaceStorageCumulativeVolume, 0); surfaceStorageCumulativeVolume = Math.Min(surfaceStorageCumulativeVolume, facility.SurfaceCapacityAtDepth1CuFt); flowOvertoppingToUnderdrain[i] = surfaceStorageCumulativeVolume < facility.SurfaceCapacityAtDepth1CuFt ? 0 : inflowToSurfaceStorageAfterInfiltration; if (i - lag < 0) infiltrationToBelowGrade[i] = totalInfiltrationCapacityToBelowGrade[0]; else infiltrationToBelowGrade[i] = totalInfiltrationCapacityToBelowGrade[i - lag]; totalFlowToBelowGrade[i] = infiltrationToBelowGrade[i] + flowOvertoppingToUnderdrain[i]; if (facility.Configuration == FacilityConfiguration.E || facility.Configuration == FacilityConfiguration.F) inflowToRockStorage[i] = totalFlowToBelowGrade[i]; else inflowToRockStorage[i] = infiltrationToBelowGrade[i]; totalInfiltrationCapacityToNative[i] = nativeInfiltrationRate; if (rockStorageCumulativeVolume + inflowToRockStorage[i] - totalInfiltrationCapacityToNative[i] < 0) totalInfiltratedToNative += rockStorageCumulativeVolume + inflowToRockStorage[i]; else totalInfiltratedToNative += totalInfiltrationCapacityToNative[i]; rockStorageCumulativeVolume += ((inflowToRockStorage[i] - totalInfiltrationCapacityToNative[i]) * 600); excessRockCumulativeVolume = rockStorageCumulativeVolume < facility.RockStorageCapacityCuFt ? 0 : rockStorageCumulativeVolume - facility.RockStorageCapacityCuFt; rockStorageCumulativeVolume = Math.Max(rockStorageCumulativeVolume, 0); if (facility.HasRockInfluencedSurfaceStorage) { aboveGradeStoragePercentCapacity[i] = (surfaceStorageCumulativeVolume + excessRockCumulativeVolume) / facility.SurfaceCapacityAtDepth1CuFt; //E facilities have a secondary storage volume if (facility.HasSecondaryOverflow) { aboveGradeSecondaryStoragePercentCapacity[i] = (surfaceStorageCumulativeVolume + excessRockCumulativeVolume) / facility.SurfaceCapacityAtDepth2CuFt; aboveGradeSecondaryStoragePercentCapacity[i] = Math.Min(aboveGradeSecondaryStoragePercentCapacity[i], 1); } } //C, D, and F facilities have a direct connection from the rock gallery to an overflow, //and therefore above grade storage capacity is independent of rock gallery volume else { aboveGradeStoragePercentCapacity[i] = surfaceStorageCumulativeVolume / facility.SurfaceCapacityAtDepth1CuFt; } aboveGradeStoragePercentCapacity[i] = Math.Min(aboveGradeStoragePercentCapacity[i], 1); if (facility.HasRockInfluencedSurfaceStorage && !facility.HasSecondaryOverflow) // A, B { // Added 6/22/2015 to handle case where surface is full, but should overflow more due to limited rock gallery. // This will occur only once during a storm, as the next cycle will limit above grade infiltration to the rock gallery // and overflow the correct amount. double belowGradeInflowMinusInfiltration = inflowToRockStorage[i] - totalInfiltrationCapacityToNative[i]; if (aboveGradeStoragePercentCapacity[i] == 1 && belowGradeInflowMinusInfiltration > 0 ) rockOverflowToEscapeRoute[i] = belowGradeInflowMinusInfiltration; else rockOverflowToEscapeRoute[i] = 0; rockPercentCapacity[i] = facility.HasRockStorage ? rockStorageCumulativeVolume / facility.RockStorageCapacityCuFt : 1; rockPercentCapacity[i] = Math.Min(rockPercentCapacity[i], 1); } else if (facility.HasSecondaryOverflow) // E { rockOverflowToEscapeRoute[i] = aboveGradeSecondaryStoragePercentCapacity[i] < 1 ? 0 : Math.Max(totalFlowToBelowGrade[i] - facility.NativeSoilInfiltrationCapacityCfs(), 0); rockPercentCapacity[i] = facility.HasRockStorage ? rockStorageCumulativeVolume / facility.RockStorageCapacityCuFt : 1; rockPercentCapacity[i] = Math.Min(rockPercentCapacity[i], 1); } else if (!facility.HasRockInfluencedSurfaceStorage) // C, D & F { underdrainStorageAreaCumulativeVolume += ((inflowToRockStorage[i] - totalInfiltrationCapacityToNative[i]) * 600); underdrainStorageAreaCumulativeVolume = Math.Max(underdrainStorageAreaCumulativeVolume, 0); underdrainStorageAreaCumulativeVolume = Math.Min(underdrainStorageAreaCumulativeVolume, facility.RockStorageCapacityCuFt); if ((underdrainStorageAreaCumulativeVolume / facility.RockStorageCapacityCuFt >= 1) || facility.RockStorageCapacityCuFt == 0) // Added facility.RockStorageCapacityCuFt for 0/0 case rockOverflowToEscapeRoute[i] = inflowToRockStorage[i] - totalInfiltrationCapacityToNative[i]; rockPercentCapacity[i] = facility.HasRockStorage ? underdrainStorageAreaCumulativeVolume / facility.RockStorageCapacityCuFt : 1; rockPercentCapacity[i] = Math.Min(rockPercentCapacity[i], 1); } if (facility.Configuration == FacilityConfiguration.F // Facility F is different, in that overflow from the surface goes to the subsurface || facility.Configuration == FacilityConfiguration.E) // Facility E is strange also, in that the flow overtopping to the underdrain doesn't also go to the escape route. overflowToEscapeRoute[i] = excessRockCumulativeVolume > 0 ? rockOverflowToEscapeRoute[i] : 0; else overflowToEscapeRoute[i] = excessRockCumulativeVolume > 0 ? flowOvertoppingToUnderdrain[i] + rockOverflowToEscapeRoute[i] : flowOvertoppingToUnderdrain[i]; } StormEventResults results = new StormEventResults(); results.PeakSurfaceOverflow = flowOvertoppingToUnderdrain.Max(); results.PeakOverflow = overflowToEscapeRoute.Max(); results.OverflowVolume = overflowToEscapeRoute.Sum() * 600; results.PercentSurfaceCapacityUsed = aboveGradeStoragePercentCapacity.Max(); results.PercentRockCapacityUsed = rockPercentCapacity.Max(); results.InflowVolume = inflowHydrograph.AsArray().Sum() * 600; results.PeakInflowRate = inflowHydrograph.AsArray().Max(); results.AboveGradePrimaryResults.Add(new Hydrograph("Inflow from rain", "cfs", inflowHydrograph.AsArray(), dt)); results.AboveGradePrimaryResults.Add(new Hydrograph("Infiltration capacity", "cfs", surfaceInfiltrationCapacity, dt)); switch (facility.Configuration) { case FacilityConfiguration.A: results.AboveGradePrimaryResults.Add(new Hydrograph("Infiltration to native soil", "cfs", infiltrationToBelowGrade, dt)); results.AboveGradePrimaryResults.Add(new Hydrograph("Overflow to approved discharge", "cfs", overflowToEscapeRoute, dt)); results.AboveGradeSecondaryResults.Add(new Hydrograph("Percent surface capacity", "%", aboveGradeStoragePercentCapacity, dt)); results.PercentRockCapacityUsed = -1; // There is no rock gallery. break; case FacilityConfiguration.B: results.AboveGradePrimaryResults.Add(new Hydrograph("Percolation to below grade storage", "cfs", infiltrationToBelowGrade, dt)); results.AboveGradePrimaryResults.Add(new Hydrograph("Overflow to approved discharge", "cfs", overflowToEscapeRoute, dt)); results.AboveGradeSecondaryResults.Add(new Hydrograph("Percent surface capacity", "%", aboveGradeStoragePercentCapacity, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Inflow to rock storage", "cfs", infiltrationToBelowGrade, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Infiltration capacity", "cfs", totalInfiltrationCapacityToNative, dt)); results.BelowGradeSecondaryResults.Add(new Hydrograph("Percent rock capacity", "%", rockPercentCapacity, dt)); break; case FacilityConfiguration.C: results.AboveGradePrimaryResults.Add(new Hydrograph("Total flow to below grade storage", "cfs", inflowToRockStorage, dt)); results.AboveGradePrimaryResults.Add(new Hydrograph("Flow bypassing growing medium", "cfs", flowOvertoppingToUnderdrain, dt)); results.AboveGradeSecondaryResults.Add(new Hydrograph("Percent surface capacity", "%", aboveGradeStoragePercentCapacity, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Inflow to rock storage", "cfs", inflowToRockStorage, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Infiltration capacity", "cfs", totalInfiltrationCapacityToNative, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Overflow to approved discharge", "cfs", rockOverflowToEscapeRoute, dt)); results.BelowGradeSecondaryResults.Add(new Hydrograph("Percent rock capacity", "%", rockPercentCapacity, dt)); break; case FacilityConfiguration.D: results.AboveGradePrimaryResults.Add(new Hydrograph("Total flow to below grade storage", "cfs", inflowToRockStorage, dt)); results.AboveGradePrimaryResults.Add(new Hydrograph("Flow bypassing growing medium", "cfs", flowOvertoppingToUnderdrain, dt)); results.AboveGradeSecondaryResults.Add(new Hydrograph("Percent surface capacity", "%", aboveGradeStoragePercentCapacity, dt)); break; case FacilityConfiguration.E: results.AboveGradePrimaryResults.Add(new Hydrograph("Overflow to approved discharge", "cfs", rockOverflowToEscapeRoute, dt)); results.AboveGradePrimaryResults.Add(new Hydrograph("Total flow to below grade storage", "cfs", inflowToRockStorage, dt)); results.AboveGradeSecondaryResults.Add(new Hydrograph("Percent surface capacity", "%", aboveGradeSecondaryStoragePercentCapacity, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Inflow to rock storage", "cfs", inflowToRockStorage, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Infiltration capacity", "cfs", totalInfiltrationCapacityToNative, dt)); results.BelowGradeSecondaryResults.Add(new Hydrograph("Percent rock capacity", "%", rockPercentCapacity, dt)); results.PercentSurfaceCapacityUsed = aboveGradeSecondaryStoragePercentCapacity.Max(); break; case FacilityConfiguration.F: results.AboveGradePrimaryResults.Add(new Hydrograph("Total flow to below grade storage", "cfs", inflowToRockStorage, dt)); results.AboveGradePrimaryResults.Add(new Hydrograph("Flow bypassing growing medium", "cfs", flowOvertoppingToUnderdrain, dt)); results.AboveGradeSecondaryResults.Add(new Hydrograph("Percent surface capacity", "%", aboveGradeStoragePercentCapacity, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Inflow to rock storage", "cfs", inflowToRockStorage, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Infiltration capacity", "cfs", totalInfiltrationCapacityToNative, dt)); results.BelowGradePrimaryResults.Add(new Hydrograph("Overflow to approved discharge", "cfs", rockOverflowToEscapeRoute, dt)); results.BelowGradeSecondaryResults.Add(new Hydrograph("Percent rock capacity", "%", rockPercentCapacity, dt)); break; } return results; }
/// <summary> /// Executes only the SBUH calculations. /// </summary> /// <param name="catchment">The catchment to calculate the SBUH results.</param> /// <returns>A PacResults object which is only minimally populated with results data. /// The *PeakInflow and *InflowVolume fields will be populated for each storm event.</returns> public static PacResults PerformSbuhCalcs(Catchment catchment) { Facility dummyFacility = new Facility(FacilityType.Basin, FacilityConfiguration.A, catchment) { BottomAreaSqFt = 100, BottomWidthFt = 10, SideSlopeRatio = 3, StorageDepth1In = 9, GrowingMediumDepthIn = 18, FreeboardIn = 3, Shape = FacilityShape.Rectangle }; return PerformCalculations(catchment, dummyFacility, 1); }
/// <summary> /// Executes the calculator and returns a PacResults. /// </summary> /// <param name="catchment">A catchment object defining the hydrologic parameters of the post-developed catchment area to be evaluated.</param> /// <param name="preCatchment">A catchment object defining the hydrologic parameters of the pre-developed catchment area to be evaluated.</param> /// <param name="facility">A Facility object defining the stormwater management facility to be evaluated.</param> /// <param name="category">Identifies the HierarchyCategory the proposed facility will be evaluated against.</param> /// <param name="dischargePoint">Identifies the DischargePoint of the proposed facility.</param> /// <returns>A PacResults object containing the results of the calculation.</returns> internal static PacResults PerformCalculations(Catchment catchment, Catchment preCatchment, Facility facility, HierarchyCategory category, DischargePoint dischargePoint) { //Define design storms RainfallEvent pollutionReduction = RainfallEvent.GetScsOneAEvent("Pollution Reduction", 0.83); RainfallEvent twoYear = RainfallEvent.GetScsOneAEvent("Two-Year", 2.4); RainfallEvent fiveYear = RainfallEvent.GetScsOneAEvent("Five-Year", 2.9); RainfallEvent tenYear = RainfallEvent.GetScsOneAEvent("Ten-Year", 3.4); RainfallEvent twentyFiveYear = RainfallEvent.GetScsOneAEvent("Twentyfive-Year", 3.9); PacResults results = new PacResults(); //Calculate hydrographs for the most important design storms Hydrograph imperviousHydrographPR = SantaBarbaraUrbanHydrograph.CalculateHydrograph (catchment, pollutionReduction); Hydrograph imperviousHydrographTwoYear = SantaBarbaraUrbanHydrograph.CalculateHydrograph (catchment, twoYear); Hydrograph imperviousHydrographFiveYear = SantaBarbaraUrbanHydrograph.CalculateHydrograph (catchment, fiveYear); Hydrograph imperviousHydrographTenYear = SantaBarbaraUrbanHydrograph.CalculateHydrograph (catchment, tenYear); Hydrograph imperviousHydrographTwentyfiveYear = SantaBarbaraUrbanHydrograph.CalculateHydrograph (catchment, twentyFiveYear); results.PollutionReductionResults = ReservoirRouter.PerformCalculations(facility, category, catchment, imperviousHydrographPR); results.PollutionReductionPeakOverflow = results.PollutionReductionResults.PeakOverflow; results.PollutionReductionTotalOverflowVolume = results.PollutionReductionResults.OverflowVolume; results.PollutionReductionSurfaceCapacity = results.PollutionReductionResults.PercentSurfaceCapacityUsed; results.PollutionReductionPercentRockCapacity = results.PollutionReductionResults.PercentRockCapacityUsed; results.PollutionReductionInflowVolume = results.PollutionReductionResults.InflowVolume; results.PollutionReductionPeakInflow = results.PollutionReductionResults.PeakInflowRate; results.TwoYearResults = ReservoirRouter.PerformCalculations(facility, category, catchment, imperviousHydrographTwoYear); results.TwoYearPeakOverflow = results.TwoYearResults.PeakOverflow; results.TwoYearTotalOverflowVolume = results.TwoYearResults.OverflowVolume; results.TwoYearInflowVolume = results.TwoYearResults.InflowVolume; results.TwoYearPeakInflow = results.TwoYearResults.PeakInflowRate; results.FiveYearResults = ReservoirRouter.PerformCalculations(facility, category, catchment, imperviousHydrographFiveYear); results.FiveYearPeakOverflow = results.FiveYearResults.PeakOverflow; results.FiveYearTotalOverflowVolume = results.FiveYearResults.OverflowVolume; results.FiveYearInflowVolume = results.FiveYearResults.InflowVolume; results.FiveYearPeakInflow = results.FiveYearResults.PeakInflowRate; results.TenYearResults = ReservoirRouter.PerformCalculations(facility, category, catchment, imperviousHydrographTenYear); results.TenYearPeakOverflow = results.TenYearResults.PeakOverflow; results.TenYearTotalOverflowVolume = results.TenYearResults.OverflowVolume; results.TenYearSurfaceCapacity = results.TenYearResults.PercentSurfaceCapacityUsed; results.TenYearPercentRockCapacity = results.TenYearResults.PercentRockCapacityUsed; results.TenYearInflowVolume = results.TenYearResults.InflowVolume; results.TenYearPeakInflow = results.TenYearResults.PeakInflowRate; results.TwentyfiveYearResults = ReservoirRouter.PerformCalculations(facility, category, catchment, imperviousHydrographTwentyfiveYear); results.TwentyfiveYearPeakOverflow = results.TwentyfiveYearResults.PeakOverflow; results.TwentyfiveYearTotalOverflowVolume = results.TwentyfiveYearResults.OverflowVolume; results.TwentyfiveYearInflowVolume = results.TwentyfiveYearResults.InflowVolume; results.TwentyfiveYearPeakInflow = results.TwentyfiveYearResults.PeakInflowRate; results.TenYearScore = PacScore.NotUsed; // Defaults results.FlowControlScore = PacScore.NotUsed; results.TwoYearFlowControlScore = PacScore.NotUsed; results.FiveYearFlowControlScore = PacScore.NotUsed; results.TenYearFlowControlScore = PacScore.NotUsed; results.TwentyfiveYearFlowControlScore = PacScore.NotUsed; switch (category) { case HierarchyCategory.Category1: case HierarchyCategory.Category2: results.TenYearScore = results.TenYearPeakOverflow > 0 ? PacScore.Fail : PacScore.Pass; break; case HierarchyCategory.Category3: //Define preliminary catchment runoff results results.PreDevelopedTwoYearPeakInflow = ReservoirRouter.PerformCalculations(facility, category, preCatchment, SantaBarbaraUrbanHydrograph.CalculateHydrograph(preCatchment, twoYear)).PeakInflowRate; results.PreDevelopedFiveYearPeakInflow = ReservoirRouter.PerformCalculations(facility, category, preCatchment, SantaBarbaraUrbanHydrograph.CalculateHydrograph(preCatchment, fiveYear)).PeakInflowRate; results.PreDevelopedTenYearPeakInflow = ReservoirRouter.PerformCalculations(facility, category, preCatchment, SantaBarbaraUrbanHydrograph.CalculateHydrograph(preCatchment, tenYear)).PeakInflowRate; results.PreDevelopedTwentyfiveYearPeakInflow = ReservoirRouter.PerformCalculations(facility, category, preCatchment, SantaBarbaraUrbanHydrograph.CalculateHydrograph(preCatchment, twentyFiveYear)).PeakInflowRate; switch (dischargePoint) { case DischargePoint.A: results.FlowControlScore = PacScore.NotUsed; break; case DischargePoint.B: if (results.TwoYearPeakOverflow <= results.PreDevelopedTwoYearPeakInflow / 2) results.TwoYearFlowControlScore = PacScore.Pass; else results.TwoYearFlowControlScore = PacScore.Fail; if (results.FiveYearPeakOverflow <= results.PreDevelopedFiveYearPeakInflow) results.FiveYearFlowControlScore = PacScore.Pass; else results.FiveYearFlowControlScore = PacScore.Fail; if (results.TenYearPeakOverflow <= results.PreDevelopedTenYearPeakInflow) results.TenYearFlowControlScore = PacScore.Pass; else results.TenYearFlowControlScore = PacScore.Fail; if (results.TwentyfiveYearPeakOverflow <= results.PreDevelopedTwentyfiveYearPeakInflow) results.TwentyfiveYearFlowControlScore = PacScore.Pass; else results.TwentyfiveYearFlowControlScore = PacScore.Fail; if (results.TwoYearPeakOverflow <= results.PreDevelopedTwoYearPeakInflow / 2 && results.FiveYearPeakOverflow <= results.PreDevelopedFiveYearPeakInflow && results.TenYearPeakOverflow <= results.PreDevelopedTenYearPeakInflow && results.TwentyfiveYearPeakOverflow <= results.PreDevelopedTwentyfiveYearPeakInflow) results.FlowControlScore = PacScore.Pass; else results.FlowControlScore = PacScore.Fail; break; case DischargePoint.C: if (results.TwoYearPeakOverflow <= results.PreDevelopedTwoYearPeakInflow) results.TwoYearFlowControlScore = PacScore.Pass; else results.TwoYearFlowControlScore = PacScore.Fail; if (results.FiveYearPeakOverflow <= results.PreDevelopedFiveYearPeakInflow) results.FiveYearFlowControlScore = PacScore.Pass; else results.FiveYearFlowControlScore = PacScore.Fail; if (results.TenYearPeakOverflow <= results.PreDevelopedTenYearPeakInflow) results.TenYearFlowControlScore = PacScore.Pass; else results.TenYearFlowControlScore = PacScore.Fail; if (results.TwoYearPeakOverflow <= results.PreDevelopedTwoYearPeakInflow && results.FiveYearPeakOverflow <= results.PreDevelopedFiveYearPeakInflow && results.TenYearPeakOverflow <= results.PreDevelopedTenYearPeakInflow) results.FlowControlScore = PacScore.Pass; else results.FlowControlScore = PacScore.Fail; break; default: break; } break; case HierarchyCategory.Category4: //Define preliminary catchment runoff results results.PreDevelopedTwoYearPeakInflow = ReservoirRouter.PerformCalculations(facility, category, preCatchment, SantaBarbaraUrbanHydrograph.CalculateHydrograph(preCatchment, twoYear)).PeakInflowRate; results.PreDevelopedFiveYearPeakInflow = ReservoirRouter.PerformCalculations(facility, category, preCatchment, SantaBarbaraUrbanHydrograph.CalculateHydrograph(preCatchment, fiveYear)).PeakInflowRate; results.PreDevelopedTenYearPeakInflow = ReservoirRouter.PerformCalculations(facility, category, preCatchment, SantaBarbaraUrbanHydrograph.CalculateHydrograph(preCatchment, tenYear)).PeakInflowRate; results.PreDevelopedTwentyfiveYearPeakInflow = ReservoirRouter.PerformCalculations(facility, category, preCatchment, SantaBarbaraUrbanHydrograph.CalculateHydrograph(preCatchment, twentyFiveYear)).PeakInflowRate; if (results.TwentyfiveYearPeakOverflow <= results.PreDevelopedTenYearPeakInflow) { results.FlowControlScore = PacScore.Pass; results.TwentyfiveYearFlowControlScore = PacScore.Pass; } else { results.FlowControlScore = PacScore.Fail; results.TwentyfiveYearFlowControlScore = PacScore.Fail; } break; default: break; } results.PollutionReductionScore = results.PollutionReductionResults.PeakSurfaceOverflow > 0 ? PacScore.Fail : PacScore.Pass; return results; }
//Category 4 methods don't care about the discharge point /// <summary> /// Executes the calculator and returns a PacResults. Supports Category 4 facilities. /// </summary> /// <param name="catchment">A catchment object defining the hydrologic parameters of the catchment area to be evaluated.</param> /// <param name="preCatchment">A catchment object defining the hydrologic parameters of the pre-developed catchment area to be evaluated.</param> /// <param name="facility">A Facility object defining the stormwater management facility to be evaluated.</param> /// <param name="category">Identifies the Hierarchy Category the proposed facility will be evaluated against. Must be an integer from 1 to 4.</param> /// <returns>A PacResults object containing the results of the calculation.</returns> public static PacResults PerformCalculations(Catchment catchment, Catchment preCatchment, Facility facility, int category) { HierarchyCategory categoryEnum; switch (category) { case 1: categoryEnum = HierarchyCategory.Category1; break; case 2: categoryEnum = HierarchyCategory.Category2; break; case 3: categoryEnum = HierarchyCategory.Category3; break; case 4: categoryEnum = HierarchyCategory.Category4; break; default: throw new ArgumentException("Invalid hierarchy category specified: Must be integer 1 - 4"); } return PerformCalculations(catchment, preCatchment, facility, categoryEnum, DischargePoint.A); }
//Category 3 methods /// <summary> /// Executes the calculator and returns a PacResults. Supports Category 3 facilities. /// </summary> /// <param name="catchment">A catchment object defining the hydrologic parameters of the catchment area to be evaluated.</param> /// <param name="preCatchment">A catchment object defining the hydrologic parameters of the pre-developed catchment area to be evaluated.</param> /// <param name="facility">A Facility object defining the stormwater management facility to be evaluated.</param> /// <param name="category">Identifies the Hierarchy Category the proposed facility will be evaluated against. Must be an integer from 1 to 4.</param> /// <param name="dischargePoint">Identifies the DischargePoint of the proposed facility.</param> /// <returns>A PacResults object containing the results of the calculation.</returns> public static PacResults PerformCalculations(Catchment catchment, Catchment preCatchment, Facility facility, int category, char dischargePoint) { HierarchyCategory categoryEnum; DischargePoint dischargeEnum; switch (category) { case 1: categoryEnum = HierarchyCategory.Category1; break; case 2: categoryEnum = HierarchyCategory.Category2; break; case 3: categoryEnum = HierarchyCategory.Category3; break; case 4: categoryEnum = HierarchyCategory.Category4; break; default: throw new ArgumentException("Invalid hierarchy category specified: Must be integer 1 - 4"); } switch (char.ToUpper(dischargePoint)) { case 'A': dischargeEnum = DischargePoint.A; break; case 'B': dischargeEnum = DischargePoint.B; break; case 'C': dischargeEnum = DischargePoint.C; break; default: throw new ArgumentException("Invalid discharge point specified: Must be character A-C"); } return PerformCalculations(catchment, preCatchment, facility, categoryEnum, dischargeEnum); }
private Facility ValidateFacility(string hierarchy, string facilityType, string configuration) { try { //Read catchment parameters to local variables double imperviousArea = Convert.ToDouble(txtImperviousArea.Text); double curveNumber = Convert.ToDouble(txtCurveNumber.Text); double preCurveNumber = Convert.ToDouble(txtPreCurveNumber.Text); double timeOfConcentration = Convert.ToDouble(txtTimeOfConcentration.Text); double nativeInfiltrationRate = Convert.ToDouble(txtNativeSoilInfiltrationRate.Text); InfiltrationTestType infiltrationTestType; switch (cmbInfiltrationProcedure.SelectedIndex) { case (0): infiltrationTestType = InfiltrationTestType.OpenPitFallingHead; break; case (1): infiltrationTestType = InfiltrationTestType.EncasedFallingHead; break; case (2): infiltrationTestType = InfiltrationTestType.DoubleRingInfiltometer; break; default: infiltrationTestType = InfiltrationTestType.OpenPitFallingHead; break; } //Create catchment object local variables Catchment catchment = new Catchment("Catchment A") { ImperviousAreaSquareFeet = imperviousArea, AcceptableSeparationFromGroundwater = chkMeetsGroundwaterRequirements.Checked, CurveNumber = curveNumber, TimeOfConcentrationMinutes = timeOfConcentration, TestedInfiltrationRateInchesPerHour = nativeInfiltrationRate, InfiltrationTestType = infiltrationTestType }; //Read facility parameters to local variables double bottomArea = Convert.ToDouble(txtBottomArea.Text); double bottomWidth = Convert.ToDouble(txtBottomWidth.Text); double sideSlope = Convert.ToDouble(txtSideSlope.Text); double storageDepth1 = Convert.ToDouble(txtStorageDepth1.Text); double storageDepth2 = Convert.ToDouble(txtStorageDepth2.Text); double storageDepth3 = Convert.ToDouble(txtStorageDepth3.Text); double growingMediumDepth = Convert.ToDouble(txtGrowingMediumDepth.Text); double freeboardDepth = Convert.ToDouble(txtFreeboardDepth.Text); double rockStorageDepth = Convert.ToDouble(txtRockStorageDepth.Text); double rockStorageVoidRatio = Convert.ToDouble(txtRockVoidRatio.Text); double rockStorageBottomArea = Convert.ToDouble(txtRockStorageBottomArea.Text); double surfaceAreaAtStorageDepth1 = Convert.ToDouble(txtSurfaceAreaAtDepth1.Text); double bottomPerimiterLength = Convert.ToDouble(txtBottomPerimeterLength.Text); double surfaceAreaAtStorageDepth2 = Convert.ToDouble(txtSurfaceAreaAtDepth2.Text); FacilityShape shape = GetFacilityShape(cmbFacilityShape.Text); FacilityConfiguration config; config = (FacilityConfiguration)cmbFacilityConfiguration.Text[0]; FacilityType type = GetFacilityType(cmbFacilityType.Text); Facility facility; if (type == FacilityType.Basin || type == FacilityType.PlanterFlat) { facility = new Facility(type, config, catchment) { BottomAreaSqFt = bottomArea, BottomWidthFt = bottomWidth, SideSlopeRatio = sideSlope, StorageDepth1In = storageDepth1, StorageDepth2In = storageDepth2, StorageDepth3In = storageDepth3, GrowingMediumDepthIn = growingMediumDepth, FreeboardIn = freeboardDepth, RockStorageDepthIn = rockStorageDepth, RockVoidRatio = rockStorageVoidRatio, RockStorageBottomAreaSqFt = rockStorageBottomArea, SurfaceAreaAtStorageDepth1SqFt = surfaceAreaAtStorageDepth1, BottomPerimeterLengthFt = bottomPerimiterLength, SurfaceAreaAtStorageDepth2SqFt = surfaceAreaAtStorageDepth2, Shape = shape }; } else //Write sloped facility parameters from UI to facility object and verify results { //ShowSlopedFacilityWS(); List<SlopedFacilitySegment> segments; if (_sfws != null) { if (_sfws.Segments != null) segments = _sfws.Segments; else segments = new List<SlopedFacilitySegment>(); } else segments = new List<SlopedFacilitySegment>(); facility = new SlopedFacility(type, config, catchment, segments) { BottomAreaSqFt = bottomArea, BottomWidthFt = bottomWidth, SideSlopeRatio = sideSlope, StorageDepth1In = storageDepth1, StorageDepth2In = storageDepth2, StorageDepth3In = storageDepth3, GrowingMediumDepthIn = growingMediumDepth, FreeboardIn = freeboardDepth, RockStorageDepthIn = rockStorageDepth, RockVoidRatio = rockStorageVoidRatio, RockStorageBottomAreaSqFt = rockStorageBottomArea, SurfaceAreaAtStorageDepth1SqFt = surfaceAreaAtStorageDepth1, Shape = shape }; } int hierarchyNumber = Convert.ToInt32(hierarchy); HierarchyCategory hierarchyCategory = (HierarchyCategory)hierarchyNumber; string message; _validFacility = Facility.Validate(facility, hierarchyNumber, out message); ToggleUIParameters(facility); if (!_validFacility) DisableUI(message); return facility; } catch (Exception ex) { MessageBox.Show("Error with facility parameters: " + ex.Message); return null; } }
private void ToggleUIParameters(Facility facility) { bool sloped = facility.Type == FacilityType.PlanterSloped || facility.Type == FacilityType.Swale; txtRockStorageDepth.Enabled = facility.HasRockStorage; txtRockVoidRatio.Enabled = facility.HasRockStorage; txtRockStorageBottomArea.Enabled = facility.HasCustomRockStorageBottomArea; if (facility.HasCustomRockStorageBottomArea) txtRockStorageBottomArea.Text = facility.RockStorageBottomAreaSqFt.ToString(); txtFreeboardDepth.Enabled = facility.SpecifyFreeboard; txtStorageDepth1.Enabled = !sloped; //Added per conversation with Richard Davies 10/2/14 txtStorageDepth2.Enabled = facility.HasSecondaryOverflow; if (facility.HasRockStorage) { txtStorageDepth3.Enabled = !facility.HasRockInfluencedSurfaceStorage || (sloped && facility.Configuration == FacilityConfiguration.C); //Added per conversation with Richard Davies 10/2/14 } else txtStorageDepth3.Enabled = false; txtSideSlope.Enabled = facility.SpecifySideSlope; cmbFacilityShape.Enabled = facility.AllowShapeSelection; txtSurfaceAreaAtDepth1.Enabled = facility.Shape == FacilityShape.UserDefined; txtBottomPerimeterLength.Enabled = facility.Shape == FacilityShape.Amoeba; txtSurfaceAreaAtDepth2.Enabled = facility.Shape == FacilityShape.UserDefined && facility.Configuration == FacilityConfiguration.E; btnShowSFWS.Enabled = sloped; txtRockStorageBottomArea.Enabled = !sloped && facility.HasCustomRockStorageBottomArea; txtBottomArea.Enabled = !sloped; txtBottomWidth.Enabled = !sloped && facility.Shape != FacilityShape.Amoeba && facility.Shape != FacilityShape.UserDefined; btnCalculate.Enabled = true; textBox1.ForeColor = SystemColors.WindowText; textBox1.Font = new Font(textBox1.Font, FontStyle.Regular); textBox1.Text = "Valid facility configuration"; }
/// <summary> /// Performs checks to determine whether a Facility has required parameters assigned, and that /// all parameters are valid. /// </summary> /// <param name="facility">The Facility to validate</param> /// <param name="category">The Hierarchy Category the facility will be validated against</param> /// <param name="message">A message indicating the first error found in an invalid facility, /// or "Valid Configuration" if the facility is valid.</param> /// <returns>True if the facility parameters are valid, otherwise false</returns> public static bool Validate(Facility facility, int category, out string message) { facility._validConfiguration = true; switch (category) { case 1: if (facility._configuration != FacilityConfiguration.A && facility._configuration != FacilityConfiguration.B) { message = "Only Configuration A or B may be used for Category 1"; facility._validConfiguration = false; return false; } break; case 2: break; case 3: if (facility._configuration == FacilityConfiguration.E || facility._configuration == FacilityConfiguration.F) { message = "Configuration E and F may not be used for Category 3"; facility._validConfiguration = false; return false; } break; case 4: if (facility._configuration == FacilityConfiguration.E || facility._configuration == FacilityConfiguration.F) { message = "Configuration E and F may not be used for Category 4"; facility._validConfiguration = false; return false; } break; default: message = "Unknown configuration, unable to validate facility"; facility._validConfiguration = false; return false; } Stack<string> invalidated = new Stack<string>(); switch (facility.Type) { case FacilityType.Basin: { switch (facility.Shape) { case FacilityShape.Rectangle: if (facility.BottomWidthFt < 0) invalidated.Push("Bottom Width must be greater than or equal to zero."); if (facility.SideSlopeRatio < 0) invalidated.Push("Side Slope Ratio must be greater than or equal to zero."); if (facility.FreeboardIn < 0) invalidated.Push("Freeboard must be greater than or equal to zero."); break; case FacilityShape.Amoeba: if (facility.BottomPerimeterLengthFt < Math.Sqrt(4 * facility.BottomAreaSqFt * Math.PI)) invalidated.Push("Bottom Perimeter Length must be greater than or equal to the circumference of a circle with area equal to bottom area."); if (facility.SideSlopeRatio < 0) invalidated.Push("Side Slope Ratio must be greater than or equal to zero."); if (facility.FreeboardIn < 0) invalidated.Push("Freeboard must be greater than or equal to zero."); break; case FacilityShape.UserDefined: if (facility.SurfaceAreaAtStorageDepth1SqFt < facility.BottomAreaSqFt) invalidated.Push("Surface Area at Storage Depth 1 must be greater than or equal to the Bottom Area."); if (facility.FreeboardIn < 0) invalidated.Push("Freeboard must be greater than or equal to zero."); if (facility.HasSecondaryOverflow && (facility.SurfaceAreaAtStorageDepth2SqFt < facility.SurfaceAreaAtStorageDepth1SqFt)) invalidated.Push("Surface Area at Storage Depth 2 must be greater than or equal to the Surface Area at Storage Depth 1."); break; default: break; } if (facility.BottomAreaSqFt < 0) invalidated.Push("Bottom Area must be greater than or equal to zero."); if (facility.StorageDepth1In <= 0) invalidated.Push("Storage Depth 1 must be greater than zero."); if (facility.HasRockStorage && (facility.RockStorageBottomAreaSqFt <= 0)) invalidated.Push("Rock Storage Bottom Area must be greater than zero."); if (facility.HasSecondaryOverflow && (facility.StorageDepth2In <= facility.StorageDepth1In)) invalidated.Push("Storage Depth 2 must be greater than or equal to Storage Depth 1."); break; } case FacilityType.PlanterFlat: { if (facility.BottomAreaSqFt <= 0) invalidated.Push("Bottom Area must be greater than zero."); if (facility.StorageDepth1In <= 0) invalidated.Push("Storage Depth 1 must be greater than zero."); if (facility.HasRockStorage && (facility.RockStorageBottomAreaSqFt < 0)) invalidated.Push("Rock Storage Bottom Area must be greater than or equal to zero."); if (facility.HasSecondaryOverflow && (facility.StorageDepth2In <= facility.StorageDepth1In)) invalidated.Push("Storage Depth 2 must be greater than Storage Depth 1."); break; } case FacilityType.PlanterSloped: case FacilityType.Swale: { if (facility is SlopedFacility) { SlopedFacility slopedFacility = facility as SlopedFacility; foreach (SlopedFacilitySegment segment in slopedFacility.Segments) { if (segment.SegmentLengthFt <= 0) invalidated.Push("Segment " + (slopedFacility.Segments.IndexOf(segment)+1).ToString() + " Segment Length must be greater than zero."); if (segment.CheckDamLengthFt < 0) invalidated.Push("Segment " + (slopedFacility.Segments.IndexOf(segment)+1).ToString() + " Check Dam Length must be greater than or equal to zero."); if (segment.SlopeRatio < 0) invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment)+1).ToString() + " Slope must be greater than or equal to zero."); if (segment.BottomWidthFt < 0) invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Bottom Width must be greater than or equal to zero."); if (segment.SideSlopeRightRatio < 0) invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Side Slope Ratio Right must be greater than or equal to zero."); if (segment.SideSlopeLeftRatio < 0) invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Side Slope Ratio Left must be greater than or equal to zero."); if (segment.DownstreamDepthIn <= 0) invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Downstream Depth must be greater than zero."); if (segment.LandscapeWidthFt < segment.DownstreamTopWidthFt) invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Landscape Width must contain facility segment at Downstream Depth."); if (facility.HasRockStorage && (segment.RockStorageWidthFt <= 0)) invalidated.Push("Segment #" + (slopedFacility.Segments.IndexOf(segment) + 1).ToString() + " Rock Storage Width must be greater than zero."); } if (facility.HasSecondaryOverflow && (facility.StorageDepth2In <= slopedFacility.Segments.Last().DownstreamDepthIn)) invalidated.Push("Storage Depth 2 must be greater than the Downstream Depth of the final facility segment."); } break; } default: break; } if (facility.GrowingMediumDepthIn <= 0) invalidated.Push("Growing Medium Depth must be greater than zero."); if (facility.HasRockStorage) { if (facility.RockStorageDepthIn <= 0) invalidated.Push("Rock Storage Depth must be greater than zero."); if (facility.RockVoidRatio <= 0 || facility.RockVoidRatio >= 1) invalidated.Push("Rock Porosity must be between zero and one."); } if ((facility.Configuration == FacilityConfiguration.F || facility.Configuration == FacilityConfiguration.C) && ((facility.StorageDepth3In < 0) || (facility.StorageDepth3In > facility._rockStorageDepthIn))) invalidated.Push("Storage Depth 3 must be greater than or equal to zero and less than or equal to the Rock Storage Depth."); if(facility.Catchment.DesignInfiltrationNativeInchesPerHour < 0) invalidated.Push("Native Infiltration must be greater than or equal to zero."); if (facility.Catchment.ImportedMediumInfiltrationInchesPerHour < 0) invalidated.Push("Imported Medium Infiltration must be greater than or equal to zero."); if(invalidated.Count()>0) { message = string.Empty; while (invalidated.Count() > 0) { message += invalidated.Pop(); if (invalidated.Count != 0) message += System.Environment.NewLine; } facility._validConfiguration = false; return false; } message = "Valid configuration."; return facility._validConfiguration; }