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); }
/// <summary> /// Carlson's symmetric form of elliptical integrals /// <para>R<sub>J</sub>(x,y,z,p) = (3/2) * ∫ dt/((t+p)*Sqrt((t+x)(t+y)(t+z))), t={0,∞}</para> /// </summary> /// <param name="x">Argument. Requires finite x >= 0</param> /// <param name="y">Argument. Requires finite y >= 0</param> /// <param name="z">Argument. Requires finite z >= 0</param> /// <param name="p">Argument. Requires finite p != 0</param> public static double EllintRJ(double x, double y, double z, double p) { if ((!(x >= 0) || double.IsInfinity(x)) || (!(y >= 0) || double.IsInfinity(y)) || (!(z >= 0) || double.IsInfinity(z)) || (p == 0 || double.IsInfinity(p) || double.IsNaN(p)) || (x + y == 0 || y + z == 0 || z + x == 0)) { Policies.ReportDomainError("EllintRJ(x: {0}, y: {1}, z: {2}, p: {3}) requires: finite x,y,z >= 0; finite p != 0; at most one of x,y,z == 0", x, y, z, p); return(double.NaN); } // See Carlson, Numerische Mathematik, vol 33, 1 (1979) double value; // error scales as the 6th power of tolerance const double tolerance = 0.002049052858245406612358440409548690638254492642397575328974; Debug.Assert(Math2.AreNearUlps(Math.Pow(DoubleLimits.MachineEpsilon / 3, 1.0 / 6.0), tolerance, 5), "Incorrect constant"); // reorder x,y,z such that x <= y <= z // so that 0 shows up in x first // and so that the p<0 case below doesn't suffer cancellation errors if (x > y) { Utility.Swap(ref x, ref y); } if (y > z) { Utility.Swap(ref y, ref z); } if (x > y) { Utility.Swap(ref x, ref y); } // for p < 0, the integral is singular, return Cauchy principal value if (p < 0) { // We must ensure that (z - y) * (y - x) is positive. Debug.Assert(x <= y && y <= z, "x, y, z must be in ascending order"); double q = -p; double pmy = (z - y) * (y - x) / (y + q); // p - y Debug.Assert(pmy >= 0); double pAdj = pmy + y; value = pmy * Math2.EllintRJ(x, y, z, pAdj); value -= 3 * Math2.EllintRF(x, y, z); value += 3 * Math.Sqrt((x * y * z) / (x * z + pAdj * q)) * Math2.EllintRC(x * z + pAdj * q, pAdj * q); value /= (y + q); return(value); } if (x == 0) { if (y == 0) //RJ(0, 0, z, p) { return(double.PositiveInfinity); } if (y == z) //RJ(0, y, y, p) { return(3 * (Math.PI / 2) / (y * Math.Sqrt(p) + p * Math.Sqrt(y))); } } if (p == z) { if (z == y) { if (x == y) { return(1 / (x * Math.Sqrt(x))); // RJ(x, x, x, x) } return(EllintRD(x, y, y)); // RJ(x, y, y, y) } return(EllintRD(x, y, z)); // RJ(x, y, z, z) } if (x == y && y == z) { return(EllintRD(p, p, x)); // RJ(x, x, x, p) } // duplication double sigma = 0; double factor = 1; int k = 1; for (; k < Policies.MaxSeriesIterations; k++) { double u = (x + y + z + p + p) / 5; double X = (u - x); double Y = (u - y); double Z = (u - z); double P = (u - p); // Termination condition: double utol = u * tolerance; if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol && Math.Abs(P) < utol) { X /= u; Y /= u; Z /= u; P /= u; // Taylor series expansion to the 5th order double EA = X * Y + Y * Z + Z * X; double EB = X * Y * Z; double EC = P * P; double E2 = EA - 3 * EC; double E3 = EB + 2 * P * (EA - EC); double S1 = 1 + E2 * (E2 * (9.0 / 88) - E3 * (9.0 / 52) - 3.0 / 14); double S2 = EB * (1.0 / 6 + P * (-6.0 / 22 + P * (3.0 / 26))); double S3 = P * ((EA - EC) / 3 - P * EA * (3.0 / 22)); value = 3 * sigma + factor * (S1 + S2 + S3) / (u * Math.Sqrt(u)); return(value); } double sx = Math.Sqrt(x); double sy = Math.Sqrt(y); double sz = Math.Sqrt(z); double lambda = sy * (sx + sz) + sz * sx; double alpha = p * (sx + sy + sz) + sx * sy * sz; alpha *= alpha; double beta = p * (p + lambda) * (p + lambda); sigma += factor * Math2.EllintRC(alpha, beta); factor /= 4; x = (x + lambda) / 4; y = (y + lambda) / 4; z = (z + lambda) / 4; p = (p + lambda) / 4; } Policies.ReportDomainError("EllintRJ(x: {0}, y: {1}, z: {2}, p: {3}) No convergence after {4} iterations", x, y, z, p, k); return(double.NaN); }
// Elliptic integrals (complete and incomplete) of the third kind // Carlson, Numerische Mathematik, vol 33, 1 (1979) public static double Imp(double k, double n, double nc) { // Note arg nc = 1-n, possibly without cancellation errors Debug.Assert(k >= -1 && k <= 1, "Requires |k| <= 1: k = " + k); // special values // See: http://dlmf.nist.gov/19.6.E3 if (n == 0) { if (k == 0) { return(Math.PI / 2); } return(Math2.EllintK(k)); } // special values // See: http://functions.wolfram.com/EllipticIntegrals/EllipticPi/03/01/01/ if (k == 0) { return((Math.PI / 2) / Math.Sqrt(nc)); } if (k == 1) { return(double.NegativeInfinity / Math.Sign(n - 1)); } if (n < 0) { // when the magnitude of n gets very large, N below becomes ~= 1, so // there can be some error, so use the series at n=Infinity // http://functions.wolfram.com/EllipticIntegrals/EllipticPi/06/01/05/ if (n < -1 / DoubleLimits.MachineEpsilon) { return((Math.PI / 2) / Math.Sqrt(-n) + (Math2.EllintE(k) - Math2.EllintK(k)) / n); } // A&S 17.7.17: // shift to k^2 < N < 1 double k2 = k * k; double N = (k2 - n) / nc; double Nc = (1 - k2) / nc; // Nc = 1-N double result = k2 / (k2 - n) * Math2.EllintK(k); result -= (n / nc) * ((1 - k2) / (k2 - n)) * _EllintPi.Imp(k, N, Nc); return(result); } double x = 0; double y = 1 - k * k; double z = 1; double p = nc; double value = Math2.EllintK(k) + n * Math2.EllintRJ(x, y, z, p) / 3; return(value); }