Repository: PlasmaPy/PlasmaPy

Issue:
Add units to Magnetostatics reprs
Objects from the Magnetostatics module currently show up in reprs without units, because they're being cast to floats on class construction. It would be nice to have them save the input units, or at the very least, to display the units they're saving their inputs to, in their reprs and strs. 


Discussion:
Would like to work on it. Do we want to have e. g. `FiniteStraightWire(p1=0.01m, p2=2.0m , current=3.0A)` or with with more code compatible format like `FiniteStraightWire(p1=0.01 * u.m, p2=2.0 * u.m , current=3.0 * u.A)`?

Resolving patch:
diff --git a/plasmapy/formulary/magnetostatics.py b/plasmapy/formulary/magnetostatics.py
--- a/plasmapy/formulary/magnetostatics.py
+++ b/plasmapy/formulary/magnetostatics.py
@@ -46,16 +46,21 @@ class MagneticDipole(MagnetoStatics):
         Position of the dipole
 
     """
+
     @validate_quantities
     def __init__(self, moment: u.A * u.m**2, p0: u.m):
         self.moment = moment.value
+        self._moment_u = moment.unit
         self.p0 = p0.value
+        self._p0_u = p0.unit
 
     def __repr__(self):
-        return "{name}(moment={moment}, p0={p0})".format(
+        return "{name}(moment={moment}{moment_u}, p0={p0}{p0_u})".format(
             name=self.__class__.__name__,
             moment=self.moment,
-            p0=self.p0
+            p0=self.p0,
+            moment_u=self._moment_u,
+            p0_u=self._p0_u,
         )
 
     def magnetic_field(self, p: u.m) -> u.T:
@@ -101,11 +106,9 @@ class GeneralWire(Wire):
         electric current
 
     """
+
     @validate_quantities
-    def __init__(self, parametric_eq,
-                 t1,
-                 t2,
-                 current: u.A):
+    def __init__(self, parametric_eq, t1, t2, current: u.A):
         if callable(parametric_eq):
             self.parametric_eq = parametric_eq
         else:
@@ -116,6 +119,20 @@ def __init__(self, parametric_eq,
         else:
             raise ValueError(f"t1={t1} is not smaller than t2={t2}")
         self.current = current.value
+        self._current_u = current.unit
+
+    def __repr__(self):
+        return (
+            "{name}(parametric_eq={parametric_eq}, t1={t1}, t2={t2}, "
+            "current={current}{current_u})".format(
+                name=self.__class__.__name__,
+                parametric_eq=self.parametric_eq.__name__,
+                t1=self.t1,
+                t2=self.t2,
+                current=self.current,
+                current_u=self._current_u,
+            )
+        )
 
     def magnetic_field(self, p: u.m, n: numbers.Integral = 1000) -> u.T:
         r"""
@@ -181,20 +198,27 @@ class FiniteStraightWire(Wire):
         electric current
 
     """
+
     @validate_quantities
     def __init__(self, p1: u.m, p2: u.m, current: u.A):
         self.p1 = p1.value
         self.p2 = p2.value
+        self._p1_u = p1.unit
+        self._p2_u = p2.unit
         if np.all(p1 == p2):
             raise ValueError("p1, p2 should not be the same point.")
         self.current = current.value
+        self._current_u = current.unit
 
     def __repr__(self):
-        return "{name}(p1={p1}, p2={p2}, current={current})".format(
+        return "{name}(p1={p1}{p1_u}, p2={p2}{p2_u}, current={current}{current_u})".format(
             name=self.__class__.__name__,
             p1=self.p1,
             p2=self.p2,
-            current=self.current
+            current=self.current,
+            p1_u=self._p1_u,
+            p2_u=self._p2_u,
+            current_u=self._current_u,
         )
 
     def magnetic_field(self, p) -> u.T:
@@ -261,18 +285,23 @@ class InfiniteStraightWire(Wire):
         electric current
 
     """
+
     @validate_quantities
     def __init__(self, direction, p0: u.m, current: u.A):
-        self.direction = direction/np.linalg.norm(direction)
+        self.direction = direction / np.linalg.norm(direction)
         self.p0 = p0.value
+        self._p0_u = p0.unit
         self.current = current.value
+        self._current_u = current.unit
 
     def __repr__(self):
-        return "{name}(direction={direction}, p0={p0}, current={current})".format(
+        return "{name}(direction={direction}, p0={p0}{p0_u}, current={current}{current_u})".format(
             name=self.__class__.__name__,
             direction=self.direction,
             p0=self.p0,
-            current=self.current
+            current=self.current,
+            p0_u=self._p0_u,
+            current_u=self._current_u,
         )
 
     def magnetic_field(self, p) -> u.T:
@@ -320,16 +349,34 @@ class CircularWire(Wire):
         electric current
 
     """
+
+    def __repr__(self):
+        return (
+            "{name}(normal={normal}, center={center}{center_u}, "
+            "radius={radius}{radius_u}, current={current}{current_u})".format(
+                name=self.__class__.__name__,
+                normal=self.normal,
+                center=self.center,
+                radius=self.radius,
+                current=self.current,
+                center_u=self._center_u,
+                radius_u=self._radius_u,
+                current_u=self._current_u,
+            )
+        )
+
     @validate_quantities
-    def __init__(self, normal, center: u.m, radius: u.m,
-                 current: u.A, n=300):
-        self.normal = normal/np.linalg.norm(normal)
+    def __init__(self, normal, center: u.m, radius: u.m, current: u.A, n=300):
+        self.normal = normal / np.linalg.norm(normal)
         self.center = center.value
+        self._center_u = center.unit
         if radius > 0:
             self.radius = radius.value
+            self._radius_u = radius.unit
         else:
             raise ValueError("Radius should bu larger than 0")
         self.current = current.value
+        self._current_u = current.unit
 
         # parametric equation
         # find other two axises in the disc plane
@@ -362,16 +409,6 @@ def curve(t):
         self.roots_legendre = scipy.special.roots_legendre(n)
         self.n = n
 
-    def __repr__(self):
-        return "{name}(normal={normal}, center={center}, \
-radius={radius}, current={current})".format(
-            name=self.__class__.__name__,
-            normal=self.normal,
-            center=self.center,
-            radius=self.radius,
-            current=self.current
-        )
-
     def magnetic_field(self, p) -> u.T:
         r"""
         Calculate magnetic field generated by this wire at position `p`


Test patch:
diff --git a/plasmapy/formulary/tests/test_magnetostatics.py b/plasmapy/formulary/tests/test_magnetostatics.py
--- a/plasmapy/formulary/tests/test_magnetostatics.py
+++ b/plasmapy/formulary/tests/test_magnetostatics.py
@@ -32,6 +32,11 @@ def test_value2(self):
         assert np.all(np.isclose(B2.value, B2_expected.value))
         assert B2.unit == u.T
 
+    def test_repr(self):
+        "Test __repr__ function"
+        B1 = MagneticDipole(self.moment, self.p0)
+        assert repr(B1) == r"MagneticDipole(moment=[0. 0. 1.]A m2, p0=[0. 0. 0.]m)"
+
 
 class Test_GeneralWire:
     def setup_method(self):
@@ -55,6 +60,16 @@ def test_close_cw(self):
         assert np.all(np.isclose(B_cw.value, B_gw_cw.value))
         assert B_cw.unit == B_gw_cw.unit
 
+    def test_repr(self):
+        "Test __repr__ function"
+        gw_cw = self.cw.to_GeneralWire()
+        # round numbers to avoid calculation accuracy mismatch
+        gw_cw.t1 = -3.1516
+        gw_cw.t2 = +3.1516
+        assert repr(
+            gw_cw
+        ) == r"GeneralWire(parametric_eq=curve, t1=-3.1516, t2=3.1516, current=1.0A)"
+
     def test_close_fw(self):
         "Test if the GeneralWire is close to the FiniteWire it converted from"
         gw_fw = self.fw.to_GeneralWire()
@@ -93,7 +108,7 @@ def test_value1(self):
     def test_repr(self):
         "Test __repr__ function"
         fw = FiniteStraightWire(self.p1, self.p2, self.current)
-        assert repr(fw) == r"FiniteStraightWire(p1=[ 0.  0. -1.], p2=[0. 0. 1.], current=1.0)"
+        assert repr(fw) == r"FiniteStraightWire(p1=[ 0.  0. -1.]m, p2=[0. 0. 1.]m, current=1.0A)"
 
 
 class Test_InfiniteStraightWire:
@@ -114,7 +129,7 @@ def test_repr(self):
         "Test __repr__ function"
         iw = InfiniteStraightWire(self.direction, self.p0, self.current)
         assert repr(iw) == \
-            r"InfiniteStraightWire(direction=[0. 1. 0.], p0=[0. 0. 0.], current=1.0)"
+            r"InfiniteStraightWire(direction=[0. 1. 0.], p0=[0. 0. 0.]m, current=1.0A)"
 
 
 class Test_CircularWire:
@@ -150,4 +165,4 @@ def test_repr(self):
         "Test __repr__ function"
         cw = CircularWire(self.normalz, self.center, self.radius, self.current)
         assert repr(cw) == \
-            r"CircularWire(normal=[0. 0. 1.], center=[0. 0. 0.], radius=1.0, current=1.0)"
+            r"CircularWire(normal=[0. 0. 1.], center=[0. 0. 0.]m, radius=1.0m, current=1.0A)"