예제 #1
0
        // Elliptic integrals (complete and incomplete) of the first kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Returns the incomplete elliptic integral of the first kind
        /// <para>F(φ, k) = F(φ | k^2) = F(sin(φ); k)</para>
        /// <para>F(φ, k) = ∫ dθ/sqrt(1-k^2*sin^2(θ)), θ={0,φ}</para>
        /// <para>F(x; k) = ∫ dt/sqrt((1-t^2)*(1-k^2*t^2)), t={0,x}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        public static double EllintF(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintF(k: {0}, phi: {1}): Requires |k| <= 1; phi not NaN", k, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi);
            }

            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintF(k: {0}, phi: {1}): |phi| > {2} not implemented", k, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

#if EXTRA_DEBUG
            Debug.WriteLine("EllintF(phi = {0}; k = {1})", phi, k);
#endif

            // special values
            if (k == 0)
            {
                return(phi);
            }
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintK(k));
            }


            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // F(k, phi + π*mult) = F(k, phi) + 2*mult*K(k)

            double result = 0;
            double rphi   = Math.Abs(phi);
            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if (mult != 0)
                {
                    result += mult * EllintK(k);
                }
            }


            double sinp = Math.Sin(rphi);
            double cosp = Math.Cos(rphi);

            double k2 = k * k;
            double x  = cosp * cosp;
            double y  = 1 - k2 * sinp * sinp;
            if (y > 0.125)   // use a more accurate form with less cancellation
            {
                y = (1 - k2) + k2 * x;
            }
            double z = 1;

            result += sinp * EllintRF(x, y, z);

            return((phi < 0) ? -result : result);
        }
예제 #2
0
        public static double Imp(double k, double n, double nc, double phi)
        {
            // Note arg nc = 1-n, possibly without cancellation errors

            Debug.Assert(k >= -1 && k <= 1, "Requires |k| <= 1: k = " + k);

            // See: http://dlmf.nist.gov/19.6#iv
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintPi(k, n));
            }

            if (n == 0)
            {
                if (k == 0)
                {
                    return(phi);
                }
                return(Math2.EllintF(k, phi));
            }


            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // Π(k, n, phi + π*mult) = Π(k, n, phi) + 2*mult*Π(k,n)


            double result = 0;
            double rphi   = Math.Abs(phi);

            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if ((mult > 0) && (nc > 0))
                {
                    result = mult * _EllintPi.Imp(k, n, nc);
                }
            }


            if (k == 0)
            {
                double ncr;

                // A&S 17.7.20:
                if (n < 1)
                {
                    if (nc == 1)
                    {
                        return(phi);
                    }
                    ncr     = Math.Sqrt(nc);
                    result += Math.Atan(ncr * Math.Tan(rphi)) / ncr;
                }
                else if (n == 1)
                {
                    result += Math.Tan(rphi);
                }
                else
                {
                    // n > 1:
                    ncr     = Math.Sqrt(-nc);
                    result += Math2.Atanh(ncr * Math.Tan(rphi)) / ncr;
                }

                return((phi < 0) ? -result : result);
            }


            double sphi = Math.Sin(Math.Abs(phi));

            if (n > 1 / (sphi * sphi))
            {
                // Complex result is a domain error:
                Policies.ReportDomainError("EllintPi(k: {0}, nu: {1}, phi: {2}): Complex results not supported. Requires n > 1 / sin^2(phi)", k, n, phi);
                return(double.NaN);
            }


            // Special cases first:

            if (n < 0)
            {
                //
                // If we don't shift to 0 <= n <= 1 we get
                // cancellation errors later on.  Use
                // A&S 17.7.15/16 to shift to n > 0:
                //
                double k2 = k * k;
                double N  = (k2 - n) / nc;
                double Nc = (1 - k2) / nc; // Nc = 1-N = (1-k^2)/nc

                // check to see if a rounding error occurred
                //  k^2 <= N <= 1
                if (N < k2)
                {
#if EXTRA_DEBUG
                    Debug.WriteLine("Rounding error: EllintPi(k: {0} , nu: {1} , phi: {2})", k, n, phi);
#endif
                    N  = k2;
                    Nc = 1 - N;
                }

                double p2    = Math.Sqrt(-n * N);
                double delta = Math.Sqrt(1 - k2 * sphi * sphi);


                // Reduce A&S 17.7.15/16
                // Mathematica eqns below
                // N is protected in Mathematica, so use V
                // V = (k2 - n)/(1 - n)
                // Assuming[(k2 >= 0 && k2 <= 1) && n < 0,  FullSimplify[Sqrt[(1 - V)*(1 - k2/V)]/Sqrt[((1 - n)*(1 - k2/n))]]]
                // Result: ((-1 + k2) n)/((-1 + n) (-k2 + n))

                // Assuming[(k2 >= 0 && k2 <= 1) && n < 0,  FullSimplify[k2/(Sqrt[-n*(k2 - n)/(1 - n)]*Sqrt[(1 - n)*(1 - k2/n)])]]
                // Result: k2/(k2 - n)

                // Assuming[(k2 >= 0 && k2 <= 1) && n < 0,  FullSimplify[Sqrt[1/((1 - n)*(1 - k2/n))]]]
                // Result: Sqrt[n/((k2 - n) (-1 + n))]

                double nResult = -(n / nc) * ((1 - k2) / (k2 - n)) * _EllintPi.Imp(k, N, Nc, rphi);
                nResult += k2 / (k2 - n) * Math2.EllintF(k, rphi);
                nResult += Math.Sqrt((n / nc) / (n - k2)) * Math.Atan((p2 / (2 * delta)) * Math.Sin(2 * rphi));

                result += nResult;

                return((phi < 0) ? -result : result);
            }


            double sinp = Math.Sin(rphi);
            double cosp = Math.Cos(rphi);
            double x    = cosp * cosp;
            double t    = sinp * sinp;
            double y    = 1 - k * k * t;
            double z    = 1;
            double p    = (n * t < 0.5) ? 1 - n * t : x + nc * t;

            result += sinp * (Math2.EllintRF(x, y, z) + n * t * Math2.EllintRJ(x, y, z, p) / 3);

            return((phi < 0) ? -result : result);
        }
