Repository: PlasmaPy/PlasmaPy

Issue:
Cython integration
Astropy's template has a way to [integrate Cython and C extensions.](http://docs.astropy.org/en/stable/development/ccython.html) We should get this working so we can have super optimized code for simulations!


Discussion:
Relates to [spectroscoPy issue](https://github.com/PlasmaPy/SpectroscoPy/issues/16)
I will take a crack at this as I need to learn Cython for my own simulations anyway.
I've been meaning to learn Cython as well. If you find any neat materials, it would be awesome if you could share them (perhaps here)! :smiley: 
[This](https://stackoverflow.com/questions/20307546/running-cython-code-within-the-interpreter) stackoverflow response has some good links (and reference to His Noodliness). Otherwise I am reading [Cython](http://shop.oreilly.com/product/0636920033431.do) by Kurt W. Smith.
For distribution, Cython [recommends](http://docs.cython.org/en/latest/src/reference/compilation.html#distributing-cython-modules) to distribute the `.c` files generated by our Cython `.pyx` files so that end users don't need to have Cython to install our package.

I think this means we need to explicitly include the generated `.c` files using the `get_extensions()` function in `setup_package.py` as mentioned in [astropy docs](http://docs.astropy.org/en/stable/development/ccython.html).
We should include guidelines for inclusion of C and Cython code into plasmaPy. [Here](https://github.com/astropy/astropy/blob/135589ff117042de5ef604889e851ecea760b759/docs/development/codeguide.rst) are astropy's guidelines.
[Examples](https://github.com/cythonbook/examples) from Cython book.

Resolving patch:
diff --git a/plasmapy/setup_package.py b/plasmapy/setup_package.py
new file mode 100644
--- /dev/null
+++ b/plasmapy/setup_package.py
@@ -0,0 +1,36 @@
+ 
+# script for astropy helpers to use to customize setup.
+from setuptools.extension import Extension
+
+# list of cython extensions in package
+# cython_extensions = [Extension("*", ["*.pyx"])]
+
+# list of c extensions in package
+# c_extensions = [Extension("*", ["*.c"])]
+
+# getting C and Cython extensions
+cython_testFile = 'plasmapy/physics/parameters_cython.pyx'
+c_testFile = 'plasmapy/physics/parameters_cython.c'
+
+# names for compiling the .c files to
+compileName = 'parameters_cython'
+#compileNamePath = 'plasmapy/physics/parameters_cython'
+compileNamePath = 'plasmapy.physics.parameters_cython'
+compileName_cython = 'plasmapy/physics/parameters_cython_pyx'
+compileName_c = 'plasmapy/physics/parameters_cython_c'
+
+
+def get_extensions():
+    extensions = []
+    # getting cython files
+    
+    # getting .c files
+
+    # expicitly adding cython and c file
+    extensions.append(Extension(name=compileNamePath,
+                                sources=[cython_testFile],
+                                include_dirs=['numpy']))
+#    extensions.append(Extension(name=compileName,
+#                                sources=[c_testFile],
+#                                include_dirs=['numpy']))
+    return extensions
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -4,6 +4,8 @@
 import glob
 import os
 import sys
+# testing if numpy exists at setup time
+import numpy
 
 import ah_bootstrap
 from setuptools import setup
@@ -118,15 +120,32 @@
                     os.path.relpath(root, PACKAGENAME), filename))
 package_info['package_data'][PACKAGENAME].extend(c_files)
 
+
+# importing Cython for cythonizing
+from Cython.Build import cythonize
+# trying to fetch extensions
+#exts = package_info['ext_modules']
+cython_exts = package_info.pop('ext_modules', [])
+print('*'*40)
+print(cython_exts)
+print('*'*40)
+print(package_info)
+print('*'*40)
+# when extensions are getting built it seems that the .pyx files
+# aren't getting cythonized, unless setuptools know to automatically
+# do this somehow, but I doubt it.
+
 # Note that requires and provides should not be included in the call to
 # ``setup``, since these are now deprecated. See this link for more details:
 # https://groups.google.com/forum/#!topic/astropy-dev/urYO8ckB2uM
 
+
 setup(name=PACKAGENAME,
       version=VERSION,
       description=DESCRIPTION,
       scripts=scripts,
-      install_requires=[s.strip() for s in metadata.get('install_requires', 'astropy').split(',')],
+      install_requires=[s.strip() for s in metadata.get(
+          'install_requires', 'astropy').split(',')],
       author=AUTHOR,
       author_email=AUTHOR_EMAIL,
       license=LICENSE,
@@ -136,5 +155,6 @@
       zip_safe=False,
       use_2to3=False,
       entry_points=entry_points,
-      **package_info
-)
+      ext_modules = cythonize(cython_exts),
+      **package_info,
+      )


Test patch:
diff --git a/plasmapy/physics/tests/test_parameters_cython.py b/plasmapy/physics/tests/test_parameters_cython.py
new file mode 100644
--- /dev/null
+++ b/plasmapy/physics/tests/test_parameters_cython.py
@@ -0,0 +1,25 @@
+"""Tests for functions that calculate plasma parameters using cython."""
+
+import numpy as np
+import pytest
+from astropy import units as u
+from warnings import simplefilter
+
+from ...utils.exceptions import RelativityWarning, RelativityError
+from ...utils.exceptions import PhysicsError
+from ...constants import c, m_p, m_e, e, mu0
+
+from ..parameters_cython import (thermal_speed,
+                                 )
+    
+def test_thermal_speed():
+    r"""Test for cythonized version of thermal_speed()."""
+    trueVal = 593083.619464999
+    T = 11604
+    methodVal = thermal_speed(T, particle="e", method="most_probable")
+    testTrue = np.isclose(methodVal,
+                          trueVal,
+                          rtol=0.0,
+                          atol=1e-16)
+    exceptStr = f'Thermal speed value is {methodVal}, should be {trueVal}.'
+    assert testTrue, exceptStr
\ No newline at end of file