Tutorial - MultiRotor System#
This is a basic tutorial on how to use the MultiRotor
class for rotordynamics analysis. Before starting this tutorial, be sure you’re already familiar with ROSS library. What changes here is basically the part of building the model, since running the analyses will be practically the same as seen in the other tutorials.
When two shafts are joined together by gears, coupling can occur between the lateral and torsional vibration. This interaction is modeled in ROSS based on [Rao et al., 1998]. In this work, a typical spur gear pair is modeled as a pair of rigid disks connected by a spring and a damping, considering the pressure angle (\(\alpha\)) and the oritentation angle (\(\varphi\)) as shown in the following:

Figure 1: Global coordinate system of a spur gear pair (Yang et al., 2016).
As you can see, an element not yet shown is needed to model the multi-rotor system, the GearElement
. This new element resembles the DiskElement
with added attributes, which will be shown next.
Section 1: GearElement Class#
The class GearElement
allows you to create gear elements. It is a subclass of DiskElement
that requires information related to its pitch diameter and pressure angle.
ROSS offers 2 (two) ways to create a gear element:
Inputing mass and inertia data
Inputing geometrical and material data
1.1 Creating a single gear element#
In this tutorial, only an example of how to create a single element will be shown. However, the same procedures presented for the DiskElement
in the first part of the Tutorial can be replicated here. The GearElement
goes into the same list of disk elements when assembling the rotor.
This example below shows how to instantiate a gear element according to the mass and inertia properties:
import ross as rs
import numpy as np
import pandas as pd
from ross.units import Q_
# Make sure the default renderer is set to 'notebook' for inline plots in Jupyter
import plotly.io as pio
import plotly.graph_objects as go
pio.renderers.default = "notebook"
gear = rs.GearElement(
n=0,
m=5,
Id=0.002,
Ip=0.004,
pitch_diameter=0.5,
pressure_angle=Q_(22.5, "deg"),
tag="Gear",
)
gear
GearElement(Id=0.002, Ip=0.004, m=5.0, color='Goldenrod', n=0, scale_factor=1.0, tag='Gear')
Section 2: MultiRotor Class#
MultiRotor
is a subclass of Rotor
class. It takes two rotors (driving and driven) as arguments and couple them with their gears. The object created has several methods that can be used to evaluate the dynamics of the model (they all start with the prefix .run_
).
To use this class, you must input the already instantiated rotors and each one need at least one gear element.
The shaft elements are renumbered starting with the elements of the driving rotor.
To assemble the matrices, the driving and driven matrices are joined. For the stiffness matrix, the coupling is considered at the nodes of the gears in contact.
2.1 Creating a spur geared two-shaft rotor system#
Let’s create a simple model with two rotors connected by a pair of spur gears and supported by flexible bearings, as shown in Fig. 2. For more details on the description of the model, see the work of [Rao et al., 1998].

