Repository: ross-rotordynamics/ross
Pull request #525 (2020-04-17 17:16:49)

Improving ross.stochastic computational efficiency
Related to Issue #524 


Patch:
diff --git a/docs/download.rst b/docs/download.rst
index 0e46bf69..a31b1dd5 100644
--- a/docs/download.rst
+++ b/docs/download.rst
@@ -12,4 +12,3 @@ Here you can download the tutorial and examples notebooks.
 | :download:`Example 6 </examples/example_6.ipynb>`
 | :download:`Example 7 </examples/example_7.ipynb>`
 | :download:`Example 8 </examples/example_8.ipynb>`
-| :download:`Stochastic ROSS - Tutorial </examples/example_stochastic_ross.ipynb>`
diff --git a/docs/examples.rst b/docs/examples.rst
index 642ab9c2..717b8e93 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -11,5 +11,4 @@ Examples
    examples/example_5
    examples/example_6
    examples/example_7
-   examples/example_8
-   examples/example_stochastic_ross
\ No newline at end of file
+   examples/example_8
\ No newline at end of file
diff --git a/docs/examples/example_stochastic_ross.ipynb b/docs/examples/tutorial_stochastic_ross.ipynb
similarity index 100%
rename from docs/examples/example_stochastic_ross.ipynb
rename to docs/examples/tutorial_stochastic_ross.ipynb
diff --git a/ross/__init__.py b/ross/__init__.py
index 3093ba63..97d27422 100644
--- a/ross/__init__.py
+++ b/ross/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "0.3.3"
+__version__ = "0.3.2"
 
 from .api_report import *
 from .bearing_seal_element import *
diff --git a/ross/stochastic/st_rotor_assembly.py b/ross/stochastic/st_rotor_assembly.py
index 4b70bfda..3668c6f1 100644
--- a/ross/stochastic/st_rotor_assembly.py
+++ b/ross/stochastic/st_rotor_assembly.py
@@ -76,8 +76,8 @@ class ST_Rotor(object):
 
     # Building random disk element
     >>> size = 5
