Source code for pyNastran.bdf.cards.methods

# pylint: disable=C0103,R0902,R0904,R0914
"""
All method cards are defined in this file.  This includes:

 * EIGB
 * EIGC
 * EIGR
 * EIGP
 * EIGRL

All cards are Method objects.

"""
from __future__ import annotations
from typing import Any, TYPE_CHECKING
from pyNastran.bdf.field_writer_8 import set_blank_if_default
from pyNastran.bdf.cards.base_card import BaseCard
from pyNastran.bdf.bdf_interface.assign_type import (
    integer, integer_or_blank, double, double_or_blank, string, string_or_blank,
    parse_components, components_or_blank, integer_double_string_or_blank, blank,
    interpret_value)
from pyNastran.bdf.field_writer_8 import print_card_8
from pyNastran.bdf.field_writer_16 import print_card_16
if TYPE_CHECKING:  # pragma: no cover
    from pyNastran.bdf.bdf import BDF


[docs] class Method(BaseCard): """ Generic class for all methods. Part of self.methods """ def __init__(self): pass
[docs] class EIGB(Method): """Defines data needed to perform buckling analysis""" type = 'EIGB'
[docs] @classmethod def _init_from_empty(cls): sid = 1 method = 'INV' G = 1 C = 1 norm = 'MAX' L1 = 1.0 L2 = 2.0 nep = 10 ndp = 20 ndn = 30 return EIGB(sid, method, L1, L2, nep, ndp, ndn, norm, G, C, comment='')
def __init__(self, sid, method, L1, L2, nep, ndp, ndn, norm, G, C, comment=''): Method.__init__(self) if comment: self.comment = comment #: Set identification number. (Unique Integer > 0) self.sid = sid #: Method of eigenvalue extraction. (Character: 'INV' for inverse #: power method or 'SINV' for enhanced inverse power method.) #: apparently it can also be blank... self.method = method #: Eigenvalue range of interest. (Real, L1 < L2) self.L1 = L1 self.L2 = L2 #: Estimate of number of roots in positive range not used for #: METHOD = 'SINV'. (Integer > 0) self.nep = nep #: Desired number of positive and negative roots. #: (Integer>0; Default = 3*NEP) self.ndp = ndp self.ndn = ndn #: Method for normalizing eigenvectors. #: ('MAX' or 'POINT';Default='MAX') self.norm = norm self.G = G self.C = C if not self.L1 < self.L2: msg = 'L1=%s L2=%s; L1<L2 is required' % (self.L1, self.L2) raise RuntimeError(msg) if self.method not in ['INV', 'SINV', None]: msg = 'method must be INV or SINV. method=%r' % self.method raise RuntimeError(msg)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds an EIGB card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') method = string_or_blank(card, 2, 'method') L1 = double(card, 3, 'L1') L2 = double(card, 4, 'L2') nep = integer_or_blank(card, 5, 'nep', 0) ndp = integer_or_blank(card, 6, 'ndp', 3 * nep) ndn = integer_or_blank(card, 7, 'ndn', 3 * nep) norm = string_or_blank(card, 9, 'norm', 'MAX') if norm == 'POINT': G = integer(card, 10, 'G') C = parse_components(card, 11, 'C', None) else: G = integer_or_blank(card, 10, 'G') C = components_or_blank(card, 11, 'C', None) assert len(card) <= 12, f'len(EIGB card) = {len(card):d}\ncard={card}' return EIGB(sid, method, L1, L2, nep, ndp, ndn, norm, G, C, comment=comment)
[docs] def cross_reference(self, model: BDF) -> None: pass
[docs] def raw_fields(self): list_fields = ['EIGB', self.sid, self.method, self.L1, self.L2, self.nep, self.ndp, self.ndn, None, self.norm, self.G, self.C] return list_fields
[docs] def repr_fields(self): #method = set_blank_if_default(self.method,'INV') nep = set_blank_if_default(self.nep, 0) ndp = set_blank_if_default(self.ndp, 3 * self.nep) ndn = set_blank_if_default(self.ndn, 3 * self.nep) norm = set_blank_if_default(self.norm, 'MAX') list_fields = ['EIGB', self.sid, self.method, self.L1, self.L2, nep, ndp, ndn, None, norm, self.G, self.C] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] class EIGC(Method): """ Defines data needed to perform complex eigenvalue analysis .. todo: not done ``inverse power`` +------+---------+---------+---------+---------+---------+---------+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +======+=========+=========+=========+=========+=========+=========+=====+ | EIGC | SID | METHOD | | | | EPS | ND0 | +------+---------+---------+---------+---------+---------+---------+-----+ | | ALPHAAj | OMEGAAj | ALPHABj | OMEGABj | Lj | NEj | NDj | +------+---------+---------+---------+---------+---------+---------+-----+ ``complex Lanczos`` +------+---------+---------+---------+---------+---------+---------+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +======+=========+=========+=========+=========+=========+=========+=====+ | | SHIFTRj | SHIFTIj | MBLKSZj | IBLKSZj | KSTEPSj | NDj | | +------+---------+---------+---------+---------+---------+---------+-----+ ``iterative Schur-Rayleigh-Ritz`` +------+---------+---------+---------+---------+---------+---------+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +======+=========+=========+=========+=========+=========+=========+=====+ | | SHIFTR1 | SHIFTI1 | | | | ISRRFLG | ND1 | +------+---------+---------+---------+---------+---------+---------+-----+ """ type = 'EIGC'
[docs] @classmethod def _init_from_empty(cls): sid = 1 method = 'CLAN' grid = 1 component = 1 epsilon = 0.1 neigenvalues = 10 return EIGC(sid, method, grid, component, epsilon, neigenvalues, norm='MAX', mblkszs=None, iblkszs=None, ksteps=None, NJIs=None, alphaAjs=None, omegaAjs=None, alphaBjs=None, omegaBjs=None, LJs=None, NEJs=None, NDJs=None, shift_r1=None, shift_i1=None, isrr_flag=None, nd1=None, comment='')
def __init__(self, sid, method, grid, component, epsilon, neigenvalues, norm='MAX', # common mblkszs=None, iblkszs=None, ksteps=None, NJIs=None, # CLAN alphaAjs=None, omegaAjs=None, alphaBjs=None, omegaBjs=None, # HESS/INV LJs=None, NEJs=None, NDJs=None, # HESS/INV shift_r1=None, shift_i1=None, isrr_flag=None, nd1=None, # ISRR comment=''): """ Creates a EIGC card, which is required for a SOL 107 analysis Parameters ---------- sid : int CMETHOD id in the case control deck method : str Method of complex eigenvalue extraction MSC 2014 = [INV, HESS, CLAN, IRAM] NX 8.5 = [INV, HESS, CLAN, ISRR] Autodesk 2015 = [ARNO, HESS, CLAN] INV : Inverse Power IRAM : Implicitly Restarted Arnoldi method ISRR : Iterative Schur-Rayleigh-Ritz method CLAN : Complex Lanczos. For linear perturbation of ANALYSIS=DCEIG with large displacement, CLAN is recommended. HESS : Upper Hessenberg. For linear perturbation of ANALYSIS=DCEIG with large displacement, please don't use HESS. ARNO: ??? norm : str; default='MAX' Method for normalizing eigenvectors valid_norm = {MAX, POINT} grid : int GRID/SPOINT id Required if norm='POINT' component : int Required if norm='POINT' epsilon : float neigenvalues : int Number of Eigenvalues mblkszs : list[float]; default=None used by CLAN iblkszs : list[int]; default=None used by CLAN ksteps : list[int]; default=None used by CLAN NJIs : list[int]; default=None used by CLAN alphaAjs : list[float]; default=None used by HESS/INV omegaAjs : list[float]; default=None used by HESS/INV alphaBjs : list[float]; default=None used by HESS/INV omegaBjs : list[float]; default=None used by HESS/INV LJs : list[float]; default=None used by HESS/INV NEJs : list[int]; default=None used by HESS/INV NDJs : list[int]; default=None used by HESS/INV shift_r1 : list[float]; default=None used by ISSR shift_i1 : list[float]; default=None used by ISSR isrr_flag : list[int]; default=None used by ISSR nd1 : list[int]; default=None used by ISSR comment : str; default='' a comment for the card """ Method.__init__(self) if comment: self.comment = comment #: Set identification number. (Unique Integer > 0) self.sid = sid #: Method of complex eigenvalue extraction #: MSC 2014 = [INV, HESS, CLAN, IRAM] #: NX 8.5 = [INV, HESS, CLAN, ISRR] #: Autodesk 2015 = [ARNO, HESS, CLAN] self.method = method #: Method for normalizing eigenvectors self.norm = norm #: Grid or scalar point identification number. Required only if #: NORM='POINT'. (Integer>0) self.G = grid #: Component number. Required only if NORM='POINT' and G is a #: geometric grid point. (1<Integer<6) self.C = component #: Convergence criterion. (Real > 0.0. Default values are: #: 10^-4 for METHOD = "INV", #: 10^-8 for METHOD = "CLAN", #: 10^-8 for METHOD = "ISRR", #: 10^-15 for METHOD = "HESS", #: E is machine dependent for METHOD = "CLAN".) self.epsilon = epsilon #Number of eigenvalues and/or eigenvectors desired. See Remark #3. (Integer > 0 or blank; No default) self.neigenvalues = neigenvalues # CLAN if mblkszs is None: mblkszs = [] if iblkszs is None: iblkszs = [] if ksteps is None: ksteps = [] if NJIs is None: NJIs = [] self.mblkszs = mblkszs self.iblkszs = iblkszs self.ksteps = ksteps self.NJIs = NJIs # HESS if alphaBjs is None: alphaBjs = [] if omegaBjs is None: omegaBjs = [] self.alphaBjs = alphaBjs self.omegaBjs = omegaBjs if LJs is None: LJs = [] self.LJs = LJs if NEJs is None: NEJs = [] self.NEJs = NEJs if NDJs is None: NDJs = [] self.NDJs = NDJs if alphaAjs is None: alphaAjs = [] if omegaAjs is None: omegaAjs = [] self.alphaAjs = alphaAjs self.omegaAjs = omegaAjs #---------- # ISRR self.shift_r1 = shift_r1 self.shift_i1 = shift_i1 self.isrr_flag = isrr_flag self.nd1 = nd1
[docs] def validate(self): assert self.norm in ['MAX', 'POINT'], 'norm=%r' % self.norm nalpha_a = len(self.alphaAjs) assert nalpha_a == len(self.omegaAjs), 'alphaAjs=%s omegaAj=%s' % (self.alphaAjs, self.omegaAjs) if self.method in ['HESS', 'INV']: nalpha_b = len(self.alphaBjs) assert nalpha_a == nalpha_b, 'alphaAjs=%s alphaBj=%s' % (self.alphaAjs, self.alphaBjs) #assert nalpha_a == len(self.omegaBjs), 'alphaAjs=%s omegaBjs=%s' % (self.alphaAjs, self.omegaBjs) assert nalpha_a == len(self.LJs), 'alphaAjs=%s LJs=%s' % (self.alphaAjs, self.LJs) assert nalpha_a == len(self.NEJs), 'alphaAjs=%s NEJs=%s' % (self.alphaAjs, self.NEJs) assert nalpha_a == len(self.NDJs), 'alphaAjs=%s NDJs=%s' % (self.alphaAjs, self.NDJs) elif self.method == 'CLAN': nalpha_b = len(self.alphaBjs) if nalpha_a == nalpha_b: assert nalpha_a == nalpha_b, f'nalpha_a={nalpha_a} nalpha_b={nalpha_b}' assert nalpha_a == len(self.omegaBjs), f'nalpha_a={nalpha_a} nomega_b={len(self.omegaBjs)}' assert nalpha_a == len(self.LJs) assert nalpha_a == len(self.NEJs) assert nalpha_a == len(self.NDJs) else: assert nalpha_a == len(self.omegaAjs) assert nalpha_a == len(self.mblkszs), 'alphaAjs=%s mblkszs=%s' % (self.alphaAjs, self.mblkszs) assert nalpha_a == len(self.iblkszs) assert nalpha_a == len(self.ksteps) assert nalpha_a == len(self.NJIs) for kstep in self.ksteps: assert isinstance(kstep, int), self.ksteps
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds an EIGC card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') method = string(card, 2, 'method') assert method in ['ARNO', 'INV', 'HESS', 'CLAN', 'ISRR', 'IRAM', 'DET'], ( 'method=%s is not ARNO, INV, HESS, CLAN, ISRR, IRAM, DET' % method) norm = string_or_blank(card, 3, 'norm', 'MAX') if norm == 'POINT': grid = integer(card, 4, 'G') component = parse_components(card, 5, 'C') else: grid = blank(card, 4, 'G') component = blank(card, 5, 'C') epsilon = double_or_blank(card, 6, 'epsilon') neigenvalues = integer_double_string_or_blank(card, 7, 'ND0/neigenvalues') # ALPHAAJ OMEGAAJ ALPHABJ OMEGABJ LJ NEJ NDJ fields = [interpret_value(field) for field in card[9:]] #-------CLAN-------------- mblkszs = [] iblkszs = [] ksteps = [] NJIs = [] #-------CLAN-------------- #-------HESS-------------- alphaAjs = [] alphaBjs = [] omegaAjs = [] omegaBjs = [] mblkszs = [] iblkszs = [] ksteps = [] LJs = [] NEJs = [] NDJs = [] #-------HESS-------------- #-------ISRR-------------- shift_r1 = 0.0 shift_i1 = 0.0 isrr_flag = 0 nd1 = None #-------ISRR-------------- nfields = len(fields) nrows = nfields // 8 if nfields % 8 > 0: nrows += 1 #if nrows == 0: #msg = 'invalid row count=0; nfields=%s \ncard=%s\nfields=%s' % ( #nfields, card, fields) #raise RuntimeError(msg) if method == 'CLAN': out = _load_clan(nrows, card) (alphaAjs, omegaAjs, mblkszs, iblkszs, ksteps, NJIs, alphaBjs, omegaBjs, LJs, NEJs, NDJs) = out elif method in ['HESS', 'INV', 'DET']: # HESS, INV alphaAjs, omegaAjs, alphaBjs, omegaBjs, LJs, NEJs, NDJs = _load_hess_inv( nrows, method, card) elif method == 'ISRR': shift_r1, shift_i1, isrr_flag, nd1 = _load_isrr(nrows, card) else: raise RuntimeError(f'invalid EIGC method...method={method!r}') #assert card.nfields() < 8, 'card = %s' % card return EIGC(sid, method, grid, component, epsilon, neigenvalues, norm, # common mblkszs, iblkszs, ksteps, NJIs, # CLAN alphaAjs, omegaAjs, alphaBjs, omegaBjs, LJs, NEJs, NDJs, # HESS/INV shift_r1, shift_i1, isrr_flag, nd1, # ISRR comment=comment)
[docs] def cross_reference(self, model: BDF) -> None: pass
[docs] def raw_method(self): list_fields = [] if self.method in {'HESS', 'INV', 'DET'}: for (alphaA, omegaA, alphaB, omegaB, Lj, NEj, NDj) in zip( self.alphaAjs, self.omegaAjs, self.alphaBjs, self.omegaBjs, self.LJs, self.NEJs, self.NDJs): alphaA = set_blank_if_default(alphaA, 0.0) omegaA = set_blank_if_default(omegaA, 0.0) alphaB = set_blank_if_default(alphaB, 0.0) omegaB = set_blank_if_default(omegaB, 0.0) list_fields += [alphaA, omegaA, alphaB, omegaB, Lj, NEj, NDj, None] elif self.method == 'CLAN': nalpha_a = len(self.alphaAjs) nalpha_b = len(self.alphaBjs) assert nalpha_a == len(self.omegaAjs) if nalpha_a == nalpha_b: # pragma:no cover assert nalpha_a == len(self.alphaBjs), f'nalpha_a={nalpha_a} nalpha_b={nalpha_b}' assert nalpha_a == len(self.omegaBjs), f'nalpha_a={nalpha_a} nomega_b={len(self.omegaBjs)}' assert nalpha_a == len(self.LJs) assert nalpha_a == len(self.NEJs) assert nalpha_a == len(self.NDJs) for (alphaA, omegaA, alphaB, omegaB, Lj, Nej, Ndj) in zip( self.alphaAjs, self.omegaAjs, self.alphaBjs, self.omegaBjs, self.LJs, self.NEJs, self.NDJs): #alphaA = set_blank_if_default(alphaA, 0.0) #omegaA = set_blank_if_default(omegaA, 0.0) #mblksz = set_blank_if_default(mblksz, 7) #iblksz = set_blank_if_default(iblksz, 2) #kstep = set_blank_if_default(kstep, 5) list_fields += [alphaA, omegaA, alphaB, omegaB, Lj, Nej, Ndj, None] else: assert nalpha_a == len(self.mblkszs) assert nalpha_a == len(self.iblkszs) assert nalpha_a == len(self.ksteps) assert nalpha_a == len(self.NJIs) for (alphaA, omegaA, mblksz, iblksz, kstep, Nj) in zip( self.alphaAjs, self.omegaAjs, self.mblkszs, self.iblkszs, self.ksteps, self.NJIs): alphaA = set_blank_if_default(alphaA, default=0.0) omegaA = set_blank_if_default(omegaA, default=0.0) mblksz = set_blank_if_default(mblksz, default=7) iblksz = set_blank_if_default(iblksz, default=2) kstep = set_blank_if_default(kstep, default=5) list_fields += [alphaA, omegaA, mblksz, iblksz, kstep, None, Nj, None] elif self.method == 'ISRR': assert self.shift_r1 is not None, self.get_stats() assert len(self.shift_r1) > 0, self.get_stats() for shift_r1i, shift_i1i, isrr_flagi, nd1i in zip( self.shift_r1, self.shift_i1, self.isrr_flag, self.nd1): list_fields += [shift_r1i, shift_i1i, None, None, None, isrr_flagi, nd1i, None] else: raise RuntimeError(f'invalid EIGC method. method={self.method!r} ' 'expected=[HESS, INV, DET, CLAN, ISRR]') return list_fields
[docs] def repr_method(self): return self.raw_method()
[docs] def raw_fields(self): list_fields = ['EIGC', self.sid, self.method, self.norm, self.G, self.C, self.epsilon, self.neigenvalues, None] list_fields += self.raw_method() return list_fields
[docs] def repr_fields(self): if self.epsilon is None: epsilon = None else: epsilon = self.epsilon list_fields = ['EIGC', self.sid, self.method, self.norm, self.G, self.C, epsilon, self.neigenvalues, None] list_fields += self.repr_method() return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] class EIGP(Method): """ Defines poles that are used in complex eigenvalue extraction by the Determinant method. +------+-------+--------+--------+-------+--------+--------+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +======+=======+========+========+=======+========+========+=====+ | EIGP | SID | ALPHA1 | OMEGA1 | M1 | ALPHA2 | OMEGA2 | M2 | +------+-------+--------+--------+-------+--------+--------+-----+ | EIGP | 15 | -5.2 | 0.0 | 2 | 6.3 | 5.5 | 3 | +------+-------+--------+--------+-------+--------+--------+-----+ """ type = 'EIGP'
[docs] @classmethod def _init_from_empty(cls): sid = 1 alpha1 = 1. omega1 = 1. m1 = 1. alpha2 = 1. omega2 = 1. m2 = 1. return EIGP(sid, alpha1, omega1, m1, alpha2, omega2, m2, comment='')
def __init__(self, sid, alpha1, omega1, m1, alpha2, omega2, m2, comment=''): Method.__init__(self) if comment: self.comment = comment #: Set identification number. (Unique Integer > 0) self.sid = sid #: Coordinates of point in complex plane. (Real) self.alpha1 = alpha1 #: Coordinates of point in complex plane. (Real) self.omega1 = omega1 #: Multiplicity of complex root at pole defined by point at ALPHAi #: and OMEGAi self.m1 = m1 #: Coordinates of point in complex plane. (Real) self.alpha2 = alpha2 #: Coordinates of point in complex plane. (Real) self.omega2 = omega2 #: Multiplicity of complex root at pole defined by point at ALPHAi #: and OMEGAi self.m2 = m2
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds an EIGPX card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') alpha1 = double(card, 2, 'alpha1') omega1 = double(card, 3, 'omega1') m1 = integer(card, 4, 'm1') alpha2 = double(card, 5, 'alpha2') omega2 = double(card, 6, 'omega2') m2 = integer(card, 7, 'm2') assert len(card) == 8, f'len(EIGP card) = {len(card):d}\ncard={card}' return EIGP(sid, alpha1, omega1, m1, alpha2, omega2, m2, comment=comment)
[docs] def cross_reference(self, model: BDF) -> None: pass
[docs] def raw_fields(self): list_fields = ['EIGP', self.sid, self.alpha1, self.omega1, self.m1, self.alpha2, self.omega2, self.m2] return list_fields
[docs] def repr_fields(self): return self.raw_fields()
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] class EIGR(Method): """ Defines data needed to perform real eigenvalue analysis # msc/nx | EIGR | SID | METH| F1 | F2 | NE | ND | | | | | NORM | G | C | | | | | | # mystran | EIGR | SID | METH| F1 | F2 | NE | ND | | CRIT | | | NORM | G | C | | | | | | """ type = 'EIGR' allowed_methods = [ 'LAN', 'AHOU', # recommended 'INV', 'SINV', 'GIV', 'MGIV', 'HOU', 'MHOU', 'AGIV' # obsolete ]
[docs] @classmethod def _init_from_empty(cls): sid = 1 return EIGR(sid, method='LAN', f1=None, f2=None, ne=None, nd=None, crit=None, norm='MASS', G=None, C=None, comment='')
def __init__(self, sid, method='LAN', f1=None, f2=None, ne=None, nd=None, crit=None, norm='MASS', G=None, C=None, comment=''): """ Adds a EIGR card Parameters ---------- sid : int method id method : str; default='LAN' eigenvalue method recommended: {LAN, AHOU} obsolete : {INV, SINV, GIV, MGIV, HOU, MHOU, AGIV} f1 / f2 : float; default=None lower/upper bound eigenvalue f2 : float; default=None upper bound eigenvalue ne : int; default=None estimate of number of roots (used for INV) nd : int; default=None desired number of roots crit : float; default=0.0 orthogonality criteria msglvl : int; default=0 debug level; 0-4 maxset : int; default=None Number of vectors in block or set shfscl : float; default=None estimate of first flexible mode natural frequency norm : str; default=None {MAX, MASS, AF, POINT} default=MASS (NX) G : int; default=None node id for normalization; only for POINT C : int; default=None component for normalization (1-6); only for POINT comment : str; default='' a comment for the card """ Method.__init__(self) if comment: self.comment = comment if G == 0: G = None if C == 0: C = None #: Set identification number. (Unique Integer > 0) self.sid = sid #: Method of eigenvalue extraction. (Character: 'INV' for inverse #: power method or 'SINV' for enhanced inverse power method.) self.method = method #: Frequency range of interest self.f1 = f1 self.f2 = f2 #: Estimate of number of roots in range (Required for #: METHOD = 'INV'). Not used by 'SINV' method. self.ne = ne #: Desired number of roots (default=600 for SINV 3*ne for INV) self.nd = nd #: orthogonality criterion self.crit = crit #: Method for normalizing eigenvectors. ('MAX' or 'POINT'; #: Default='MAX') self.norm = norm #: Grid or scalar point identification number. Required only if #: NORM='POINT'. (Integer>0) self.G = G #: Component number. Required only if NORM='POINT' and G is a #: geometric grid point. (1<Integer<6) self.C = C if self.method not in self.allowed_methods: msg = 'method=%s; allowed_methods=[%s]' % ( self.method, ', '.join(self.allowed_methods)) raise ValueError(msg) assert norm in ['POINT', 'MASS', 'MAX']
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds an EIGR card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') method = string_or_blank(card, 2, 'method', 'LAN') f1 = double_or_blank(card, 3, 'f1') f2 = double_or_blank(card, 4, 'f2') ne = integer_or_blank(card, 5, 'ne') if method not in cls.allowed_methods: msg = 'method=%s; allowed_methods=[%s]' % ( method, ', '.join(cls.allowed_methods)) raise ValueError(msg) if method == 'SINV': nd = integer_or_blank(card, 6, 'nd', default=600) elif method == 'INV': ne = integer_or_blank(card, 5, 'ne', default=0) nd = integer_or_blank(card, 6, 'nd', default=3 * ne) elif method in {'GIV', 'MGIV', 'AGIV', 'HOU', 'MHOU', 'AHOU'}: # AHOU = automatic selection of HOU or MHOU # AGIV = automatic selection of GIV or MGIV nd = integer_or_blank(card, 6, 'nd', default=0) elif method == 'LAN': #The LAN method is the most general-purpose method, and may be used on #both small- and large-size problems. It takes advantage of sparsity of input #matrices, leading to greater efficiency on large-size problems. Because Lanczos #performance is tuned for medium to large problems, this has caused difficulties #with very small problems. Thus, by default, on problems with fewer than 20 #degrees-of-freedom when the LAN method is selected, the method is switched to #AHOU. The criteria for automatic switching is controlled by SYSTEM(359) on the #NASTRAN entry. #The NE, G, and C fields are ignored for the LAN method. The #NORM field may be set to MASS (the default value) or NORM. # # The conventions used when both the Fi and ND fields are specified are # described in Table 1 of the IGRL entry description. # # The EIGRL entry is an alternate method to select the # LAN method. It has several other input options for special cases. # # When both and EIGRL and EIGR have the same SID and that SID is # selected by a METHOD ommand the EIGRL entry takes precedence. nd = integer_or_blank(card, 6, 'nd') else: nd = integer(card, 6, 'nd') crit = double_or_blank(card, 8, 'crit') norm = string_or_blank(card, 9, 'norm', default='MASS') if norm == 'POINT': G = integer(card, 10, 'G') C = parse_components(card, 11, 'C') else: G = blank(card, 10, 'G') C = blank(card, 11, 'C') assert len(card) <= 12, f'len(EIGR card) = {len(card):d}\ncard={card}' return EIGR(sid, method, f1, f2, ne, nd, crit=crit, norm=norm, G=G, C=C, comment=comment)
[docs] def cross_reference(self, model: BDF) -> None: pass
[docs] def raw_fields(self): list_fields = ['EIGR', self.sid, self.method, self.f1, self.f2, self.ne, self.nd, None, None, self.norm, self.G, self.C] return list_fields
[docs] def repr_fields(self): method = set_blank_if_default(self.method, 'LAN') norm = set_blank_if_default(self.norm, 'MASS') list_fields = ['EIGR', self.sid, method, self.f1, self.f2, self.ne, self.nd, None, None, norm, self.G, self.C] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] class EIGRL(Method): """ Defines data needed to perform real eigenvalue (vibration or buckling) analysis with the Lanczos method +-------+-----+----+----+----+--------+--------+--------+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+=====+====+====+====+========+========+========+======+ | EIGRL | SID | V1 | V2 | ND | MSGLVL | MAXSET | SHFSCL | NORM | +-------+-----+----+----+----+--------+--------+--------+------+ | option_1 = value_1 option_2 = value_2, etc. | +--------------------------------------------------------------+ """ type = 'EIGRL'
[docs] @classmethod def _init_from_empty(cls): sid = 1 return EIGRL(sid, v1=None, v2=None, nd=None, msglvl=0, maxset=None, shfscl=None, norm=None, options=None, values=None, comment='')
def __init__(self, sid, v1=None, v2=None, nd=None, msglvl=0, maxset=None, shfscl=None, norm=None, options=None, values=None, comment=''): """ Adds an EIGRL card Parameters ---------- sid : int method id v1 : float; default=None lower bound eigenvalue v2 : float; default=None upper bound eigenvalue nd : int number of roots msglvl : int; default=0 debug level; 0-4 maxset : int; default=None Number of vectors in block or set shfscl : float; default=None estimate of first flexible mode natural frequency norm : str; default=None {MAX, MASS, AF} options : list[str]; default=None -> [] line 2 keys values : list[int | float | str]; default=None -> [] line 2 values comment : str; default='' a comment for the card """ Method.__init__(self) if comment: self.comment = comment if options is None: options = [] if values is None: values = [] #: Set identification number. (Unique Integer > 0) self.sid = sid #: For vibration analysis: frequency range of interest. For #: buckling analysis: eigenvalue range of interest. See Remark 4. #: (Real or blank, -5 10e16 <= V1 < V2 <= 5.10e16) self.v1 = v1 self.v2 = v2 #: Number of roots desired self.nd = nd #: Diagnostic level. (0 < Integer < 4; Default = 0) self.msglvl = msglvl #: Number of vectors in block or set. Default is machine dependent self.maxset = maxset #: Estimate of the first flexible mode natural frequency #: (Real or blank) self.shfscl = shfscl #: Method for normalizing eigenvectors (Character: 'MASS' or 'MAX') self.norm = norm self.options = options self.values = values
[docs] def validate(self): assert self.norm in [None, 'MAX', 'MASS', 'AF'], f'norm={self.norm!r}' assert self.msglvl in [0, 1, 2, 3, 4, -999], f'msglvl={self.msglvl:d}' if len(self.options) != len(self.values): raise RuntimeError('len(options) != len(values); noptions=%s nvalues=%s\n' 'options=%s values=%s' % (len(self.options), len(self.values), self.options, self.values)) for option, value in zip(self.options, self.values): if option == 'NORM': assert value in ['MAX', ], 'option=%r value=%r' % (option, value) elif option == 'ALPH': # float pass elif option == 'NUMS': # integer pass else: raise NotImplementedError('option=%r value=%r' % (option, value))
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds an EIGRL card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') v1 = double_or_blank(card, 2, 'v1') v2 = double_or_blank(card, 3, 'v2') nd = integer_or_blank(card, 4, 'nd') msglvl = integer_or_blank(card, 5, 'msglvl', 0) maxset = integer_or_blank(card, 6, 'maxset') shfscl = double_or_blank(card, 7, 'shfscl') norm = string_or_blank(card, 8, 'norm') option_values = [interpret_value(field) for field in card[9:]] options = [] values = [] for option_value in option_values: try: (option, value) = option_value.split('=') except AttributeError: msg = 'parsing EIGRL card incorrectly; option_values=%s\ncard=%s' % ( option_values, card) raise RuntimeError(msg) options.append(option) values.append(value) #: Method for normalizing eigenvectors #if sol in [103, 115, 146]: ## normal modes,cyclic normal modes, flutter #self.norm = string_or_blank(card, 8, 'norm', 'MASS') #elif sol in [105, 110, 111, 116]: ## buckling, modal complex eigenvalues, ## modal frequency response,cyclic buckling #self.norm = string_or_blank(card, 8, 'norm', 'MAX') #else: norm = string_or_blank(card, 8, 'norm') #assert len(card) <= 9, f'len(EIGRL card) = {len(card):d}\ncard={card}' assert len(card) <= 10, f'len(EIGRL card) = {len(card):d}\ncard={card}' #msg = 'norm=%s sol=%s' % (self.norm, sol) #assert self.norm in ['MASS', 'MAX'],msg #assert len(card) < 9,'card = %s' % (card.fields(0)) return EIGRL(sid, v1, v2, nd, msglvl, maxset, shfscl, norm, options, values, comment=comment)
[docs] def cross_reference(self, model: BDF) -> None: pass
#if self.norm is None: #if model.is_modal_solution(): #self.norm = 'MASS' #elif model.is_buckling_solution(): #self.norm = 'MAX'
[docs] def raw_fields(self): list_fields = ['EIGRL', self.sid, self.v1, self.v2, self.nd, self.msglvl, self.maxset, self.shfscl, self.norm] for (option, value) in zip(self.options, self.values): list_fields += [option + '=' + str(value)] return list_fields
[docs] def repr_fields(self): msglvl = set_blank_if_default(self.msglvl, 0) list_fields = ['EIGRL', self.sid, self.v1, self.v2, self.nd, msglvl, self.maxset, self.shfscl, self.norm] for (option, value) in zip(self.options, self.values): list_fields += [option + '=' + str(value)] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] def _load_isrr(nrows, card): """loads the iterative Schur-Rayleigh-Ritz""" shift_r1 = [] shift_i1 = [] isrr_flag = [] nd1 = [] for irow in range(nrows): i = 9 + 8 * irow shift_r1i = double_or_blank(card, i, 'SHIFT_R1', 0.0) shift_i1i = double_or_blank(card, i + 1, 'SHIFT_I1', 0.0) #2 #3 #4 isrr_flagi = integer_or_blank(card, i + 5, 'ISRR_FLAG', 0) nd1i = integer(card, i + 6, 'ND1') shift_r1.append(shift_r1i) shift_i1.append(shift_i1i) isrr_flag.append(isrr_flagi) nd1.append(nd1i) return shift_r1, shift_i1, isrr_flag, nd1
[docs] def _load_clan(nrows, card): """loads complex Lanczos""" alphaAjs = [] omegaAjs = [] mblkszs = [] iblkszs = [] ksteps = [] NJIs = [] alphaBjs = [] omegaBjs = [] ljs = [] nejs = [] ndjs = [] is_nej = None for irow in range(nrows): #NDJ_default = None i = 9 + 8 * irow alphaAjs.append( double_or_blank(card, i, 'alpha' + str(irow), 0.0)) omegaAjs.append( double_or_blank(card, i + 1, 'omega' + str(irow), 0.0)) nej_blank = integer_or_blank(card, i + 6, 'NEJ_blank') if nej_blank is not None and 0: # pragma: no cover assert is_nej in [True, None], is_nej is_nej = True # ALPHAAJ OMEGAAJ ALPHABJ OMEGABJ LJ NEJ NDJ assert isinstance(nej_blank, int), nej_blank alpha_bj = double(card, i + 2, 'alpha_bj' + str(irow)) omega_bj = double(card, i + 3, 'omega_bj' + str(irow)) lj = double_or_blank(card, i + 4, 'LJ' + str(irow), 1.0) nej = integer_or_blank(card, i + 5, 'NEJ' + str(irow)) ndj = integer(card, i + 6, 'NDJ' + str(irow)) alphaBjs.append(alpha_bj) omegaBjs.append(omega_bj) ljs.append(lj) nejs.append(nej) ndjs.append(ndj) else: assert is_nej in [False, None], is_nej is_nej = False # ALPHAAJ OMEGAAJ MBLKSZ IBLKSZ KSTEPS blank NJi mblock_size = double_or_blank(card, i + 2, 'mblock' + str(irow), default=7) # iblkszs is an integer, but entered as a float... iblock_size = double_or_blank(card, i + 3, 'iblksz' + str(irow), default=2.0) kstep = integer_or_blank(card, i + 4, 'kstep' + str(irow), default=5) nji = integer(card, i + 6, 'NJI' + str(irow)) mblkszs.append(mblock_size) iblkszs.append(iblock_size) ksteps.append(kstep) NJIs.append(nji) out = ( alphaAjs, omegaAjs, mblkszs, iblkszs, ksteps, NJIs, alphaBjs, omegaBjs, ljs, nejs, ndjs, ) return out
[docs] def _load_hess_inv(nrows, method, card): """loads inverse power""" alpha_omega_default = None LJ_default = None if method == 'INV': alpha_omega_default = 0.0 LJ_default = 1.0 alphaAjs = [] alphaBjs = [] omegaAjs = [] omegaBjs = [] #mblkszs = [] #iblkszs = [] #ksteps = [] LJs = [] NEJs = [] NDJs = [] for irow in range(nrows): NEj = integer_or_blank(card, 9 + 7 * irow + 5, 'NE%s' % str(irow), 0) NDJ_default = None if method == 'INV': NDJ_default = 3 * NEj i = 9 + 8 * irow alphaAjs.append( double_or_blank(card, i, 'alphaA' + str(irow), alpha_omega_default)) omegaAjs.append( double_or_blank(card, i + 1, 'omegaA' + str(irow), alpha_omega_default)) alphaBjs.append( double_or_blank(card, i + 2, 'alphaB' + str(irow), alpha_omega_default)) omegaBjs.append( double_or_blank(card, i + 3, 'omegaB' + str(irow), alpha_omega_default)) LJs.append( double_or_blank(card, i + 4, 'LJ' + str(irow), LJ_default)) NEJs.append(NEj) NDJs.append(integer_or_blank(card, i + 6, 'NDJ' + str(irow), NDJ_default)) return alphaAjs, omegaAjs, alphaBjs, omegaBjs, LJs, NEJs, NDJs
[docs] class MODTRAK(BaseCard): """ MODTRAK SID LOWRNG HIGHRNG MTFILTER MODTRAK 100 1 26 0.80 """ type = 'MODTRAK' def __init__(self, sid: int, low_range: int, high_range: int, mt_filter: float, comment: str=''): BaseCard.__init__(self) self.sid = sid self.low_range = low_range self.high_range = high_range self.mt_filter = mt_filter
[docs] @classmethod def _init_from_empty(cls): sid = 1 low_range = 0 high_range = 0 mt_filter = 0.0 return MODTRAK(sid, low_range, high_range, mt_filter, comment='')
[docs] @classmethod def add_card(cls, card, comment=''): sid = integer(card, 1, 'sid') low_range = integer_or_blank(card, 2, 'low_range', default=0) high_range = integer(card, 3, 'high_range') mt_filter = double_or_blank(card, 4, 'mt_filter', default=0.9) assert len(card) <= 5, f'len(MODTRAK card) = {len(card):d}\ncard={card}' return MODTRAK(sid, low_range, high_range, mt_filter, comment=comment)
[docs] def raw_fields(self) -> list[Any]: list_fields = ['MODTRAK', self.sid, self.low_range, self.high_range, self.mt_filter] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: fields = self.raw_fields() #if size == 8: return self.comment + print_card_8(fields)
#return self.comment + print_card_16(fields)