Source code for pyNastran.bdf.cards.dynamic

# coding: utf-8
# pylint: disable=C0103,R0902,R0904,R0914
"""
All dynamic control cards are defined in this file.  This includes:

 * FREQ
 * FREQ1
 * FREQ2
 * FREQ3
 * FREQ4
 * FREQ5
 * NLPCI
 * NLPARM
 * TSTEP
 * TSTEP1
 * TSTEPNL
 * ROTORG
 * ROTORD
 * TIC
 * TF

All cards are BaseCard objects.

"""
from __future__ import annotations
from math import log, exp
from typing import TYPE_CHECKING

import numpy as np
from numpy import unique, hstack

from pyNastran.utils.numpy_utils import integer_types
from pyNastran.bdf.field_writer_8 import set_blank_if_default
from pyNastran.bdf.cards.base_card import BaseCard
from pyNastran.bdf.bdf_interface.bdf_card import BDFCard
from pyNastran.bdf.bdf_interface.assign_type import (
    integer, integer_or_blank, double, double_or_blank,
    string_or_blank, blank, fields, components_or_blank,
    integer_string_or_blank, integer_or_double, #parse_components,
    modal_components_or_blank,
)
from pyNastran.bdf.bdf_interface.assign_type_force import force_double
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


#if method_int == 1:
    #method = 'AUTO'
#elif method_int == 2:
    #method = 'TSTEP'
#elif method_int == 3:
    #method = 'ADAPT'

NLPARM_KMETHOD_MAP = {
    1 : 'AUTO',
    2 : 'ITER',
    3 : 'ADAPT',
    4 : 'SEMI',
}
NLPARM_CONV_MAP = {
    1 : 'W',
    2 : 'P',
    3 : 'PW',
    4 : 'U',
    5 : 'UW',
    6 : 'UP',
    7 : 'UPW',
    10: '', # NLSTEP
    14: '', # NLSTEP
    42: 'PVA',
    -1 : 'PW',  # Nastran-CoFE : blank -> assuming default
}
#nlparm_conv_map = {
    #1 : 'W',
    #2 : 'P',  # guess based on format
    #3 : 'PW',
    #4 : 'U',
    #7 : 'UPW',
    #10 : '',
    ##3: 'ADAPT'
#}



NLPARM_INT_OUT_MAP = {
    0 : 'NO',
    1 : 'YES',
    2 : 'ALL',
}

