Page 1 of 4

Modal Analysis + Mass Partiticipation Factors

Posted: Fri Feb 28, 2020 6:05 pm
by STKO Team
Dear Users,

We are currently working in implementing modal analysis and response spectrum analysis in STKO, since there is not such a feature in OpenSEES.

In the meantime here you can find a TCL procedure that peforms a modal analysis and prints results to both the terminal and a textfile.

Some notes:
  • It detects automatically the dimension of the problem (2D or 3D)
  • It assumes that the Mass matrix is diagonal. So it works well if you don't use the distributed mass command available in some elements. This limitation is due to the fact that in TCL we can get only the diagonal terms of nodal masses.
  • you can call the procedure in 2 ways:
    • modal $num_modes "ModalAnalysisReport.txt". In this way we use the defaul eigen solver wich uses the ARPACK library. This is the most efficient one for large sparse matrices. However there is a limitation in the ARNOLDI algorithm. You can ask for at most N-1 modes, where N is the maximum number of eigenmodes of the structure.
    • modal $num_modes "ModalAnalysisReport.txt" -fullGenLapack. With the LAPACK solver you can ask for all modes, however the LAPACK is made for small dense matrices.
  • We are currently validating it! It should work. However if you use it, please give us feedback, letting us know how it works for you.
Here is the code:

Code: Select all

