Exemple #1
0
        public MeshPoint( Point pt , MaterialType material )
        {
            location = new Ellipse();
            location.Height = 3;
            location.Width = 3;
            location.Fill = Brushes.Black;
            location.Stroke = Brushes.Black;
            location.Visibility = Visibility.Visible;
            location.Margin = new Thickness( pt.X - 1.5 , pt.Y - 1.5 , 0 , 0 );

            this.material = material;
        }
Exemple #2
0
        public MaterialBlock( SlopePlotCanvas canvas , MaterialType mtl, Point[] pts )
        {
            this.plotCanvas = canvas;

            Boundary = new Polygon();
            boundaryPoints = new List<DrawingPoint>();
            Boundary.Stroke = Brushes.Black;
            Boundary.StrokeThickness = 0.8;
            Boundary.StrokeLineJoin = PenLineJoin.Round;
            Boundary.StrokeStartLineCap = PenLineCap.Round;
            Boundary.StrokeEndLineCap = PenLineCap.Round;
            //Boundary.Fill = mtl.Fill;
            Boundary.Opacity = 0.6;
            Boundary.Visibility = Visibility.Visible;

            for ( int i = 0 ; i < pts.Length ; i++ )
            {
                Boundary.Points.Add( pts[i] );
            }

            //Material = mtl;

            lineConstraints = new List<LineConstraint>();
            lineLoads = new List<LineLoad>();
            pointLoads = new List<PointLoad>();
        }
Exemple #3
0
        public MaterialBlock( SlopeDefineCanvas canvas , MaterialType mtl , Point[] pts )
        {
            this.defineCanvas = canvas;

            Boundary = new Polygon();
            boundaryPoints = new List<DrawingPoint>();
            Boundary.Stroke = Brushes.Black;
            Boundary.StrokeThickness = 0.8;
            Boundary.StrokeLineJoin = PenLineJoin.Round;
            Boundary.StrokeStartLineCap = PenLineCap.Round;
            Boundary.StrokeEndLineCap = PenLineCap.Round;
            Boundary.Fill = mtl.Fill;
            Boundary.Opacity = 0.8;
            Boundary.Visibility = Visibility.Visible;

            for ( int i = 0 ; i < pts.Length ; i++ )
            {
                // check if point is same as existing point
                bool foundPoint = false;
                foreach ( MaterialBlock mb in canvas.Substructs )
                {
                    foreach ( DrawingPoint p in mb.BoundaryPoints )
                    {
                        if ( (pts[i] - p.Point).Length < p.Dot.Width / 2 )
                        {
                            Boundary.Points.Add( p.Point );
                            boundaryPoints.Add( p );
                            p.ParentBlocks.Add( this );
                            foundPoint = true;
                            break;
                        }
                    }
                    if ( foundPoint ) break;
                }

                if ( !foundPoint )
                {
                    Boundary.Points.Add( pts[i] );
                    boundaryPoints.Add( new DrawingPoint( canvas , this , pts[i] ) );
                }
            }

            Material = mtl;

            lineConstraints = new List<LineConstraint>();
            lineLoads = new List<LineLoad>();
            pointLoads = new List<PointLoad>();

            foreach ( MaterialBlock mb in canvas.Substructs )
            {
                if ( mb == this ) continue;

                foreach ( LineConstraint lc in mb.LineConstraints )
                {
                    if ( this.BoundaryPoints.Contains( lc.Nodes[0] ) && this.BoundaryPoints.Contains( lc.Nodes[1] ) )
                    {
                        if ( !this.LineConstraints.Contains( lc ) ) this.LineConstraints.Add( lc );
                    }
                }
            }

            SortPoints();
        }
