// Result must be as large as Mesh.MaxVertexID
        public bool SolveMultipleRHS(Vector3d[] Result)
        {
            if (WeightsM == null)
            {
                Initialize();                       // force initialize...
            }

            UpdateForSolve();

            // use initial positions as initial solution.
            double[][] B = BufferUtil.InitNxM(3, N, new double[][] { Bx, By, Bz });
            double[][] X = BufferUtil.InitNxM(3, N, new double[][] { Px, Py, Pz });

            Action <double[][], double[][]> CombinedMultiply = (Xt, Bt) =>
            {
                PackedM.Multiply_Parallel_3(Xt, Bt);
                gParallel.ForEach(Interval1i.Range(3), (j) =>
                {
                    BufferUtil.MultiplyAdd(Bt[j], WeightsM.D, Xt[j]);
                });
            };

            var Solver = new SparseSymmetricCGMultipleRHS()
            {
                B                     = B,
                X                     = X,
                MultiplyF             = CombinedMultiply,
                PreconditionMultiplyF = null,
                UseXAsInitialGuess    = true
            };

            bool ok = Solver.Solve();

            if (ok == false)
            {
                return(false);
            }

            for (int i = 0; i < N; ++i)
            {
                int vid = ToMeshV[i];
                Result[vid] = new Vector3d(X[0][i], X[1][i], X[2][i]);
            }

            // apply post-fixed constraints
            if (HavePostFixedConstraints)
            {
                foreach (var constraint in SoftConstraints)
                {
                    if (constraint.Value.PostFix)
                    {
                        int vid = constraint.Key;
                        Result[vid] = constraint.Value.Position;
                    }
                }
            }

            return(true);
        }
        //
        // DMesh3 construction utilities
        //

        /// <summary>
        /// ultimate generic mesh-builder, pass it arrays of floats/doubles, or lists
        /// of Vector3d, or anything in-between. Will figure out how to interpret
        ///
        /// This static function attempts to retain a manifold mesh, if you need finer
        /// control use the concrete class.
        ///
        /// Number of issues encountered adding verices or triangls are stored in the
        /// mesh metadata. Metadata can be cleared once the returning object is evaluated.
        /// </summary>
        public static DMesh3 Build <VType, TType, NType>(IEnumerable <VType> Vertices,
                                                         IEnumerable <TType> Triangles,
                                                         IEnumerable <NType> Normals = null,
                                                         IEnumerable <int> TriGroups = null)
        {
            DMesh3 mesh = new DMesh3(Normals != null, false, false, TriGroups != null);

            // build outcomes are stored in the metadata to avoid changes to the function signature
            //
            int iAppendTriangleIssues = 0;

            Vector3d[] v = BufferUtil.ToVector3d(Vertices);
            for (int i = 0; i < v.Length; ++i)
            {
                mesh.AppendVertex(v[i]);
            }


            if (Normals != null)
            {
                Vector3f[] n = BufferUtil.ToVector3f(Normals);
                if (n.Length != v.Length)
                {
                    throw new Exception("DMesh3Builder.Build: incorrect number of normals provided");
                }
                for (int i = 0; i < n.Length; ++i)
                {
                    mesh.SetVertexNormal(i, n[i]);
                }
            }

            Index3i[] t = BufferUtil.ToIndex3i(Triangles);
            for (int i = 0; i < t.Length; ++i)
            {
                var last = mesh.AppendTriangle(t[i]);
                if (last == DMesh3.InvalidID || last == DMesh3.NonManifoldID)
                {
                    iAppendTriangleIssues++;
                }
            }

            if (TriGroups != null)
            {
                List <int> groups = new List <int>(TriGroups);
                if (groups.Count != t.Length)
                {
                    throw new Exception("DMesh3Builder.Build: incorect number of triangle groups");
                }
                for (int i = 0; i < t.Length; ++i)
                {
                    mesh.SetTriangleGroup(i, groups[i]);
                }
            }
            mesh.AttachMetadata("AppendTriangleIssues", iAppendTriangleIssues);

            return(mesh);
        }
        //
        // DMesh3 construction utilities
        //

        /// <summary>
        /// ultimate generic mesh-builder, pass it arrays of floats/doubles, or lists
        /// of Vector3d, or anything in-between. Will figure out how to interpret
        /// </summary>
        public static DMesh3 Build <VType, TType, NType>(IEnumerable <VType> Vertices,
                                                         IEnumerable <TType> Triangles,
                                                         IEnumerable <NType> Normals = null,
                                                         IEnumerable <int> TriGroups = null)
        {
            var mesh = new DMesh3(Normals != null, false, false, TriGroups != null);

            Vector3d[] v = BufferUtil.ToVector3d(Vertices);
            for (int i = 0; i < v.Length; ++i)
            {
                mesh.AppendVertex(v[i]);
            }

            if (Normals != null)
            {
                Vector3f[] n = BufferUtil.ToVector3f(Normals);
                if (n.Length != v.Length)
                {
                    throw new Exception("DMesh3Builder.Build: incorrect number of normals provided");
                }

                for (int i = 0; i < n.Length; ++i)
                {
                    mesh.SetVertexNormal(i, n[i]);
                }
            }

            Index3i[] t = BufferUtil.ToIndex3i(Triangles);
            for (int i = 0; i < t.Length; ++i)
            {
                mesh.AppendTriangle(t[i]);
            }

            if (TriGroups != null)
            {
                var groups = new List <int>(TriGroups);
                if (groups.Count != t.Length)
                {
                    throw new Exception("DMesh3Builder.Build: incorect number of triangle groups");
                }

                for (int i = 0; i < t.Length; ++i)
                {
                    mesh.SetTriangleGroup(i, groups[i]);
                }
            }

            return(mesh);
        }
        /// <summary>
        /// Find the set of boundary EdgeLoops. Note that if we encounter topological
        /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible)
        /// </summary>
        public bool Compute()
        {
            // This algorithm assumes that triangles are oriented consistently,
            // so closed boundary-loop can be followed by walking edges in-order

            Loops = new List <EdgeLoop>();
            Spans = new List <EdgeSpan>();

            // early-out if we don't actually have boundaries
            if (Mesh.CachedIsClosed)
            {
                return(true);
            }

            int NE = Mesh.MaxEdgeID;

            // Temporary memory used to indicate when we have "used" an edge.
            BitArray used_edge = new BitArray(NE);

            used_edge.SetAll(false);

            // current loop is stored here, cleared after each loop extracted
            List <int> loop_edges = new List <int>();     // [RMS] not sure we need this...
            List <int> loop_verts = new List <int>();
            List <int> bowties    = new List <int>();

            // Temp buffer for reading back all boundary edges of a vertex.
            // probably always small but in pathological cases it could be large...
            int[] all_e = new int[16];

            // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx?

            // process all edges of mesh
            for (int eid = 0; eid < NE; ++eid)
            {
                if (!Mesh.IsEdge(eid))
                {
                    continue;
                }
                if (used_edge[eid] == true)
                {
                    continue;
                }
                if (Mesh.IsBoundaryEdge(eid) == false)
                {
                    continue;
                }

                if (EdgeFilterF != null && EdgeFilterF(eid) == false)
                {
                    used_edge[eid] = true;
                    continue;
                }

                // ok this is start of a boundary chain
                int eStart = eid;
                used_edge[eStart] = true;
                loop_edges.Add(eStart);

                int eCur = eid;

                // follow the chain in order of oriented edges
                bool bClosed     = false;
                bool bIsOpenSpan = false;
                while (!bClosed)
                {
                    Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur);
                    int     cure_a = ev.a, cure_b = ev.b;
                    if (bIsOpenSpan)
                    {
                        cure_a = ev.b; cure_b = ev.a;
                    }
                    else
                    {
                        loop_verts.Add(cure_a);
                    }

                    int e0 = -1, e1 = 1;
                    int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1);

                    // have to filter this list, if we are filtering. this is ugly.
                    if (EdgeFilterF != null)
                    {
                        if (bdry_nbrs > 2)
                        {
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[bdry_nbrs];
                            }
                            // we may repreat this below...irritating...
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be);
                        }
                        else
                        {
                            if (EdgeFilterF(e0) == false)
                            {
                                bdry_nbrs--;
                            }
                            if (EdgeFilterF(e1) == false)
                            {
                                bdry_nbrs--;
                            }
                        }
                    }


                    if (bdry_nbrs < 2)     // hit an 'endpoint' vertex (should only happen when Filter is on...)
                    {
                        if (SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b)
                                  {
                                      UnclosedLoop = true
                                  }
                        }
                        ;
                        if (bIsOpenSpan)
                        {
                            bClosed = true;
                            continue;
                        }
                        else
                        {
                            bIsOpenSpan = true;          // begin open span
                            eCur        = loop_edges[0]; // restart at other end of loop
                            loop_edges.Reverse();        // do this so we can push to front
                            continue;
                        }
                    }

                    int eNext = -1;

                    if (bdry_nbrs > 2)
                    {
                        // found "bowtie" vertex...things just got complicated!

                        if (cure_b == loop_verts[0])
                        {
                            // The "end" of the current edge is the same as the start vertex.
                            // This means we can close the loop here. Might as well!
                            eNext = -2;                               // sentinel value used below
                        }
                        else
                        {
                            // try to find an unused outgoing edge that is oriented properly.
                            // This could create sub-loops, we will handle those later
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[2 * bdry_nbrs];
                            }
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            Debug.Assert(num_be == bdry_nbrs);

                            if (EdgeFilterF != null)
                            {
                                num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be);
                            }

                            // Try to pick the best "turn left" vertex.
                            eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge);
                            if (eNext == -1)
                            {
                                if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                                {
                                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b)
                                          {
                                              BowtieFailure = true
                                          }
                                }
                                ;

                                // ok, we are stuck. all we can do now is terminate this loop and keep it as a span
                                if (bIsOpenSpan)
                                {
                                    bClosed = true;
                                }
                                else
                                {
                                    bIsOpenSpan = true;
                                    bClosed     = true;
                                }
                                continue;
                            }
                        }

                        if (bowties.Contains(cure_b) == false)
                        {
                            bowties.Add(cure_b);
                        }
                    }
                    else
                    {
                        // walk forward to next available edge
                        Debug.Assert(e0 == eCur || e1 == eCur);
                        eNext = (e0 == eCur) ? e1 : e0;
                    }

                    if (eNext == -2)
                    {
                        // found a bowtie vert that is the same as start-of-loop, so we
                        // are just closing it off explicitly
                        bClosed = true;
                    }
                    else if (eNext == eStart)
                    {
                        // found edge at start of loop, so loop is done.
                        bClosed = true;
                    }
                    else if (used_edge[eNext] != false)
                    {
                        // disaster case - the next edge is already used, but it is not the start of our loop
                        // All we can do is convert to open span and terminate
                        if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext)
                                  {
                                      RepeatedEdge = true
                                  }
                        }
                        ;
                        bIsOpenSpan = true;
                        bClosed     = true;
                    }
                    else
                    {
                        // push onto accumulated list
                        Debug.Assert(used_edge[eNext] == false);
                        loop_edges.Add(eNext);
                        used_edge[eNext] = true;
                        eCur             = eNext;
                    }
                }

                if (bIsOpenSpan)
                {
                    SawOpenSpans = true;
                    if (SpanBehavior == SpanBehaviors.Compute)
                    {
                        loop_edges.Reverse();  // orient properly
                        EdgeSpan span = EdgeSpan.FromEdges(Mesh, loop_edges);
                        Spans.Add(span);
                    }
                }
                else if (bowties.Count > 0)
                {
                    // if we saw a bowtie vertex, we might need to break up this loop,
                    // so call extract_subloops
                    Subloops subloops = extract_subloops(loop_verts, loop_edges, bowties);
                    foreach (var loop in subloops.Loops)
                    {
                        Loops.Add(loop);
                    }
                    if (subloops.Spans.Count > 0)
                    {
                        FellBackToSpansOnFailure = true;
                        foreach (var span in subloops.Spans)
                        {
                            Spans.Add(span);
                        }
                    }
                }
                else
                {
                    // clean simple loop, convert to EdgeLoop instance
                    EdgeLoop loop = new EdgeLoop(Mesh);
                    loop.Vertices = loop_verts.ToArray();
                    loop.Edges    = loop_edges.ToArray();
                    Loops.Add(loop);
                }

                // reset these lists
                loop_edges.Clear();
                loop_verts.Clear();
                bowties.Clear();
            }

            return(true);
        }

        // [TODO] cache this in a dictionary? we will not need very many, but we will
        //   need each multiple times!
        Vector3d get_vtx_normal(int vid)
        {
            Vector3d n = Vector3d.Zero;

            foreach (int ti in Mesh.VtxTrianglesItr(vid))
            {
                n += Mesh.GetTriNormal(ti);
            }
            n.Normalize();
            return(n);
        }

        // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v.
        // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e.
        // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v.
        // So, we compute the tangent plane at bowtie_v, and then the signed angle for each
        // viable edge in this plane.
        //
        // [TODO] handle degenerate edges. what do we do then? Currently will only chose
        //  degenerate edge if there are no other options (I think...)
        int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges)
        {
            // compute normal and edge [a,bowtie]
            Vector3d n       = get_vtx_normal(bowtie_v);
            int      other_v = Mesh.edge_other_v(incoming_e, bowtie_v);
            Vector3d ab      = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v);

            // our winner
            int    best_e     = -1;
            double best_angle = double.MaxValue;

            for (int i = 0; i < bdry_edges_count; ++i)
            {
                int bdry_eid = bdry_edges[i];
                if (used_edges[bdry_eid] == true)
                {
                    continue;       // this edge is already used
                }
                Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid);
                if (bdry_ev.a != bowtie_v)
                {
                    continue;       // have to be able to chain to end of current edge, orientation-wise
                }
                // compute projected angle
                Vector3d bc      = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v);
                float    fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n);

                // turn left!
                if (best_angle == double.MaxValue || fAngleS < best_angle)
                {
                    best_angle = fAngleS;
                    best_e     = bdry_eid;
                }
            }

            // [RMS] w/ bowtie vertices and open spans, this does happen
            //Debug.Assert(best_e != -1);

            return(best_e);
        }
