// series for L=0 for both F and G // this has the same convergence properties as the L != 0 series for F above private static void Coulomb_Zero_Series(double eta, double rho, out double F, out double FP, out double G, out double GP) { if (rho == 0.0) { double C = CoulombFactorZero(eta); F = 0.0; FP = C; G = 1.0 / C; GP = Double.NegativeInfinity; return; } double eta_rho = eta * rho; double rho_2 = rho * rho; double u0 = 0.0; double u1 = rho; double u = u0 + u1; double up = u1; double v0 = 1.0; double v1 = 0.0; double v = v0 + v1; for (int n = 2; n <= Global.SeriesMax; n++) { double u2 = (2.0 * eta_rho * u1 - rho_2 * u0) / (n * (n - 1)); double v2 = (2.0 * eta_rho * v1 - rho_2 * v0 - 2.0 * eta * (2 * n - 1) * u2) / (n * (n - 1)); double u_old = u; u += u2; up += n * u2; double v_old = v; v += v2; if ((u == u_old) && (v == v_old)) { double C = CoulombFactorZero(eta); F = C * u; FP = C * up / rho; double r = AdvancedComplexMath.Psi(new Complex(1.0, eta)).Re + 2.0 * AdvancedMath.EulerGamma - 1.0; G = (v + 2.0 * eta * u * (Math.Log(2.0 * rho) + r)) / C; GP = (FP * G - 1.0) / F; return; } u0 = u1; u1 = u2; v0 = v1; v1 = v2; } throw new NonconvergenceException(); }
// asymptotic region private static void Coulomb_Asymptotic(double L, double eta, double rho, out double F, out double G) { // compute phase // reducing the eta = 0 and eta != 0 parts seperately preserves accuracy for large rho and small eta double t0 = Reduce(rho, -L / 4.0); double t1 = Reduce(AdvancedComplexMath.LogGamma(new Complex(L + 1.0, eta)).Im - eta * Math.Log(2.0 * rho), 0.0); double t = t0 + t1; double s = Math.Sin(t); double c = Math.Cos(t); // determine the weights of sin and cos double f0 = 1.0; double g0 = 0.0; double f = f0; double g = g0; for (int k = 0; true; k++) { // compute the next contributions to f and g double q = 2 * (k + 1) * rho; double a = (2 * k + 1) * eta / q; double b = ((L * (L + 1) - k * (k + 1)) + eta * eta) / q; double f1 = a * f0 - b * g0; double g1 = a * g0 + b * f0; // add them double f_old = f; f += f1; double g_old = g; g += g1; if ((f == f_old) && (g == g_old)) { break; } // check for non-convergence if (k > Global.SeriesMax) { throw new NonconvergenceException(); } // prepare for the next iteration f0 = f1; g0 = g1; } F = g * c + f * s; G = f * c - g * s; }
/// <summary> /// Computes the Riemann zeta function for complex values. /// </summary> /// <param name="z">The argument.</param> /// <returns>The value of ζ(z).</returns> /// <remarks> /// <para>As the imaginary part of the argument increases, the computation of the zeta function becomes slower and more difficult. /// The computation time is approximately proprotional to the imaginary part of z. The result also slowly looses accuracy for arguments with /// very large imaginary parts; for arguments with z.Im of order 10^d, approximately the last d digits of the result are suspect.</para> /// <para>The image below shows the complex Γ function near the origin using domain coloring. You can see the first non-trivial /// zeros at (1/2, ±14.13...) as well as the trivial zeros along the negative real axis.</para> /// <img src="../images/ComplexRiemannZetaPlot.png" /> /// </remarks> public static Complex RiemannZeta(Complex z) { // Use conjugation and reflection symmetry to move to the first quadrant. if (z.Im < 0.0) { return(RiemannZeta(z.Conjugate).Conjugate); } if (z.Re < 0.0) { Complex zp = Complex.One - z; return(2.0 * ComplexMath.Pow(Global.TwoPI, -zp) * ComplexMath.Cos(Global.HalfPI * zp) * AdvancedComplexMath.Gamma(zp) * RiemannZeta(zp)); } // Close to pole, use Laurent series. Complex zm1 = z - Complex.One; if (ComplexMath.Abs(zm1) < 0.50) { return(RiemannZeta_LaurentSeries(zm1)); } // Fall back to Euler-Maclaurin summation. int n = RiemannZeta_EulerMaclaurin_N(z.Re, z.Im); return(RiemannZeta_EulerMaclaurin(z, n)); }
// asymptotic region private static SolutionPair Coulomb_Asymptotic(double L, double eta, double rho) { // Abromowitz & Stegun 14.5 describes this asymptotic expansion /* * double F, G; * Coulomb_Asymptotic(L, eta, rho, out F, out G); * return (new SolutionPair(F, 0.0, G, 0.0)); */ /* * // Old reduction algorithm * double t0 = Reduce(rho, -L / 4.0); * double t1 = Reduce(AdvancedComplexMath.LogGamma(new Complex(L + 1.0, eta)).Im - eta * Math.Log(2.0 * rho), 0.0); * double t = t0 + t1; * double s = Math.Sin(t); * double c = Math.Cos(t); */ /* * // New reduction algorithm * double t = AdvancedComplexMath.LogGamma(new Complex(L + 1.0, eta)).Im - eta * Math.Log(2.0 * rho) + rho; * double s = MoreMath.Sin(t); * double c = MoreMath.Cos(t); * * int r = -((int) L) % 4; * if (r < 0) r += 4; * * switch (r) { * case 0: * // no change * break; * case 1: * Global.Swap(ref s, ref c); * c = -c; * break; * case 2: * s = -s; * c = -c; * break; * case 3: * Global.Swap(ref s, ref c); * s = -s; * break; * default: * throw new InvalidOperationException(); * }; */ long t00; double t01; RangeReduction.ReduceByPiHalves(rho, out t00, out t01); long t10; double t11; RangeReduction.ReduceByPiHalves(AdvancedComplexMath.LogGamma(new Complex(L + 1.0, eta)).Im - eta * Math.Log(2.0 * rho), out t10, out t11); long t20; double t1; RangeReduction.ReduceByOnes(t01 + t11, out t20, out t1); long t0 = t00 + t10 + t20 - (long)L; double s = RangeReduction.Sin(t0, t1); double c = RangeReduction.Cos(t0, t1); /* * long tt0; double tt1; * RangeReduction.ReduceByPiHalves(rho - eta * Math.Log(2.0 * rho) + AdvancedComplexMath.LogGamma(new Complex(L + 1.0, eta)).Im, out tt0, out tt1); * * tt0 -= (long) L; * * double s = RangeReduction.Sin(tt0, tt1); * double c = RangeReduction.Cos(tt0, tt1); */ /* * // compute phase * // reducing the eta = 0 and eta != 0 parts seperately preserves accuracy for large rho and small eta * double t0 = Reduce(rho, -L / 4.0); * double t1 = Reduce(AdvancedComplexMath.LogGamma(new Complex(L + 1.0, eta)).Im - eta * Math.Log(2.0 * rho), 0.0); * double t = t0 + t1; * double s = Math.Sin(t); * double c = Math.Cos(t); */ // determine the weights of sin and cos double f0 = 1.0; double g0 = 0.0; double fp0 = 0.0; double gp0 = 1.0 - eta / rho; double f = f0; double g = g0; double fp = fp0; double gp = gp0; for (int k = 0; k < Global.SeriesMax; k++) { // Remember the old values for comparison double f_old = f; double g_old = g; double fp_old = fp; double gp_old = gp; // Compute the next corrections double q = 2 * (k + 1) * rho; double a = (2 * k + 1) * eta / q; double b = ((L * (L + 1) - k * (k + 1)) + eta * eta) / q; double f1 = a * f0 - b * g0; double g1 = a * g0 + b * f0; double fp1 = a * fp0 - b * gp0 - f1 / rho; double gp1 = a * gp0 + b * fp0 - g1 / rho; // Apply them f += f1; g += g1; fp += fp1; gp += gp1; // Test for convergence if ((f == f_old) && (g == g_old) && (fp == fp_old)) { return(new SolutionPair( g * c + f * s, gp * c + fp * s, f * c - g * s, fp * c - gp * s) ); } // Prepare for the next iteration f0 = f1; g0 = g1; fp0 = fp1; gp0 = gp1; } throw new NonconvergenceException(); }
// asymptotic region private static SolutionPair Coulomb_Asymptotic(double L, double eta, double rho) { // Abromowitz & Stegun 14.5 describes this asymptotic expansion RangeReduction.ReduceByPiHalves(rho, out long t00, out double t01); RangeReduction.ReduceByPiHalves(AdvancedComplexMath.LogGamma(new Complex(L + 1.0, eta)).Im - eta * Math.Log(2.0 * rho), out long t10, out double t11); RangeReduction.ReduceByOnes(t01 + t11, out long t20, out double t1); long t0 = t00 + t10 + t20 - (long)L; double s = RangeReduction.Sin(t0, t1); double c = RangeReduction.Cos(t0, t1); // determine the weights of sin and cos double f0 = 1.0; double g0 = 0.0; double fp0 = 0.0; double gp0 = 1.0 - eta / rho; double f = f0; double g = g0; double fp = fp0; double gp = gp0; for (int k = 0; k < Global.SeriesMax; k++) { // Remember the old values for comparison double f_old = f; double g_old = g; double fp_old = fp; //double gp_old = gp; // Compute the next corrections double q = 2 * (k + 1) * rho; double a = (2 * k + 1) * eta / q; double b = ((L * (L + 1) - k * (k + 1)) + eta * eta) / q; double f1 = a * f0 - b * g0; double g1 = a * g0 + b * f0; double fp1 = a * fp0 - b * gp0 - f1 / rho; double gp1 = a * gp0 + b * fp0 - g1 / rho; // Apply them f += f1; g += g1; fp += fp1; gp += gp1; // Test for convergence if ((f == f_old) && (g == g_old) && (fp == fp_old)) { return(new SolutionPair( g * c + f * s, gp * c + fp * s, f * c - g * s, fp * c - gp * s) ); } // Prepare for the next iteration f0 = f1; g0 = g1; fp0 = fp1; gp0 = gp1; } throw new NonconvergenceException(); }