pyyeti.ode.getmodepart

pyyeti.ode.getmodepart(h_or_frq, sols, mfreq, factor=0.6666666666666666, helpmsg=True, ylog=False, auto=None, idlabel='', frf_ttl='')[source]

Get modal participation from frequency response plots.

Parameters:
  • h_or_frq (list/tuple or 1d ndarray) – Plot line handles or frequency vector:

    • If list/tuple, it contains the plot line handles to the FRF curves; in this case, the analysis frequency is retrieved from the plot.

    • If it is a 1d ndarray, it is the frequency vector; in this case, a plot of FRFs (from sols) is generated in figure ‘FRF’ (or ‘FRF - ‘+idlabel)

  • sols (list/tuple of lists/tuples) –

    Contains info to determine modal particpation:

    sols = [[Trecover1, accel1, Trecover1_row_labels],
            [Trecover2, accel2, Trecover2_row_labels],
             ... ]
    
    • each Trecover matrix is: any number x modes

    • each accel matrix is: modes x frequencies

    • each row_labels entry is a list/tuple: len = # rows in corresponding Trecover (if Trecover only has 1 row, then row_labels may be just a string)

    The FRFs are recovered by:

    FRFs1 = Trecover1*accel1
    FRFs2 = Trecover2*accel2
    ...
    

    accel1, accel2, etc are the complex modal acceleration (or displacement or velocity) frequency responses; normally output by, for example, SolveUnc.fsolve()

  • mfreq (array_like) – Vector of modal frequencies (Hz)

  • factor (scalar; optional) – From 0 to 1 for setting the criteria for choosing secondary modes: if mode participation of secondary mode(s) >= factor * max_participation, include it.

  • helpmsg (bool; optional) – If True, print brief message explaining the mouse buttons before plotting anything

  • ylog (bool; optional) – If True, y-axis will be log

  • auto (list/tuple or None; optional) –

    • If None, operate interactively.

    • If a 2 element vector, it specifies an item in sols and a Trecover row (0-offset) that this routine will automatically (non-interactively) use to select modes. It will select the peak of the specified response and, based on mode participation, return the results. In other words, it acts as if you picked the peak of the specified curve and then hit ‘t’. The 1st element of auto selects which sols entry to use and the 2nd selects the matrix row. For example, to choose the 12th row of Trecover3, set auto to [2, 11].

  • idlabel (string; optional If not ‘’, it will be) – used in the figure name. This allows multiple getmodepart()’s to be run with the same model, each using its own FRF and MP windows. The figure names will be:

    'FRF - '+idlabel   <-- used only if h_or_frq is freq
    'MP - '+idlabel
    
  • frf_ttl (string; optional) – Title used for FRF plot

Returns:

  • modes (list) – List of selected mode numbers; 0-offset

  • freqs (1d ndarray) – Vector of frequencies in Hz corresponding to modes.

Notes

FRF peaks can only be selected in range of the analysis frequency, but modes outside this range may be selected based on modal participation.

This routine will echo modal participation factors to the screen and plot them in figure ‘MP’ (or ‘MP - ‘+idlabel).

If auto is None (or some other non-2-element object), this routine works as follows:

  1. If h is frequency vector, plot FRFs from sols in figure ‘FRF’ or ‘FRF - ‘+idlabel

  2. Repeat:

    1. Waits for user to select response. Mouse/key commands:

      Left  - select response point; valid only in the
              FRF figure
      Right - erase last selected mode(s)
      't'   - done
      
    2. Plots mode participation bar graph in figure ‘MP’ (or ‘MP - ‘+idlabel) showing the frequency(s) of modes selected.

If using auto, no plots are generated (see auto description above).

Examples

>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> from pyyeti.ode import SolveUnc
>>> from pyyeti.ode import getmodepart
>>> import scipy.linalg as la
>>> rng = np.random.default_rng()
>>> K = 40*rng.normal(size=(5, 5))
>>> K = K @ K.T  # pos-definite K matrix, M is identity
>>> M = None
>>> w2, phi = la.eigh(K)
>>> mfreq = np.sqrt(w2)/2/np.pi
>>> zetain = np.array([ .02, .02, .05, .02, .05 ])
>>> Z = np.diag(2*zetain*np.sqrt(w2))
>>> freq = np.arange(0.1, 15.05, .1)
>>> f = np.ones((1, len(freq)))
>>> Tbot = phi[0:1, :]
>>> Tmid = phi[2:3, :]
>>> Ttop = phi[4:5, :]
>>> fs = SolveUnc(M, Z, w2)
>>> sol_bot = fs.fsolve(Tbot.T @ f, freq)
>>> sol_mid = fs.fsolve(Tmid.T @ f, freq)

Prepare transforms and solutions for getmodepart(): (Note: the top 2 items in sols could be combined since they both use the same acceleration)

>>> sols = [[Tmid, sol_bot.a, 'Bot to Mid'],
...         [Ttop, sol_bot.a, 'Bot to Top'],
...         [Ttop, sol_mid.a, 'Mid to Top']]

Approach 1: let getmodepart() do the FRF plotting:

>>> lbl = 'getmodepart demo 1'
>>> mds, frqs = getmodepart(freq, sols,
...                         mfreq, ylog=1,
...                         idlabel=lbl)
>>> print('modes =', mds)
>>> print('freqs =', frqs)

Approach 2: plot FRFs first, then call getmodepart():

>>> fig = plt.figure('approach 2 FRFs')
>>> fig.clf()
>>> for s in sols:
...     plt.plot(freq, abs(s[0] @ s[1]).T,
...              label=s[2])
>>> _ = plt.xlabel('Frequency (Hz)')
>>> plt.yscale('log')
>>> _ = plt.legend(loc='best')
>>> h = plt.gca().lines
>>> lbl = 'getmodepart demo 2'
>>> modes, freqs = getmodepart(h, sols,
...                            mfreq,
...                            idlabel=lbl)
>>> print('modes =', modes)
>>> print('freqs =', freqs)