// 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); }
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); }
// 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); }
// 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); }