-    >>> i_d=np.random.uniform(0.05, 0.06, size)
-    >>> o_d=np.random.uniform(0.35, 0.39, size)
+    >>> i_d = np.random.uniform(0.05, 0.06, size)
+    >>> o_d = np.random.uniform(0.35, 0.39, size)
     >>> disk0 = srs.ST_DiskElement.from_geometry(n=1,
     ...                                          material=steel,
     ...                                          width=0.07,
@@ -89,7 +89,7 @@ class ST_Rotor(object):
     >>> bearing0 = rs.BearingElement(0, kxx=stf, cxx=0)
     >>> bearing1 = rs.BearingElement(2, kxx=stf, cxx=0)
     >>> rand_rotor = srs.ST_Rotor(shaft_elm, [disk0], [bearing0, bearing1])
-    >>> len(list(rand_rotor.__iter__()))
+    >>> len(list(iter(rand_rotor)))
     5
     """
 
@@ -106,7 +106,6 @@ class ST_Rotor(object):
         rated_w=None,
         tag=None,
     ):
-
         if disk_elements is None:
             disk_elements = []
         if bearing_elements is None:
@@ -116,6 +115,7 @@ class ST_Rotor(object):
 
         # checking for random elements and matching sizes
         is_random = []
+        len_list = []
 
         if any(isinstance(elm, ST_ShaftElement) for elm in shaft_elements):
             is_random.append("shaft_elements")
@@ -123,19 +123,21 @@ class ST_Rotor(object):
             it = iter(
                 [elm for elm in shaft_elements if isinstance(elm, ST_ShaftElement)]
             )
-            the_len = len(list(next(it).__iter__()))
-            if not all(len(list(l)) == the_len for l in it):
+            len_sh = len(list(next(iter(it))))
+            if not all(len(list(l)) == len_sh for l in it):
                 raise ValueError(
                     "not all random shaft elements lists have same length."
                 )
+            len_list.append(len_sh)
 
         if any(isinstance(elm, ST_DiskElement) for elm in disk_elements):
             is_random.append("disk_elements")
 
             it = iter([elm for elm in disk_elements if isinstance(elm, ST_DiskElement)])
-            the_len = len(list(next(it).__iter__()))
-            if not all(len(list(l)) == the_len for l in it):
+            len_dk = len(list(next(iter(it))))
+            if not all(len(list(l)) == len_dk for l in it):
                 raise ValueError("not all random disk elements lists have same length.")
+            len_list.append(len_dk)
 
         if any(isinstance(elm, ST_BearingElement) for elm in bearing_elements):
             is_random.append("bearing_elements")
@@ -143,11 +145,12 @@ class ST_Rotor(object):
             it = iter(
                 [elm for elm in bearing_elements if isinstance(elm, ST_BearingElement)]
             )
-            the_len = len(list(next(it).__iter__()))
-            if not all(len(list(l)) == the_len for l in it):
+            len_brg = len(list(next(iter(it))))
+            if not all(len(list(l)) == len_brg for l in it):
                 raise ValueError(
                     "not all random bearing elements lists have same length."
                 )
+            len_list.append(len_brg)
 
         if any(isinstance(elm, ST_PointMass) for elm in point_mass_elements):
             is_random.append("point_mass_elements")
@@ -155,25 +158,31 @@ class ST_Rotor(object):
             it = iter(
                 [elm for elm in point_mass_elements if isinstance(elm, ST_PointMass)]
             )
-            the_len = len(list(next(it).__iter__()))
-            if not all(len(list(l)) == the_len for l in it):
+            len_pm = len(list(next(iter(it))))
+            if not all(len(list(l)) == len_pm for l in it):
                 raise ValueError("not all random point mass lists have same length.")
+            len_list.append(len_pm)
+
+        if len_list.count(len_list[0]) == len(len_list):
+            RV_size = len_list[0]
+        else:
+            raise ValueError("not all the random elements lists have the same length.")
 
         for i, elm in enumerate(shaft_elements):
             if isinstance(elm, ST_ShaftElement):
-                shaft_elements[i] = list(elm.__iter__())
+                shaft_elements[i] = list(iter(elm))
 
         for i, elm in enumerate(disk_elements):
             if isinstance(elm, ST_DiskElement):
-                disk_elements[i] = list(elm.__iter__())
+                disk_elements[i] = list(iter(elm))
 
         for i, elm in enumerate(bearing_elements):
             if isinstance(elm, ST_BearingElement):
-                bearing_elements[i] = list(elm.__iter__())
+                bearing_elements[i] = list(iter(elm))
 
         for i, elm in enumerate(point_mass_elements):
             if isinstance(elm, ST_PointMass):
-                point_mass_elements[i] = list(elm.__iter__())
+                point_mass_elements[i] = list(iter(elm))
 
         attribute_dict = dict(
             shaft_elements=shaft_elements,
@@ -191,14 +200,14 @@ class ST_Rotor(object):
         # Assembling random rotors
         self.is_random = is_random
         self.attribute_dict = attribute_dict
-        self.shaft_elements = shaft_elements
-        self.disk_elements = disk_elements
-        self.bearing_elements = bearing_elements
-        self.point_mass_elements = point_mass_elements
+
+        # iter_break allows the iteration to stop at once to collect ndof
+        self.iter_break = True
+        ndof = list(iter(self))[0].ndof
 
         # common parameters
-        self.RV_size = sum(1 for _ in self.__iter__())
-        self.ndof = list(self.__iter__())[0].ndof
+        self.RV_size = RV_size
+        self.ndof = ndof
 
     def __iter__(self):
         """Return an iterator for the container.
@@ -206,10 +215,78 @@ class ST_Rotor(object):
         Returns
         -------
         An iterator over random rotors.
+
+        Examples
+        --------
+        >>> import ross.stochastic as srs
+        >>> rotors = srs.st_rotor_example()
+        >>> len(list(iter(rotors)))
+        10
         """
         return iter(self.use_random_var(Rotor, self.is_random, self.attribute_dict))
 
-    def get_args(self, idx, *args):
+    def __getitem__(self, key):
+        """Return the value for a given key from attribute_dict.
+
+        Parameters
+        ----------
+        key : str
+            A class parameter as string.
+
+        Raises
+        ------
+        KeyError
+            Raises an error if the parameter doesn't belong to the class.
+
+        Returns
+        -------
+        Return the value for the given key.
+
+        Example
+        -------
+        >>> import ross.stochastic as srs
+        >>> rotors = srs.st_rotor_example()
+        >>> rotors["shaft_elements"] # doctest: +ELLIPSIS
+        [ShaftElement...
+        """
+        if key not in self.attribute_dict.keys():
+            raise KeyError("Object does not have parameter: {}.".format(key))
+
+        return self.attribute_dict[key]
+
+    def __setitem__(self, key, value):
+        """Set new parameter values for the object.
+
+        Function to change a parameter value.
+        It's not allowed to add new parameters to the object.
+
+        Parameters
+        ----------
+        key : str
+            A class parameter as string.
+        value : The corresponding value for the attrbiute_dict's key.
+            ***check the correct type for each key in ST_Rotor
+            docstring.
+
+        Raises
+        ------
+        KeyError
+            Raises an error if the parameter doesn't belong to the class.
+
+        Example
+        -------
+        >>> import ross.stochastic as srs
+        >>> rotors = srs.st_rotor_example()
+        >>> rotors["n_eigen"] = 18
+        >>> rotors["n_eigen"]
+        18
+        """
+        if key not in self.attribute_dict.keys():
+            raise KeyError("Object does not have parameter: {}.".format(key))
+        self.attribute_dict[key] = value
+
+    @staticmethod
+    def get_args(idx, *args):
         """Build new list of arguments from a random list of arguments.
 
         This funtion takes a list with random values or with lists of random
@@ -259,7 +336,7 @@ class ST_Rotor(object):
             List of the object attributes to become stochastic.
             Default is None
         *args : dict
-            Dictionary instanciating the ShaftElement class.
+            Dictionary instanciating the ross.Rotor class.
             The attributes that are supposed to be stochastic should be
             set as lists of random variables.
 
@@ -271,13 +348,18 @@ class ST_Rotor(object):
         args_dict = args[0]
         new_args = []
 
-        for v in list(map(args_dict.get, is_random))[0]:
-            if isinstance(v, Iterable):
-                var_size = len(v)
-                break
-            else:
-                var_size = len(list(map(args_dict.get, is_random))[0])
-                break
+        if self.iter_break is True:
+            var_size = 1
+            self.iter_break = False
+        else:
+            for v in list(map(args_dict.get, is_random))[0]:
+                if isinstance(v, Iterable):
+                    var_size = len(v)
+                    break
+                else:
+                    var_size = len(list(map(args_dict.get, is_random))[0])
+                    break
+
         for i in range(var_size):
             arg = []
             for key, value in args_dict.items():
@@ -295,18 +377,18 @@ class ST_Rotor(object):
         """Generate a list of random objects from random attributes.
 
         This function creates a list of objects with random values for selected
-        attributes from a given class or method.
+        attributes from ross.Rotor class or its methods.
 
         Parameters
         ----------
         f : callable
             Function to be instantiated randomly with its respective *args.
-            Default is instantiating the ST_Rotor class
+            Default is instantiating the ross.Rotor class
         is_random : list
             List of the object attributes to become stochastic.
             Default is None
         *args : dict
-            Dictionary instanciating the ShaftElement class.
+            Dictionary instanciating the ross.Rotor class.
             The attributes that are supposed to be stochastic should be
             set as lists of random variables.
 
@@ -368,7 +450,7 @@ class ST_Rotor(object):
         log_dec = np.zeros((frequencies, CAMP_size, RV_size))
 
         # Monte Carlo - results storage
-        for i, rotor in enumerate(self.__iter__()):
+        for i, rotor in enumerate(iter(self)):
             results = rotor.run_campbell(speed_range, frequencies, frequency_type)
             for j in range(frequencies):
                 wd[j, :, i] = results.wd[:, j]
@@ -427,7 +509,7 @@ class ST_Rotor(object):
         phase = np.zeros(((FRF_size, RV_size)))
 
         # Monte Carlo - results storage
-        for i, rotor in enumerate(self.__iter__()):
+        for i, rotor in enumerate(iter(self)):
             results = rotor.run_freq_response(speed_range, modes)
             magnitude[:, i] = results.magnitude[inp, out, :]
             phase[:, i] = results.phase[inp, out, :]
@@ -503,7 +585,7 @@ class ST_Rotor(object):
         # force is not a random variable
         if len(force.shape) == 2:
             # Monte Carlo - results storage
-            for i, rotor in enumerate(self.__iter__()):
+            for i, rotor in enumerate(iter(self)):
                 t_, y, x = rotor.time_response(speed, force, time_range, ic)
                 xout[i] = x
                 yout[i] = y
@@ -512,7 +594,7 @@ class ST_Rotor(object):
         if len(force.shape) == 3:
             # Monte Carlo - results storage
             i = 0
-            for rotor, F in zip(self.__iter__(), force):
+            for rotor, F in zip(iter(self), force):
                 t_, y, x = rotor.time_response(speed, F, time_range, ic)
                 xout[i] = x
                 yout[i] = y
@@ -602,14 +684,14 @@ class ST_Rotor(object):
         if len(is_random):
             i = 0
             unbalance_args = self._random_var(is_random, args_dict)
-            for rotor, args in zip(self.__iter__(), unbalance_args):
+            for rotor, args in zip(iter(self), unbalance_args):
                 results = rotor.unbalance_response(*args)
                 forced_resp[i] = results.forced_resp.T
                 mag_resp[i] = results.magnitude.T
                 phs_resp[i] = results.phase.T
                 i += 1
         else:
-            for i, rotor in enumerate(self.__iter__()):
+            for i, rotor in enumerate(iter(self)):
                 results = rotor.unbalance_response(
                     node, magnitude, phase, frequency_range
                 )
@@ -641,7 +723,7 @@ def st_rotor_example():
     --------
     >>> import ross.stochastic as srs
     >>> rotors = srs.st_rotor_example()
-    >>> len(list(rotors.__iter__()))
+    >>> len(list(iter(rotors)))
     10
     """
     import ross as rs


Test patch:
diff --git a/ross/tests/test_stochastic_rotor_assembly.py b/ross/tests/test_stochastic_rotor_assembly.py
index 1039a8a8..8687df5f 100644
--- a/ross/tests/test_stochastic_rotor_assembly.py
+++ b/ross/tests/test_stochastic_rotor_assembly.py
@@ -10,15 +10,39 @@ from numpy.testing import assert_allclose
 from ross.bearing_seal_element import BearingElement
 from ross.disk_element import DiskElement
 from ross.materials import steel
-from ross.rotor_assembly import rotor_example
 from ross.shaft_element import ShaftElement
 from ross.stochastic.st_bearing_seal_element import ST_BearingElement
 from ross.stochastic.st_disk_element import ST_DiskElement
 from ross.stochastic.st_point_mass import ST_PointMass
-from ross.stochastic.st_rotor_assembly import ST_Rotor
+from ross.stochastic.st_rotor_assembly import ST_Rotor, st_rotor_example
 from ross.stochastic.st_shaft_element import ST_ShaftElement
 
 
+@pytest.fixture
+def rotor1():
+    # rotor with 6 shaft elements, 2 disks and 2 random bearings
+    i_d = 0
+    o_d = 0.05
+    n = 6
+    L = [0.25 for _ in range(n)]
+
+    shaft_elem = [ShaftElement(l, i_d, o_d, material=steel) for l in L]
+
+    disk0 = DiskElement.from_geometry(
+        n=2, material=steel, width=0.07, i_d=0.05, o_d=0.28
+    )
+    disk1 = DiskElement.from_geometry(
+        n=4, material=steel, width=0.07, i_d=0.05, o_d=0.28
+    )
+
+    kxx = [1e6, 2e6]
+    cxx = [1e3, 2e3]
+    bearing0 = ST_BearingElement(n=0, kxx=kxx, cxx=cxx, is_random=["kxx", "cxx"])
+    bearing1 = ST_BearingElement(n=6, kxx=kxx, cxx=cxx, is_random=["kxx", "cxx"])
+
+    return ST_Rotor(shaft_elem, [disk0, disk1], [bearing0, bearing1])
+
+
 ###############################################################################
 # testing error messages
 ###############################################################################
@@ -92,36 +116,43 @@ def test_st_point_mass_elements_odd_length():
     assert "not all random point mass lists have same length." in str(ex.value)
 
 
-###############################################################################
-# testing methods
-###############################################################################
-
-
-@pytest.fixture
-def rotor1():
-    # rotor with 6 shaft elements, 2 disks and 2 random bearings
-    i_d = 0
-    o_d = 0.05
-    n = 6
-    L = [0.25 for _ in range(n)]
+def test_elements_odd_length():
+    tim0 = ST_ShaftElement(
+        L=[1, 1.1], idl=0, odl=[0.1, 0.2], material=steel, is_random=["L", "odl"],
+    )
+    shaft_elm = [tim0, tim0]
 
-    shaft_elem = [ShaftElement(l, i_d, o_d, material=steel) for l in L]
+    disk0 = ST_DiskElement(n=0, m=[20, 30, 40], Id=1, Ip=1, is_random=["m"])
+    disk1 = ST_DiskElement(n=2, m=[20, 30, 40], Id=1, Ip=1, is_random=["m"])
+    disks = [disk0, disk1]
 
-    disk0 = DiskElement.from_geometry(
-        n=2, material=steel, width=0.07, i_d=0.05, o_d=0.28
+    brg0 = ST_BearingElement(
+        n=0, kxx=[1e6, 2e6], cxx=[1e3, 2e3], is_random=["kxx", "cxx"],
     )
-    disk1 = DiskElement.from_geometry(
-        n=4, material=steel, width=0.07, i_d=0.05, o_d=0.28
+    brg1 = ST_BearingElement(
+        n=2, kxx=[1e6, 2e6], cxx=[1e3, 2e3], is_random=["kxx", "cxx"],
     )
+    bearings = [brg0, brg1]
 
-    kxx = [1e6, 2e6]
-    cxx = [1e3, 2e3]
-    bearing0 = ST_BearingElement(n=0, kxx=kxx, cxx=cxx, is_random=["kxx", "cxx"])
-    bearing1 = ST_BearingElement(n=6, kxx=kxx, cxx=cxx, is_random=["kxx", "cxx"])
+    with pytest.raises(ValueError) as ex:
+        ST_Rotor(shaft_elm, disks, bearings)
+    assert "not all the random elements lists have the same length." in str(ex.value)
 
-    return ST_Rotor(shaft_elem, [disk0, disk1], [bearing0, bearing1])
 
+def test_getter_and_setter_error_messages():
+    rotor = st_rotor_example()
+    with pytest.raises(KeyError) as ex:
+        rotor["odd"]
+    assert "Object does not have parameter: odd." in str(ex.value)
 
+    with pytest.raises(KeyError) as ex:
+        rotor["odd"] = 0
+    assert "Object does not have parameter: odd." in str(ex.value)
+
+
+###############################################################################
+# testing methods
+###############################################################################
 def test_run_campbell(rotor1):
     speed_range = np.linspace(0, 300, 11)
     results = rotor1.run_campbell(speed_range)
@@ -292,44 +323,44 @@ def test_run_unbalance_response(rotor1):
     freq_range = np.linspace(0, 500, 21)
     n = 3
     m = [0.001, 0.002]
-    p = 0.0
+    p = [0.0, np.pi]
     results = rotor1.run_unbalance_response(n, m, p, freq_range)
 
     # fmt: off
     forced_resp = np.array(
         [
-           [ 0.00000000e+00+0.00000000e+00j,  0.00000000e+00+0.00000000e+00j,
-             0.00000000e+00+0.00000000e+00j,  0.00000000e+00+0.00000000e+00j,
-             0.00000000e+00+0.00000000e+00j,  0.00000000e+00+0.00000000e+00j,
-             0.00000000e+00+0.00000000e+00j,  0.00000000e+00+0.00000000e+00j],
-           [ 3.37435976e-07-8.68482070e-09j, -8.68482070e-09-3.37435976e-07j,
-             9.00209719e-10+1.45114527e-06j,  1.45114527e-06-9.00209715e-10j,
-             6.87245293e-07-8.90049158e-09j, -8.90049158e-09-6.87245293e-07j,
-             7.83414977e-10+1.28831088e-06j,  1.28831088e-06-7.83414974e-10j],
-           [ 1.75768084e-06-1.01068880e-07j, -1.01068880e-07-1.75768084e-06j,
-             4.79346233e-08+7.33456232e-06j,  7.33456232e-06-4.79346233e-08j,
-             3.52371378e-06-1.12554546e-07j, -1.12554546e-07-3.52371378e-06j,
-             4.17303088e-08+6.48704574e-06j,  6.48704574e-06-4.17303088e-08j],
-           [ 7.50676607e-06-9.12209199e-07j, -9.12209199e-07-7.50676607e-06j,
-             1.28086454e-06+2.98912386e-05j,  2.98912386e-05-1.28086454e-06j,
-             1.46900142e-05-1.21918813e-06j, -1.21918813e-06-1.46900142e-05j,
-             1.11573867e-06+2.62670396e-05j,  2.62670396e-05-1.11573867e-06j],
-           [-6.18015733e-05-3.00382924e-05j, -3.00382924e-05+6.18015733e-05j,
-             1.34528953e-04-2.14597175e-04j, -2.14597175e-04-1.34528953e-04j,
-            -1.13214987e-04-6.22904820e-05j, -6.22904820e-05+1.13214987e-04j,
-             1.17283028e-04-1.86678717e-04j, -1.86678717e-04-1.17283028e-04j],
-           [-1.38700091e-05+2.08259695e-08j,  2.08259695e-08+1.38700091e-05j,
-             6.46783937e-06-4.63275792e-05j, -4.63275792e-05-6.46783937e-06j,
-            -2.49272456e-05-1.53041944e-06j, -1.53041944e-06+2.49272456e-05j,
-             5.64471769e-06-3.97928561e-05j, -3.97928561e-05-5.64471769e-06j],
-           [-1.02696016e-05+5.04852144e-07j,  5.04852144e-07+1.02696016e-05j,
-             3.99127894e-06-3.09402605e-05j, -3.09402605e-05-3.99127894e-06j,
-            -1.76161220e-05-4.52895726e-07j, -4.52895726e-07+1.76161220e-05j,
-             3.48788819e-06-2.61160471e-05j, -2.61160471e-05-3.48788819e-06j],
-           [-9.35892715e-06+7.25075254e-07j,  7.25075254e-07+9.35892715e-06j,
-             3.60633532e-06-2.49610892e-05j, -2.49610892e-05-3.60633532e-06j,
-            -1.52457821e-05-1.40815948e-07j, -1.40815948e-07+1.52457821e-05j,
-             3.15637404e-06-2.05887194e-05j, -2.05887194e-05-3.15637404e-06j],
+            [ 0.00000000e+00+0.00000000e+00j,  0.00000000e+00+0.00000000e+00j,
+              0.00000000e+00+0.00000000e+00j,  0.00000000e+00+0.00000000e+00j,
+              0.00000000e+00+0.00000000e+00j,  0.00000000e+00+0.00000000e+00j,
+              0.00000000e+00+0.00000000e+00j,  0.00000000e+00+0.00000000e+00j],
+            [ 3.37435976e-07-8.68482070e-09j, -8.68482070e-09-3.37435976e-07j,
+              9.00209719e-10+1.45114527e-06j,  1.45114527e-06-9.00209715e-10j,
+              6.87245293e-07-8.90049158e-09j, -8.90049158e-09-6.87245293e-07j,
+              7.83414977e-10+1.28831088e-06j,  1.28831088e-06-7.83414974e-10j],
+            [ 1.75768084e-06-1.01068880e-07j, -1.01068880e-07-1.75768084e-06j,
+              4.79346233e-08+7.33456232e-06j,  7.33456232e-06-4.79346233e-08j,
+              3.52371378e-06-1.12554546e-07j, -1.12554546e-07-3.52371378e-06j,
+              4.17303088e-08+6.48704574e-06j,  6.48704574e-06-4.17303088e-08j],
+            [ 7.50676607e-06-9.12209199e-07j, -9.12209199e-07-7.50676607e-06j,
+              1.28086454e-06+2.98912386e-05j,  2.98912386e-05-1.28086454e-06j,
+              1.46900142e-05-1.21918813e-06j, -1.21918813e-06-1.46900142e-05j,
+              1.11573867e-06+2.62670396e-05j,  2.62670396e-05-1.11573867e-06j],
+            [-6.18015733e-05-3.00382924e-05j, -3.00382924e-05+6.18015733e-05j,
+              1.34528953e-04-2.14597175e-04j, -2.14597175e-04-1.34528953e-04j,
+             -1.13214987e-04-6.22904820e-05j, -6.22904820e-05+1.13214987e-04j,
+              1.17283028e-04-1.86678717e-04j, -1.86678717e-04-1.17283028e-04j],
+            [-1.38700091e-05+2.08259695e-08j,  2.08259695e-08+1.38700091e-05j,
+              6.46783937e-06-4.63275792e-05j, -4.63275792e-05-6.46783937e-06j,
+             -2.49272456e-05-1.53041944e-06j, -1.53041944e-06+2.49272456e-05j,
+              5.64471769e-06-3.97928561e-05j, -3.97928561e-05-5.64471769e-06j],
+            [-1.02696016e-05+5.04852144e-07j,  5.04852144e-07+1.02696016e-05j,
+              3.99127894e-06-3.09402605e-05j, -3.09402605e-05-3.99127894e-06j,
+             -1.76161220e-05-4.52895726e-07j, -4.52895726e-07+1.76161220e-05j,
+              3.48788819e-06-2.61160471e-05j, -2.61160471e-05-3.48788819e-06j],
+            [-9.35892715e-06+7.25075254e-07j,  7.25075254e-07+9.35892715e-06j,
+              3.60633532e-06-2.49610892e-05j, -2.49610892e-05-3.60633532e-06j,
+             -1.52457821e-05-1.40815948e-07j, -1.40815948e-07+1.52457821e-05j,
+              3.15637404e-06-2.05887194e-05j, -2.05887194e-05-3.15637404e-06j]
         ]
     )
 
@@ -350,7 +381,7 @@ def test_run_unbalance_response(rotor1):
             [1.02820033e-05, 1.02820033e-05, 3.11966349e-05, 3.11966349e-05,
              1.76219428e-05, 1.76219428e-05, 2.63479275e-05, 2.63479275e-05],
             [9.38697244e-06, 9.38697244e-06, 2.52202623e-05, 2.52202623e-05,
-             1.52464324e-05, 1.52464324e-05, 2.08292598e-05, 2.08292598e-05],
+             1.52464324e-05, 1.52464324e-05, 2.08292598e-05, 2.08292598e-05]
         ]
     )
 
@@ -371,7 +402,7 @@ def test_run_unbalance_response(rotor1):
             [ 3.09247234e+00,  1.52167601e+00, -1.44250530e+00, -3.01330162e+00,
              -3.11588915e+00,  1.59649983e+00, -1.43802853e+00, -3.00882486e+00],
             [ 3.06427292e+00,  1.49347659e+00, -1.42731092e+00, -2.99810724e+00,
-             -3.13235653e+00,  1.58003245e+00, -1.41867471e+00, -2.98947104e+00],
+             -3.13235653e+00,  1.58003245e+00, -1.41867471e+00, -2.98947104e+00]
         ]
     )
     # fmt: on