proc modal { num_modes filename {eig_solver -genBandArpack}} {
	
	# begin
	puts "\nRunning modal analyis ..."
	
	# get all node tags
	set nodes [getNodeTags]
	if {[llength $nodes] == 0} {
		error "modal - Error: no node in model"
	}
	
	# check problem size (2D or 3D) from the first node, we do not support mixed dimesions!!
	set ndm [llength [nodeCoord [lindex $nodes 0]]]
	
	# compute total masses
	if {$ndm == 3} { 
		set ndf_max 6 
		set total_mass {0.0 0.0 0.0 0.0 0.0 0.0}
		set mass_labels {"MX" "MY" "MZ" "MRX" "MRY" "MRZ"}
		set mass_labels1 {"MODE" "MX" "MY" "MZ" "MRX" "MRY" "MRZ"}
	} else {
		set ndf_max 3
		set total_mass {0.0 0.0 0.0}
		set mass_labels {"MX" "MY" "MRZ"}
		set mass_labels1 {"MODE" "MX" "MY" "MRZ"}
	}
	foreach node $nodes {
		set indf [llength [nodeDisp $node]]
		for {set i 0} {$i < $indf} {incr i} {
			set imass [nodeMass $node [expr $i+1]]
			set imass_total [lindex $total_mass $i]
			lset total_mass $i [expr $imass_total + $imass]
		}
	}
	
	# some constants
	set pi [expr acos(-1.0)]
	
	# solve the eigenvalue problem
	set lambdas [eigen $eig_solver $num_modes]
	if {[llength $lambdas] != $num_modes} {
		error "modal - Error: something went wrong in the eigen analysis"
	}
	
	# results for each mode
	set mode_data [lrepeat $num_modes [lrepeat 4 0.0]]
	set mode_MPM [lrepeat $num_modes [lrepeat $ndf_max 0.0]]
	
	# process each mode of vibration
	for {set imode 0} {$imode < $num_modes} {incr imode} {
		
		# compute i-mode data
		set lambda [lindex $lambdas $imode]
		set omega [expr {sqrt($lambda)}]
		set frequency [expr $omega / 2.0 / $pi]
		set period [expr 1.0 / $frequency]
		lset mode_data $imode [list $lambda $omega $frequency $period]
		
		# M = mass matrix
		# V = eigen vector matrix
		# gm = V'* M * V = generalized mass matrix
		# R = influence vector
		# L = V' * M * R = coefficient vector
		# MPMi = L(i)^2 / gm(i,i) / total_mass * 100.0 = modal participation mass ratio (%)
		
		# compute L and gm
		set L [lrepeat $ndf_max 0.0]
		set gm 0.0
		foreach node $nodes {
			# get eigenvector
			set V [nodeEigenvector $node [expr $imode+1]]
			set indf [llength [nodeDisp $node]]
			# for each dof
			for {set i 0} {$i < $indf} {incr i} {
				set Mi [nodeMass $node [expr $i+1]]
				set Vi [lindex $V $i]
				set Li [expr $Mi * $Vi]
				set gm [expr $gm + $Vi * $Vi * $Mi]
				lset L $i [expr [lindex $L $i]+ $Li]
			}
		}
		
		# compute MPM
		set MPM [lrepeat $ndf_max 0.0]
		for {set i 0} {$i < $ndf_max} {incr i} {
			set Li [lindex $L $i]
			set TMi [lindex $total_mass $i]
			set MPMi [expr $Li * $Li]
			if {$gm > 0.0} {set MPMi [expr $MPMi / $gm]}
			if {$TMi > 0.0} {set MPMi [expr $MPMi / $TMi * 100.0]}
			lset MPM $i $MPMi
		}
		lset mode_MPM $imode $MPM
	}
	
	# print results to both stdout and file
	proc multiputs {args} {
		if { [llength $args] == 0 } {
			error "Usage: multiputs ?channel ...? string"
		} elseif { [llength $args] == 1 } {
			set channels stdout
		} else {
			set channels [lrange $args 0 end-1]
		}
		set str [lindex $args end]
		foreach ch $channels {
			puts $ch $str
		}
	}
	
	# open file for output
	set fp [open $filename w]
	
	multiputs stdout $fp "MODAL ANALYSIS REPORT"
	multiputs stdout $fp "\nPROBELM SIZE IS ${ndm}D"
	
	# print mode data
	multiputs stdout $fp "\nEIGENVALUE ANALYSIS"
	set format_string [string repeat "%16s" 5]
	set format_double [string repeat "%16g" 5]
	multiputs stdout $fp [format $format_string "MODE" "LAMBDA" "OMEGA" "FREQUENCY" "PERIOD"]
	for {set i 0} {$i < $num_modes} {incr i} {
		multiputs stdout $fp [format $format_double [expr $i+1] {*}[lindex $mode_data $i]]
	}
	
	multiputs stdout $fp "\nTOTAL MASS OF THE STRUCTURE"
	set format_string [string repeat "%16s" $ndf_max]
	set format_double [string repeat "%16g" $ndf_max]
	multiputs stdout $fp [format $format_string {*}$mass_labels]
	multiputs stdout $fp [format $format_double {*}$total_mass]
	
	# print modal participation masses ratio
	multiputs stdout $fp "\nMODAL PARTICIPATION MASSES (%)"
	set format_string [string repeat "%16s" [expr $ndf_max+1]]
	set format_double [string repeat "%16g" [expr $ndf_max+1]]
	multiputs stdout $fp [format $format_string {*}$mass_labels1]
	for {set i 0} {$i < $num_modes} {incr i} {
		multiputs stdout $fp [format $format_double [expr $i+1] {*}[lindex $mode_MPM $i]]
	}
	
	# print modal participation masses ratio
	multiputs stdout $fp "\nCUMULATIVE MODAL PARTICIPATION MASSES (%)"
	set format_string [string repeat "%16s" [expr $ndf_max+1]]
	set format_double [string repeat "%16g" [expr $ndf_max+1]]
	multiputs stdout $fp [format $format_string {*}$mass_labels1]
	set MPMsum [lrepeat $ndf_max 0.0]
	for {set i 0} {$i < $num_modes} {incr i} {
		set MPMi [lindex $mode_MPM $i]
		for {set j 0} {$j < $ndf_max} {incr j} {
			lset MPMsum $j [expr [lindex $MPMsum $j] + [lindex $MPMi $j]]
		}
		multiputs stdout $fp [format $format_double [expr $i+1] {*}$MPMsum]
	}
	
	# done
	close $fp
	puts "\nModal Analysis done\n"
}
And this is the kind of output you obtain:

Code: Select all

MODAL ANALYSIS REPORT

PROBELM SIZE IS 3D

EIGENVALUE ANALYSIS
            MODE          LAMBDA           OMEGA       FREQUENCY          PERIOD
               1         7921.43         89.0024         14.1652       0.0705957
               2           62190         249.379         39.6899       0.0251953
               3          129862         360.363         57.3536       0.0174357

TOTAL MASS OF THE STRUCTURE
              MX              MY              MZ             MRX             MRY             MRZ
               0               0               0            3.39               0               0

MODAL PARTICIPATION MASSES (%)
            MODE              MX              MY              MZ             MRX             MRY             MRZ
               1               0               0               0         91.4079               0              0
               2               0               0               0          7.4877               0              0
               3               0               0               0         1.10435               0              0

CUMULATIVE MODAL PARTICIPATION MASSES (%)
            MODE              MX              MY              MZ             MRX             MRY             MRZ
               1               0               0               0         91.4079               0              0
               2               0               0               0         98.8956               0              0
               3               0               0               0             100               0              0


Re: Modal Analysis + Mass Partiticipation Factors

Posted: Fri Apr 10, 2020 4:45 pm
by AcetoDivino
Thank you ! It Work very well !

Re: Modal Analysis + Mass Partiticipation Factors

Posted: Wed Jun 10, 2020 2:15 am
by gifariz
Thank you for creating this script, it works!

Re: Modal Analysis + Mass Partiticipation Factors

Posted: Sun Aug 16, 2020 5:24 am
by ankurjain1992
Why STKO Opensees is not considering the Nodal and Element loads for Eigen Analysis ?

I have done a problem in STKO as well as SAP2000. STKO is not considering the load for the Eigen Value Analysis whereas SAP2000 is considering. As soon as I remove the loads in SAP2000, the Eigen result matches with that of STKO ....

Why STKO is not considering loads for Eigen value calculation ?

Re: Modal Analysis + Mass Partiticipation Factors

Posted: Mon Aug 17, 2020 9:42 am
by STKO Team
Keep in mind that loads do not play a role in the eigenvalue analysis, it only requires K(stiffness) and M(mass) matrices.
Probably in SAP2000 loads are automatically converted in masses, while in OpenSees (and so in STKO) loads and masses are 2 indipendent things. Loads are not automatically converted into masses.

Re: Modal Analysis + Mass Partiticipation Factors

Posted: Tue Oct 20, 2020 9:12 am
by ankurjain1992
STKO Team wrote:
Fri Feb 28, 2020 6:05 pm
Dear Users,

We are currently working in implementing modal analysis and response spectrum analysis in STKO, since there is not such a feature in OpenSEES.

In the meantime here you can find a TCL procedure that peforms a modal analysis and prints results to both the terminal and a textfile.

Some notes:
  • It detects automatically the dimension of the problem (2D or 3D)
  • It assumes that the Mass matrix is diagonal. So it works well if you don't use the distributed mass command available in some elements. This limitation is due to the fact that in TCL we can get only the diagonal terms of nodal masses.
  • you can call the procedure in 2 ways:
    • modal $num_modes "ModalAnalysisReport.txt". In this way we use the defaul eigen solver wich uses the ARPACK library. This is the most efficient one for large sparse matrices. However there is a limitation in the ARNOLDI algorithm. You can ask for at most N-1 modes, where N is the maximum number of eigenmodes of the structure.
    • modal $num_modes "ModalAnalysisReport.txt" -fullGenLapack. With the LAPACK solver you can ask for all modes, however the LAPACK is made for small dense matrices.
  • We are currently validating it! It should work. However if you use it, please give us feedback, letting us know how it works for you.
Here is the code:

Code: Select all

