Repository: einsteinpy/einsteinpy

Issue:
Add Kerr and Kerr-Newman metric in Boyer-Lindquist coordinates to predefined metrics
🐞 **Problem**
`symbolic/predefined/vacuum_solutions.py` needs Kerr and Kerr-Newman Metrics. Implement them similar to already implemented `Schwarzschild()` in the same file.

💡 **Possible solutions**

  - [x] Kerr (c737c8b)
  - [ ] Kerr Newman

The metrics can be found be simple search on wikipedia. Also implementation can be found in `utils/kerr_utils.py` and `utils/kerrnewman_utils.py`

📋  **Steps to solve the problem**

 * Comment below about what you've started working on.
 * Add, commit, push your changes
 * Submit a pull request and add this in comments - `Addresses #<put issue number here>`
 * Ask for a review in comments section of pull request
 * Celebrate your contribution to this project 🎉


Comments:

Patch:
diff --git a/src/einsteinpy/symbolic/constants.py b/src/einsteinpy/symbolic/constants.py
index 509082c..09da6fc 100644
--- a/src/einsteinpy/symbolic/constants.py
+++ b/src/einsteinpy/symbolic/constants.py
@@ -39,6 +39,7 @@ class SymbolicConstant(Symbol):
 c = SymbolicConstant("c", "Speed Of Light")
 G = SymbolicConstant("G", "Gravitational Constant")
 Cosmo_Const = SymbolicConstant("Lambda", "Cosmological Constant")
+eps_0 = SymbolicConstant("eps_0", "Permittivity of free space")
 
 
 def get_constant(name):
@@ -49,7 +50,7 @@ def get_constant(name):
     ----------
     name : str
         Name of the constant. 
-        Currently available names are 'c', 'G', 'Cosmo_Const'.
+        Currently available names are 'c', 'G', 'Cosmo_Const', 'eps_0'.
     
     Returns
     -------
