Modal Participation Factors

Computing modal participation factors (yes, I know it’s a misnomer) from an OpenSees model is straightforward if you define only nodal mass with no element mass. Examples are available online showing how to compute the factors in OpenSees using Tcl, but let’s go over how to do it with OpenSeesPy.

After you define your model with nodal mass and do an eigenvalue analysis, you can write a simple loop with a couple utility functions to evaluate \Gamma_n = \phi_n^T m \iota / \phi_n^T m \phi_n for the n^{th} mode. In the code below, I’m wrapping that simple loop with another loop over N modes.

# Define your model
ndf = 6 # Nodal degrees of freedom

N = 3 # Or whatever
omega2 = ops.eigen(N)

for n in range(N):
   Mn = 0.0
   Ln = 0.0
   for nd in ops.getNodeTags():
      ndMass = ops.nodeMass(nd)
      ndEigen = ops.nodeEigenvector(nd,n+1)
      Ln += ndEigen[0]*ndMass[0] # 0 for X, 1 for Y, 2 for Z excitation
      for dof in range(ndf):
         Mn += (ndEigen[dof]**2)*ndMass[dof]
   Gamman = Ln/Mn
   Tn = 2*3.14159/omega2[n]**0.5
   print(f'Mode {n+1}, Tn = {Tn}, Mn = {Mn}, Gamma = {Gamman}')

The default eigenvalue solver returns mass orthonormal eigenvectors, leading to unit values of modal mass. However, this solver will fail if you ask for as many modes as there are dynamic DOFs, e.g., asking for 4 modes from a model with 4 dynamic DOFs gives the following error:

To get all modes from the eigenvalue analysis, you can use the 'fullGenLapack' option to the eigen command.

omega2 = ops.eigen('fullGenLapack',Nmodes)

While this solver returns the same eigenvalues as the default solver, the eigenvectors are not normalized by mass. So, the modal masses will not be 1.0 and the modal participation factors you calculate will be different from the default solver. For example, asking for 3 modes using both solvers gives the following output. The periods are the same, but not the modal quantities.

The first output uses the default eigenvalue solver in OpenSees, while the second output uses ‘fullGenLapack’.

Most of these differences come out in the wash. Except for modal damping. The non-mass-normalized eigenvectors can really mess up your modal damping. So, once again, be careful with modal damping.

Update: The issue with fullGenLapack not returning mass orthonormal eigenvectors was fixed in January 2022 with this commit.

16 thoughts on “Modal Participation Factors

  1. Dear PD,

    I have generalized the calculation of modal mass participation to consider also cases where you have distributed element mass definition, and proposed my function to the developers who contribute to openseespy post-processing section. My function is basically written to find out modal mass participation in transnational excitation directions.
    Yet, I have doubts regarding the calculation of modal mass participation in rotational excitation directions. The commercial software such as SAP2000 and SeismoStruct yields in different results for these terms. I guess this is due to choice of different strategies in calculation of influence vectors. Simply, the vertical reference axis we use influences the values we find. Should we use the center of mass? Or center of stiffness? What is the mechanical meaning of the mass participation axis around a vertical axis? I am not sure if there are right answers for this question but I was wondering your opinion on this manner.

    Thank you


    1. Hello Volkan,
      It’s not something I’ve looked at too much, but it seems rotational excitations are not uniquely defined. You can define the mass influence vector for rotation about any point. As long as it’s clearly documented what each software is doing it’s probably OK, although I assume you can’t change the assumptions made in SAP2000 or SeismoStruct.


    1. You may have look at this, I have utilized recent addition by PD (the integrator GimmeMCK) to calculate them, and proposed this function.
      Cheers, volkan


      1. Hello volkan,
        Thanks for sharing! I was planning to write a short post about getting modal mass, stiffness, damping. I’ll show the basic idea, then refer to your code 🙂


      2. Dear PD,

        I think your posts and the opensees community deserve the real credit, it was only possible for me to develop the code after reading your posts. I am glad if it is going to be useful to the research community.


        Liked by 1 person

  2. Hello, PD!

    What version of OpenSeesPy are you using? I tried running your code with version 3.2.2 (last one from pip) and I got an error because the command ops.nodeMass() expects two arguments. I fixed it by looping over the number of degrees of freedom and appending to a list. However, I wonder if it is just a version problem.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.