private IList <Vector> FEAnalysisHalfMbbBeam(Matrix x)
        {
            int numAllDofs = 2 * (nelx + 1) * (nely + 1);
            var K          = DokSymmetric.CreateEmpty(numAllDofs);
            var F          = Vector.CreateZero(numAllDofs);
            var U          = Vector.CreateZero(numAllDofs);

            Matrix Ke             = ElementStiffness();
            int    numElementDofs = Ke.NumRows;

            int[] elementDofsLocal = Enumerable.Range(0, numElementDofs).ToArray();

            // Global stiffness matrix assembly
            for (int ely = 1; ely <= nely; ++ely)
            {
                for (int elx = 1; elx <= nelx; ++elx)
                {
                    int[]  elementDofsGlobal = GetElementDofs(elx, ely);
                    double density           = Math.Pow(x[ely - 1, elx - 1], penal);
                    K.AddSubmatrixSymmetric(density * Ke, elementDofsLocal, elementDofsGlobal);
                }
            }

            // Define loads and supports for half MBB beam
            F[1] = -1.0;
            var fixedDofs = new HashSet <int>(); //TODO: Use LINQ to simplify this

            for (int i = 0; i < 2 * (nely + 1); i += 2)
            {
                fixedDofs.Add(i);
            }
            fixedDofs.Add(numAllDofs - 1);
            int[] freeDofs = Enumerable.Range(0, numAllDofs).Except(fixedDofs).ToArray();

            // Solve linear system
            DokSymmetric Kf = K.GetSubmatrix(freeDofs);
            Vector       Ff = F.GetSubvector(freeDofs);
            Vector       Uf = CholeskyCSparseNet.Factorize(Kf.BuildSymmetricCscMatrix(true)).SolveLinearSystem(Ff);

            for (int i = 0; i < freeDofs.Length; ++i)
            {
                U[freeDofs[i]] = Uf[i];
            }
            //U.CopyNonContiguouslyFrom(freeDofs, Uf, Enumerable.Range(0, freeDofs.Length).ToArray()); // alternative way, but probably slower.
            //foreach (int i in fixedDofs) U[i] = 0.0; // They are 0 by default

            return(new Vector[] { U });
        }
        public void UpdateModelAndAnalyze(IVectorView youngModuli)
        {
            this.youngModuli = youngModuli;

            // Global stiffness matrix assembly
            int numAllDofs = 2 * (numElementsX + 1) * (numElementsY + 1);
            var K          = DokSymmetric.CreateEmpty(numAllDofs);

            for (int e = 0; e < NumElements; ++e)
            {
                int[] elementDofsGlobal = GetElementDofs(e);
                K.AddSubmatrixSymmetric(unitStiffness.Scale(youngModuli[e]), elementDofsLocal, elementDofsGlobal);
            }

            // Apply boundary conditions
            (int[] freeDofs, Vector[] Fs) = ApplyBoundaryConditions();
            int numLoadCases = Fs.Length;

            globalDisplacements = new Vector[numLoadCases];

            // Solve linear system
            DokSymmetric Kf     = K.GetSubmatrix(freeDofs);
            var          factor = CholeskyCSparseNet.Factorize(Kf.BuildSymmetricCscMatrix(true));

            for (int c = 0; c < numLoadCases; ++c)
            {
                Vector Ff = Fs[c].GetSubvector(freeDofs);
                Vector Uf = factor.SolveLinearSystem(Ff);
                var    U  = Vector.CreateZero(numAllDofs);
                for (int i = 0; i < freeDofs.Length; ++i)
                {
                    U[freeDofs[i]] = Uf[i];
                }
                globalDisplacements[c] = U;
                //U.CopyNonContiguouslyFrom(freeDofs, Uf, Enumerable.Range(0, freeDofs.Length).ToArray()); // alternative way, but probably slower.
            }
        }