Repository: PlasmaPy/PlasmaPy
Pull request #1084 (2021-03-17 11:57:39)

Add Chandrasekhar G function + tests through pytest-regressions
Another addition from #1059. I still have to write a litte more about this in the docstring, and the tidbit I currently have in there is a direct quote from Helander & Sigmar's book on collisional transport. Will fix later.

This also adds `pytest-regressions`, a handy plugin for writing regression tests. It provides the `num_regression` fixture, which takes care of saving/loading data into/from an attached CSV file.

- [x] I have added a changelog entry for this pull request.
- [x] If adding new functionality, I have added tests and
      docstrings.
- [x] I have fixed any newly failing tests.

codecov[bot]: # [Codecov](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084?src=pr&el=h1) Report
> Merging [#1084](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084?src=pr&el=desc) (afa3b8f) into [master](https://codecov.io/gh/PlasmaPy/PlasmaPy/commit/79b2ad7f7711dfa1376358e23cba51897eaf5fd1?el=desc) (79b2ad7) will **decrease** coverage by `0.58%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084/graphs/tree.svg?width=650&height=150&src=pr&token=LzZ5mnXVad)](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #1084      +/-   ##
==========================================
- Coverage   96.91%   96.32%   -0.59%     
==========================================
  Files          70       70              
  Lines        6929     6934       +5     
==========================================
- Hits         6715     6679      -36     
- Misses        214      255      +41     
```


| [Impacted Files](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [plasmapy/formulary/mathematics.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084/diff?src=pr&el=tree#diff-cGxhc21hcHkvZm9ybXVsYXJ5L21hdGhlbWF0aWNzLnB5) | `100.00% <100.00%> (ø)` | |
| [plasmapy/simulation/particletracker.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084/diff?src=pr&el=tree#diff-cGxhc21hcHkvc2ltdWxhdGlvbi9wYXJ0aWNsZXRyYWNrZXIucHk=) | `36.20% <0.00%> (-62.07%)` | :arrow_down: |
| [plasmapy/plasma/sources/plasma3d.py](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084/diff?src=pr&el=tree#diff-cGxhc21hcHkvcGxhc21hL3NvdXJjZXMvcGxhc21hM2QucHk=) | `93.75% <0.00%> (-6.25%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084?src=pr&el=footer). Last update [79b2ad7...afa3b8f](https://codecov.io/gh/PlasmaPy/PlasmaPy/pull/1084?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

StanczakDominik: Oh yeah, I should latexify the equation. On it in a sec...

Patch:
diff --git a/.github/labeler.yml b/.github/labeler.yml
index c41e1493..8a156d5a 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -1,3 +1,5 @@
+Continuous integration: .github/**/*
+
 Diagnostics: plasmapy/diagnostics/**/*
 
 Formulary: plasmapy/formulary/**/*
@@ -8,10 +10,17 @@ parameters.quantum: plasmapy/formulary/quantum.py
 
 Particles: plasmapy/particles/**/*
 
-Documentation: docs/**/*
+# Add Documentation label to any change in docs but notebooks
+Documentation:
+- any: ['docs/**/*']
+  all: ['!docs/notebooks/**/*']
+
+Notebooks: docs/notebooks/**/*
 
 Plasma class: plasmapy/plasma/**/*
 
 simulations: plasmapy/simulations/**/*
 
 Utilities: plasmapy/utils/**/*
+
+Breaking: changelog/*.breaking*rst
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index 32fd2204..54435d6d 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -6,19 +6,15 @@ on:
     branches: [master]
 
 jobs:
-  pre-commit:
-    name: pre-commit
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v2
-    - uses: actions/setup-python@v2
-    - uses: pre-commit/action@v2.0.0
-  gitHubActionForFlake8:
+  flake8:
     name: flake8
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@master
-    - name: GitHub Action for Flake8
-      uses: cclauss/GitHub-Action-for-Flake8@master
-      with:
-        args: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --extend-exclude ".jupyter"
+    - uses: actions/setup-python@v2
+    - name: Install requirements
+      run: pip install flake8 pydocstyle flake8-rst-docstrings pygments
+    - name: Print debug info
+      run: flake8 --bug-report
+    - name: Run flake8
+      run: flake8 . --count --show-source --statistics
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 00000000..0fad7073
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,18 @@
+name: 'Close stale issues and PR'
+on:
+  schedule:
+    - cron: '30 1 * * *'
+
+jobs:
+  stale:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/stale@v3
+        with:
+          repo-token: ${{ secrets.GITHUB_TOKEN }}
+          stale-pr-message: 'This pull request will be closed in 14 days due to a year of inactivity unless the stale label or comment is removed.'
+          close-pr-message: 'This pull request was closed because it has had no activity for the past year.'
+          days-before-pr-stale: 365
+          days-before-pr-close: 15
+          # never close issues
+          days-before-close: -1
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 48f46ff3..3f292aef 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,3 +1,5 @@
+ci:
+  autofix_prs: false
 repos:
 -   repo: https://github.com/pre-commit/pre-commit-hooks
     rev: v3.1.0
@@ -23,7 +25,7 @@ repos:
     hooks:
     -   id: black
 - repo: https://github.com/nbQA-dev/nbQA
-  rev: 0.5.7
+  rev: 0.6.0
   hooks:
     - id: nbqa-black
       additional_dependencies: [black==19.10b0]
diff --git a/README.md b/README.md
index f3830c5b..0cd9db14 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ # PlasmaPy
 
 [![GitHub Actions — CI](https://github.com/PlasmaPy/PlasmaPy/workflows/CI/badge.svg)](https://github.com/PlasmaPy/PlasmaPy/actions?query=workflow%3ACI+branch%3Amaster)
 [![GitHub Actions — Style linters](https://github.com/PlasmaPy/PlasmaPy/workflows/Style%20linters/badge.svg)](https://github.com/PlasmaPy/PlasmaPy/actions?query=workflow%3AStyle-linters+branch%3Amaster)
+[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/PlasmaPy/PlasmaPy/master.svg)](https://results.pre-commit.ci/latest/github/PlasmaPy/PlasmaPy/master)
 [![codecov](https://codecov.io/gh/PlasmaPy/PlasmaPy/branch/master/graph/badge.svg)](https://codecov.io/gh/PlasmaPy/PlasmaPy)
 [![Read the Docs Status](https://readthedocs.org/projects/plasmapy/badge/?version=latest&logo=twitter)](http://plasmapy.readthedocs.io/en/latest/?badge=latest)
 [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/PlasmaPy/PlasmaPy/master?filepath=docs/notebooks)
diff --git a/changelog/1062.trivial.2.rst b/changelog/1062.trivial.2.rst
new file mode 100644
index 00000000..f674c02b
--- /dev/null
+++ b/changelog/1062.trivial.2.rst
@@ -0,0 +1,2 @@
+Rework flake8 configuration in CI, which was previously only checking a few
+errors due to a misconfiguration.
diff --git a/changelog/1062.trivial.3.rst b/changelog/1062.trivial.3.rst
new file mode 100644
index 00000000..a62fb1ec
--- /dev/null
+++ b/changelog/1062.trivial.3.rst
@@ -0,0 +1,3 @@
+Add flake8-rst-docstrings to catch RST formatting
+errors in documentation in the linter stage of
+continuous integration.
diff --git a/changelog/1062.trivial.rst b/changelog/1062.trivial.rst
new file mode 100644
index 00000000..87dd6d91
--- /dev/null
+++ b/changelog/1062.trivial.rst
@@ -0,0 +1,2 @@
+Add pydocstyle to continuous integration, to
+hopefully make writing prettier docstrings easier.
diff --git a/changelog/1084.feature.rst b/changelog/1084.feature.rst
new file mode 100644
index 00000000..303115fa
--- /dev/null
+++ b/changelog/1084.feature.rst
@@ -0,0 +1,2 @@
+Added `~plasmapy.formulary.mathematics.Chandrasekhar_G` function, which is
+helpful in neoclassical transport theory.
diff --git a/changelog/1084.trivial.rst b/changelog/1084.trivial.rst
new file mode 100644
index 00000000..fe9e98f9
--- /dev/null
+++ b/changelog/1084.trivial.rst
@@ -0,0 +1,3 @@
+Added `pytest-regressions
+<https://pytest-regressions.readthedocs.io/en/latest/>`__ to testing
+dependencies, to make regression tests a little easier to write.
diff --git a/changelog/1101.breaking.rst b/changelog/1101.breaking.rst
new file mode 100644
index 00000000..9e871fa2
--- /dev/null
+++ b/changelog/1101.breaking.rst
@@ -0,0 +1,3 @@
+Remove alias: `tfds_` to `~plasmapy.dispersion.two_fluid_dispersion_solution`,
+with the reasoning behind the removal outlined in `#1101
+<https://github.com/PlasmaPy/PlasmaPy/pull/1101#issue-608337879>`_
diff --git a/changelog/1104.trivial.rst b/changelog/1104.trivial.rst
new file mode 100644
index 00000000..b7cf0d78
--- /dev/null
+++ b/changelog/1104.trivial.rst
@@ -0,0 +1,1 @@
+Upgrade nbqa to latest available version (0.6.0)
diff --git a/changelog/1106.trivial.rst b/changelog/1106.trivial.rst
new file mode 100644
index 00000000..71346199
--- /dev/null
+++ b/changelog/1106.trivial.rst
@@ -0,0 +1,4 @@
+Move our custom pre-commit style testing suite to pre-commit.ci,
+taking advantage of the new `pre-commit.ci autofix` command that
+allows manually calling for pre-commit to be run. Simply type
+that as a comment to a PR.
diff --git a/plasmapy/dispersion/__init__.py b/plasmapy/dispersion/__init__.py
index ca680645..51860f06 100644
--- a/plasmapy/dispersion/__init__.py
+++ b/plasmapy/dispersion/__init__.py
@@ -5,7 +5,6 @@
 __all__ = [
     "plasma_dispersion_func",
     "plasma_dispersion_func_deriv",
-    "tfds_",
     "two_fluid_dispersion_solution",
 ]
 
@@ -13,7 +12,4 @@
     plasma_dispersion_func,
     plasma_dispersion_func_deriv,
 )
-from plasmapy.dispersion.two_fluid_dispersion import (
-    tfds_,
-    two_fluid_dispersion_solution,
-)
+from plasmapy.dispersion.two_fluid_dispersion import two_fluid_dispersion_solution
diff --git a/plasmapy/dispersion/two_fluid_dispersion.py b/plasmapy/dispersion/two_fluid_dispersion.py
index c228f00a..455cbbfd 100644
--- a/plasmapy/dispersion/two_fluid_dispersion.py
+++ b/plasmapy/dispersion/two_fluid_dispersion.py
@@ -1,4 +1,4 @@
-__all__ = ["two_fluid_dispersion_solution", "tfds_"]
+__all__ = ["two_fluid_dispersion_solution"]
 
 import astropy.units as u
 import numpy as np
@@ -41,8 +41,6 @@ def two_fluid_dispersion_solution(
     field :math:`\mathbf{E_o}=0`, and quasi-neutrality.  For more information
     see the **Notes** section below.
 
-    **Aliases:** `tfds_`
-
     Parameters
     ----------
     B : `~astropy.units.Quantity`
@@ -346,7 +344,3 @@ def two_fluid_dispersion_solution(
             )
 
     return omega
-
-
-tfds_ = two_fluid_dispersion_solution
-""" Alias to :func:`two_fluid_dispersion_solution`. """
diff --git a/plasmapy/formulary/mathematics.py b/plasmapy/formulary/mathematics.py
index 7cf5893b..121f85c2 100644
--- a/plasmapy/formulary/mathematics.py
+++ b/plasmapy/formulary/mathematics.py
@@ -1,10 +1,11 @@
 """Mathematical formulas relevant to plasma physics."""
 
-__all__ = ["Fermi_integral", "rot_a_to_b"]
+__all__ = ["Fermi_integral", "Chandrasekhar_G", "rot_a_to_b"]
 
 import numbers
 import numpy as np
 
+from scipy import special
 from typing import Union
 
 
@@ -90,6 +91,64 @@ def Fermi_integral(
         raise TypeError(f"Improper type {type(x)} given for argument x.")
 
 
+def Chandrasekhar_G(x: float):
+    r"""
+    Calculate the Chandrasekhar G function used in transport theory.
+
+    Parameters
+    ----------
+    x : `float` or `~numpy.ndarray`
+        Usually the ratio of a particle's velocity to its species' thermal
+        velocity.
+
+    Returns
+    -------
+    `float` or `numpy.ndarray`
+
+    Notes
+    -----
+
+    The Chandrasekhar function is defined as:
+
+    .. math::
+        G(x) = \frac{\Phi(x) - x * \Phi'(x)}{2x^2}
+
+    Where :math:`\Phi(x)` is the Gauss error function. G goes as :math:`2x /
+    3 \sqrt{π}` at :math:`x \to 0` and :math:`0.5 x^{-2}` at :math:`x \to
+    \infty`. It describes the drag on a particle by collisions with a
+    Maxwellian background.
+
+    Since it goes to zero at infinity, for any applied electric field you can
+    always find electrons for which the field is larger than the friction.
+    These electrons will then enter a feedback loop, accelerating endlessly (in
+    the non-relativistic limit) and becoming runaways.
+
+    Incidentally, if your field is barely strong enough to accelerate thermal
+    electrons to infinity, it's called the Dreicer electric field.
+
+    Examples
+    --------
+    >>> Chandrasekhar_G(1)
+    0.21379664776456
+    >>> Chandrasekhar_G(1e-6)
+    3.7608262858090935e-07
+    >>> Chandrasekhar_G(1e6)
+    5e-13
+    >>> Chandrasekhar_G(-1)
+    -0.21379664776456
+
+    References
+    ----------
+    Collisional Transport in Magnetized Plasmas,
+    Per Helander & Dieter J. Sigmar, 2005
+
+    """
+
+    erf = special.erf(x)
+    erf_derivative = 2 * np.exp(-(x ** 2)) / np.sqrt(np.pi)
+    return (erf - x * erf_derivative) / (2 * x ** 2)
+
+
 def rot_a_to_b(a: np.ndarray, b: np.ndarray) -> np.ndarray:
     r"""
     Calculates the 3D rotation matrix that will rotate vector ``a`` to be aligned
diff --git a/requirements/environment.yml b/requirements/environment.yml
index 879416cb..55938a87 100644
--- a/requirements/environment.yml
+++ b/requirements/environment.yml
@@ -17,6 +17,7 @@ dependencies:
   - pip
   - xarray
   - pip:
+    - docutils < 0.17
     - numpydoc
     - pytest
     - lmfit
@@ -25,3 +26,4 @@ dependencies:
     - sphinx-gallery
     - sphinx-automodapi
     - sphinx_rtd_theme
+    - pytest-regressions
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 3e025f52..83f580cd 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -7,6 +7,7 @@ matplotlib >= 2.0
 mpmath >= 1.0.0
 numpy >= 1.18.1
 pandas >= 1.0.0
+pytest-regressions
 scipy >= 1.2
 setuptools >= 41.2
 tqdm >= 4.56.0
diff --git a/setup.cfg b/setup.cfg
index 46f44cf5..746ef3c3 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -47,10 +47,12 @@ diagnostics =
   matplotlib
 tests =
   pytest >= 5.1
+  pytest-regressions
 theory =
   mpmath
   lmfit
 docs =
+  docutils < 0.17
   numpydoc
   pillow
   pygments >= 2.4.1
@@ -90,43 +92,96 @@ looponfailroots =
 
 
 
-[pycodestyle]
-# E101 - mix of tabs and spaces
-# W191 - use of tabs
-# W291 - trailing whitespace
-# W292 - no newline at end of file
-# W293 - trailing whitespace
-# W391 - blank line at end of file
-# E111 - 4 spaces per indentation level
-# E112 - 4 spaces per indentation level
-# E113 - 4 spaces per indentation level
-# E901 - SyntaxError or IndentationError
-# E902 - IOError
-# select = E226,E241,E242,E704,W504
-exclude = version.py,build
-max-line-length = 88
-
-[pydocstyle]
-# D302 is unnecessary as we are using Python 3.6+. Ignoring D202 allows blank
-# lines to be put on either side of code "paragraphs" at the beginning of a
-# function. D205 and D400 are ignored to allow the "one-liner" to exceed one
-# line, which is sometimes necessary for even concise descriptions of plasma
-# physics functions and classes.
-convention = numpy
-add-select = D402
-add-ignore = D202,D205,D302,D400,D105
-
 [flake8]
-# D302 is unnecessary as we are using Python 3.6+. Ignoring D202 allows blank
-# lines to be put on either side of code "paragraphs" at the beginning of a
-# function. D205 and D400 are ignored to allow the "one-liner" to exceed one
-# line, which is sometimes necessary for even concise descriptions of plasma
-# physics functions and classes.
 convention = numpy
-select = D402,D413
-ignore = D202,D205,D302,D400,D105,D107,F401
-exclude = extern,sphinx,*parsetab.py,conftest.py,docs/conf.py,setup.py
-max-line-length = 88
+# Checks marked as TODO should be eventually enabled,
+# but are failing right now. These are *great*
+# first contributions. ;)
+# Checks not marked by TODO *maybe* should be
+# eventually enabled ― ask!
+extend-ignore =
+    # TODO D100: Missing docstring in public module
+    D100,
+    # TODO D102: Missing docstring in public method
+    D102,
+    # TODO D103: Missing docstring in public function
+    D103,
+    # TODO D104: Missing docstring in public package
+    D104,
+    D105,
+    D107,
+    # TODO D200: One-line docstring should fit on one line with quotes
+    D200,
+    # Ignoring D202 allows blank lines to be put on either side of code "paragraphs" at the beginning of a function.
+    D202,
+    # D205 and D400 are ignored to allow the "one-liner" to exceed one
+    # line, which is sometimes necessary for even concise descriptions of plasma
+    # physics functions and classes.
+    D205,
+    # TODO D210: No whitespaces allowed surrounding docstring text
+    D210,
+    # TODO D301: Use r""" if any backslashes in a docstring
+    D301,
+    # D302 is unnecessary as we are using Python 3.6+.
+    D302,
+    # D205 and D400 are ignored to allow the "one-liner" to exceed one
+    # line, which is sometimes necessary for even concise descriptions of plasma
+    # physics functions and classes.
+    D400,
+    # TODO D401: First line should be in imperative mood
+    D401,
+    # D403: First word of the first line should be properly capitalized ('Latex', not 'LaTeX') # Dominik: good job, pydocstyle /s
+    D403,
+    # TODO D407: Missing dashed underline after section ('References')
+    D407,
+    # TODO D409: Section underline should match the length of its name (Expected 5 dashes in section 'Notes', got 10)
+    D409,
+    # TODO D412: No blank lines allowed between a section header and its content
+    D412
+    # E203 something something whitespace? TODO check again
+    E203,
+    E501,
+    # TODO E712 comparison to False should be if cond is False or if not cond
+    E712,
+    # TODO E713 test for membership should be 'not in'
+    E713,
+    # E731 do not assign a lambda, use a def - Dominik: disagreed, lambdas have uses
+    E731,
+    F401,
+    # TODO star imports and undefined variables
+    F403,
+    F405,
+    # all TODO from here on out, probably
+    F541,
+    # W605 invalid escape sequence,
+    W605,
+    RST210,
+    RST213,
+    RST306,
+    RST902,
+exclude =
+    extern,
+    sphinx,
+    *test*,
+    *parsetab.py,
+    conftest.py,
+    docs/conf.py,
+    setup.py,
+    .jupyter
+rst-roles =
+    # TODO look into these
+    attr,
+    class,
+    func,
+    data,
+    exc,
+    file,
+    meth,
+    mod
+rst-directives =
+    deprecated,
+    nbgallery,
+    todo
 
 [coverage:run]
 omit =
diff --git a/tox.ini b/tox.ini
index 3fb142d6..01777f8e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -86,6 +86,8 @@ deps =
   pytest==5.1
   mpmath==1.0
   pillow
+  pytest-regressions
+
 setenv =
     PYTEST_COMMAND = pytest --pyargs plasmapy --durations=25 {toxinidir}/docs --ignore={toxinidir}/docs/conf.py
 


Test patch:
diff --git a/plasmapy/dispersion/tests/test_two_fluid_dispersion.py b/plasmapy/dispersion/tests/test_two_fluid_dispersion.py
index 52113dc3..dae6ab5a 100644
--- a/plasmapy/dispersion/tests/test_two_fluid_dispersion.py
+++ b/plasmapy/dispersion/tests/test_two_fluid_dispersion.py
@@ -5,10 +5,7 @@
 
 from astropy import units as u
 
-from plasmapy.dispersion.two_fluid_dispersion import (
-    tfds_,
-    two_fluid_dispersion_solution,
-)
+from plasmapy.dispersion.two_fluid_dispersion import two_fluid_dispersion_solution
 from plasmapy.formulary import parameters as pfp
 from plasmapy.particles import Particle
 from plasmapy.utils.exceptions import PhysicsWarning
@@ -33,10 +30,6 @@ class TestTwoFluidDispersionSolution:
         "k": (2 * np.pi * u.rad) / (0.56547 * u.m),
     }
 
-    def test_alias(self):
-        """Test the associated alias is defined correctly."""
-        assert tfds_ is two_fluid_dispersion_solution
-
     @pytest.mark.parametrize(
         "kwargs, _error",
         [
diff --git a/plasmapy/formulary/tests/test_Chandrasekhar_G.py b/plasmapy/formulary/tests/test_Chandrasekhar_G.py
new file mode 100644
index 00000000..711cc644
--- /dev/null
+++ b/plasmapy/formulary/tests/test_Chandrasekhar_G.py
@@ -0,0 +1,27 @@
+import numpy as np
+
+from plasmapy.formulary.mathematics import Chandrasekhar_G
+
+
+def test_Chandrasekhar_regression(num_regression):
+    x = np.linspace(-5, 5, 100)
+    y = Chandrasekhar_G(x)
+    num_regression.check({"x": x, "y": y})
+
+
+def test_limiting_behavior_small_x():
+    x = np.logspace(-7, -4, 100)
+    y = Chandrasekhar_G(x)
+    limiting_behavior = 2 * x / (3 * np.sqrt(np.pi))
+    # useful debug check
+    # import matplotlib.pyplot as plt
+    # plt.semilogx(x, y - limiting_behavior)
+    # plt.show()
+    np.testing.assert_allclose(y, limiting_behavior, rtol=0, atol=1e-8)
+
+
+def test_limiting_behavior_high_x():
+    x = np.logspace(6, 9, 100)
+    y = Chandrasekhar_G(x)
+    limiting_behavior = x ** -2 / 2
+    np.testing.assert_allclose(y, limiting_behavior)
diff --git a/plasmapy/formulary/tests/test_Chandrasekhar_G/test_Chandrasekhar_regression.csv b/plasmapy/formulary/tests/test_Chandrasekhar_G/test_Chandrasekhar_regression.csv
new file mode 100644
index 00000000..5cf2818b
--- /dev/null
+++ b/plasmapy/formulary/tests/test_Chandrasekhar_G/test_Chandrasekhar_regression.csv
@@ -0,0 +1,101 @@
+,x,y
+0,-5,-0.019999999998402164
+1,-4.8989898989898988,-0.020833244761213954
+2,-4.7979797979797976,-0.021719667577955522
+3,-4.6969696969696972,-0.022663891747190433
+4,-4.595959595959596,-0.023671054136277677
+5,-4.4949494949494948,-0.024746875178577075
+6,-4.3939393939393936,-0.02589774024200989
+7,-4.2929292929292933,-0.027130794510262211
+8,-4.191919191919192,-0.028454053961327188
+9,-4.0909090909090908,-0.029876535555815226
+10,-3.9898989898989896,-0.031408410359829435
+11,-3.8888888888888888,-0.033061184006911909
+12,-3.7878787878787881,-0.034847909599045664
+13,-3.6868686868686869,-0.036783438745231774
+14,-3.5858585858585856,-0.038884716723297151
+15,-3.4848484848484849,-0.041171127353065509
+16,-3.3838383838383841,-0.043664891481392638
+17,-3.2828282828282829,-0.046391519080894596
+18,-3.1818181818181817,-0.049380307542243825
+19,-3.0808080808080809,-0.052664866056376818
+20,-2.9797979797979797,-0.056283625933850744
+21,-2.8787878787878789,-0.060280267054029765
+22,-2.7777777777777777,-0.064703949486404538
+23,-2.6767676767676769,-0.06960918595602171
+24,-2.5757575757575757,-0.075055126851884355
+25,-2.4747474747474749,-0.08110396031756914
+26,-2.3737373737373737,-0.087818066336382186
+27,-2.2727272727272729,-0.095255522758958988
+28,-2.1717171717171717,-0.10346356652731219
+29,-2.0707070707070709,-0.11246969327708679
+30,-1.9696969696969697,-0.12227026191691955
+31,-1.868686868686869,-0.13281678003777819
+32,-1.7676767676767677,-0.1440004874811753
+33,-1.6666666666666665,-0.15563640910157125
+34,-1.5656565656565657,-0.16744865850326851
+35,-1.4646464646464645,-0.17905934812976979
+36,-1.3636363636363638,-0.1899838691886418
+37,-1.2626262626262625,-0.1996354010879953
+38,-1.1616161616161618,-0.20734115749701842
+39,-1.0606060606060606,-0.2123719836526497
+40,-0.95959595959595934,-0.21398547925376946
+41,-0.85858585858585901,-0.21148093788128131
+42,-0.75757575757575779,-0.20426229454451697
+43,-0.65656565656565657,-0.19190328956700733
+44,-0.55555555555555536,-0.17420757762055744
+45,-0.45454545454545503,-0.15125590540986592
+46,-0.35353535353535381,-0.12343301671526302
+47,-0.2525252525252526,-0.091428704566333993
+48,-0.15151515151515138,-0.056210274226641876
+49,-0.050505050505050164,-0.018967235766268246
+50,0.050505050505050164,0.018967235766268246
+51,0.15151515151515138,0.056210274226641876
+52,0.2525252525252526,0.091428704566333993
+53,0.35353535353535381,0.12343301671526302
+54,0.45454545454545414,0.15125590540986572
+55,0.55555555555555536,0.17420757762055744
+56,0.65656565656565657,0.19190328956700733
+57,0.75757575757575779,0.20426229454451697
+58,0.85858585858585812,0.21148093788128131
+59,0.95959595959595934,0.21398547925376946
+60,1.0606060606060606,0.2123719836526497
+61,1.1616161616161618,0.20734115749701842
+62,1.2626262626262621,0.19963540108799532
+63,1.3636363636363633,0.18998386918864185
+64,1.4646464646464645,0.17905934812976979
+65,1.5656565656565657,0.16744865850326851
+66,1.666666666666667,0.15563640910157123
+67,1.7676767676767673,0.14400048748117533
+68,1.8686868686868685,0.13281678003777825
+69,1.9696969696969697,0.12227026191691955
+70,2.0707070707070709,0.11246969327708679
+71,2.1717171717171713,0.10346356652731221
+72,2.2727272727272725,0.09525552275895903
+73,2.3737373737373737,0.087818066336382186
+74,2.4747474747474749,0.08110396031756914
+75,2.5757575757575752,0.075055126851884396
+76,2.6767676767676765,0.069609185956021738
+77,2.7777777777777777,0.064703949486404538
+78,2.8787878787878789,0.060280267054029765
+79,2.9797979797979792,0.056283625933850757
+80,3.0808080808080813,0.052664866056376797
+81,3.1818181818181817,0.049380307542243825
+82,3.282828282828282,0.046391519080894623
+83,3.3838383838383841,0.043664891481392638
+84,3.4848484848484844,0.041171127353065516
+85,3.5858585858585865,0.038884716723297123
+86,3.6868686868686869,0.036783438745231774
+87,3.7878787878787872,0.034847909599045677
+88,3.8888888888888893,0.033061184006911902
+89,3.9898989898989896,0.031408410359829435
+90,4.0909090909090899,0.02987653555581524
+91,4.191919191919192,0.028454053961327188
+92,4.2929292929292924,0.027130794510262221
+93,4.3939393939393945,0.025897740242009876
+94,4.4949494949494948,0.024746875178577075
+95,4.5959595959595951,0.023671054136277684
+96,4.6969696969696972,0.022663891747190433
+97,4.7979797979797976,0.021719667577955522
+98,4.8989898989898997,0.020833244761213947
+99,5,0.019999999998402164
diff --git a/requirements/automated-documentation-tests.txt b/requirements/automated-documentation-tests.txt
index 0181735c..03e08f26 100644
--- a/requirements/automated-documentation-tests.txt
+++ b/requirements/automated-documentation-tests.txt
@@ -1,10 +1,9 @@
+# requirments for documentation
 -r requirements.txt
-
-# doc pkgs
+docutils < 0.17
 ipykernel
 ipywidgets
 nbsphinx
-
 numpydoc
 pillow
 pygments >= 2.4.1