Repository: einsteinpy/einsteinpy

Issue:
Extend support of Tensor Class(symbolic) to support scalars
🐞 **Problem**
Scalars are also zero order tensors. The Tensor class currently supports 1st order tensors and above.

🎯 **Goal**

  - Make `Tensor` support scalars. This issue can also help in #287 .

📋  **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 🎉


Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?src=pr&el=h1) Report
> Merging [#322](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?src=pr&el=desc) into [master](https://codecov.io/gh/einsteinpy/einsteinpy/commit/32d3c476f849761798037b3e325fef5b6c8fb30d?src=pr&el=desc) will **decrease** coverage by `0.08%`.
> The diff coverage is `100%`.

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

```diff
@@            Coverage Diff             @@
##           master     #322      +/-   ##
==========================================
- Coverage   94.06%   93.97%   -0.09%     
==========================================
  Files          38       38              
  Lines        1482     1460      -22     
==========================================
- Hits         1394     1372      -22     
  Misses         88       88
```


| [Impacted Files](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/einsteinpy/symbolic/tensor.py](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322/diff?src=pr&el=tree#diff-c3JjL2VpbnN0ZWlucHkvc3ltYm9saWMvdGVuc29yLnB5) | `96.84% <100%> (-1.01%)` | :arrow_down: |
| [src/einsteinpy/plotting/geodesics/static.py](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322/diff?src=pr&el=tree#diff-c3JjL2VpbnN0ZWlucHkvcGxvdHRpbmcvZ2VvZGVzaWNzL3N0YXRpYy5weQ==) | `100% <0%> (+1.11%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?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/einsteinpy/einsteinpy/pull/322?src=pr&el=footer). Last update [32d3c47...c1adefb](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

codecov[bot]: # [Codecov](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?src=pr&el=h1) Report
> Merging [#322](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?src=pr&el=desc) into [master](https://codecov.io/gh/einsteinpy/einsteinpy/commit/32d3c476f849761798037b3e325fef5b6c8fb30d?src=pr&el=desc) will **decrease** coverage by `0.08%`.
> The diff coverage is `100%`.

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

```diff
@@            Coverage Diff             @@
##           master     #322      +/-   ##
==========================================
- Coverage   94.06%   93.97%   -0.09%     
==========================================
  Files          38       38              
  Lines        1482     1460      -22     
==========================================
- Hits         1394     1372      -22     
  Misses         88       88
```


| [Impacted Files](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/einsteinpy/symbolic/tensor.py](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322/diff?src=pr&el=tree#diff-c3JjL2VpbnN0ZWlucHkvc3ltYm9saWMvdGVuc29yLnB5) | `96.84% <100%> (-1.01%)` | :arrow_down: |
| [src/einsteinpy/plotting/geodesics/static.py](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322/diff?src=pr&el=tree#diff-c3JjL2VpbnN0ZWlucHkvcGxvdHRpbmcvZ2VvZGVzaWNzL3N0YXRpYy5weQ==) | `100% <0%> (+1.11%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?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/einsteinpy/einsteinpy/pull/322?src=pr&el=footer). Last update [32d3c47...c1adefb](https://codecov.io/gh/einsteinpy/einsteinpy/pull/322?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

ritzvik: Also can you edit `RicciScalar` to reflect the new changes?
pep8speaks: Hello @OVSofia! Thanks for updating this PR. We checked the lines you've touched for [PEP 8](https://www.python.org/dev/peps/pep-0008) issues, and found:

* In the file [`src/einsteinpy/symbolic/tensor.py`](https://github.com/einsteinpy/einsteinpy/blob/eb6d461366d61924c8014983f4e185bfab8af771/src/einsteinpy/symbolic/tensor.py):

> [Line 80:121](https://github.com/einsteinpy/einsteinpy/blob/eb6d461366d61924c8014983f4e185bfab8af771/src/einsteinpy/symbolic/tensor.py#L80): [E501](https://duckduckgo.com/?q=pep8%20E501) line too long (121 > 120 characters)




OVSofia: > Also can you edit `RicciScalar` to reflect the new changes?

Do you want to change code or just docstring?
ritzvik: > > Also can you edit `RicciScalar` to reflect the new changes?
> 
> Do you want to change code or just docstring?

the code, I was implying. Make it a derived class of `BaseRelativityTensor`
ritzvik: There was no need for changing tests in `RicciScalar`, we are not breaking any APIs.

Anyways, pull the new commits, squash and rebase 
OVSofia: > There was no need for changing tests in `RicciScalar`, we are not breaking any APIs.
> 
> Anyways, pull the new commits, squash and rebase


Yes, in fact I was just making the changes of your last commits
ritzvik: you might need to rebase one more time. Sorry xD
OVSofia: sorry, I had a big mess with git XDD
ritzvik: > you might need to rebase one more time. Sorry xD

.


> sorry, I had a big mess with git XDD

What happened? 🤣 
OVSofia: > > you might need to rebase one more time. Sorry xD
> 
> .
> 
> > sorry, I had a big mess with git XDD
> 
> What happened?


I had not pushed the last commit because I had seen your changes and started to give conflicts, 
which I have solved in an inefficient way xDD
OVSofia: @ritzvik I can not see why it's failing :/
ritzvik: Well the docs started failing out of the blue. I raised the issue in #324

Patch:
diff --git a/src/einsteinpy/symbolic/constants.py b/src/einsteinpy/symbolic/constants.py
index 0ab4863..509082c 100644
--- a/src/einsteinpy/symbolic/constants.py
+++ b/src/einsteinpy/symbolic/constants.py
@@ -5,19 +5,24 @@ class SymbolicConstant(Symbol):
     """
     This class inherits from ~sympy.core.symbol.Symbol 
 
-    Parameters
-    ----------
-    name : str
-        Short, commonly accepted name of the constant. 
-        For example, 'c' for Speed of light.
-    descriptive_name : str
-        The extended name of the constant. 
-        For example, 'Speed of Light' for 'c'. 
-        Defaults to None.
-
     """
 
     def __new__(cls, name, descriptive_name=None, **assumptions):
+        """
+        Constructor and Initializer
+
+        Parameters
+        ----------
+        name : str
+            Short, commonly accepted name of the constant. 
+            For example, 'c' for Speed of light.
+        descriptive_name : str
+            The extended name of the constant. 
+            For example, 'Speed of Light' for 'c'. 
+            Defaults to None.
+
+        """
+
         instance = super(SymbolicConstant, cls).__new__(cls, name, **assumptions)
         instance._descriptive_name = descriptive_name
         return instance
diff --git a/src/einsteinpy/symbolic/ricci.py b/src/einsteinpy/symbolic/ricci.py
index 6c029c6..f9049b2 100644
--- a/src/einsteinpy/symbolic/ricci.py
+++ b/src/einsteinpy/symbolic/ricci.py
@@ -141,19 +141,20 @@ class RicciTensor(BaseRelativityTensor):
         return new_obj
 
 
-class RicciScalar:
+class RicciScalar(BaseRelativityTensor):
     """
+    Inherits from ~einsteinpy.symbolic.tensor.BaseRelativityTensor
     Class for defining Ricci Scalar
     """
 
-    def __init__(self, expression, syms, parent_metric=None):
+    def __init__(self, arr, syms, parent_metric=None):
         """
         Constructor and Initializer
 
         Parameters
         ----------
-        expression : ~sympy.core.expr.Expr
-            Any sympy expression
+        arr : ~sympy.tensor.array.dense_ndim_array.ImmutableDenseNDimArray or list
+            Sympy Array, multi-dimensional list containing Sympy Expressions, or Sympy Expressions or int or float scalar
         syms : tuple or list
             Tuple of crucial symbols denoting time-axis, 1st, 2nd, and 3rd axis (t,x1,x2,x3)
         parent_metric : ~einsteinpy.symbolic.metric.MetricTensor or None
@@ -166,14 +167,11 @@ class RicciScalar:
             Raised when syms is not a list or tuple
 
         """
+        super(RicciScalar, self).__init__(
+            arr=arr, syms=syms, config="", parent_metric=parent_metric
+        )
         self._order = 0
-        self._parent_metric = parent_metric
-        self._expr = expression
-        if isinstance(syms, (list, tuple)):
-            self.syms = syms
-            self.dims = len(self.syms)
-        else:
-            raise TypeError("syms should be a list or tuple")
+        self._expr = self.arr[0]
 
     @property
     def expr(self):
@@ -182,13 +180,6 @@ class RicciScalar:
         """
         return self._expr
 
-    @property
-    def parent_metric(self):
-        """
-        Returns the Parent Metric, if available.
-        """
-        return self._parent_metric
-
     @classmethod
     def from_riccitensor(cls, riccitensor, parent_metric=None):
         """
diff --git a/src/einsteinpy/symbolic/tensor.py b/src/einsteinpy/symbolic/tensor.py
index 5bf381d..fbd55ff 100644
--- a/src/einsteinpy/symbolic/tensor.py
+++ b/src/einsteinpy/symbolic/tensor.py
@@ -1,6 +1,7 @@
 import numpy as np
 import sympy
 from sympy import simplify, tensorcontraction, tensorproduct
+from sympy.core.expr import Expr
 from sympy.core.function import AppliedUndef, UndefinedFunction
 
 
@@ -76,7 +77,7 @@ class Tensor:
         Parameters
         ----------
         arr : ~sympy.tensor.array.dense_ndim_array.ImmutableDenseNDimArray or list
-            Sympy Array or multi-dimensional list containing Sympy Expressions
+            Sympy Array, multi-dimensional list containing Sympy Expressions, or Sympy Expressions or int or float scalar
         config : str
             Configuration of contravariant and covariant indices in tensor. 'u' for upper and 'l' for lower indices. Defaults to 'll'.
 
@@ -89,7 +90,7 @@ class Tensor:
 
         """
 
-        if isinstance(arr, (list, tuple, np.ndarray)):
+        if isinstance(arr, (list, tuple, np.ndarray, int, float, Expr)):
             self.arr = sympy.Array(arr)
         elif isinstance(arr, sympy.Array):
             self.arr = arr


Test patch:
diff --git a/src/einsteinpy/tests/test_symbolic/test_tensor.py b/src/einsteinpy/tests/test_symbolic/test_tensor.py
index 36eff8f..7d85b44 100644
--- a/src/einsteinpy/tests/test_symbolic/test_tensor.py
+++ b/src/einsteinpy/tests/test_symbolic/test_tensor.py
@@ -1,5 +1,7 @@
 import numpy as np
+import pytest
 from sympy import Array, Function, cos, simplify, sin, symbols
+from sympy.abc import y, z
 
 from einsteinpy.symbolic.tensor import BaseRelativityTensor, Tensor
 
@@ -72,15 +74,6 @@ def test_Tensor_repr():
     assert not "object at 0x" in machine_representation
 
 
-def test_TypeError1():
-    arr = 0
-    try:
-        obj = Tensor(arr)
-        assert False
-    except TypeError:
-        assert True
-
-
 def test_TypeError2():
     scht = schwarzschild_tensor().tensor()
     # pass non str object
@@ -147,3 +140,12 @@ def test_BaseRelativityTensor_automatic_calculation_of_free_variables():
             and (f in t2.functions)
             and (f in functions)
         )
+
+
+# tests fot Tensor Class to support scalars and sympy expression type scalars
+
+
+@pytest.mark.parametrize("scalar", [11.89, y * z + 5])
+def test_tensor_scalar(scalar):
+    scalar_tensor = Tensor(scalar)
+    assert scalar_tensor.tensor().rank() == 0