/// <summary>
        /// Computes the complex Faddeeva function.
        /// </summary>
        /// <param name="z">The complex argument.</param>
        /// <returns>The complex value of w(z).</returns>
        /// <remarks>
        /// <para>The Faddeeva function w(z) is related to the error function with a complex argument.</para>
        /// <img src="../images/FaddeevaErfcRelation.png" />
        /// <para>It also has an integral representation.</para>
        /// <img src="../images/FaddeevaIntegral.png" />
        /// <para>For purely imaginary values, it reduces to the complementary error function (<see cref="AdvancedMath.Erfc"/>).
        /// For purely real values, it reduces to Dawson's integral (<see cref="AdvancedMath.Dawson"/>).</para>
        /// <para>It appears in the computation of the Voigt line profile function V(x;&#x3C3;,&#x3B3;).</para>
        /// <img src="../images/Voigt.png" />
        /// <para>Near the origin, w(z) &#x2248; 1. To accurately determine w(z) - 1 in this region, use the <see cref="Erf"/>
        /// function. Away from the origin near the large negative imaginary axis, the magnitude w(z) increases rapidly and
        /// may overflow.</para>
        /// <para>The image below shows the complex Faddeeva function near the origin, using domain coloring.</para>
        /// <img src="../images/ComplexFaddeevaPlot.png" />
        /// </remarks>
        /// <seealso cref="AdvancedComplexMath.Erf"/>
        /// <seealso cref="AdvancedMath.Erf" />
        /// <seealso cref="AdvancedMath.Erfc" />
        /// <seealso cref="AdvancedMath.Dawson"/>
        /// <seealso href="http://en.wikipedia.org/wiki/Voigt_profile" />
        public static Complex Faddeeva(Complex z)
        {
            // use reflection formulae to ensure that we are in the first quadrant
            if (z.Im < 0.0)
            {
                return(2.0 * ComplexMath.Exp(-ComplexMath.Sqr(z)) - Faddeeva(-z));
            }
            if (z.Re < 0.0)
            {
                return(Faddeeva(-z.Conjugate).Conjugate);
            }

            double r = ComplexMath.Abs(z);

            if (r < 2.0)
            {
                // use series for small z
                return(ComplexMath.Exp(-ComplexMath.Sqr(z)) * (1.0 - Erf_Series(-ComplexMath.I * z)));
                //return (Faddeeva_Series(z));
            }
            else if ((z.Im < 0.1) && (z.Re < 30.0))
            {
                // this is a special, awkward region
                // along the real axis, Re{w(x)} ~ e^{-x^2}; the Weideman algorithm doesn't compute this small number
                // well and the Laplace continued fraction misses it entirely; therefore very close to the real axis
                // we will use an analytic result on the real axis and Taylor expand to where we need to go.
                // unfortunately the Taylor expansion converges poorly for large x, so we drop this work-around near x~30,
                // when this real part becomes too small to represent as a double anyway
                double x = z.Re;
                double y = z.Im;
                return(Faddeeva_Taylor(new Complex(x, 0.0),
                                       Math.Exp(-x * x) + 2.0 * AdvancedMath.Dawson(x) / Global.SqrtPI * ComplexMath.I,
                                       new Complex(0.0, y)));
            }
            else if (r > 7.0)
            {
                // use Laplace continued fraction for large z
                return(Faddeeva_ContinuedFraction(z));
            }
            else
            {
                // use Weideman algorithm for intermediate region
                return(Faddeeva_Weideman(z));
            }
        }
Пример #2
0
        /*
         * private static double Fadeeva_Weideman_L = Math.Sqrt(20.0 / Math.Sqrt(2.0));
         * private static double[] Faddeeva_Weideman_Coefficients = {
         *  0.145204167876254758882, // 0
         *  0.136392115479649636711,
         *  0.112850364752776356308,
         *  0.081814663814142472504,
         *  0.051454522270816839073,
         *  0.027588397192576112169, // 5
         *  0.012224615682792673110,
         *  0.004202799708104555100,
         *  0.00094232196590738341762,
         *  0.000024250951611669019436,
         * -0.000075616164647923403908, // 10
         * -0.000025048115578198088771,
         *  1.35444636058911259445e-6,
         *  3.1103803514939499680e-6,
         *  4.3812443485181690079e-7,
         * -3.1958626141517655223e-7, // 15
         * -9.9512459304812101824e-8,
         *  3.38225585699344840075e-8,
         *  1.68263253379413070440e-8,
         * -4.206345505308078461584e-9,
         * -2.7027917878529318399463e-9 // 20
         * };
         */

        // The power series for the error function (DLMF 7.6.1)
        //   erf(z) = \frac{2}{\sqrt{\pi}} \sum_{k=0}^{\infty} \frac{ (-1)^k z^{2k + 1} }{(2k+1) k!}
        //          = \frac{2}{\sqrt{\pi}} \left[ z - z^3 / 3 + z^5 / 10 - \cdots \right]
        // requires about 15 terms at |z| ~ 1, 30 terms at |z| ~ 2, 45 terms at |z| ~ 3

        private static Complex Erf_Series(Complex z)
        {
            Complex zp = 2.0 / Global.SqrtPI * z;
            Complex zz = -ComplexMath.Sqr(z);
            Complex f  = zp;

            for (int k = 1; k < Global.SeriesMax; k++)
            {
                Complex f_old = f;
                zp *= zz / k;
                f  += zp / (2 * k + 1);
                if (f == f_old)
                {
                    return(f);
                }
            }
            throw new NonconvergenceException();
        }
