public Func <double[], double> GetF(string species, int d) { double rho = double.NaN; switch (species) { case "A": rho = rho_A; break; case "B": rho = rho_B; break; throw new ArgumentException(); } if (mu_A == 0.0 && mu_B == 0) { return(X => 0.0); } else { double sc = Math.Min(this.mu_A, this.mu_B); double[] Fvec = new double[] { (1.0) * sc, 0 }; var FvecT = ROT.Transform(Fvec); return(X => FvecT[d] / rho); } }
public Func <double[], double, double> GetU(string species, int d) { double a2, a1, a0; if (species == "A") { ParabolaCoeffs_A(out a2, out a1, out a0); } else if (species == "B") { ParabolaCoeffs_B(out a2, out a1, out a0); } else { throw new ArgumentException(); } return(((_2D)(delegate(double _x, double _y) { var Coord = ROTinv.Transform(_x, _y); double y = Coord[1]; Debug.Assert(Coord[0] >= -2); Debug.Assert(Coord[0] <= +2); Debug.Assert(Coord[1] >= -1); Debug.Assert(Coord[1] <= +1); double u = (a0 + a1 * y + a2 * y * y); //double u = 1 - y*y; var UT = ROT.Transform(u, 0.0); return UT[d]; })).Convert_xy2X().Convert_X2Xt()); }
public Vector DistanceBetweenNodes(int jEdge) { int sourceCell = GridData.iLogicalEdges.CellIndices[jEdge, 0]; int targetCell = GridData.iLogicalEdges.CellIndices[jEdge, 1]; if (sourceCell < 0 || targetCell < 0) { throw new Exception("Boundary edge does not have two connected nodes."); } Vector sourceNode = new Vector(nodes.Positions[sourceCell, 0], nodes.Positions[sourceCell, 1]); Vector targetNode = new Vector(nodes.Positions[targetCell, 0], nodes.Positions[targetCell, 1]); //transform if Edge is periodic int jGeomEdge = GridData.iLogicalEdges.EdgeToParts[jEdge][0]; if (this.iGridData.iGeomEdges.EdgeTags[jGeomEdge] >= GridCommons.FIRST_PERIODIC_BC_TAG) { int periodicEdgeTag = this.iGridData.iGeomEdges.EdgeTags[jGeomEdge] - GridCommons.FIRST_PERIODIC_BC_TAG; AffineTrafo PerT = ((GridCommons)ParentGrid).PeriodicTrafo[periodicEdgeTag]; targetNode = PerT.Transform(targetNode); } ; Vector distance = targetNode - sourceNode; return(distance); }
public double NormalEdgeVelocity(int jEdge, double[] x, Vector normal) { int jCellIn = this.iGridData.iGeomEdges.CellIndices[jEdge, 1]; int jCellOut = this.iGridData.iGeomEdges.CellIndices[jEdge, 0]; int jCell_in = this.iGridData.iGeomCells.GeomCell2LogicalCell[jCellIn]; int jCell_ot = this.iGridData.iGeomCells.GeomCell2LogicalCell[jCellOut]; MultidimensionalArray positions = Nodes.Positions; MultidimensionalArray velocities = Nodes.Velocity; double[] posOt = positions.GetRow(jCell_ot); double[] posIn = positions.GetRow(jCell_in); double[] velOt = velocities.GetRow(jCell_ot); double[] velIn = velocities.GetRow(jCell_in); //transform if Edge is periodic if (this.iGridData.iGeomEdges.EdgeTags[jEdge] >= GridCommons.FIRST_PERIODIC_BC_TAG) { int periodicEdgeTag = this.iGridData.iGeomEdges.EdgeTags[jEdge] - GridCommons.FIRST_PERIODIC_BC_TAG; AffineTrafo PerT = ((GridCommons)ParentGrid).PeriodicTrafo[periodicEdgeTag]; posIn = PerT.Transform(posIn); } ; double result = VoronoiEdge.NormalVelocity(posOt, velOt, posIn, velIn, x, normal); return(result); }
public Func <double[], double, double> GetU(string species, int d) { return(((_2D)(delegate(double _x, double _y) { var Coord = ROTinv.Transform(_x, _y); double y = Coord[1]; double u = (1.0 - y * y); var UT = ROT.Transform(u, 0.0); return UT[d]; })).Convert_xy2X().Convert_X2Xt()); }
/// <summary> /// Subdivides the leaves of thee tree. /// </summary> /// <param name="leavesLevel"> /// <see cref="SubdivideCutLeaves"/>. /// </param> /// <param name="refinedVertexSet"> /// <see cref="SubdivideCutLeaves"/>. /// </param> /// <param name="checkIsCut"> /// If true, leaves will only be subdivided if <see cref="IsCut"/> /// returns true. /// </param> /// <param name="minDistance"> /// See <see cref="IsCut"/>. /// </param> /// <returns> /// True, if at least one node has been subdivided. Otherwise, /// false is returned. /// </returns> private bool SubdivideLeaves(int leavesLevel, NestedVertexSet refinedVertexSet, bool checkIsCut, double minDistance) { bool result = false; if (level < leavesLevel) { if (Children.Count > 0) { foreach (Node child in Children) { result |= child.SubdivideLeaves(leavesLevel, refinedVertexSet, checkIsCut, minDistance); } } } else if (level == leavesLevel) { Debug.Assert(Children.Count == 0, "Can only subdivide leaves"); if (!checkIsCut || (checkIsCut && IsPotentiallyCut(minDistance))) { AffineTrafo[] transformations = owner.refElement.GetSubdivision(); int N = globalVertexIndices.Length; int D = owner.refElement.SpatialDimension; foreach (AffineTrafo elementaryTransformation in transformations) { AffineTrafo combinedTransformation = Transformation * elementaryTransformation; int[] transfomedVertexIndices = new int[N]; for (int i = 0; i < N; i++) { double[] vertex = owner.refElement.Vertices.GetRow(i); vertex = combinedTransformation.Transform(vertex); transfomedVertexIndices[i] = refinedVertexSet.RegisterVertex(vertex); } Children.Add(new Node( owner, this, level + 1, refinedVertexSet, transfomedVertexIndices, combinedTransformation)); result = true; } } else { return(false); } } else { throw new ArgumentException("Given leaves level is higher than the depth of the tree", "leavesLevel"); } return(result); }
/// <summary> /// If this is a node set defined in the edge coordinate system, this method provides /// the nodes transformed to the cell coordinate system. /// </summary> /// <param name="g"></param> /// <param name="Edge2CellTrafoIndex"> /// The transformation index (from edge to cell coordinate system), i.e. an index into <see cref="GridData.EdgeData.Edge2CellTrafos"/>, /// see also <see cref="GridData.EdgeData.Edge2CellTrafoIndex"/>. /// </param> /// <returns></returns> public NodeSet GetVolumeNodeSet(IGridData g, int Edge2CellTrafoIndex) { if (!base.IsLocked) { throw new NotSupportedException("NodeSet must be locked before first usage."); } Debug.Assert((base.GetLength(1) == this.RefElement.SpatialDimension) || (base.GetLength(1) == 1 && this.RefElement.SpatialDimension == 0), "Mismatch between number of spatial directions in node set and reference element."); #if DEBUG if (this.GetNodeCoordinateSystem(g) != NodeCoordinateSystem.EdgeCoord) { throw new NotSupportedException("Operation only supported for edge node sets."); } #endif int D = g.SpatialDimension; int NN = this.NoOfNodes; int idx = Edge2CellTrafoIndex + g.iGeomEdges.e2C_offet; if (VolumeNodeSets == null) { // alloc mem, if necessary // ++++++++++++++++++++++++ VolumeNodeSets = new NodeSet[g.iGeomEdges.Edge2CellTrafos.Count + g.iGeomEdges.e2C_offet]; } if (VolumeNodeSets.Length < (g.iGeomEdges.Edge2CellTrafos.Count + g.iGeomEdges.e2C_offet)) { // re-alloc mem, if necessary // ++++++++++++++++++++++++++ var newVNS = new NodeSet[g.iGeomEdges.Edge2CellTrafos.Count + g.iGeomEdges.e2C_offet]; Array.Copy(VolumeNodeSets, 0, newVNS, 0, VolumeNodeSets.Length); VolumeNodeSets = newVNS; } if (VolumeNodeSets[idx] == null) { // transform edge-nodes to cell-nodes, if necessary // ++++++++++++++++++++++++++++++++++++++++++++++++ AffineTrafo Trafo = g.iGeomEdges.Edge2CellTrafos[Edge2CellTrafoIndex]; int iKref = g.iGeomEdges.Edge2CellTrafosRefElementIndices[Edge2CellTrafoIndex]; NodeSet volNS = new NodeSet(g.iGeomCells.RefElements[iKref], NN, D); Trafo.Transform(this, volNS); volNS.LockForever(); VolumeNodeSets[idx] = volNS; } return(VolumeNodeSets[idx]); }
/// <summary> /// Evaluates the vector-valued anti-derivatives defining the /// moment-fitting basis (cf. <see cref="lambdaBasis"/>) in each node /// of all cells in the given range /// </summary> /// <param name="i0"> /// First cell in range /// </param> /// <param name="length"> /// Number of cells /// </param> /// <returns> /// The values of <see cref="lambdaBasis"/> in each node /// <list type="bullet"> /// <item>1st index: Node index</item> /// <item>2nd index: Basis function index</item> /// <item>3rd index: Spatial dimension</item> /// </list> /// </returns> /// <remarks> /// This method does not evaluate a Lambda which is constant since the /// derivative of such function is zero. As a result, the integral over /// this function is zero, too, which makes it useless for the /// construction of a quadrature rule /// </remarks> /// <param name="NS"> /// Nodes at which to evaluate. /// </param> private MultidimensionalArray EvaluateLambdas(int cell, NodeSet NS) { int D = LevelSetData.GridDat.SpatialDimension; Debug.Assert( lambdaBasis.Count % D == 0, "Number of polynomials in basis should be divisible by D = " + D); int noOfLambdas = lambdaBasis.Count / D; int noOfNodes = NS.NoOfNodes; if (RestrictNodes) { AffineTrafo trafo = trafos[localCellIndex2SubgridIndex[cell]]; AffineTrafo inverse = trafo.Invert(); NS = new NodeSet(RefElement, inverse.Transform(NS)); NS.LockForever(); MultidimensionalArray lambdaValues = lambdaBasis.Values.GetValues(NS); lambdaValues = lambdaValues.ResizeShallow(noOfNodes, noOfLambdas, D); for (int i = 0; i < noOfNodes; i++) { for (int j = 0; j < noOfLambdas; j++) { for (int d = 0; d < D; d++) { // Bounding box transformation is assumed to just a // stretching, i.e. off-diagonals are zero lambdaValues[i, j, d] *= trafo.Matrix[d, d]; } } } return(lambdaValues); } else { MultidimensionalArray lambdaValues = lambdaBasis.Values.GetValues(NS); return(lambdaValues.ResizeShallow(noOfNodes, noOfLambdas, D)); } }
/// <summary> /// Non-vectorized reference implementation of /// <see cref="GetOptimizedRule(int, AffineTrafo, NodeSet, double[,], int)"/> /// </summary> /// <param name="cell"></param> /// <param name="trafo"></param> /// <param name="nodes"></param> /// <param name="quadResults"></param> /// <param name="order"></param> /// <returns></returns> private QuadRule GetOptimizedRule(int cell, AffineTrafo trafo, NodeSet nodes, double[,] quadResults, int order) { int maxLambdaDegree = order + 1; int noOfLambdas = GetNumberOfLambdas(maxLambdaDegree); int noOfNodes = nodes.GetLength(0); // Leading dimension of B (rhs); required by DGELSY int LDB = Math.Max(noOfLambdas, noOfNodes); double[] rhs = new double[LDB]; AffineTrafo inverseTrafo = trafo.Invert(); NodeSet trafoNodes = new NodeSet(RefElement, inverseTrafo.Transform(nodes)); trafoNodes.LockForever(); Basis basis = new Basis(LevelSetData.GridDat, order); MultidimensionalArray basisValues = basis.Evaluate(trafoNodes); int iSubGrid = localCellIndex2SubgridIndex[cell]; for (int k = 0; k < noOfLambdas; k++) { rhs[k] += quadResults[iSubGrid, k]; } LAPACK.F77_LAPACK.DGELSY(noOfLambdas, noOfNodes, basisValues.Storage, rhs, 1, RCOND); QuadRule optimizedRule = new QuadRule() { Nodes = nodes, Weights = MultidimensionalArray.Create(noOfNodes), OrderOfPrecision = order }; for (int j = 0; j < noOfNodes; j++) { optimizedRule.Weights[j] = rhs[j]; } return(optimizedRule); }
/// <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]); } }