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 for the 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.

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.

I have been involved in the development, maintenance, and growth of OpenSees since its early days. I am interested in learning Python and improving my academic writing.
View all posts by Michael H. Scott

Published

16 thoughts on “Modal Participation Factors”

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.

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.
PD

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

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 ðŸ™‚
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.

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.

Hola Gustavo,
I recently changed the nodeMass command to return a list, but it is in the latest version of OpenSeesPy (3.2.2.6). Try ‘pip install openseespy –upgrade’.
PD

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

LikeLike

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.

PD

LikeLike

Thank you for your reply, and valuable comments.

LikeLiked by 1 person

It would be cool to have things like this in OpenSeesPy.

I look forward to the daily posts this month!

LikeLiked by 1 person

Thanks, denavit!

By “in OpenSeesPy”, do you mean in the pip install package?

LikeLike

Yeah!

Something like “from openseespy.postprocessing import modal_participation_factors”

LikeLiked by 1 person

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

LikeLike

https://github.com/volkanozsarac/ModalAnalysis-OpenSeespy/blob/master/ModalAnalysis.py

LikeLiked by 1 person

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 ðŸ™‚

PD

LikeLike

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.

Cheers,

Volkan

LikeLiked by 1 person

Thanks, I was looking for a function that helped me to calculate Mass factor and so on.

Hope it well be integrated soon on OpenseesPy

LikeLike

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.

LikeLike

Hola Gustavo,

I recently changed the nodeMass command to return a list, but it is in the latest version of OpenSeesPy (3.2.2.6). Try ‘pip install openseespy –upgrade’.

PD

LikeLike