@@ -379,9 +410,9 @@ def test_run_unbalance_response(rotor1):
     assert results.forced_resp.shape == (2, 21, 28)
     assert results.magnitude.shape == (2, 21, 28)
     assert results.phase.shape == (2, 21, 28)
-    assert_allclose(results.forced_resp[0, :8, :8], forced_resp, atol=1e-6)
-    assert_allclose(results.magnitude[0, :8, :8], magnitude, atol=1e-6)
-    assert_allclose(results.phase[0, :8, :8], phase, atol=1e-6)
+    assert_allclose(results.forced_resp[0, :8, :8], forced_resp, atol=1e-8)
+    assert_allclose(results.magnitude[0, :8, :8], magnitude, atol=1e-8)
+    assert_allclose(results.phase[0, :8, :8], phase, atol=1e-8)
 
 
 def test_time_response(rotor1):
@@ -402,39 +433,33 @@ def test_time_response(rotor1):
 
     # fmt: off
     yout = np.array([
-       [[ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-          0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-          0.00000000e+00,  0.00000000e+00],
-        [ 1.41926788e-06, -4.76460045e-06,  2.07102404e-05,
-          6.16414329e-06,  2.90572612e-06, -9.75899792e-06,
-          1.84111116e-05,  5.47908613e-06],
-        [-4.19299249e-06, -2.72072007e-06,  1.18190692e-05,
-         -1.82276630e-05, -8.58856216e-06, -5.57088030e-06,
-          1.05060401e-05, -1.62024453e-05],
-        [-3.79851214e-06,  3.24877791e-06, -1.41256904e-05,
-         -1.65046953e-05, -7.77860208e-06,  6.65516143e-06,
-         -1.25561980e-05, -1.46710490e-05],
-        [ 2.03783269e-06,  4.56404302e-06, -1.98336856e-05,
-          8.86369112e-06,  4.17529112e-06,  9.34691210e-06,
-         -1.76301510e-05,  7.87880520e-06]],
-
-       [[ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-          0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
-          0.00000000e+00,  0.00000000e+00],
-        [ 1.48084281e-06, -4.85047585e-06,  4.21928512e-05,
-          1.28581579e-05,  4.58105873e-06, -1.50244567e-05,
-          3.74957287e-05,  1.14234040e-05],
-        [-4.19558895e-06, -2.72284012e-06,  2.36499572e-05,
-         -3.64745372e-05, -1.29913427e-05, -8.42599304e-06,
-          2.10223709e-05, -3.24217457e-05],
-        [-3.79910377e-06,  3.24942961e-06, -2.82561248e-05,
-         -3.30133748e-05, -1.17602388e-05,  1.00633351e-05,
-         -2.51165462e-05, -2.93455705e-05],
-        [ 2.03823954e-06,  4.56492726e-06, -3.96735422e-05,
-          1.77304732e-05,  6.31389854e-06,  1.41321453e-05,
-         -3.52656873e-05,  1.57603189e-05]],
+        [
+            [ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
+              0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00],
+            [ 1.41926788e-06, -4.76460045e-06,  2.07102404e-05,  6.16414329e-06,
+              2.90572612e-06, -9.75899792e-06,  1.84111116e-05,  5.47908613e-06],
+            [-4.19299249e-06, -2.72072007e-06,  1.18190692e-05, -1.82276630e-05,
+             -8.58856216e-06, -5.57088030e-06,  1.05060401e-05, -1.62024453e-05],
+            [-3.79851214e-06,  3.24877791e-06, -1.41256904e-05, -1.65046953e-05,
+             -7.77860208e-06,  6.65516143e-06, -1.25561980e-05, -1.46710490e-05],
+            [ 2.03783269e-06,  4.56404302e-06, -1.98336856e-05,  8.86369112e-06,
+              4.17529112e-06,  9.34691210e-06, -1.76301510e-05,  7.87880520e-06]
+        ],
+
+        [
+            [ 0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00,
+              0.00000000e+00,  0.00000000e+00,  0.00000000e+00,  0.00000000e+00],
+            [ 1.48084281e-06, -4.85047585e-06,  4.21928512e-05,  1.28581579e-05,
+              4.58105873e-06, -1.50244567e-05,  3.74957287e-05,  1.14234040e-05],
+            [-4.19558895e-06, -2.72284012e-06,  2.36499572e-05, -3.64745372e-05,
+             -1.29913427e-05, -8.42599304e-06,  2.10223709e-05, -3.24217457e-05],
+            [-3.79910377e-06,  3.24942961e-06, -2.82561248e-05, -3.30133748e-05,
+             -1.17602388e-05,  1.00633351e-05, -2.51165462e-05, -2.93455705e-05],
+            [ 2.03823954e-06,  4.56492726e-06, -3.96735422e-05,  1.77304732e-05,
+              6.31389854e-06,  1.41321453e-05, -3.52656873e-05,  1.57603189e-05]
+        ],
     ])
     # fmt: on
 
     assert results.yout.shape == (2, 5, 28)
-    assert_allclose(results.yout[:, :, :8], yout, atol=1e-6)
+    assert_allclose(results.yout[:, :, :8], yout, atol=1e-8)