[docs] class DELAY(BaseCard): """ +-------+-----+-----------+-----+--------+------+-----+--------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +=======+=====+===========+=====+========+======+=====+========+ | DELAY | SID | POINT ID1 | C1 | T1 | P2 | C2 | T2 | +-------+-----+-----------+-----+--------+------+-----+--------+ """ type = 'DELAY' _properties = ['node_id1', 'node_id2', 'node_ids']
[docs] @classmethod def _init_from_empty(cls): sid = 1 nodes = [1] components = [1] delays = [1.] return DELAY(sid, nodes, components, delays, comment='')
def __init__(self, sid: int, nodes: list[int], components: list[int], delays: list[float], comment: str=''): """ Creates a DELAY card Parameters ---------- sid : int DELAY id that is referenced by a TLOADx, RLOADx or ACSRCE card nodes : list[int] list of nodes that see the delay len(nodes) = 1 or 2 components : list[int] the components corresponding to the nodes that see the delay len(nodes) = len(components) delays : list[float] Time delay (tau) for designated point Pi and component Ci len(nodes) = len(delays) comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment if isinstance(nodes, integer_types): nodes = [nodes] if isinstance(components, integer_types): components = [components] if isinstance(delays, float): delays = [delays] assert len(nodes) == len(components) assert len(nodes) == len(delays) #: Identification number of DELAY entry. (Integer > 0) self.sid = sid #: Grid, extra, or scalar point identification number. (Integer > 0) self.nodes = nodes #: Component number. (Integers 1 through 6 for grid points; zero or blank for extra #: or scalar points) self.components = components #: Time delay (tau) for designated point Pi and component Ci. (Real) self.delays = delays self.nodes_ref = None
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a DELAY card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') nodes = [integer(card, 2, 'node')] components = [integer_or_blank(card, 3, 'components', 0)] delays = [double_or_blank(card, 4, 'delay')] assert components[0] in [0, 1, 2, 3, 4, 5, 6], components if card.field(5): nodes.append(integer(card, 5, 'node')) components.append(integer_or_blank(card, 6, 'components', 0)) delays.append(double_or_blank(card, 7, 'delay')) assert components[1] in [0, 1, 2, 3, 4, 5, 6], components return DELAY(sid, nodes, components, delays, comment=comment)
[docs] def add(self, delay: DELAY): assert self.sid == delay.sid, 'sid=%s delay.sid=%s' % (self.sid, delay.sid) if delay.comment: if hasattr('_comment'): self._comment += delay.comment else: self._comment = delay.comment self.nodes += delay.nodes self.components += delay.components self.delays += delay.delays assert len(self.nodes) == len(self.components) assert len(self.nodes) == len(self.delays)
[docs] def get_delay_at_freq(self, freq): return self.nodes, self.components, self.delays
[docs] def cross_reference(self, model: BDF, xref_errors): """ Cross links the card so referenced cards can be extracted directly Parameters ---------- model : BDF() the BDF object """ msg = ', which is required by DELAY sid=%s' % self.sid self.nodes_ref = model.Node(self.node_ids, msg=msg)
[docs] def uncross_reference(self) -> None: """Removes cross-reference links""" self.nodes = self.node_ids self.nodes_ref = None
@property def node_id1(self): if self.nodes_ref is not None: return self.nodes_ref[0].nid return self.nodes[0] @property def node_id2(self): if self.nodes_ref is not None: return self.nodes_ref[1].nid return self.nodes[1] @property def node_ids(self): node_ids = [self.node_id1] if len(self.components) == 2: node_ids.append(self.node_id2) return node_ids
[docs] def raw_fields(self): list_fields = ['DELAY', self.sid] node_ids = self.node_ids assert len(node_ids) == len(self.components) assert len(node_ids) == len(self.delays) for nid, comp, delay in zip(node_ids, self.components, self.delays): if isinstance(nid, integer_types): nidi = nid else: nidi = nid.nid list_fields += [nidi, comp, delay] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: msg = self.comment node_ids = self.node_ids assert len(node_ids) == len(self.components) assert len(node_ids) == len(self.delays) if size == 8: for nid, comp, delay in zip(node_ids, self.components, self.delays): msg += print_card_8(['DELAY', self.sid, nid, comp, delay]) else: for nid, comp, delay in zip(node_ids, self.components, self.delays): msg += print_card_16(['DELAY', self.sid, nid, comp, delay]) return msg
[docs] class DPHASE(BaseCard): """ Defines the phase lead term θ in the equation of the dynamic loading function. +--------+-----+-----------+-----+------+------+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +========+=====+===========+=====+======+======+=====+=====+ | DPHASE | SID | POINT ID1 | C1 | TH1 | P2 | C2 | TH2 | +--------+-----+-----------+-----+------+------+-----+-----+ """ type = 'DPHASE' _properties = ['node_id1', 'node_id2', 'node_ids']
[docs] @classmethod def _init_from_empty(cls): sid = 1 nodes = [1] components = [1] phase_leads = [1.] return DPHASE(sid, nodes, components, phase_leads, comment='')
def __init__(self, sid, nodes, components, phase_leads, comment=''): """ Creates a DPHASE card Parameters ---------- sid : int DPHASE id that is referenced by a RLOADx or ACSRCE card nodes : list[int] list of nodes that see the delay len(nodes) = 1 or 2 components : list[int] the components corresponding to the nodes that see the delay len(nodes) = len(components) phase_leads : list[float] Phase lead θ in degrees. len(nodes) = len(delays) comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment if isinstance(nodes, integer_types): nodes = [nodes] if isinstance(components, integer_types): components = [components] if isinstance(phase_leads, float): phase_leads = [phase_leads] self.sid = sid self.nodes = nodes self.components = components self.phase_leads = phase_leads self.nodes_ref = None
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a DPHASE card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') nodes = [integer(card, 2, 'node')] components = [integer(card, 3, 'components')] phase_leads = [double_or_blank(card, 4, 'phase_lead')] assert components[0] in [0, 1, 2, 3, 4, 5, 6], components if card.field(5): nodes.append(integer(card, 5, 'node')) components.append(integer(card, 6, 'components')) phase_leads.append(double_or_blank(card, 7, 'phase_lead')) assert components[1] in [0, 1, 2, 3, 4, 5, 6], components return DPHASE(sid, nodes, components, phase_leads, comment=comment)
[docs] def add(self, dphase: DPHASE): assert self.sid == dphase.sid, 'sid=%s dphase.sid=%s' % (self.sid, dphase.sid) if dphase.comment: if hasattr('_comment'): self._comment += dphase.comment else: self._comment = dphase.comment self.nodes += dphase.nodes self.components += dphase.components self.phase_leads += dphase.phase_leads assert len(self.nodes) == len(self.components) assert len(self.nodes) == len(self.phase_leads)
[docs] def cross_reference(self, model: BDF) -> None: """ Cross links the card so referenced cards can be extracted directly Parameters ---------- model : BDF() the BDF object """ msg = ', which is required by DPHASE sid=%s' % self.sid self.nodes_ref = model.Nodes(self.node_ids, msg=msg)
[docs] def safe_cross_reference(self, model: BDF, xref_errors): return self.cross_reference(model)
[docs] def uncross_reference(self) -> None: """Removes cross-reference links""" self.nodes = self.node_ids self.nodes_ref = None
[docs] def get_dphase_at_freq(self, freq): return self.nodes, self.components, self.phase_leads
@property def node_id1(self): if self.nodes_ref is not None: return self.nodes_ref[0].nid return self.nodes[0] @property def node_id2(self): if self.nodes_ref is not None: return self.nodes_ref[1].nid return self.nodes[1] @property def node_ids(self): if self.nodes_ref is None: return self.nodes node_ids = [self.node_id1] if len(self.components) == 2: node_ids.append(self.node_id2) return node_ids
[docs] def raw_fields(self): list_fields = ['DPHASE', self.sid] node_ids = self.node_ids assert len(node_ids) == len(self.components) assert len(node_ids) == len(self.phase_leads) for nid, comp, delay in zip(node_ids, self.components, self.phase_leads): list_fields += [nid, comp, delay] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: msg = self.comment node_ids = self.node_ids assert len(node_ids) == len(self.components) assert len(node_ids) == len(self.phase_leads) if size == 8: for nid, comp, delay in zip(node_ids, self.components, self.phase_leads): msg += print_card_8(['DPHASE', self.sid, nid, comp, delay]) else: for nid, comp, delay in zip(node_ids, self.components, self.phase_leads): msg += print_card_16(['DPHASE', self.sid, nid, comp, delay]) return msg
[docs] class FREQ(BaseCard): """ Defines a set of frequencies to be used in the solution of frequency response problems. +------+-----+-----+-----+------+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +======+=====+=====+=====+======+=====+=====+=====+=====+ | FREQ | SID | F1 | F2 | etc. | | | | | +------+-----+-----+-----+------+-----+-----+-----+-----+ """ type = 'FREQ'
[docs] @classmethod def _init_from_empty(cls): sid = 1 freqs = [1., 2., 3.] return FREQ(sid, freqs, comment='')
def __init__(self, sid, freqs, comment=''): """ Creates a FREQ card Parameters ---------- sid : int set id referenced by case control FREQUENCY freqs : list[float] the frequencies for a FREQx object comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment self.sid = sid if isinstance(freqs, float): freqs = [freqs] self.freqs = np.unique(freqs)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a FREQ card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') freqs = fields(double, card, 'freq', i=2, j=len(card)) return FREQ(sid, freqs, comment=comment)
[docs] def get_freqs(self): return self.freqs
[docs] def add_frequencies(self, freqs): """ Combines the frequencies from 1 FREQx object with another. All FREQi entries with the same frequency set identification numbers will be used. Duplicate frequencies will be ignored. Parameters ---------- freqs : list[float] / (nfreq, ) float ndarray the frequencies for a FREQx object """ #print("self.freqs = ",self.freqs) #print("freqs = ",freqs) self.freqs = unique(hstack([self.freqs, freqs]))
[docs] def add_frequency_object(self, freq): """ :param freq: a FREQx object .. seealso:: :func:`addFrequencies` """ self.add_frequencies(freq.freqs)
[docs] def raw_fields(self): list_fields = ['FREQ', self.sid] + list(self.freqs) 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 FREQ1(BaseCard): """ Defines a set of frequencies to be used in the solution of frequency response problems by specification of a starting frequency, frequency increment, and the number of increments desired. +-------+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | +=======+=====+=====+=====+=====+ | FREQ1 | SID | F1 | DF | NDF | +-------+-----+-----+-----+-----+ .. note:: this card rewrites as a FREQ card """ type = 'FREQ1'
[docs] @classmethod def _init_from_empty(cls): sid = 1 f1 = 1. df = 10. return FREQ1(sid, f1, df, ndf=1, comment='')
def __init__(self, sid, f1, df, ndf=1, comment=''): """ Creates a FREQ1 card Parameters ---------- sid : int set id referenced by case control FREQUENCY f1 : float first frequency df : float frequency increment ndf : int number of frequency increments comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment self.sid = sid self.f1 = f1 self.df = df self.ndf = ndf freqs = [] for i in range(ndf + 1): freqs.append(f1 + i * df) self.freqs = unique(freqs)
[docs] @classmethod def add_card(cls, card: BDFCard, comment: str=''): """ Adds a FREQ1 card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') f1 = double_or_blank(card, 2, 'f1', default=0.0) df = double(card, 3, 'df') ndf = integer_or_blank(card, 4, 'ndf', default=1) assert len(card) <= 5, f'len(FREQ card) = {len(card):d}\ncard={card}' return FREQ1(sid, f1, df, ndf, comment=comment)
[docs] @classmethod def add_card_lax(cls, card: BDFCard, comment: str=''): """ Adds a FREQ1 card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') f1 = double_or_blank(card, 2, 'f1', default=0.0) df = force_double(card, 3, 'df') ndf = integer_or_blank(card, 4, 'ndf', default=1) assert len(card) <= 5, f'len(FREQ card) = {len(card):d}\ncard={card}' return FREQ1(sid, f1, df, ndf, comment=comment)
[docs] def raw_fields(self): list_fields = ['FREQ1', self.sid, self.f1, self.df, self.ndf] 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 FREQ2(BaseCard): """ Defines a set of frequencies to be used in the solution of frequency response problems by specification of a starting frequency, final frequency, and the number of logarithmic increments desired. +-------+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | +=======+=====+=====+=====+=====+ | FREQ2 | SID | F1 | F2 | NF | +-------+-----+-----+-----+-----+ .. note:: this card rewrites as a FREQ card """ type = 'FREQ2'
[docs] @classmethod def _init_from_empty(cls): sid = 1 f1 = 1. f2 = 2. return FREQ2(sid, f1, f2, nf=1, comment='')
def __init__(self, sid, f1, f2, nf=1, comment=''): """ Creates a FREQ2 card Parameters ---------- sid : int set id referenced by case control FREQUENCY f1 : float first frequency f2 : float last frequency nf : int; default=1 number of logorithmic intervals comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment self.sid = sid self.f1 = f1 self.f2 = f2 self.nf = nf assert f1 > 0., 'FREQ2: f1=%s f2=%s nf=%s' % (f1, f2, nf) assert nf > 0, 'FREQ2: f1=%s f2=%s nf=%s' % (f1, f2, nf) d = 1. / nf * log(f2 / f1) freqs = [f1] for i in range(1, nf + 1): freqs.append(f1 * exp(i * d)) # 0 based index self.freqs = np.unique(freqs)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a FREQ2 card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') f1 = double(card, 2, 'f1') # default=0.0 ? f2 = double(card, 3, 'f2') nf = integer_or_blank(card, 4, 'nf', 1) assert len(card) <= 5, f'len(FREQ2 card) = {len(card):d}\ncard={card}' return FREQ2(sid, f1, f2, nf, comment=comment)
#return FREQ(sid, freqs, comment=comment)
[docs] def raw_fields(self): list_fields = ['FREQ2', self.sid, self.f1, self.f2, self.nf] 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 FREQ3(FREQ): """ +-------+-----+------+-------+--------+-----+---------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +=======+=====+======+=======+========+=====+=========+ | FREQ3 | SID | F1 | F2 | TYPE | NEF | CLUSTER | +-------+-----+------+-------+--------+-----+---------+ | FREQ3 | 6 | 20.0 | 200.0 | LINEAR | 10 | 2.0 | +-------+-----+------+-------+--------+-----+---------+ """ type = 'FREQ3'
[docs] @classmethod def _init_from_empty(cls): sid = 1 f1 = 1. return FREQ3(sid, f1, f2=None, freq_type='LINEAR', nef=10, cluster=1.0, comment='')
def __init__(self, sid, f1, f2=None, freq_type='LINEAR', nef=10, cluster=1.0, comment=''): """ Creates a FREQ3 card Parameters ---------- sid : int set id referenced by case control FREQUENCY f1 : float; default=0.0??? Lower bound of frequency range in cycles per unit time. f2 : float; default=1E20??? Upper bound of frequency range in cycles per unit time. freq_type : str; default=LINEAR valid_types={LINEAR, LOG} nef : int; default=10 ??? cluster : float; default=1.0 ??? comment : str; default='' a comment for the card """ if comment: self.comment = comment if f2 is None: f2 = f1 self.sid = sid self.f1 = f1 self.f2 = f2 self.freq_type = freq_type self.nef = nef self.cluster = cluster #def get_freqs(self): #raise NameError()
[docs] def validate(self): assert self.freq_type in ['LINEAR', 'LOG'], 'freq_type=%r' % self.freq_type
[docs] def get_frequencies(self, natural_freq: np.ndarray) -> np.ndarray: f1 = self.f1 f2 = self.f2 freq_type = self.freq_type cluster = 1 / self.cluster nef = self.nef if f1 == f2: return np.array([f1], dtype='float64') natural_freq2 = np.unique(np.hstack([[f1, f2], natural_freq])) ifreq = (f1 <= natural_freq2) & (natural_freq2 <= f2) natural_freq3 = natural_freq2[ifreq] #f1_plus_f2 = 0.5 * (f1s + f2s) #f2_minus_f1 = 0.5 * (f2s - f1s) #nef = 11 #cluster = 1 / 0.25 k = np.arange(nef, dtype='int32') zeta = -1 + 2 * k / (nef - 1) zeta2 = np.abs(zeta) ** cluster * np.sign(zeta) #f1s = [10.] #f2s = [20.] frequencies_list = [] if freq_type == 'LINEAR': f1s = natural_freq3[:-1] f2s = natural_freq3[1:] for f1, f2 in zip(f1s, f2s): fhat = 0.5 * (f1 + f2) + 0.5 * (f2 - f1) * zeta2 frequencies_list.append(fhat) elif freq_type == 'LOG': f1s = np.log10(natural_freq3[:-1]) f2s = np.log10(natural_freq3[1:]) for f1, f2 in zip(f1s, f2s): flog_hat = 0.5 * (f1 + f2) + 0.5 * (f2 - f1) * zeta2 fhat = 10 ** flog_hat frequencies_list.append(fhat) else: # pragma: no cover raise RuntimeError(self.get_stats()) frequencies = np.unique(np.hstack(frequencies_list)) return frequencies
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a FREQ3 card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') f1 = double(card, 2, 'f1') f2 = double_or_blank(card, 3, 'f2', default=f1) freq_type = string_or_blank(card, 4, 'Type', 'LINEAR') nef = integer_or_blank(card, 5, 'nef', default=10) cluster = double_or_blank(card, 6, 'cluster', default=1.0) return FREQ3(sid, f1, f2=f2, freq_type=freq_type, nef=nef, cluster=cluster, comment=comment)
[docs] def raw_fields(self): return ['FREQ3', self.sid, self.f1, self.f2, self.freq_type, self.nef, self.cluster]
[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 FREQ4(FREQ): """ Defines a set of frequencies used in the solution of modal frequency response problems by specifying the amount of 'spread' around each natural frequency and the number of equally spaced excitation frequencies within the spread. +-------+-----+-----+-----+------+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+=====+=====+=====+======+=====+=====+=====+=====+ | FREQ4 | SID | F1 | F2 | FSPD | NFM | | | | +-------+-----+-----+-----+------+-----+-----+-----+-----+ .. note:: this card rewrites as a FREQ card .. todo:: not done... """ type = 'FREQ4'
[docs] @classmethod def _init_from_empty(cls): sid = 1 return FREQ4(sid, f1=0., f2=1e20, fspread=0.1, nfm=3, comment='')
def __init__(self, sid, f1=0., f2=1e20, fspread=0.1, nfm=3, comment=''): """ Creates a FREQ4 card Parameters ---------- sid : int set id referenced by case control FREQUENCY f1 : float; default=0.0 Lower bound of frequency range in cycles per unit time. f2 : float; default=1E20 Upper bound of frequency range in cycles per unit time. nfm : int; default=3 Number of evenly spaced frequencies per 'spread' mode. comment : str; default='' a comment for the card """ if comment: self.comment = comment self.sid = sid self.f1 = f1 self.f2 = f2 self.fspread = fspread self.nfm = nfm
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a FREQ4 card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') f1 = double_or_blank(card, 2, 'f1', 0.0) f2 = double_or_blank(card, 3, 'f2', 1.e20) fspread = double_or_blank(card, 4, 'fspd', 0.1) nfm = integer_or_blank(card, 5, 'nfm', 3) assert len(card) <= 6, f'len(FREQ card) = {len(card):d}\ncard={card}' return FREQ4(sid, f1=f1, f2=f2, fspread=fspread, nfm=nfm, comment=comment)
[docs] def get_frequencies(self, natural_freq: np.ndarray) -> np.ndarray: # 21 equally spaced bands for each mode num = self.nfm f1 = self.f1 f2 = self.f2 fspread = self.fspread frequencies_list = [] for natural_freqi in natural_freq: if not(f1 <= natural_freqi <= f2): continue f1i = (1 - fspread) * natural_freqi f2i = (1 + fspread) * natural_freqi freqs = np.linspace(f1i, f2i, num=num) frequencies_list.append(freqs) return np.unique(np.hstack(frequencies_list))
[docs] def raw_fields(self): list_fields = ['FREQ4', self.sid, self.f1, self.f2, self.fspread, self.nfm] 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 FREQ5(FREQ): """ Defines a set of frequencies used in the solution of modal frequency-response problems by specification of a frequency range and fractions of the natural frequencies within that range. +-------+------+------+--------+------+-----+-----+-----+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+======+======+========+======+=====+=====+=====+======+ | FREQ5 | SID | F1 | F2 | FR1 | FR2 | FR3 | FR4 | FR5 | +-------+------+------+--------+------+-----+-----+-----+------+ | FREQ5 | 6 | 20.0 | 2000.0 | 1.0 | 0.6 | 0.8 | 0.9 | 0.95 | +-------+------+------+--------+------+-----+-----+-----+------+ | | 1.05 | 1.1 | 1.2 | | | | | | +-------+------+------+--------+------+-----+-----+-----+------+ """ type = 'FREQ5'
[docs] @classmethod def _init_from_empty(cls): sid = 1 fractions = [0.1, 0.2, 0.3] return FREQ5(sid, fractions, f1=0., f2=1e20, comment='')
def __init__(self, sid, fractions, f1=0., f2=1e20, comment=''): """ Creates a FREQ5 card Parameters ---------- sid : int set id referenced by case control FREQUENCY f1 : float; default=0.0 Lower bound of frequency range in cycles per unit time. f2 : float; default=1e20 Upper bound of frequency range in cycles per unit time. fractions : list[float] Fractions of the natural frequencies in the range F1 to F2. comment : str; default='' a comment for the card .. note:: FREQ5 is only valid in modal frequency-response solutions (SOLs 111, 146, and 200) and is ignored in direct frequency response solutions. """ if comment: self.comment = comment if f2 is None: f2 = f1 self.sid = sid self.f1 = f1 self.f2 = f2 self.fractions = fractions
[docs] def validate(self): assert len(self.fractions) > 0, 'FREQ5: fractions=%s' % self.fractions
[docs] def get_frequencies(self, natural_freq: np.ndarray) -> np.ndarray: """ #f1 : 20.0 #f2 : 200.0 #fractions : [0.6, 0.8, 0.9, 0.95, 1.0, 1.05, 1.1, 1.2] """ fractions = np.unique(self.fractions) freqsi = fractions[:, np.newaxis] * natural_freq[np.newaxis, :] freqsi2 = np.unique(freqsi) ifreq = (self.f1 <= freqsi2) & (freqsi2 <= self.f2) frequencies = freqsi2[ifreq] return frequencies
[docs] @classmethod def add_card(cls, card, comment=''): sid = integer(card, 1, 'sid') f1 = double(card, 2, 'f1') f2 = double_or_blank(card, 3, 'f2', f1) i = 1 fractions = [] for ifield in range(4, len(card)): fraction = double(card, ifield, 'FR%i' % (i)) fractions.append(fraction) i += 1 return FREQ5(sid, fractions, f1=f2, f2=f2, comment=comment)
[docs] def raw_fields(self): return ['FREQ5', self.sid, self.f1, self.f2] + list(self.fractions)
[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 NLPARM(BaseCard): """ Defines a set of parameters for nonlinear static analysis iteration strategy. +--------+--------+------+------+---------+-------+---------+---------+--------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +========+========+======+======+=========+=======+=========+=========+========+ | NLPARM | ID | NINC | DT | KMETHOD | KSTEP | MAXITER | CONV | INTOUT | +--------+--------+------+------+---------+-------+---------+---------+--------+ | | ESPU | EPSP | EPSW | MAXDIV | MAXQN | MAXLS | FSTRESS | LSTOL | +--------+--------+------+------+---------+-------+---------+---------+--------+ | | MAXBIS | | | | MAXR | | RTOLB | CONV | +--------+--------+------+------+---------+-------+---------+---------+--------+ """ type = 'NLPARM' _properties = ['nlparm_id']
[docs] @classmethod def _init_from_empty(cls): nlparm_id = 1 return NLPARM(nlparm_id, ninc=None, dt=0.0, kmethod='AUTO', kstep=5, max_iter=25, conv='PW', int_out='NO', eps_u=0.01, eps_p=0.01, eps_w=0.01, max_div=3, max_qn=None, max_ls=4, fstress=0.2, ls_tol=0.5, max_bisect=5, max_r=20., rtol_b=20., comment='')
def __init__(self, nlparm_id, ninc=None, dt=0.0, kmethod='AUTO', kstep=5, max_iter=25, conv='PW', int_out='NO', eps_u=0.01, eps_p=0.01, eps_w=0.01, max_div=3, max_qn=None, max_ls=4, fstress=0.2, ls_tol=0.5, max_bisect=5, max_r=20., rtol_b=20., comment=''): """ Creates an NLPARM card Parameters ---------- nlparm_id : int NLPARM id; points to the Case Control NLPARM ninc :int; default=None The default ninc changes default based on the solution/element type & params. The default for NINC is 10, except if there is a GAP, Line Contact, Heat Transfer or PARAM,NLTOL,0, in which case the default is 1. dt : float; default=0.0 ??? kmethod : str; default='AUTO' ??? kstep : int; default=5 ??? max_iter : int; default=25 ??? conv : str; default='PW' ??? int_out : str; default='NO' ??? eps_u : float; default=0.01 ??? eps_p : float; default=0.01 ??? eps_w : float; default=0.01 ??? max_div : int; default=3 ??? max_qn; default=None -> varies ??? max_ls : int; default=4 ??? fstress : float; default=0.2 ??? ls_tol : float; default=0.5 ??? max_bisect : int; default=5 max number of bisections max_r : float; default=20. ??? rtol_b : float; default=20. ??? comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment if max_qn is None: if kmethod == 'PFNT': max_qn = 0 else: max_qn = max_iter self.nlparm_id = nlparm_id self.ninc = ninc self.dt = dt self.kmethod = kmethod self.kstep = kstep self.max_iter = max_iter self.conv = conv self.int_out = int_out # line 2 self.eps_p = eps_p self.eps_u = eps_u self.eps_w = eps_w self.max_div = max_div self.max_qn = max_qn self.max_ls = max_ls self.fstress = fstress self.ls_tol = ls_tol # line 3 self.max_bisect = max_bisect self.max_r = max_r self.rtol_b = rtol_b
[docs] def validate(self) -> None: # line 1 assert self.nlparm_id > 0, self.get_stats() assert self.ninc is None or self.ninc >= 0, self.get_stats() # is this >0 or >= 0? assert self.dt >= 0., self.get_stats() assert self.kstep >= -1, self.get_stats() assert len(set(self.conv)) == len(self.conv), f'There are duplicate values in conv={self.conv!r}' for conv_key in set(self.conv): #MSC: 'U', 'P', 'W', 'V', 'N', 'A' assert conv_key in ['U', 'P', 'W', 'V', 'N', 'A', ''], self.get_stats() assert self.int_out in ['YES', 'NO', 'ALL'], self.get_stats() # line 2 assert self.eps_p > 0.0, self.get_stats() assert self.eps_u > 0.0 or self.eps_u < 0.0, self.get_stats() assert self.eps_w > 0.0 or self.eps_w < 0.0, self.get_stats() assert isinstance(self.max_div, int), self.get_stats() assert self.max_qn >= 0, self.get_stats() assert self.max_ls >= 0, self.get_stats() assert 0.0 < self.fstress < 1.0, self.get_stats() assert 0.01 < self.ls_tol < 0.9, self.get_stats() # line 3 # [-10, 10] is the official limit, but is not limited by that; 100 is insane... assert -100 <= self.max_bisect <= 100, self.get_stats() assert 1.0 <= self.max_r <= 40.0, self.get_stats() assert self.rtol_b > 0.0, self.get_stats()
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds an NLPARM card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ nlparm_id = integer(card, 1, 'nlparm_id') # ninc changes default based on the solution/element type & params # # The default for NINC is 10, except if there is a GAP, Line Contact, Heat Transfer or # PARAM,NLTOL,0, in which case the default is 1. ninc = integer_or_blank(card, 2, 'ninc') dt = double_or_blank(card, 3, 'dt', default=0.0) kmethod = string_or_blank(card, 4, 'kmethod', default='AUTO') kstep = integer_or_blank(card, 5, 'kstep', default=5) max_iter = integer_or_blank(card, 6, 'max_iter', default=25) conv = string_or_blank(card, 7, 'conv', default='PW') int_out = string_or_blank(card, 8, 'intOut', default='NO') # line 2 eps_u = double_or_blank(card, 9, 'eps_u', default=0.01) eps_p = double_or_blank(card, 10, 'eps_p', default=0.01) eps_w = double_or_blank(card, 11, 'eps_w', default=0.01) max_div = integer_or_blank(card, 12, 'max_div', default=3) if kmethod == 'PFNT': max_qn = integer_or_blank(card, 13, 'max_qn', default=0) else: max_qn = integer_or_blank(card, 13, 'max_qn', default=max_iter) max_ls = integer_or_blank(card, 14, 'max_ls', default=4) fstress = double_or_blank(card, 15, 'fstress', default=0.2) ls_tol = double_or_blank(card, 16, 'ls_tol', default=0.5) # line 3 max_bisect = integer_or_blank(card, 17, 'max_bisect', default=5) max_r = double_or_blank(card, 21, 'max_r', default=20.) rtol_b = double_or_blank(card, 23, 'rtol_b', default=20.) assert len(card) <= 24, f'len(NLPARM card) = {len(card):d}\ncard={card}' return NLPARM(nlparm_id, ninc, dt, kmethod, kstep, max_iter, conv, int_out, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, ls_tol, max_bisect, max_r, rtol_b, comment=comment)
@classmethod def add_op2_data(cls, data, comment=''): """ Adds an NLPARM card from the OP2 Parameters ---------- data : list[varies] a list of fields defined in OP2 format comment : str; default='' a comment for the card """ if len(data) == 19: (nlparm_id, ninc, dt, kmethod, kstep, max_iter, conv, int_out, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, ls_tol, max_bisect, max_r, rtol_b) = data else: (nlparm_id, ninc, dt, kmethod, kstep, max_iter, conv, int_out, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, ls_tol, max_bisect, max_r, rtol_b, end_zero) = data assert end_zero == 0, data try: kmethod = NLPARM_KMETHOD_MAP[kmethod] except KeyError: msg = 'nlparm_id=%s kmethod=%r data=%s' % (nlparm_id, kmethod, data) raise NotImplementedError(msg) try: conv = NLPARM_CONV_MAP[conv] except KeyError: msg = 'nlparm_id=%s conv=%r data=%s' % (nlparm_id, conv, data) raise NotImplementedError(msg) try: int_out = NLPARM_INT_OUT_MAP[int_out] except KeyError: msg = 'nlparm_id=%s int_out=%r data=%s' % (nlparm_id, int_out, data) raise NotImplementedError(msg) nlparm = NLPARM(nlparm_id, ninc, dt, kmethod, kstep, max_iter, conv, int_out, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, ls_tol, max_bisect, max_r, rtol_b, comment=comment) nlparm.validate() return nlparm
[docs] def raw_fields(self): list_fields = ['NLPARM', self.nlparm_id, self.ninc, self.dt, self.kmethod, self.kstep, self.max_iter, self.conv, self.int_out, self.eps_u, self.eps_p, self.eps_w, self.max_div, self.max_qn, self.max_ls, self.fstress, self.ls_tol, self.max_bisect, None, None, None, self.max_r, None, self.rtol_b] return list_fields
[docs] def repr_fields(self): # ninc changes default based on the solution/element type & params # # The default for NINC is 10, except if there is a GAP, Line Contact, Heat Transfer or # PARAM,NLTOL,0, in which case the default is 1. dt = set_blank_if_default(self.dt, 0.0) kmethod = set_blank_if_default(self.kmethod, 'AUTO') kstep = set_blank_if_default(self.kstep, 5) max_iter = set_blank_if_default(self.max_iter, 25) conv = set_blank_if_default(self.conv, 'PW') int_out = set_blank_if_default(self.int_out, 'NO') eps_u = set_blank_if_default(self.eps_u, 0.01) eps_p = set_blank_if_default(self.eps_p, 0.01) eps_w = set_blank_if_default(self.eps_w, 0.01) max_div = set_blank_if_default(self.max_div, 3) max_qn = set_blank_if_default(self.max_qn, self.max_iter) max_ls = set_blank_if_default(self.max_ls, 4) fstress = set_blank_if_default(self.fstress, 0.2) ls_tol = set_blank_if_default(self.ls_tol, 0.5) max_bisect = set_blank_if_default(self.max_bisect, 5) max_r = set_blank_if_default(self.max_r, 20.) rtol_b = set_blank_if_default(self.rtol_b, 20.) list_fields = ['NLPARM', self.nlparm_id, self.ninc, dt, kmethod, kstep, max_iter, conv, int_out, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, ls_tol, max_bisect, None, None, None, max_r, None, rtol_b] 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) # having trouble with double precision... return self.comment + print_card_16(card)
[docs] class NLPCI(BaseCard): type = 'NLPCI' _properties = ['nlpci_id']
[docs] @classmethod def _init_from_empty(cls): nlpci_id = 1 return NLPCI(nlpci_id, Type='CRIS', minalr=0.25, maxalr=4., scale=0., desiter=12, mxinc=20, comment='')
def __init__(self, nlpci_id, Type='CRIS', minalr=0.25, maxalr=4., scale=0., desiter=12, mxinc=20, comment=''): BaseCard.__init__(self) if comment: self.comment = comment self.nlpci_id = nlpci_id self.Type = Type self.minalr = minalr self.maxalr = maxalr self.scale = scale self.desiter = desiter self.mxinc = mxinc
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds an NLPCI card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ nlpci_id = integer(card, 1, 'nlpci_id') Type = string_or_blank(card, 2, 'Type', 'CRIS') minalr = double_or_blank(card, 3, 'minalr', 0.25) maxalr = double_or_blank(card, 4, 'maxalr', 4.0) scale = double_or_blank(card, 5, 'scale', 0.0) blank(card, 6, 'blank') desiter = integer_or_blank(card, 7, 'desiter', 12) mxinc = integer_or_blank(card, 8, 'mxinc', 20) return NLPCI(nlpci_id, Type=Type, minalr=minalr, maxalr=maxalr, scale=scale, desiter=desiter, mxinc=mxinc, comment=comment)
[docs] def raw_fields(self): list_fields = ['NLPCI', self.nlpci_id, self.Type, self.minalr, self.maxalr, self.scale, None, self.desiter, self.mxinc] return list_fields
[docs] def repr_fields(self): #minalr = set_blank_if_default(self.minalr, 0.25) 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 ROTORD(BaseCard): """ NX-specific card Define Rotor Dynamics Solution Options +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +========+========+========+==========+=========+========+========+==========+==========+ | ROTORD | SID | RSTART | RSTEP | NUMSTEP | REFSYS | CMOUT | RUNIT | FUNIT | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | ZSTEIN | ORBEPS | ROTPRT | SYNC | ETYPE | EORDER | THRSHOLD | MAXITER | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | RID1 | RSET1 | RSPEED1 | RCORD1 | W3_1 | W4_1 | RFORCE1 | BRGSET1 | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | RID2 | RSET2 | RSPEED2 | RCORD2 | W3_2 | W4_2 | RFORCE2 | BRGSET2 | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | ... | | | | | | | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | RIDi | RSETi | RSPEEDi | RCORDi | W3_i | W4_i | RFORCEi | BRGSETi | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | ... | | | | | | | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | RID10 | RSET10 | RSPEED10 | RCORD10 | W3_10 | W4_10 | RFORCE10 | BRGSET10 | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +========+========+========+==========+=========+========+========+==========+==========+ | ROTORD | 998 | 0.0 | 250.0 | 58 | fix | -1.0 | cps | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | no | | | | | | | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 1 | 11 | 1 | 0.0 | 0.0 | 1 | 101 | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 2 | 12 | 1 | 0.0 | 0.0 | 102 | | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 3 | 13 | 1.5 | 1 | 0.0 | 0.0 | 103 | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 4 | 14 | 1.75 | 1 | 0.0 | 0.0 | 104 | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 5 | 15 | 1.75 | 1 | 0.0 | 0.0 | 105 | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 6 | 16 | 1 | 0.0 | 0.0 | 106 | | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 7 | 17 | 2.0 | 1 | 0.0 | 0.0 | 107 | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 8 | 18 | 2.25 | 1 | 0.0 | 0.0 | 108 | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 9 | 19 | 7.5 | 1 | 0.0 | 0.0 | 109 | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ | | 10 | 20 | 1 | 0.0 | 0.0 | 10 | 110 | | +--------+--------+--------+----------+---------+--------+--------+----------+----------+ """ type = 'ROTORD'
[docs] @classmethod def _init_from_empty(cls): sid = 1 rstart = 1 rstep = 1 numstep = 1 rids = [1] rsets = [1] rspeeds = [1] rcords = [1] w3s = [1] w4s = [1] rforces = [1] brgsets = [1] return ROTORD(sid, rstart, rstep, numstep, rids, rsets, rspeeds, rcords, w3s, w4s, rforces, brgsets, refsys='ROT', cmout=0.0, runit='RPM', funit='RPM', zstein='NO', orbeps=1.e-6, roprt=0, sync=1, etype=1, eorder=1.0, threshold=0.02, maxiter=10, comment='')
def __init__(self, sid, rstart, rstep, numstep, rids, rsets, rspeeds, rcords, w3s, w4s, rforces, brgsets, refsys='ROT', cmout=0.0, runit='RPM', funit='RPM', zstein='NO', orbeps=1.e-6, roprt=0, sync=1, etype=1, eorder=1.0, threshold=0.02, maxiter=10, comment=''): """ Adds a ROTORD card Parameters ---------- sid : int Set identifier for all rotors. Must be selected in the case control deck by RMETHOD = SID. rstart : float Starting value of reference rotor speed. rstart : float Step-size of reference rotor speed. See Remark 3. (Real ≠ 0.0) numstep : int Number of steps for reference rotor speed including RSTART. rids : list[int] Identification number of rotor i. (Integer > 0 with RID(i+1) > RIDi; Default = i) rsets : list[int] Refers to the RSETID value on the ROTORG, ROTORB, and ROTSE bulk entries for rotor RIDi. (Integer > 0 or blank if only one rotor) rspeeds : list[int/float, ..., int/float] float : rotor speeds int : TABLEDi rcords : list[int] ??? w3s : list[float] ??? w4s : list[float] ??? rforces : list[int] ??? brgsets : list[int] ??? refsys : str; default='ROT' Reference system 'FIX' analysis is performed in the fixed reference system. 'ROT' analysis is performed in the rotational reference system. cmout : float; default=0.0 ??? runit : str; default=='RPM' ??? funit : str; default=='RPM', ??? zstein : str; default=='NO' ??? orbeps : float; default=1.e-6 ??? roprt : int; default=0 ??? sync : int; default=1 ??? etype : int; default=1 ??? eorder : float; default=1.0 ??? threshold : float; default=0.02 ??? maxiter : int; default=10 ??? comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment self.sid = sid self.rstart = rstart self.rstep = rstep self.numstep = numstep self.refsys = refsys self.cmout = cmout self.runit = runit self.funit = funit self.zstein = zstein self.orbeps = orbeps self.roprt = roprt self.sync = sync self.etype = etype self.eorder = eorder self.threshold = threshold self.maxiter = maxiter self.rids = rids self.rsets = rsets self.rspeeds = rspeeds self.rcords = rcords self.w3s = w3s self.w4s = w4s self.rforces = rforces self.brgsets = brgsets self.rspeeds_ref = None
[docs] def validate(self): assert isinstance(self.rids, list), self.rids assert isinstance(self.rsets, list), self.rsets assert isinstance(self.rspeeds, list), self.rspeeds assert isinstance(self.rcords, list), self.rcords assert isinstance(self.w3s, list), self.w3s assert isinstance(self.w4s, list), self.w4s assert isinstance(self.rforces, list), self.rforces assert isinstance(self.brgsets, list), self.brgsets nrsets = len(self.rsets) if nrsets == 0: raise RuntimeError('nrsets=0') elif nrsets > 1: for rset in self.rsets: assert rset is not None, self.rsets
#assert len(self.grids1) > 0, 'ngrids1=%s\n%s' % (len(self.grids1), str(self))
[docs] def cross_reference(self, model: BDF) -> None: self.rspeeds_ref = [] for rspeed in self.rspeeds: if isinstance(rspeed, integer_types): self.rspeeds_ref.append(model.TableD(rspeed)) else: self.rspeeds_ref.append(rspeed) for rforce in self.rforces: self.rspeeds_ref.append(model.DLoads(rforce))
# ..todo :: BRGSETi # ..todo :: RSETi
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a ROTORD card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') rstart = double(card, 2, 'rstart') rstep = double(card, 3, 'rstep') numstep = integer(card, 4, 'numstep') refsys = string_or_blank(card, 5, 'refsys', 'ROT') cmout = double_or_blank(card, 6, 'cmout', 0.0) runit = string_or_blank(card, 7, 'runit', 'RPM') funit = string_or_blank(card, 8, 'funit', 'RPM') zstein = string_or_blank(card, 9, 'zstein', 'NO') orbeps = double_or_blank(card, 10, 'orbeps', 1.E-6) roprt = integer_or_blank(card, 11, 'roprt', 0) sync = integer_or_blank(card, 12, 'sync', 1) etype = integer_or_blank(card, 13, 'etype', 1) eorder = double_or_blank(card, 14, 'eorder', 1.0) threshold = double_or_blank(card, 15, 'threshold', 0.02) maxiter = integer_or_blank(card, 16, 'maxiter', 10) nfields = len(card) - 17 nrows = nfields // 8 if nfields % 8 > 0: nrows += 1 rids = [] rsets = [] rspeeds = [] rcords = [] w3s = [] w4s = [] rforces = [] brgsets = [] for irow in range(nrows): j = irow * 8 + 17 rid = integer_or_blank(card, j, 'rid_%i' % (irow + 1), irow + 1) rset = integer_or_blank(card, j+1, 'rset_%i' % (irow + 1)) rspeed = integer_or_double(card, j+2, 'rspeed_%i' % (irow + 1)) rcord = integer_or_blank(card, j+3, 'rcord_%i' % (irow + 1), 0) w3 = double_or_blank(card, j+4, 'w3_%i' % (irow + 1), 0.) w4 = double_or_blank(card, j+5, 'w4_%i' % (irow + 1), 0.) rforce = integer_or_blank(card, j+6, 'rforce_%i' % (irow + 1), 0) brgset = integer_or_blank(card, j+7, 'brgset_%i' % (irow + 1)) rids.append(rid) rsets.append(rset) rspeeds.append(rspeed) rcords.append(rcord) w3s.append(w3) w4s.append(w4) rforces.append(rforce) brgsets.append(brgset) return ROTORD(sid, rstart, rstep, numstep, rids, rsets, rspeeds, rcords, w3s, w4s, rforces, brgsets, refsys=refsys, cmout=cmout, runit=runit, funit=funit, zstein=zstein, orbeps=orbeps, roprt=roprt, sync=sync, etype=etype, eorder=eorder, threshold=threshold, maxiter=maxiter, comment=comment)
[docs] def raw_fields(self): list_fields = [ 'ROTORD', self.sid, self.rstart, self.rstep, self.numstep, self.refsys, self.cmout, self.runit, self.funit, self.zstein, self.orbeps, self.roprt, self.sync, self.etype, self.eorder, self.threshold, self.maxiter ] for rid, rset, rspeed, rcord, w3, w4, rforce, brgset in zip( self.rids, self.rsets, self.rspeeds, self.rcords, self.w3s, self.w4s, self.rforces, self.brgsets): list_fields += [rid, rset, rspeed, rcord, w3, w4, rforce, brgset] #print(print_card_8(list_fields)) return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: # double precision? card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] class ROTORG(BaseCard): """ Rotor Grids Selection Selects the grids that define a rotor. +--------+--------+------+------+-----+----+----+----+----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +========+========+======+======+=====+====+====+====+====+ | ROTORG | RSETID | G1 | G2 | G3 | G4 | G5 | G6 | G7 | +--------+--------+------+------+-----+----+----+----+----+ | ROTORG | 14 | 101 | THRU | 190 | BY | 5 | | | +--------+--------+------+------+-----+----+----+----+----+ | | 46 | 23 | 57 | 82 | 9 | 16 | | | +--------+--------+------+------+-----+----+----+----+----+ | | 201 | THRU | 255 | | | | | | +--------+--------+------+------+-----+----+----+----+----+ | | 93 | 94 | 95 | 97 | | | | | +--------+--------+------+------+-----+----+----+----+----+ """ type = 'ROTORG'
[docs] @classmethod def _init_from_empty(cls): sid = 1 nids = [2, 3] return ROTORG(sid, nids, comment='')
def __init__(self, sid, nids, comment=''): BaseCard.__init__(self) if comment: self.comment = comment self.sid = sid self.nids = nids
[docs] def validate(self): pass
#assert len(self.grids1) > 0, 'ngrids1=%s\n%s' % (len(self.grids1), str(self))
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a ROTORG card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') nid1 = integer(card, 2, 'nid1') nid2 = integer_string_or_blank(card, 3, 'nid2') if nid2 is None: nids = [nid1] elif nid2 == 'THRU': nid_thru = integer(card, 4, 'nid_thru') by_flag = string_or_blank(card, 5, 'BY') if by_flag == 'BY': nid_by = integer(card, 6, 'nid_by') nids = [i for i in range(nid1, nid_thru+1, nid_by)] else: assert by_flag is None, by_flag nids = [i for i in range(nid1, nid_thru+1)] elif isinstance(nid2, int): nids = [nid1, nid2] nid = integer_or_blank(card, 4, 'nid3') if nid: nids.append(nid) nid = integer_or_blank(card, 5, 'nid4') if nid: nids.append(nid) nid = integer_or_blank(card, 6, 'nid5') if nid: nids.append(nid) nid = integer_or_blank(card, 7, 'nid6') if nid: nids.append(nid) nid = integer_or_blank(card, 8, 'nid7') if nid: nids.append(nid) else: raise NotImplementedError(nid2) nfields = len(card) - 9 nrows = nfields // 8 if nfields % 8 > 0: nrows += 1 for irow in range(nrows): j = irow * 8 + 9 nid1 = integer(card, j, 'grid_%i' % (irow + 1)) nid2 = integer_string_or_blank(card, j+1, 'nid2') if nid2 is None: pass elif nid2 == 'THRU': nid_thru = integer(card, j+2, 'nid_thru') by_flag = string_or_blank(card, j+3, 'BY') if by_flag == 'BY': nid_by = integer(card, j+4, 'nid3') nids = [i for i in range(nid1, nid_thru+1, nid_by)] else: assert by_flag is None, by_flag nids = [i for i in range(nid1, nid_thru+1)] elif isinstance(nid2, int): nid = integer_or_blank(card, j+2, 'nid3') if nid: nids.append(nid) nid = integer_or_blank(card, j+3, 'nid4') if nid: nids.append(nid) nid = integer_or_blank(card, j+4, 'nid5') if nid: nids.append(nid) nid = integer_or_blank(card, j+5, 'nid6') if nid: nids.append(nid) nid = integer_or_blank(card, j+6, 'nid7') if nid: nids.append(nid) else: raise NotImplementedError(nid2) return ROTORG(sid, nids, comment=comment)
[docs] def cross_reference(self, model: BDF) -> None: pass
[docs] def uncross_reference(self) -> None: """Removes cross-reference links""" pass
[docs] def raw_fields(self): list_fields = ['ROTORG', self.sid] + self.nids return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: # double precision? card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] class TF(BaseCard): """ Defines a dynamic transfer function of the form: (B0 + B1 p + B2 *p2)*ud sum(A0_i + A1_i*p + A2_i*p2)*ui = 0 +----+-----+-----+------+------+------+--------+----+----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +====+=====+=====+======+======+======+========+====+====+ | TF | SID | GD | CD | B0 | B1 | B2 | | | +----+-----+-----+------+------+------+--------+----+----+ | | G_1 | C_1 | A0_1 | A1_1 | A2_1 | etc. | | | +----+-----+-----+------+------+------+--------+----+----+ """ type = 'TF'
[docs] @classmethod def _init_from_empty(cls): sid = 1 nid0 = 2 c = '3' b0 = 1. b1 = 2. b2 = 3. nids = [2, 3] components = ['4', '5'] a = [] #f2 = 2. return TF(sid, nid0, c, b0, b1, b2, nids, components, a, comment='')
def __init__(self, sid, nid0, c, b0, b1, b2, nids, components, a, comment=''): BaseCard.__init__(self) if comment: self.comment = comment self.sid = sid self.nid0 = nid0 self.c = c self.b0 = b0 self.b1 = b1 self.b2 = b2 self.nids = nids self.components = components self.a = a
[docs] def validate(self): pass
#assert len(self.grids1) > 0, 'ngrids1=%s\n%s' % (len(self.grids1), str(self))
[docs] def cross_reference(self, model: BDF) -> None: pass
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a TF card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') nid0 = integer(card, 2, 'nid0') # component 0 means an SPOINT/EPOINT c = components_or_blank(card, 3, 'components_0', '0') b0 = double_or_blank(card, 4, 'b0', 0.) b1 = double_or_blank(card, 5, 'b1', 0.) b2 = double_or_blank(card, 6, 'b2', 0.) nfields = len(card) - 9 nrows = nfields // 8 if nfields % 8 > 0: nrows += 1 nids = [] components = [] a = [] for irow in range(nrows): j = irow * 8 + 9 #ifield = irow + 1 nid = integer(card, j, 'grid_%i' % (irow + 1)) component = components_or_blank(card, j + 1, 'components_%i' % (irow + 1), default='0') a0 = double_or_blank(card, j + 2, 'a0_%i' % (irow + 1), default=0.) a1 = double_or_blank(card, j + 3, 'a1_%i' % (irow + 1), default=0.) a2 = double_or_blank(card, j + 4, 'a2_%i' % (irow + 1), default=0.) nids.append(nid) components.append(component) a.append([a0, a1, a2]) return TF(sid, nid0, c, b0, b1, b2, nids, components, a, comment=comment)
[docs] def raw_fields(self): list_fields = ['TF', self.sid, self.nid0, self.c, self.b0, self.b1, self.b2, None, None] for grid, c, (a0, a1, a2) in zip(self.nids, self.components, self.a): list_fields += [grid, c, a0, a1, a2, None, None, None] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: # double precision? card = self.repr_fields() if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
[docs] class TSTEP(BaseCard): """ Transient Time Step Defines time step intervals at which a solution will be generated and output in transient analysis. +-------+------+------+------+------+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+======+======+======+======+=====+=====+=====+=====+ | TSTEP | SID | N1 | DT1 | NO1 | | | | | +-------+------+------+------+------+-----+-----+-----+-----+ | | | N2 | DT2 | NO2 | | | | | +-------+------+------+------+------+-----+-----+-----+-----+ | | | etc. | | | | | | | +-------+------+------+------+------+-----+-----+-----+-----+ +-------+------+------+------+------+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+======+======+======+======+=====+=====+=====+=====+ | TSTEP | 101 | 9000 | .001 | 9000 | | | | | +-------+------+------+------+------+-----+-----+-----+-----+ | | | 1000 | .001 | 1 | | | | | +-------+------+------+------+------+-----+-----+-----+-----+ """ type = 'TSTEP'
[docs] @classmethod def _init_from_empty(cls): sid = 1 N = 4 DT = 1. NO = None return TSTEP(sid, N, DT, NO, comment='')
def __init__(self, sid, N, DT, NO, comment=''): """ Creates a TSTEP card Parameters ---------- sid : int the time step id N : list[int/None] List of number of time steps for each step section. DT : list[float/None] List of time steps for each step section. NO : list[int/None] List of step frequency for each step section. Every N steps, results will be printed. comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment if isinstance(N, integer_types): N = [N] if isinstance(DT, float): DT = [DT] if isinstance(NO, integer_types): NO = [NO] self.sid = sid #: Number of time steps of value DTi. (Integer > 1) self.N = N #: Time increment (float) self.DT = DT #: Skip factor for output. Every NOi-th step will be saved for output (default=1) self.NO = NO
[docs] def validate(self): assert len(self.N) == len(self.DT), 'N=%s DT=%s' % (self.N, self.DT) assert len(self.N) == len(self.NO), 'N=%s NO=%s' % (self.N, self.NO)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a TSTEP card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') N = [] DT = [] NO = [] nfields = len(card) nrows = (nfields - 1) // 8 if (nfields - 1) % 8 != 0: nrows += 1 for i in range(nrows): n = 8 * i + 1 #if i == 0: ni = integer(card, n + 1, 'Ntimes' + str(i)) dt = double(card, n + 2, 'dt' + str(i)) #else: #ni = integer_or_blank(card, n + 1, 'N' + str(i), 1) #dt = double_or_blank(card, n + 2, 'dt' + str(i), 0.) no = integer_or_blank(card, n + 3, 'NO' + str(i), 1) N.append(ni) DT.append(dt) NO.append(no) return TSTEP(sid, N, DT, NO, comment=comment)
[docs] def raw_fields(self): list_fields = ['TSTEP', self.sid] for (N, dt, no) in zip(self.N, self.DT, self.NO): list_fields += [N, dt, no, None, None, None, None, None] 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 TSTEP1(BaseCard): """ Transient Time Step Defines time step intervals at which a solution will be generated and output in transient analysis. +--------+------+-------+-------+-------+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +========+======+=======+=======+=======+=====+=====+=====+=====+ | TSTEP1 | SID | TEND1 | NINC1 | NOUT1 | | | | | +--------+------+-------+-------+-------+-----+-----+-----+-----+ | | | TEND2 | NINC2 | NOUT2 | | | | | +--------+------+-------+-------+-------+-----+-----+-----+-----+ | | | etc. | | | | | | | +--------+------+-------+-------+-------+-----+-----+-----+-----+ +--------+------+-------+-------+-------+-----+-----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +========+======+=======+=======+=======+=====+=====+=====+=====+ | TSTEP1 | 1 | 10.0 | 5 | 2 | | | | | +--------+------+-------+-------+-------+-----+-----+-----+-----+ | | | 50.0 | 4 | 3 | | | | | +--------+------+-------+-------+-------+-----+-----+-----+-----+ | | | 100 | 2 | ALL | | | | | +--------+------+-------+-------+-------+-----+-----+-----+-----+ """ type = 'TSTEP1'
[docs] @classmethod def _init_from_empty(cls): sid = 1 ninc = 4 tend = 1. nout = None return TSTEP1(sid, tend, ninc, nout, comment='')
def __init__(self, sid, tend, ninc, nout, comment=''): """ Creates a TSTEP1 card Parameters ---------- sid : int the time step id tend : list[float/None] ??? ninc : list[int/None] ??? nout : list[int/str/None] ??? comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment self.sid = sid self.tend = tend self.ninc = ninc self.nout = nout
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a TSTEP1 card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') tend = [] ninc = [] nout = [] nfields = len(card) nrows = (nfields - 1) // 8 if (nfields - 1) % 8 != 0: nrows += 1 for i in range(nrows): n = 8 * i + 1 tendi = double_or_blank(card, n + 1, 'TEND' + str(i), 1.) ninci = integer_or_blank(card, n + 2, 'NINC' + str(i), 1) nouti = integer_string_or_blank(card, n + 3, 'NOUT' + str(i), 'END') tend.append(tendi) ninc.append(ninci) nout.append(nouti) if not isinstance(nouti, integer_types): assert nouti in ['YES', 'END', 'ALL', 'CPLD'], nouti return TSTEP1(sid, tend, ninc, nout, comment=comment)
[docs] def raw_fields(self): list_fields = ['TSTEP1', self.sid] for (tend, ninc, nout) in zip(self.tend, self.ninc, self.nout): list_fields += [tend, ninc, nout, None, None, None, None, None] 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 TSTEPNL(BaseCard): """ Defines parametric controls and data for nonlinear transient structural or heat transfer analysis. TSTEPNL is intended for SOLs 129, 159, and 600. Parameters for Nonlinear Transient Analysis. MSC 2005.2 +---------+---------+--------+-------+--------+--------+-------+---------+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=========+=========+========+=======+========+========+=======+=========+======+ | TSTEPNL | ID | NDT | DT | NO | METHOD | KSTEP | MAXITER | CONV | +---------+---------+--------+-------+--------+--------+-------+---------+------+ | | ESPU | EPSP | EPSW | MAXDIV | MAXQN | MAXLS | FSTRESS | | +---------+---------+--------+-------+--------+--------+-------+---------+------+ | | MAXBIS | ADJUST | MSTEP | RB | MAXR | UTOL | RTOLB | | +---------+---------+--------+-------+--------+--------+-------+---------+------+ NX 2019.2 +---------+---------+--------+-------+--------+--------+-------+---------+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=========+=========+========+=======+========+========+=======+=========+======+ | TSTEPNL | ID | NDT | DT | NO | METHOD | KSTEP | MAXITER | CONV | +---------+---------+--------+-------+--------+--------+-------+---------+------+ | | ESPU | EPSP | EPSW | MAXDIV | MAXQN | MAXLS | FSTRESS | | +---------+---------+--------+-------+--------+--------+-------+---------+------+ | | MAXBIS | ADJUST | MSTEP | RB | MAXR | UTOL | RTOLB | | +---------+---------+--------+-------+--------+--------+-------+---------+------+ | | KUPDATE | | | | | | | | +---------+---------+--------+-------+--------+--------+-------+---------+------+ method = None for NX, but apparently TSTEP as well, which is not in the QRG """ type = 'TSTEPNL' allowed_methods = ['AUTO', 'ITER', 'ADAPT', 'SEMI', 'FNT', 'PFNT', # MSC 'TSTEP'] # NX
[docs] @classmethod def _init_from_empty(cls): sid = 1 ndt = 10 dt = 0.1 no = 2 return TSTEPNL(sid, ndt, dt, no, method='ADAPT', kstep=None, max_iter=10, conv='PW', eps_u=1.e-2, eps_p=1.e-3, eps_w=1.e-6, max_div=2, max_qn=10, max_ls=2, fstress=0.2, max_bisect=5, adjust=5, mstep=None, rb=0.6, max_r=32., utol=0.1, rtol_b=20., min_iter=None, comment='')
def __init__(self, sid, ndt, dt, no, method='ADAPT', kstep=None, max_iter=10, conv='PW', eps_u=1.e-2, eps_p=1.e-3, eps_w=1.e-6, max_div=2, max_qn=10, max_ls=2, fstress=0.2, max_bisect=5, adjust=5, mstep=None, rb=0.6, max_r=32., utol=0.1, rtol_b=20., min_iter=None, comment=''): """ Creates a TSTEPNL card Parameters ---------- sid : int the time step id ndt : int ??? dt : float ??? no : int ??? method : str ??? MSC = {AUTO, ITER, ADAPT, SEMI, FNT, PFNT} NX = {None, TSTEP} kstep : ???; default=None ??? max_iter : int; default=10 ??? conv : str; default='PW' ??? PW, W, U eps_u : float; default=1.e-2 ??? eps_p : float; default=1.e-3 ??? eps_w : float; default=1.e-6 ??? max_div : int; default=2 ??? max_qn : int; default=10 ??? max_ls : int; default=2 ??? fstress : float; default=0.2 ??? max_bisect : int; default=5 ??? adjust : int; default=5 ??? mstep : int; default=None ??? rb : float; default=0.6 ??? max_r = float; default=32. ??? utol = float; default=0.1 ??? rtol_b = float; default=20. ??? min_iter : int; default=None not listed in all QRGs comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment # line 1 self.sid = sid self.ndt = ndt self.dt = dt self.no = no self.method = method self.kstep = kstep self.max_iter = max_iter self.conv = conv self.eps_u = eps_u self.eps_p = eps_p self.eps_w = eps_w self.max_div = max_div self.max_qn = max_qn self.max_ls = max_ls self.fstress = fstress # line 3 self.max_bisect = max_bisect self.adjust = adjust self.mstep = mstep self.rb = rb self.max_r = max_r self.utol = utol self.rtol_b = rtol_b self.min_iter = min_iter #assert self.ndt >= 3, self assert self.dt > 0. assert isinstance(self.method, str), f'method={self.method}; type={type(self.method)}'
[docs] def validate(self): if self.method not in self.allowed_methods: msg = 'method=%r allowed_methods=[%s]' % ( self.method, ', '.join(self.allowed_methods)) raise ValueError(msg) assert self.sid > 0, self.get_stats() assert self.ndt > 0, self.get_stats() # not correct...should be ndt > 0, but has test issues... assert self.dt > 0.0, self.get_stats() assert self.no >= 1, self.get_stats() assert self.method in self.allowed_methods, self.get_stats() if self.method in {'FNT', 'PFNT'}: assert self.kstep in {-1, None, 1}, self.get_stats() else: assert self.kstep is None or self.kstep >= 1, self.get_stats() assert self.min_iter is None or self.min_iter >= 0, self.get_stats() assert self.max_iter != 0, self.get_stats() assert self.max_div != 0, self.get_stats() assert self.max_qn >= 0, self.get_stats() assert self.max_ls >= 0, self.get_stats() # -9 <= Integer <= 9 assert -9 <= self.max_bisect <= 9, self.get_stats() # default in MSC 2020=0 assert self.adjust >= 0, self.get_stats() # is this >0 or >= 0? #def __init__(self, sid, ndt, dt, no, method='ADAPT', kstep=None, #max_iter=10, conv='PW', eps_u=1.e-2, eps_p=1.e-3, #eps_w=1.e-6, max_div=2, max_qn=10, max_ls=2, #fstress=0.2, max_bisect=5, adjust=5, mstep=None, #rb=0.6, max_r=32., utol=0.1, rtol_b=20., #min_iter=None, comment=''): assert self.eps_u > 0.0 or self.eps_u < 0, self.get_stats() assert self.eps_p > 0.0, self.get_stats() assert self.eps_w > 0.0 or self.eps_u < 0, self.get_stats() assert self.fstress > 0.0, self.get_stats() assert self.rb > 0.0, self.get_stats() assert self.max_r > 0.0, self.get_stats() assert self.utol > 0.0, self.get_stats() assert self.rtol_b > 0.0, self.get_stats()
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a TSTEPNL card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') ndt = integer(card, 2, 'ndt') dt = double(card, 3, 'dt') no = integer_or_blank(card, 4, 'no', 1) #: .. note:: not listed in all QRGs method = string_or_blank(card, 5, 'method', default='ADAPT') if method == 'ADAPT': kstep = integer_or_blank(card, 6, 'kStep', default=2) elif method == 'ITER': kstep = integer_or_blank(card, 6, 'kStep', default=10) elif method in {'AUTO', 'TSTEP', 'SEMI'}: kstep = None #kstep = blank(card, 6, 'kStep') #: .. todo:: not blank elif method in {'PFNT', 'FNT'}: #METHOD = 'PFNT': This is the Pure Full Newton iteration method. #The PFNT method is the same as the FNT method except that the defaults #for PFNT method are EPSU=-0.01, EPSW=-0.01, and MAXLS=0. # # For FNT and PFNT methods, whether the stiffness matrix will # be updated between the convergence of a load increment and # the start of the next load increment depends on the value of # KSTEP. In this case, KSTEP = -1, BLANK, or 1. A user fatal # error will be issued if other value is input. # KSTEP=1 stiffness matrix will not be updated. # KSTEP=BLANK: the program will decide whether to update depending element type. # KSTEP=-1: stiffness matrix will be forced to be updated kstep = integer_or_blank(card, 6, 'kstep') assert kstep in {1, -1, None}, f'TSTEPNL method={method!r} and kstep={kstep}, which should be -1, 1 or blank' else: msg = 'invalid TSTEPNL Method. method=%r; allowed_methods=[%s]' % ( method, ', '.join(cls.allowed_methods)) raise RuntimeError(msg) max_iter = integer_or_blank(card, 7, 'maxIter', 10) conv = string_or_blank(card, 8, 'conv', 'PW') # line 2 eps_u = double_or_blank(card, 9, 'epsU', 1.E-2) eps_p = double_or_blank(card, 10, 'epsP', 1.E-3) eps_w = double_or_blank(card, 11, 'epsW', 1.E-6) max_div = integer_or_blank(card, 12, 'maxDiv', 2) max_qn = integer_or_blank(card, 13, 'maxQn', 10) max_ls = integer_or_blank(card, 14, 'MaxLs', 2) fstress = double_or_blank(card, 15, 'fStress', 0.2) # line 3 max_bisect = integer_or_blank(card, 17, 'maxBisect', 5) adjust = integer_or_blank(card, 18, 'adjust', 5) mstep = integer_or_blank(card, 19, 'mStep') rb = double_or_blank(card, 20, 'rb', 0.6) max_r = double_or_blank(card, 21, 'maxR', 32.) utol = double_or_blank(card, 22, 'uTol', 0.1) rtol_b = double_or_blank(card, 23, 'rTolB', 20.) # not listed in all QRGs min_iter = integer_or_blank(card, 24, 'minIter') assert len(card) <= 25, f'len(TSTEPNL card) = {len(card):d}\ncard={card}' return TSTEPNL( sid, ndt, dt, no, method, kstep, max_iter, conv, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, max_bisect, adjust, mstep, rb, max_r, utol, rtol_b, min_iter, comment=comment)
@classmethod def add_op2_data(cls, data, comment=''): """ Adds a TSTEPNL card from the OP2 Parameters ---------- data : list[varies] a list of fields defined in OP2 format comment : str; default='' a comment for the card """ if len(data) == 22: (sid, ndt, dt, no, method_int, kstep, max_iter, conv_int, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, max_bisect, adjust, mstep, rb, max_r, utol, rtol_b) = data else: assert len(data) == 27, len(data) (sid, ndt, dt, no, method_int, kstep, max_iter, conv_int, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, max_bisect, adjust, mstep, rb, max_r, utol, rtol_b, kdamp, kupdate, kustep, tintopt, gamma) = data if method_int == 1: method = 'AUTO' elif method_int == 2: method = 'TSTEP' elif method_int == 3: method = 'ADAPT' else: raise NotImplementedError('tstepnl=%s method_int=%r data=%s' % (sid, method_int, data)) if conv_int == 1: conv = 'W' elif conv_int == 2: # guess based on format conv = 'P' elif conv_int == 3: conv = 'PW' elif conv_int == 4: conv = 'U' elif conv_int == 7: conv = 'UPW' #elif conv_int == 3: #conv = 'ADAPT' else: raise NotImplementedError('tstepnl=%s conv_int=%r data=%s' % (sid, conv_int, data)) min_iter = None # not listed in DMAP 2005 return TSTEPNL( sid, ndt, dt, no, method, kstep, max_iter, conv, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, max_bisect, adjust, mstep, rb, max_r, utol, rtol_b, min_iter, comment=comment) #self.sid = sid #self.ndt = ndt #self.dt = dt #self.no = no #self.method = method #self.kStep = kStep #self.maxIter = maxIter #self.conv = conv ## line 2 #self.epsU = epsU #self.epsP = epsP #self.epsW = epsW #self.maxDiv = maxDiv #self.maxQn = maxQn #self.MaxLs = maxLs #self.fStress = fStress ## line 3 #self.maxBisect = maxBisect #self.adjust = adjust #self.mStep = mStep #self.rb = rb #self.maxR = maxR #self.uTol = uTol #self.rTolB = rTolB
[docs] def raw_fields(self): list_fields = ['TSTEPNL', self.sid, self.ndt, self.dt, self.no, self.method, self.kstep, self.max_iter, self.conv, self.eps_u, self.eps_p, self.eps_w, self.max_div, self.max_qn, self.max_ls, self.fstress, None, self.max_bisect, self.adjust, self.mstep, self.rb, self.max_r, self.utol, self.rtol_b, self.min_iter] return list_fields
[docs] def repr_fields(self): #no = set_blank_if_default(self.no,1) no = self.no method = set_blank_if_default(self.method, 'ADAPT') kstep = self.kstep #if self.method == 'ADAPT': #kStep = set_blank_if_default(self.kStep, 2) #elif self.method == 'ITER': #kStep = set_blank_if_default(self.kStep, 10) #else: #msg = 'invalid TSTEPNL Method. method=|%s|' %(self.method) #raise RuntimeError(msg) #maxIter = set_blank_if_default(self.maxIter, 10) conv = set_blank_if_default(self.conv, 'PW') eps_u = set_blank_if_default(self.eps_u, 1e-2) eps_p = set_blank_if_default(self.eps_p, 1e-3) eps_w = set_blank_if_default(self.eps_w, 1e-6) max_div = set_blank_if_default(self.max_div, 2) max_qn = set_blank_if_default(self.max_qn, 10) max_ls = set_blank_if_default(self.max_ls, 2) fstress = set_blank_if_default(self.fstress, 0.2) max_bisect = set_blank_if_default(self.max_bisect, 5) adjust = set_blank_if_default(self.adjust, 5) rb = set_blank_if_default(self.rb, 0.6) max_r = set_blank_if_default(self.max_r, 32.) utol = set_blank_if_default(self.utol, 0.1) rtol_b = set_blank_if_default(self.rtol_b, 20.) list_fields = ['TSTEPNL', self.sid, self.ndt, self.dt, no, method, kstep, self.max_iter, conv, eps_u, eps_p, eps_w, max_div, max_qn, max_ls, fstress, None, max_bisect, adjust, self.mstep, rb, max_r, utol, rtol_b, self.min_iter] 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 TIC(BaseCard): """ Transient Initial Condition Defines values for the initial conditions of variables used in structural transient analysis. Both displacement and velocity values may be specified at independent degrees-of-freedom. This entry may not be used for heat transfer analysis. """ type = 'TIC' _properties = ['node_ids']
[docs] @classmethod def _init_from_empty(cls): sid = 1 nodes = [1, 2] components = [1, 2] return TIC(sid, nodes, components, u0=0., v0=0., comment='')
def __init__(self, sid, nodes, components, u0=0., v0=0., comment=''): """ Creates a TIC card Parameters ---------- sid : int Case Control IC id nodes : int / list[int] the nodes to which apply the initial conditions components : int / list[int] the DOFs to which apply the initial conditions u0 : float / list[float] Initial displacement. v0 : float / list[float] Initial velocity. comment : str; default='' a comment for the card """ BaseCard.__init__(self) if comment: self.comment = comment if isinstance(nodes, integer_types): nodes = [nodes] if isinstance(components, integer_types): components = [components] if isinstance(u0, float): u0 = [u0] if isinstance(v0, float): v0 = [v0] self.sid = sid self.nodes = nodes self.components = components self.u0 = u0 self.v0 = v0 self.nodes_ref = None
[docs] def validate(self): for nid in self.nodes: assert nid > 0, self.nodes
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a TIC card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') nid = integer(card, 2, 'G') comp = modal_components_or_blank(card, 3, 'C', default=0) u0 = double_or_blank(card, 4, 'U0', 0.) v0 = double_or_blank(card, 5, 'V0', 0.) return TIC(sid, nid, comp, u0=u0, v0=v0, comment=comment)
@classmethod def add_op2_data(cls, data, comment=''): """ Adds a TIC card from the OP2 Parameters ---------- data : list[varies] a list of fields defined in OP2 format comment : str; default='' a comment for the card """ sid = data[0] nid = data[1] comp = data[2] u0 = data[3] v0 = data[4] return TIC(sid, nid, comp, u0, v0, comment=comment) @property def node_ids(self): #return _node_ids(self, self.nodes, ) if self.nodes_ref is None: return self.nodes nodes = [] for node in self.nodes_ref: nodes.append(node.nid) return nodes
[docs] def add(self, tic): assert self.sid == tic.sid, 'sid=%s tic.sid=%s' % (self.sid, tic.sid) if tic.comment: if hasattr('_comment'): self._comment += tic.comment else: self._comment = tic.comment self.nodes += tic.nodes self.components += tic.components self.u0 += tic.u0 self.v0 += tic.v0
[docs] def cross_reference(self, model: BDF) -> None: self.nodes_ref = model.Nodes(self.nodes)
[docs] def safe_cross_reference(self, model: BDF, xref_errors): return self.cross_reference(model)
[docs] def uncross_reference(self) -> None: """Removes cross-reference links""" self.nodes = self.node_ids self.nodes_ref = None
[docs] def raw_fields(self): list_fields = [] for nid, comp, u0, v0 in zip(self.node_ids, self.components, self.u0, self.v0): list_fields += ['TIC', self.sid, nid, comp, u0, v0] return list_fields
#def repr_fields(self): #return self.raw_fields()
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: msg = self.comment node_ids = self.node_ids if size == 8: for nid, comp, u0, v0 in zip(node_ids, self.components, self.u0, self.v0): msg += print_card_8(['TIC', self.sid, nid, comp, u0, v0]) else: for nid, comp, u0, v0 in zip(node_ids, self.components, self.u0, self.v0): msg += print_card_16(['TIC', self.sid, nid, comp, u0, v0]) return msg