/// <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;
            }
        }
Пример #3
0
        /// <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);
                }
            }
        }
Пример #5
0
        /// <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);
                }
            }
        }