Source code for pyNastran.bdf.cards.contact

"""
Defines the following contact cards:

 - BCONP
 - BLSEG
 - BCRPARA
 - BCTPARA
 - BCTADD
 - BCTSET
 - BSURF
 - BSURFS
 - BFRIC

"""
from __future__ import annotations
from typing import TYPE_CHECKING

from pyNastran.bdf.cards.base_card import BaseCard, expand_thru_by, _node_ids
from pyNastran.bdf.bdf_interface.assign_type import (
    integer, integer_or_blank, integer_string_or_blank, double_or_blank,
    integer_double_or_blank, string, string_or_blank, 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


[docs]class BFRIC(BaseCard): """ Slideline Contact Friction Defines frictional properties between two bodies in contact. +-------+------+-------+-------+-------+ | 1 | 2 | 3 | 4 | 5 | +=======+======+=======+=======+=======+ | BFRIC | FID | FSTIF | | MU1 | +-------+------+-------+-------+-------+ """ type = 'BFRIC'
[docs] @classmethod def _init_from_empty(cls): friction_id = 1 mu1 = 0.2 return BFRIC(friction_id, mu1)
def __init__(self, friction_id: int, mu1: float, fstiff=None, comment=''): """ Creates a BFRIC card, which defines a frictional contact. Parameters ---------- friction_id : int Friction identification number. mu1 : float Coefficient of static friction. fstiff : float; default=None Frictional stiffness in stick. See Remarks 2 and 3 Default=automatically selected by the program. """ BaseCard.__init__(self) if comment: self.comment = comment self.friction_id = friction_id self.fstiff = fstiff self.mu1 = mu1
[docs] @classmethod def add_card(cls, card, comment=''): friction_id = integer(card, 1, 'friction_id') fstiff = double_or_blank(card, 2, 'fstiff') # mu1 = double(card, 4, 'mu1') return BFRIC(friction_id, mu1, fstiff=fstiff, comment='')
[docs] def raw_fields(self): list_fields = [ 'BFRIC', self.friction_id, self.fstiff, None, self.mu1] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() return self.comment + print_card_8(card)
[docs]class BLSEG(BaseCard): """ 3D Contact Region Definition by Shell Elements (SOLs 101, 601 and 701) Defines a 3D contact region by shell element IDs. +=======+====+====+======+====+====+=====+====+====+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +-------+----+----+------+----+----+-----+----+----+ | BLSEG | ID | G1 | G2 | G3 | G4 | G5 | G6 | G7 | +-------+----+----+------+----+----+-----+----+----+ | BLSEG | ID | G1 | THRU | G2 | BY | INC | | | +-------+----+----+------+----+----+-----+----+----+ """ type = 'BLSEG'
[docs] @classmethod def _init_from_empty(cls): line_id = 1 nodes = [1] return BLSEG(line_id, nodes, comment='')
def __init__(self, line_id, nodes, comment=''): if comment: self.comment = comment self.line_id = line_id self.nodes = expand_thru_by(nodes) self.nodes_ref = None
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a BLSEG card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ line_id = integer(card, 1, 'line_id') #: Number (float) nfields = card.nfields i = 2 nodes = [] while i < nfields: d = integer_string_or_blank(card, i, 'field_%s' % i) if d is not None: nodes.append(d) i += 1 return BLSEG(line_id, nodes, comment=comment)
[docs] def cross_reference(self, model: BDF) -> None: msg = f', which is required by BLSEG line_id={self.line_id}' self.nodes_ref = model.Nodes(self.nodes, msg=msg)
[docs] def uncross_reference(self) -> None: #msg = f', which is required by BLSEG line_id={self.line_id}' self.nodes = self.node_ids self.nodes_ref = None
@property def node_ids(self): """returns nodeIDs for repr functions""" return _node_ids(self, nodes=self.nodes_ref, allow_empty_nodes=False, msg='')
[docs] def raw_fields(self): list_fields = ['BLSEG', self.line_id] + self.node_ids return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() return self.comment + print_card_8(card)
[docs]class BCONP(BaseCard): """ 3D Contact Region Definition by Shell Elements (SOLs 101, 601 and 701) Defines a 3D contact region by shell element IDs. +-------+----+-------+--------+-----+------+--------+-------+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+====+=======+========+=====+======+========+=======+=====+ | BCONP | ID | SLAVE | MASTER | | SFAC | FRICID | PTYPE | CID | +-------+----+-------+--------+-----+------+--------+-------+-----+ | BCONP | 95 | 10 | 15 | | 1.0 | 33 | 1 | | +-------+----+-------+--------+-----+------+--------+-------+-----+ """ type = 'BCONP'
[docs] @classmethod def _init_from_empty(cls): contact_id = 1 slave = 2 master = 3 sfac = 4 friction_id = 5 ptype = 'cat' cid = 0 return BCONP(contact_id, slave, master, sfac, friction_id, ptype, cid, comment='')
def __init__(self, contact_id, slave, master, sfac, friction_id, ptype, cid, comment=''): if comment: self.comment = comment self.contact_id = contact_id self.slave = slave self.master = master self.sfac = sfac self.friction_id = friction_id self.ptype = ptype self.cid = cid self.cid_ref = None self.friction_id_ref = None self.slave_ref = None self.master_ref = None
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a BCONP card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ contact_id = integer(card, 1, 'contact_id') slave = integer(card, 2, 'slave') master = integer(card, 3, 'master') sfac = double_or_blank(card, 5, 'sfac', 1.0) friction_id = integer_or_blank(card, 6, 'fric_id') ptype = integer_or_blank(card, 7, 'ptype', 1) cid = integer_or_blank(card, 8, 'cid', 0) return BCONP(contact_id, slave, master, sfac, friction_id, ptype, cid, comment=comment)
[docs] def cross_reference(self, model: BDF) -> None: msg = f', which is required by BCONP line_id={self.contact_id}' #self.nodes_ref = model.Nodes(self.nodes, msg=msg) self.cid_ref = model.Coord(self.cid, msg=msg) if self.friction_id is not None: self.friction_id_ref = model.bfric[self.friction_id] self.slave_ref = model.blseg[self.slave] self.master_ref = model.blseg[self.master]
[docs] def uncross_reference(self) -> None: self.cid = self.Cid() self.friction_id = self.FrictionId() self.cid_ref = None self.friction_id_ref = None self.slave_ref = None self.master_ref = None
[docs] def Cid(self) -> int: if self.cid_ref is not None: return self.cid_ref.cid return self.cid
[docs] def FrictionId(self) -> int: if self.friction_id_ref is not None: return self.friction_id_ref.friction_id return self.friction_id
[docs] def Slave(self) -> int: if self.slave_ref is not None: return self.slave_ref.line_id return self.slave
[docs] def Master(self) -> int: if self.master_ref is not None: return self.master_ref.line_id return self.master
[docs] def raw_fields(self): list_fields = [ 'BCONP', self.contact_id, self.Slave(), self.Master(), None, self.sfac, self.FrictionId(), self.ptype, self.Cid()] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() return self.comment + print_card_8(card)
[docs]class BSURF(BaseCard): """ 3D Contact Region Definition by Shell Elements (SOLs 101, 601 and 701) Defines a 3D contact region by shell element IDs. +-------+------+------+-------+-------+--------+------+------+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+======+======+=======+=======+========+======+======+======+ | BSURF | ID | EID1 | EID2 | EID3 | EID4 | EID5 | EID6 | EID7 | +-------+------+------+-------+-------+--------+------+------+------+ | | EID8 | EID9 | EID10 | etc. | | | | | +-------+------+------+-------+-------+--------+------+------+------+ | BSURF | ID | EID1 | THRU | EID2 | BY | INC | | | +-------+------+------+-------+-------+--------+------+------+------+ | | EID8 | EID9 | EID10 | EID11 | etc. | | | | +-------+------+------+-------+-------+--------+------+------+------+ | | EID8 | THRU | EID9 | BY | INC | | | | +-------+------+------+-------+-------+--------+------+------+------+ | BSURF | 15 | 5 | THRU | 21 | BY | 4 | | | +-------+------+------+-------+-------+--------+------+------+------+ | | 27 | 30 | 32 | 33 | | | | | +-------+------+------+-------+-------+--------+------+------+------+ | | 35 | THRU | 44 | | | | | | +-------+------+------+-------+-------+--------+------+------+------+ | | 67 | 68 | 70 | 85 | 92 | | | | +-------+------+------+-------+-------+--------+------+------+------+ """ type = 'BSURF'
[docs] @classmethod def _init_from_empty(cls): sid = 1 eids = [1] return BSURF(sid, eids, comment='')
def __init__(self, sid, eids, comment=''): if comment: self.comment = comment #: Set identification number. (Unique Integer > 0) self.sid = sid #: Element identification numbers of shell elements. (Integer > 0) self.eids = eids
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a BSURF card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ sid = integer(card, 1, 'sid') #: Number (float) nfields = card.nfields i = 2 eid_data = [] while i < nfields: d = integer_string_or_blank(card, i, 'field_%s' % i) if d is not None: eid_data.append(d) i += 1 eids = expand_thru_by(eid_data) return BSURF(sid, eids, comment=comment)
[docs] def raw_fields(self): fields = ['BSURF', self.sid] return fields + list(self.eids)
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() return self.comment + print_card_8(card)
[docs]class BSURFS(BaseCard): """ Defines a 3D contact region by the faces of the CHEXA, CPENTA or CTETRA elements. Notes ----- 1. The continuation field is optional. 2. BSURFS is a collection of one or more element faces on solid elements. BSURFS defines a contact region which may act as a contact source (contactor) or target. 3. The ID must be unique with respect to all other BSURFS, BSURF, and BCPROP entries. """ type = 'BSURFS'
[docs] @classmethod def _init_from_empty(cls): bsurfs_id = 1 eids = [1] g1s = [1] g2s = [1] g3s = [1] return BSURFS(bsurfs_id, eids, g1s, g2s, g3s, comment='')
def __init__(self, bsurfs_id, eids, g1s, g2s, g3s, comment=''): if comment: self.comment = comment #: Identification number of a contact region. See Remarks 2 and 3. #: (Integer > 0) self.id = bsurfs_id #: Element identification numbers of solid elements. (Integer > 0) self.eids = eids #: Identification numbers of 3 corner grid points on the face (triangular #: or quadrilateral) of the solid element. (Integer > 0) self.g1s = g1s self.g2s = g2s self.g3s = g3s
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a BSURFS card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ bsurfs_id = integer(card, 1, 'id') eids = [] g1s = [] g2s = [] g3s = [] n = card.nfields - 5 i = 0 j = 1 while i < n: eid = integer(card, 5 + i, 'eid%s' % j) g1 = integer(card, 5 + i + 1, 'g3_%s' % j) g2 = integer(card, 5 + i + 2, 'g2_%s' % j) g3 = integer(card, 5 + i + 3, 'g1_%s' % j) j += 1 i += 4 eids.append(eid) g1s.append(g1) g2s.append(g2) g3s.append(g3) return BSURFS(bsurfs_id, eids, g1s, g2s, g3s, comment=comment)
[docs] def raw_fields(self): fields = ['BSURFS', self.id, None, None, None] for eid, g1, g2, g3 in zip(self.eids, self.g1s, self.g2s, self.g3s): fields += [eid, g1, g2, g3] return fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() return self.comment + print_card_8(card)
[docs]class BCTSET(BaseCard): """ 3D Contact Set Definition (SOLs 101, 601 and 701 only) Defines contact pairs of a 3D contact set. +--------+-------+------+-------+-------+-------+-------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +========+=======+======+=======+=======+=======+=======+ | BCTSET | CSID | SID1 | TID1 | FRIC1 | MIND1 | MAXD1 | +--------+-------+------+-------+-------+-------+-------+ | | | SID2 | TID2 | FRIC2 | MIND2 | MAXD2 | +--------+-------+------+-------+-------+-------+-------+ | | etc. | | | | | | +--------+-------+------+-------+-------+-------+-------+ """ type = 'BCTSET'
[docs] @classmethod def _init_from_empty(cls): csid = 1 sids = [1] tids = [1] frictions = [0.01] min_distances = [0.1] max_distances = [1.] return BCTSET(csid, sids, tids, frictions, min_distances, max_distances, comment='', sol=101)
def __init__(self, csid, sids, tids, frictions, min_distances, max_distances, comment='', sol=101): if comment: self.comment = comment #: CSID Contact set identification number. (Integer > 0) self.csid = csid #: SIDi Source region (contactor) identification number for contact pair i. #: (Integer > 0) self.sids = sids #: TIDi Target region identification number for contact pair i. (Integer > 0) self.tids = tids #: FRICi Static coefficient of friction for contact pair i. (Real; Default=0.0) self.frictions = frictions #: MINDi Minimum search distance for contact. (Real) (Sol 101 only) self.min_distances = min_distances #: MAXDi Maximum search distance for contact. (Real) (Sol 101 only) self.max_distances = max_distances
[docs] @classmethod def add_card(cls, card, comment='', sol=101): csid = integer(card, 1, 'csid') sids = [] tids = [] frictions = [] min_distances = [] max_distances = [] nfields = card.nfields i = 2 j = 1 while i < nfields: sids.append(integer(card, i, 'sid%s' % j)) tids.append(integer(card, i + 1, 'tid%s' % j)) frictions.append(double_or_blank(card, i + 2, 'fric%s' % j, 0.0)) # we ignore what the NX QRG says about the min/max distance for SOL 401 # if you don't, Nastran will crash #if sol == 101: min_distances.append(double_or_blank(card, i + 3, 'mind%s' % j, 0.0)) max_distances.append(double_or_blank(card, i + 4, 'maxd%s' % j, 0.0)) #else: #min_distances.append(None) #max_distances.append(None) i += 8 j += 1 return BCTSET(csid, sids, tids, frictions, min_distances, max_distances, comment=comment, sol=sol)
[docs] def raw_fields(self): fields = ['BCTSET', self.csid] for sid, tid, fric, mind, maxd in zip(self.sids, self.tids, self.frictions, self.min_distances, self.max_distances): fields += [sid, tid, fric, mind, maxd, None, None, None] return 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 BCRPARA(BaseCard): """ +---------+------+------+--------+------+-----+---+---+---+----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | +=========+======+======+========+======+=====+===+===+===+====+ | BCRPARA | CRID | SURF | OFFSET | TYPE | GP | | | | | +---------+------+------+--------+------+-----+---+---+---+----+ """ type = 'BCRPARA'
[docs] @classmethod def _init_from_empty(cls): crid = 1 return BCRPARA(crid, offset=None, surf='TOP', Type='FLEX', grid_point=0, comment='')
def __init__(self, crid, offset=None, surf='TOP', Type='FLEX', grid_point=0, comment=''): """ Creates a BCRPARA card Parameters ---------- crid : int CRID Contact region ID. offset : float; default=None Offset distance for the contact region (Real > 0.0). None : OFFSET value in BCTPARA entry surf : str; default='TOP' SURF Indicates the contact side. See Remark 1. {'TOP', 'BOT'; ) Type : str; default='FLEX' Indicates whether a contact region is a rigid surface if it is used as a target region. {'RIGID', 'FLEX'}. This is not supported for SOL 101. grid_point : int; default=0 Control grid point for a target contact region with TYPE=RIGID or when the rigid-target algorithm is used. The grid point may be used to control the motion of a rigid surface. (Integer > 0). This is not supported for SOL 101. comment : str; default='' a comment for the card """ if comment: self.comment = comment #: CRID Contact region ID. (Integer > 0) self.crid = crid #: SURF Indicates the contact side. See Remark 1. (Character = "TOP" or #: "BOT"; Default = "TOP") self.surf = surf #: Offset distance for the contact region. See Remark 2. (Real > 0.0, #: Default =OFFSET value in BCTPARA entry) self.offset = offset #: Indicates whether a contact region is a rigid surface if it is used as a #: target region. See Remarks 3 and 4. (Character = "RIGID" or "FLEX", #: Default = "FLEX"). This is not supported for SOL 101. self.Type = Type #: Control grid point for a target contact region with TYPE=RIGID or #: when the rigid-target algorithm is used. The grid point may be #: used to control the motion of a rigid surface. (Integer > 0) #: This is not supported for SOL 101. self.grid_point = grid_point
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a BCRPARA card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ crid = integer(card, 1, 'crid') surf = string_or_blank(card, 2, 'surf', 'TOP') offset = double_or_blank(card, 3, 'offset', None) Type = string_or_blank(card, 4, 'type', 'FLEX') grid_point = integer_or_blank(card, 5, 'grid_point', 0) return BCRPARA(crid, surf=surf, offset=offset, Type=Type, grid_point=grid_point, comment=comment)
[docs] def raw_fields(self): fields = ['BCRPARA', self.crid, self.surf, self.offset, self.Type, self.grid_point] return 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 BCTPARA(BaseCard): """ Defines parameters for a surface-to-surface contact region. +---------+--------+--------+--------+--------+--------+---------+--------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | +=========+========+========+========+========+========+=========+========+ | BCTPARA | CSID | Param1 | Value1 | Param2 | Value2 | Param3 | Value3 | +---------+--------+--------+--------+--------+--------+---------+--------+ | | Param4 | Value4 | Param5 | Value5 | etc. | | | +---------+--------+--------+--------+--------+--------+---------+--------+ | BCTPARA | 1 | TYPE | 0 | NSIDE | 2 | SEGNORM | -1 | +---------+--------+--------+--------+--------+--------+---------+--------+ | | CSTIFF | 1 | OFFSET | 0.015 | | | | +---------+--------+--------+--------+--------+--------+---------+--------+ """ type = 'BCTPARA'
[docs] @classmethod def _init_from_empty(cls): csid = 1 params = {'CSTIFF' : 1} return BCTPARA(csid, params, comment='')
[docs] def _finalize_hdf5(self, encoding): """hdf5 helper function""" keys, values = self.params self.params = {key : value for key, value in zip(keys, values)}
def __init__(self, csid, params, comment=''): """ Creates a BCTPARA card Parameters ---------- csid : int Contact set ID. Parameters defined in this command apply to contact set CSID defined by a BCTSET entry. (Integer > 0) params : dict[key] : value the optional parameters comment : str; default='' a comment for the card """ if comment: self.comment = comment #: Contact set ID. Parameters defined in this command apply to #: contact set CSID defined by a BCTSET entry. (Integer > 0) self.csid = csid self.params = params
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a BCTPARA card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ csid = integer(card, 1, 'csid') i = 2 j = 1 params = {} while i < card.nfields: param = string(card, i, f'param{j}') i += 1 if param == 'TYPE': value = integer_or_blank(card, i, 'value%s' % j, 0) assert value in [0, 1, 2], 'TYPE must be [0, 1, 2]; TYPE=%r' % value #elif param == 'TYPE': # NX #value = string_or_blank(card, i, 'value%s' % j, 'FLEX').upper() #assert value in ['FLEX', 'RIGID', 'COATING'], 'TYPE must be [FLEX, RIGID, COATING.]; CSTIFF=%r' % value elif param == 'NSIDE': value = integer_or_blank(card, i, 'value%s' % j, 1) assert value in [1, 2], 'NSIDE must be [1, 2]; NSIDE=%r' % value elif param == 'TBIRTH': value = double_or_blank(card, i, 'value%s' % j, 0.0) elif param == 'TDEATH': value = double_or_blank(card, i, 'value%s' % j, 0.0) elif param == 'INIPENE': value = integer_or_blank(card, i, 'value%s' % j, 0) assert value in [0, 1, 2, 3], 'INIPENE must be [0, 1, 2]; INIPENE=%r' % value elif param == 'PDEPTH': value = double_or_blank(card, i, 'value%s' % j, 0.0) elif param == 'SEGNORM': value = integer_or_blank(card, i, 'value%s' % j, 0) assert value in [-1, 0, 1], 'SEGNORM must be [-1, 0, 1]; SEGNORM=%r' % value elif param == 'OFFTYPE': value = integer_or_blank(card, i, 'value%s' % j, 0) assert value in [0, 1, 2], 'OFFTYPE must be [0, 1, 2]; OFFTYPE=%r' % value elif param == 'OFFSET': value = double_or_blank(card, i, 'value%s' % j, 0.0) elif param == 'TZPENE': value = double_or_blank(card, i, 'value%s' % j, 0.0) elif param == 'CSTIFF': value = integer_or_blank(card, i, 'value%s' % j, 0) assert value in [0, 1], 'CSTIFF must be [0, 1]; CSTIFF=%r' % value elif param == 'TIED': value = integer_or_blank(card, i, 'value%s' % j, 0) assert value in [0, 1], 'TIED must be [0, 1]; TIED=%r' % value elif param == 'TIEDTOL': value = double_or_blank(card, i, 'value%s' % j, 0.0) elif param == 'EXTFAC': value = double_or_blank(card, i, 'value%s' % j, 0.001) assert 1.0E-6 <= value <= 0.1, 'EXTFAC must be 1.0E-6 < EXTFAC < 0.1; EXTFAC=%r' % value else: # FRICMOD, FPARA1/2/3/4/5, EPSN, EPST, CFACTOR1, PENETOL # NCMOD, TCMOD, RFORCE, LFORCE, RTPCHECK, RTPMAX, XTYPE # ... value = integer_double_or_blank(card, i, 'value%s' % j) assert value is not None, '%s%i must not be None' % (param, j) params[param] = value i += 1 j += 1 if j == 4: i += 1 return BCTPARA(csid, params, comment=comment)
[docs] def raw_fields(self): fields = ['BCTPARA', self.csid] i = 0 for key, value in sorted(self.params.items()): if i == 3: fields.append(None) i = 0 fields.append(key) fields.append(value) i += 1 return 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 BCTADD(BaseCard): """ +--------+------+----+-------+----+----+----+----+----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +========+======+====+=======+====+====+====+====+====+ | BCTADD | CSID | SI | S2 | S3 | S4 | S5 | S6 | S7 | +--------+------+----+-------+----+----+----+----+----+ | | S8 | S9 | etc. | | | | | | +--------+------+----+-------+----+----+----+----+----+ Remarks: 1. To include several contact sets defined via BCTSET entries in a model, BCTADD must be used to combine the contact sets. CSID in BCTADD is then selected with the Case Control command BCSET. 2. Si must be unique and may not be the identification of this or any other BCTADD entry. """ type = 'BCTADD'
[docs] @classmethod def _init_from_empty(cls): csid = 1 contact_sets = [1, 2] return BCTADD(csid, contact_sets, comment='')
def __init__(self, csid, contact_sets, comment=''): if comment: self.comment = comment #: Contact set identification number. (Integer > 0) self.csid = csid #: Identification numbers of contact sets defined via BCTSET entries. #: (Integer > 0) self.contact_sets = contact_sets
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a BCTADD card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ csid = integer(card, 1, 'csid') contact_sets = [] i = 1 j = 1 while i < card.nfields: contact_set = integer(card, i, 'S%i' % j) contact_sets.append(contact_set) i += 1 j += 1 return BCTADD(csid, contact_sets, comment=comment)
[docs] def raw_fields(self): fields = ['BCTADD'] + self.contact_sets return 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)