예제 #3
0
        // Elliptic integrals (complete and incomplete) of the second kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Returns the incomplete elliptic integral of the second kind E(φ, k)
        /// <para>E(φ, k) =  ∫ sqrt(1-k^2*sin^2(θ)) dθ, θ={0,φ}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        public static double EllintE(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintE(k: {0}, phi: {1}): Requires |k| <= 1; phi not NaN", k, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi);
            }

            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintE(k: {0}, phi: {1}): |phi| > {2} not implemented", k, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

            // special values
            if (k == 0)
            {
                return(phi);
            }
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintE(k));
            }

            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // E(k, phi + π*mult) = E(k, phi) + 2*mult*E(k)

            double result = 0;
            double rphi   = Math.Abs(phi);

            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if (mult != 0)
                {
                    result += mult * EllintE(k);
                }
            }

            if (k == 1)
            {
                result += Math.Sin(rphi);
            }
            else
            {
                double k2   = k * k;
                double sinp = Math.Sin(rphi);
                double cosp = Math.Cos(rphi);
                double x    = cosp * cosp;
                double t    = k2 * sinp * sinp;
                double y    = (t < 0.875) ? 1 - t : (1 - k2) + k2 * x;
                double z    = 1;

                result += sinp * (Math2.EllintRF(x, y, z) - t * Math2.EllintRD(x, y, z) / 3);
            }

            return((phi < 0) ? -result : result);
        }
예제 #4
0
        // Elliptic integrals (complete and incomplete) of the second kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Returns the incomplete elliptic integral of the second kind D(φ, k)
        /// <para>D(φ, k) =  ∫ sin^2(θ)/sqrt(1-k^2*sin^2(θ)) dθ, θ={0,φ}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        public static double EllintD(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintD(k: {0}, phi: {1}): Requires |k| <= 1; phi not NaN", k, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi); // Note: result != phi, only +/- infinity
            }
            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintD(k: {0}, phi: {1}): |phi| > {2} not implemented", k, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

            // special values
            //if (k == 0) // too much cancellation error near phi=0, general case works fine
            //    return (phi - Cos(phi)*Sin(phi))/2
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintD(k));
            }

            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // D(k, phi + π*mult) = D(k, phi) + 2*mult*D(k)

            double result = 0;
            double rphi   = Math.Abs(phi);

            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if (mult != 0)
                {
                    result += mult * EllintD(k);
                }
            }

            double k2   = k * k;
            double sinp = Math.Sin(rphi);
            double cosp = Math.Cos(rphi);
            double x    = cosp * cosp;
            double t    = k2 * sinp * sinp;
            double y    = (t < 0.875) ? 1 - t : (1 - k2) + k2 * x;
            double z    = 1;

            // http://dlmf.nist.gov/19.25#E13
            // and RD(lambda*x, lambda*y, lambda*z) = lambda^(-3/2) * RD(x, y, z)
            result += EllintRD(x, y, z) * sinp * sinp * sinp / 3;

            return((phi < 0) ? -result : result);
        }