public void WriteBoundaryNodes(string path, IReadOnlyList <XSubdomain2D_old> subdomains)
        {
            var boundaryNodes = new Dictionary <INode, double>();

            foreach (var subdomain in subdomains)
            {
                foreach (var node in subdomain.BoundaryNodes)
                {
                    boundaryNodes[node] = 0.0;
                }
            }
            using (var writer = new VtkPointWriter(path))
            {
                writer.WriteScalarField("boundary", boundaryNodes);
            }
        }
        public void Log()
        {
            // Log the new tip enriched nodes and the signs of their crack body level sets.
            using (var writer = new VtkPointWriter($"{outputDirectory}\\tip_nodes_new_{iteration}.vtk"))
            {
                var tipNodesNew = new Dictionary <CartesianPoint, double>();
                foreach (var node in lsm.CrackTipNodesNew[lsm.CrackTipEnrichments])
                {
                    double sign = Math.Sign(lsm.LevelSetsTip[node]);
                    tipNodesNew.Add(node, sign);
                }
                writer.WriteScalarField("Tip_nodes_new", tipNodesNew);
            }

            // Log the old tip enriched nodes and the signs of their crack body level sets.
            using (var writer = new VtkPointWriter($"{outputDirectory}\\tip_nodes_old_{iteration}.vtk"))
            {
                var tipNodesOld = new Dictionary <CartesianPoint, double>();
                if (iteration > 0)
                {
                    foreach (var node in lsm.CrackTipNodesOld[lsm.CrackTipEnrichments])
                    {
                        double sign = Math.Sign(lsm.LevelSetsTip[node]);
                        tipNodesOld.Add(node, sign);
                    }
                }
                else // else a phony node just to get the Paraview reader working. TODO: find a more elegant solution.
                {
                    tipNodesOld.Add(new CartesianPoint(-0.01, -0.01), 0);
                }
                writer.WriteScalarField("Tip_nodes_old", tipNodesOld);
            }

            // Log all Heaviside enriched nodes and the signs of their crack body level sets.
            using (var writer = new VtkPointWriter($"{outputDirectory}\\heaviside_nodes_all_{iteration}.vtk"))
            {
                var heavisideNodesAll = new Dictionary <CartesianPoint, double>();
                foreach (var node in lsm.CrackBodyNodesAll[lsm.CrackBodyEnrichment])
                {
                    double sign = Math.Sign(lsm.LevelSetsBody[node]);
                    heavisideNodesAll.Add(node, sign);
                }
                writer.WriteScalarField("Heaviside_nodes_all", heavisideNodesAll);
            }

            // Log only the new Heaviside enriched nodes and the signs of their crack body level sets.
            using (var writer = new VtkPointWriter($"{outputDirectory}\\heaviside_nodes_new_{iteration}.vtk"))
            {
                var heavisideNodesNew = new Dictionary <CartesianPoint, double>();
                foreach (var node in lsm.CrackBodyNodesNew[lsm.CrackBodyEnrichment])
                {
                    double sign = Math.Sign(lsm.LevelSetsBody[node]);
                    heavisideNodesNew.Add(node, sign);
                }
                writer.WriteScalarField("Heaviside_nodes_new", heavisideNodesNew);
            }

            // Log the nodes that belong to elements intersected by the crack, but are not enriched with Heaviside
            using (var writer = new VtkPointWriter($"{outputDirectory}\\heaviside_rejected_nodes_{iteration}.vtk"))
            {
                var rejectedNodes = new Dictionary <CartesianPoint, double>();
                foreach (var node in lsm.CrackBodyNodesRejected[lsm.CrackBodyEnrichment])
                {
                    double sign = Math.Sign(lsm.LevelSetsBody[node]);
                    rejectedNodes.Add(node, sign);
                }
                if (rejectedNodes.Count == 0) //a phony node just to get the Paraview reader working. TODO: find a more elegant solution.
                {
                    rejectedNodes.Add(new CartesianPoint(-0.01, -0.01), 0);
                }
                writer.WriteScalarField("Heaviside_rejected_nodes", rejectedNodes);
            }


            // Log unmodified Heaviside nodes of elements with at least one modified node
            using (var writer = new VtkPointWriter($"{outputDirectory}\\near_modified_heaviside_nodes_{iteration}.vtk"))
            {
                var nearModifiedHeavisideNodes = new Dictionary <CartesianPoint, double>();
                foreach (var node in lsm.CrackBodyNodesNearModified[lsm.CrackBodyEnrichment])
                {
                    double sign = Math.Sign(lsm.LevelSetsBody[node]);
                    nearModifiedHeavisideNodes.Add(node, sign);
                }
                if (nearModifiedHeavisideNodes.Count == 0) // a phony node just to get the Paraview reader working. TODO: find a more elegant solution.
                {
                    nearModifiedHeavisideNodes.Add(new CartesianPoint(-0.01, -0.01), 0);
                }
                writer.WriteScalarField("near_modified_heaviside_nodes", nearModifiedHeavisideNodes);
            }

            ++iteration;
        }