proc modal { num_modes filename {eig_solver -genBandArpack}} {
	
	# begin
	puts "\nRunning modal analyis ..."
	
	# get all node tags
	set nodes [getNodeTags]
	if {[llength $nodes] == 0} {
		error "modal - Error: no node in model"
	}
	
	# check problem size (2D or 3D) from the first node, we do not support mixed dimesions!!
	set ndm [llength [nodeCoord [lindex $nodes 0]]]
	
	# compute total masses
	if {$ndm == 3} { 
		set ndf_max 6 
		set total_mass {0.0 0.0 0.0 0.0 0.0 0.0}
		set mass_labels {"MX" "MY" "MZ" "MRX" "MRY" "MRZ"}
		set mass_labels1 {"MODE" "MX" "MY" "MZ" "MRX" "MRY" "MRZ"}
	} else {
		set ndf_max 3
		set total_mass {0.0 0.0 0.0}
		set mass_labels {"MX" "MY" "MRZ"}
		set mass_labels1 {"MODE" "MX" "MY" "MRZ"}
	}
	foreach node $nodes {
		set indf [llength [nodeDisp $node]]
		for {set i 0} {$i < $indf} {incr i} {
			set imass [nodeMass $node [expr $i+1]]
			set imass_total [lindex $total_mass $i]
			lset total_mass $i [expr $imass_total + $imass]
		}
	}
	
	# some constants
	set pi [expr acos(-1.0)]
	
	# solve the eigenvalue problem
	set lambdas [eigen $eig_solver $num_modes]
	if {[llength $lambdas] != $num_modes} {
		error "modal - Error: something went wrong in the eigen analysis"
	}
	
	# results for each mode
	set mode_data [lrepeat $num_modes [lrepeat 4 0.0]]
	set mode_MPM [lrepeat $num_modes [lrepeat $ndf_max 0.0]]
	
	# process each mode of vibration
	for {set imode 0} {$imode < $num_modes} {incr imode} {
		
		# compute i-mode data
		set lambda [lindex $lambdas $imode]
		set omega [expr {sqrt($lambda)}]
		set frequency [expr $omega / 2.0 / $pi]
		set period [expr 1.0 / $frequency]
		lset mode_data $imode [list $lambda $omega $frequency $period]
		
		# M = mass matrix
		# V = eigen vector matrix
		# gm = V'* M * V = generalized mass matrix
		# R = influence vector
		# L = V' * M * R = coefficient vector
		# MPMi = L(i)^2 / gm(i,i) / total_mass * 100.0 = modal participation mass ratio (%)
		
		# compute L and gm
		set L [lrepeat $ndf_max 0.0]
		set gm 0.0
		foreach node $nodes {
			# get eigenvector
			set V [nodeEigenvector $node [expr $imode+1]]
			set indf [llength [nodeDisp $node]]
			# for each dof
			for {set i 0} {$i < $indf} {incr i} {
				set Mi [nodeMass $node [expr $i+1]]
				set Vi [lindex $V $i]
				set Li [expr $Mi * $Vi]
				set gm [expr $gm + $Vi * $Vi * $Mi]
				lset L $i [expr [lindex $L $i]+ $Li]
			}
		}
		
		# compute MPM
		set MPM [lrepeat $ndf_max 0.0]
		for {set i 0} {$i < $ndf_max} {incr i} {
			set Li [lindex $L $i]
			set TMi [lindex $total_mass $i]
			set MPMi [expr $Li * $Li]
			if {$gm > 0.0} {set MPMi [expr $MPMi / $gm]}
			if {$TMi > 0.0} {set MPMi [expr $MPMi / $TMi * 100.0]}
			lset MPM $i $MPMi
		}
		lset mode_MPM $imode $MPM
	}
	
	# print results to both stdout and file
	proc multiputs {args} {
		if { [llength $args] == 0 } {
			error "Usage: multiputs ?channel ...? string"
		} elseif { [llength $args] == 1 } {
			set channels stdout
		} else {
			set channels [lrange $args 0 end-1]
		}
		set str [lindex $args end]
		foreach ch $channels {
			puts $ch $str
		}
	}
	
	# open file for output
	set fp [open $filename w]
	
	multiputs stdout $fp "MODAL ANALYSIS REPORT"
	multiputs stdout $fp "\nPROBELM SIZE IS ${ndm}D"
	
	# print mode data
	multiputs stdout $fp "\nEIGENVALUE ANALYSIS"
	set format_string [string repeat "%16s" 5]
	set format_double [string repeat "%16g" 5]
	multiputs stdout $fp [format $format_string "MODE" "LAMBDA" "OMEGA" "FREQUENCY" "PERIOD"]
	for {set i 0} {$i < $num_modes} {incr i} {
		multiputs stdout $fp [format $format_double [expr $i+1] {*}[lindex $mode_data $i]]
	}
	
	multiputs stdout $fp "\nTOTAL MASS OF THE STRUCTURE"
	set format_string [string repeat "%16s" $ndf_max]
	set format_double [string repeat "%16g" $ndf_max]
	multiputs stdout $fp [format $format_string {*}$mass_labels]
	multiputs stdout $fp [format $format_double {*}$total_mass]
	
	# print modal participation masses ratio
	multiputs stdout $fp "\nMODAL PARTICIPATION MASSES (%)"
	set format_string [string repeat "%16s" [expr $ndf_max+1]]
	set format_double [string repeat "%16g" [expr $ndf_max+1]]
	multiputs stdout $fp [format $format_string {*}$mass_labels1]
	for {set i 0} {$i < $num_modes} {incr i} {
		multiputs stdout $fp [format $format_double [expr $i+1] {*}[lindex $mode_MPM $i]]
	}
	
	# print modal participation masses ratio
	multiputs stdout $fp "\nCUMULATIVE MODAL PARTICIPATION MASSES (%)"
	set format_string [string repeat "%16s" [expr $ndf_max+1]]
	set format_double [string repeat "%16g" [expr $ndf_max+1]]
	multiputs stdout $fp [format $format_string {*}$mass_labels1]
	set MPMsum [lrepeat $ndf_max 0.0]
	for {set i 0} {$i < $num_modes} {incr i} {
		set MPMi [lindex $mode_MPM $i]
		for {set j 0} {$j < $ndf_max} {incr j} {
			lset MPMsum $j [expr [lindex $MPMsum $j] + [lindex $MPMi $j]]
		}
		multiputs stdout $fp [format $format_double [expr $i+1] {*}$MPMsum]
	}
	
	# done
	close $fp
	puts "\nModal Analysis done\n"
}
And this is the kind of output you obtain:

Code: Select all

MODAL ANALYSIS REPORT

PROBELM SIZE IS 3D

EIGENVALUE ANALYSIS
            MODE          LAMBDA           OMEGA       FREQUENCY          PERIOD
               1         7921.43         89.0024         14.1652       0.0705957
               2           62190         249.379         39.6899       0.0251953
               3          129862         360.363         57.3536       0.0174357

TOTAL MASS OF THE STRUCTURE
              MX              MY              MZ             MRX             MRY             MRZ
               0               0               0            3.39               0               0

MODAL PARTICIPATION MASSES (%)
            MODE              MX              MY              MZ             MRX             MRY             MRZ
               1               0               0               0         91.4079               0              0
               2               0               0               0          7.4877               0              0
               3               0               0               0         1.10435               0              0

CUMULATIVE MODAL PARTICIPATION MASSES (%)
            MODE              MX              MY              MZ             MRX             MRY             MRZ
               1               0               0               0         91.4079               0              0
               2               0               0               0         98.8956               0              0
               3               0               0               0             100               0              0


Sir this code works fine for the masses that we assign to various nodes. But in case of Modal Participation factor, it is not considering the uniform mass distributed over the element (https://opensees.berkeley.edu/wiki/inde ... mn_Element).
I ran this code and i am getting the correct Eigen values but the modal participation factor includes the calculation of the Nodal mass only and not the Mass density defined for an element as per the above link.

Kindly tell me how shall i include that mass density also for the Modal Participation factor calculation.

Re: Modal Analysis + Mass Partiticipation Factors

Posted: Wed Oct 21, 2020 10:40 am
by STKO Team
Sir this code works fine for the masses that we assign to various nodes. But in case of Modal Participation factor, it is not considering the uniform mass distributed over the element
Yes it is correct. Since this script is made completely in TCL, the only function that we have to obtain masses at a node is the nodeMass command. However it only gives nodal masses epxlicitly given to the nodes, not those coming from the element.

I ran this code and i am getting the correct Eigen values but the modal participation factor includes the calculation of the Nodal mass only
Indeed the eigenvalues are correct, because the computation of the eigenvalue-problem is done by OpenSees, that have access to the full mass matrix (nodal masses + element masses). but the MPF computation is only a post.processing done in TCL, and there we don't have access to element masses.

We are currently working on implementing this modal computation directly inside opensees, maybe as an optional feature of the "eigen" command.
Until then, you can replace your distributed element masses, with equivalent distributed nodal masses that STKO offers, for example, in condition->masses->edgeMass/faceMass/volumeMass. They can be defined by the user as distrbuted masses (mass per unit length, mass per unit area, mass per unit volume, respectively), then STKO will do the nodal lumping based on the mesh, and will write equivalent nodal masses. In this way you will have only nodal masses.

Re: Modal Analysis + Mass Partiticipation Factors

Posted: Sun Nov 29, 2020 8:42 am
by volkanozs
STKO Team wrote:
Wed Oct 21, 2020 10:40 am
Sir this code works fine for the masses that we assign to various nodes. But in case of Modal Participation factor, it is not considering the uniform mass distributed over the element
Yes it is correct. Since this script is made completely in TCL, the only function that we have to obtain masses at a node is the nodeMass command. However it only gives nodal masses epxlicitly given to the nodes, not those coming from the element.

I ran this code and i am getting the correct Eigen values but the modal participation factor includes the calculation of the Nodal mass only
Indeed the eigenvalues are correct, because the computation of the eigenvalue-problem is done by OpenSees, that have access to the full mass matrix (nodal masses + element masses). but the MPF computation is only a post.processing done in TCL, and there we don't have access to element masses.

We are currently working on implementing this modal computation directly inside opensees, maybe as an optional feature of the "eigen" command.
Until then, you can replace your distributed element masses, with equivalent distributed nodal masses that STKO offers, for example, in condition->masses->edgeMass/faceMass/volumeMass. They can be defined by the user as distrbuted masses (mass per unit length, mass per unit area, mass per unit volume, respectively), then STKO will do the nodal lumping based on the mesh, and will write equivalent nodal masses. In this way you will have only nodal masses.
May I propose to use of the integrator GimmeMCK which is recently included in the OpenSees framework. It can be used to extract mass matrix and then modal analysis can be performed. I have written an example function for OpenSeespy version:
https://github.com/volkanozsarac/ModalA ... nalysis.py

I believe this can solve the issue with element masses.
Regards,
Volkan

Re: Modal Analysis + Mass Partiticipation Factors

Posted: Mon Nov 30, 2020 9:49 am
by STKO Team
May I propose to use of the integrator GimmeMCK which is recently included in the OpenSees framework. It can be used to extract mass matrix and then modal analysis can be performed. I have written an example function for OpenSeespy version
Dear user, thank you for your suggestion. However the complete mass matrix can already be extracted with the print command also with other integrators. The point is that you must use the FullGeneral as system. And it is fine for small models, because it uses a dense matrix storage.

Furthermore we are currently implementing this functionality directly in the C++ source code of OpenSees also with the possibility to run a Response Spectrum Analysis. These new features will be presented in our next e-learning course on December 17th!

Re: Modal Analysis + Mass Partiticipation Factors

Posted: Wed Dec 09, 2020 8:46 am
by volkanozs
That would be great! Thank you for the implementation.