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