Exemple #5
0
        /// <summary>
        /// Preconditioned variant
        /// Similar to non-preconditioned version, this can suffer if one solution converges
        /// much slower than others, as we can't skip matrix multiplies in that case.
        /// </summary>
        public bool SolvePreconditioned()
        {
            Iterations = 0;
            if (B == null || MultiplyF == null || PreconditionMultiplyF == null)
            {
                throw new Exception("SparseSymmetricCGMultipleRHS.SolvePreconditioned(): Must set B and MultiplyF and PreconditionMultiplyF!");
            }

            int NRHS = B.Length;

            if (NRHS == 0)
            {
                throw new Exception("SparseSymmetricCGMultipleRHS.SolvePreconditioned(): Need at least one RHS vector in B");
            }

            int n = B[0].Length;

            R  = BufferUtil.AllocNxM(NRHS, n);
            P  = BufferUtil.AllocNxM(NRHS, n);
            AP = BufferUtil.AllocNxM(NRHS, n);
            Z  = BufferUtil.AllocNxM(NRHS, n);

            if (X == null || UseXAsInitialGuess == false)
            {
                if (X == null)
                {
                    X = BufferUtil.AllocNxM(NRHS, n);
                }

                for (int j = 0; j < NRHS; ++j)
                {
                    Array.Clear(X[j], 0, n);
                    Array.Copy(B[j], R[j], n);
                }
            }
            else
            {
                // hopefully is X is a decent initialization...
                InitializeR(R);
            }

            // [RMS] for convergence test?
            double[] norm = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                norm[j] = BufferUtil.Dot(B[j], B[j]);
            }

            double[] root1 = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                root1[j] = Math.Sqrt(norm[j]);
            }


            // r_0 = b - A*x_0
            MultiplyF(X, R);
            for (int j = 0; j < NRHS; ++j)
            {
                for (int i = 0; i < n; ++i)
                {
                    R[j][i] = B[j][i] - R[j][i];
                }
            }

            // z0 = M_inverse * r_0
            PreconditionMultiplyF(R, Z);

            // p0 = z0
            for (int j = 0; j < NRHS; ++j)
            {
                Array.Copy(Z[j], P[j], n);
            }

            // compute initial R*Z
            double[] RdotZ_k = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                RdotZ_k[j] = BufferUtil.Dot(R[j], Z[j]);
            }

            double[] alpha_k   = new double[NRHS];
            double[] beta_k    = new double[NRHS];
            bool[]   converged = new bool[NRHS];
            var      rhs       = Interval1i.Range(NRHS);

            int iter = 0;

            while (iter++ < MaxIterations)
            {
                // convergence test
                bool done = true;
                for (int j = 0; j < NRHS; ++j)
                {
                    if (converged[j] == false)
                    {
                        double root0 = Math.Sqrt(RdotZ_k[j]);
                        if (root0 <= ConvergeTolerance * root1[j])
                        {
                            converged[j] = true;
                        }
                    }
                    if (converged[j] == false)
                    {
                        done = false;
                    }
                }
                if (done)
                {
                    break;
                }

                MultiplyF(P, AP);

                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        alpha_k[j] = RdotZ_k[j] / BufferUtil.Dot(P[j], AP[j]);
                    }
                });

                // x_k+1 = x_k + alpha_k * p_k
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        BufferUtil.MultiplyAdd(X[j], alpha_k[j], P[j]);
                    }
                });

                // r_k+1 = r_k - alpha_k * A * p_k
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        BufferUtil.MultiplyAdd(R[j], -alpha_k[j], AP[j]);
                    }
                });

                // z_k+1 = M_inverse * r_k+1
                PreconditionMultiplyF(R, Z);

                // beta_k = (z_k+1 * r_k+1) / (z_k * r_k)
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        beta_k[j] = BufferUtil.Dot(Z[j], R[j]) / RdotZ_k[j];
                    }
                });

                // can do these in parallel but improvement is minimal

                // p_k+1 = z_k+1 + beta_k * p_k
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        for (int i = 0; i < n; ++i)
                        {
                            P[j][i] = Z[j][i] + beta_k[j] * P[j][i];
                        }
                    }
                });

                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        RdotZ_k[j] = BufferUtil.Dot(R[j], Z[j]);
                    }
                });
            }


            //System.Console.WriteLine("{0} iterations", iter);
            Iterations = iter;
            return(iter < MaxIterations);
        }