Пример #3
0
        private static Complex Sum(Complex z)
        {
            Complex rzPower   = 1.0 / z;
            Complex rzSquared = ComplexMath.Sqr(rzPower);
            Complex f         = 0.5 * AdvancedIntegerMath.Bernoulli[1] * rzPower;

            for (int k = 2; k < AdvancedIntegerMath.Bernoulli.Length; k++)
            {
                Complex f_old = f;
                rzPower *= rzSquared;
                f       += AdvancedIntegerMath.Bernoulli[k] / ((2 * k) * (2 * k - 1)) * rzPower;
                if (f == f_old)
                {
                    return(f);
                }
            }
            throw new NonconvergenceException();
        }
Пример #4
0
        public static Complex AiryAi_Series(Complex z)
        {
            Complex p = AdvancedMath.Ai0;
            Complex q = AdvancedMath.AiPrime0 * z;
            Complex f = p + q;

            Complex z3 = ComplexMath.Sqr(z) * z;

            for (int k = 0; k < Global.SeriesMax; k += 3)
            {
                Complex f_old = f;
                p *= z3 / ((k + 2) * (k + 3));
                q *= z3 / ((k + 3) * (k + 4));
                f += p + q;
                if (f == f_old)
                {
                    return(f);
                }
            }
            throw new NonconvergenceException();
        }
Пример #5
0
        /// <summary>
        /// Computes the complex error function.
        /// </summary>
        /// <param name="z">The complex argument.</param>
        /// <returns>The value of erf(z).</returns>
        /// <remarks>
        /// <para>This function is the analytic continuation of the error function (<see cref="AdvancedMath.Erf"/>) to the complex plane.</para>
        /// <para>The image below shows the complex error function near the origin, using domain coloring.</para>
        /// <img src="../images/ComplexErfPlot.png" />
        /// <para>The complex error function is entire: it has no poles, cuts, or discontinuities anywhere in the complex plane.</para>
        /// <para>For pure imaginary arguments, erf(z) reduces to the Dawson integral (<see cref="AdvancedMath.Dawson"/>).</para>
        /// <para>Away from the origin near the real axis, the real part of erf(z) quickly approaches &#x0b1;1. To accurately determine
        /// the small difference erf(z) &#8723; 1 in this region, use the <see cref="Faddeeva"/> function. Away from the origin near
        /// the imaginary axis, the magnitude of erf(z) increases very quickly. Although erf(z) may overflow in this region, you
        /// can still accurately determine the value of the product erf(z) exp(z<sup>2</sup>) using the <see cref="Faddeeva"/>
        /// function.</para>
        /// </remarks>
        /// <seealso cref="AdvancedMath.Erf"/>
        /// <seealso cref="AdvancedMath.Dawson"/>
        /// <seealso cref="AdvancedComplexMath.Faddeeva"/>
        public static Complex Erf(Complex z)
        {
            double r = ComplexMath.Abs(z);

            if (r < 4.0)
            {
                // near the origin, use the series
                return(Erf_Series(z));
            }
            else
            {
                // otherwise, just compute from Faddeva
                if (z.Re < 0.0)
                {
                    // since Fadddeeva blows up for negative z.Re, use erf(z) = -erf(-z)
                    return(ComplexMath.Exp(-ComplexMath.Sqr(z)) * Faddeeva(-ComplexMath.I * z) - 1.0);
                }
                else
                {
                    return(1.0 - ComplexMath.Exp(-ComplexMath.Sqr(z)) * Faddeeva(ComplexMath.I * z));
                }
                // we don't do this near the origin beause we would loose accuracy in the very small real parts there by subtracting from 1
            }
        }