Exemple #4
0
        public MaterialBlock( SlopeCanvas canvas , Point[] pts )
        {
            if ( selectFill == null )
            {
                Color selectColour = new Color();
                selectColour = new Color();
                selectColour.A = 20;
                selectColour.R = 200;
                selectColour.G = 0;
                selectColour.B = 0;
                selectFill = new SolidColorBrush( selectColour );
            }

            this.canvas = canvas;

            Boundary = new Polygon();
            boundaryPoints = new List<DrawingPoint>();
            Boundary.Stroke = Brushes.Black;
            Boundary.StrokeThickness = 1.1;
            Boundary.StrokeLineJoin = PenLineJoin.Round;
            Boundary.StrokeStartLineCap = PenLineCap.Round;
            Boundary.StrokeEndLineCap = PenLineCap.Round;
            Boundary.Fill = Brushes.WhiteSmoke;
            Boundary.Opacity = 0.8;
            Boundary.MouseLeftButtonDown += new MouseButtonEventHandler( this.MouseLeftButtonDown );
            Boundary.Visibility = Visibility.Visible;

            canvas.Children.Add( Boundary );

            for ( int i = 0 ; i < pts.Length ; i++ )
            {
                // check if point is same as existing point
                bool foundPoint = false;
                foreach ( MaterialBlock mb in canvas.MaterialBlocks )
                {
                    foreach ( DrawingPoint p in mb.BoundaryPoints )
                    {
                        if ( (pts[i] - p.Point).Length < p.Dot.Width / 2 )
                        {
                            Boundary.Points.Add( p.Point );
                            boundaryPoints.Add( p );
                            p.ParentBlocks.Add( this );
                            foundPoint = true;
                            break;
                        }
                    }
                    if ( foundPoint ) break;
                }

                if ( !foundPoint )
                {
                    Boundary.Points.Add( pts[i] );
                    boundaryPoints.Add( new DrawingPoint( canvas , this , pts[i] ) );
                }
            }

            Material = new MaterialType();

            lineConstraints = new List<LineConstraint>();
            lineLoads = new List<LineLoad>();
            pointLoads = new List<PointLoad>();

            foreach ( MaterialBlock mb in canvas.MaterialBlocks )
            {
                if ( mb == this ) continue;

                foreach ( LineConstraint lc in mb.LineConstraints )
                {
                    if ( this.BoundaryPoints.Contains( lc.Nodes[0] ) && this.BoundaryPoints.Contains( lc.Nodes[1] ) )
                    {
                        if ( !this.LineConstraints.Contains( lc ) ) this.LineConstraints.Add( lc );
                    }
                }
            }

            phaseMaterials = new List<MaterialType>();
            MaterialType nullMaterial = canvas.MaterialTypes[canvas.MaterialTypes.Count - 1];
            int numAnalysisPhases = canvas.AnalysisPhases.Count - 1;
            while ( phaseMaterials.Count < numAnalysisPhases ) phaseMaterials.Add( nullMaterial );

            SortPoints();
        }
