private double[] NormalizedGradientByFlux(SinglePhaseField field, int jCell, NodeSet nodeSet) { if (this.gradientX == null) { // Evaluate gradient gradientX = new SinglePhaseField(field.Basis, "gradientX"); gradientY = new SinglePhaseField(field.Basis, "gradientY"); gradientX.DerivativeByFlux(1.0, field, d: 0); gradientY.DerivativeByFlux(1.0, field, d: 1); if (this.patchRecoveryGradient) { gradientX = ShockFindingExtensions.PatchRecovery(gradientX); gradientY = ShockFindingExtensions.PatchRecovery(gradientY); } } if (this.resultGradX == null) { resultGradX = MultidimensionalArray.Create(1, 1); resultGradY = MultidimensionalArray.Create(1, 1); } gradientX.Evaluate(jCell, 1, nodeSet, resultGradX); gradientY.Evaluate(jCell, 1, nodeSet, resultGradY); double[] gradient = new double[] { resultGradX[0, 0], resultGradY[0, 0] }; gradient.Normalize(); return(gradient); }
/// <summary> /// Create clustering based on the density /// </summary> /// <param name="numOfClusters">Needed by <see cref="Kmeans"/></param> /// <param name="initialMeans">Needed by <see cref="Kmeans"/></param> /// <returns> /// Clustering as <see cref="MultidimensionalArray"/> /// [0]: x, [1]: y, [2]: data, [3]: cellToCluster (e.g. cell 0 is in cluster 1), [4]: local cell index /// </returns> public MultidimensionalArray CreateClustering_Density(int numOfClusters, double[] initialMeans) { Console.WriteLine("CreateClustering_Density: START"); double[] data = ShockFindingExtensions.GetFinalFunctionValues(input, inputExtended.ExtractSubArrayShallow(-1, 0)); Kmeans kmeans = new Kmeans(data, numOfClusters, initialMeans); int[] cellToCluster = kmeans.Cluster(); MultidimensionalArray clustering = MultidimensionalArray.Create(data.Length, 5); for (int i = 0; i < input.Lengths[0]; i++) { clustering[i, 0] = input[i, (int)inputExtended[i, 0] - 1, 0]; // x clustering[i, 1] = input[i, (int)inputExtended[i, 0] - 1, 1]; // y clustering[i, 2] = data[i]; // data value clustering[i, 3] = cellToCluster[i]; // cellToCluster (e.g. cell 0 is in cluster 1) clustering[i, 4] = inputExtended[i, 2]; // local cell index } _clusterings.Add(clustering); Console.WriteLine("CreateClustering_Density: END"); return(clustering); }
/// <summary> /// Reconstructs a level set <see cref="SinglePhaseField"/> from a given set of points /// </summary> /// <param name="field">The DG field to work with</param> /// <param name="clustering"> as <see cref="MultidimensionalArray"/> /// Lenghts --> [0]: numOfPoints, [1]: 5 /// [1] --> [0]: x, [1]: y, [2]: data, [3]: cellToCluster (e.g. cell 0 is in cluster 1), [4]: local cell index /// </param> /// <param name="patchRecovery">Use <see cref="ShockFindingExtensions.PatchRecovery(SinglePhaseField)"/></param> /// <param name="continuous">Use <see cref="ShockFindingExtensions.ContinuousLevelSet(SinglePhaseField, MultidimensionalArray)"/></param> /// <returns>Returns the reconstructed level set as <see cref="SinglePhaseField"/></returns> public SinglePhaseField ReconstructLevelSet(SinglePhaseField field = null, MultidimensionalArray clustering = null, bool patchRecovery = true, bool continuous = true) { Console.WriteLine("ReconstructLevelSet: START"); if (field == null) { field = this.densityField; } Console.WriteLine(string.Format("ReconstructLevelSet based on field {0}", field.Identification)); if (clustering == null) { clustering = _clusterings.Last(); Console.WriteLine(string.Format("ReconstructLevelSet based on clustering {0}", _clusterings.Count() - 1)); } else { Console.WriteLine("ReconstructLevelSet based on user-defined clustering"); } // Extract points (x-coordinates, y-coordinates) for reconstruction from clustering MultidimensionalArray points = clustering.ExtractSubArrayShallow(new int[] { 0, 0 }, new int[] { clustering.Lengths[0] - 1, 1 }); _levelSetFields.Add(geometryLevelSetField); SinglePhaseField levelSetField = ShockFindingExtensions.ReconstructLevelSetField(field, points); _levelSetFields.Add(levelSetField); if (patchRecovery) { levelSetField = ShockFindingExtensions.PatchRecovery(levelSetField); _levelSetFields.Add(levelSetField); } if (continuous) { levelSetField = ShockFindingExtensions.ContinuousLevelSet(levelSetField, clustering.ExtractSubArrayShallow(-1, 4).To1DArray()); _levelSetFields.Add(levelSetField); } Console.WriteLine("ReconstructLevelSet: END"); return(levelSetField); }
/// <summary> /// Algorithm to find an inflection point along a curve in the direction of the gradient /// </summary> /// <param name="gridData">The corresponding grid</param> /// <param name="field">The DG field, which shall be evalauted</param> /// <param name="results"> /// The points along the curve (first point has to be user defined) /// Lenghts --> [0]: maxIterations + 1, [1]: 5 /// [1]: x | y | function values | second derivatives | step sizes /// </param> /// <param name="iterations">The amount of iterations needed in order to find the inflection point</param> /// <param name="converged">Has an inflection point been found?</param> /// <param name = "jLocal" >Local cell index of the inflection point</ param > /// <param name="byFlux">Option for the calculation of the first and second order derivatives, default: true</param> private void WalkOnCurve(GridData gridData, SinglePhaseField field, MultidimensionalArray results, out int iterations, out bool converged, out int jLocal, bool byFlux = true) { // Init converged = false; // Current (global) point double[] currentPoint = new double[] { results[0, 0], results[0, 1] }; // Compute global cell index of current point gridData.LocatePoint(currentPoint, out long GlobalId, out long GlobalIndex, out bool IsInside, out bool OnThisProcess); // Compute local node set NodeSet nodeSet = ShockFindingExtensions.GetLocalNodeSet(gridData, currentPoint, (int)GlobalIndex); // Get local cell index of current point int j0Grd = gridData.CellPartitioning.i0; jLocal = (int)(GlobalIndex - j0Grd); // Evaluate the second derivative try { if (byFlux) { results[0, 3] = SecondDerivativeByFlux(field, jLocal, nodeSet); } else { results[0, 3] = ShockFindingExtensions.SecondDerivative(field, jLocal, nodeSet); } } catch (NotSupportedException) { iterations = 0; return; } // Evaluate the function MultidimensionalArray f = MultidimensionalArray.Create(1, 1); field.Evaluate(jLocal, 1, nodeSet, f); results[0, 2] = f[0, 0]; // Set initial step size to 0.5 * h_minGlobal results[0, 4] = 0.5 * gridData.Cells.h_minGlobal; int n = 1; while (n < results.Lengths[0]) { // Evaluate the gradient of the current point double[] gradient; if (byFlux) { gradient = NormalizedGradientByFlux(field, jLocal, nodeSet); } else { gradient = ShockFindingExtensions.NormalizedGradient(field, jLocal, nodeSet); } // Compute new point along curve currentPoint[0] = currentPoint[0] + gradient[0] * results[n - 1, 4]; currentPoint[1] = currentPoint[1] + gradient[1] * results[n - 1, 4]; // New point has been calculated --> old node set is invalid nodeSet = null; // Check if new point is still in the same cell or has moved to one of its neighbours if (!gridData.Cells.IsInCell(currentPoint, jLocal)) { // Get indices of cell neighbours gridData.GetCellNeighbours(jLocal, GetCellNeighbours_Mode.ViaVertices, out int[] cellNeighbours, out int[] connectingEntities); double[] newLocalCoord = new double[currentPoint.Length]; bool found = false; // Find neighbour foreach (int neighbour in cellNeighbours) { if (gridData.Cells.IsInCell(currentPoint, neighbour, newLocalCoord)) { // If neighbour has been found, update jLocal = neighbour + j0Grd; nodeSet = ShockFindingExtensions.GetLocalNodeSet(gridData, currentPoint, jLocal); found = true; break; } } if (found == false) { iterations = n; return; } } else { // New point is still in the same cell --> update only the coordiantes (local node set) nodeSet = ShockFindingExtensions.GetLocalNodeSet(gridData, currentPoint, jLocal + j0Grd); } // Update output results[n, 0] = currentPoint[0]; results[n, 1] = currentPoint[1]; // Evaluate the function f.Clear(); field.Evaluate(jLocal, 1, nodeSet, f); results[n, 2] = f[0, 0]; // Evaluate the second derivative of the new point try { if (byFlux) { results[n, 3] = SecondDerivativeByFlux(field, jLocal, nodeSet); } else { results[n, 3] = ShockFindingExtensions.SecondDerivative(field, jLocal, nodeSet); } } catch (NotSupportedException) { break; } // Check if sign of second derivative has changed // Halve the step size and change direction if (Math.Sign(results[n, 3]) != Math.Sign(results[n - 1, 3])) { results[n, 4] = -results[n - 1, 4] / 2; } else { results[n, 4] = results[n - 1, 4]; } n++; // Termination criterion // Remark: n has already been incremented! double[] diff = new double[currentPoint.Length]; diff[0] = results[n - 1, 0] - results[n - 2, 0]; diff[1] = results[n - 1, 1] - results[n - 2, 1]; if (diff.L2Norm() < 1e-12) { converged = true; break; } } iterations = n; return; }
private double SecondDerivativeByFlux(SinglePhaseField field, int jCell, NodeSet nodeSet) { if (this.gradientX == null) { // Evaluate gradient gradientX = new SinglePhaseField(field.Basis, "gradientX"); gradientY = new SinglePhaseField(field.Basis, "gradientY"); gradientX.DerivativeByFlux(1.0, field, d: 0); gradientY.DerivativeByFlux(1.0, field, d: 1); if (this.patchRecoveryGradient) { gradientX = ShockFindingExtensions.PatchRecovery(gradientX); gradientY = ShockFindingExtensions.PatchRecovery(gradientY); } } if (this.hessianXX == null) { // Evaluate Hessian matrix hessianXX = new SinglePhaseField(field.Basis, "hessianXX"); hessianXY = new SinglePhaseField(field.Basis, "hessianXY"); hessianYX = new SinglePhaseField(field.Basis, "hessianYX"); hessianYY = new SinglePhaseField(field.Basis, "hessianYY"); hessianXX.DerivativeByFlux(1.0, gradientX, d: 0); hessianXY.DerivativeByFlux(1.0, gradientX, d: 1); hessianYX.DerivativeByFlux(1.0, gradientY, d: 0); hessianYY.DerivativeByFlux(1.0, gradientY, d: 1); if (this.patchRecoveryHessian) { hessianXX = ShockFindingExtensions.PatchRecovery(hessianXX); hessianXY = ShockFindingExtensions.PatchRecovery(hessianXY); hessianYX = ShockFindingExtensions.PatchRecovery(hessianYX); hessianYY = ShockFindingExtensions.PatchRecovery(hessianYY); } } if (this.resultGradX == null) { resultGradX = MultidimensionalArray.Create(1, 1); resultGradY = MultidimensionalArray.Create(1, 1); } if (this.resultHessXX == null) { resultHessXX = MultidimensionalArray.Create(1, 1); resultHessXY = MultidimensionalArray.Create(1, 1); resultHessYX = MultidimensionalArray.Create(1, 1); resultHessYY = MultidimensionalArray.Create(1, 1); } gradientX.Evaluate(jCell, 1, nodeSet, resultGradX); gradientY.Evaluate(jCell, 1, nodeSet, resultGradY); hessianXX.Evaluate(jCell, 1, nodeSet, resultHessXX); hessianXY.Evaluate(jCell, 1, nodeSet, resultHessXY); hessianYX.Evaluate(jCell, 1, nodeSet, resultHessYX); hessianYY.Evaluate(jCell, 1, nodeSet, resultHessYY); // Compute second derivative along curve double g_alpha_alpha = 2 * ((resultHessXX[0, 0] * resultGradX[0, 0] + resultHessXY[0, 0] * resultGradY[0, 0]) * resultGradX[0, 0] + (resultHessYX[0, 0] * resultGradX[0, 0] + resultHessYY[0, 0] * resultGradY[0, 0]) * resultGradY[0, 0]); if (g_alpha_alpha == 0.0 || g_alpha_alpha.IsNaN()) { throw new NotSupportedException("Second derivative is zero"); } return(g_alpha_alpha); }
/// <summary> /// Main method in order to find inflection points of a DG field /// </summary> /// <param name="seeding">Setup for seeding, <see cref="SeedingSetup"/>></param> /// <param name="patchRecoveryGradient">Enable patch recovery for the gradient of the DG field</param> /// <param name="patchRecoveryHessian">Enable patch recovery for the second derivatives of the DG field</param> /// <param name="eliminateNonConverged"> Eliminate non-converged points entirely</param> /// <param name="maxNumOfIterations">Maximum number of iterations when searching for the inflection point</param> /// <param name="eps">Threshold (inflection point is reached)</param> /// <returns></returns> public MultidimensionalArray FindPoints(SeedingSetup seeding = SeedingSetup.av, bool patchRecoveryGradient = true, bool patchRecoveryHessian = true, bool eliminateNonConverged = false, int maxNumOfIterations = 100, double eps = 1e-12) { this.patchRecoveryGradient = patchRecoveryGradient; this.patchRecoveryHessian = patchRecoveryHessian; #region Create seedings points based on artificial viscosity int numOfPoints; switch (seeding) { case SeedingSetup.av: MultidimensionalArray avValues = ShockFindingExtensions.GetAVMeanValues(gridData, avField); numOfPoints = avValues.Lengths[0]; Results = MultidimensionalArray.Create(numOfPoints, maxNumOfIterations + 1, 5); // Seed points for (int i = 0; i < numOfPoints; i++) { int jLocal = (int)avValues[i, 0]; double[] cellCenter = gridData.Cells.GetCenter(jLocal); Results[i, 0, 0] = cellCenter[0]; Results[i, 0, 1] = cellCenter[1]; } break; case SeedingSetup.av3x3: MultidimensionalArray avValues3x3 = ShockFindingExtensions.GetAVMeanValues(gridData, avField); int numOfCells = avValues3x3.Lengths[0]; int numOfPointsPerCell = 9; numOfPoints = numOfCells * numOfPointsPerCell; Results = MultidimensionalArray.Create(numOfPoints, maxNumOfIterations + 1, 5); // Seed points for (int i = 0; i < numOfCells; i++) { int jLocal = (int)avValues3x3[i, 0]; MultidimensionalArray grid = ShockFindingExtensions.Get3x3Grid(gridData, jLocal); for (int j = 0; j < numOfPointsPerCell; j++) { Results[i * numOfPointsPerCell + j, 0, 0] = grid[j, 0]; Results[i * numOfPointsPerCell + j, 0, 1] = grid[j, 1]; } } break; case SeedingSetup.everywhere: numOfPoints = gridData.Cells.Count; Results = MultidimensionalArray.Create(numOfPoints, maxNumOfIterations + 1, 5); // Seed points for (int i = 0; i < numOfPoints; i++) { double[] cellCenter = gridData.Cells.GetCenter(i); Results[i, 0, 0] = cellCenter[0]; Results[i, 0, 1] = cellCenter[1]; } break; default: throw new NotSupportedException("This setting does not exist."); } ResultsExtended = MultidimensionalArray.Create(numOfPoints, 3); //IterationsNeeded = new int[numOfPoints]; //Converged = new bool[numOfPoints]; //jCell = new int[numOfPoints]; Console.WriteLine("Total number of seeding points: " + Results.Lengths[0]); #endregion #region Find inflection point for every seeding point Console.WriteLine("WALKING ON CURVES: START"); Console.WriteLine("(Counting starts with 0)"); for (int i = 0; i < Results.Lengths[0]; i++) { WalkOnCurve(gridData, densityField, Results.ExtractSubArrayShallow(i, -1, -1), out int iter, out bool pointFound, out int jLocal); // Save more stuff ResultsExtended[i, 0] = iter; ResultsExtended[i, 1] = pointFound == true ? 1 : -1; ResultsExtended[i, 2] = jLocal; if (i == 0) { Console.WriteLine("Point " + i + " (first)"); } else if (i == Results.Lengths[0] - 1) { Console.WriteLine("Point " + i + " (last)"); } else if (i % 100 == 0) { Console.WriteLine("Point " + i); } #if DEBUG if (!pointFound) { Console.WriteLine(String.Format("Point {0}: not converged", i)); } #endif } if (eliminateNonConverged) { ShockFindingExtensions.EliminateNonConvergedPoints(Results, ResultsExtended, out MultidimensionalArray results_SO, out MultidimensionalArray resultsExtended_SO); //Results_SO = results_SO; //ResultsExtended_SO = resultsExtended_SO; Results = results_SO; ResultsExtended = resultsExtended_SO; } Console.WriteLine("WALKING ON CURVES: END"); #endregion return(Results); }