public static Validate ( |
||
facility | The Facility to validate | |
category | int | The Hierarchy Category the facility will be validated against |
message | string | A message indicating the first error found in an invalid facility, /// or "Valid Configuration" if the facility is valid. |
return | bool |
/// <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); }
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); } }