/// <summary> /// Creates a node-set and a corresponding set of nodal polynomials; /// </summary> public void SelectNodalPolynomials(int px, out NodeSet Nodes, out PolynomialList NodalBasis, out int[] Type, out int[] EntityIndex, Func <Polynomial, bool> ModalBasisSelector = null, int[] NodeTypeFilter = null) { MultidimensionalArray a, b; SelectNodalPolynomials(px, out Nodes, out NodalBasis, out Type, out EntityIndex, out a, out b, ModalBasisSelector, NodeTypeFilter); Debug.Assert(ContainsZero(NodalBasis), "interpolation poly not found, 4"); }
/// <summary> /// Returns the 1st derivatives of the orthonormal approximation polynomials, /// i.e. \f$ \nabla_{\vec{\xi}} \phi_n \f$ up to a specific degree. /// </summary> /// <returns> /// 1st index: reference element; /// 2nd index: spatial direction /// </returns> public PolynomialList[,] GetOrthonormalPolynomials1stDeriv(int Degree) { PolynomialList[,] R; if (!this.m_Polynomial1stDerivLists.TryGetValue(Degree, out R)) { var Krefs = this.m_Owner.iGeomCells.RefElements; int D = this.m_Owner.SpatialDimension; R = new PolynomialList[Krefs.Length, D]; PolynomialList[] Polys = this.GetOrthonormalPolynomials(Degree); int[] DerivExp = new int[D]; for (int d = 0; d < D; d++) { DerivExp[d] = 1; for (int iKref = 0; iKref < Krefs.Length; iKref++) { int N = Polys[iKref].Count; Polynomial[] tmp = new Polynomial[N]; for (int n = 0; n < N; n++) { tmp[n] = Polys[iKref][n].Derive(DerivExp); } R[iKref, d] = new PolynomialList(tmp); } DerivExp[d] = 0; } this.m_Polynomial1stDerivLists.Add(Degree, R); } return(R); }
/// <summary> /// Used by the <see cref="BasisValues"/>-cache. /// </summary> MultidimensionalArray EvaluateBasis(NodeSet NS, int MinDegree) { int iKref = NS.GetVolumeRefElementIndex(this.m_Owner); PolynomialList Polys = this.GetOrthonormalPolynomials(MinDegree)[iKref]; MultidimensionalArray R = MultidimensionalArray.Create(NS.NoOfNodes, Polys.Count); Polys.Evaluate(NS, R); return(R); }
/// <summary> /// Computes the matrix matrix /// V_ij = sum_k{p_i(nodes(k) * p_j(nodes(k)) * weights(k)} /// for a given quadrature rule (where p_i and p_j are the orthonormal /// polynomials supplied by the quadrature rule) and reports an error /// if this matrix differs from the identity matrix /// </summary> /// <param name="order">The <b>requested</b> order</param> /// <param name="rule">The quadrature rule</param> /// <param name="polynomials">The orthonormal polynomials</param> private void TestMassMatrix(int order, QuadRule rule, PolynomialList polynomials) { MultidimensionalArray[] values = new MultidimensionalArray[polynomials.Count]; for (int i = 0; i < polynomials.Count; i++) { values[i] = MultidimensionalArray.Create(rule.NoOfNodes); polynomials[i].Evaluate(values[i], rule.Nodes); } //For every exactly integrable polynomial i for (int i = 0; i < polynomials.Count; i++) { // Check if quadrature rule is suitable if (polynomials[i].AbsoluteDegree > order) { continue; } //For every exactly integrable co-polynomial j for (int j = 0; j <= i; j++) { // Check if quadrature rule is suitable if (polynomials[i].AbsoluteDegree + polynomials[j].AbsoluteDegree > order) { continue; } double result = 0; for (int k = 0; k < rule.NoOfNodes; k++) { result += values[i][k] * values[j][k] * rule.Weights[k]; } //Elements should be zero except of diagonal elements double expectedResult = 0.0; if (i == j) { expectedResult = 1.0; } //Store maximum absolute error for the report we want to create double error = Math.Abs(result - expectedResult); //Console.WriteLine("MassMatrixError: {0}", error); Assert.That( error <= 1e-9, String.Format( "MassMatrix[" + i + "," + j + "] should be {0} but is off by {1:e} for order {2}", expectedResult, error, order)); } } }
/// <summary> /// Change-of-basis, in cell 0 /// </summary> /// <param name="jCell"></param> /// <param name="pl"></param> public MultidimensionalArray GetChangeofBasisMatrix(int jCell, PolynomialList pl) { var m_Context = this.GridDat; int N = this.Length; int M = pl.Count; int J = this.GridDat.iLogicalCells.NoOfLocalUpdatedCells; if (jCell < 0 || jCell >= J) { throw new ArgumentOutOfRangeException("cell index out of range"); } MultidimensionalArray Mtx = MultidimensionalArray.Create(N, M); var cellMask = new CellMask(m_Context, new[] { new Chunk() { i0 = jCell, Len = 1 } }, MaskType.Geometrical); // we project the basis function from 'jCell1' onto 'jCell0' CellQuadrature.GetQuadrature(new int[2] { N, M }, m_Context, (new CellQuadratureScheme(true, cellMask)).Compile(m_Context, this.Degree + pl.MaxAbsoluteDegree), // integrate over target cell delegate(int i0, int Length, QuadRule QR, MultidimensionalArray _EvalResult) { NodeSet nodes_Cell0 = QR.Nodes; Debug.Assert(Length == 1); //NodesGlobal.Allocate(1, nodes_Cell0.GetLength(0), nodes_Cell0.GetLength(1)); //m_Context.TransformLocal2Global(nodes_Cell0, jCell0, 1, NodesGlobal, 0); //var nodes_Cell1 = new NodeSet(GridDat.iGeomCells.GetRefElement(jCell1), nodes_Cell0.GetLength(0), nodes_Cell0.GetLength(1)); //m_Context.TransformGlobal2Local(NodesGlobal.ExtractSubArrayShallow(0, -1, -1), nodes_Cell1, jCell1, null); //nodes_Cell1.LockForever(); var phi_0 = this.CellEval(nodes_Cell0, jCell, 1).ExtractSubArrayShallow(0, -1, -1); MultidimensionalArray R = MultidimensionalArray.Create(QR.NoOfNodes, pl.Count); pl.Evaluate(nodes_Cell0, R); var EvalResult = _EvalResult.ExtractSubArrayShallow(0, -1, -1, -1); EvalResult.Multiply(1.0, R, phi_0, 0.0, "knm", "km", "kn"); }, delegate(int i0, int Length, MultidimensionalArray ResultsOfIntegration) { Debug.Assert(Length == 1); var res = ResultsOfIntegration.ExtractSubArrayShallow(0, -1, -1); Mtx.Clear(); Mtx.Acc(1.0, res); }).Execute(); return(Mtx); }
static MultidimensionalArray Transform(RefElement Kref, Cell Cl, NodeSet Nodes) { int D = Kref.SpatialDimension; PolynomialList polys = Kref.GetInterpolationPolynomials(Cl.Type); MultidimensionalArray polyVals = polys.Values.GetValues(Nodes); MultidimensionalArray GlobalVerticesOut = MultidimensionalArray.Create(Nodes.NoOfNodes, D); for (int d = 0; d < D; d++) { GlobalVerticesOut.ExtractSubArrayShallow(-1, d) .Multiply(1.0, polyVals, Cl.TransformationParams.ExtractSubArrayShallow(-1, d), 0.0, "k", "kn", "n"); } return(GlobalVerticesOut); }
/// <summary> /// Returns the orthonormal approximation polynomials \f$ \phi_n \f$ up to a specific degree. /// </summary> public PolynomialList[] GetOrthonormalPolynomials(int Degree) { PolynomialList[] R; if (!this.m_PolynomialLists.TryGetValue(Degree, out R)) { var Krefs = this.m_Owner.iGeomCells.RefElements; R = new PolynomialList[Krefs.Length]; for (int iKref = 0; iKref < Krefs.Length; iKref++) { R[iKref] = Krefs[iKref].GetOrthonormalPolynomials(Degree); } this.m_PolynomialLists.Add(Degree, R); } return(R); }
/// <summary> /// Projects every basis polynomial for the quadrature rule of the /// given order onto a single cell grid supplied by the sub-class and /// approximates the LInfinity error of the projection. /// </summary> /// <remarks> /// The DG projection uses a quadrature rule of 2*n+1 for a basis of /// order n. Thus, make sure <paramref name="order"/> that a quadrature /// rule of order 2*order+1 exists before calling this method</remarks> /// <param name="order">The <b>requested</b> order</param> /// <param name="polynomials">The orthonormal polynomials</param> /// <param name="rule">The tested quadrature rule</param> private void TestProjectionError(int order, PolynomialList polynomials, QuadRule rule) { Basis basis = new Basis(context, order); for (int i = 0; i < polynomials.Count; i++) { if (2 * polynomials[i].AbsoluteDegree + 1 > order) { // Not integrable _exactly_ with this rule continue; } DGField field = new SinglePhaseField(basis); ScalarFunction function = delegate(MultidimensionalArray input, MultidimensionalArray output) { polynomials[i].Evaluate(output, new NodeSet(rule.RefElement, input)); }; field.ProjectField(function); double LInfError = 0; for (int j = 0; j < rule.NoOfNodes; j++) { MultidimensionalArray result = MultidimensionalArray.Create(1, 1); NodeSet point = new NodeSet(context.iGeomCells.GetRefElement(0), 1, rule.SpatialDim); for (int k = 0; k < rule.SpatialDim; k++) { point[0, k] = rule.Nodes[j, k]; } point.LockForever(); //Evaluate pointwise because we don't want the accumulated //result field.Evaluate(0, 1, point, result, 0.0); MultidimensionalArray exactResult = MultidimensionalArray.Create(1); polynomials[i].Evaluate(exactResult, point); LInfError = Math.Max(Math.Abs(result[0, 0] - exactResult[0]), LInfError); } //Console.WriteLine("ProjectionError: {0}", LInfError); Assert.IsTrue( LInfError <= 1e-13, String.Format( "Projection error too high for polynomial {0}. Error: {1:e}", i, LInfError)); } }
private void DoMaths() { PolynomialList.Clear(); double eps = 0; int nodesCount, degree; if (!int.TryParse(NodesCountBind, out nodesCount) || !int.TryParse(PolynomialDegreeBind, out degree) || (!ApproxByExplicitDegree && !double.TryParse(ErrorMarginBind, out eps))) { MessageBox.Show(Locale["#MethArgParsing"], Locale["#ParsingErr"], MessageBoxButton.OK, MessageBoxImage.Error); } else { try { AccuratePlot = NumCore.NumCore.GetAccuratePlotDataPoints(SelectedFunction, DrawInterval).ToList(); Polynomial approx; var timer = new Stopwatch(); timer.Start(); ApproximationCriterium criterium; if (ApproxByExplicitDegree) { criterium = new ApproximationByPolynomialLevel(degree, UseCotes); } else { criterium = new ApproximationByAccuracy(eps, UseCotes); } var points = NumCore.NumCore.GetApproximatedPlotDataPoints(SelectedFunction, ApproxInterval, nodesCount, criterium, out approx); timer.Stop(); ApproxPlot = points.Select(x => new DataPoint(x.X, x.Y)).ToList(); ApproxTime = timer.ElapsedTicks.ToString(); Error = NumCore.NumCore.GetError(SelectedFunction, approx).ToString(); Polynomial = GetPolynom(approx); } catch (Exception e) { MessageBox.Show(e.Message + Locale["#BadIntervalCont"], Locale["#ParsingErr"], MessageBoxButton.OK, MessageBoxImage.Error); } } }
private string GetPolynom(Polynomial approx) { var result = ""; var coefs = approx.Coefficients; var prec = coefs.Count - 1; var pr = prec; foreach (var coef in coefs) { var key = "x" + (pr / 10 > 0 ? PolynomCoefs[pr / 10] + PolynomCoefs[pr % 10] : PolynomCoefs[pr]); PolynomialList.Add(new KeyValuePair <string, double>(key, coef)); pr--; } for (var i = prec / 10; i >= 0; i--) { for (var j = prec % 10; j >= 0; j--) { var coef = coefs[10 * i + j]; if (i == 0 && j == 0) { return(result + (coef > 0 ? $"+{coef:N2}" : $"-{Math.Abs(coef):N2}")); } result += coef > 0 ? $"+{coef:N2}x" : $"-{Math.Abs(coef):N2}x"; if (i > 0) { for (var k = 1; k <= i; k++) { result += PolynomCoefs[k]; prec--; } } result += PolynomCoefs[j]; } } return(result); }
/// <summary> /// Constructs suitable quadrature rules cells in /// <paramref name="mask"/>. /// </summary> /// <param name="mask"> /// Cells for which quadrature rules shall be created /// </param> /// <param name="order"> /// Desired order of the moment-fitting system. Assuming that /// <see cref="surfaceRuleFactory"/> integrates the basis polynomials /// exactly over the zero iso-contour (which it usually /// doesn't!), the resulting quadrature rules will be exact up to this /// order. /// </param> /// <returns>A set of quadrature rules</returns> /// <remarks> /// Since the selected level set is generally discontinuous across cell /// boundaries, this method does not make use of the fact that /// neighboring cells share edges. That is, the optimization will be /// performed twice for each inner edge in <paramref name="mask"/>. /// </remarks> public IEnumerable <IChunkRulePair <QuadRule> > GetQuadRuleSet(ExecutionMask mask, int order) { using (var tr = new FuncTrace()) { CellMask cellMask = mask as CellMask; if (cellMask == null) { throw new ArgumentException("Mask must be a volume mask", "mask"); } // Note: This is a parallel call, so do this early to avoid parallel confusion localCellIndex2SubgridIndex = new SubGrid(cellMask).LocalCellIndex2SubgridIndex; int maxLambdaDegree = order + 1; int noOfLambdas = GetNumberOfLambdas(maxLambdaDegree); int noOfEdges = LevelSetData.GridDat.Grid.RefElements[0].NoOfFaces; int D = RefElement.SpatialDimension; // Get the basis polynomials and integrate them analytically Polynomial[] basePolynomials = RefElement.GetOrthonormalPolynomials(order).ToArray(); Polynomial[] polynomials = new Polynomial[basePolynomials.Length * D]; for (int i = 0; i < basePolynomials.Length; i++) { Polynomial p = basePolynomials[i]; for (int d = 0; d < D; d++) { Polynomial pNew = p.CloneAs(); for (int j = 0; j < p.Coeff.Length; j++) { pNew.Exponents[j, d]++; pNew.Coeff[j] /= pNew.Exponents[j, d]; pNew.Coeff[j] /= D; // Make sure divergence is Phi again } polynomials[i * D + d] = pNew; } } // basePolynomials[i] == div(polynomials[i*D], ... , polynomials[i*D + D - 1]) lambdaBasis = new PolynomialList(polynomials); if (RestrictNodes) { trafos = new AffineTrafo[mask.NoOfItemsLocally]; foreach (Chunk chunk in mask) { foreach (var cell in chunk.Elements.AsSmartEnumerable()) { CellMask singleElementMask = new CellMask( LevelSetData.GridDat, Chunk.GetSingleElementChunk(cell.Value)); LineAndPointQuadratureFactory.LineQRF lineFactory = this.edgeRuleFactory as LineAndPointQuadratureFactory.LineQRF; if (lineFactory == null) { throw new Exception(); } var lineRule = lineFactory.GetQuadRuleSet(singleElementMask, order).Single().Rule; var pointRule = lineFactory.m_Owner.GetPointFactory().GetQuadRuleSet(singleElementMask, order).Single().Rule; // Also add point rule points since line rule points // are constructed from Gauss rules that do not include // the end points BoundingBox box = new BoundingBox(lineRule.Nodes); box.AddPoints(pointRule.Nodes); int noOfRoots = pointRule.Nodes.GetLength(0); if (noOfRoots <= 1) { // Cell is considered cut because the level set // is very close, but actually isn't. Note that // we can NOT omit the cell (as in the surface // case) as it will be missing in the list of // uncut cells, i.e. this cell would be ignored // completely trafos[localCellIndex2SubgridIndex[cell.Value]] = AffineTrafo.Identity(RefElement.SpatialDimension); continue; } else if (noOfRoots == 2) { // Go a bit into the direction of the normal // from the center between the nodes in order // not to miss regions with strong curvature double[] center = box.Min.CloneAs(); center.AccV(1.0, box.Max); center.ScaleV(0.5); NodeSet centerNode = new NodeSet(RefElement, center); centerNode.LockForever(); MultidimensionalArray normal = LevelSetData.GetLevelSetReferenceNormals(centerNode, cell.Value, 1); MultidimensionalArray dist = LevelSetData.GetLevSetValues(centerNode, cell.Value, 1); double scaling = Math.Sqrt(LevelSetData.GridDat.Cells.JacobiDet[cell.Value]); double[] newPoint = new double[D]; for (int d = 0; d < D; d++) { newPoint[d] = center[d] - normal[0, 0, d] * dist[0, 0] / scaling; } box.AddPoint(newPoint); // Make sure points stay in box for (int d = 0; d < D; d++) { box.Min[d] = Math.Max(box.Min[d], -1); box.Max[d] = Math.Min(box.Max[d], 1); } } MultidimensionalArray preImage = RefElement.Vertices.ExtractSubArrayShallow( new int[] { 0, 0 }, new int[] { D, D - 1 }); MultidimensionalArray image = MultidimensionalArray.Create(D + 1, D); image[0, 0] = box.Min[0]; // Top left image[0, 1] = box.Max[1]; image[1, 0] = box.Max[0]; // Top right image[1, 1] = box.Max[1]; image[2, 0] = box.Min[0]; // Bottom left; image[2, 1] = box.Min[1]; AffineTrafo trafo = AffineTrafo.FromPoints(preImage, image); trafos[localCellIndex2SubgridIndex[cell.Value]] = trafo; } } } LambdaCellBoundaryQuadrature cellBoundaryQuadrature = new LambdaCellBoundaryQuadrature(this, edgeRuleFactory, cellMask); cellBoundaryQuadrature.Execute(); LambdaLevelSetSurfaceQuadrature surfaceQuadrature = new LambdaLevelSetSurfaceQuadrature(this, surfaceRuleFactory, cellMask); surfaceQuadrature.Execute(); // Must happen _after_ all parallel calls (e.g., definition of // the sub-grid or quadrature) in order to avoid problems in // parallel runs if (mask.NoOfItemsLocally == 0) { var empty = new ChunkRulePair <QuadRule> [0]; return(empty); } if (cachedRules.ContainsKey(order)) { order = cachedRules.Keys.Where(cachedOrder => cachedOrder >= order).Min(); CellMask cachedMask = new CellMask(mask.GridData, cachedRules[order].Select(p => p.Chunk).ToArray()); if (cachedMask.Equals(mask)) { return(cachedRules[order]); } else { throw new NotImplementedException( "Case not yet covered yet in combination with caching; deactivate caching to get rid of this message"); } } double[,] quadResults = cellBoundaryQuadrature.Results; foreach (Chunk chunk in mask) { for (int i = 0; i < chunk.Len; i++) { int iSubGrid = localCellIndex2SubgridIndex[chunk.i0 + i]; switch (jumpType) { case JumpTypes.Heaviside: for (int k = 0; k < noOfLambdas; k++) { quadResults[iSubGrid, k] -= surfaceQuadrature.Results[iSubGrid, k]; } break; case JumpTypes.OneMinusHeaviside: for (int k = 0; k < noOfLambdas; k++) { quadResults[iSubGrid, k] += surfaceQuadrature.Results[iSubGrid, k]; } break; case JumpTypes.Sign: for (int k = 0; k < noOfLambdas; k++) { quadResults[iSubGrid, k] -= 2.0 * surfaceQuadrature.Results[iSubGrid, k]; } break; default: throw new NotImplementedException(); } } } BitArray voidCellsArray = new BitArray(LevelSetData.GridDat.Cells.NoOfLocalUpdatedCells); BitArray fullCellsArray = new BitArray(LevelSetData.GridDat.Cells.NoOfLocalUpdatedCells); foreach (Chunk chunk in cellMask) { foreach (var cell in chunk.Elements) { double rhsL2Norm = 0.0; for (int k = 0; k < noOfLambdas; k++) { double entry = quadResults[localCellIndex2SubgridIndex[cell], k]; rhsL2Norm += entry * entry; } if (rhsL2Norm < 1e-14) { // All integrals are zero => cell not really cut // (level set is tangent) and fully in void region voidCellsArray[cell] = true; continue; } double l2NormFirstIntegral = quadResults[localCellIndex2SubgridIndex[cell], 0]; l2NormFirstIntegral *= l2NormFirstIntegral; double rhsL2NormWithoutFirst = rhsL2Norm - l2NormFirstIntegral; // Beware: This check is only sensible if basis is orthonormal on RefElement! if (rhsL2NormWithoutFirst < 1e-14 && Math.Abs(l2NormFirstIntegral - RefElement.Volume) < 1e-14) { // All integrals are zero except integral over first integrand // If basis is orthonormal, this implies that cell is uncut and // fully in non-void region since then // \int_K \Phi_i dV = \int_A \Phi_i dV = \delta_{0,i} // However, we have to compare RefElement.Volume since // integration is performed in reference coordinates! fullCellsArray[cell] = true; } } } var result = new List <ChunkRulePair <QuadRule> >(cellMask.NoOfItemsLocally); CellMask emptyCells = new CellMask(LevelSetData.GridDat, voidCellsArray); foreach (Chunk chunk in emptyCells) { foreach (int cell in chunk.Elements) { QuadRule emptyRule = QuadRule.CreateEmpty(RefElement, 1, RefElement.SpatialDimension); emptyRule.Nodes.LockForever(); result.Add(new ChunkRulePair <QuadRule>( Chunk.GetSingleElementChunk(cell), emptyRule)); } } CellMask fullCells = new CellMask(LevelSetData.GridDat, fullCellsArray); foreach (Chunk chunk in fullCells) { foreach (int cell in chunk.Elements) { QuadRule fullRule = RefElement.GetQuadratureRule(order); result.Add(new ChunkRulePair <QuadRule>( Chunk.GetSingleElementChunk(cell), fullRule)); } } CellMask realCutCells = cellMask.Except(emptyCells).Except(fullCells); if (RestrictNodes) { foreach (Chunk chunk in realCutCells) { foreach (int cell in chunk.Elements) { CellMask singleElementMask = new CellMask( LevelSetData.GridDat, Chunk.GetSingleElementChunk(cell)); AffineTrafo trafo = trafos[localCellIndex2SubgridIndex[cell]]; Debug.Assert(Math.Abs(trafo.Matrix.Determinant()) > 1e-10); NodeSet nodes = GetNodes(noOfLambdas).CloneAs(); NodeSet mappedNodes = new NodeSet(RefElement, trafo.Transform(nodes)); mappedNodes.LockForever(); // Remove nodes in negative part MultidimensionalArray levelSetValues = LevelSetData.GetLevSetValues(mappedNodes, cell, 1); List <int> nodesToBeCopied = new List <int>(mappedNodes.GetLength(0)); for (int n = 0; n < nodes.GetLength(0); n++) { if (levelSetValues[0, n] >= 0.0) { nodesToBeCopied.Add(n); } } NodeSet reducedNodes = new NodeSet( this.RefElement, nodesToBeCopied.Count, D); for (int n = 0; n < nodesToBeCopied.Count; n++) { for (int d = 0; d < D; d++) { reducedNodes[n, d] = mappedNodes[nodesToBeCopied[n], d]; } } reducedNodes.LockForever(); QuadRule optimizedRule = GetOptimizedRule( cell, trafo, reducedNodes, quadResults, order); result.Add(new ChunkRulePair <QuadRule>( singleElementMask.Single(), optimizedRule)); } } } else { // Use same nodes in all cells QuadRule[] optimizedRules = GetOptimizedRules( realCutCells, GetNodes(noOfLambdas), quadResults, order); int ruleIndex = 0; foreach (Chunk chunk in realCutCells) { foreach (var cell in chunk.Elements) { result.Add(new ChunkRulePair <QuadRule>( Chunk.GetSingleElementChunk(cell), optimizedRules[ruleIndex])); ruleIndex++; } } } cachedRules[order] = result.OrderBy(p => p.Chunk.i0).ToArray(); return(cachedRules[order]); } }
/// <summary> /// Updates <see cref="lambdaBasis"/>, <see cref="baseRule"/> and /// <see cref="basisValuesEdge"/> every time a different order is /// requested in <see cref="GetQuadRuleSet"/> /// </summary> /// <param name="order"> /// The new order of the moment-fitting basis /// </param> private void SwitchOrder(int order) { int iKref = this.LevelSetData.GridDat.Cells.RefElements.IndexOf(this.RefElement, (A, B) => object.ReferenceEquals(A, B)); int noOfFaces = LevelSetData.GridDat.Grid.RefElements[iKref].NoOfFaces; int D = RefElement.FaceRefElement.SpatialDimension; Polynomial[] basePolynomials = RefElement.FaceRefElement.GetOrthonormalPolynomials(order).ToArray(); Polynomial[] antiderivativePolynomials = new Polynomial[basePolynomials.Length * D]; for (int i = 0; i < basePolynomials.Length; i++) { Polynomial p = basePolynomials[i]; for (int d = 0; d < D; d++) { Polynomial pNew = p.CloneAs(); for (int j = 0; j < p.Coeff.Length; j++) { pNew.Exponents[j, d]++; pNew.Coeff[j] /= pNew.Exponents[j, d]; // Make sure divergence is Phi again pNew.Coeff[j] /= D; } antiderivativePolynomials[i * D + d] = pNew; } } lambdaBasis = new PolynomialList(antiderivativePolynomials); int minNoOfPoints = GetNumberOfLambdas(); int minOrder = 1; double safetyFactor = 1.6; while (RefElement.FaceRefElement.GetQuadratureRule(minOrder).NoOfNodes < safetyFactor * minNoOfPoints) { minOrder += 1; } QuadRule singleEdgeRule = RefElement.FaceRefElement.GetQuadratureRule(minOrder); baseRule = new CellBoundaryFromEdgeRuleFactory <CellBoundaryQuadRule>( LevelSetData.GridDat, LevelSetData.GridDat.Grid.RefElements[0], new FixedRuleFactory <QuadRule>(singleEdgeRule)). GetQuadRuleSet(new CellMask(LevelSetData.GridDat, Chunk.GetSingleElementChunk(0)), -1). First().Rule; //Basis singleEdgeBasis = new Basis(tracker.GridDat, order, RefElement.FaceRefElement); PolynomialList singleEdgeBasis = new PolynomialList(RefElement.FaceRefElement.GetOrthonormalPolynomials(order)); //MultidimensionalArray edgeNodes = singleEdgeRule.Nodes.CloneAs(); //basisValuesEdge = MultidimensionalArray.Create( // singleEdgeRule.NoOfNodes, singleEdgeBasis.Count); //MultidimensionalArray monomials = Polynomial.GetMonomials( // edgeNodes, RefElement.FaceRefElement.SpatialDimension, singleEdgeBasis.Degree); //for (int j = 0; j < singleEdgeBasis.MinimalLength; j++) { // singleEdgeBasis.Polynomials[iKref, j].Evaluate( // basisValuesEdge.ExtractSubArrayShallow(-1, j), // edgeNodes, // monomials); //} this.basisValuesEdge = singleEdgeBasis.Values.GetValues(singleEdgeRule.Nodes); }
/// <summary> /// Creates a node-set and a corresponding set of nodal polynomials; /// </summary> public void SelectNodalPolynomials(int px, out NodeSet Nodes, out PolynomialList _NodalBasis, out int[] Type, out int[] EntityIndex, out MultidimensionalArray Nodal2Modal, out MultidimensionalArray Modal2Nodal, Func <Polynomial, bool> ModalBasisSelector = null, int[] NodeTypeFilter = null) { int D = this.SpatialDimension; if (ModalBasisSelector == null) { ModalBasisSelector = delegate(Polynomial p) { Debug.Assert(p.Coeff.Length == p.Exponents.GetLength(0)); Debug.Assert(p.Exponents.GetLength(1) == D); for (int l = 0; l < p.Coeff.Length; l++) { for (int d = 0; d < D; d++) { if (p.Exponents[l, d] > (px - 1)) { return(false); } } } return(true); } } ; // Find node set // ============= GetNodeSet(px, out Nodes, out Type, out EntityIndex, NodeTypeFilter); int NoOfNodes = Type.Length; #if DEBUG double[,] DIST = new double[NoOfNodes, NoOfNodes]; for (int j1 = 0; j1 < NoOfNodes; j1++) { for (int j2 = 0; j2 < NoOfNodes; j2++) { if (j2 == j1) { continue; } var node_j1 = Nodes.GetRow(j1); var node_j2 = Nodes.GetRow(j2); DIST[j1, j2] = GenericBlas.L2Dist(node_j1, node_j2); if (DIST[j1, j2] <= 1.0e-8) { throw new ApplicationException("internal error."); } } } #endif // Find modal basis of approximation space // ======================================= var ortho_polys = this.OrthonormalPolynomials; List <Polynomial> Basis = new List <Polynomial>(); int deg; if (px > 1) { for (deg = 0; deg <= (px - 1) * D; deg++) { var r = ortho_polys.Where(p => ((p.AbsoluteDegree == deg) && ModalBasisSelector(p))); Basis.AddRange(r); if (Basis.Count >= NoOfNodes) { break; } } } else { var r = ortho_polys.Where(pol => pol.AbsoluteDegree <= 1); Basis.AddRange(r); deg = 1; } if (Basis.Count != NoOfNodes) { throw new ApplicationException("Basis selection failed."); } // check for basis selection // ========================== // case Triangle, Tetra: the complete P_{px-1}(x,y) should be selected // case Quad, Cube: P_{px-1}(x)*P_{px-1)(y) subset of P_{px-1}(x,y) will be selected int NoOfPolys = NoOfNodes; int[] idx_Basis = new int[NoOfPolys]; for (int i = 0; i < NoOfPolys; i++) { idx_Basis[i] = Array.IndexOf(ortho_polys, Basis[i]); Debug.Assert(idx_Basis[i] >= 0); } // Construct nodal Polynomials // =========================== MultidimensionalArray PolyAtNodes = MultidimensionalArray.Create(NoOfPolys, NoOfNodes); for (int i = 0; i < NoOfNodes; i++) { Basis[i].Evaluate(PolyAtNodes.ExtractSubArrayShallow(-1, i), Nodes); } //FullMatrix MtxPolyAtNodes = new FullMatrix(NoOfPolys, NoOfNodes); //MtxPolyAtNodes.Set(PolyAtNodes); Debug.Assert(!PolyAtNodes.ContainsNanOrInf(), "Nodal Poly generation, illegal value"); var Sol = PolyAtNodes.GetInverse(); Debug.Assert(!Sol.ContainsNanOrInf(), "Nodal Poly generation, solution, illegal value"); Modal2Nodal = MultidimensionalArray.Create(ortho_polys.Where(p => p.AbsoluteDegree <= deg).Count(), NoOfPolys); Nodal2Modal = MultidimensionalArray.Create(Modal2Nodal.NoOfCols, Modal2Nodal.NoOfRows); MultidimensionalArray _Modal2Nodal = MultidimensionalArray.Create(NoOfPolys, NoOfPolys); MultidimensionalArray _Nodal2Modal = MultidimensionalArray.Create(NoOfPolys, NoOfPolys); var b = new double[NoOfPolys]; var rhs = new double[NoOfNodes]; Polynomial[] NodalBasis = new Polynomial[NoOfNodes]; for (int k = 0; k < NoOfNodes; k++) { Array.Clear(rhs, 0, rhs.Length); rhs[k] = 1.0; Sol.GEMV(1.0, rhs, 0.0, b); for (int i = 0; i < NoOfPolys; i++) { if (Math.Abs(b[i]) > 1.0e-12) { if (NodalBasis[k] == null) { NodalBasis[k] = b[i] * Basis[i]; } else { NodalBasis[k] = NodalBasis[k] + b[i] * Basis[i]; } } else { //Console.WriteLine("tresh {0}, {1}", Math.Abs(b[i]), b[i]); } _Modal2Nodal[i, k] = b[i]; } //Console.WriteLine("k: {0}, NoOfPolys {1}, isnull {2} ", k, NoOfPolys, NodalBasis[k] == null); } _NodalBasis = new PolynomialList(NodalBasis); Debug.Assert(ContainsZero(_NodalBasis), "interpolation poly not found, 5"); _Modal2Nodal.InvertTo(_Nodal2Modal); for (int k = 0; k < NoOfNodes; k++) { for (int i = 0; i < NoOfPolys; i++) { Modal2Nodal[idx_Basis[i], k] = _Modal2Nodal[i, k]; Nodal2Modal[i, idx_Basis[k]] = _Nodal2Modal[i, k]; } } } }
/// <summary> /// Orthonormalization for curved elements /// </summary> protected override MultidimensionalArray Compute_OrthonormalizationTrafo(int j0, int Len, int Degree) { // init // ==== int iKref = this.m_Owner.iGeomCells.GetRefElementIndex(j0); PolynomialList Polys = this.GetOrthonormalPolynomials(Degree)[iKref]; int N = Polys.Count; CellType cellType = this.m_Owner.iGeomCells.GetCellType(j0); #if DEBUG // checking for (int j = 1; j < Len; j++) { int jCell = j + j0; if (this.m_Owner.iGeomCells.GetCellType(jCell) != cellType) { throw new NotSupportedException("All cells in chunk must have same type."); } } #endif // storage for result MultidimensionalArray NonlinOrtho = MultidimensionalArray.Create(Len, N, N); if (m_Owner.iGeomCells.IsCellAffineLinear(j0)) { // affine-linear branch // ++++++++++++++++++++ MultidimensionalArray scl = this.Scaling; for (int j = 0; j < Len; j++) { int jCell = j + j0; double scl_j = scl[jCell]; for (int n = 0; n < N; n++) { NonlinOrtho[j, n, n] = scl_j; } } } else { // nonlinear cells branch // ++++++++++++++++++++++ // init // ==== var Kref = m_Owner.iGeomCells.GetRefElement(j0); int deg; // polynomial degree of integrand: Degree of Jacobi determinat + 2* degree of basis polynomials in ref.-space. { int D = m_Owner.SpatialDimension; deg = Kref.GetInterpolationDegree(cellType); if (deg > 1) { deg -= 1; } deg *= D; deg += 2 * Degree; } var qr = Kref.GetQuadratureRule((int)deg); int K = qr.NoOfNodes; // evaluate basis polys in ref space // ================================= MultidimensionalArray BasisValues = this.EvaluateBasis(qr.Nodes, Degree); Debug.Assert(BasisValues.GetLength(0) == K); if (BasisValues.GetLength(0) > N) { BasisValues = BasisValues.ExtractSubArrayShallow(new int[] { 0, 0 }, new int[] { K - 1, N - 1 }); } // compute \f$ A_{k n m} = \phi_{k n} \phi_{k m} w_{k} \f$ // (cell-INdependentd part of mass matrix computation) // ================================================================== MultidimensionalArray A = MultidimensionalArray.Create(K, N, N); A.Multiply(1.0, qr.Weights, BasisValues, BasisValues, 0.0, "knm", "k", "kn", "km"); // Determine mass matrix \f$ M \f$ in all cells by quadrature // \f$ M_{n m} = \sum_{k} J_k A_{k n m} \f$ // ===================================================== MultidimensionalArray J = m_Owner.JacobianDeterminat.GetValue_Cell(qr.Nodes, j0, Len); // store mass-matrix in 'NonlinOrtho' to save mem alloc NonlinOrtho.Multiply(1.0, A, J, 0.0, "jmn", "knm", "jk"); // Compute change-of-basis for all cells // (invese of cholesky) // ===================================== for (int j = 0; j < Len; j++) { MultidimensionalArray Mj = NonlinOrtho.ExtractSubArrayShallow(j, -1, -1); // mass-matrix of basis on refrence element in cell j+j0 #if DEBUG MultidimensionalArray MjClone = Mj.CloneAs(); #endif //Mj.InvertSymmetrical(); Mj.SymmetricLDLInversion(Mj, null); // clear lower triangular part // Debug.Assert(Mj.NoOfCols == N); for (int n = 1; n < N; n++) { for (int m = 0; m < n; m++) { Mj[n, m] = 0.0; } } #if DEBUG MultidimensionalArray B = NonlinOrtho.ExtractSubArrayShallow(j, -1, -1); MultidimensionalArray Bt = B.Transpose(); double MjNorm = MjClone.InfNorm(); double Bnorm = B.InfNorm(); MultidimensionalArray check = IMatrixExtensions.GEMM(Bt, MjClone, B); check.AccEye(-1.0); double checkNorm = check.InfNorm(); double RelErr = checkNorm / Math.Max(MjNorm, Bnorm); Debug.Assert(RelErr < 1.0e-5, "Fatal error in numerical orthonomalization on nonlinear cell."); #endif } } // return // ========= return(NonlinOrtho); }
/// <summary> /// Not implemented. /// </summary> protected override void GetInterpolationNodes_NonLin(CellType Type, out NodeSet InterpolationNodes, out PolynomialList InterpolationPolynomials, out int[] NodeType, out int[] EntityIndex) { throw new NotImplementedException(); }
/// <summary> /// Returns the 2nd derivatives of the orthonormal approximation polynomials, /// i.e. \f$ \nabla_{\vec{\xi}} \phi_n \f$ up to a specific degree. /// </summary> /// <returns> /// 1st index: reference element; /// 2nd index: spatial direction /// 2nd index: spatial direction /// </returns> public PolynomialList[,,] GetOrthonormalPolynomials2ndDeriv(int Degree) { PolynomialList[,,] R; if (!this.m_Polynomial2ndDerivLists.TryGetValue(Degree, out R)) { var Krefs = this.m_Owner.iGeomCells.RefElements; int D = this.m_Owner.SpatialDimension; R = new PolynomialList[Krefs.Length, D, D]; PolynomialList[] Polys = this.GetOrthonormalPolynomials(Degree); int[] DerivExp = new int[D]; for (int d1 = 0; d1 < D; d1++) { DerivExp[d1]++; for (int d2 = 0; d2 < D; d2++) { DerivExp[d2]++; for (int iKref = 0; iKref < Krefs.Length; iKref++) { int N = Polys[iKref].Count; Polynomial[] tmp = new Polynomial[N]; for (int n = 0; n < N; n++) { tmp[n] = Polys[iKref][n].Derive(DerivExp); } R[iKref, d1, d2] = new PolynomialList(tmp); } DerivExp[d2]--; } DerivExp[d1]--; } #if DEBUG for (int d1 = 0; d1 < D; d1++) { for (int d2 = d1 + 1; d2 < D; d2++) { for (int iKref = 0; iKref < Krefs.Length; iKref++) { int N = Polys[iKref].Count; for (int n = 0; n < N; n++) { Polynomial P_d1d2 = R[iKref, d1, d2][n]; Polynomial P_d2d1 = R[iKref, d2, d1][n]; Debug.Assert(P_d1d2.Equals(P_d2d1), "Hessian seems unsymmetric."); } } } } #endif this.m_Polynomial2ndDerivLists.Add(Degree, R); } return(R); }