@@ -57,5 +58,5 @@ def get_constant(name):
         An instance of the required constant
 
     """
-    const_dict = {"c": c, "G": G, "Cosmo_Const": Cosmo_Const}
+    const_dict = {"c": c, "G": G, "Cosmo_Const": Cosmo_Const, "eps_0": eps_0}
     return const_dict[name]
diff --git a/src/einsteinpy/symbolic/predefined/__init__.py b/src/einsteinpy/symbolic/predefined/__init__.py
index c7b6d12..eb20cb8 100644
--- a/src/einsteinpy/symbolic/predefined/__init__.py
+++ b/src/einsteinpy/symbolic/predefined/__init__.py
@@ -4,4 +4,4 @@ from .de_sitter import AntiDeSitter, AntiDeSitterStatic, DeSitter
 from .find import find
 from .godel import Godel
 from .minkowski import Minkowski
-from .vacuum_solutions import Kerr, Schwarzschild
+from .vacuum_solutions import Kerr, KerrNewman, Schwarzschild
diff --git a/src/einsteinpy/symbolic/predefined/vacuum_solutions.py b/src/einsteinpy/symbolic/predefined/vacuum_solutions.py
index 414b435..5f13f9a 100644
--- a/src/einsteinpy/symbolic/predefined/vacuum_solutions.py
+++ b/src/einsteinpy/symbolic/predefined/vacuum_solutions.py
@@ -1,4 +1,4 @@
-from sympy import cos, diag, sin, symbols
+from sympy import cos, diag, pi, sin, symbols
 
 from einsteinpy.symbolic import constants
 from einsteinpy.symbolic.metric import MetricTensor
@@ -66,6 +66,52 @@ def Kerr(c=constants.c, sch=symbols("r_s"), a=symbols("a")):
 
 
 def KerrNewman(
-    c=constants.c, G=constants.G, sch=symbols("r_s"), a=symbols("a"), Q=symbols("Q")
+    c=constants.c,
+    G=constants.G,
+    eps_0=constants.eps_0,
+    sch=symbols("r_s"),
+    a=symbols("a"),
+    Q=symbols("Q"),
 ):
-    raise NotImplementedError
+    """
+    Kerr-Newman Metric in Boyer Lindquist coordinates.
+
+    Parameters
+    ----------
+    c : ~sympy.core.basic.Basic or int or float
+        Any value to assign to speed of light. Defaults to ``c``.
+    G : ~sympy.core.basic.Basic or int or float
+        Any value to assign to the Newton's (or gravitational) constant. Defaults to ``G``.
+    eps_0 : ~sympy.core.basic.Basic or int or float
+        Any value to assign to the electric constant or permittivity of free space. Defaults to ``eps_0``.
+    sch : ~sympy.core.basic.Basic or int or float
+        Any value to assign to Schwarzschild Radius of the central object. 
+        Defaults to ``r_s``.
+    a : ~sympy.core.basic.Basic or int or float
+        Spin factor of the heavy body. Usually, given by ``J/(Mc)``,
+        where ``J`` is the angular momentum.
+        Defaults to ``a``.
+    Q:  ~sympy.core.basic.Basic or int or float
+        Any value to assign to eletric charge of the central object. 
+        Defaults to ``Q``.
+
+    """
+    coords = symbols("t r theta phi")
+    t, r, theta, phi = coords
+    Sigma = r ** 2 + (a ** 2 * cos(theta) ** 2)
+    rQsq = ((Q ** 2) * G) / (4 * pi * eps_0 * (c ** 4))
+    Delta = r ** 2 - sch * r + a ** 2 + rQsq
+    c2 = c ** 2
+
+    metric = diag(
+        1 - (sch * r / Sigma),
+        -Sigma / (Delta * c2),
+        -Sigma / c2,
+        -(
+            (r ** 2 + a ** 2 + (sch * r * (a ** 2) * (sin(theta) ** 2) / Sigma))
+            * (sin(theta) ** 2)
+        )
+        / c2,
+    ).tolist()
+    metric[0][3] = metric[3][0] = sch * r * a * (sin(theta) ** 2) / (Sigma * c)
+    return MetricTensor(metric, coords, "ll")


Test patch:
diff --git a/src/einsteinpy/tests/test_symbolic/test_predefined/test_all.py b/src/einsteinpy/tests/test_symbolic/test_predefined/test_all.py
index 2bc70b3..37772f8 100644
--- a/src/einsteinpy/tests/test_symbolic/test_predefined/test_all.py
+++ b/src/einsteinpy/tests/test_symbolic/test_predefined/test_all.py
@@ -1,6 +1,8 @@
+import numpy as np
 import pytest
+from sympy import Array
 
-from einsteinpy.symbolic import MetricTensor
+from einsteinpy.symbolic import MetricTensor, simplify_sympy_array
 from einsteinpy.symbolic.predefined import (
     AntiDeSitter,
     AntiDeSitterStatic,
@@ -9,6 +11,7 @@ from einsteinpy.symbolic.predefined import (
     DeSitter,
     Godel,
     Kerr,
+    KerrNewman,
     Minkowski,
     Schwarzschild,
 )
@@ -23,6 +26,7 @@ from einsteinpy.symbolic.predefined import (
         Schwarzschild(),
         Schwarzschild(c=1, sch=2),
         Kerr(),
+        KerrNewman(),
         Minkowski(),
         CMetric(),
         Davidson(),
@@ -31,3 +35,15 @@ from einsteinpy.symbolic.predefined import (
 )
 def test_all_predefined_metrics(metric_instance):
     assert isinstance(metric_instance, MetricTensor)
+
+
+@pytest.mark.parametrize(
+    "m1, m2",
+    [
+        (Schwarzschild(), Kerr(a=0)),  # Schwarzschild is a special case of Kerr
+        (Kerr(), KerrNewman(Q=0)),  # Kerr is a special case of Kerr-Newman
+    ],
+)
+def test_check_two_metrics_are_equal(m1, m2):
+    zero_arr = Array(np.zeros(shape=m1.tensor().shape, dtype=int))
+    assert simplify_sympy_array(m1.tensor() - m2.tensor()) == zero_arr