Exemple #6
0
        public bool Solve()
        {
            Iterations = 0;
            int size = B.Length;

            // Based on the algorithm in "Matrix Computations" by Golum and Van Loan.
            R  = new double[size];
            P  = new double[size];
            AP = new double[size];

            if (X == null || UseXAsInitialGuess == false)
            {
                if (X == null)
                {
                    X = new double[size];
                }

                Array.Clear(X, 0, X.Length);
                Array.Copy(B, R, B.Length);
            }
            else
            {
                // hopefully is X is a decent initialization...
                InitializeR(R);
            }

            // [RMS] these were inside loop but they are constant!
            double norm  = BufferUtil.Dot(B, B);
            double root1 = Math.Sqrt(norm);

            // The first iteration.
            double rho0 = BufferUtil.Dot(R, R);

            // [RMS] If we were initialized w/ constraints already satisfied,
            //   then we are done! (happens for example in mesh deformations)
            if (rho0 < MathUtil.ZeroTolerance * root1)
            {
                return(true);
            }

            Array.Copy(R, P, R.Length);

            MultiplyF(P, AP);

            double alpha = rho0 / BufferUtil.Dot(P, AP);

            BufferUtil.MultiplyAdd(X, alpha, P);
            BufferUtil.MultiplyAdd(R, -alpha, AP);
            double rho1 = BufferUtil.Dot(R, R);

            // The remaining iterations.
            int iter;

            for (iter = 1; iter < MaxIterations; ++iter)
            {
                double root0 = Math.Sqrt(rho1);
                if (root0 <= MathUtil.ZeroTolerance * root1)
                {
                    break;
                }

                double beta = rho1 / rho0;
                UpdateP(P, beta, R);

                MultiplyF(P, AP);

                alpha = rho1 / BufferUtil.Dot(P, AP);

                // can compute these two steps simultaneously
                double RdotR = 0;
                gParallel.Evaluate(
                    () => { BufferUtil.MultiplyAdd(X, alpha, P); },
                    () => { RdotR = BufferUtil.MultiplyAdd_GetSqrSum(R, -alpha, AP); }
                    );

                rho0 = rho1;
                rho1 = RdotR;                 // BufferUtil.Dot(R, R);
            }

            //System.Console.WriteLine("{0} iterations", iter);
            Iterations = iter;
            return(iter < MaxIterations);
        }
