Repository: PlasmaPy/PlasmaPy

Issue:
1D Maxwellian example update
The 1D Maxwellian [example](http://docs.plasmapy.org/en/stable/auto_examples/plot_distribution.html#sphx-glr-auto-examples-plot-distribution-py) in our docs should have a plot for 1D speed distribution with lines that demarcate the various thermal velocities (i.e. "most probable", "mean magnitude", etc.).


Discussion:
Here is an example

![image](https://user-images.githubusercontent.com/28945309/39843137-f91a4572-53a6-11e8-9fc8-ce30d426a46c.png)


Resolving patch:
diff --git a/plasmapy/formulary/parameters.py b/plasmapy/formulary/parameters.py
--- a/plasmapy/formulary/parameters.py
+++ b/plasmapy/formulary/parameters.py
@@ -412,6 +412,16 @@ def ion_sound_speed(
     return V_S
 
 
+# This dictionary defines coefficients for thermal speeds
+# calculated for different methods and values of ndim.
+# Created here to avoid re-instantiating on each call
+_coefficients = {
+    1: {"most_probable": 0, "rms": 1, "mean_magnitude": 2 / np.pi},
+    2: {"most_probable": 1, "rms": 2, "mean_magnitude": np.pi / 2},
+    3: {"most_probable": 2, "rms": 3, "mean_magnitude": 8 / np.pi},
+}
+
+
 @check_relativistic
 @validate_quantities(
     T={"can_be_negative": False, "equivalencies": u.temperature_energy()},
@@ -423,6 +433,7 @@ def thermal_speed(
     particle: particles.Particle = "e-",
     method="most_probable",
     mass: u.kg = np.nan * u.kg,
+    ndim=3,
 ) -> u.m / u.s:
     r"""
     Return the most probable speed for a particle within a Maxwellian
@@ -448,6 +459,10 @@ def thermal_speed(
         anything, but if set, overrides mass acquired from `particle`. Useful
         with relative velocities of particles.
 
+    ndim : int
+        Dimensionality of space in which to calculate thermal velocity. Valid
+        values are 1,2,3.
+
     Returns
     -------
     V : ~astropy.units.Quantity
@@ -479,10 +494,34 @@ def thermal_speed(
     The particle thermal speed is given by:
 
     .. math::
-        V_{th,i} = \sqrt{\frac{2 k_B T_i}{m_i}}
-
-    This function yields the most probable speed within a distribution
-    function.  However, the definition of thermal velocity varies by
+        V_{th,i} = \sqrt{\frac{N k_B T_i}{m_i}}
+
+    where the value of N depends on the dimensionality and the definition of
+    :math:`v_{th}`: most probable, root-mean-square (RMS), or mean magnitude.
+    The value of N in each case is
+
+    .. list-table:: Values of constant N
+       :widths: 50, 25, 25, 25
+       :header-rows: 1
+
+       * - Dim.
+         - Most-Probable
+         - RMS
+         - Mean-Magnitude
+       * - 1D
+         - 0
+         - 1
+         - :math:`2/\pi`
+       * - 2D
+         - 1
+         - 2
+         - :math:`\pi/2`
+       * - 3D
+         - 2
+         - 3
+         - :math:`8/\pi`
+
+    The definition of thermal velocity varies by
     the square root of two depending on whether or not this velocity
     absorbs that factor in the expression for a Maxwellian
     distribution.  In particular, the expression given in the NRL
@@ -509,16 +548,16 @@ def thermal_speed(
     m = mass if np.isfinite(mass) else particles.particle_mass(particle)
 
     # different methods, as per https://en.wikipedia.org/wiki/Thermal_velocity
-    if method == "most_probable":
-        V = np.sqrt(2 * k_B * T / m)
-    elif method == "rms":
-        V = np.sqrt(3 * k_B * T / m)
-    elif method == "mean_magnitude":
-        V = np.sqrt(8 * k_B * T / (m * np.pi))
-    else:
+    try:
+        coef = _coefficients[ndim]
+    except KeyError:
+        raise ValueError("{ndim} is not a supported value for ndim in " "thermal_speed")
+    try:
+        coef = coef[method]
+    except KeyError:
         raise ValueError("Method {method} not supported in thermal_speed")
 
-    return V
+    return np.sqrt(coef * k_B * T / m)
 
 
 @validate_quantities(


Test patch:
diff --git a/plasmapy/formulary/tests/test_parameters.py b/plasmapy/formulary/tests/test_parameters.py
--- a/plasmapy/formulary/tests/test_parameters.py
+++ b/plasmapy/formulary/tests/test_parameters.py
@@ -365,6 +365,45 @@ def test_thermal_speed():
         thermal_speed(1 * u.MK, particle="p").si.value, 128486.56960876315
     )
 
+    # Explicitly check all three modes and dimensionalities
+    # ndim = 1
+    assert np.isclose(thermal_speed(T_e, method="most_probable", ndim=1).si.value, 0.0)
+
+    # Regression tests start here!
+    assert np.isclose(
+        thermal_speed(T_e, method="rms", ndim=1).si.value, 3893114.2008620175
+    )
+
+    assert np.isclose(
+        thermal_speed(T_e, method="mean_magnitude", ndim=1).si.value, 3106255.714310189
+    )
+
+    # ndim = 2
+    assert np.isclose(
+        thermal_speed(T_e, method="most_probable", ndim=2).si.value, 3893114.2008620175
+    )
+
+    assert np.isclose(
+        thermal_speed(T_e, method="rms", ndim=2).si.value, 5505694.902726359
+    )
+
+    assert np.isclose(
+        thermal_speed(T_e, method="mean_magnitude", ndim=2).si.value, 4879295.066124102
+    )
+
+    # ndim = 3
+    assert np.isclose(
+        thermal_speed(T_e, method="most_probable", ndim=3).si.value, 5505694.902726359
+    )
+
+    assert np.isclose(
+        thermal_speed(T_e, method="rms", ndim=3).si.value, 6743071.595560921
+    )
+
+    assert np.isclose(
+        thermal_speed(T_e, method="mean_magnitude", ndim=3).si.value, 6212511.428620378
+    )
+
     # Case when Z=1 is assumed
     assert thermal_speed(T_i, particle="p") == thermal_speed(T_i, particle="H-1+")
 
@@ -398,9 +437,14 @@ def test_thermal_speed():
         thermal_speed(1e6 * u.K, method="rms").si.value, 6743070.475775486
     )
 
+    # Test invalid method
     with pytest.raises(ValueError):
         thermal_speed(T_i, method="sadks")
 
+    # Test invalid ndim
+    with pytest.raises(ValueError):
+        thermal_speed(T_i, ndim=4)
+
     assert_can_handle_nparray(thermal_speed)