Source code for pyNastran.bdf.cards.properties.bush

"""
All bush properties are defined in this file.  This includes:

 *   PBUSH
 *   PBUSH1D
 *   PBUSH2D (not implemented)
 *   PBUSHT

All bush properties are BushingProperty and Property objects.

"""
from __future__ import annotations
import warnings
from typing import TYPE_CHECKING

from pyNastran.utils.numpy_utils import integer_types
from pyNastran.bdf.cards.base_card import Property
from pyNastran.bdf.bdf_interface.assign_type import (
    integer, integer_or_blank, double, double_or_blank, string,
    double_string_or_blank, string_or_blank, blank, fields)
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_interface.bdf_card import BDFCard
    from pyNastran.bdf.bdf import BDF


[docs] class BushingProperty(Property): type = 'BushingProperty' def __init__(self): Property.__init__(self)
[docs] def cross_reference(self, model: BDF) -> None: pass
[docs] def uncross_reference(self) -> None: """Removes cross-reference links""" pass
[docs] class PBUSH(BushingProperty): """ Generalized Spring-and-Damper Property Defines the nominal property values for a generalized spring-and-damper structural element. +-------+-----+-------+------+-------+-----+-----+-----+----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+=====+=======+======+=======+=====+=====+=====+====+ | PBUSH | PID | K | K1 | K2 | K3 | K4 | K5 | K6 | +-------+-----+-------+------+-------+-----+-----+-----+----+ | | B | B1 | B2 | B3 | B4 | B5 | B6 | | +-------+-----+-------+------+-------+-----+-----+-----+----+ | | GE | GE1 | GE2 | GE3 | GE4 | GE5 | GE6 | | +-------+-----+-------+------+-------+-----+-----+-----+----+ | | RCV | SA | ST | EA | ET | | | | +-------+-----+-------+------+-------+-----+-----+-----+----+ | | M | MASS | | | | | | | +-------+-----+-------+------+-------+-----+-----+-----+----+ | | T | ALPHA | TREF | COINL | | | | | +-------+-----+-------+------+-------+-----+-----+-----+----+ RCV was added <= MSC 2016 MASS was added <= MSC 2016 T/ALPHA/TREF/COINL was added in MSC 2021 """ type = 'PBUSH' _field_map = { 1: 'pid', } pname_map = { -2 : 'K1', -3 : 'K2', -4 : 'K3', -5 : 'K4', -6 : 'K5', -7 : 'K6', -8 : 'B1', -9 : 'B2', -10 : 'B3', -11 : 'B4', -12 : 'B5', -13 : 'B6', -14 : 'GE1', -15 : 'GE2', -16 : 'GE3', -17 : 'GE4', -18 : 'GE5', -19 : 'GE6', -20 : 'SA', -21 : 'ST', -22 : 'EA', -23 : 'ET', }
[docs] def update_by_pname_fid(self, name, value): if name == 'B1': self.Bi[0] = value elif name == 'B2': self.Bi[1] = value elif name == 'B3': self.Bi[2] = value elif name == 'B4': self.Bi[3] = value elif name == 'B5': self.Bi[4] = value elif name == 'B6': self.Bi[5] = value elif name == 'K1': self.Ki[0] = value elif name == 'K2': self.Ki[1] = value elif name == 'K3': self.Ki[2] = value elif name == 'K4': self.Ki[3] = value elif name == 'K5': self.Ki[4] = value elif name == 'K6': self.Ki[5] = value elif name == 'GE1': self.GEi[0] = value elif name == 'GE2': self.GEi[1] = value elif name == 'GE3': self.GEi[2] = value elif name == 'GE4': self.GEi[3] = value elif name == 'GE5': self.GEi[4] = value elif name == 'GE6': self.GEi[5] = value #elif name == 'M': #self.mass elif isinstance(name, int) and name in self.pname_map: name2 = self.pname_map[name] self.update_by_pname_fid(name2, value) else: raise NotImplementedError('property_type=%r has not implemented %r in pname_map' % ( self.type, name))
#pname_fid_map = { #4 : 'A', 'A' : 'A', #5 : 'i1', 'I1' : 'i1', #6 : 'i2', 'I2' : 'i2', #7 : 'i12', 'I12' : 'i12', #5 : 'j', 'J' : 'j', #} def __init__(self, pid, k, b, ge, rcv=None, mass=None, t=None, comment=''): """ Creates a PBUSH card, which defines a property for a CBUSH Parameters ---------- pid : int property id k : list[float] Nominal stiffness values in directions 1 through 6. len(k) = 6 b : list[float] Nominal damping coefficients in direction 1 through 6 in units of force per unit velocity len(b) = 6 ge : list[float] Nominal structural damping constant in directions 1 through 6. len(ge) = 6 rcv : list[float]; default=None -> (None, None, None, None) [sa, st, ea, et] = rcv_fields length(rcv_fields) = 4 mass : float; default=None lumped mass of the CBUSH This is an MSC only parameter. t : list[float]; default=None -> (None, None, None) [alpha, tref, coinl] = t_fields length(t_fields) = 3 alpha: thermal expansion coefficient; default=0.0 tref: reference temperature; default=0.0 coincident_length: length of CBUSH with coincident grids; default=0.0 comment : str; default='' a comment for the card """ BushingProperty.__init__(self) if comment: self.comment = comment #: Property ID self.pid = pid self.vars = [] # K parameter self.Ki = k if k: nk = len(k) if nk < 6: k.extend([0.] * (6 - nk)) self.vars.append('K') # B parameter self.Bi = b if b: nb = len(b) if nb < 6: b.extend([0.] * (6 - nb)) self.vars.append('B') # GE parameter self.GEi = ge if ge: nge = len(ge) if nge < 6: ge.extend([0.] * (6 - nge)) self.vars.append('GE') # RCV parameters if rcv is None: sa, st, ea, et = None, None, None, None else: sa, st, ea, et = rcv if sa is not None or st is not None or ea is not None or et is not None: self.vars.append('RCV') self.sa = sa self.st = st self.ea = ea self.et = et # M parameter (MSC only; in 2016, not in 2005) self.mass = mass if mass: self.vars.append('M') # T parameters if t is None: alpha, tref, coincident_length = None, None, None else: alpha, tref, coincident_length = t if alpha is not None or tref is not None or coincident_length is not None: self.vars.append('T') self.alpha = alpha self.tref = tref self.coincident_length = coincident_length
[docs] @classmethod def _init_from_empty(cls): pid = 1 k = [1.] b = [1.] ge = [1.] return PBUSH(pid, k, b, ge, rcv=None, mass=None, t=None, comment='')
[docs] def validate(self): assert isinstance(self.Ki, list), 'PBUSH: pid=%i type(Ki)=%s Ki=%s' % (self.pid, type(self.Ki), self.Ki) assert isinstance(self.Bi, list), 'PBUSH: pid=%i type(Bi)=%s Bi=%s' % (self.pid, type(self.Bi), self.Bi) assert isinstance(self.GEi, list), 'PBUSH: pid=%i type(GEi)=%s GEi=%s' % (self.pid, type(self.GEi), self.GEi)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBUSH card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ k_fields = [] b_fields = [] ge_fields = [] rcv_fields = [None, None, None, None] mass = None pid = integer(card, 1, 'pid') nfields = card.nfields istart = 2 while istart < nfields: pname = string(card, istart, 'pname') if pname == 'K': # Flag indicating that the next 1 to 6 fields are stiffness values in # the element coordinate system. #self.k = string(card, istart, 'k') #: Nominal stiffness values in directions 1 through 6. #: See Remarks 2 and 3. (Real; Default = 0.0) k_fields = cls._read_var(card, 'Ki', istart + 1, istart + 7) elif pname == 'B': # Flag indicating that the next 1 to 6 fields are force-per-velocity # damping. #self.b = string(card, istart, 'b') #: Force per unit velocity (Real) #: Nominal damping coefficients in direction 1 through 6 in units of #: force per unit velocity. See Remarks 2, 3, and 9. (Real; Default=0.) b_fields = cls._read_var(card, 'Bi', istart + 1, istart + 7) elif pname == 'GE': # Flag indicating that the next fields, 1 through 6 are structural # damping constants. See Remark 7. (Character) #self.ge = string(card, istart, 'ge') #: Nominal structural damping constant in directions 1 through 6. See #: Remarks 2. and 3. (Real; Default = 0.0) ge_fields = cls._read_var(card, 'GEi', istart + 1, istart + 7) elif pname == 'RCV': rcv_fields = cls._read_rcv(card, istart) elif pname == 'M': # Lumped mass of the cbush; default=0.0 mass = double_or_blank(card, istart + 1, 'mass', 0.) elif pname == 'T': t_fields = cls._read_var(card, 'Ti', istart + 1, istart + 4) assert len(t_fields) == 3, t_fields else: raise RuntimeError('unsupported PBUSH type; pname=%r' % pname) #break # old version... istart += 8 return PBUSH(pid, k_fields, b_fields, ge_fields, rcv_fields, mass, comment=comment)
[docs] @classmethod def _read_var(cls, card, var_prefix, istart, iend): Ki = fields(double_or_blank, card, var_prefix, istart, iend) return Ki
@classmethod def add_op2_data(cls, data, comment=''): """ Adds a PBUSH card from the OP2 Parameters ---------- data : list[varies] a list of fields defined in OP2 format comment : str; default='' a comment for the card """ t_fields = None ndata = len(data) if ndata == 23: (pid, k1, k2, k3, k4, k5, k6, b1, b2, b3, b4, b5, b6, g1, g2, g3, g4, g5, g6, sa, st, ea, et) = data mass = 0. elif ndata == 24: (pid, k1, k2, k3, k4, k5, k6, b1, b2, b3, b4, b5, b6, g1, g2, g3, g4, g5, g6, sa, st, ea, et, mass) = data elif ndata == 27: (pid, k1, k2, k3, k4, k5, k6, b1, b2, b3, b4, b5, b6, g1, g2, g3, g4, g5, g6, sa, st, ea, et, mass, alpha, tref, coinl) = data if max(abs(mass), abs(alpha), abs(tref), abs(coinl)) != 0: warnings.warn(f'verify PBUSH pid={pid} mass={mass} alpha={alpha} tref={tref} coinl={coinl}') t_fields = [alpha, tref, coinl] else: raise NotImplementedError(f'ndata={ndata} and must be [23, 24, 27]; data={data}') k_fields = [k1, k2, k3, k4, k5, k6] b_fields = [b1, b2, b3, b4, b5, b6] ge_fields = [g1, g2, g3, g4, g5, g6] rcv_fields = [sa, st, ea, et] return PBUSH(pid, k_fields, b_fields, ge_fields, rcv=rcv_fields, mass=mass, t=t_fields, comment=comment) def _verify(self, xref): pid = self.Pid() assert isinstance(pid, integer_types), 'pid=%r' % pid
[docs] @classmethod def _read_rcv(cls, card, istart): # Flag indicating that the next 1 to 4 fields are stress or strain # coefficients. (Character) #self.rcv = string(card, istart, 'rcv') sa = double_or_blank(card, istart + 1, 'sa', 1.) st = double_or_blank(card, istart + 2, 'st', 1.) ea = double_or_blank(card, istart + 3, 'ea', 1.) et = double_or_blank(card, istart + 4, 'et', 1.) return [sa, st, ea, et]
[docs] def raw_fields(self): list_fields = ['PBUSH', self.pid] for var in self.vars: if var == 'K': list_fields += ['K'] + self.Ki elif var == 'B': list_fields += ['B'] + self.Bi elif var == 'GE': list_fields += ['GE'] + self.GEi elif var == 'RCV': list_fields += ['RCV', self.sa, self.st, self.ea, self.et] elif var == 'M': list_fields += ['M', self.mass] elif var == 'T': list_fields += ['T', self.alpha, self.tref, self.coincident_length] else: raise RuntimeError('not supported PBUSH field...') nspaces = 8 - (len(list_fields) - 1) % 8 if nspaces == 8: list_fields += [None] elif nspaces < 8: list_fields += [None] * (nspaces + 1) 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 PBUSH1D(BushingProperty): """ +---------+--------+-------+--------+--------+-------+-------+-------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +=========+========+=======+========+========+=======+=======+=======+ | PBUSH1D | PID | K | C | M | | SA | SE | +---------+--------+-------+--------+--------+-------+-------+-------+ | | SHOCKA | TYPE | CVT | CVC | EXPVT | EXPVC | IDTS | +---------+--------+-------+--------+--------+-------+-------+-------+ | | IDETS | IDECS | IDETSD | IDECSD | | | | +---------+--------+-------+--------+--------+-------+-------+-------+ | | SPRING | TYPE | IDT | IDC | IDTDU | IDCDU | | +---------+--------+-------+--------+--------+-------+-------+-------+ | | DAMPER | TYPE | IDT | IDC | IDTDV | IDCDV | | +---------+--------+-------+--------+--------+-------+-------+-------+ | | GENER | IDT | IDC | IDTDU | IDCDU | IDTDV | IDCDV | +---------+--------+-------+--------+--------+-------+-------+-------+ """ type = 'PBUSH1D' _properties = ['pname_fid_map'] pname_fid_map = { 'K' : 'k', 'C' : 'c', 'M' : 'm', }
[docs] @classmethod def _init_from_empty(cls): pid = 1 return PBUSH1D(pid, k=0., c=0., m=0., sa=0., se=0., optional_vars=None, comment='')
def __init__(self, pid: int, k: float=0., c: float=0., m: float=0., sa: float=0., se: float=0., optional_vars=None, comment: str=''): """ Creates a PBUSH1D card Parameters ---------- pid : int property id k : float stiffness c : float Viscous damping m : float mass sa : float Stress recovery coefficient [1/area]. se : float Strain recovery coefficient [1/length]. optional_vars : dict[name] : value; default=None name : str SHOCKA, SPRING, DAMPER, GENER values : list[varies] the values SHOCKA: Coefficients of the following force versus velocity/displacement relationship F(u, v) = Cv * S(u) * sign(v) * |v|^EXPV TYPE CVT CVC EXPVT EXPVC IDTS IDETS IDECS IDETSD IDECSD CVT/CVC : int Viscous damping coefficient CV for tension v > 0, force per unit velocity. EXPVT/EXPVC : int Exponent of velocity EXPV for tension v > 0 (or compression v < 0). IDTS : int Identification number of a TABLEDi entry for tension and compression if TYPE=TABLE. The TABLEDi entry defines the scale factor S, versus displacement u. IDETS/IDECS : int Identification number of a DEQATN entry for tension if TYPE=EQUAT. The DEQATN entry defines the scale factor S, versus displacement u, for tension u > 0 (or compression v < 0). IDETSD/IDECSD : int Identification number of a DEQATN entry for tension if TYPE=EQUAT. The DEQATN entry defines the defines the scale factor S, versus displacement u, for tension u > 0 (or compression v < 0). SPRING: Nonlinear elastic spring element in terms of a force versus displacement relationship TYPE IDT IDC IDTDU IDCDU DAMPER: Nonlinear viscous element in terms of a force versus velocity relationship. TYPE IDT IDC IDTDV IDCDV GENER: General nonlinear elastic spring and viscous damper element in terms of a force versus displacement and velocity relationship. For this element, the relationship can only be defined with TYPE=EQUAT (and it's implicit). IDT IDC IDTDU IDCDU IDTDV IDCDV TYPE : int the type of the result; {TABLE, EQUAT} IDT/IDC : int tension/compression table/equation IDTDU/IDCDU : int du/dt tension/compression table/eq IDTDV/IDCDV : int dv/dt tension/compression table/eq """ BushingProperty.__init__(self) if comment: self.comment = comment #: Property ID self.pid = pid self.k = k self.c = c self.m = m self.sa = sa self.se = se # SPRING parameters self.spring_type = None self.spring_idt = None self.spring_idc = None self.spring_idtdu = None self.spring_idcdu = None # DAMPER parameters self.damper_type = None self.damper_idt = None self.damper_idc = None self.damper_idtdv = None self.damper_idcdv = None # GENER parameters #self.gener_idt = None #self.gener_idc = None #self.gener_idtdu = None #self.gener_idcdu = None #self.gener_idtdv = None #self.gener_idcdv = None # SHOCK parameters self.shock_type = None self.shock_cvt = None self.shock_cvc = None self.shock_exp_vt = None self.shock_exp_vc = None self.shock_idts = None self.shock_idets = None self.shock_idecs = None self.shock_idetsd = None self.shock_idecsd = None if optional_vars: for key, values in optional_vars.items(): if key == 'SHOCKA': (shock_type, shock_cvt, shock_cvc, shock_exp_vt, shock_exp_vc, shock_idts, shock_idets, shock_idecs, shock_idetsd, shock_idecsd ) = values self.shock_type = shock_type self.shock_cvt = shock_cvt self.shock_cvc = shock_cvc self.shock_exp_vt = shock_exp_vt self.shock_exp_vc = shock_exp_vc self.shock_idts = shock_idts self.shock_idets = shock_idets self.shock_idecs = shock_idecs self.shock_idetsd = shock_idetsd self.shock_idecsd = shock_idecsd assert isinstance(self.shock_type, str), f'shock_type={shock_type}' elif key == 'SPRING': (spring_type, spring_idt, spring_idc, spring_idtdu, spring_idcdu) = values self.spring_type = spring_type self.spring_idt = spring_idt self.spring_idtc = spring_idc self.spring_idc = spring_idc self.spring_idtdu = spring_idtdu self.spring_idcdu = spring_idcdu assert isinstance(self.spring_type, str), f'spring_type={spring_type}' elif key == 'DAMPER': (damper_type, damper_idt, damper_idc, damper_idtdv, damper_idcdv) = values self.damper_type = damper_type self.damper_idt = damper_idt self.damper_idc = damper_idc self.damper_idtdv = damper_idtdv self.damper_idcdv = damper_idcdv assert isinstance(self.damper_type, str), f'damper_type={damper_type}' elif key == 'GENER': ( gener_idt, gener_idc, gener_idtdu, gener_idcdu, gener_idtdv, gener_idcdv ) = values self.gener_idt = gener_idt self.gener_idc = gener_idc self.gener_idtdu = gener_idtdu self.gener_idcdu = gener_idcdu self.gener_idtdv = gener_idtdv self.gener_idcdv = gener_idcdv else: msg = ('PBUSH1D: pid=%s key=%r and must be ' '{SHOCKA, SPRING, DAPMER, GENER}' % self.pid) raise RuntimeError(msg) if optional_vars is None: self.vars = [] else: self.vars = list(optional_vars.keys()) self.vars.sort()
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBUSH1D card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ pid = integer(card, 1, 'pid') k = double_or_blank(card, 2, 'k', default=0.0) c = double_or_blank(card, 3, 'c', default=0.0) m = double_or_blank(card, 4, 'm', default=0.0) sa = double_or_blank(card, 6, 'sa', default=0.0) se = double_or_blank(card, 7, 'se', default=0.0) nfields = card.nfields optional_vars = {} istart = 9 while istart < nfields: pname = string(card, istart, 'pname') if pname == 'SHOCKA': istart, out = cls._read_shock(card, istart) optional_vars['SHOCKA'] = out elif pname == 'SPRING': out = cls._read_spring(card, istart) optional_vars['SPRING'] = out elif pname == 'DAMPER': out = cls._read_damper(card, istart) optional_vars['DAMPER'] = out elif pname == 'GENER': out = cls._read_gener(card, istart) optional_vars['GENER'] = out else: break istart += 8 #self.pid = pid #self.k = k #self.c = c #self.m = m #self.sa = sa #self.se = se return PBUSH1D(pid, k=k, c=c, m=m, sa=sa, se=se, optional_vars=optional_vars, comment=comment)
#@classmethod #def add_op2_data(cls, data, comment=''): #pid = data[0] #b = data[1] #raise NotImplementedError('PBUSH1D data...') def _verify(self, xref): pid = self.Pid() assert isinstance(pid, int), 'pid=%r' % pid
[docs] @staticmethod def _read_shock(card: BDFCard, istart: int) -> tuple[str, float, float, float, float]: """ F(u, v) = Cv * S(u) * sign(v) * |v|^ev """ shock_type = string_or_blank(card, istart + 1, 'shockType') shock_cvt = double(card, istart + 2, 'shockCVT') shock_cvc = double_or_blank(card, istart + 3, 'shockCVC') shock_exp_vt = double_or_blank(card, istart + 4, 'shockExpVT', default=1.0) shock_exp_vc = double_or_blank(card, istart + 5, 'shockExpVC', default=shock_exp_vt) if shock_type == 'TABLE': shock_idts = None shock_idets = None shock_idecs = None shock_idetsd = None shock_idecsd = None # shock_idts = integer(card, istart + 6, 'shockIDTS') # shock_idets = blank(card, istart + 9, 'shockIDETS') # shock_idecs = blank(card, istart + 10, 'shockIDECS') # shock_idetsd = blank(card, istart + 11, 'shockIDETSD') # shock_idecsd = blank(card, istart + 12, 'shockIDECSD') elif shock_type == 'EQUAT': shock_idts = blank(card, istart + 6, 'shockIDTS') shock_idets = integer(card, istart + 9, 'shockIDETS') shock_idecs = integer_or_blank(card, istart + 10, 'shockIDECS', shock_idets) shock_idetsd = integer(card, istart + 11, 'shockIDETSD') shock_idecsd = integer_or_blank(card, istart + 11, 'shockIDECSD', shock_idetsd) #def DEquation(self): #if isinstance(self.dequation, int): #return self.dequation #return self.dequation.equation_id else: msg = 'Invalid shockType=%r on card\n%s' % (shock_type, card) raise RuntimeError(msg) out = ( shock_type, shock_cvt, shock_cvc, shock_exp_vt, shock_exp_vc, shock_idts, shock_idets, shock_idecs, shock_idetsd, shock_idecsd ) istart += 8 return istart, out
[docs] @staticmethod def _read_spring(card, istart: int) -> tuple[str, int, int, int, int]: """ F(u) = Ft(u) """ spring_type = string_or_blank(card, istart + 1, 'springType', default='TABLE') spring_idt = integer(card, istart + 2, 'springIDT') if spring_type == 'TABLE': spring_idc = integer_or_blank(card, istart + 3, 'springIDC', default=spring_idt) spring_idtdu = integer_or_blank(card, istart + 4, 'springIDTDU') spring_idcdu = integer_or_blank(card, istart + 5, 'springIDCDU', default=spring_idtdu) elif spring_type == 'EQUAT': spring_idc = integer_or_blank(card, istart + 3, 'springIDC', default=spring_idt) spring_idtdu = integer(card, istart + 4, 'springIDTDU') spring_idcdu = integer_or_blank(card, istart + 5, 'springIDCDU', default=spring_idtdu) else: msg = 'Invalid springType=%r on card\n%s' % (spring_type, card) raise RuntimeError(msg) return spring_type, spring_idt, spring_idc, spring_idtdu, spring_idcdu
[docs] @staticmethod def _read_damper(card, istart: int) -> tuple[str, int, int, int, int]: """ F(v) = Ft(u) """ damper_type = string_or_blank(card, istart + 1, 'damperType', default='TABLE') damper_idt = integer(card, istart + 2, 'damperIDT') if damper_type == 'TABLE': damper_idc = integer_or_blank(card, istart + 3, 'damperIDC', default=damper_idt) damper_idtdv = integer_or_blank(card, istart + 4, 'damperIDTDV') damper_idcdv = integer_or_blank(card, istart + 5, 'damperIDCDV', default=damper_idtdv) elif damper_type == 'EQUAT': damper_idc = integer_or_blank(card, istart + 3, 'damperIDC', default=damper_idt) damper_idtdv = integer(card, istart + 4, 'damperIDTDV') damper_idcdv = integer_or_blank(card, istart + 5, 'damperIDCDV', default=damper_idtdv) else: msg = 'Invalid damper_type=%r on card\n%s' % (damper_type, card) raise RuntimeError(msg) return damper_type, damper_idt, damper_idc, damper_idtdv, damper_idcdv
[docs] @staticmethod def _read_gener(card, istart): """ F(u, v) = Ft(u, v) """ gener_idt = integer(card, istart + 2, 'generIDT') gener_idc = integer_or_blank(card, istart + 3, 'generIDC', gener_idt) gener_idtdu = integer(card, istart + 4, 'generIDTDU') gener_idcdu = integer_or_blank(card, istart + 5, 'generIDCDU', gener_idtdu) gener_idtdv = integer(card, istart + 6, 'generIDTDV') gener_idcdv = integer_or_blank(card, istart + 7, 'generIDCDV', gener_idtdv) out = ( gener_idt, gener_idc, gener_idtdu, gener_idcdu, gener_idtdv, gener_idcdv ) return out
[docs] def _shock_fields(self): list_fields = ['SHOCKA', self.shock_type, self.shock_cvt, self.shock_cvc, self.shock_exp_vt, self.shock_exp_vc, self.shock_idts, None, None, self.shock_idets, self.shock_idecs, self.shock_idetsd, self.shock_idecsd] return list_fields
[docs] def _spring_fields(self): list_fields = ['SPRING', self.spring_type, self.spring_idt, self.spring_idc, self.spring_idtdu, self.spring_idcdu] return list_fields
[docs] def _damper_fields(self): list_fields = ['DAMPER', self.damper_type, self.damper_idt, self.damper_idc, self.damper_idtdv, self.damper_idcdv] return list_fields
[docs] def _gener_fields(self): list_fields = ['GENER', None, self.gener_idt, self.gener_idc, self.gener_idtdu, self.gener_idcdu, self.gener_idtdv, self.gener_idcdv] return list_fields
[docs] def raw_fields(self): list_fields = ['PBUSH1D', self.pid, self.k, self.c, self.m, None, self.sa, self.se, None] for var in self.vars: if var == 'SHOCKA': list_fields += self._shock_fields() elif var == 'SPRING': list_fields += self._spring_fields() elif var == 'DAMPER': list_fields += self._damper_fields() elif var == 'GENER': list_fields += self._gener_fields() else: msg = 'var=%s not supported PBUSH1D field...' % var raise RuntimeError(msg) nspaces = 8 - (len(list_fields) - 1) % 8 if nspaces < 8: list_fields += [None] * (nspaces) 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 PBUSH2D(BushingProperty): """ MSC only card CROSS / blank options supported; no SQUEEZE support +---------+---------+-------+--------+-------+--------+---------+---------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +=========+=========+=======+========+=======+========+=========+=========+ | PBUSH2D | PID | K11 | K22 | B11 | B22 | M11 | M22 | +---------+---------+-------+--------+-------+--------+---------+---------+ | | CROSS | K12 | K21 | B12 | B21 | M12 | M21 | +---------+---------+-------+--------+-------+--------+---------+---------+ """ type = 'PBUSH2D'
[docs] @classmethod def _init_from_empty(cls): pid = 1 k11 = k22 = b11 = b22 = m11 = m22 = 1.0 cross_flag = '' k12 = k21 = b12 = b21 = m12 = m21 = 0.0 return PBUSH2D(pid, k11, k22, b11, b22, m11, m22, cross_flag, k12, k21, b12, b21, m12, m21)
def __init__(self, pid: int, k11, k22, b11, b22, m11, m22, cross_flag, k12, k21, b12, b21, m12, m21, comment: str=''): BushingProperty.__init__(self) if comment: self.comment = comment self.pid = pid self.k11 = k11 self.k22 = k22 self.b11 = b11 self.b22 = b22 self.m11 = m11 self.m22 = m22 self.cross_flag = cross_flag self.k12 = k12 self.k21 = k21 self.b12 = b12 self.b21 = b21 self.m12 = m12 self.m21 = m21
[docs] @classmethod def add_card(cls, card: BDFCard, comment: str=''): """ Adds a PBUSH1D card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ pid = integer(card, 1, 'pid') k11 = double(card, 2, 'k11') k22 = double(card, 3, 'k22') b11 = double_or_blank(card, 4, 'b11', default=0.0) b22 = double_or_blank(card, 5, 'b22', default=0.0) m11 = double_or_blank(card, 6, 'm11', default=0.0) m22 = double_or_blank(card, 7, 'm22', default=0.0) cross_flag = string_or_blank(card, 9, 'cross_flag', default='') if cross_flag == 'CROSS': k12 = double(card, 10, 'k12') k21 = double(card, 11, 'k21') b12 = double_or_blank(card, 12, 'b12', default=0.0) b21 = double_or_blank(card, 13, 'b21', default=0.0) m12 = double_or_blank(card, 14, 'm12', default=0.0) m21 = double_or_blank(card, 15, 'm21', default=0.0) else: assert cross_flag == '', card k12 = None k21 = None b12 = None b21 = None m12 = None m21 = None return PBUSH2D(pid, k11, k22, b11, b22, m11, m22, cross_flag, k12, k21, b12, b21, m12, m21, comment=comment)
[docs] def repr_fields(self) -> list: list_fields = ['PBUSH2D', self.pid, self.k11, self.k22, self.b11, self.b22, self.m11, self.m22, None] if self.cross_flag == 'CROSS': list_fields.extend(['CROSS', self.k12, self.k21, self.b12, self.b21, self.m12, self.m21]) return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: """ Writes the card with the specified width and precision Parameters ---------- size : int (default=8) size of the field; {8, 16} is_double : bool (default=False) is this card double precision Returns ------- msg : str the string representation of the card """ card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] def _append_nones(list_obj, nrequired): """this function has side effects""" n_none = nrequired - len(list_obj) list_obj.extend([None] * n_none)
[docs] class PBUSHT(BushingProperty): type = 'PBUSHT'
[docs] @classmethod def _init_from_empty(cls): pid = 1 k_tables = [] b_tables = [] ge_tables = [] kn_tables = [] return PBUSHT(pid, k_tables, b_tables, ge_tables, kn_tables, comment='')
[docs] def update_by_pname_fid(self, name, value): if name == 'TGEID1': self.ge_tables[0] = value elif name == 'TGEID2': self.ge_tables[1] = value else: raise NotImplementedError('%r has not implemented update_by_pname_fid for %r' % (self.type, name))
def __init__(self, pid, k_tables, b_tables, ge_tables, kn_tables, comment=''): BushingProperty.__init__(self) if comment: self.comment = comment self.pid = pid _append_nones(k_tables, 6) _append_nones(b_tables, 6) _append_nones(ge_tables, 6) _append_nones(kn_tables, 6) self.k_tables = k_tables self.b_tables = b_tables self.ge_tables = ge_tables self.kn_tables = kn_tables
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBUSHT card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ k_tables = [] b_tables = [] ge_tables = [] kn_tables = [] pid = integer(card, 1, 'pid') nfields = len(card) - 1 nrows = nfields // 8 if nfields % 8 != 0: nrows += 1 for irow in range(nrows): ifield = 1 + irow * 8 param = string(card, ifield + 1, 'param_type') table = [] for j in range(6): table_value = integer_or_blank(card, ifield + j + 2, param + '%i' % (j+1)) table.append(table_value) if param == 'K': k_tables = table elif param == 'B': b_tables = table elif param == 'GE': ge_tables = table elif param == 'KN': kn_tables = table else: raise ValueError(param) return PBUSHT(pid, k_tables, b_tables, ge_tables, kn_tables, comment=comment)
[docs] def raw_fields(self): list_fields = ['PBUSHT', self.pid] if self.k_tables: list_fields += ['K'] + self.k_tables + [None] if self.b_tables: list_fields += ['B'] + self.b_tables + [None] if self.ge_tables: list_fields += ['GE'] + self.ge_tables + [None] if self.kn_tables: list_fields += ['KN'] + self.kn_tables 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 PBUSH_OPTISTRUCT(BushingProperty): """ Generalized Spring-and-Damper Property Defines the nominal property values for a generalized spring-and-damper structural element. +-------+-----+------+-----+-----+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+=====+======+=====+=====+=====+=====+=====+=====+ | PBUSH | PID | K | K1 | K2 | K3 | K4 | K5 | K6 | +-------+-----+------+-----+-----+-----+-----+-----+-----+ | | | B | B1 | B2 | B3 | B4 | B5 | B6 | +-------+-----+------+-----+-----+-----+-----+-----+-----+ | | | GE | GE1 | GE2 | GE3 | GE4 | GE5 | GE6 | +-------+-----+------+-----+-----+-----+-----+-----+-----+ | | | M | M1 | M2 | M3 | M4 | M5 | M6 | +-------+-----+------+-----+-----+-----+-----+-----+-----+ """ type = 'PBUSH' _field_map = { 1: 'pid', } pname_map = { -2 : 'K1', -3 : 'K2', -4 : 'K3', -5 : 'K4', -6 : 'K5', -7 : 'K6', -8 : 'B1', -9 : 'B2', -10 : 'B3', -11 : 'B4', -12 : 'B5', -13 : 'B6', -14 : 'GE1', -15 : 'GE2', -16 : 'GE3', -17 : 'GE4', -18 : 'GE5', -19 : 'GE6', -20 : 'SA', -21 : 'ST', -22 : 'EA', -23 : 'ET', } #def update_by_pname_fid(self, name, value): #if name == 'B1': #self.Bi[0] = value #elif name == 'B2': #self.Bi[1] = value #elif name == 'B3': #self.Bi[2] = value #elif name == 'B4': #self.Bi[3] = value #elif name == 'B5': #self.Bi[4] = value #elif name == 'B6': #self.Bi[5] = value #elif name == 'K1': #self.Ki[0] = value #elif name == 'K2': #self.Ki[1] = value #elif name == 'K3': #self.Ki[2] = value #elif name == 'K4': #self.Ki[3] = value #elif name == 'K5': #self.Ki[4] = value #elif name == 'K6': #self.Ki[5] = value #elif name == 'GE1': #self.GEi[0] = value #elif name == 'GE2': #self.GEi[1] = value #elif name == 'GE3': #self.GEi[2] = value #elif name == 'GE4': #self.GEi[3] = value #elif name == 'GE5': #self.GEi[4] = value #elif name == 'GE6': #self.GEi[5] = value ##elif name == 'M': ##self.mass #elif isinstance(name, int) and name in self.pname_map: #name2 = self.pname_map[name] #self.update_by_pname_fid(name2, value) #else: #raise NotImplementedError('property_type=%r has not implemented %r in pname_map' % ( #self.type, name)) #pname_fid_map = { #4 : 'A', 'A' : 'A', #5 : 'i1', 'I1' : 'i1', #6 : 'i2', 'I2' : 'i2', #7 : 'i12', 'I12' : 'i12', #5 : 'j', 'J' : 'j', #} def __init__(self, pid, k, b, ge, mass, comment=''): """ Creates a PBUSH card, which defines a property for a CBUSH Parameters ---------- pid : int property id k : list[float] Nominal stiffness values in directions 1 through 6. len(k) = 6 b : list[float] Nominal damping coefficients in direction 1 through 6 in units of force per unit velocity len(b) = 6 ge : list[float] Nominal structural damping constant in directions 1 through 6. len(ge) = 6 mass : list[float] Nominal mass in directions 1 through 6. comment : str; default='' a comment for the card """ BushingProperty.__init__(self) if comment: self.comment = comment #: Property ID self.pid = pid self.vars = [] # K parameter self.k = k if k: nk = len(k) if nk < 6: k.extend([0.] * (6 - nk)) self.vars.append('K') # B parameter self.b = b if b: nb = len(b) if nb < 6: b.extend([0.] * (6 - nb)) self.vars.append('B') # GE parameter self.ge = ge if ge: nge = len(ge) if nge < 6: ge.extend([0.] * (6 - nge)) self.vars.append('GE') self.mass = mass if mass: nmass = len(mass) if nmass < 6: mass.extend([0.] * (6 - nmass)) self.vars.append('M')
[docs] @classmethod def _init_from_empty(cls): pid = 1 k = [1.] b = [1.] ge = [1.] mass = [1.] return PBUSH(pid, k, b, ge, mass, comment='')
[docs] def validate(self): assert isinstance(self.k, list), 'PBUSH: pid=%i type(k)=%s k=%s' % (self.pid, type(self.k), self.k) assert isinstance(self.b, list), 'PBUSH: pid=%i type(b)=%s b=%s' % (self.pid, type(self.b), self.b) assert isinstance(self.ge, list), 'PBUSH: pid=%i type(ge)=%s ge=%s' % (self.pid, type(self.ge), self.ge) assert isinstance(self.mass, list), 'PBUSH: pid=%i type(mass)=%s mass=%s' % (self.pid, type(self.mass), self.mass)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBUSH card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ k_fields = [] b_fields = [] ge_fields = [] mass_fields = [] pid = integer(card, 1, 'pid') nfields = card.nfields istart = 2 while istart < nfields: pname = string(card, istart, 'pname') print('panme =', pname) if pname == 'K': # Flag indicating that the next 1 to 6 fields are stiffness values in # the element coordinate system. #self.k = string(card, istart, 'k') #: Nominal stiffness values in directions 1 through 6. #: See Remarks 2 and 3. (Real; Default = 0.0) k_fields = cls._read_var(card, 'Ki', istart + 1, istart + 7) elif pname == 'B': # Flag indicating that the next 1 to 6 fields are force-per-velocity # damping. #self.b = string(card, istart, 'b') #: Force per unit velocity (Real) #: Nominal damping coefficients in direction 1 through 6 in units of #: force per unit velocity. See Remarks 2, 3, and 9. (Real; Default=0.) b_fields = cls._read_var(card, 'Bi', istart + 1, istart + 7) elif pname == 'GE': # Flag indicating that the next fields, 1 through 6 are structural # damping constants. See Remark 7. (Character) #self.ge = string(card, istart, 'ge') #: Nominal structural damping constant in directions 1 through 6. See #: Remarks 2. and 3. (Real; Default = 0.0) ge_fields = cls._read_var(card, 'GEi', istart + 1, istart + 7) elif pname == 'M': # Lumped mass of the cbush; default=0.0 mass_fields = cls._read_var(card, 'MASSi', istart + 1, istart + 7) else: raise RuntimeError('unsupported PBUSH type; pname=%r' % pname) #break # old version... istart += 8 return PBUSH_OPTISTRUCT(pid, k_fields, b_fields, ge_fields, mass_fields, comment=comment)
[docs] @classmethod def _read_var(cls, card, var_prefix, istart, iend): print(card[istart:iend+1]) Ki = fields(double_string_or_blank, card, var_prefix, istart, iend) return Ki
@classmethod def add_op2_data(cls, data, comment=''): """ Adds a PBUSH card from the OP2 Parameters ---------- data : list[varies] a list of fields defined in OP2 format comment : str; default='' a comment for the card """ (pid, k1, k2, k3, k4, k5, k6, b1, b2, b3, b4, b5, b6, g1, g2, g3, g4, g5, g6, m1, m2, m3, m4, m5, m6) = data k_fields = [k1, k2, k3, k4, k5, k6] b_fields = [b1, b2, b3, b4, b5, b6] ge_fields = [g1, g2, g3, g4, g5, g6] mass_fields = [m1, m2, m3, m4, m5, m6] return PBUSH_OPTISTRUCT(pid, k_fields, b_fields, ge_fields, mass_fields, comment=comment) def _verify(self, xref): pid = self.Pid() assert isinstance(pid, integer_types), 'pid=%r' % pid
[docs] def raw_fields(self): list_fields = ['PBUSH', self.pid] for var in self.vars: if var == 'K': list_fields += ['K'] + self.k elif var == 'B': list_fields += ['B'] + self.b elif var == 'GE': list_fields += ['GE'] + self.ge elif var == 'M': list_fields += ['M', self.mass] else: raise RuntimeError('not supported PBUSH field...') nspaces = 8 - (len(list_fields) - 1) % 8 if nspaces == 8: list_fields += [None] elif nspaces < 8: list_fields += [None] * (nspaces + 1) 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)