public void CoulombAgreement() { // The individual CoulombF and CoulombG functions should agree with the Coulomb // function that computes both. foreach (int L in TestUtilities.GenerateIntegerValues(1, 100, 4)) { foreach (double eta in TestUtilities.GenerateRealValues(1.0E-1, 1.0E2, 8)) { foreach (double rho in TestUtilities.GenerateRealValues(1.0E-2, 1.0E4, 16)) { double F = AdvancedMath.CoulombF(L, eta, rho); double G = AdvancedMath.CoulombG(L, eta, rho); SolutionPair s = AdvancedMath.Coulomb(L, eta, rho); Assert.IsTrue(TestUtilities.IsNearlyEqual(F, s.FirstSolutionValue)); // We are producing NaNs for divergent G rathar than infinities if (F == 0.0) { continue; } Assert.IsTrue(TestUtilities.IsNearlyEqual(G, s.SecondSolutionValue)); } } } }
public void CoulombRhoZero() { // L = 0 foreach (double eta in TestUtilities.GenerateRealValues(1.0E-2, 1.0E3, 4)) { foreach (int sign in new int[] { 1, -1 }) { // F is zero Assert.IsTrue(AdvancedMath.CoulombF(0, sign * eta, 0.0) == 0.0); // G is 1/C_{\ell}(\eta), F' = C_{\ell}(eta), so product is 1 SolutionPair s = AdvancedMath.Coulomb(0, sign * eta, 0.0); Assert.IsTrue(TestUtilities.IsNearlyEqual(s.FirstSolutionDerivative * s.SecondSolutionValue, 1.0)); Assert.IsTrue(Double.IsNegativeInfinity(s.SecondSolutionDerivative)); } } // L > 0 foreach (int L in TestUtilities.GenerateIntegerValues(1, 100, 4)) { foreach (int sign in new int[] { +1, -1 }) { foreach (double eta in TestUtilities.GenerateRealValues(1.0E-2, 1.0E3, 8)) { // F is zero, G is infinite Assert.IsTrue(AdvancedMath.CoulombF(L, sign * eta, 0.0) == 0.0); Assert.IsTrue(Double.IsInfinity(AdvancedMath.CoulombG(L, sign * eta, 0.0))); } } } }
public void CoulombRecursion() { foreach (int L in TestUtilities.GenerateIntegerValues(1, 100, 8)) { foreach (double eta in TestUtilities.GenerateRealValues(1.0E-2, 1.0E2, 16)) { foreach (double rho in TestUtilities.GenerateRealValues(1.0E-3, 1.0E3, 32)) { CoulombRecursionHelper(L, eta, rho, AdvancedMath.CoulombF(L - 1, eta, rho), AdvancedMath.CoulombF(L, eta, rho), AdvancedMath.CoulombF(L + 1, eta, rho) ); CoulombRecursionHelper(L, -eta, rho, AdvancedMath.CoulombF(L - 1, -eta, rho), AdvancedMath.CoulombF(L, -eta, rho), AdvancedMath.CoulombF(L + 1, -eta, rho) ); CoulombRecursionHelper(L, eta, rho, AdvancedMath.CoulombG(L - 1, eta, rho), AdvancedMath.CoulombG(L, eta, rho), AdvancedMath.CoulombG(L + 1, eta, rho) ); CoulombRecursionHelper(L, -eta, rho, AdvancedMath.CoulombG(L - 1, -eta, rho), AdvancedMath.CoulombG(L, -eta, rho), AdvancedMath.CoulombG(L + 1, -eta, rho) ); } } } }
public void CoulombAgreement() { // The individual CoulombF and CoulombG functions should agree with the Coulomb // function that computes both. foreach (int L in TestUtilities.GenerateIntegerValues(1, 100, 8)) { foreach (int sign in new int[] { +1, -1 }) { foreach (double eta in TestUtilities.GenerateRealValues(1.0E-1, 1.0E2, 16)) { foreach (double rho in TestUtilities.GenerateRealValues(1.0E-2, 1.0E4, 32)) { double F = AdvancedMath.CoulombF(L, sign * eta, rho); double G = AdvancedMath.CoulombG(L, sign * eta, rho); SolutionPair s = AdvancedMath.Coulomb(L, sign * eta, rho); // Deep in transition region, there are cases where we integrate for full solution pair in order to get G, // then use Wronskian from that G to get F. But if we are just computing F, we use series to get a different // (and more accurate) answer. So we relax agreement criteria a bit. Eventually, would be great to do // series for G everywhere we can do series for F. if (!TestUtilities.IsNearlyEqual(F, s.FirstSolutionValue, 4.0 * TestUtilities.TargetPrecision)) { Console.WriteLine($"L={L} eta={eta} rho={rho} F={F} s.F={s.FirstSolutionValue} b={Double.IsNaN(s.SecondSolutionDerivative)}"); } Assert.IsTrue(TestUtilities.IsNearlyEqual(F, s.FirstSolutionValue, 4.0 * TestUtilities.TargetPrecision)); Assert.IsTrue(TestUtilities.IsNearlyEqual(G, s.SecondSolutionValue)); } } } } }
public void CoulombAtOrigin() { // F is zero at origin for all L and eta Assert.IsTrue(AdvancedMath.CoulombF(0, -1.2, 0.0) == 0.0); Assert.IsTrue(AdvancedMath.CoulombF(5, 4.3, 0.0) == 0.0); // For L = 0, F' and G are finite, non-zero, and related SolutionPair s = AdvancedMath.Coulomb(0, 5.6, 0.0); Assert.IsTrue(s.FirstSolutionValue == 0.0); Assert.IsTrue(TestUtilities.IsNearlyEqual( s.FirstSolutionDerivative * s.SecondSolutionValue, 1.0 )); Assert.IsTrue(s.SecondSolutionDerivative == Double.NegativeInfinity); }
public void CoulombEtaZero() { // For \eta = 0, Coulomb wave functions reduce to spherical Bessel functions foreach (int L in TestUtilities.GenerateIntegerValues(1, 100, 8)) { foreach (double rho in TestUtilities.GenerateRealValues(1.0E-2, 1.0E4, 16)) { double F = AdvancedMath.CoulombF(L, 0, rho); double j = AdvancedMath.SphericalBesselJ(L, rho); Assert.IsTrue(TestUtilities.IsNearlyEqual(F, rho * j, 16.0 * TestUtilities.TargetPrecision)); double G = AdvancedMath.CoulombG(L, 0, rho); double y = AdvancedMath.SphericalBesselY(L, rho); Assert.IsTrue(TestUtilities.IsNearlyEqual(G, -rho * y, 32.0 * TestUtilities.TargetPrecision)); } } }
private static Complex[] GetCoulombWaveArray( int quantumNumberL, double alphaEff, double quarkMass_MeV, double waveVector_fm, double[] radius_fm ) { double twoOverPi = Math.Sqrt(2 / Math.PI); double eta = -0.5 * alphaEff * quarkMass_MeV / waveVector_fm / Constants.HbarC_MeV_fm; Complex[] coulombWave = new Complex[radius_fm.Length]; for (int j = 0; j < radius_fm.Length; j++) { coulombWave[j] = new Complex(twoOverPi * AdvancedMath.CoulombF(quantumNumberL, eta, waveVector_fm * radius_fm[j]), 0); } return(coulombWave); }