Exemple #5
0
        public AnalysisMeshPoint( Point pt , MaterialType material , MeshPointType type )
        {
            location.X = pt.X;
            location.Y = pt.Y;

            this.material = material;
            this.type = type;
        }
        private void materialList_SelectionChanged( object sender , SelectionChangedEventArgs e )
        {
            selectedMaterial = materialList.SelectedItem as MaterialType;

            if ( selectedMaterial != null )
            {
                colour.Fill = selectedMaterial.Fill;
                phi.Text = string.Format( "{0}" , Math.Round( selectedMaterial.Phi , 2 ) );
                coh.Text = string.Format( "{0}" , Math.Round( selectedMaterial.Cohesion , 2 ) );
                psi.Text = string.Format( "{0}" , Math.Round( selectedMaterial.Psi , 2 ) );
                gamma.Text = string.Format( "{0}" , Math.Round( selectedMaterial.Gamma , 2 ) );
                emod.Text = string.Format( "{0}" , Math.Round( selectedMaterial.Emod , 2 ) );
                nu.Text = string.Format( "{0}" , Math.Round( selectedMaterial.Nu , 2 ) );
            }
        }
        private static double GenAlg( SlopeCanvas canvas , BackgroundWorker worker , DoWorkEventArgs e )
        {
            // Set the delegate function for evaluating the factor of safety
            EvaluateSafetyFactor evalSF = null;
            switch ( canvas.AnalysisType )
            {
                case AnalysisType.Bishop: evalSF += Bishop; break;
                default: evalSF += RFEM; break;
            }

            // Get material data
            List<MaterialType> materialTypes = new List<MaterialType>();
            MaterialType copyMaterial;
            for ( int i = 0 ; i < canvas.MaterialTypes.Count ; i++ )
            {
                copyMaterial = new MaterialType();
                copyMaterial.Phi = canvas.MaterialTypes[i].Phi;
                copyMaterial.Cohesion = canvas.MaterialTypes[i].Cohesion;
                copyMaterial.Gamma = canvas.MaterialTypes[i].Gamma;
                copyMaterial.Emod = canvas.MaterialTypes[i].Emod;
                copyMaterial.Nu = canvas.MaterialTypes[i].Nu;
                copyMaterial.Name = canvas.MaterialTypes[i].Name;

                if ( canvas.AnalysisType == AnalysisType.RFEM ) copyMaterial.ComputeRFEMProperties();

                materialTypes.Add( copyMaterial );
            }

            double angleFactor = Math.PI / 180.0;
            double lengthFactor;
            double cohFactor = 1.0 , gammaFactor = 1.0;
            switch ( canvas.Units )
            {
                case Units.Metres:
                    lengthFactor = 0.0254;
                    break;

                case Units.Millimetres:
                    lengthFactor = 25.4;
                    cohFactor /= Math.Pow( 1000 , 2 );
                    gammaFactor /= Math.Pow( 1000 , 3 );
                    break;

                case Units.Feet:
                    lengthFactor = 1.0 / 12.0;
                    cohFactor *= Math.Pow( 12 , 2 );
                    break;

                default:
                    lengthFactor = 1.0;
                    gammaFactor /= Math.Pow( 12 , 3 );
                    break;
            }

            for ( int i = 0 ; i < materialTypes.Count ; i++ )
            {
                materialTypes[i].Phi *= angleFactor;
                materialTypes[i].Cohesion *= cohFactor;
                materialTypes[i].Gamma *= gammaFactor;
            }

            // Load upper surface and mesh
            string[] split = canvas.FilePath.Split( '.' );

            if ( split[split.Length - 1] != "slp" )
            {
                MessageBox.Show( "Input file format incorrect. Input data must be in a *.slp file." , "Error" );
                return 0;
            }

            split[split.Length - 1] = "msh";

            string path = string.Join( "." , split );

            if ( !File.Exists( path ) )
            {
                MessageBox.Show( "Could not find mesh data file." , "Error" );
                return 0;
            }

            Units meshUnits;
            List<Point> surface = new List<Point>();
            List<List<AnalysisMeshPoint>> mesh = new List<List<AnalysisMeshPoint>>();
            List<AnalysisMeshPoint> line;
            try
            {
                using ( TextReader tr = new StreamReader( path ) )
                {
                    // Read in actual units
                    string units = tr.ReadLine().Split( new char[] { '=' , ' ' } , StringSplitOptions.RemoveEmptyEntries )[1];
                    switch ( units )
                    {
                        case "m":
                            meshUnits = Units.Metres;
                            break;
                        case "mm":
                            meshUnits = Units.Millimetres;
                            break;
                        case "ft":
                            meshUnits = Units.Feet;
                            break;
                        default:
                            meshUnits = Units.Inches;
                            break;
                    }

                    // Check that canvas and mesh files have same units
                    if ( canvas.Units != meshUnits )
                    {
                        MessageBox.Show( "Units mismatch. Mesh file must have same units as input file." , "Error" );
                        return 0;
                    }

                    tr.ReadLine();

                    // Get number of upper surface points
                    int count = int.Parse( tr.ReadLine().Split( '=' )[1] );

                    tr.ReadLine();
                    tr.ReadLine();

                    // Read in units and add to surface point list
                    for ( int i = 0 ; i < count ; i++ )
                    {
                        split = tr.ReadLine().Split( ',' );
                        surface.Add( new Point( double.Parse( split[0] ) , double.Parse( split[1] ) ) );
                    }

                    tr.ReadLine();

                    // Get number of mesh lines
                    count = int.Parse( tr.ReadLine().Split( '=' )[1] );

                    tr.ReadLine();

                    int pointCount;
                    string type , material;
                    MeshPointType mpType;
                    MaterialType matType = null;
                    for ( int i = 0 ; i < count ; i++ )
                    {
                        // Create new list for points in current mesh line
                        line = new List<AnalysisMeshPoint>();

                        tr.ReadLine();

                        // Get number of points in current mesh line
                        pointCount = int.Parse( tr.ReadLine().Split( '=' )[1] );

                        for ( int j = 0 ; j < pointCount ; j++ )
                        {
                            // Read data for current point
                            split = tr.ReadLine().Split( ',' );

                            // Get point type (entrance or exit from material block)
                            type = split[2].Split( new char[] { ' ' } , StringSplitOptions.RemoveEmptyEntries )[0];
                            switch ( type )
                            {
                                case "Entrance": mpType = MeshPointType.Entrance; break;
                                default: mpType = MeshPointType.Exit; break;
                            }

                            // Get material type name and find the corresponding material from the list
                            material = split[3].Split( '\"' )[1];
                            matType = materialTypes.Find( delegate( MaterialType mt ) { return mt.Name == material; } );

                            // Add this point to the list of points for the current mesh line
                            line.Add( new AnalysisMeshPoint( new Point( double.Parse( split[0] ) , double.Parse( split[1] ) ) ,
                                                            matType , mpType ) );
                        }

                        mesh.Add( line );

                        tr.ReadLine();
                    }
                }
            }
            catch
            {
                MessageBox.Show( "Error in reading mesh file. Make sure it is formatted correctly." , "Error" );
                return 0;
            }

            // Get genetic algorithm parameters
            int population = canvas.GeneticAlgorithmParameters.Population;
            int generations = canvas.GeneticAlgorithmParameters.Generations;
            double fittestProp = canvas.GeneticAlgorithmParameters.FittestProportion;
            double matingProp = canvas.GeneticAlgorithmParameters.MatingPoolProportion;
            double crossProb = canvas.GeneticAlgorithmParameters.CrossoverProbability;
            double mutProb = canvas.GeneticAlgorithmParameters.MutationProbability;

            // For reporting progress
            int numberOfEvaluations = population * generations;

            // Compute additional genetic algorithm parameters
            int fittest = (int) Math.Ceiling( fittestProp * population );
            int parents = population - fittest;
            fittest += parents % 2;
            parents = population - fittest;
            int mating = (int) Math.Ceiling( matingProp * population );
            int dof = 3;
            int mutations = (int) Math.Ceiling( mutProb * (population - fittest) * dof );

            List<double> radiusBreaks = FindRadiusBreaks( surface );

            double yBoundMin = canvas.Boundary.YMin;
            yBoundMin = ((canvas.ActualHeight - yBoundMin) - canvas.OriginOffsetY) / canvas.DpiY * lengthFactor * canvas.Scale;

            SoilMovement soilDirection = canvas.Boundary.SoilDirection;

            List<CircularSurface> currentSolutions = new List<CircularSurface>();
            List<CircularSurface> masterSolutions = new List<CircularSurface>();
            List<CircularSurface> matingPool = new List<CircularSurface>();
            List<double> parentSelectors = new List<double>();

            // Generate initial random population of failure surfaces
            while ( currentSolutions.Count < population )
            {
                currentSolutions.Insert( 0 , new CircularSurface( surface , yBoundMin , radiusBreaks , soilDirection ) );
                currentSolutions[0].GenerateSurface();
            }

            bool foundTwin;
            double xdiff , ydiff , rdiff;
            double toler = 1e-5;
            int progressCount = 1;
            int percentComplete;
            double upperSF , totalWeight;
            for ( int igen = 0 ; igen < generations ; igen++ )
            {
                for ( int isoln = 0 ; isoln < currentSolutions.Count ; isoln++ )
                {
                    // Check if analysis has been cancelled
                    if ( worker.CancellationPending )
                    {
                        e.Cancel = true;
                        return 0;
                    }

                    foundTwin = false;

                    // Check master solution list for existing solution
                    for ( int imaster = 0 ; imaster < masterSolutions.Count ; imaster++ )
                    {
                        // Determine if (x,y,r) parameters are all within
                        // tolerance of a solution from the master list
                        xdiff = Math.Abs( currentSolutions[isoln].X - masterSolutions[imaster].X );
                        if ( xdiff > toler ) continue;

                        ydiff = Math.Abs( currentSolutions[isoln].Y - masterSolutions[imaster].Y );
                        if ( ydiff > toler ) continue;

                        rdiff = Math.Abs( currentSolutions[isoln].R - masterSolutions[imaster].R );
                        if ( rdiff > toler ) continue;

                        currentSolutions[isoln].SF = masterSolutions[imaster].SF;
                        currentSolutions[isoln].XEnter = masterSolutions[imaster].XEnter;
                        currentSolutions[isoln].XExit = masterSolutions[imaster].XExit;
                        currentSolutions[isoln].YEnter = masterSolutions[imaster].YEnter;
                        currentSolutions[isoln].YExit = masterSolutions[imaster].YExit;

                        foundTwin = true;
                    }

                    // If solution found in master list, do not evaluate
                    if ( foundTwin )
                    {
                        // Update progress bar
                        percentComplete = (int) ((float) progressCount++ / (float) numberOfEvaluations * (float) 100);
                        worker.ReportProgress( percentComplete );
                        continue;
                    }

                    // Evaluate safety factor using the appropriate method
                    currentSolutions[isoln].SF = evalSF( currentSolutions[isoln] , surface , mesh );

                    // Add new surface to master list
                    masterSolutions.Add( new CircularSurface( surface , yBoundMin , radiusBreaks , soilDirection ,
                        currentSolutions[isoln].X , currentSolutions[isoln].Y , currentSolutions[isoln].R ,
                        currentSolutions[isoln].Limits , currentSolutions[isoln].SF ,
                        currentSolutions[isoln].XEnter , currentSolutions[isoln].YEnter ,
                        currentSolutions[isoln].XExit , currentSolutions[isoln].YExit ) );

                    percentComplete = (int) ((float) progressCount++ / (float) numberOfEvaluations * (float) 100);
                    worker.ReportProgress( percentComplete );
                }

                // Sort the master solution list
                masterSolutions.Sort( SortSolutions );

                // Only keep top 20 master solutions
                while ( masterSolutions.Count > 20 ) masterSolutions.RemoveAt( masterSolutions.Count - 1 );

                // Begin mating cycle on all but the last generation
                if ( igen < generations )
                {
                    // Sort the current solution list
                    currentSolutions.Sort( SortSolutions );

                    // Clear previous mating pool
                    matingPool.Clear();

                    // Take specified proportion into mating pool
                    while ( matingPool.Count < mating )
                    {
                        matingPool.Add( currentSolutions[0] );
                        currentSolutions.RemoveAt( 0 );
                    }

                    // --------------------------------------
                    // Reproduction
                    // --------------------------------------

                    // Get value of SF @ mating+1
                    // (for producing weighting scheme)
                    upperSF = currentSolutions[0].SF;

                    // Clear current solution list
                    currentSolutions.Clear();

                    // Reverse SF weighting (minimization)
                    // and sum up SF weights
                    totalWeight = 0;
                    for ( int imate = 0 ; imate < mating ; imate++ )
                    {
                        totalWeight += matingPool[imate].SFWeight = matingPool[imate].SF - upperSF;
                    }

                    // Divide each SF by sum of SFs to get individual weight,
                    // and add sum of previous weights to obtain
                    // cumulative weighting scheme
                    matingPool[0].SFWeight /= totalWeight;
                    for ( int imate = 1 ; imate < mating - 1 ; imate++ )
                    {
                        matingPool[imate].SFWeight /= totalWeight;
                        matingPool[imate].SFWeight += matingPool[imate - 1].SFWeight;
                    }
                    matingPool[mating - 1].SFWeight = 1.0;

                    // Generate random numbers for selecting parents
                    parentSelectors.Clear();
                    while ( parentSelectors.Count < parents ) parentSelectors.Add( random.NextDouble() );

                    // Add "fittest" solutions to front of solution list
                    for ( int ifit = 0 ; ifit < fittest ; ifit++ )
                    {
                        currentSolutions.Add( new CircularSurface( surface , yBoundMin , radiusBreaks , soilDirection ,
                            matingPool[ifit].X , matingPool[ifit].Y , matingPool[ifit].R ,
                            matingPool[ifit].Limits ) );
                    }

                    // Select random parents based on weighting scheme
                    int iparent = 0;
                    while ( currentSolutions.Count < population )
                    {
                        for ( int imate = 0 ; imate < mating ; imate++ )
                        {
                            if ( parentSelectors[iparent] <= matingPool[imate].SFWeight )
                            {
                                currentSolutions.Add( new CircularSurface( surface , yBoundMin , radiusBreaks , soilDirection ,
                                    matingPool[imate].X , matingPool[imate].Y , matingPool[imate].R ,
                                    matingPool[imate].Limits ) );

                                break;
                            }
                        }

                        iparent++;
                    }

                    int interpt , crosspt;
                    double crosscoeff;
                    double mother , father;
                    // Loop through parents performing crossover
                    // (beginning from one past the fittest)
                    for ( int ichild = fittest ; ichild < population ; ichild += 2 )
                    {
                        // Check if crossover is to occur
                        if ( random.NextDouble() <= crossProb )
                        {
                            // Randomly select X, Y, or R for interpolation
                            interpt = (int) (random.NextDouble() * dof);

                            // Randomly select another point for crossover
                            crosspt = (int) (random.NextDouble() * (dof - 1));

                            // Randomly generate crossover coefficient
                            crosscoeff = random.NextDouble();

                            switch ( interpt )
                            {
                                // Interpolation on X
                                case 0:
                                    mother = currentSolutions[ichild].X;
                                    father = currentSolutions[ichild + 1].X;

                                    currentSolutions[ichild].X = mother - crosscoeff * (mother - father);
                                    currentSolutions[ichild + 1].X = father + crosscoeff * (mother - father);

                                    switch ( crosspt )
                                    {
                                        // Crossover on Y
                                        case 0:
                                            mother = currentSolutions[ichild].Y;
                                            father = currentSolutions[ichild + 1].Y;

                                            currentSolutions[ichild].Y = father;
                                            currentSolutions[ichild + 1].Y = mother;
                                            break;

                                        // Crossover on R
                                        default:
                                            mother = currentSolutions[ichild].R;
                                            father = currentSolutions[ichild + 1].R;

                                            currentSolutions[ichild].R = father;
                                            currentSolutions[ichild + 1].R = mother;
                                            break;
                                    }
                                    break;

                                // Interpolation on Y
                                case 1:
                                    mother = currentSolutions[ichild].Y;
                                    father = currentSolutions[ichild + 1].Y;

                                    currentSolutions[ichild].Y = mother - crosscoeff * (mother - father);
                                    currentSolutions[ichild + 1].Y = father + crosscoeff * (mother - father);

                                    switch ( crosspt )
                                    {
                                        // Crossover on X
                                        case 0:
                                            mother = currentSolutions[ichild].X;
                                            father = currentSolutions[ichild + 1].X;

                                            currentSolutions[ichild].X = father;
                                            currentSolutions[ichild + 1].X = mother;
                                            break;

                                        // Crossover on R
                                        default:
                                            mother = currentSolutions[ichild].R;
                                            father = currentSolutions[ichild + 1].R;

                                            currentSolutions[ichild].R = father;
                                            currentSolutions[ichild + 1].R = mother;
                                            break;
                                    }
                                    break;

                                // Interpolation on R
                                default:
                                    mother = currentSolutions[ichild].R;
                                    father = currentSolutions[ichild + 1].R;

                                    currentSolutions[ichild].R = mother - crosscoeff * (mother - father);
                                    currentSolutions[ichild + 1].R = father + crosscoeff * (mother - father);

                                    switch ( crosspt )
                                    {
                                        // Crossover on X
                                        case 0:
                                            mother = currentSolutions[ichild].X;
                                            father = currentSolutions[ichild + 1].X;

                                            currentSolutions[ichild].X = father;
                                            currentSolutions[ichild + 1].X = mother;
                                            break;

                                        // Crossover on Y
                                        default:
                                            mother = currentSolutions[ichild].Y;
                                            father = currentSolutions[ichild + 1].Y;

                                            currentSolutions[ichild].Y = father;
                                            currentSolutions[ichild + 1].Y = mother;
                                            break;
                                    }
                                    break;
                            }
                        }
                    }

                    // Loop through population performing mutations
                    int mutchild , mutpt;
                    for ( int imut = 0 ; imut < mutations ; imut++ )
                    {
                        // Select child and parameter randomly
                        mutchild = (int) (random.NextDouble() * parents) + fittest;
                        mutpt = (int) (random.NextDouble() * dof);

                        switch ( mutpt )
                        {
                            case 0: currentSolutions[mutchild].GenerateX(); break;
                            case 1: currentSolutions[mutchild].GenerateY(); break;
                            default: currentSolutions[mutchild].GenerateR(); break;
                        }
                    }
                }
            }

            // Maintain top 10 solutions
            masterSolutions.Sort( SortSolutions );
            while ( masterSolutions.Count > 10 ) masterSolutions.RemoveAt( masterSolutions.Count - 1 );
            masterSolutions.Sort( SortSolutions );

            // ----------------------------
            // OUTPUT FILE GENERATION
            // ----------------------------
            // Read back existing results (if any), print them to output and add this run's output to end.
            // Save the minimum case thus far immediately after geometry data.

            // Update the file path
            split = canvas.FilePath.Split( '.' );
            switch ( canvas.AnalysisType )
            {
                case AnalysisType.Bishop: split[split.Length - 1] = "bish"; break;
                default: split[split.Length - 1] = "rfem"; break;
            }
            path = string.Join( "." , split );

            // MODIFY THE EXISTING RESULTS FILE
            if ( File.Exists( path ) )
            {
                // Read in all existing output file contents
                List<string> contents = new List<string>( File.ReadAllLines( path ) );

                // Find line with number of runs, increment run count, and update this line
                int iruncount = contents.FindIndex( delegate( string s ) { return s.Contains( "Number of Runs = " ); } );
                int runs = int.Parse( contents[iruncount].Split( '=' )[1] );
                runs++;
                contents[iruncount] = string.Format( "Number of Runs = {0}" , runs );

                // Find region with critical surface information and obtain its Fs
                int icrit = contents.FindIndex( delegate( string s ) { return s.Contains( "MOST CRITICAL SURFACE" ); } );
                double prevSF = double.Parse( contents[icrit + 9].Split( '=' )[1] );

                // If min Fs from current run is less than all previous runs, update this section
                if ( masterSolutions[0].SF < prevSF )
                {
                    contents[icrit + 2] = string.Format( "X_centre =\t{0}" , Math.Round( masterSolutions[0].X , 2 ) );
                    contents[icrit + 3] = string.Format( "Y_centre =\t{0}" , Math.Round( masterSolutions[0].Y , 2 ) );
                    contents[icrit + 4] = string.Format( "R =\t\t{0}" , Math.Round( masterSolutions[0].R , 2 ) );
                    contents[icrit + 5] = string.Format( "X_enter =\t{0}" , Math.Round( masterSolutions[0].XEnter , 2 ) );
                    contents[icrit + 6] = string.Format( "Y_enter =\t{0}" , Math.Round( masterSolutions[0].YEnter , 2 ) );
                    contents[icrit + 7] = string.Format( "X_exit =\t{0}" , Math.Round( masterSolutions[0].XExit , 2 ) );
                    contents[icrit + 8] = string.Format( "Y_exit =\t{0}" , Math.Round( masterSolutions[0].YExit , 2 ) );
                    contents[icrit + 9] = string.Format( "Fs =\t\t{0}" , Math.Round( masterSolutions[0].SF , 3 ) );
                    contents[icrit + 10] = string.Format( "Run No. =\t{0}" , runs );
                }

                // Write all contents back to file
                File.WriteAllLines( path , contents );

                // Append current run results to end of file
                using ( TextWriter tw = File.AppendText( path ) )
                {
                    tw.WriteLine();
                    tw.WriteLine( "Run #{0}" , runs );
                    tw.WriteLine( "{0}" , DateTime.Now );
                    tw.WriteLine( "X_centre\tY_centre\tR\t\tX_enter\t\tY_enter\t\tX_exit\t\tY_exit\t\tFs" );

                    for ( int i = 0 ; i < masterSolutions.Count ; i++ )
                    {
                        tw.WriteLine( "{0}\t\t{1}\t\t{2}\t\t{3}\t\t{4}\t\t{5}\t\t{6}\t\t{7}\t\t" ,
                            Math.Round( masterSolutions[i].X , 2 ) , Math.Round( masterSolutions[i].Y , 2 ) , Math.Round( masterSolutions[i].R , 2 ) ,
                            Math.Round( masterSolutions[i].XEnter , 2 ) , Math.Round( masterSolutions[i].YEnter , 2 ) ,
                            Math.Round( masterSolutions[i].XExit , 2 ) , Math.Round( masterSolutions[i].YExit , 2 ) ,
                            Math.Round( masterSolutions[i].SF , 3 ) );
                    }
                }
            }

            // CREATE A NEW RESULTS FILE
            else
            {
                using ( TextWriter tw = new StreamWriter( path ) )
                {
                    tw.WriteLine( "*****************************************" );
                    tw.WriteLine();
                    tw.WriteLine( "        GENETIC ALGORITHM OUTPUT" );
                    tw.WriteLine();
                    switch ( canvas.AnalysisType )
                    {
                        case AnalysisType.Bishop: tw.WriteLine( "    ANALYSIS METHOD: Bishop's Method" ); break;
                        default: tw.WriteLine( "         ANALYSIS METHOD: RFEM" ); break;
                    }
                    tw.WriteLine();
                    tw.WriteLine( "*****************************************" );
                    tw.WriteLine();

                    string units;
                    switch ( canvas.Units )
                    {
                        case Units.Metres: units = "m, kPa, kN/m^3"; break;
                        case Units.Millimetres: units = "mm, kPa, kN/m^3"; break;
                        case Units.Feet: units = "ft, psi, pcf"; break;
                        default: units = "in, psi, pcf"; break;
                    }

                    tw.WriteLine( "Units = {0}" , units );
                    tw.WriteLine();
                    tw.WriteLine();
                    tw.WriteLine( "UPPER SURFACE GEOMETRY" );
                    tw.WriteLine( "--------------------------" );
                    tw.WriteLine( "Number of Points = {0}" , surface.Count );
                    tw.WriteLine();
                    tw.WriteLine( "(X,Y)" );

                    for ( int i = 0 ; i < surface.Count ; i++ )
                        tw.WriteLine( "{0}, {1}" , Math.Round( surface[i].X , 2 ) , Math.Round( surface[i].Y , 2 ) );

                    tw.WriteLine();
                    tw.WriteLine();
                    tw.WriteLine( "MATERIAL DATA" );
                    tw.WriteLine( "--------------------------" );
                    tw.WriteLine( "Number of Material Types = {0}" , canvas.MaterialTypes.Count );
                    tw.WriteLine();

                    for ( int i = 0 ; i < canvas.MaterialTypes.Count ; i++ )
                    {
                        tw.WriteLine( "Material #{0}" , i + 1 );
                        tw.WriteLine( "Name = \"{0}\"" , canvas.MaterialTypes[i].Name );
                        tw.WriteLine( "Phi = {0}" , Math.Round( canvas.MaterialTypes[i].Phi , 2 ) );
                        tw.WriteLine( "Cohesion = {0}" , Math.Round( canvas.MaterialTypes[i].Cohesion , 2 ) );
                        tw.WriteLine( "Unit Weight = {0}" , Math.Round( canvas.MaterialTypes[i].Gamma , 2 ) );
                        tw.WriteLine();
                    }

                    tw.WriteLine();
                    tw.WriteLine( "MATERIAL BLOCK GEOMETRY" );
                    tw.WriteLine( "--------------------------" );
                    tw.WriteLine( "Number of Material Blocks = {0}" , canvas.MaterialBlocks.Count );
                    tw.WriteLine();

                    Point p;
                    double xCoord , yCoord;

                    for ( int i = 0 ; i < canvas.MaterialBlocks.Count ; i++ )
                    {
                        tw.WriteLine( "MB{0}" , i + 1 );
                        tw.WriteLine( "Material Type = \"{0}\"" , canvas.MaterialBlocks[i].Material );
                        tw.WriteLine( "Number of Points = {0}" , canvas.MaterialBlocks[i].BoundaryPoints.Count );
                        tw.WriteLine( "(X,Y)" );

                        for ( int j = 0 ; j < canvas.MaterialBlocks[i].BoundaryPoints.Count ; j++ )
                        {
                            p = canvas.MaterialBlocks[i].BoundaryPoints[j].Point;

                            xCoord = (p.X - canvas.OriginOffsetX) / canvas.DpiX * lengthFactor * canvas.Scale;
                            yCoord = (canvas.ActualHeight - p.Y - canvas.OriginOffsetY) / canvas.DpiY * lengthFactor * canvas.Scale;

                            tw.WriteLine( "{0}, {1}" , Math.Round( xCoord , 2 ) , Math.Round( yCoord , 2 ) );
                        }

                        tw.WriteLine();
                    }

                    tw.WriteLine();
                    tw.WriteLine( "MOST CRITICAL SURFACE" );
                    tw.WriteLine( "--------------------------" );
                    tw.WriteLine( "X_centre =\t{0}" , Math.Round( masterSolutions[0].X , 2 ) );
                    tw.WriteLine( "Y_centre =\t{0}" , Math.Round( masterSolutions[0].Y , 2 ) );
                    tw.WriteLine( "R =\t\t{0}" , Math.Round( masterSolutions[0].R , 2 ) );
                    tw.WriteLine( "X_enter =\t{0}" , Math.Round( masterSolutions[0].XEnter , 2 ) );
                    tw.WriteLine( "Y_enter =\t{0}" , Math.Round( masterSolutions[0].YEnter , 2 ) );
                    tw.WriteLine( "X_exit =\t{0}" , Math.Round( masterSolutions[0].XExit , 2 ) );
                    tw.WriteLine( "Y_exit =\t{0}" , Math.Round( masterSolutions[0].YExit , 2 ) );
                    tw.WriteLine( "Fs =\t\t{0}" , Math.Round( masterSolutions[0].SF , 3 ) );
                    tw.WriteLine( "Run No. =\t{0}" , 1 );       // this is the first run

                    tw.WriteLine();
                    tw.WriteLine();
                    tw.WriteLine( "ADDITIONAL RUN OUTPUT" );
                    tw.WriteLine( "--------------------------" );
                    tw.WriteLine( "Number of Runs = {0}" , 1 ); // this is the first run
                    tw.WriteLine();
                    tw.WriteLine( "Run #{0}" , 1 );             // this is the first run
                    tw.WriteLine( "{0}" , DateTime.Now );

                    tw.WriteLine( "X_centre\tY_centre\tR\t\tX_enter\t\tY_enter\t\tX_exit\t\tY_exit\t\tFs" );

                    for ( int i = 0 ; i < masterSolutions.Count ; i++ )
                    {
                        tw.WriteLine( "{0}\t\t{1}\t\t{2}\t\t{3}\t\t{4}\t\t{5}\t\t{6}\t\t{7}\t\t" ,
                            Math.Round( masterSolutions[i].X , 2 ) , Math.Round( masterSolutions[i].Y , 2 ) , Math.Round( masterSolutions[i].R , 2 ) ,
                            Math.Round( masterSolutions[i].XEnter , 2 ) , Math.Round( masterSolutions[i].YEnter , 2 ) ,
                            Math.Round( masterSolutions[i].XExit , 2 ) , Math.Round( masterSolutions[i].YExit , 2 ) ,
                            Math.Round( masterSolutions[i].SF , 3 ) );
                    }
                }
            }

            return masterSolutions[0].SF;
        }
        private void add_Click( object sender , RoutedEventArgs e )
        {
            MaterialType newMaterial = new MaterialType();

            if ( materialList.Text == "Add new material..." || materialList.Text == "" )
            {
                MessageBox.Show( "Must give the material a name." , "Error" );
                return;
            }

            if ( materialList.Text == "NULL" )
            {
                MessageBox.Show( "NULL material name is reserved." , "Error" );
                return;
            }

            if ( colour.Fill == Brushes.Transparent )
            {
                MessageBox.Show( "Must define a colour." , "Error" );
                return;
            }

            double newPhi;
            if ( !double.TryParse( phi.Text , out newPhi ) || newPhi < 0 )
            {
                MessageBox.Show( "Angle of friction must be a positive value." , "Error" );
                return;
            }

            double newCoh;
            if ( !double.TryParse( coh.Text , out newCoh ) || newCoh < 0 )
            {
                MessageBox.Show( "Cohesion must be a positive value." , "Error" );
                return;
            }

            double newPsi;
            if ( !double.TryParse( psi.Text , out newPsi ) || newPsi < 0 )
            {
                MessageBox.Show( "Dilatancy angle must be a positive value." , "Error" );
                return;
            }

            double newGamma;
            if ( !double.TryParse( gamma.Text , out newGamma ) || newGamma < 0 )
            {
                MessageBox.Show( "Unit weight must be a positive value." , "Error" );
                return;
            }

            double newEmod;
            if ( !double.TryParse( emod.Text , out newEmod ) || newEmod < 0 )
            {
                MessageBox.Show( "Elastic modulus must be a positive value." , "Error" );
                return;
            }

            double newNu;
            if ( !double.TryParse( nu.Text , out newNu ) || newNu < 0 || newNu >= 0.5 )
            {
                MessageBox.Show( "Poisson's ratio must be a value in the range: 0 <= nu < 0.5" , "Error" );
                return;
            }

            newMaterial.Name = materialList.Text;
            newMaterial.Fill = colour.Fill;
            newMaterial.Phi = newPhi;
            newMaterial.Cohesion = newCoh;
            newMaterial.Psi = newPsi;
            newMaterial.Gamma = newGamma;
            newMaterial.Emod = newEmod;
            newMaterial.Nu = newNu;

            canvas.MaterialTypes.Insert( canvas.MaterialTypes.Count - 1 , newMaterial );

            materialList.Items.Clear();

            materialList.Items.Add( "Add new material..." );

            for ( int i = 0 ; i < canvas.MaterialTypes.Count - 1 ; i++ )
            {
                materialList.Items.Add( canvas.MaterialTypes[i] );
            }

            materialList.SelectedIndex = 0;
            materialList.Focus();

            canvas.IsSaved = false;
        }