/// <summary> /// Function that varies over the plane depending on the circular type /// </summary> private static Func <double, double, double> ValueFunction(CircularValueTypes cvt, double xMin, double yMin, double xMax, double yMax) { Func <double, double, double> function; switch (cvt) { case CircularValueTypes.Normal: function = (x, y) => 0.0001 * ((x - xMin) + 2.0 * (y - yMin)); break; case CircularValueTypes.Degrees180: function = (x, y) => - 180 + (360 * (x - xMin) / (xMax - xMin) + 360 * (y - yMin) / (yMax - yMin)) % 360.0; break; case CircularValueTypes.Degrees360: function = (x, y) => (360 * (x - xMin) / (xMax - xMin) + 360 * (y - yMin) / (yMax - yMin)) % 360.0; break; case CircularValueTypes.RadiansPi: function = (x, y) => - Math.PI + (2 * Math.PI * (x - xMin) / (xMax - xMin) + 2 * Math.PI * (y - yMin) / (yMax - yMin)) % (2 * Math.PI); break; case CircularValueTypes.Radians2Pi: function = (x, y) => (2 * Math.PI * (x - xMin) / (xMax - xMin) + 2 * Math.PI * (y - yMin) / (yMax - yMin)) % (2 * Math.PI); break; default: throw new ArgumentOutOfRangeException(nameof(cvt), cvt, null); } return(function); }
/// <summary> /// Make <paramref name="sourceValue"/> sufficiently close to <paramref name="refValue"/> /// in order to interpolate on angular values. /// </summary> public static void ToReference(CircularValueTypes circularType, ref double sourceValue, double refValue) { switch (circularType) { case CircularValueTypes.RadiansPi: case CircularValueTypes.Radians2Pi: if (sourceValue - refValue > Math.PI) { sourceValue -= 2 * Math.PI; } else if (sourceValue - refValue < -Math.PI) { sourceValue += 2 * Math.PI; } break; case CircularValueTypes.Degrees180: case CircularValueTypes.Degrees360: if (sourceValue - refValue > 180.0) { sourceValue -= 360.0; } else if (sourceValue - refValue < -180.0) { sourceValue += 360.0; } break; } }
/// <summary> /// Make <paramref name="sourceValue"/> sufficiently close to <paramref name="refValue"/> /// in order to interpolate on angular values. /// </summary> public static double ToReference(CircularValueTypes circularType, double sourceValue, double refValue) { switch (circularType) { case CircularValueTypes.RadiansPi: case CircularValueTypes.Radians2Pi: if (sourceValue - refValue > Math.PI) { return(sourceValue - 2 * Math.PI); } else if (sourceValue - refValue < -Math.PI) { return(sourceValue + 2 * Math.PI); } break; case CircularValueTypes.Degrees180: case CircularValueTypes.Degrees360: if (sourceValue - refValue > 180.0) { return(sourceValue - 360.0); } else if (sourceValue - refValue < -180.0) { return(sourceValue + 360.0); } break; } return(sourceValue); }
public void NodeInterpolationTest(string meshFileName, CircularValueTypes cvt = CircularValueTypes.Normal) { // Source mesh MeshFile meshFile = MeshFile.ReadMesh(meshFileName); MeshData mesh = meshFile.ToMeshData(); // Allow for extrapolation on boundary nodes (disable clipping) MeshNodeInterpolation interpolation = new MeshNodeInterpolation(mesh) { AllowExtrapolation = true, }; interpolation.Setup(); Interpolator nodeInterpolator = interpolation.NodeInterpolator; nodeInterpolator.CircularType = cvt; // Find reference x and y value as the smallest x and y value double xMin = mesh.Nodes.Select(mn => mn.X).Min(); double xMax = mesh.Nodes.Select(mn => mn.X).Max(); double yMin = mesh.Nodes.Select(mn => mn.Y).Min(); double yMax = mesh.Nodes.Select(mn => mn.Y).Max(); // Function over the (x,y) plane. Func <double, double, double> function = ValueFunction(cvt, xMin, yMin, xMax, yMax); // Calculate element center values double[] elmtVals = new double[mesh.Elements.Count]; for (int i = 0; i < mesh.Elements.Count; i++) { MeshElement elmt = mesh.Elements[i]; elmtVals[i] = function(elmt.XCenter, elmt.YCenter); } // Write out bounds, to check we got things right Console.Out.WriteLine("{0,10} (min,max) = ({1},{2})", cvt, elmtVals.Min(), elmtVals.Max()); // Interpolate to nodes double[] nodeValues = new double[mesh.Nodes.Count]; nodeInterpolator.Interpolate(elmtVals, nodeValues); // Check node values for (int i = 0; i < mesh.Nodes.Count; i++) { MeshNode node = mesh.Nodes[i]; double exactValue = function(node.X, node.Y); double interpValue = nodeValues[i]; double diff = exactValue - interpValue; // It can only extrapolate when there is at least three elements per node. // When there is two or less elements, the inverse distance weighting takes over // and the results are not correct, so we skip the check here. if (node.Elements.Count > 2 && diff > 1e-6) { string msg = string.Format("{0,2} {6}: {1}-{2}={3} ({4},{5})", i, exactValue, interpValue, diff, node.X, node.Y, node.Elements.Count); Console.Out.WriteLine(msg); Assert.Fail(msg); } } }
/// <summary> /// Make <paramref name="value"/> within limits of circular value. /// </summary> public static double ToCircular(CircularValueTypes circularType, double value) { switch (circularType) { case CircularValueTypes.RadiansPi: if (value > Math.PI) { value -= 2 * Math.PI; } else if (value < -Math.PI) { value += 2 * Math.PI; } break; case CircularValueTypes.Radians2Pi: if (value > 2 * Math.PI) { value -= 2 * Math.PI; } else if (value < 0) { value += 2 * Math.PI; } break; case CircularValueTypes.Degrees180: if (value > 180.0) { value -= 360.0; } else if (value < -180.0) { value += 360.0; } break; case CircularValueTypes.Degrees360: if (value > 360.0) { value -= 360.0; } else if (value < 0.0) { value += 360.0; } break; } return(value); }
public void InterpolationTest(string sourceMeshFileName, string targetMeshFileName, CircularValueTypes cvt = CircularValueTypes.Normal) { // Source mesh MeshFile meshFile = MeshFile.ReadMesh(sourceMeshFileName); MeshData mesh = meshFile.ToMeshData(); mesh.BuildDerivedData(); // Mesh to interpolate to MeshFile targetFile = MeshFile.ReadMesh(targetMeshFileName); MeshData targetmesh = targetFile.ToMeshData(); // Setup interpolator MeshInterpolator2D interpolator = new MeshInterpolator2D(mesh) { CircularType = cvt, AllowExtrapolation = true }; interpolator.SetupNodeInterpolation(); interpolator.SetTarget(targetmesh); // Find reference x and y value as the smallest x and y value double xMin = mesh.Nodes.Select(mn => mn.X).Min(); double xMax = mesh.Nodes.Select(mn => mn.X).Max(); double yMin = mesh.Nodes.Select(mn => mn.Y).Min(); double yMax = mesh.Nodes.Select(mn => mn.Y).Max(); // Function over the (x,y) plane. Func <double, double, double> function = ValueFunction(cvt, xMin, yMin, xMax, yMax); // Calculate element center values of function double[] elmtVals = new double[mesh.Elements.Count]; for (int i = 0; i < mesh.Elements.Count; i++) { MeshElement elmt = mesh.Elements[i]; elmtVals[i] = function(elmt.XCenter, elmt.YCenter); } // Write out bounds, to check we got things right Console.Out.WriteLine("{0,10} (min,max) = ({1},{2})", cvt, elmtVals.Min(), elmtVals.Max()); // Interpolate to nodes double[] targetValues = new double[targetmesh.Elements.Count]; interpolator.InterpolateToTarget(elmtVals, targetValues); // Check node values for (int i = 0; i < targetmesh.Elements.Count; i++) { MeshElement targetElmt = targetmesh.Elements[i]; double exactValue = function(targetElmt.XCenter, targetElmt.YCenter); double interpValue = targetValues[i]; double diff = exactValue - interpValue; // Check if target element has a boundary node. // Nodes on the boundary may not have correctly interpolated value due to // inverse distance interpolation on the boundary, and hence also interpolation // to target element value will not be exact. So only check on those elements that are // fully internal (no boundary nodes). bool internalElmt = targetElmt.Nodes.Select(node => node.Code).All(code => code == 0); if (internalElmt && diff > 1e-6 * Math.Max(Math.Abs(exactValue), 1)) { string msg = string.Format("{0,2} : {1}-{2}={3} ({4},{5})", i, exactValue, interpValue, diff, targetElmt.XCenter, targetElmt.YCenter); Console.Out.WriteLine(msg); Assert.Fail(msg); } } }