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); }
public void createNodes(int[,] TrafoIdx, SinglePhaseField Phi, ConventionalDGField ExtProperty) { int iTrafo = TrafoIdx[associatedNeighbour.Item2, associatedNeighbour.Item3]; NodeSet CellNodes = this.EdgeNodes.GetVolumeNodeSet(this.Solver_Grid, iTrafo); //Writes Phi and ExtProperty values at edge nodes into the respective Buffer Phi.Evaluate(associatedNeighbour.Item1, 1, CellNodes, this.PhiEdgeEvalBuffer); ExtProperty.Evaluate(associatedNeighbour.Item1, 1, CellNodes, this.ExtEdgeEvalBuffer); //Writes the corresponding nodes into CellNodesGlobalBuffer this.Solver_Grid.TransformLocal2Global(this.EdgeNodes.GetVolumeNodeSet(this.Solver_Grid, iTrafo), this.EdgeNodesGlobal, associatedNeighbour.Item1); }
/// <summary> /// Evaluates the modified integrand which is /// \f$ /// \vec{g} = g \frac{\nabla \Phi}{|\nabla \Phi|} /// \f$ /// where g represents <see cref="m_Field"/>. /// </summary> /// <param name="j0"> /// <see cref="LevelSetIntegrator.EvaluateIntegrand"/> /// </param> /// <param name="Length"> /// <see cref="LevelSetIntegrator.EvaluateIntegrand"/> /// </param> /// <param name="EvalResult"> /// <see cref="LevelSetIntegrator.EvaluateIntegrand"/> /// </param> public override void EvaluateIntegrand(NodeSet N, int j0, int Length, MultidimensionalArray EvalResult) { using (new FuncTrace()) { int D = base.m_LevSetTrk.GridDat.SpatialDimension; // spatial dimension int noOfNodes = EvalResult.GetLength(1); // number of nodes if (m_LevelSetGradientBuffer.GetLength(0) != Length || m_LevelSetGradientBuffer.GetLength(1) != noOfNodes) { m_IntegrandBuffer.Allocate(Length, noOfNodes); m_LevelSetGradientBuffer.Allocate(Length, noOfNodes, D); } m_Field.Evaluate(j0, Length, N, m_IntegrandBuffer, 0.0); m_levSet.EvaluateGradient(j0, Length, N, m_LevelSetGradientBuffer); for (int i = 0; i < Length; i++) { for (int j = 0; j < noOfNodes; j++) { double AbsGradPhi = 0; for (int d = 0; d < D; d++) { double GradPhi_d = m_LevelSetGradientBuffer[i, j, d]; AbsGradPhi += GradPhi_d * GradPhi_d; } AbsGradPhi = Math.Sqrt(AbsGradPhi); double ooAbsGradPhi = 1.0 / AbsGradPhi; for (int d = 0; d < D; d++) { EvalResult[i, j, 0, d] = ooAbsGradPhi * m_LevelSetGradientBuffer[i, j, d] * m_IntegrandBuffer[i, j]; } } } } }
/// <summary> /// Update Forces and Torque acting from fluid onto the particle /// </summary> /// <param name="U"></param> /// <param name="P"></param> /// <param name="LsTrk"></param> /// <param name="muA"></param> public void UpdateForcesAndTorque(VectorField <SinglePhaseField> U, SinglePhaseField P, LevelSetTracker LsTrk, double muA, double dt, double fluidDensity, bool NotFullyCoupled) { if (skipForceIntegration) { skipForceIntegration = false; return; } HydrodynamicForces[0][0] = 0; HydrodynamicForces[0][1] = 0; HydrodynamicTorque[0] = 0; int RequiredOrder = U[0].Basis.Degree * 3 + 2; Console.WriteLine("Forces coeff: {0}, order = {1}", LsTrk.CutCellQuadratureType, RequiredOrder); double[] Forces = new double[SpatialDim]; SinglePhaseField[] UA = U.ToArray(); ConventionalDGField pA = null; pA = P; if (IncludeTranslation) { for (int d = 0; d < SpatialDim; d++) { void ErrFunc(int CurrentCellID, int Length, NodeSet Ns, MultidimensionalArray result) { int NumberOfNodes = result.GetLength(1); MultidimensionalArray Grad_UARes = MultidimensionalArray.Create(Length, NumberOfNodes, SpatialDim, SpatialDim); MultidimensionalArray pARes = MultidimensionalArray.Create(Length, NumberOfNodes); var Normals = LsTrk.DataHistories[0].Current.GetLevelSetNormals(Ns, CurrentCellID, Length); for (int i = 0; i < SpatialDim; i++) { UA[i].EvaluateGradient(CurrentCellID, Length, Ns, Grad_UARes.ExtractSubArrayShallow(-1, -1, i, -1), 0, 1); } pA.Evaluate(CurrentCellID, Length, Ns, pARes); for (int j = 0; j < Length; j++) { for (int k = 0; k < NumberOfNodes; k++) { result[j, k] = ForceIntegration.CalculateStressTensor(Grad_UARes, pARes, Normals, muA, k, j, this.SpatialDim, d); } } } var SchemeHelper = LsTrk.GetXDGSpaceMetrics(new[] { LsTrk.GetSpeciesId("A") }, RequiredOrder, 1).XQuadSchemeHelper; CellQuadratureScheme cqs = SchemeHelper.GetLevelSetquadScheme(0, CutCells_P(LsTrk)); CellQuadrature.GetQuadrature(new int[] { 1 }, LsTrk.GridDat, cqs.Compile(LsTrk.GridDat, RequiredOrder), delegate(int i0, int Length, QuadRule QR, MultidimensionalArray EvalResult) { ErrFunc(i0, Length, QR.Nodes, EvalResult.ExtractSubArrayShallow(-1, -1, 0)); }, delegate(int i0, int Length, MultidimensionalArray ResultsOfIntegration) { Forces[d] = ParticleAuxillary.ForceTorqueSummationWithNeumaierArray(Forces[d], ResultsOfIntegration, Length); } ).Execute(); } } double Torque = 0; if (IncludeRotation) { void ErrFunc2(int j0, int Len, NodeSet Ns, MultidimensionalArray result) { int K = result.GetLength(1); // No nof Nodes MultidimensionalArray Grad_UARes = MultidimensionalArray.Create(Len, K, SpatialDim, SpatialDim);; MultidimensionalArray pARes = MultidimensionalArray.Create(Len, K); // Evaluate tangential velocity to level-set surface var Normals = LsTrk.DataHistories[0].Current.GetLevelSetNormals(Ns, j0, Len); for (int i = 0; i < SpatialDim; i++) { UA[i].EvaluateGradient(j0, Len, Ns, Grad_UARes.ExtractSubArrayShallow(-1, -1, i, -1), 0, 1); } pA.Evaluate(j0, Len, Ns, pARes); for (int j = 0; j < Len; j++) { MultidimensionalArray tempArray = Ns.CloneAs(); LsTrk.GridDat.TransformLocal2Global(Ns, tempArray, j0 + j); for (int k = 0; k < K; k++) { result[j, k] = ForceIntegration.CalculateTorqueFromStressTensor2D(Grad_UARes, pARes, Normals, tempArray, muA, k, j, Position[0]); } } } var SchemeHelper2 = LsTrk.GetXDGSpaceMetrics(new[] { LsTrk.GetSpeciesId("A") }, RequiredOrder, 1).XQuadSchemeHelper; CellQuadratureScheme cqs2 = SchemeHelper2.GetLevelSetquadScheme(0, CutCells_P(LsTrk)); CellQuadrature.GetQuadrature(new int[] { 1 }, LsTrk.GridDat, cqs2.Compile(LsTrk.GridDat, RequiredOrder), delegate(int i0, int Length, QuadRule QR, MultidimensionalArray EvalResult) { ErrFunc2(i0, Length, QR.Nodes, EvalResult.ExtractSubArrayShallow(-1, -1, 0)); }, delegate(int i0, int Length, MultidimensionalArray ResultsOfIntegration) { Torque = ParticleAuxillary.ForceTorqueSummationWithNeumaierArray(Torque, ResultsOfIntegration, Length); } ).Execute(); } // add gravity { Forces[1] += (particleDensity - fluidDensity) * Area_P * GravityVertical; } // Sum forces and moments over all MPI processors // ============================================== { int NoOfVars = 1 + SpatialDim; double[] StateBuffer = new double[NoOfVars]; StateBuffer[0] = Torque; for (int d = 0; d < SpatialDim; d++) { StateBuffer[1 + d] = Forces[d]; } double[] GlobalStateBuffer = StateBuffer.MPISum(); Torque = GlobalStateBuffer[0]; for (int d = 0; d < SpatialDim; d++) { Forces[d] = GlobalStateBuffer[1 + d]; } } if (neglectAddedDamping == false) { double fest = Forces[0]; Forces[0] = Forces[0] + AddedDampingCoefficient * dt * (AddedDampingTensor[0, 0] * TranslationalAcceleration[0][0] + AddedDampingTensor[1, 0] * TranslationalAcceleration[0][1] + AddedDampingTensor[0, 2] * RotationalAcceleration[0]); Forces[1] = Forces[1] + AddedDampingCoefficient * dt * (AddedDampingTensor[0, 1] * TranslationalAcceleration[0][0] + AddedDampingTensor[1, 1] * TranslationalAcceleration[0][1] + AddedDampingTensor[1, 2] * RotationalAcceleration[0]); Torque += AddedDampingCoefficient * dt * (AddedDampingTensor[2, 0] * TranslationalAcceleration[0][0] + AddedDampingTensor[2, 1] * TranslationalAcceleration[0][1] + AddedDampingTensor[2, 2] * RotationalAcceleration[0]); } if (iteration_counter_P == -1 || NotFullyCoupled || iteration_counter_P == 250 || stupidcounter == 0) { Console.WriteLine(); if (iteration_counter_P == 1) { Console.WriteLine("First iteration of the current timestep, all relaxation factors are set to 1"); } if (iteration_counter_P == 250) { Console.WriteLine("250 iterations, I'm trying to jump closer to the real solution"); } for (int d = 0; d < SpatialDim; d++) { HydrodynamicForces[0][d] = 0; if (Math.Abs(Forces[d]) < ForceAndTorque_convergence * 1e-2 && ClearSmallValues == true) { Forces[d] = 0; } HydrodynamicForces[0][d] = Forces[d]; } HydrodynamicTorque[0] = 0; if (Math.Abs(Torque) < ForceAndTorque_convergence * 1e-2 && ClearSmallValues == true) { Torque = 0; } HydrodynamicTorque[0] = Torque; stupidcounter = 1; } else { double[] RelaxatedForceAndTorque = Underrelaxation.RelaxatedForcesAndTorque(Forces, Torque, ForcesPrevIteration, TorquePrevIteration, ForceAndTorque_convergence, underrelaxation_factor, ClearSmallValues, AddaptiveUnderrelaxation, AverageDistance, iteration_counter_P); for (int d = 0; d < SpatialDim; d++) { HydrodynamicForces[0][d] = RelaxatedForceAndTorque[d]; } HydrodynamicTorque[0] = RelaxatedForceAndTorque[SpatialDim]; } //for (int d = 0; d < SpatialDim; d++)// changes sign depending on the sign of Forces[d], should increase the convergence rate. (testing needed) //{ // if (Math.Abs(HydrodynamicForces[0][d] - Forces[0]) > Math.Abs(Forces[d])) // { // HydrodynamicForces[0][d] *= -1; // } //} if (double.IsNaN(HydrodynamicForces[0][0]) || double.IsInfinity(HydrodynamicForces[0][0])) { throw new ArithmeticException("Error trying to calculate hydrodynamic forces (x). Value: " + HydrodynamicForces[0][0]); } if (double.IsNaN(HydrodynamicForces[0][1]) || double.IsInfinity(HydrodynamicForces[0][1])) { throw new ArithmeticException("Error trying to calculate hydrodynamic forces (y). Value: " + HydrodynamicForces[0][1]); } if (double.IsNaN(HydrodynamicTorque[0]) || double.IsInfinity(HydrodynamicTorque[0])) { throw new ArithmeticException("Error trying to calculate hydrodynamic torque. Value: " + HydrodynamicTorque[0]); } }
public bool LocalSolve(int jCell, BitArray AcceptedMask, SinglePhaseField Phi, double _sign, out double Min, out double Max) { Debug.Assert(_sign.Abs() == 1); // find all accepted neighbors // =========================== var NeighCells = this.GridDat.GetCellNeighboursViaEdges(jCell); int iKref = this.GridDat.Cells.GetRefElementIndex(jCell); int NN = NeighCells.Length; var NeighCellsK = new Tuple <int, int, int> [NN]; int NNK = 0; for (int nn = 0; nn < NN; nn++) { if (AcceptedMask[NeighCells[nn].Item1] == true) { NeighCellsK[NNK] = NeighCells[nn]; NNK++; } } // evaluate accepted neighbors // ============================ Min = double.MaxValue; Max = double.MinValue; var TrafoIdx = this.GridDat.Edges.Edge2CellTrafoIndex; int K = this.PhiEvalBuffer[0].GetLength(1); // Nodes per edge for (int nnk = 0; nnk < NNK; nnk++) // loop over accepted neighbours { int jNC = NeighCellsK[nnk].Item1; int iEdg = NeighCellsK[nnk].Item2; int InOrOut = NeighCellsK[nnk].Item3; int iTrafo = TrafoIdx[iEdg, InOrOut]; this.PhiEvalBuffer[nnk].Clear(); Phi.Evaluate(jNC, 1, this.EdgeNodes.GetVolumeNodeSet(this.GridDat, iTrafo), this.PhiEvalBuffer[nnk]); this.GridDat.TransformLocal2Global(this.EdgeNodes.GetVolumeNodeSet(this.GridDat, iTrafo), this.CellNodesGlobal[nnk], jNC); Max = Math.Max(Max, PhiEvalBuffer[nnk].Max()); Min = Math.Min(Min, PhiEvalBuffer[nnk].Min()); } var _QuadNodesGlobal = this.QuadNodesGlobal[iKref]; this.GridDat.TransformLocal2Global(this.DaRuleS[iKref].Nodes, _QuadNodesGlobal, jCell); if (_sign > 0) { Max += this.GridDat.Cells.h_max[jCell]; } else { Min -= this.GridDat.Cells.h_max[jCell]; } // perform projection of geometric reinit // ====================================== // temp storage double[] Y = new double[2]; double[] X = new double[2]; double[] X1 = new double[2]; double[] X2 = new double[2]; // basis values at cell quadrature nodes var BasisValues = this.LevelSetBasis_Geometric.CellEval(this.DaRuleS[iKref].Nodes, jCell, 1).ExtractSubArrayShallow(0, -1, -1); int NoOfQn = BasisValues.GetLength(0); // number of quadrature nodes // result at quadrature nodes var PhiAtQuadNodes = MultidimensionalArray.Create(NoOfQn); for (int iQn = NoOfQn - 1; iQn >= 0; iQn--) // loop over all quadrature nodes { Y[0] = _QuadNodesGlobal[iQn, 0]; Y[1] = _QuadNodesGlobal[iQn, 1]; double _dist_min1 = double.MaxValue, _phi_min1 = double.NaN; int _nnk_Min1 = int.MinValue, _k_min1 = int.MinValue; double _dist_min2 = double.MaxValue, _phi_min2 = double.NaN; int _nnk_Min2 = int.MinValue, _k_min2 = int.MinValue; // find closest point: brute force approach for (int nnk = 0; nnk < NNK; nnk++) // loop over all edges with known values { double dist_min1 = double.MaxValue, phi_min1 = double.NaN; int nnk_Min1 = int.MinValue, k_min1 = int.MinValue; double dist_min2 = double.MaxValue, phi_min2 = double.NaN; int nnk_Min2 = int.MinValue, k_min2 = int.MinValue; for (int k = 0; k < K; k++) // loop over all nodes on this edge { X[0] = this.CellNodesGlobal[nnk][k, 0]; X[1] = this.CellNodesGlobal[nnk][k, 1]; double phi = this.PhiEvalBuffer[nnk][0, k]; phi *= _sign; double dist = GenericBlas.L2Dist(X, Y) + phi; bool NoBlock = true; if (dist < dist_min1) { nnk_Min2 = nnk_Min1; k_min2 = k_min1; dist_min2 = dist_min1; phi_min2 = phi_min1; dist_min1 = dist; nnk_Min1 = nnk; k_min1 = k; phi_min1 = phi; NoBlock = false; } if (dist >= dist_min1 && dist < dist_min2 && NoBlock) { dist_min2 = dist; nnk_Min2 = nnk; k_min2 = k; phi_min2 = phi; } } if (dist_min1 < _dist_min1) { _dist_min1 = dist_min1; _k_min1 = k_min1; _nnk_Min1 = nnk_Min1; _phi_min1 = phi_min1; _dist_min2 = dist_min2; _k_min2 = k_min2; _nnk_Min2 = nnk_Min2; _phi_min2 = phi_min2; } } { Debug.Assert(_nnk_Min1 == _nnk_Min2); Debug.Assert(_k_min1 != _k_min2); double PhiMin1 = this.PhiEvalBuffer[_nnk_Min1][0, _k_min1]; double PhiMin2 = this.PhiEvalBuffer[_nnk_Min2][0, _k_min2]; X1[0] = this.CellNodesGlobal[_nnk_Min1][_k_min1, 0]; X1[1] = this.CellNodesGlobal[_nnk_Min1][_k_min1, 1]; X2[0] = this.CellNodesGlobal[_nnk_Min2][_k_min2, 0]; X2[1] = this.CellNodesGlobal[_nnk_Min2][_k_min2, 1]; } _dist_min1 *= _sign; PhiAtQuadNodes[iQn] = _dist_min1 * this.DaRuleS[iKref].Weights[iQn]; } // finalize projection & return // ============================ if (this.GridDat.Cells.IsCellAffineLinear(jCell)) { int N = this.LevelSetBasis_Geometric.GetLength(jCell); int N2 = Phi.Basis.GetLength(jCell); MultidimensionalArray Phi_1 = MultidimensionalArray.Create(N); double scale = this.GridDat.Cells.JacobiDet[jCell]; Phi_1.Multiply(scale, BasisValues, PhiAtQuadNodes, 0.0, "m", "km", "k"); for (int n = 0; n < N; n++) { Phi.Coordinates[jCell, n] = Phi_1[n]; } for (int n = N; n < N2; n++) { Phi.Coordinates[jCell, n] = 0; } } else { throw new NotImplementedException("not implemented for curved cells"); } return(true); }
/// <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); }