Exemple #7
0
        /// <summary>
        /// standard CG solve
        /// </summary>
        public bool Solve()
        {
            Iterations = 0;
            if (B == null || MultiplyF == null)
            {
                throw new Exception("SparseSymmetricCGMultipleRHS.Solve(): Must set B and MultiplyF!");
            }

            int NRHS = B.Length;

            if (NRHS == 0)
            {
                throw new Exception("SparseSymmetricCGMultipleRHS.Solve(): Need at least one RHS vector in B");
            }

            int size = B[0].Length;

            // Based on the algorithm in "Matrix Computations" by Golum and Van Loan.
            R = BufferUtil.AllocNxM(NRHS, size);
            P = BufferUtil.AllocNxM(NRHS, size);
            W = BufferUtil.AllocNxM(NRHS, size);

            if (X == null || UseXAsInitialGuess == false)
            {
                if (X == null)
                {
                    X = BufferUtil.AllocNxM(NRHS, size);
                }

                for (int j = 0; j < NRHS; ++j)
                {
                    Array.Clear(X[j], 0, size);
                    Array.Copy(B[j], R[j], size);
                }
            }
            else
            {
                // hopefully is X is a decent initialization...
                InitializeR(R);
            }

            // [RMS] these were inside loop but they are constant!
            double[] norm = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                norm[j] = BufferUtil.Dot(B[j], B[j]);
            }

            double[] root1 = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                root1[j] = Math.Sqrt(norm[j]);
            }

            // The first iteration.
            double[] rho0 = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                rho0[j] = BufferUtil.Dot(R[j], R[j]);
            }

            // [RMS] If we were initialized w/ constraints already satisfied,
            //   then we are done! (happens for example in mesh deformations)
            bool[] converged  = new bool[NRHS];
            int    nconverged = 0;

            for (int j = 0; j < NRHS; ++j)
            {
                converged[j] = rho0[j] < (ConvergeTolerance * root1[j]);
                if (converged[j])
                {
                    nconverged++;
                }
            }
            if (nconverged == NRHS)
            {
                return(true);
            }

            for (int j = 0; j < NRHS; ++j)
            {
                Array.Copy(R[j], P[j], size);
            }

            MultiplyF(P, W);

            double[] alpha = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                alpha[j] = rho0[j] / BufferUtil.Dot(P[j], W[j]);
            }

            for (int j = 0; j < NRHS; ++j)
            {
                BufferUtil.MultiplyAdd(X[j], alpha[j], P[j]);
            }

            for (int j = 0; j < NRHS; ++j)
            {
                BufferUtil.MultiplyAdd(R[j], -alpha[j], W[j]);
            }

            double[] rho1 = new double[NRHS];
            for (int j = 0; j < NRHS; ++j)
            {
                rho1[j] = BufferUtil.Dot(R[j], R[j]);
            }

            double[] beta = new double[NRHS];

            var rhs = Interval1i.Range(NRHS);

            // The remaining iterations.
            int iter;

            for (iter = 1; iter < MaxIterations; ++iter)
            {
                bool done = true;
                for (int j = 0; j < NRHS; ++j)
                {
                    if (converged[j] == false)
                    {
                        double root0 = Math.Sqrt(rho1[j]);
                        if (root0 <= ConvergeTolerance * root1[j])
                        {
                            converged[j] = true;
                        }
                    }
                    if (converged[j] == false)
                    {
                        done = false;
                    }
                }
                if (done)
                {
                    break;
                }

                for (int j = 0; j < NRHS; ++j)
                {
                    beta[j] = rho1[j] / rho0[j];
                }

                UpdateP(P, beta, R, converged);

                MultiplyF(P, W);

                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        alpha[j] = rho1[j] / BufferUtil.Dot(P[j], W[j]);
                    }
                });

                // can do all these in parallel, but improvement is minimal
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        BufferUtil.MultiplyAdd(X[j], alpha[j], P[j]);
                    }
                });
                gParallel.ForEach(rhs, (j) =>
                {
                    if (converged[j] == false)
                    {
                        rho0[j] = rho1[j];
                        rho1[j] = BufferUtil.MultiplyAdd_GetSqrSum(R[j], -alpha[j], W[j]);
                    }
                });
            }

            //System.Console.WriteLine("{0} iterations", iter);
            Iterations = iter;
            return(iter < MaxIterations);
        }
Exemple #8
0
        public bool SolvePreconditioned()
        {
            Iterations = 0;
            int n = B.Length;

            R  = new double[n];
            P  = new double[n];
            AP = new double[n];
            Z  = new double[n];

            if (X == null || UseXAsInitialGuess == false)
            {
                if (X == null)
                {
                    X = new double[n];
                }

                Array.Clear(X, 0, X.Length);
                Array.Copy(B, R, B.Length);
            }
            else
            {
                // hopefully is X is a decent initialization...
                InitializeR(R);
            }

            // [RMS] for convergence test?
            double norm  = BufferUtil.Dot(B, B);
            double root1 = Math.Sqrt(norm);

            // r_0 = b - A*x_0
            MultiplyF(X, R);
            for (int i = 0; i < n; ++i)
            {
                R[i] = B[i] - R[i];
            }

            // z0 = M_inverse * r_0
            PreconditionMultiplyF(R, Z);

            // p0 = z0
            Array.Copy(Z, P, n);

            double RdotZ_k = BufferUtil.Dot(R, Z);

            int iter = 0;

            while (iter++ < MaxIterations)
            {
                // convergence test
                double root0 = Math.Sqrt(RdotZ_k);
                if (root0 <= MathUtil.ZeroTolerance * root1)
                {
                    break;
                }

                MultiplyF(P, AP);
                double alpha_k = RdotZ_k / BufferUtil.Dot(P, AP);

                gParallel.Evaluate(
                    // x_k+1 = x_k + alpha_k * p_k
                    () => { BufferUtil.MultiplyAdd(X, alpha_k, P); },
                    // r_k+1 = r_k - alpha_k * A * p_k
                    () => { BufferUtil.MultiplyAdd(R, -alpha_k, AP); }
                    );

                // z_k+1 = M_inverse * r_k+1
                PreconditionMultiplyF(R, Z);

                // beta_k = (z_k+1 * r_k+1) / (z_k * r_k)
                double beta_k = BufferUtil.Dot(Z, R) / RdotZ_k;

                gParallel.Evaluate(
                    // p_k+1 = z_k+1 + beta_k * p_k
                    () =>
                {
                    for (int i = 0; i < n; ++i)
                    {
                        P[i] = Z[i] + beta_k * P[i];
                    }
                },
                    () => { RdotZ_k = BufferUtil.Dot(R, Z); }
                    );
            }


            //System.Console.WriteLine("{0} iterations", iter);
            Iterations = iter;
            return(iter < MaxIterations);
        }
