using System; using Deltares.Geometry; namespace Deltares.Stability.Calculation2 { public class SpencerCalculation : SlidingCurveCalculation { public const int CBoundsMin = 0; public const int CBoundsInc = 1; private const double CDeltaShift = 0.01; private const double CInverseSafetyShift = 0.01; private const int CMaxIterations = 10; private const double CDegreeToRad = Math.PI/180; private double delta = 0; private double safety; private SlidingPlane slidingCurve; public override void FillSlices(SlidingCurve slidingCurve) { var fillSlicesFiller = new SlicesFiller(); fillSlicesFiller.PreProces = PreProcessCalculation; for (int i = 0; i < slidingCurve.Slices.Count; i++) { fillSlicesFiller.FillSlicesByInterpolation(slidingCurve.Slices[i], 0.00001); } } public override double GetSafetyFactor(SlidingCurve slidingCurve) { if (slidingCurve is SlidingPlane) { var spencerPlane = (SlidingPlane) slidingCurve; this.slidingCurve = spencerPlane; // TODO: use sliding circle data instead of internal fields return CalculateStabilityFactor(spencerPlane); // to be implemented } throw new ArgumentException("Sliding curve should be sliding plane"); } public override void CreateSlices(SlidingCurve slidingCurve) { var sliceCreatorCreator = new SlicesCreator(); sliceCreatorCreator.ChangingPoints = PreProcessCalculation.ChangingPoints; sliceCreatorCreator.SetSoilProfile2D(PreProcessCalculation.Profile2D); sliceCreatorCreator.SetWaternet(PreProcessCalculation.Waternet); sliceCreatorCreator.SetSurfaceLine(PreProcessCalculation.SurfaceLine); sliceCreatorCreator.Spencerplane = fillPlane((SlidingPlane) slidingCurve); SlipplaneConstraints constraints = PreProcessCalculation.Constraints; sliceCreatorCreator.StriveWidth = slidingCurve.MaximumSliceWidth; sliceCreatorCreator.CreateSpencerSlices((SlidingPlane) slidingCurve); } private void SwapSlices(int firstIndex, int secondIndex, SlidingCurve slidingCurve) { Slice slice = slidingCurve.Slices[firstIndex]; slidingCurve.Slices[firstIndex] = slidingCurve.Slices[secondIndex]; slidingCurve.Slices[secondIndex] = slice; } private void ReverseData() { int i; int j; int Last; double Temp; var TempPoint = new GeometryPoint(); TempPoint.X = slidingCurve.LeftPoint.X; TempPoint.Z = slidingCurve.LeftPoint.Z; slidingCurve.LeftPoint.X = -slidingCurve.RightPoint.X; slidingCurve.LeftPoint.Z = -slidingCurve.RightPoint.Z; slidingCurve.RightPoint.X = -TempPoint.X; slidingCurve.RightPoint.Z = -TempPoint.Z; Slice lSlice; for (int k = 0; k < slidingCurve.Slices.Count; k++) { lSlice = slidingCurve.Slices[k]; // xcoors Temp = lSlice.TopLeft.X; lSlice.TopLeft.X = -lSlice.TopRight.X; lSlice.BottomLeft.X = -lSlice.TopRight.X; lSlice.TopRight.X = -Temp; lSlice.BottomRight.X = -Temp; // ZCoors Temp = lSlice.BottomLeft.Y; lSlice.BottomLeft.Y = lSlice.BottomRight.Y; lSlice.BottomRight.Y = Temp; Temp = lSlice.TopLeft.Y; lSlice.TopLeft.Y = lSlice.TopRight.Y; lSlice.TopRight.Y = Temp; Temp = lSlice.LeftForceY; lSlice.LeftForceY = lSlice.RightForceY; lSlice.RightForceY = Temp; Temp = lSlice.LeftForce; lSlice.LeftForce = lSlice.RightForce; lSlice.RightForce = Temp; // Loading lSlice.HPoreOnSurface = -lSlice.HPoreOnSurface; lSlice.LeftForceAngle = -lSlice.LeftForceAngle; // right force angle is missing (is conected to leftforce angle } // change numbering of slices // wissel de slices om (aleen de adressen) Last = Convert.ToInt32(slidingCurve.Slices.Count/2); for (i = 0; i < Last; i++) { j = slidingCurve.Slices.Count - 1 - i; SwapSlices(i, j, slidingCurve); } } private void DeltaGrens(ref double AMinDelta, ref double AMaxDelta) { int i; double Ltmpphi; double LDeltaMin; double LDeltaMax; AMinDelta = -0.5*Math.PI; AMaxDelta = 0.5*Math.PI; for (i = 0; i < slidingCurve.Slices.Count; i++) { Slice LSice = slidingCurve.Slices[i]; if (LSice.Phi*Constants.CRad < 1.0E-12) { Ltmpphi = 1.0E-12; } else { Ltmpphi = LSice.Phi*Constants.CRad; } // minimale grenswaarde bepalen LDeltaMin = -slidingCurve.Slices[i].BottomAngle - (Math.PI + Math.Atan(-safety/Math.Tan(Ltmpphi))); AMinDelta = Math.Max(LDeltaMin, AMinDelta); // maximale grenswaarde bepalen LDeltaMax = LDeltaMin + Math.PI; AMaxDelta = Math.Min(LDeltaMax, AMaxDelta); } } private void DetermineResultantMoment() { int LSliceIndex; for (LSliceIndex = 0; LSliceIndex < slidingCurve.Slices.Count; LSliceIndex++) { slidingCurve.Slices[LSliceIndex].DetermineResultantMoment(); } } //======================================================================================================================= //Comment: The blue print of the theoretical implications is found in: // https://repos.deltares.nl/repos/pvcsprojects/documents/trunk/DGeoStability/Design/BluePrintGeoStability.doc // //Date ID Modification //2010-11-17 Sel Created //=======================================================================================================================} private void ForceSolutionWithinRestraints() { int LSliceIndex; double LInverseSafety; double LDelta; //Confine solution space; the constraints are found in equation (9) LInverseSafety = 1/safety + CInverseSafetyShift; if (LInverseSafety < CInverseSafetyShift) { // throw EDSpencerPlaneError.Create('New safety factor is negative; no proper solution is anymore expected'); } // The resultant slope range is sought for LInversSafety; then, also valid for LInversSafety - CInverseSafetyShift double LMinimumDelta = double.MinValue; double LMaximumDelta = double.MaxValue; // Find the Delta range belonging to LSafety for (LSliceIndex = 0; LSliceIndex < slidingCurve.Slices.Count; LSliceIndex++) { // BottomAngle is defined negative here LMinimumDelta = Math.Max(LMinimumDelta, (-Math.PI/2 - slidingCurve.Slices[LSliceIndex].BottomAngle)); LMaximumDelta = Math.Min(LMaximumDelta, (Math.PI/2 - Math.Atan( Math.Tan(slidingCurve.Slices[LSliceIndex].Phi*CDegreeToRad)* LInverseSafety) - slidingCurve.Slices[LSliceIndex].BottomAngle)); } // Stay away CDeltaShift from the restraints; reserve space for LDelta - CDeltaShift if (LMinimumDelta > LMaximumDelta - 3*CDeltaShift) { // throw EDSpencerPlaneError.Create('No valid range for resultant slope; no proper solution is anymore expected'); } LDelta = Math.Max(LMinimumDelta + 2*CDeltaShift, Math.Min(delta, LMaximumDelta - CDeltaShift)); //{ Allow adapted proposed shift of resultant slope delta = LDelta; } private void DetermineInterSliceForces() //============================================== { Slice slice = slidingCurve.Slices[0]; // The first slice has no left force } slice.LeftForce = 0; slice.LeftForceY = slice.BottomLeft.Y; slice.LeftForceAngle = delta; // Determine the right force and slope and pass them to the left values of the next slice } for (int sliceIndex = 0; sliceIndex < slidingCurve.Slices.Count; sliceIndex++) { slice = slidingCurve.Slices[sliceIndex]; // Determine resultant } slice.TmpSafety = safety; slice.ResultantAngle = delta; slice.DetermineResultantNorm(); slice.DetermineResultantForce(); } for (int sliceIndex = 0; sliceIndex < slidingCurve.Slices.Count - 1; sliceIndex++) { slice = slidingCurve.Slices[sliceIndex]; slice.DetermineInterSliceForces(); // Pass to next slice } slidingCurve.Slices[sliceIndex + 1].LeftForce = slice.RightForce; slidingCurve.Slices[sliceIndex + 1].LeftForceY = slice.RightForceY; slidingCurve.Slices[sliceIndex + 1].LeftForceAngle = slice.RightForceAngle; } // It is neat to specify proper end values of the inter-slice slopes, though they are not relevant } slice = slidingCurve.Slices[slidingCurve.Slices.Count - 1]; slice.RightForce = 0; slice.RightForceY = slice.BottomRight.Y; slice.RightForceAngle = delta; } private double DetermineForceImbalance() { //See equation (10) and (13); the right inter-slice force should vanish at the last slice. In (10) the ForceAngle's // are equal. We use this routine for both methods, Constant Resultant Slope and Equally Spaced Action ratio } Slice slice = slidingCurve.Slices[slidingCurve.Slices.Count - 1]; return slice.LeftForce*Math.Cos(slice.LeftForceAngle - slice.RightForceAngle) + slice.ResultantForce*Math.Cos(slice.ResultantAngle - slice.RightForceAngle); } private double DetermineMomentImbalance() { // See equation (5); the right inter-slice force should vanish at the last slice } Slice slice = slidingCurve.Slices[slidingCurve.Slices.Count - 1]; return slice.ResultantMoment + slice.LeftForce*Math.Cos(slice.LeftForceAngle)* (slice.LeftForceY - slice.ZCenterBottom - Math.Tan(slice.LeftForceAngle)*slice.Width/2.0); } private bool IterateConstantResultantSlopes() { const double CSafetyAccuracy = 0.0001; const double CDeltaAccuracy = 0.0001; bool result; double LRestForce; double LRestMoment; double LMomentPerSafety; double LForcePerSafety; double LMomentPerDelta; double LForcePerDelta; double LInverseSafetyShift; double LDeltaShift; // Determine present imbalance DetermineInterSliceForces(); LRestMoment = DetermineMomentImbalance(); LRestForce = DetermineForceImbalance(); // Determine imbalance for variation in resultant slope } delta = delta - CDeltaShift; DetermineInterSliceForces(); LMomentPerDelta = (LRestMoment - DetermineMomentImbalance())/CDeltaShift; LForcePerDelta = (LRestForce - DetermineForceImbalance())/CDeltaShift; delta = delta + CDeltaShift; // etermine imbalance for variation in safety factor } safety = safety/(1 + safety*CInverseSafetyShift); DetermineInterSliceForces(); LMomentPerSafety = (DetermineMomentImbalance() - LRestMoment)/CInverseSafetyShift; LForcePerSafety = (DetermineForceImbalance() - LRestForce)/CInverseSafetyShift; safety = safety/(1 - safety*CInverseSafetyShift); // ermine the adjustments } LDeltaShift = LForcePerSafety*LMomentPerDelta - LForcePerDelta*LMomentPerSafety; LInverseSafetyShift = (LMomentPerDelta*LRestForce - LForcePerDelta*LRestMoment)/LDeltaShift; LDeltaShift = (LForcePerSafety*LRestMoment - LMomentPerSafety*LRestForce)/LDeltaShift; //Check accuracy and adjust } result = (Math.Abs(LInverseSafetyShift) < CSafetyAccuracy) && (Math.Abs(LDeltaShift) < CDeltaAccuracy); // Result := ((Abs(LRestForce) < CEpsForce) and (Abs(LRestMoment) < CEpsMom)); safety = safety/(1 - safety*LInverseSafetyShift); delta = delta - LDeltaShift; return result; } private bool IsInterSliceForceOutsideSlice() { int LSliceIndex; for (LSliceIndex = 0; LSliceIndex < slidingCurve.Slices.Count - 1; LSliceIndex++) { if ((slidingCurve.Slices[LSliceIndex].RightForceY < slidingCurve.Slices[LSliceIndex].BottomRight.Y) || (slidingCurve.Slices[LSliceIndex].RightForceY > slidingCurve.Slices[LSliceIndex].TopRight.Y)) { return true; } } return false; } private void GetShearForces() { Slice LSlice; int LSliceIndex; double LeftCombination; double LRightCombination; double LTotalPore; double LTotPore; double LCosDif; double LCosBot; double LMass; double LNormalStress; double LTanPhiBot; double LCohBottom; for (LSliceIndex = 0; LSliceIndex < slidingCurve.Slices.Count - 1; LSliceIndex++) { LSlice = slidingCurve.Slices[LSliceIndex]; if (LSlice.ArcLength > 0) { LeftCombination = LSlice.LeftForce*Math.Sin(-LSlice.BottomAngle - LSlice.LeftForceAngle); LRightCombination = LSlice.RightForce*Math.Sin(-LSlice.BottomAngle - LSlice.RightForceAngle); LTotPore = Math.Sqrt(LSlice.VPoreOnSurface*LSlice.VPoreOnSurface + LSlice.HPoreOnSurface*LSlice.HPoreOnSurface); LTotalPore = LSlice.TotalPorePressure; LCosDif = Math.Cos(LSlice.TopAngle - LSlice.BottomAngle); LCosBot = Math.Cos(-LSlice.BottomAngle); LMass = LSlice.Weight + LSlice.ExternalLoad + LSlice.SigmaSoilQuake*LSlice.Width; // in kN/m LSlice.NormalStress = (LTotPore*LCosDif - LTotalPore*LSlice.ArcLength + (LMass)*LCosBot - LeftCombination + LRightCombination)/LSlice.ArcLength; LTanPhiBot = Math.Tan(LSlice.Phi*CDegreeToRad); LNormalStress = LSlice.NormalStress; LCohBottom = LSlice.Cohesion; LSlice.ShearStress = (LNormalStress*LTanPhiBot + LCohBottom)/safety; } } } /// /// Calculate spencer with /// ApplyConstantResultantSlopes /// /// private double CalculateStabilityFactor(SlidingPlane spencerPlane) { int LIter; bool LIsAccurate = false; bool isReverseGeometry = slidingCurve.Slices[0].ZCenterTop < slidingCurve.Slices[slidingCurve.Slices.Count - 1].ZCenterTop; // Fill soil,stress data of slices ( in een apparte routine STSliceFill if (isReverseGeometry) { // om uit te kunnene rekenen moet de geometrie gespiegeld worden // dit gebeurd door alle xen van een - teken te voorzien en de lamelen // andersom te sorteren ReverseData(); } //{ Initialize } LIter = 0; // begin startwaarde stabiliteits factor van 1.1 safety = Constants.CFMinStart; delta = 0; DetermineResultantMoment(); // For safety's sake check restraints } ForceSolutionWithinRestraints(); // Take care of the iteration } while (!LIsAccurate && LIter <= CMaxIterations) { LIsAccurate = IterateConstantResultantSlopes(); ForceSolutionWithinRestraints(); LIter++; if ((LIter == CMaxIterations) || (safety < 0)) { /*throw new Exception( "Gradient approach not successful; no proper solution is anymore expected");*/ safety = Double.MaxValue; break; } } // Check position of the inter-slice forces } if (LIsAccurate) { DetermineInterSliceForces(); } if (IsInterSliceForceOutsideSlice()) { //excludedSpencerPlanes.Add(spencerPlane); } if (LIsAccurate) { GetShearForces(); } if (isReverseGeometry) { ReverseData(); } spencerPlane.SafetyFactor = safety; return safety; } //=========================================================================== private GeometryPointString fillPlane(SlidingPlane slidingPlane) { var result = new GeometryPointString(); for (int i = 0; i < slidingPlane.SpencerSlipPlane.Count; i++) { var LPoint = new GeometryPoint(); LPoint.X = slidingPlane.SpencerSlipPlane[i].X; LPoint.Z = slidingPlane.SpencerSlipPlane[i].Y; result.Points.Add(LPoint); } return result; } } }