Figure 2: Rotors connected by a pair of spur gears (Friswell et al., 2010).
In Figure 2, the first node is 1 (one), but we must remember that in ROSS the node count starts at 0 (zero).
2.1.1 Creating material#
# Creating material
material = rs.Material(name="mat_steel", rho=7800, E=207e9, G_s=79.5e9)
2.1.2 Creating the driving rotor#
# Creating rotor 1
L1 = [0.1, 4.24, 1.16, 0.3]
d1 = [0.3, 0.3, 0.22, 0.22]
shaft1 = [
rs.ShaftElement(
L=L1[i],
idl=0.0,
odl=d1[i],
material=material,
shear_effects=True,
rotary_inertia=True,
gyroscopic=True,
)
for i in range(len(L1))
]
generator = rs.DiskElement(
n=1,
m=525.7,
Id=16.1,
Ip=32.2,
)
disk = rs.DiskElement(
n=2,
m=116.04,
Id=3.115,
Ip=6.23,
)
pressure_angle = Q_(22.5, "deg").to_base_units().m
base_radius = 0.5086
pitch_diameter = 2 * base_radius / np.cos(pressure_angle)
gear1 = rs.GearElement(
n=4,
m=726.4,
Id=56.95,
Ip=113.9,
pitch_diameter=pitch_diameter,
pressure_angle=pressure_angle,
)
bearing1 = rs.BearingElement(n=0, kxx=183.9e6, kyy=200.4e6, cxx=3e3)
bearing2 = rs.BearingElement(n=3, kxx=183.9e6, kyy=200.4e6, cxx=3e3)
rotor1 = rs.Rotor(
shaft1,
[generator, disk, gear1],
[bearing1, bearing2],
)
rotor1.plot_rotor()
2.1.3 Creating the driven rotor#
# Creating rotor 2
L2 = [0.3, 5, 0.1]
d2 = [0.15, 0.15, 0.15]
shaft2 = [
rs.ShaftElement(
L=L2[i],
idl=0.0,
odl=d2[i],
material=material,
shear_effects=True,
rotary_inertia=True,
gyroscopic=True,
)
for i in range(len(L2))
]
base_radius = 0.03567
gear2 = rs.GearElement(
n=0,
m=5,
Id=0.002,
Ip=0.004,
base_diameter=base_radius * 2,
pressure_angle=pressure_angle,
)
turbine = rs.DiskElement(n=2, m=7.45, Id=0.0745, Ip=0.149)
bearing3 = rs.BearingElement(n=1, kxx=10.1e6, kyy=41.6e6, cxx=3e3)
bearing4 = rs.BearingElement(n=3, kxx=10.1e6, kyy=41.6e6, cxx=3e3)
rotor2 = rs.Rotor(
shaft2,
[gear2, turbine],
[bearing3, bearing4],
)
rotor2.plot_rotor()
2.1.4 Connecting rotors#
To build the multi-rotor model, we need to inform, in the following order:
the driving rotor,
the driven rotor,
the tuple with the pair of coupled nodes (first number corresponds to the gear node of the driving rotor, and the second of the driven rotor),
the gear ratio, and
the gear mesh stiffness.
Finally, we can inform:
the orientation angle (if not defined, zero is adopted as the default),
the position of the driven rotor in relation to the driving rotor only for visualization in the plot (“above” or “below”), and
a tag.
# Creating multi-rotor
N1 = 328 # Number of teeth of gear 1
N2 = 23 # Number of teeth of gear 2
k_mesh = 1e8 # Mesh stiffness
gear_ratio = N1 / N2
multirotor = rs.MultiRotor(
rotor1,
rotor2,
coupled_nodes=(4, 0),
gear_ratio=gear_ratio,
gear_mesh_stiffness=k_mesh,
orientation_angle=0,
position="below",
)
multirotor.plot_rotor()
2.2 Running analyses#
We will run some analyses for the multi-rotor in this section and even compare results from the literature.
2.2.1 Modal analysis#
Let’s start with the modal analysis to obtain the natural frequencies for the coupled rotor when the generator runs at 1500 RPM. Then we will compare the results with [Friswell, 2010].
It is worth noting that in the analyses, we must always inform the respective speed of the driving rotor and not the driven one.
# Friswell et al. (2010) results for natural frequencies:
Friswell_results = np.array(
[
11.641,
12.284,
17.268,
18.458,
23.956,
37.681,
49.889,
50.861,
56.248,
57.752,
59.188,
63.113,
74.203,
]
)
speed = Q_(1500, "RPM")
frequencies = 13
modal = multirotor.run_modal(speed, num_modes=2 * frequencies)
wd = np.round(Q_(modal.wd, "rad/s").to("Hz").m, 5)
print("Natural frequencies (Hz)")
pd.DataFrame(
{
"Friswell et al.": Friswell_results,
"ROSS": wd,
"Error (%)": np.abs(wd - Friswell_results) / Friswell_results * 100,
}
)
Natural frequencies (Hz)
Friswell et al. | ROSS | Error (%) | |
---|---|---|---|
0 | 11.641 | 11.64052 | 0.004123 |
1 | 12.284 | 12.28429 | 0.002361 |
2 | 17.268 | 17.26763 | 0.002143 |
3 | 18.458 | 18.45766 | 0.001842 |
4 | 23.956 | 23.95561 | 0.001628 |
5 | 37.681 | 37.68111 | 0.000292 |
6 | 49.889 | 49.88884 | 0.000321 |
7 | 50.861 | 50.86107 | 0.000138 |
8 | 56.248 | 56.24761 | 0.000693 |
9 | 57.752 | 57.75190 | 0.000173 |
10 | 59.188 | 59.18845 | 0.000760 |
11 | 63.113 | 63.11337 | 0.000586 |
12 | 74.203 | 74.20263 | 0.000499 |
2.2.2 Campbell diagram#
To obtain the Campbell diagram we can proceed in the same way as seen for a single rotor. Remember that the reference speeds / frequencies are in relation to the driving rotor.
In the Campbell diagram below, the dashed lines show the shaft rotation speeds corresponding to the generator (blue, node 1) and turbine (yellow, node 7).
frequency_range = Q_(np.arange(0, 5000, 100), "RPM")
campbell = multirotor.run_campbell(frequency_range, frequencies=13)
campbell.plot(frequency_units="Hz", harmonics=[1, round(gear_ratio, 3)]).show()
2.2.3 Time response#
Using the run_time_response
function we can obtain the system response due to an unbalance at nodes 2 and 7. The same setup presented in the work of [Yang et al., 2016] was applied here for comparison purposes.
nodes = [2, 7]
unb_mag = [35.505e-3, 0.449e-3]
unb_phase = [0, 0]
dt = 1e-4
t = np.arange(0, 1200, dt)
speed1 = Q_(5000, "RPM").to_base_units().m # Generator rotor speed
num_dof = multirotor.number_dof
# Unbalance force
F = np.zeros((len(t), multirotor.ndof))
for i, node in enumerate(nodes):
speed = multirotor.check_speed(node, speed1)
phi = speed * t + unb_phase[i]
dofx = num_dof * node + 0
dofy = num_dof * node + 1
F[:, dofx] += unb_mag[i] * (speed**2) * np.cos(phi)
F[:, dofy] += unb_mag[i] * (speed**2) * np.sin(phi)
# Time response
time_resp = multirotor.run_time_response(speed1, F, t)
amp_resp = time_resp.yout
Due to computational cost limitations, the entire analysis performed for comparison is not included in this tutorial. However, by running the run_time_response
function across the complete range of speeds, it is possible to obtain the following responses at nodes 2 and 7 as shown in the figures below:


Figure 3: Comparison of ROSS results with Yang et al. (2016) for the spur geared two-shaft rotor system.
2.3 Creating a spur geared multi-shaft rotor system#
Let’s create a more complex model with three connected rotors. More details of this example can be found in [Yang et al., 2016].
# Rotor 1
L1 = [300, 92, 200, 200, 92, 300]
r1 = [61.5, 75, 75, 75, 75, 61.5]
shaft1 = [
rs.ShaftElement(
L=L1[i] * 1e-3,
idl=0.0,
odl=r1[i] * 2e-3,
material=material,
shear_effects=True,
rotary_inertia=True,
gyroscopic=True,
)
for i in range(len(L1))
]
D1 = rs.DiskElement(
n=0,
m=66.63,
Id=0.431,
Ip=0.735,
)
D2 = rs.DiskElement(
n=6,
m=69.83,
Id=0.542,
Ip=0.884,
)
cxx = 3e3
B1 = rs.BearingElement(n=2, kxx=5.5e8, kyy=6.7e8, cxx=cxx)
B2 = rs.BearingElement(n=4, kxx=5.5e8, kyy=6.7e8, cxx=cxx)
pressure_angle = Q_(22.5, "deg").to_base_units().m
G1 = rs.GearElement(
n=3,
m=14.37,
Id=0.068,
Ip=0.136,
base_diameter=0.19,
pressure_angle=pressure_angle,
)
rotor1 = rs.Rotor(
shaft1,
[D1, D2, G1],
[B1, B2],
)
# Rotor 2
L2 = [80, 200, 200, 640]
r2 = [160.5, 160.5, 130.5, 130.5]
shaft2 = [
rs.ShaftElement(
L=L2[i] * 1e-3,
idl=0.0,
odl=r2[i] * 2e-3,
material=material,
shear_effects=True,
rotary_inertia=True,
gyroscopic=True,
)
for i in range(len(L2))
]
B3 = rs.BearingElement(n=1, kxx=3.2e9, kyy=4.6e9, cxx=cxx)
B4 = rs.BearingElement(n=3, kxx=3.2e9, kyy=4.6e9, cxx=cxx)
G2 = rs.GearElement(
n=2,
m=813.79,
Id=52.36,
Ip=104.72,
base_diameter=1.23,
pressure_angle=pressure_angle,
)
rotor2 = rs.Rotor(
shaft2,
[G2],
[B3, B4],
)
# Rotor 3
L3 = [300, 110, 200, 200, 110, 300]
r3 = [64.5, 76.5, 76.5, 76.5, 76.5, 64.5]
shaft3 = [
rs.ShaftElement(
L=L3[i] * 1e-3,
idl=0.0,
odl=r3[i] * 2e-3,
material=material,
shear_effects=True,
rotary_inertia=True,
gyroscopic=True,
)
for i in range(len(L1))
]
D3 = rs.DiskElement(
n=0,
m=95.06,
Id=1.097,
Ip=1.532,
)
D4 = rs.DiskElement(
n=6,
m=96.22,
Id=1.110,
Ip=1.620,
)
G3 = rs.GearElement(
n=3,
m=19.52,
Id=0.098,
Ip=0.195,
base_diameter=0.24,
pressure_angle=pressure_angle,
)
B5 = rs.BearingElement(n=2, kxx=7.2e8, kyy=8.4e8, cxx=cxx)
B6 = rs.BearingElement(n=4, kxx=7.2e8, kyy=8.4e8, cxx=cxx)
rotor3 = rs.Rotor(
shaft3,
[D3, D4, G3],
[B5, B6],
)
# Connect rotor 1 with rotor 2 (driving rotor)
multi_rotor1 = rs.MultiRotor(
rotor2,
rotor1,
coupled_nodes=(2, 3),
gear_ratio=244 / 37,
gear_mesh_stiffness=2.55e8,
orientation_angle=Q_(270, "deg").to_base_units().m,
position="above",
)
# Connect rotor 3 with rotor 2 in multi rotor
psi = 90
final_system = rs.MultiRotor(
multi_rotor1,
rotor3,
coupled_nodes=(2, 3),
gear_ratio=244 / 47,
gear_mesh_stiffness=2.6e8,
orientation_angle=Q_(270 - psi, "deg").to_base_units().m,
position="below",
)
final_system.plot_rotor()
If we apply two unbalances with 92 g.mm in phase opposition at two ends of rotor 1 (nodes 5 and 11), while one unbalance with 294 g.mm at the middle of rotor 3 (node 15), we can obtain the following responses at node 15:

Figure 4: Comparison of ROSS results with Yang et al. (2016) for the spur geared multi-shaft rotor system.
We can also vary the orientation angle between the rotors:

Figure 5: Unbalance responses at node 15 with three orientation angles.
References#
Michael I Friswell. Dynamics of rotating machines. Cambridge University Press, 2010.
JS Rao, TN Shiau, and JR Chang. Theoretical analysis of lateral response due to torsional excitation of geared rotors. Mechanism and Machine Theory, 33(6):761–783, 1998. doi:https://doi.org/10.1016/S0094-114X(97)00056-6.
Yi Yang, Jiaying Wang, Xurong Wang, and Yiping Dai. A general method to predict unbalance responses of geared rotor systems. Journal of Sound and Vibration, 381:246–263, 2016. doi:https://doi.org/10.1016/j.jsv.2016.06.031.