protected override double[] FindRoots(LinearPSI <Cube> psi, MultidimensionalArray X, int heightDirection, double[] bounds, int cell) { MultidimensionalArray XonPsi = psi.ProjectOnto(X); XonPsi = XonPsi.ExtractSubArrayShallow(0, -1); double[] start = XonPsi.To1DArray(); double[] end = XonPsi.To1DArray(); start[heightDirection] = -1; end[heightDirection] = 1; HMF.LineSegment line = new HMF.LineSegment(3, RefElement, start, end); LevelSet levelSet = lsData.LevelSet as LevelSet; line.ProjectBasisPolynomials(levelSet.Basis); double[] roots = rootFinder.GetRoots(line, levelSet, cell, this.iKref); return(roots); }
/// <summary> /// Gathers the line segments bounding <see cref="RefElement"/> in /// reference coordinates. /// </summary> /// <returns> /// An array of line segments /// <list type="bullet"> /// <item>1st index: Edge index</item> /// <item>2nd index: Edge of edge index</item> /// </list> /// </returns> /// <remarks> /// Each line segment will appear twice in the result, since it is /// stored for each edge separately. However, this method ensures that /// corresponding segments are represented by the same object (reference /// equality) which ensures that this does not affect performance. /// </remarks> private LineSegment[,] GetReferenceLineSegments() { int D = lsData.GridDat.SpatialDimension; int noOfEdges = lsData.GridDat.Grid.RefElements[0].NoOfFaces; int noOfEdgesOfEdge = RefElement.FaceRefElement.NoOfFaces; MultidimensionalArray edgeOfEdgeVertices = MultidimensionalArray.Create(2, 1); edgeOfEdgeVertices[0, 0] = -1.0; edgeOfEdgeVertices[1, 0] = 1.0; MultidimensionalArray edgeVertices = MultidimensionalArray.Create( edgeOfEdgeVertices.GetLength(0), RefElement.FaceRefElement.SpatialDimension); MultidimensionalArray volumeVertices = MultidimensionalArray.Create( edgeVertices.GetLength(0), D); // Remember encountered segments so that $lineSegments contains no // duplicates and roots can be cached efficiently Dictionary <LineSegment, LineSegment> seenSegments = new Dictionary <LineSegment, LineSegment>( noOfEdges * noOfEdgesOfEdge / 2); LineSegment[,] lineSegments = new LineSegment[noOfEdges, noOfEdgesOfEdge]; LevelSet levelSetField = lsData.LevelSet as LevelSet; for (int ee = 0; ee < noOfEdgesOfEdge; ee++) { RefElement.FaceRefElement.TransformFaceCoordinates(ee, edgeOfEdgeVertices, edgeVertices); for (int e = 0; e < noOfEdges; e++) { lsData.GridDat.Grid.RefElements[0].TransformFaceCoordinates( e, edgeVertices, volumeVertices); //double[] start = new double[D]; //double[] end = new double[D]; //for (int d = 0; d < D; d++) { // start[d] = volumeVertices[0, d]; // end[d] = volumeVertices[1, d]; //} var start = volumeVertices.GetRowPt(0); var end = volumeVertices.GetRowPt(1); LineSegment newSegment = new LineSegment(D, this.RefElement, start, end, rootFindingAlgorithm: RootFindingAlgorithm); // Assert that the segment does not already exist LineSegment segment; if (!seenSegments.TryGetValue(newSegment, out segment)) { segment = newSegment; seenSegments.Add(segment, segment); if (levelSetField != null) { segment.ProjectBasisPolynomials(levelSetField.Basis); } //tracker.Subscribe(segment); } lineSegments[e, ee] = segment; } } return(lineSegments); }
/// <summary> /// Returns a set of <see cref="CellEdgeBoundaryQuadRule"/>s that /// enables the integration over sub-segments of the edges of the edges /// of a (three-dimensional) domain. This is obviously only useful if /// the integrand has a discontinuity that is aligned with the zero /// iso-contour of the level set function. /// </summary> /// <param name="mask"></param> /// <param name="order"></param> /// <returns></returns> public IEnumerable <IChunkRulePair <CellEdgeBoundaryQuadRule> > GetQuadRuleSet(ExecutionMask mask, int order) { if (mask == null) { mask = CellMask.GetFullMask(this.lsData.GridDat, MaskType.Geometrical); } if (mask is CellMask == false) { throw new ArgumentException("Edge mask required", "mask"); } if (mask.MaskType != MaskType.Geometrical) { throw new ArgumentException("Expecting a geometrical mask."); } if (lastOrder != order) { cache.Clear(); } QuadRule baseRule = lineSimplex.GetQuadratureRule(order); int D = lsData.GridDat.SpatialDimension; int noOfEdges = lsData.GridDat.Grid.RefElements[0].NoOfFaces; int noOfEdgesOfEdge = RefElement.FaceRefElement.NoOfFaces; var result = new List <ChunkRulePair <CellEdgeBoundaryQuadRule> >(mask.NoOfItemsLocally); foreach (Chunk chunk in mask) { for (int i = 0; i < chunk.Len; i++) { int cell = i + chunk.i0; if (cache.ContainsKey(cell)) { result.Add(new ChunkRulePair <CellEdgeBoundaryQuadRule>( Chunk.GetSingleElementChunk(cell), cache[cell])); continue; } List <Vector> nodes = new List <Vector>(); List <double> weights = new List <double>(); if (lsData.GridDat.Cells.Cells2Edges[cell].Length != noOfEdges) { throw new NotImplementedException("Not implemented for hanging nodes"); } int[] noOfNodesPerEdge = new int[noOfEdges]; int[,] noOfNodesPerEdgeOfEdge = new int[noOfEdges, noOfEdgesOfEdge]; for (int e = 0; e < noOfEdges; e++) { int edge = Math.Abs(lsData.GridDat.Cells.Cells2Edges[cell][e]) - 1; double edgeDet = lsData.GridDat.Edges.SqrtGramian[edge]; for (int ee = 0; ee < noOfEdgesOfEdge; ee++) { LineSegment refSegment = referenceLineSegments[e, ee]; double edgeOfEdgeDet = RefElement.FaceRefElement.FaceTrafoGramianSqrt[ee]; double[] roots = refSegment.GetRoots(lsData.LevelSet, cell, 0); LineSegment[] subSegments = refSegment.Split(roots); for (int k = 0; k < subSegments.Length; k++) { // Evaluate sub segment at center to determine sign NodeSet _point = new NodeSet(this.RefElement, subSegments[k].GetPointOnSegment(0.0)); double scaling = edgeOfEdgeDet * subSegments[k].Length / refSegment.Length; if (jumpType != JumpTypes.Implicit) { //using (tracker.GridDat.NSC.CreateLock( // MultidimensionalArray.CreateWrapper(point, 1, D), 0, -1.0)) { MultidimensionalArray levelSetValue = lsData.GetLevSetValues(_point, cell, 1); switch (jumpType) { case JumpTypes.Heaviside: if (levelSetValue[0, 0] <= 0.0) { continue; } break; case JumpTypes.OneMinusHeaviside: if (levelSetValue[0, 0] > 0.0) { continue; } break; case JumpTypes.Sign: scaling *= levelSetValue[0, 0].Sign(); break; default: throw new NotImplementedException(); } } for (int m = 0; m < baseRule.NoOfNodes; m++) { // Base rule _always_ is a line rule, thus Nodes[*, _0_] var point = subSegments[k].GetPointOnSegment(baseRule.Nodes[m, 0]); weights.Add(baseRule.Weights[m] * scaling); nodes.Add(point); noOfNodesPerEdge[e]++; noOfNodesPerEdgeOfEdge[e, ee]++; } } } } if (weights.Count == 0) { CellEdgeBoundaryQuadRule emptyRule = CellEdgeBoundaryQuadRule.CreateEmpty(1, RefElement); emptyRule.Nodes.LockForever(); cache.Add(cell, emptyRule); result.Add(new ChunkRulePair <CellEdgeBoundaryQuadRule>( Chunk.GetSingleElementChunk(cell), emptyRule)); continue; } NodeSet localNodes = new NodeSet(this.RefElement, nodes.Count, D); for (int j = 0; j < nodes.Count; j++) { for (int d = 0; d < D; d++) { localNodes[j, d] = nodes[j][d]; } } localNodes.LockForever(); CellEdgeBoundaryQuadRule subdividedRule = new CellEdgeBoundaryQuadRule() { OrderOfPrecision = order, Weights = MultidimensionalArray.Create(weights.Count), Nodes = localNodes, NumbersOfNodesPerFace = noOfNodesPerEdge, NumbersOfNodesPerFaceOfFace = noOfNodesPerEdgeOfEdge }; subdividedRule.Weights.SetSubVector(weights, -1); cache.Add(cell, subdividedRule); result.Add(new ChunkRulePair <CellEdgeBoundaryQuadRule>( Chunk.GetSingleElementChunk(cell), subdividedRule)); } } return(result); }
/// <summary> /// Uses a safe-guarded Newton method to find all roots on /// <paramref name="segment"/> /// </summary> /// <param name="segment"></param> /// <param name="levelSet"></param> /// <param name="cell"></param> /// <param name="iKref"></param> /// <returns></returns> public double[] GetRoots(LineSegment segment, ILevelSet levelSet, int cell, int iKref) { LevelSet levelSetField = levelSet as LevelSet; if (levelSetField == null) { throw new NotImplementedException("Method currently only works for polynomial level sets"); } int maxNoOfCoefficientsPerDimension = levelSetField.Basis.Degree + 1; int noOfPolynomials = segment.ProjectedPolynomialCoefficients.GetLength(1); double[] coefficients = new double[maxNoOfCoefficientsPerDimension]; for (int i = 0; i < noOfPolynomials; i++) { double dgCoefficient = levelSetField.Coordinates[cell, i]; for (int j = 0; j < maxNoOfCoefficientsPerDimension; j++) { coefficients[j] += dgCoefficient * segment.ProjectedPolynomialCoefficients[iKref, i, j]; } } //if(coefficients.L2NormPow2() < this.Tolerance) { // // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // // special case : // // the zero-level-set is probably parallel to this line segment // // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // return new double[] { -1.0, 1.0 }; //} double[] roots; unsafe { fixed(double *pCoeff = &coefficients[coefficients.Length - 1]) { double xLow = -1.0; double xHigh = 1.0; int NO_OF_BRACKETS = 8; List <double> lowerBounds = new List <double>(); List <double> upperBounds = new List <double>(); double dx2 = 2.0 / NO_OF_BRACKETS; double x = xLow; double fOld = Eval(x, pCoeff, coefficients.Length); for (int i = 0; i < NO_OF_BRACKETS; i++) { x += dx2; double fNew = Eval(x, pCoeff, coefficients.Length); if (i == 0 && fOld.Abs() < Tolerance) { lowerBounds.Add(x - dx2); upperBounds.Add(x); fOld = fNew; continue; } else { if (fNew.Abs() < Tolerance) { lowerBounds.Add(x - dx2); upperBounds.Add(x); x += dx2; fOld = Eval(x, pCoeff, coefficients.Length); continue; } } if (fNew * fOld <= 0.0) { lowerBounds.Add(x - dx2); upperBounds.Add(x); } fOld = fNew; } // Actual Newton-Raphson int MAX_ITERATIONS = 50; roots = new double[lowerBounds.Count]; for (int j = 0; j < lowerBounds.Count; j++) { xLow = lowerBounds[j]; xHigh = upperBounds[j]; double fLeft = Eval(xLow, pCoeff, coefficients.Length); double fRight = Eval(xHigh, pCoeff, coefficients.Length); if (fLeft.Abs() < Tolerance) { roots[j] = xLow; break; } if (fRight.Abs() < Tolerance) { roots[j] = xHigh; break; } if (fLeft.Sign() == fRight.Sign()) { throw new Exception(); } if (fLeft > 0.0) { xLow = upperBounds[j]; xHigh = lowerBounds[j]; } double root = 0.5 * (xLow + xHigh); double f; double df; Eval(root, pCoeff, coefficients.Length, out f, out df); double dxOld = (xHigh - xLow).Abs(); double dx = dxOld; int i = 0; while (true) { if (i > MAX_ITERATIONS) { throw new Exception("Max iterations exceeded"); } double a = ((root - xHigh) * df - f) * ((root - xLow) * df - f); if (a > 0.0 || 2.0 * Math.Abs(f) > Math.Abs(dxOld * df)) { // Newton out of range or too slow -> Bisect dxOld = dx; dx = 0.5 * (xHigh - xLow); root = xLow + dx; } else { // Take Newton step dxOld = dx; dx = -f / df; //// Convergence acceleration according to Yao2014 //double fDelta = Eval(root + dx, pCoeff, coefficients.Length); //dx = -(f + fDelta) / df; root += dx; } Eval(root, pCoeff, coefficients.Length, out f, out df); if (Math.Abs(f) <= Tolerance) { roots[j] = root; break; } if (f < 0.0) { xLow = root; } else { xHigh = root; } i++; } } } } return(roots); }
/// <summary> /// Finds the roots using <see cref="GSL.gsl_poly_complex_solve"/> /// </summary> /// <param name="segment"></param> /// <param name="levelSet"></param> /// <param name="cell"></param> /// <param name="iKref"></param> /// <returns></returns> public double[] GetRoots(LineSegment segment, ILevelSet levelSet, int cell, int iKref) { LevelSet levelSetField = levelSet as LevelSet; if (levelSetField == null) { throw new NotImplementedException("Method currently only works for polynomial level sets"); } int maxNoOfCoefficientsPerDimension = levelSetField.Basis.Degree + 1; int noOfPolynomials = segment.ProjectedPolynomialCoefficients.GetLength(1); double[] coefficients = new double[maxNoOfCoefficientsPerDimension]; for (int i = 0; i < noOfPolynomials; i++) { double dgCoefficient = levelSetField.Coordinates[cell, i]; for (int j = 0; j < maxNoOfCoefficientsPerDimension; j++) { coefficients[j] += dgCoefficient * segment.ProjectedPolynomialCoefficients[iKref, i, j]; } } // Make sure "leading" coefficient (i.e., the last element of the // list of coefficients) is not too small since this will make gsl // crash (or lead to bogus results) int newLength = coefficients.Length; while (newLength > 0 && Math.Abs(coefficients[newLength - 1]) < EPSILON) { newLength--; } if (newLength != coefficients.Length) { coefficients = coefficients.Take(newLength).ToArray(); } // Make sure polynomial is not constant since this will make gsl crash if (newLength < 2) { return(new double[0]); } int maxNoOfRoots = coefficients.Length - 1; double[] roots = new double[2 * maxNoOfRoots]; GCHandle coefficientHandle = GCHandle.Alloc(coefficients, GCHandleType.Pinned); GCHandle rootsHandle = GCHandle.Alloc(roots, GCHandleType.Pinned); IntPtr workSpace = GSL.gsl_poly_complex_workspace_alloc(coefficients.Length); GSL.gsl_poly_complex_solve( coefficientHandle.AddrOfPinnedObject(), coefficients.Length, workSpace, rootsHandle.AddrOfPinnedObject()); GSL.gsl_poly_complex_workspace_free(workSpace); coefficientHandle.Free(); rootsHandle.Free(); List <double> realRoots = new List <double>(levelSetField.Basis.Degree); for (int i = 0; i < maxNoOfRoots; i++) { double realPart = roots[2 * i]; double imaginaryPart = roots[2 * i + 1]; // Exclude |$realPart| == 1.0 since it doesn't matter if (imaginaryPart == 0.0 && Math.Abs(realPart) <= 1.0 - EPSILON) { realRoots.Add(realPart); } } return(realRoots.OrderBy(d => d).ToArray()); }