Exemple #9
0
        /// <summary>
        /// Find the set of boundary EdgeLoops. Note that if we encounter topological
        /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible)
        /// </summary>
        public bool Compute()
        {
            // This algorithm assumes that triangles are oriented consistently,
            // so closed boundary-loop can be followed by walking edges in-order

            Loops = new List <EdgeLoop>();
            Spans = new List <EdgeSpan>();

            int NE = Mesh.MaxEdgeID;

            // Temporary memory used to indicate when we have "used" an edge.
            BitArray used_edge = new BitArray(NE);

            used_edge.SetAll(false);

            // current loop is stored here, cleared after each loop extracted
            List <int> loop_edges = new List <int>();     // [RMS] not sure we need this...
            List <int> loop_verts = new List <int>();
            List <int> bowties    = new List <int>();

            // Temp buffer for reading back all boundary edges of a vertex.
            // probably always small but in pathological cases it could be large...
            int[] all_e = new int[16];

            // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx?

            // process all edges of mesh
            for (int eid = 0; eid < NE; ++eid)
            {
                if (!Mesh.IsEdge(eid))
                {
                    continue;
                }
                if (used_edge[eid] == true)
                {
                    continue;
                }
                if (Mesh.IsBoundaryEdge(eid) == false)
                {
                    continue;
                }

                if (EdgeFilterF != null && EdgeFilterF(eid) == false)
                {
                    used_edge[eid] = true;
                    continue;
                }

                // ok this is start of a boundary chain
                int eStart = eid;
                used_edge[eStart] = true;
                loop_edges.Add(eStart);

                int eCur = eid;

                // follow the chain in order of oriented edges
                bool bClosed     = false;
                bool bIsOpenSpan = false;
                while (!bClosed)
                {
                    Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur);
                    int     cure_a = ev.a, cure_b = ev.b;
                    if (bIsOpenSpan)
                    {
                        cure_a = ev.b; cure_b = ev.a;
                    }
                    else
                    {
                        loop_verts.Add(cure_a);
                    }

                    int e0 = -1, e1 = 1;
                    int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1);

                    // have to filter this list, if we are filtering. this is ugly.
                    if (EdgeFilterF != null)
                    {
                        if (bdry_nbrs > 2)
                        {
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[bdry_nbrs];
                            }
                            // we may repreat this below...irritating...
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be);
                        }
                        else
                        {
                            if (EdgeFilterF(e0) == false)
                            {
                                bdry_nbrs--;
                            }
                            if (EdgeFilterF(e1) == false)
                            {
                                bdry_nbrs--;
                            }
                        }
                    }


                    if (bdry_nbrs < 2)     // hit an 'endpoint' vertex (should only happen when Filter is on...)
                    {
                        if (SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b)
                                  {
                                      UnclosedLoop = true
                                  }
                        }
                        ;
                        if (bIsOpenSpan)
                        {
                            bClosed = true;
                            continue;
                        }
                        else
                        {
                            bIsOpenSpan = true;          // begin open span
                            eCur        = loop_edges[0]; // restart at other end of loop
                            loop_edges.Reverse();        // do this so we can push to front
                            continue;
                        }
                    }

                    int eNext = -1;

                    if (bdry_nbrs > 2)
                    {
                        // found "bowtie" vertex...things just got complicated!

                        if (cure_b == loop_verts[0])
                        {
                            // The "end" of the current edge is the same as the start vertex.
                            // This means we can close the loop here. Might as well!
                            eNext = -2;                               // sentinel value used below
                        }
                        else
                        {
                            // try to find an unused outgoing edge that is oriented properly.
                            // This could create sub-loops, we will handle those later
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[2 * bdry_nbrs];
                            }
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            Debug.Assert(num_be == bdry_nbrs);

                            if (EdgeFilterF != null)
                            {
                                num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be);
                            }

                            // Try to pick the best "turn left" vertex.
                            eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge);
                            if (eNext == -1)
                            {
                                if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                                {
                                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b)
                                          {
                                              BowtieFailure = true
                                          }
                                }
                                ;

                                // ok, we are stuck. all we can do now is terminate this loop and keep it as a span
                                if (bIsOpenSpan)
                                {
                                    bClosed = true;
                                }
                                else
                                {
                                    bIsOpenSpan = true;
                                    bClosed     = true;
                                }
                                continue;
                            }
                        }

                        if (bowties.Contains(cure_b) == false)
                        {
                            bowties.Add(cure_b);
                        }
                    }
                    else
                    {
                        // walk forward to next available edge
                        Debug.Assert(e0 == eCur || e1 == eCur);
                        eNext = (e0 == eCur) ? e1 : e0;
                    }

                    if (eNext == -2)
                    {
                        // found a bowtie vert that is the same as start-of-loop, so we
                        // are just closing it off explicitly
                        bClosed = true;
                    }
                    else if (eNext == eStart)
                    {
                        // found edge at start of loop, so loop is done.
                        bClosed = true;
                    }
                    else if (used_edge[eNext] != false)
                    {
                        // disaster case - the next edge is already used, but it is not the start of our loop
                        // All we can do is convert to open span and terminate
                        if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext)
                                  {
                                      RepeatedEdge = true
                                  }
                        }
                        ;
                        bIsOpenSpan = true;
                        bClosed     = true;
                    }
                    else
                    {
                        // push onto accumulated list
                        Debug.Assert(used_edge[eNext] == false);
                        loop_edges.Add(eNext);
                        used_edge[eNext] = true;
                        eCur             = eNext;
                    }
                }

                if (bIsOpenSpan)
                {
                    SawOpenSpans = true;
                    if (SpanBehavior == SpanBehaviors.Compute)
                    {
                        loop_edges.Reverse();  // orient properly
                        EdgeSpan span = EdgeSpan.FromEdges(Mesh, loop_edges);
                        Spans.Add(span);
                    }
                }
                else if (bowties.Count > 0)
                {
                    // if we saw a bowtie vertex, we might need to break up this loop,
                    // so call extract_subloops
                    List <EdgeLoop> subloops = extract_subloops(loop_verts, loop_edges, bowties);
                    for (int i = 0; i < subloops.Count; ++i)
                    {
                        Loops.Add(subloops[i]);
                    }
                }
                else
                {
                    // clean simple loop, convert to EdgeLoop instance
                    EdgeLoop loop = new EdgeLoop(Mesh);
                    loop.Vertices = loop_verts.ToArray();
                    loop.Edges    = loop_edges.ToArray();
                    Loops.Add(loop);
                }

                // reset these lists
                loop_edges.Clear();
                loop_verts.Clear();
                bowties.Clear();
            }

            return(true);
        }

        // [TODO] cache this in a dictionary? we will not need very many, but we will
        //   need each multiple times!
        Vector3d get_vtx_normal(int vid)
        {
            Vector3d n = Vector3d.Zero;

            foreach (int ti in Mesh.VtxTrianglesItr(vid))
            {
                n += Mesh.GetTriNormal(ti);
            }
            n.Normalize();
            return(n);
        }

        // ok, bdry_edges[0...bdry_edges_count] contains the boundary edges coming out of bowtie_v.
        // We want to pick the best one to continue the loop that came in to bowtie_v on incoming_e.
        // If the loops are all sane, then we will get the smallest loops by "turning left" at bowtie_v.
        // So, we compute the tangent plane at bowtie_v, and then the signed angle for each
        // viable edge in this plane.
        //
        // [TODO] handle degenerate edges. what do we do then? Currently will only chose
        //  degenerate edge if there are no other options (I think...)
        int find_left_turn_edge(int incoming_e, int bowtie_v, int[] bdry_edges, int bdry_edges_count, BitArray used_edges)
        {
            // compute normal and edge [a,bowtie]
            Vector3d n       = get_vtx_normal(bowtie_v);
            int      other_v = Mesh.edge_other_v(incoming_e, bowtie_v);
            Vector3d ab      = Mesh.GetVertex(bowtie_v) - Mesh.GetVertex(other_v);

            // our winner
            int    best_e     = -1;
            double best_angle = double.MaxValue;

            for (int i = 0; i < bdry_edges_count; ++i)
            {
                int bdry_eid = bdry_edges[i];
                if (used_edges[bdry_eid] == true)
                {
                    continue;       // this edge is already used
                }
                Index2i bdry_ev = Mesh.GetOrientedBoundaryEdgeV(bdry_eid);
                if (bdry_ev.a != bowtie_v)
                {
                    continue;       // have to be able to chain to end of current edge, orientation-wise
                }
                // compute projected angle
                Vector3d bc      = Mesh.GetVertex(bdry_ev.b) - Mesh.GetVertex(bowtie_v);
                float    fAngleS = MathUtil.PlaneAngleSignedD((Vector3f)ab, (Vector3f)bc, (Vector3f)n);

                // turn left!
                if (best_angle == double.MaxValue || fAngleS < best_angle)
                {
                    best_angle = fAngleS;
                    best_e     = bdry_eid;
                }
            }

            // [RMS] w/ bowtie vertices and open spans, this does happen
            //Debug.Assert(best_e != -1);

            return(best_e);
        }

        // This is called when loopV contains one or more "bowtie" vertices.
        // These vertices *might* be duplicated in loopV (but not necessarily)
        // If they are, we have to break loopV into subloops that don't contain duplicates.
        //
        // The list bowties contains all the possible duplicates
        // (all v in bowties occur in loopV at least once)
        //
        // Currently loopE is not used, and the returned EdgeLoop objects do not have their Edges
        // arrays initialized. Perhaps to improve in future.
        List <EdgeLoop> extract_subloops(List <int> loopV, List <int> loopE, List <int> bowties)
        {
            List <EdgeLoop> subs = new List <EdgeLoop>();

            // figure out which bowties we saw are actually duplicated in loopV
            List <int> dupes = new List <int>();

            foreach (int bv in bowties)
            {
                if (count_in_list(loopV, bv) > 1)
                {
                    dupes.Add(bv);
                }
            }

            // we might not actually have any duplicates, if we got luck. Early out in that case
            if (dupes.Count == 0)
            {
                subs.Add(new EdgeLoop(Mesh)
                {
                    Vertices = loopV.ToArray(), Edges = loopE.ToArray(), BowtieVertices = bowties.ToArray()
                });
                return(subs);
            }

            // This loop extracts subloops until we have dealt with all the
            // duplicate vertices in loopV
            while (dupes.Count > 0)
            {
                // Find shortest "simple" loop, ie a loop from a bowtie to itself that
                // does not contain any other bowties. This is an independent loop.
                // We're doing a lot of extra work here if we only have one element in dupes...
                int bi = 0, bv = 0;
                int start_i = -1, end_i = -1;
                int bv_shortest = -1; int shortest = int.MaxValue;
                for ( ; bi < dupes.Count; ++bi)
                {
                    bv = dupes[bi];
                    if (is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i))
                    {
                        int len = count_span(loopV, start_i, end_i);
                        if (len < shortest)
                        {
                            bv_shortest = bv;
                            shortest    = len;
                        }
                    }
                }
                if (bv_shortest == -1)
                {
                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: Cannot find a valid simple loop");
                }
                if (bv != bv_shortest)
                {
                    bv = bv_shortest;
                    // running again just to get start_i and end_i...
                    is_simple_bowtie_loop(loopV, dupes, bv, out start_i, out end_i);
                }

                Debug.Assert(loopV[start_i] == bv && loopV[end_i] == bv);

                EdgeLoop loop = new EdgeLoop(Mesh);
                loop.Vertices       = extract_span(loopV, start_i, end_i, true);
                loop.Edges          = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices);
                loop.BowtieVertices = bowties.ToArray();
                subs.Add(loop);

                // If there are no more duplicates of this bowtie, we can treat
                // it like a regular vertex now
                if (count_in_list(loopV, bv) < 2)
                {
                    dupes.Remove(bv);
                }
            }

            // Should have one loop left that contains duplicates.
            // Extract this as a separate loop
            int nLeft = 0;

            for (int i = 0; i < loopV.Count; ++i)
            {
                if (loopV[i] != -1)
                {
                    nLeft++;
                }
            }
            if (nLeft > 0)
            {
                EdgeLoop loop = new EdgeLoop(Mesh);
                loop.Vertices = new int[nLeft];
                int vi = 0;
                for (int i = 0; i < loopV.Count; ++i)
                {
                    if (loopV[i] != -1)
                    {
                        loop.Vertices[vi++] = loopV[i];
                    }
                }
                loop.Edges          = EdgeLoop.VertexLoopToEdgeLoop(Mesh, loop.Vertices);
                loop.BowtieVertices = bowties.ToArray();
                subs.Add(loop);
            }

            return(subs);
        }

        /*
         * In all the functions below, the list loopV is assumed to possibly
         * contain "removed" vertices indicated by -1. These are ignored.
         */


        // Check if the loop from bowtieV to bowtieV inside loopV contains any other bowtie verts.
        // Also returns start and end indices in loopV of "clean" loop
        // Note that start may be < end, if the "clean" loop wraps around the end
        bool is_simple_bowtie_loop(List <int> loopV, List <int> bowties, int bowtieV, out int start_i, out int end_i)
        {
            // find two indices of bowtie vert
            start_i = find_index(loopV, 0, bowtieV);
            end_i   = find_index(loopV, start_i + 1, bowtieV);

            if (is_simple_path(loopV, bowties, bowtieV, start_i, end_i))
            {
                return(true);
            }
            else if (is_simple_path(loopV, bowties, bowtieV, end_i, start_i))
            {
                int tmp = start_i; start_i = end_i; end_i = tmp;
                return(true);
            }
            else
            {
                return(false);       // not a simple bowtie loop!
            }
        }

        // check if forward path from loopV[i1] to loopV[i2] contains any bowtie verts other than bowtieV
        bool is_simple_path(List <int> loopV, List <int> bowties, int bowtieV, int i1, int i2)
        {
            int N = loopV.Count;

            for (int i = i1; i != i2; i = (i + 1) % N)
            {
                int vi = loopV[i];
                if (vi == -1)
                {
                    continue;       // skip removed vertices
                }
                if (vi != bowtieV && bowties.Contains(vi))
                {
                    return(false);
                }
            }
            return(true);
        }

        // Read out the span from loop[i0] to loop [i1-1] into an array.
        // If bMarkInvalid, then these values are set to -1 in loop
        int[] extract_span(List <int> loop, int i0, int i1, bool bMarkInvalid)
        {
            int num = count_span(loop, i0, i1);

            int[] a  = new int[num];
            int   ai = 0;
            int   N  = loop.Count;

            for (int i = i0; i != i1; i = (i + 1) % N)
            {
                if (loop[i] != -1)
                {
                    a[ai++] = loop[i];
                    if (bMarkInvalid)
                    {
                        loop[i] = -1;
                    }
                }
            }
            return(a);
        }

        // count number of valid vertices in l between loop[i0] and loop[i1-1]
        int count_span(List <int> l, int i0, int i1)
        {
            int c = 0;
            int N = l.Count;

            for (int i = i0; i != i1; i = (i + 1) % N)
            {
                if (l[i] != -1)
                {
                    c++;
                }
            }
            return(c);
        }

        // find the index of item in loop, starting at start index
        int find_index(List <int> loop, int start, int item)
        {
            for (int i = start; i < loop.Count; ++i)
            {
                if (loop[i] == item)
                {
                    return(i);
                }
            }
            return(-1);
        }

        // count number of times item appears in loop
        int count_in_list(List <int> loop, int item)
        {
            int c = 0;

            for (int i = 0; i < loop.Count; ++i)
            {
                if (loop[i] == item)
                {
                    c++;
                }
            }
            return(c);
        }
    }
}
        /// <summary>
        /// Find the set of boundary EdgeLoops. Note that if we encounter topological
        /// issues, we will throw MeshBoundaryLoopsException w/ more info (if possible)
        /// </summary>
        public bool Compute()
        {
            // This algorithm assumes that triangles are oriented consistently,
            // so closed boundary-loop can be followed by walking edges in-order

            Loops = new List <EdgeLoop>();
            Spans = new List <EdgeSpan>();

            // early-out if we don't actually have boundaries
            if (Mesh.CachedIsClosed)
            {
                return(true);
            }

            int NE = Mesh.MaxEdgeID;

            // Temporary memory used to indicate when we have "used" an edge.
            var used_edge = new BitArray(NE);

            used_edge.SetAll(false);

            // current loop is stored here, cleared after each loop extracted
            var loop_edges = new List <int>();                // [RMS] not sure we need this...
            var loop_verts = new List <int>();
            var bowties    = new List <int>();

            // Temp buffer for reading back all boundary edges of a vertex.
            // probably always small but in pathological cases it could be large...
            int[] all_e = new int[16];

            // [TODO] might make sense to precompute some things here, like num_be for each bdry vtx?

            // process all edges of mesh
            for (int eid = 0; eid < NE; ++eid)
            {
                if (!Mesh.IsEdge(eid))
                {
                    continue;
                }

                if (used_edge[eid] == true)
                {
                    continue;
                }

                if (Mesh.IsBoundaryEdge(eid) == false)
                {
                    continue;
                }

                if (EdgeFilterF != null && EdgeFilterF(eid) == false)
                {
                    used_edge[eid] = true;
                    continue;
                }

                // ok this is start of a boundary chain
                int eStart = eid;
                used_edge[eStart] = true;
                loop_edges.Add(eStart);

                int eCur = eid;

                // follow the chain in order of oriented edges
                bool bClosed     = false;
                bool bIsOpenSpan = false;
                while (!bClosed)
                {
                    Index2i ev = Mesh.GetOrientedBoundaryEdgeV(eCur);
                    int     cure_a = ev.a, cure_b = ev.b;
                    if (bIsOpenSpan)
                    {
                        cure_a = ev.b; cure_b = ev.a;
                    }
                    else
                    {
                        loop_verts.Add(cure_a);
                    }

                    int e0 = -1, e1 = 1;
                    int bdry_nbrs = Mesh.VtxBoundaryEdges(cure_b, ref e0, ref e1);

                    // have to filter this list, if we are filtering. this is ugly.
                    if (EdgeFilterF != null)
                    {
                        if (bdry_nbrs > 2)
                        {
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[bdry_nbrs];
                            }
                            // we may repreat this below...irritating...
                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            num_be = BufferUtil.CountValid(all_e, EdgeFilterF, num_be);
                        }
                        else
                        {
                            if (EdgeFilterF(e0) == false)
                            {
                                bdry_nbrs--;
                            }

                            if (EdgeFilterF(e1) == false)
                            {
                                bdry_nbrs--;
                            }
                        }
                    }


                    if (bdry_nbrs < 2)
                    {                       // hit an 'endpoint' vertex (should only happen when Filter is on...)
                        if (SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: found open span at vertex " + cure_b)
                                  {
                                      UnclosedLoop = true
                                  };
                        }

                        if (bIsOpenSpan)
                        {
                            bClosed = true;
                            continue;
                        }
                        else
                        {
                            bIsOpenSpan = true;                                // begin open span
                            eCur        = loop_edges[0];                       // restart at other end of loop
                            loop_edges.Reverse();                              // do this so we can push to front
                            continue;
                        }
                    }

                    int eNext = -1;

                    if (bdry_nbrs > 2)
                    {
                        // found "bowtie" vertex...things just got complicated!

                        if (cure_b == loop_verts[0])
                        {
                            // The "end" of the current edge is the same as the start vertex.
                            // This means we can close the loop here. Might as well!
                            eNext = -2;                               // sentinel value used below
                        }
                        else
                        {
                            // try to find an unused outgoing edge that is oriented properly.
                            // This could create sub-loops, we will handle those later
                            if (bdry_nbrs >= all_e.Length)
                            {
                                all_e = new int[2 * bdry_nbrs];
                            }

                            int num_be = Mesh.VtxAllBoundaryEdges(cure_b, all_e);
                            Debug.Assert(num_be == bdry_nbrs);

                            if (EdgeFilterF != null)
                            {
                                num_be = BufferUtil.FilterInPlace(all_e, EdgeFilterF, num_be);
                            }

                            // Try to pick the best "turn left" vertex.
                            eNext = find_left_turn_edge(eCur, cure_b, all_e, num_be, used_edge);
                            if (eNext == -1)
                            {
                                if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                                {
                                    throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: cannot find valid outgoing edge at bowtie vertex " + cure_b)
                                          {
                                              BowtieFailure = true
                                          };
                                }

                                // ok, we are stuck. all we can do now is terminate this loop and keep it as a span
                                if (bIsOpenSpan)
                                {
                                    bClosed = true;
                                }
                                else
                                {
                                    bIsOpenSpan = true;
                                    bClosed     = true;
                                }
                                continue;
                            }
                        }

                        if (bowties.Contains(cure_b) == false)
                        {
                            bowties.Add(cure_b);
                        }
                    }
                    else
                    {
                        // walk forward to next available edge
                        Debug.Assert(e0 == eCur || e1 == eCur);
                        eNext = (e0 == eCur) ? e1 : e0;
                    }

                    if (eNext == -2)
                    {
                        // found a bowtie vert that is the same as start-of-loop, so we
                        // are just closing it off explicitly
                        bClosed = true;
                    }
                    else if (eNext == eStart)
                    {
                        // found edge at start of loop, so loop is done.
                        bClosed = true;
                    }
                    else if (used_edge[eNext] != false)
                    {
                        // disaster case - the next edge is already used, but it is not the start of our loop
                        // All we can do is convert to open span and terminate
                        if (FailureBehavior == FailureBehaviors.ThrowException || SpanBehavior == SpanBehaviors.ThrowException)
                        {
                            throw new MeshBoundaryLoopsException("MeshBoundaryLoops.Compute: encountered repeated edge " + eNext)
                                  {
                                      RepeatedEdge = true
                                  };
                        }

                        bIsOpenSpan = true;
                        bClosed     = true;
                    }
                    else
                    {
                        // push onto accumulated list
                        Debug.Assert(used_edge[eNext] == false);
                        loop_edges.Add(eNext);
                        used_edge[eNext] = true;
                        eCur             = eNext;
                    }
                }

                if (bIsOpenSpan)
                {
                    SawOpenSpans = true;
                    if (SpanBehavior == SpanBehaviors.Compute)
                    {
                        loop_edges.Reverse();                          // orient properly
                        var span = EdgeSpan.FromEdges(Mesh, loop_edges);
                        Spans.Add(span);
                    }
                }
                else if (bowties.Count > 0)
                {
                    // if we saw a bowtie vertex, we might need to break up this loop,
                    // so call extract_subloops
                    Subloops subloops = extract_subloops(loop_verts, loop_edges, bowties);
                    foreach (var loop in subloops.Loops)
                    {
                        Loops.Add(loop);
                    }

                    if (subloops.Spans.Count > 0)
                    {
                        FellBackToSpansOnFailure = true;
                        foreach (var span in subloops.Spans)
                        {
                            Spans.Add(span);
                        }
                    }
                }
                else
                {
                    // clean simple loop, convert to EdgeLoop instance
                    var loop = new EdgeLoop(Mesh);
                    loop.Vertices = loop_verts.ToArray();
                    loop.Edges    = loop_edges.ToArray();
                    Loops.Add(loop);
                }

                // reset these lists
                loop_edges.Clear();
                loop_verts.Clear();
                bowties.Clear();
            }

            return(true);
        }
        /// <summary>
        /// Similar to the static <see cref="Build()"/> method below, but uses the
        /// <see cref="NonManifoldTriBehavior"/> and <see cref="AddTriangleFailBehaviors"/> properties
        /// to affect the meshing process and avoids exceptions, preferring feedback in the mesh metadata.
        /// </summary>
        public DMesh3 AppendMesh <VType, TType, NType>(IEnumerable <VType> Vertices,
                                                       IEnumerable <TType> Triangles,
                                                       IEnumerable <NType> Normals = null,
                                                       IEnumerable <int> TriGroups = null)
        {
            // build outcomes are stored in the metadata to keep the function signature like the static method Build
            //
            int iAppendTriangleIssues = 0;

            bool   addNormals      = Normals != null;
            string NormalsMetadata = "None";

            bool   addTriGroups      = TriGroups != null;
            string TriGroupsMetadata = "None";

            // data preparation
            Vector3d[] v = BufferUtil.ToVector3d(Vertices);
            Vector3f[] n = null;
            if (addNormals)
            {
                n = BufferUtil.ToVector3f(Normals);
                if (n.Length != v.Length)
                {
                    NormalsMetadata = "Error: incorrect number of normals provided, ignored.";
                    addNormals      = false;
                }
            }
            Index3i[] t = BufferUtil.ToIndex3i(Triangles);

            List <int> groups = null;

            if (addTriGroups)
            {
                groups = new List <int>(TriGroups);
                if (groups.Count != t.Length)
                {
                    TriGroupsMetadata = "Error: incorrect number of groups provided, ignored.";
                    addTriGroups      = false;
                }
            }

            DMesh3 mesh = new DMesh3(addNormals, false, false, addTriGroups);

            AppendNewMesh(mesh);

            // vertices
            for (int i = 0; i < v.Length; ++i)
            {
                mesh.AppendVertex(v[i]);
            }

            // normals
            if (addNormals)
            {
                for (int i = 0; i < n.Length; ++i)
                {
                    mesh.SetVertexNormal(i, n[i]);
                }
                NormalsMetadata = "Ok";
            }

            // triangles
            for (int i = 0; i < t.Length; ++i)
            {
                var last = AppendTriangle(t[i]);
                if (last == DMesh3.InvalidID || last == DMesh3.NonManifoldID)
                {
                    iAppendTriangleIssues++;
                }
            }

            // groups
            if (addTriGroups)
            {
                for (int i = 0; i < t.Length; ++i)
                {
                    mesh.SetTriangleGroup(i, groups[i]);
                }
                TriGroupsMetadata = "Ok";
            }

            // adding the metadata
            //
            mesh.AttachMetadata("AppendTriangleIssues", iAppendTriangleIssues);
            mesh.AttachMetadata("Normals", NormalsMetadata);
            mesh.AttachMetadata("TriGroups", TriGroupsMetadata);

            return(mesh);
        }
        public bool Solve()
        {
            int size = B.Length;

            // Based on the algorithm in "Matrix Computations" by Golum and Van Loan.
            R = new double[size];
            P = new double[size];
            W = new double[size];

            if (X == null || UseXAsInitialGuess == false)
            {
                if (X == null)
                {
                    X = new double[size];
                }
                Array.Clear(X, 0, X.Length);
                Array.Copy(B, R, B.Length);
            }
            else
            {
                // hopefully is X is a decent initialization...
                InitializeR(R);
            }

            // The first iteration.
            double rho0 = BufferUtil.Dot(R, R);

            Array.Copy(R, P, R.Length);

            MultiplyF(P, W);

            double alpha = rho0 / BufferUtil.Dot(P, W);

            BufferUtil.MultiplyAdd(X, alpha, P);
            BufferUtil.MultiplyAdd(R, -alpha, W);
            double rho1 = BufferUtil.Dot(R, R);

            // [RMS] these were inside loop but they are constant!
            double norm  = BufferUtil.Dot(B, B);
            double root1 = Math.Sqrt(norm);

            // The remaining iterations.
            int iter;

            for (iter = 1; iter < MaxIterations; ++iter)
            {
                double root0 = Math.Sqrt(rho1);
                if (root0 <= MathUtil.ZeroTolerance * root1)
                {
                    break;
                }

                double beta = rho1 / rho0;
                UpdateP(P, beta, R);

                MultiplyF(P, W);

                alpha = rho1 / BufferUtil.Dot(P, W);
                BufferUtil.MultiplyAdd(X, alpha, P);
                BufferUtil.MultiplyAdd(R, -alpha, W);
                rho0 = rho1;
                rho1 = BufferUtil.Dot(R, R);
            }


            System.Console.WriteLine("{0} iterations", iter);
            return(iter < MaxIterations);
        }