Repository: petrobras/ross
Issue: Run_modal not working with Q_

Something like:
```python
modal = rotor1x.run_modal(speed=Q_(3577, "RPM"))
```
Gives this error:

```python
C:\\Users\\up49\\Anaconda3\\envs\\rs\\lib\\site-packages\\scipy\\_lib\\_util.py:293: UnitStrippedWarning:

The unit of the quantity is stripped when downcasting to ndarray.

---------------------------------------------------------------------------
DimensionalityError                       Traceback (most recent call last)
~\\AppData\\Local\\Temp/ipykernel_15016/1135117022.py in <module>
----> 1 modal = rotor1x.run_modal(speed=Q_(3577, "RPM"))

c:\\users\\up49\\ross\\ross\\rotor_assembly.py in run_modal(self, speed, num_modes, sparse)
    621         >>> fig = modal.plot_mode_2d(mode2)
    622         """
--> 623         evalues, evectors = self._eigen(speed, num_modes=num_modes, sparse=sparse)
    624         wn_len = num_modes // 2
    625         wn = (np.absolute(evalues))[:wn_len]

c:\\users\\up49\\ross\\ross\\rotor_assembly.py in _eigen(self, speed, num_modes, frequency, sorted_, A, sparse)
   1247         """
   1248         if A is None:
-> 1249             A = self.A(speed=speed, frequency=frequency)
   1250 
   1251         if sparse is True:

c:\\users\\up49\\ross\\ross\\rotor_assembly.py in A(self, speed, frequency)
   1056         A = np.vstack(
   1057             [np.hstack([Z, I]),
-> 1058              np.hstack([la.solve(-self.M(), self.K(frequency) + self.Kst()*speed), la.solve(-self.M(), (self.C(frequency) + self.G() * speed))])])
   1059         # fmt: on
   1060 

~\\Anaconda3\\envs\\rs\\lib\\site-packages\\pint\\quantity.py in __array_ufunc__(self, ufunc, method, *inputs, **kwargs)
   1721         )
   1722 
-> 1723         return numpy_wrap("ufunc", ufunc, inputs, kwargs, types)
   1724 
   1725     def __array_function__(self, func, types, args, kwargs):

~\\Anaconda3\\envs\\rs\\lib\\site-packages\\pint\\numpy_func.py in numpy_wrap(func_type, func, args, kwargs, types)
    919     if name not in handled or any(is_upcast_type(t) for t in types):
    920         return NotImplemented
--> 921     return handled[name](*args, **kwargs)

~\\Anaconda3\\envs\\rs\\lib\\site-packages\\pint\\numpy_func.py in _add(x1, x2, *args, **kwargs)
    501 @implements("add", "ufunc")
    502 def _add(x1, x2, *args, **kwargs):
--> 503     (x1, x2), output_wrap = unwrap_and_wrap_consistent_units(x1, x2)
    504     return output_wrap(np.add(x1, x2, *args, **kwargs))
    505 

~\\Anaconda3\\envs\\rs\\lib\\site-packages\\pint\\numpy_func.py in unwrap_and_wrap_consistent_units(*args)
    124 
    125     first_input_units = _get_first_input_units(args)
--> 126     args, _ = convert_to_consistent_units(*args, pre_calc_units=first_input_units)
    127     return (
    128         args,

~\\Anaconda3\\envs\\rs\\lib\\site-packages\\pint\\numpy_func.py in convert_to_consistent_units(pre_calc_units, *args, **kwargs)
    105     """
    106     return (
--> 107         tuple(convert_arg(arg, pre_calc_units=pre_calc_units) for arg in args),
    108         {
    109             key: convert_arg(arg, pre_calc_units=pre_calc_units)

~\\Anaconda3\\envs\\rs\\lib\\site-packages\\pint\\numpy_func.py in <genexpr>(.0)
    105     """
    106     return (
--> 107         tuple(convert_arg(arg, pre_calc_units=pre_calc_units) for arg in args),
    108         {
    109             key: convert_arg(arg, pre_calc_units=pre_calc_units)

~\\Anaconda3\\envs\\rs\\lib\\site-packages\\pint\\numpy_func.py in convert_arg(arg, pre_calc_units)
     87                 return arg
     88             else:
---> 89                 raise DimensionalityError("dimensionless", pre_calc_units)
     90     elif _is_quantity(arg):
     91         return arg.m

DimensionalityError: Cannot convert from 'dimensionless' to 'RPM'


```

Changed files:
ross/tests/test_rotor_assembly.py, ross/rotor_assembly.py

diff --git a/ross/rotor_assembly.py b/ross/rotor_assembly.py
index a862c1b..7a90058 100644
--- a/ross/rotor_assembly.py
+++ b/ross/rotor_assembly.py
@@ -565,6 +565,7 @@ class Rotor(object):
         else:
             return False
 
+    @check_units
     def run_modal(self, speed, num_modes=12, sparse=True):
         """Run modal analysis.
 
@@ -1205,6 +1206,7 @@ class Rotor(object):
 
         return idx
 
+    @check_units
     def _eigen(
         self, speed, num_modes=12, frequency=None, sorted_=True, A=None, sparse=True
     ):
@@ -1217,10 +1219,10 @@ class Rotor(object):
 
         Parameters
         ----------
-        speed : float
-            Rotor speed.
-        frequency: float
-            Excitation frequency.
+        speed : float, pint.Quantity
+            Rotor speed. Default unit is rad/s.
+        frequency: float, pint.Quantity
+            Excitation frequency. Default units is rad/s.
         sorted_ : bool, optional
             Sort considering the imaginary part (wd)
             Default is True
diff --git a/ross/tests/test_rotor_assembly.py b/ross/tests/test_rotor_assembly.py
index 6aa6d1f..9def09f 100644
--- a/ross/tests/test_rotor_assembly.py
+++ b/ross/tests/test_rotor_assembly.py
@@ -12,6 +12,7 @@ from ross.materials import steel
 from ross.point_mass import *
 from ross.rotor_assembly import *
 from ross.shaft_element import *
+from ross.units import Q_
 
 
 @pytest.fixture
@@ -286,6 +287,10 @@ def test_evals_sorted_rotor2(rotor2):
     modal2_10000 = rotor2.run_modal(speed=10000)
     assert_allclose(modal2_10000.evalues, evals_sorted_w_10000, rtol=1e-1)
 
+    # test run_modal with Q_
+    modal2_10000 = rotor2.run_modal(speed=Q_(95492.96585513721, "RPM"))
+    assert_allclose(modal2_10000.evalues, evals_sorted_w_10000, rtol=1e-1)
+
 
 @pytest.fixture
 def rotor3():
@@ -543,7 +548,6 @@ def test_freq_response(rotor4):
 
 
 def test_freq_response_w_force(rotor4):
-    # modal4 = rotor4.run_modal(0)
     F0 = np.array(
         [
             [0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j, 0.0 + 0.0j],