pyyeti.cla.DR_Results.solvepsd

DR_Results.solvepsd(nas, case, DR, fs, forcepsd, t_frc, freq, *, use_apply_uf=False, verbose=False, allow_force_trimming=False, **kwargs)[source]

Solve equations of motion in frequency domain with PSD forces

See also pyyeti.ode.solvepsd() for a very similar routine, but one that is independent of the cla module.

Parameters:
  • nas (dictionary) – Typically, this is the nas2cam dictionary: nas = pyyeti.nastran.op2.rdnas2cam(). However, only the “nrb” member is needed directly by this routine (for uncertainty factor application). It is also passed to the data recovery routines (the drfunc setting in DR_Def.add()).

  • case (string) – Unique string identifying the case; stored in, for example, the self['SC_atm'].cases and the .mincase and .maxcase lists. Also used to index the temporary dictionary self['SC_atm']._psd (see Notes below).

  • DR (instance of DR_Event) – Defines data recovery for an event simulation (and is created in the simulation script via DR = cla.DR_Event()). It is an event specific version of all combined DR_Def objects with all ULVS matrices applied.

  • fs (class instance) – An instance of pyyeti.ode.SolveUnc or pyyeti.ode.FreqDirect (or similar … must have .fsolve method)

  • forcepsd (2d array_like) – Matrix of force psds; each row is a force

  • t_frc (2d array_like) – Transform to put forcepsd into the coordinates of the equations of motion: t_frc @ forcepsd. Commonly, t_frc is simply the transpose of a row-partition of the mode shape matrix (phi) and the conversion of forcepsd is from physical space to modal space. In that case, the row-partition is from the full set down to just the forced DOF. However, t_frc can also have force mappings (as from the TLOAD capability in Nastran); in that case, t_frc = phi.T @ mapping_vectors. In any case, the number of columns in t_frc is the number of rows in forcepsd: t_frc.shape[1] == forcepsd.shape[0]

  • freq (1d array_like) – Frequency vector at which solution will be computed

  • use_apply_uf (bool; optional) – If True, use DR_Event.apply_uf() instead of DR_Event.frf_apply_uf() to apply the uncertainty factors.

    Added in version 1.4.1.3.

    The primary difference is in the handling of displacements. Specifically, DR_Event.apply_uf() does the following things while DR_Event.frf_apply_uf() does not:

    1. Breaks the elastic displacements are broken into static and dynamic parts for uncertainty factor application.

    2. Zeros out the rigid-body displacements. This means that the displacement setting in incrb irrelevant for DR_Event.apply_uf() (as if “d” is not present in incrb).

    3. Handles residual flexibility responses statically.

  • verbose (bool; optional) – If True, print status messages and timer results.

  • allow_force_trimming (bool; optional) – If True, zero forces will be trimmed off to save time. Since this can cause trouble during data recovery if you have any force-dependent data recovery matrices (“drmf”), the default is False. It is advisable to trim off zero forces before calling this routine and trim the corresponding columns off any “drmf” matrices.

  • **kwargs (keyword arguments for fs.fsolve; optional) – Currently, there are two arguments available:

    argument

    brief description

    incrb

    specifies how to handle rigid-body responses

    rf_disp_only

    specifies how to handle residual-flexibility modes

    See, for example, pyyeti.ode.SolveUnc.fsolve().

Notes

The self results dictionary is updated (see DR_Results for an example).

This routine calls DR_Event.frf_apply_uf() or DR_Event.apply_uf() to apply the uncertainty factors (see use_apply_uf above).

The response PSDs are stored in a temporary dictionary index by the case; eg: self['SC_atm'][case]._psd. The routine psd_data_recovery() operates on this variable and deletes it after processing that last case. Note that some or all rows of this variable might be saved in self['SC_atm'].psd; this is determined according to the histpv setting defined through the call to DR_Def.add().

The solution loop is essentially as follows, written in a python-like pseudo-code:

for cat in categories:
    cat[case]._psd = 0.0

for i in range(forcepsd.shape[0]):
    # solve for unit frequency response function for i'th
    # force:
    genforce = t_frc[:, [i]] @ unitforce;
    sol = fs.fsolve(genforce, freq, **kwargs)
    sol.pg[:] = 0.0
    sol.pg[i] = unitforce
    sol = DR.frf_apply_uf(sol, nas["nrb"])

    # compute the unit frf's for all categories:
    for cat in categories:
        # call the drfunc set in cla.DR_Def.add for
        # current category
        resp = cat_drfunc(sol[uf_reds], nas, DR.Vars, se)
        # ex: resp = (ltma @ sol.a + ltmd @ sol.d
        #             + ltmf @ sol.pg)

        # compute psd response and add it on:
        cat._psd[case] += forcepsd[i] * abs(resp) ** 2

Note

If you have force-dependent data recovery matrices (which is typically from using the mode-acceleration method of data recovery for fundamentally displacement-based quantities), you may need to trim/reorder its columns to match forcepsd. Note that while looping over each PSD in forcepsd, this routine creates sol.pg sized compatibly with forcepsd and has only one row of 1.0 values. See also allow_force_trimming above.