# coding: utf-8
# pylint: disable=C0103
"""
All beam properties are defined in this file. This includes:
* PBEAM
* PBEAML
* PBCOMP
* PBRSECT
All beams are LineProperty objects.
Multi-segment beams are IntegratedLineProperty objects.
"""
from __future__ import annotations
from itertools import count
from typing import TYPE_CHECKING
import numpy as np
from numpy import unique, allclose, ndarray
from pyNastran.bdf.bdf_interface.utils import to_fields
from pyNastran.bdf.bdf_interface.bdf_card import BDFCard
from pyNastran.bdf.cards.properties.bars import (
IntegratedLineProperty, LineProperty, _bar_areaL,
get_beam_sections, split_arbitrary_thickness_section, write_arbitrary_beam_section,
plot_arbitrary_section, A_I1_I2_I12)
from pyNastran.bdf.field_writer_8 import set_blank_if_default
from pyNastran.bdf.bdf_interface.assign_type import (
integer, integer_or_blank, double, double_or_blank,
string, string_or_blank, double_string_or_blank)
from pyNastran.utils.mathematics import integrate_unit_line, integrate_positive_unit_line
from pyNastran.bdf.field_writer_8 import print_card_8
from pyNastran.bdf.field_writer_16 import print_card_16
from pyNastran.bdf.cards.base_card import (
break_word_by_trailing_parentheses_integer_ab, break_word_by_trailing_integer)
from pyNastran.utils.numpy_utils import integer_types, float_types
if TYPE_CHECKING: # pragma: no cover
from pyNastran.bdf.bdf import BDF
[docs]
class PBEAM(IntegratedLineProperty):
"""
Defines the properties of a beam element (CBEAM entry). This element may be
used to model tapered beams.
+-------+-------+-------+-------+-------+-------+--------+-------+--------+
| PBEAM | PID | MID | A(A) | I1(A) | I2(A) | I12(A) | J(A) | NSM(A) |
+-------+-------+-------+-------+-------+-------+--------+-------+--------+
| | C1(A) | C2(A) | D1(A) | D2(A) | E1(A) | E2(A) | F1(A) | F2(A) |
+-------+-------+-------+-------+-------+-------+--------+-------+--------+
The next two continuations are repeated for each intermediate station as
described in Remark 5. and SO and X/XB must be specified.
+----+------+----+----+----+-----+----+-----+
| SO | X/XB | A | I1 | I2 | I12 | J | NSM |
+----+------+----+----+----+-----+----+-----+
| C1 | C2 | D1 | D2 | E1 | E2 | F1 | F2 |
+----+------+----+----+----+-----+----+-----+
The last two continuations are:
+-------+-------+-------+-------+--------+--------+-------+-------+
| K1 | K2 | S1 | S2 | NSI(A) | NSI(B) | CW(A) | CW(B) |
+-------+-------+-------+-------+--------+--------+-------+-------+
| M1(A) | M2(A) | M1(B) | M2(B) | N1(A) | N2(A) | N1(B) | N2(B) |
+-------+-------+-------+-------+--------+--------+-------+-------+
"""
type = 'PBEAM'
#_opt_map = {
#'I1(B)' : 'i1',
#'I1(B)',
#}
[docs]
def update_by_pname_fid(self, pname_fid, value):
if isinstance(pname_fid, integer_types):
if pname_fid < 0:
# convert fid to pname
pname_fid = update_pbeam_negative_integer(pname_fid)
# call this function again to update pname
self.update_by_pname_fid(pname_fid, value)
else: # pragma: no cover
msg = "property_type='PBEAM' has not implemented %r in pname_map" % pname_fid
raise NotImplementedError(msg)
#elif pname_fid.startswith('DIM'):
#word, num = break_word_by_trailing_integer(pname_fid)
#ndim = len(self.dim[0])
#num = int(num) - 1
#idim = num % ndim
#istation = num // ndim
#try:
#dim_station = self.dim[istation]
#dim_station[idim] = value
#except Exception:
#print('pname_fid=%r num=%r ndim=%r' % (pname_fid, num, ndim))
#print('istation=%r idim=%r' % (istation, idim))
#print(self)
#raise
elif isinstance(pname_fid, str):
if '(A)' in pname_fid:
i = 0
end = '(A)'
elif '(' not in pname_fid:
i = 0
end = ''
elif '(B)' in pname_fid:
i = -1
end = '(B)'
elif '(' in pname_fid:
word, num = break_word_by_trailing_parentheses_integer_ab(pname_fid)
end = '(%i)' % num
pname_fid = word + end
i = num - 1
else:
raise NotImplementedError(pname_fid)
if pname_fid.startswith('I12'):
self.i12[i] = value
word = 'I12'
elif pname_fid.startswith('I1'):
self.i1[i] = value
word = 'I1'
elif pname_fid.startswith('I2'):
self.i2[i] = value
word = 'I2'
elif pname_fid.startswith('J'):
self.j[i] = value
word = 'J'
elif pname_fid.startswith('A'):
self.A[i] = value
word = 'A'
elif pname_fid.startswith('C1'):
self.c1[i] = value
word = 'C1'
elif pname_fid.startswith('C2'):
self.c2[i] = value
word = 'C2'
elif pname_fid.startswith('D1'):
self.d1[i] = value
word = 'D1'
elif pname_fid.startswith('D2'):
self.d2[i] = value
word = 'D2'
elif pname_fid.startswith('E1'):
self.e1[i] = value
word = 'E1'
elif pname_fid.startswith('E2'):
self.e2[i] = value
word = 'E2'
elif pname_fid.startswith('F1'):
self.f1[i] = value
word = 'F1'
elif pname_fid.startswith('F2'):
self.f2[i] = value
word = 'F2'
else: # pragma: no cover
msg = "property_type='PBEAM' has not implemented %r in pname_map" % (
pname_fid)
raise NotImplementedError(msg)
expected_word = word + end
if pname_fid != expected_word:
raise RuntimeError('pname_fid=%r expected_word=%r is invalid' % (
pname_fid, expected_word))
else: # pragma: no cover
msg = "property_type='PBEAM' has not implemented %r in pname_map; type=%s" % (
pname_fid, type(pname_fid))
raise NotImplementedError(msg)
[docs]
@classmethod
def _init_from_empty(cls):
pid = 1
mid = 1
xxb = [0.]
so = ['YES']
area = [0.]
i1 = [0.]
i2 = [0.]
i12 = [0.]
j = [0.]
return PBEAM(pid, mid, xxb, so, area, i1, i2, i12, j, nsm=None,
c1=None, c2=None, d1=None, d2=None,
e1=None, e2=None, f1=None, f2=None,
k1=1., k2=1., s1=0., s2=0.,
nsia=0., nsib=None, cwa=0., cwb=None,
m1a=0., m2a=0., m1b=None, m2b=None,
n1a=0., n2a=0., n1b=None, n2b=None,
comment='')
def __init__(self, pid, mid, xxb, so, area, i1, i2, i12, j, nsm=None,
c1=None, c2=None, d1=None, d2=None,
e1=None, e2=None, f1=None, f2=None,
k1=1., k2=1., s1=0., s2=0.,
nsia=0., nsib=None, cwa=0., cwb=None,
m1a=0., m2a=0., m1b=None, m2b=None,
n1a=0., n2a=0., n1b=None, n2b=None,
comment=''):
"""
.. todo:: fix 0th entry of self.so, self.xxb
Creates a PBEAM card
Parameters
----------
pid : int
property id
mid : int
material id
xxb : list[float]
The percentage locations along the beam [0., ..., 1.]
so : list[str]
YES, YESA, NO
area : list[float]
area
i1, i2, i12, j : list[float]
moments of inertia
nsm : list[float]
nonstructural mass per unit length
c1/c2, d1/d2, e1/e2, f1/f2 : list[float]; default=None -> [0.]*nxxb
the y/z locations of the stress recovery points
c1 - point C.y
c2 - point C.z
k1 / k2 : float; default=1.
Shear stiffness factor K in K*A*G for plane 1/2.
s1 / s2 : float; default=0.
Shear relief coefficient due to taper for plane 1/2.
nsia / nsia : float; default=0. / nsia
non structural mass moment of inertia per unit length
about nsm center of gravity at Point A/B.
cwa / cwb : float; default=0. / cwa
warping coefficient for end A/B.
m1a / m2a : float; default=0. / 0.
y/z coordinate of center of gravity of
nonstructural mass for end A.
m1b / m2b : float; default=m1a / m2a
y/z coordinate of center of gravity of
nonstructural mass for end B.
n1a / n2a : float; default=0. / 0.
y/z coordinate of neutral axis for end A.
n1b / n2b : float; default=n1a / n2a
y/z coordinate of neutral axis for end B.
comment : str; default=''
a comment for the card
"""
IntegratedLineProperty.__init__(self)
if comment:
self.comment = comment
if nsib is None:
nsib = nsia
if cwb is None:
cwb = cwa
# A / B - the grid points
# 1 / 2 - the y/z coords
assert m1a is not None, m1a
assert m2a is not None, m2a
if m1b is None:
m1b = m1a
if m2b is None:
m2b = m2a
assert m1b is not None, m1b
assert m2b is not None, m2b
# A / B - the grid points
# 1 / 2 - the y/z coords
assert n1a is not None, n1a
assert n2a is not None, n2a
if n1b is None:
n1b = n1a
if n2b is None:
n2b = n2a
assert n1b is not None, n1b
assert n2b is not None, n2b
nxxb = len(xxb)
if nsm is None:
nsm = [0.] * nxxb
if c1 is None:
c1 = [None] * nxxb
if c2 is None:
c2 = [None] * nxxb
if d1 is None:
d1 = [None] * nxxb
if d2 is None:
d2 = [None] * nxxb
if e1 is None:
e1 = [None] * nxxb
if e2 is None:
e2 = [None] * nxxb
if f1 is None:
f1 = [None] * nxxb
if f2 is None:
f2 = [None] * nxxb
if isinstance(so, str):
so = [so] * nxxb
if isinstance(area, float):
area = [area] * nxxb
if isinstance(i1, float):
i1 = [i1] * nxxb
if isinstance(i2, float):
i2 = [i2] * nxxb
if isinstance(i12, float):
i12 = [i12] * nxxb
if isinstance(j, float):
j = [j] * nxxb
#: Property ID
self.pid = pid
#: Material ID
self.mid = mid
assert isinstance(xxb, list), xxb
assert isinstance(so, list), so
#assert min(so) in [0., 1.], so # YES, NO
#assert max(so) == 1.0, so
#print('xxb', xxb)
assert np.allclose(min(xxb), 0.), 'pid=%s min(xxb)=%s xxb=%s' % (pid, min(xxb), xxb) # x/L
assert 0. <= max(xxb) <= 1.0, 'pid=%s max(xxb)=%s xxb=%s' % (pid, max(xxb), xxb) # x/L
assert isinstance(area, list), area
assert isinstance(i1, list), i1
assert isinstance(i2, list), i2
assert isinstance(i12, list), i12
assert isinstance(j, list), j
assert isinstance(nsm, list), nsm
# at least one cross section is required; even if it's empty
# xxb[0] isn't explicitly used
#: Section position
#self.xxb = xxb
#: Output flag
#self.so = so
#: Area
#self.A = area
#: Moment of Inertia about the 1 axis :math:`I_1`
#self.i1 = i1
#: Moment of Inertia about the 2 axis :math:`I_2`
#self.i2 = i2
#: Moment of Inertia about the 12 axis :math:`I_{12}`
#self.i12 = i12
#: Polar Moment of Inertia :math:`J`
#self.j = j
#: Non-structural mass :math:`nsm`
#self.nsm = nsm
assert isinstance(c1, list), c1
assert isinstance(c2, list), c2
assert isinstance(d1, list), d1
assert isinstance(d2, list), d2
assert isinstance(e1, list), e1
assert isinstance(e2, list), e2
assert isinstance(f1, list), f1
assert isinstance(f2, list), f2
#self.c1 = c1
#self.c2 = c2
#self.d1 = d1
#self.d2 = d2
#self.e1 = e1
#self.e2 = e2
#self.f1 = f1
#self.f2 = f2
#: Shear stiffness factor K in K*A*G for plane 1.
self.k1 = k1
#: Shear stiffness factor K in K*A*G for plane 2.
self.k2 = k2
#: Shear relief coefficient due to taper for plane 1.
self.s1 = s1
#assert self.s1 == 0., self.s1
#: Shear relief coefficient due to taper for plane 2.
self.s2 = s2
#: non structural mass moment of inertia per unit length
#: about nsm center of gravity at Point A.
self.nsia = nsia
#: non structural mass moment of inertia per unit length
#: about nsm center of gravity at Point B.
self.nsib = nsib
#: warping coefficient for end A.
self.cwa = cwa
#: warping coefficient for end B.
self.cwb = cwb
#: y coordinate of center of gravity of
#: nonstructural mass for end A.
self.m1a = m1a
#: z coordinate of center of gravity of
#: nonstructural mass for end A.
self.m2a = m2a
#: y coordinate of center of gravity of
#: nonstructural mass for end B.
self.m1b = m1b
#: z coordinate of center of gravity of
#: nonstructural mass for end B.
self.m2b = m2b
#: y coordinate of neutral axis for end A.
self.n1a = n1a
#: z coordinate of neutral axis for end A.
self.n2a = n2a
#: y coordinate of neutral axis for end B.
self.n1b = n1b
#: z coordinate of neutral axis for end B.
self.n2b = n2b
out = _sort_pbeam(pid, xxb, so, area, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2,
ensure_xxb_1_section=False)
(
self_xxb, self_so, self_A, self_i1, self_i2, self_i12, self_j, self_nsm,
self_c1, self_c2, self_d1, self_d2, self_e1, self_e2, self_f1, self_f2,
) = out
self.so = self_so
self.xxb = self_xxb
self.A = self_A
self.i1 = self_i1
self.i2 = self_i2
self.i12 = self_i12
self.j = self_j
self.nsm = self_nsm
self.c1 = self_c1
self.c2 = self_c2
self.d1 = self_d1
self.d2 = self_d2
self.e1 = self_e1
self.e2 = self_e2
self.f1 = self_f1
self.f2 = self_f2
self._interpolate_sections()
self.mid_ref = None
[docs]
def _interpolate_sections(self):
"""
now we interpolate to fix up missing data
from arrays that were potentially out of order
(they're sorted now)
we've also already checked xxb=0.0 and xxb=1.0 for I1, I2, I12, J
"""
xxb = self.xxb
if len(xxb) == 1:
return
assert len(xxb) > 0
def _linear_interpolate2(xxb, area):
isnan = np.isnan(area[1:-1])
inan = np.arange(1, len(area) - 1)[isnan]
m = (area[-1] - area[0]) / (xxb[-1] - xxb[0])
area[inan] = area[0] + m * xxb[inan]
return area
self.A = _linear_interpolate2(xxb, self.A)
self.i1 = _linear_interpolate2(xxb, self.i1)
self.i2 = _linear_interpolate2(xxb, self.i2)
self.i12 = _linear_interpolate2(xxb, self.i12)
self.j = _linear_interpolate2(xxb, self.j)
self.nsm = _linear_interpolate2(xxb, self.nsm)
self.c1 = _linear_interpolate2(xxb, self.c1)
self.c2 = _linear_interpolate2(xxb, self.c2)
self.d1 = _linear_interpolate2(xxb, self.d1)
self.d2 = _linear_interpolate2(xxb, self.d2)
self.e1 = _linear_interpolate2(xxb, self.e1)
self.e2 = _linear_interpolate2(xxb, self.e2)
self.f1 = _linear_interpolate2(xxb, self.f1)
self.f2 = _linear_interpolate2(xxb, self.f2)
if self.cwa or self.cwb: # if either is non-zero
for i, xxbi, ji in zip(count(), self.xxb, self.j):
if ji < 0.:
msg = 'Warping Check Error; j[%i] must be greater than 0.0' % i
msg += ' cwa=%s cwb=%s\n' % (self.cwa, self.cwb)
msg += ' i=%s xxb=%s j=%s; j[%i]=%s\n' % (i, xxbi, self.j, i, ji)
raise ValueError(msg)
[docs]
def validate(self):
nstations = len(self.A)
for ilayer in range(nstations):
di12 = self.i1[ilayer] * self.i2[ilayer] - self.i12[ilayer] ** 2
if not di12 > 0.:
msg = 'I1 * I2 - I12^2=0 and must be greater than 0.0 at End B\n'
msg += 'pid=%s xxb=%s i1=%s i2=%s i12=%s i1*i2-i12^2=%s' % (
self.pid, self.xxb[ilayer], self.i1[ilayer], self.i2[ilayer],
self.i12[ilayer], di12)
raise ValueError(msg)
[docs]
@classmethod
def add_card(cls, card, comment=''):
"""
Adds a PBEAM card from ``BDF.add_card(...)``
Parameters
----------
card : BDFCard()
a BDFCard object
comment : str; default=''
a comment for the card
"""
pid = integer(card, 1, 'property_id')
mid = integer(card, 2, 'material_id')
area0 = double(card, 3, 'Area')
i1a = double_or_blank(card, 4, 'I1', default=0.0)
i2a = double_or_blank(card, 5, 'I2', default=0.0)
i12a = double_or_blank(card, 6, 'I12', default=0.0)
ja = double_or_blank(card, 7, 'J', default=0.0)
nsma = double_or_blank(card, 8, 'nsm', default=0.0)
area = [area0]
i1 = [i1a]
i2 = [i2a]
i12 = [i12a]
j = [ja]
nsm = [nsma]
assert area[0] >= 0., 'PBEAM pid=%s area=%s' % (pid, area)
assert i1[0] >= 0., 'PBEAM pid=%s i1=%s' % (pid, i1)
assert i2[0] >= 0., 'PBEAM pid=%s i2=%s' % (pid, i2)
# we'll do a check for warping later; cwa/cwb -> j > 0.0
assert j[0] >= 0., 'PBEAM pid=%s j=%s' % (pid, j)
if i1a * i2a - i12a ** 2 <= 0.:
msg = 'I1 * I2 - I12^2=0 and must be greater than 0.0 at End A\n'
msg += 'i1=%s i2=%s i12=%s i1*i2-i12^2=%s' % (i1a, i2a, i12a, i1a*i2a-i12a**2)
raise ValueError(msg)
# TODO: can you have a single lined PBEAM...I think so...
# the second line is blank, so all values would be None (End A)
# the NO xxb would be implicitly defined as 1.0
# the End B values would try to use End A values, but because they're not set,
# the defaults would get applied
# the final 2 lines will default
# finally, there would be no output at End A, but there would be output at End A.
ifield = 9
field9 = double_string_or_blank(card, 9, 'field9', default=0.0)
if isinstance(field9, float):
# C/D/E/F
c1a = double_or_blank(card, 9, 'c1', default=0.0)
c2a = double_or_blank(card, 10, 'c2', default=0.0)
d1a = double_or_blank(card, 11, 'd1', default=0.0)
d2a = double_or_blank(card, 12, 'd2', default=0.0)
e1a = double_or_blank(card, 13, 'e1', default=0.0)
e2a = double_or_blank(card, 14, 'e2', default=0.0)
f1a = double_or_blank(card, 15, 'f1', default=0.0)
f2a = double_or_blank(card, 16, 'f2', default=0.0)
c1 = [c1a]
c2 = [c2a]
d1 = [d1a]
d2 = [d2a]
e1 = [e1a]
e2 = [e2a]
f1 = [f1a]
f2 = [f2a]
so = ['YES']
ifield += 8 # 9 + 8 = 17
else:
c1a = c2a = d1a = d2a = e1a = e2a = f1a = f2a = 0.0
c1 = [None]
c2 = [None]
d1 = [None]
d2 = [None]
e1 = [None]
e2 = [None]
f1 = [None]
f2 = [None]
so = ['NO']
if field9 not in ['YES', 'YESA', 'NO']:
msg = ('field9=%r on the PBEAM pid=%s must be [YES, YESA, NO] '
'because C/D/E/F at A is not specified' % field9)
raise ValueError(field9)
xxb = [0.]
irow = 0
nrows_max = 10
for irow in range(nrows_max):
nrepeated = irow + 1
SOi_k1 = double_string_or_blank(card, ifield, 'SO_%i/K1' % nrepeated)
if isinstance(SOi_k1, float) or SOi_k1 is None:
# we found K1
break
else:
soi = string(card, ifield, 'SO%i' % nrepeated)
xxbi = double(card, ifield + 1, 'x/xb%i' % nrepeated)
if xxbi == 1.0:
# these have already been checked such that they're greater than 0
# so when we interpolate, our values will be correct
areai = double_or_blank(card, ifield + 2, 'Area%d' % nrepeated, default=area0)
i1i = double_or_blank(card, ifield + 3, 'I1 %d' % nrepeated, default=i1a)
i2i = double_or_blank(card, ifield + 4, 'I2 %d' % nrepeated, default=i2a)
i12i = double_or_blank(card, ifield + 5, 'I12 %d' % nrepeated, default=i12a)
ji = double_or_blank(card, ifield + 6, 'J%d' % nrepeated, default=ja)
nsmi = double_or_blank(card, ifield + 7, 'nsm%d' % nrepeated, default=nsma)
assert areai >= 0., areai
assert i1i >= 0., i1i
assert i2i >= 0., i2i
# we'll do a check for warping later; cwa/cwb -> j > 0.0
assert ji >= 0., ji
if i1i * i2i - i12i ** 2 <= 0.:
msg = 'I1 * I2 - I12^2=0 and must be greater than 0.0 at End B\n'
msg += 'xxb=1.0 i1=%s i2=%s i12=%s' % (i1i, i2i, i12i)
raise ValueError(msg)
else:
# we'll go through and do linear interpolation afterwards
areai = double_or_blank(card, ifield + 2, 'Area%d' % nrepeated, default=None)
i1i = double_or_blank(card, ifield + 3, 'I1 %d' % nrepeated, default=None)
i2i = double_or_blank(card, ifield + 4, 'I2 %d' % nrepeated, default=None)
i12i = double_or_blank(card, ifield + 5, 'I12 %d' % nrepeated, default=None)
ji = double_or_blank(card, ifield + 6, 'J%d' % nrepeated, default=None)
nsmi = double_or_blank(card, ifield + 7, 'nsm%d' % nrepeated, default=None)
so.append(soi)
xxb.append(xxbi)
area.append(areai)
i1.append(i1i)
i2.append(i2i)
i12.append(i12i)
j.append(ji)
nsm.append(nsmi)
if soi == 'YES':
c1i = double_or_blank(card, ifield + 8, 'c1 %i' % nrepeated, default=0.0)
c2i = double_or_blank(card, ifield + 9, 'c2 %i' % nrepeated, default=0.0)
d1i = double_or_blank(card, ifield + 10, 'd1 %i' % nrepeated, default=0.0)
d2i = double_or_blank(card, ifield + 11, 'd2 %i' % nrepeated, default=0.0)
e1i = double_or_blank(card, ifield + 12, 'e1 %i' % nrepeated, default=0.0)
e2i = double_or_blank(card, ifield + 13, 'e2 %i' % nrepeated, default=0.0)
f1i = double_or_blank(card, ifield + 14, 'f1 %i' % nrepeated, default=0.0)
f2i = double_or_blank(card, ifield + 15, 'f2 %i' % nrepeated, default=0.0)
ifield += 16
elif soi == 'YESA':
c1i = c1a
c2i = c2a
d1i = d1a
d2i = d2a
e1i = e1a
e2i = e2a
f1i = f1a
f2i = f2a
ifield += 8
elif soi == 'NO':
c1i = None
c2i = None
d1i = None
d2i = None
e1i = None
e2i = None
f1i = None
f2i = None
ifield += 8
else:
raise RuntimeError(f'so={so!r} and not [YES, YESA, NO]')
c1.append(c1i)
c2.append(c2i)
d1.append(d1i)
d2.append(d2i)
e1.append(e1i)
e2.append(e2i)
f1.append(f1i)
f2.append(f2i)
if irow != 0:
assert min(xxb) == 0.0, 'pid=%s x/xb=%s' % (pid, xxb)
assert max(xxb) == 1.0, 'pid=%s x/xb=%s' % (pid, xxb)
assert len(xxb) == len(unique(xxb)), xxb
# calculate:
# k1, k2, s1, s2
# m1a, m2a, n1a, n2a, etc.
# footer fields
#: Shear stiffness factor K in K*A*G for plane 1.
k1 = double_or_blank(card, ifield, 'k1', default=1.0)
#: Shear stiffness factor K in K*A*G for plane 2.
k2 = double_or_blank(card, ifield + 1, 'k2', default=1.0)
#: Shear relief coefficient due to taper for plane 1.
s1 = double_or_blank(card, ifield + 2, 's1', default=0.0)
#: Shear relief coefficient due to taper for plane 2.
s2 = double_or_blank(card, ifield + 3, 's2', default=0.0)
#: non structural mass moment of inertia per unit length
#: about nsm center of gravity at Point A.
nsia = double_or_blank(card, ifield + 4, 'nsia', default=0.0)
#: non structural mass moment of inertia per unit length
#: about nsm center of gravity at Point B.
nsib = double_or_blank(card, ifield + 5, 'nsib', default=nsia)
#: warping coefficient for end A.
cwa = double_or_blank(card, ifield + 6, 'cwa', default=0.0)
#: warping coefficient for end B.
cwb = double_or_blank(card, ifield + 7, 'cwb', default=cwa)
#: y coordinate of center of gravity of
#: nonstructural mass for end A.
m1a = double_or_blank(card, ifield + 8, 'm1a', default=0.0)
#: z coordinate of center of gravity of
#: nonstructural mass for end A.
m2a = double_or_blank(card, ifield + 9, 'm2a', default=0.0)
#: y coordinate of center of gravity of
#: nonstructural mass for end B.
m1b = double_or_blank(card, ifield + 10, 'm1b', default=m1a)
#: z coordinate of center of gravity of
#: nonstructural mass for end B.
m2b = double_or_blank(card, ifield + 11, 'm2b', default=m2a)
#: y coordinate of neutral axis for end A.
n1a = double_or_blank(card, ifield + 12, 'n1a', default=0.0)
#: z coordinate of neutral axis for end A.
n2a = double_or_blank(card, ifield + 13, 'n2a', default=0.0)
#: y coordinate of neutral axis for end B.
n1b = double_or_blank(card, ifield + 14, 'n1a', default=n1a)
#: z coordinate of neutral axis for end B.
n2b = double_or_blank(card, ifield + 15, 'n2b', default=n2a)
ifield += 16
if len(card) > ifield:
msg = 'len(card)=%s is too long; max=%s\n' % (len(card), ifield)
msg += 'You probably have a empty line after the YESA/NO line.\n'
msg += 'The next line must have K1.\n'
msg += 'pid = %s\n' % pid
msg += 'mid = %s\n' % mid
msg += 's0 = %s\n' % so
msg += 'xxb = %s\n' % xxb
msg += 'A = %s\n' % area
msg += 'i1 = %s\n' % i1
msg += 'i2 = %s\n' % i2
msg += 'i12 = %s\n' % i12
msg += 'j = %s\n' % j
msg += 'nsm = %s\n\n' % nsm
msg += 'c1 = %s\n' % c1
msg += 'c2 = %s\n' % c2
msg += 'd1 = %s\n' % d1
msg += 'd2 = %s\n' % d2
msg += 'e1 = %s\n' % e1
msg += 'e2 = %s\n' % e2
msg += 'f1 = %s\n' % f1
msg += 'f2 = %s\n\n' % f2
msg += 'k1 = %s\n' % k1
msg += 'k2 = %s\n' % k2
msg += 's1 = %s\n' % s1
msg += 's2 = %s\n' % s2
msg += 'nsia = %s\n' % nsia
msg += 'nsib = %s\n\n' % nsib
msg += 'cwa = %s\n' % cwa
msg += 'cwb = %s\n' % cwb
msg += 'm1a = %s\n' % m1a
msg += 'm2a = %s\n' % m2a
msg += 'mb1 = %s\n' % m1b
msg += 'm2b = %s\n' % m2b
msg += 'n1a = %s\n' % n1a
msg += 'n2a = %s\n' % n2a
msg += 'n1b = %s\n' % n1b
msg += 'n2b = %s\n' % n2b
raise RuntimeError(msg)
return PBEAM(
pid, mid, xxb, so, area, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2,
k1, k2, s1, s2,
nsia, nsib, cwa, cwb, m1a,
m2a, m1b, m2b, n1a, n2a, n1b, n2b,
comment=comment)
@classmethod
def add_op2_data(cls, data, comment=''):
"""
Adds a PBEAM card from the OP2
Parameters
----------
data : list[varies]
a list of fields defined in OP2 format
comment : str; default=''
a comment for the card
"""
(pid, mid, xxb, so, area, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2,
k1, k2, s1, s2,
nsia, nsib, cwa, cwb,
m1a, m2a, m1b, m2b,
n1a, n2a, n1b, n2b) = pbeam_op2_data_to_init(data)
return PBEAM(pid, mid, xxb, so, area, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2,
k1=k1, k2=k2, s1=s1, s2=s2,
nsia=nsia, nsib=nsib, cwa=cwa, cwb=cwb,
m1a=m1a, m2a=m2a, m1b=m1b, m2b=m2b,
n1a=n1a, n2a=n2a, n1b=n1b, n2b=n2b,
comment=comment)
[docs]
def set_optimization_value(self, name_str: str, value: float) -> None:
if name_str == 'I1(A)':
self.i1[0] = value
elif name_str == 'I1(B)':
self.i1[-1] = value
elif name_str == 'I2(A)':
self.i1[-1] = value
elif name_str == 'I2(B)':
self.i2[-1] = value
else:
raise NotImplementedError(name_str)
[docs]
def get_optimization_value(self, name_str: str) -> float:
if name_str == 'I1(A)':
out = self.i1[0]
elif name_str == 'I1(B)':
out = self.i1[-1]
elif name_str == 'I2(A)':
out = self.i2[-1]
elif name_str == 'I2(B)':
out = self.i2[-1]
else:
raise NotImplementedError(name_str)
return out
#def Area(self):
#""".. warning:: area field not supported fully on PBEAM card"""
##raise RuntimeError(self.A[0])
#return self.A[0]
#def Nsm(self):
#""".. warning:: nsm field not supported fully on PBEAM card"""
##raise RuntimeError(self.nsm[0])
#return self.nsm[0]
[docs]
def I1_I2_I12(self) -> tuple[float, float, float]:
#assert self.i1 is not None, 'I1=%r' % self.i1
#assert self.i2 is not None, 'I2=%r' % self.i2
#assert self.i12 is not None, 'I12=%r' % self.i12
return self.i1[0], self.i2[0], self.i12[0]
[docs]
def MassPerLength(self) -> float:
"""
mass = L*(Area*rho+nsm)
mass/L = Area*rho+nsm
"""
rho = self.Rho()
max_nsm = np.abs(self.nsm).max()
if rho == 0.0 and max_nsm == 0.0:
return 0.0
mass_per_lengths = []
for (area, nsm) in zip(self.A, self.nsm):
#print('area=%s rho=%s nsm=%s' % (area, rho, nsm))
mass_per_lengths.append(area * rho + nsm)
mass_per_length = integrate_positive_unit_line(self.xxb, mass_per_lengths)
return mass_per_length
[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 PBEAM mid=%s' % self.mid
self.mid_ref = model.Material(self.mid, msg=msg)
#if model.sol != 600:
#assert max(self.j) == 0.0, self.j
#assert min(self.j) == 0.0, self.j
[docs]
def uncross_reference(self) -> None:
"""Removes cross-reference links"""
self.mid = self.Mid()
self.mid_ref = None
def _verify(self, xref):
pid = self.Pid()
mid = self.Mid()
A = self.Area()
try:
J = self.J()
except NotImplementedError:
msg = "J is not implemented for pid.type=%s pid.Type=%s" % (self.type, self.beam_type)
print(msg)
J = 0.0
nsm = self.Nsm()
assert isinstance(pid, int), 'PBEAM: pid=%r' % pid
assert isinstance(mid, int), 'PBEAM: mid=%r' % mid
assert isinstance(A, float), 'PBEAM: pid=%r' % A
assert isinstance(J, float), 'PBEAM: cid=%r' % J
assert isinstance(nsm, float), 'PBEAM: nsm=%r' % nsm
if xref:
assert self.mid_ref.type in ['MAT1', 'MAT4', 'MAT5'], 'pid.type=%s; mid_ref.type=%s' % (
self.type, self.mid_ref.type)
#self.MassPerLength()
[docs]
def raw_fields(self):
list_fields = ['PBEAM', self.pid, self.Mid()]
i = 0
loop_vars = (
self.so, self.xxb, self.A, self.i1, self.i2, self.i12, self.j, self.nsm,
self.c1, self.c2, self.d1, self.d2, self.e1, self.e2, self.f1, self.f2
)
for out in zip(*loop_vars):
(so, xxb, A, i1, i2, i12, j, nsm, c1, c2, d1, d2, e1, e2, f1, f2) = out
if i == 0: # the first 2 fields aren't written
list_fields += [A, i1, i2, i12, j, nsm]
if any([isinstance(cdefi, float) for cdefi in [c1, c2, d1, d2, e1, e2, f1, f2]]):
list_fields += [c1, c2, d1, d2, e1, e2, f1, f2]
else:
if so in ['YES']:
list_fields += [
'YES', xxb, A, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2
]
elif so in ['NO']:
list_fields += ['NO', xxb, A, i1, i2, i12, j, nsm]
elif so in ['YESA']:
list_fields += ['YESA', xxb, A, i1, i2, i12, j, nsm]
else:
raise RuntimeError('so=%r type(so)=%s' % (so, type(so)))
i += 1
footer = [
self.k1, self.k2, self.s1, self.s2, self.nsia, self.nsib, self.cwa, self.cwb,
self.m1a, self.m2a, self.m1b, self.m2b, self.n1a, self.n2a, self.n1b, self.n2b
]
list_fields += footer
return list_fields
[docs]
def repr_fields(self):
list_fields = ['PBEAM', self.pid, self.Mid()]
#print("c1=%r c2=%r" % (self.c1, self.c2))
#print("d1=%r d2=%r" % (self.d1, self.d2))
#print("e1=%r e2=%r" % (self.e1, self.e2))
#print("f1=%r f2=%r" % (self.f1, self.f2))
#print('i1 = %r' % self.i1)
#print('i2 = %r' % self.i2)
#print('i12 = %r' % self.i12)
nxxb = len(self.xxb)
for (i, so, xxb, A, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2) in zip(
count(),
self.so, self.xxb, self.A, self.i1, self.i2, self.i12,
self.j, self.nsm, self.c1, self.c2, self.d1, self.d2,
self.e1, self.e2, self.f1, self.f2):
i1 = set_blank_if_default(i1, 0.0)
i2 = set_blank_if_default(i2, 0.0)
i12 = set_blank_if_default(i12, 0.0)
j = set_blank_if_default(j, 0.0)
nsm = set_blank_if_default(nsm, 0.0)
c1 = set_blank_if_default(c1, 0.0)
d1 = set_blank_if_default(d1, 0.0)
e1 = set_blank_if_default(e1, 0.0)
f1 = set_blank_if_default(f1, 0.0)
c2 = set_blank_if_default(c2, 0.0)
d2 = set_blank_if_default(d2, 0.0)
e2 = set_blank_if_default(e2, 0.0)
f2 = set_blank_if_default(f2, 0.0)
if i == 0: # the first 2 fields aren't written
list_fields += [A, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2]
else:
if so in ['YES']:
yes_footer = [c1, c2, d1, d2, e1, e2, f1, f2]
if yes_footer == [None] * len(yes_footer):
c1 = c2 = 0.0
list_fields += ['YES', xxb, A, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2]
elif so in ['NO']:
list_fields += ['NO', xxb, A, i1, i2, i12, j, nsm]
elif so in ['YESA']:
list_fields += ['YESA', xxb, A, i1, i2, i12, j, nsm]
else:
raise RuntimeError('so=%r type(so)=%s' % (so, type(so)))
k1 = set_blank_if_default(self.k1, 1.0)
k2 = set_blank_if_default(self.k2, 1.0)
s1 = set_blank_if_default(self.s1, 0.0)
s2 = set_blank_if_default(self.s2, 0.0)
#k1 = self.k1
#k2 = self.k2
#s1 = self.s1
#s2 = self.s2
nsia = set_blank_if_default(self.nsia, 0.0)
nsib = set_blank_if_default(self.nsib, self.nsia)
cwa = set_blank_if_default(self.cwa, 0.0)
cwb = set_blank_if_default(self.cwb, self.cwa)
#m1a = self.m1a
#m2a = self.m2a
#m1b = self.m1b
#m2b = self.m2b
# Point A/B
# Directions 1/2
m1a = set_blank_if_default(self.m1a, 0.0)
m2a = set_blank_if_default(self.m2a, 0.0)
m1b = set_blank_if_default(self.m1b, self.m1a)
m2b = set_blank_if_default(self.m2b, self.m2a)
n1a = set_blank_if_default(self.n1a, 0.0)
n2a = set_blank_if_default(self.n2a, 0.0)
n1b = set_blank_if_default(self.n1b, self.n1a)
n2b = set_blank_if_default(self.n2b, self.n2a)
footer = [k1, k2, s1, s2, nsia, nsib, cwa, cwb,
m1a, m2a, m1b, m2b, n1a, n2a, n1b, n2b]
if footer != [None] * len(footer):
list_fields += footer
return list_fields
[docs]
def write_card(self, size: int=8, is_double: bool=False) -> str:
card = self.repr_fields()
if size == 8:
return self.comment + print_card_8(card)
return self.comment + print_card_16(card)
[docs]
def write_card_16(self, is_double=False):
card = self.repr_fields()
return self.comment + print_card_16(card)
[docs]
def pbeam_op2_data_to_init(data):
(pid, mid, unused_nsegs, unused_ccf, unused_x) = data[:5]
rows = data[5:]
if len(rows) != 12:
msg = 'PBEAM: len(rows)=%s expected=13\n' % len(rows)
for datai in data:
msg += ' %s\n' % str(datai)
raise SyntaxError(msg)
area = []
so = []
xxb = []
i1 = []
i2 = []
i12 = []
j = []
nsm = []
c1 = []
c2 = []
d1 = []
d2 = []
e1 = []
e2 = []
f1 = []
f2 = []
## TODO: PBEAM: op2 handle repeated x/xb = 0.0, 1.0
iis = []
for i, pack in enumerate(rows[:-1]):
(soi, xxbi, areai, i1i, i2i, i12i, ji, nsmi, c1i, c2i,
d1i, d2i, e1i, e2i, f1i, f2i) = pack
if i > 0 and allclose(xxbi, 0.0):
#print('PBEAM - skipping i=%s x/xb=%s' % (i, xxbi))
continue
if i > 0 and i != 10 and allclose(xxbi, 1.0):
#print('PBEAM - skipping i=%s x/xb=%s' % (i, xxbi))
continue
area.append(areai)
xxb.append(xxbi)
iis.append(i)
so.append(soi)
i1.append(i1i)
i2.append(i2i)
i12.append(i12i)
j.append(ji)
nsm.append(nsmi)
c1.append(c1i)
c2.append(c2i)
d1.append(d1i)
d2.append(d2i)
e1.append(e1i)
e2.append(e2i)
f1.append(f1i)
f2.append(f2i)
#print('*i = %s' % iis)
#print('*xxb = %s' % xxb)
#print('*i1 = %s' % i1)
(k1, k2, s1, s2, nsia, nsib, cwa, unused_cwb,
m1a, m2a, m1b, m2b, n1a, n2a, n1b, n2b) = data[-1]
cwb = None
out = (pid, mid, xxb, so, area, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2,
k1, k2, s1, s2,
nsia, nsib, cwa, cwb,
m1a, m2a, m1b, m2b,
n1a, n2a, n1b, n2b)
return out
[docs]
def update_pbeam_negative_integer(pname_fid):
"""
Converts the negative PBEAM value to a positive one
Parameters
----------
pname_fid : int
for a PBEAM this should be between [-5, -167]
the negative values correspond to the numbers in the MPT OP2 table
Returns
-------
pname_fid : str
a pname is of 'J(3)' is far more clear than -37
TODO: only handles istation=0 for now (e.g., 'J(1)')
"""
# shift to divisible by 16
if not (-167 <= pname_fid <= -6): # pragma: no cover
msg = "A-property_type='PBEAM' has not implemented %r in pname_map" % (
pname_fid)
raise NotImplementedError(msg)
ioffset = -pname_fid - 6
istation = ioffset // 16
iterm = ioffset % 16
# 0 1 2 3 4 5 6 7 8 9
#(soi, xxb, a, i1, i2, i12, j, nsm, c1, c2,
#
#10 11 12 13 14 15
#d1, d2, e1, e2, f1, f2) = pack
assert istation == 0, istation
if iterm == 2:
word = 'A'
elif iterm == 3:
word = 'I1'
elif iterm == 4:
word = 'I2'
elif iterm == 5:
word = 'I12'
elif iterm == 6:
word = 'J'
#elif iterm == 7:
#self.nsm[istation] = value # 13-6 = 6
elif iterm == 8:
word = 'C1'
elif iterm == 9:
word = 'C2'
elif iterm == 10:
word = 'D1'
elif iterm == 11:
word = 'D2'
elif iterm == 12:
word = 'E1'
elif iterm == 13:
word = 'E2'
elif iterm == 14:
word = 'F1'
elif iterm == 15:
word = 'F2'
else: # pragma: no cover
#print('istation=%s iterm=%s' % (istation, iterm))
msg = ("property_type='PBEAM' has not implemented %r (istation=%r, iterm=%r)"
" in pname_map" % (pname_fid, istation, iterm))
raise NotImplementedError(msg)
word = '%s(%d)' % (word, istation + 1)
return word
[docs]
class PBEAML(IntegratedLineProperty):
"""
+--------+---------+---------+---------+---------+---------+---------+---------+---------+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+========+=========+=========+=========+=========+=========+=========+=========+=========+
| PBEAML | PID | MID | GROUP | TYPE | | | | |
+--------+---------+---------+---------+---------+---------+---------+---------+---------+
| | DIM1(A) | DIM2(A) | etc. | DIMn(A) | NSM(A) | SO(1) | X(1)/XB | DIM1(1) |
+--------+---------+---------+---------+---------+---------+---------+---------+---------+
| | DIM2(1) | etc. | DIMn(1) | NSM(1) | SO(2) | X(2)/XB | DIM1(2) | DIM2(2) |
+--------+---------+---------+---------+---------+---------+---------+---------+---------+
| | etc. | DIMn(2) | NSM(m) | etc. | SO(m) | X(m)/XB | DIM1(m) | etc. |
+--------+---------+---------+---------+---------+---------+---------+---------+---------+
| | DIMn(m) | NSM(m) | SO(B) | 1.0 | DIM1(B) | DIM2(B) | etc. | DIMn(B) |
+--------+---------+---------+---------+---------+---------+---------+---------+---------+
| | NSM(B) | | | | | | | |
+--------+---------+---------+---------+---------+---------+---------+---------+---------+
"""
type = 'PBEAML'
_properties = ['valid_types', 'Type']
valid_types = {
"ROD": 1,
"TUBE": 2,
"TUBE2": 2,
"L": 4,
"I": 6,
"CHAN": 4,
"T": 4,
"BOX": 4,
"BAR": 2,
"CROSS": 4,
"H": 4,
"T1": 4,
"I1": 4,
"CHAN1": 4,
"Z": 4,
"CHAN2": 4,
"T2": 4,
"BOX1": 6,
"HEXA": 3,
"HAT": 4,
"HAT1": 5,
"DBOX": 10, # TODO: was 12???
} # for GROUP="MSCBML0"
[docs]
def update_by_pname_fid(self, pname_fid: str, value: float):
if isinstance(pname_fid, int):
raise NotImplementedError('property_type=%r has not implemented %r in pname_map' % (
self.type, pname_fid))
elif pname_fid.startswith('DIM'):
# DIM1, DIM1(A), DIM1(10), DIM2(B)
#
# (DIM, num, station)
#
station_str = 'A' # the default, A
if '(' in pname_fid:
assert pname_fid.endswith(')'), pname_fid
pname_fid, station_str = pname_fid[:-1].split('(', 1)
ndim = len(self.dim[0])
if station_str == 'A':
istation = 0
elif station_str == 'B':
istation = ndim - 1
else:
istation = int(station_str) - 1
unused_dim_word, num = break_word_by_trailing_integer(pname_fid)
idim = int(num) - 1
# in Nastran syntax
#print('DIM%i(%i)' % (idim+1, istation+1))
try:
dim_station = self.dim[istation]
dim_station[idim] = value
except Exception:
print('pname_fid=%r num=%r ndim=%r' % (pname_fid, num, ndim))
print('istation=%r idim=%r' % (istation, idim))
print(self)
raise
elif isinstance(pname_fid, str):
if not pname_fid[-1].isdigit():
param_name = pname_fid
idim = -1
else:
param_name, num = break_word_by_trailing_integer(pname_fid)
idim = int(num) - 1
if param_name == 'NSM':
self.nsm[idim] = value
else:
raise NotImplementedError('property_type=%r param_name=%r idim=%s '
'has not implemented %r in pname_map' % (
self.type, param_name, idim, pname_fid))
else:
raise NotImplementedError('property_type=%r has not implemented %r in pname_map' % (
self.type, pname_fid))
[docs]
@classmethod
def _init_from_empty(cls):
pid = 1
mid = 1
beam_type = 'ROD'
xxb = [1.]
dims = [[1.]]
return PBEAML(pid, mid, beam_type, xxb, dims,
so=None, nsm=None, group='MSCBML0', comment='')
def __init__(self, pid: int, mid: int, beam_type: str,
xxb, dims, so=None, nsm=None,
group: str='MSCBML0', comment: str=''):
"""
Creates a PBEAML card
Parameters
----------
pid : int
property id
mid : int
material id
beam_type : str
the section profile
xxb : list[float]
The percentage locations along the beam [0., ..., 1.]
dims : list[dim]
dim : list[float]
The dimensions for each section
so : list[str]; default=None
YES, YESA, NO
None : [0.] * len(xxb)
nsm : list[float]; default=None
nonstructural mass per unit length
None : [0.] * len(xxb)
group : str; default='MSCBML0'
this parameter can lead to a very broken deck with a very
bad error message; don't touch it!
comment : str; default=''
a comment for the card
"""
IntegratedLineProperty.__init__(self)
if comment:
self.comment = comment
#: Property ID
self.pid = pid
#: Material ID
self.mid = mid
self.group = group
#: Section Type (e.g. 'ROD', 'TUBE', 'I', 'H')
self.beam_type = beam_type
ndim = self.valid_types[self.beam_type]
nxxb = len(xxb)
if nxxb == 0:
raise IndexError(f'pid={pid:d}; len(xxb)=0; at least 1 station must be defined')
if nsm is None:
nsm = [0.] * nxxb
elif not isinstance(nsm, (list, tuple, ndarray)):
msg = f'pid={pid}; nsm={nsm} and must be a list/tuple/ndarray; type={type(nsm)}'
raise TypeError(msg)
if so is None:
so = ['YES'] * nxxb
elif not isinstance(so, (list, tuple, ndarray)):
msg = f'pid={pid:d}; so={so} and must be a list/tuple/ndarray; type={type(so)}'
raise TypeError(msg)
for istation, xxbi, nsmi, dim in zip(count(), xxb, nsm, dims):
if not isinstance(dim, (list, ndarray)):
msg = f'Expected list[list[float]] for dims. Did you forget [] around dims?\n'
msg += 'dims = list[dim]; dim=list[floats]; type(dim)={type(dim)}'
raise TypeError(msg)
assert len(dim) == ndim, f'beam_type={beam_type!r} ndim={ndim} len(dim)={len(dim)} xxb={xxbi} dim={dim}'
msg = ''
if not isinstance(xxbi, float_types):
msg += f'istation={istation:d} xxb={xxbi} and must be a float\n'
if not isinstance(nsmi, float_types):
msg += f'istation={istation:d} nsm={nsmi} and must be a float\n'
for idim, dimi in enumerate(dim):
if not isinstance(dimi, float_types):
msg += f'istation={istation:d} dim{idim+1:d}={dimi} and must be a float\n'
if msg:
raise TypeError(msg.rstrip())
self.dim = np.asarray(dims)
self.xxb = np.asarray(xxb)
self.so = so
self.nsm = np.asarray(nsm)
#dim0 = dims[0]
#if beam_type == 'BOX':
# w, h, *ts = dim0
# print(f'I pid={pid}: type={beam_type}; h={h:.2f} w={w:.2f} ts={ts} I1={self.I11():.3f} I2={self.I22():.3f}')
#if beam_type == 'I':
# print(f'I pid={pid}: type={beam_type}; h={dim0[0]:.2f}')
#assert len(xxb) == len(dim), f'xxb={xxb}; nsm={dim}'
#assert len(xxb) == len(so), f'xxb={xxb}; nsm={so}'
#assert len(xxb) == len(nsm), f'xxb={xxb}; nsm={nsm}'
self.mid_ref = None
unused_A = self.Area()
[docs]
def validate(self):
uxxb = np.unique(self.xxb)
if len(self.xxb) != len(uxxb):
raise ValueError(f'xxb={self.xxb} unique(xxb)={uxxb}')
[docs]
def _finalize_hdf5(self, encoding):
"""hdf5 helper function"""
if isinstance(self.dim, list):
self.dim = np.asarray(self.dim)
self.xxb = np.asarray(self.xxb)
self.nsm = np.asarray(self.nsm)
[docs]
@classmethod
def add_card(cls, card, comment=''):
"""
Adds a PBEAML card from ``BDF.add_card(...)``
Parameters
----------
card : BDFCard()
a BDFCard object
comment : str; default=''
a comment for the card
"""
pid = integer(card, 1, 'pid')
mid = integer(card, 2, 'mid')
group = string_or_blank(card, 3, 'group', default='MSCBML0')
beam_type = string(card, 4, 'Type')
# determine the number of required dimensions on the PBEAM
ndim = cls.valid_types[beam_type]
#: dimension list
dims = []
dim = []
#: Section position
xxb = [0.]
#: Output flag
so = ['YES'] # station 0
#: non-structural mass :math:`nsm`
nsm = []
i = 9
n = 0
#n_so = (len(card) - 9) // (ndim + 2) #- 1
#n_extra = (len(card) - 9) % (ndim + 2)
xxbi = 0.0
while i < len(card):
if n > 0:
soi = string_or_blank(card, i, 'so_n=%d' % n, default='YES')
xxbi = double_or_blank(card, i + 1, 'xxb_n=%d' % n, default=1.0)
so.append(soi)
xxb.append(xxbi)
i += 2
# PBARL
# 9. For DBOX section, the default value for DIM5 to DIM10 are
# based on the following rules:
# a. DIM5, DIM6, DIM7 and DIM8 have a default value of
# DIM4if not provided.
# b. DIM9 and DIM10 have a default value of DIM6 if not
# provided.
#If any of the fields NSM(B), DIMi(B) are blank on the
#continuation entry for End B, the values are set to the
#values given for end A. For the continuation entries that
#have values of X(j)/XB between 0.0 and 1.0 and use the
#default option (blank field), a linear interpolation between
#the values at ends A and B is performed to obtain the
#missing field.
dim = []
if beam_type == 'DBOX':
for ii in range(ndim):
field_name = 'istation=%d; ndim=%d; dim%d' % (n, ndim, ii+1)
if ii in [4, 5, 6, 7]:
dim4 = dim[3]
dimi = double_or_blank(card, i, field_name, default=dim4)
elif ii in [8, 9]:
dim6 = dim[5]
dimi = double_or_blank(card, i, field_name, default=dim6)
else:
dimi = double(card, i, field_name)
dim.append(dimi)
i += 1
else:
for ii in range(ndim):
field_name = 'istation=%s; ndim=%s; dim%d' % (n, ndim, ii+1)
if xxbi == 0.0:
dimi = double(card, i, field_name)
elif xxbi == 1.0:
dims0 = dims[0]
dimi = double_or_blank(card, i, field_name, dims0[ii])
else:
## TODO: use linear interpolation
dimi = double(card, i, field_name)
dim.append(dimi)
i += 1
dims.append(dim)
nsmi = double_or_blank(card, i, 'nsm_n=%i' % n, 0.0)
nsm.append(nsmi)
n += 1
i += 1
assert len(card) > 5, card
return PBEAML(pid, mid, beam_type, xxb, dims, group=group,
so=so, nsm=nsm, comment=comment)
def _verify(self, xref):
pid = self.Pid()
nsm = self.Nsm()
area = self.Area()
assert isinstance(pid, int), 'pid=%r\n%s' % (pid, str(self))
assert isinstance(nsm, float), 'nsm=%r\n%s' % (nsm, str(self))
assert isinstance(area, float), 'area=%r\n%s' % (area, str(self))
if xref:
rho = self.Rho()
mass_per_length = self.MassPerLength()
assert isinstance(rho, float), 'rho=%r\n%s' % (rho, str(self))
assert isinstance(mass_per_length, float), 'mass/L=%r\n%s' % (mass_per_length, str(self))
@classmethod
def add_op2_data(cls, data, comment=''):
"""
Adds a PBEAML card from the OP2
Parameters
----------
data : list[varies]
a list of fields defined in OP2 format
comment : str; default=''
a comment for the card
TODO: this doesn't work right for the calculation of area
the card is all messed up
"""
pid, mid, beam_type, xxb, dims, group, so, nsm = pbeaml_op2_data_to_init(data, cls.valid_types)
return PBEAML(pid, mid, beam_type, xxb, dims, group=group,
so=so, nsm=nsm, comment=comment)
@property
def Type(self):
"""gets Type"""
return self.beam_type
@Type.setter
def Type(self, beam_type):
"""sets Type"""
self.beam_type = beam_type
[docs]
def get_mass_per_lengths(self):
"""
helper method for MassPerLength
a*rho + nsm
"""
rho = self.Rho()
mass_per_lengths = []
for (dim, nsm) in zip(self.dim, self.nsm):
a = _bar_areaL('PBEAML', self.beam_type, dim, self)[0]
try:
mass_per_lengths.append(a * rho + nsm)
except Exception:
msg = "PBEAML a*rho+nsm a=%s rho=%s nsm=%s" % (a, rho, nsm)
raise RuntimeError(msg)
return mass_per_lengths
[docs]
def MassPerLength(self):
r"""
Gets the mass per length :math:`\frac{m}{L}` of the PBEAML.
.. math:: \frac{m}{L} = A(x) \rho + nsm
.. math:: \frac{m}{L} = nsm L + \rho \int \, A(x) dx
"""
mass_per_lengths = self.get_mass_per_lengths()
mass_per_length = integrate_positive_unit_line(self.xxb, mass_per_lengths)
return mass_per_length
[docs]
def Area(self):
r"""
Gets the Area :math:`A` of the PBEAML.
.. math:: A = \int \, A(x) dx
Notes
-----
a spline is fit to :math:`A(x)` and then integrated.
"""
areas = []
for dim in self.dim:
areas.append(_bar_areaL('PBEAML', self.beam_type, dim, self)[0])
try:
A = integrate_unit_line(self.xxb, areas)
except ValueError:
print('PBEAML integration error; pid=%s x/xb=%s areas=%s' % (
self.pid, self.xxb, areas))
assert len(self.xxb) == len(areas)
raise
#A = mean(areas)
return A
#def Mid(self):
# return self.mid
[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
.. warning:: For structural problems, PBEAML entries must
reference a MAT1 material entry
.. warning:: For heat-transfer problems, the MID must
reference a MAT4 or MAT5 material entry.
.. todo:: What happens when there are 2 subcases?
"""
msg = ', which is required by PBEAML mid=%s' % self.mid
self.mid_ref = model.Material(self.mid, msg=msg)
[docs]
def uncross_reference(self) -> None:
"""Removes cross-reference links"""
self.mid = self.Mid()
self.mid_ref = None
[docs]
def verify(self, model, isubcase):
if model.is_thermal_solution(isubcase):
assert self.mid_ref.type in ['MAT4', 'MAT5']
else:
assert self.mid_ref.type in ['MAT1']
[docs]
def J(self):
#j = []
#for unused_dims in self.dim:
# calculate J for the station
#Js = self._J()
#j = integrate_positive_unit_line(self.xxb, Js)
j = None
return j
[docs]
def I11(self):
i1_list = []
for dim in self.dim:
i1 = A_I1_I2_I12(self, self.beam_type, dim)[1]
i1_list.append(i1)
try:
i1 = integrate_positive_unit_line(self.xxb, i1_list)
except:
print(i1_list)
raise
return i1
[docs]
def I22(self):
i2_list = []
for dim in self.dim:
i2 = A_I1_I2_I12(self, self.beam_type, dim)[2]
i2_list.append(i2)
try:
i2 = integrate_positive_unit_line(self.xxb, i2_list)
except:
print(i2_list)
raise
return i2
[docs]
def I12(self):
#i12 = integrate_unit_line(self.xxb,self.i12)
i12 = None
return i12
[docs]
def raw_fields(self):
list_fields = ['PBEAML', self.pid, self.Mid(), self.group, self.beam_type,
None, None, None, None]
#print("xxb=%s so=%s dim=%s nsm=%s" % (
#self.xxb,self.so, self.dim,self.nsm))
dims_equal = True
dim0 = self.dim[0, :]
for dim in self.dim[1:]:
if not np.array_equal(dim0, dim):
dims_equal = False
break
#print(self.get_stats())
if dims_equal and len(self.xxb) == 2 and self.so[0] == self.so[1] and len(self.nsm) == 2 and self.nsm[0] == self.nsm[1]:
list_fields += self.dim[0].tolist() + [self.nsm[0]]
else:
for (i, xxb, so, dim, nsm) in zip(count(), self.xxb, self.so,
self.dim, self.nsm):
if i == 0:
list_fields += dim.tolist() + [nsm]
else:
list_fields += [so, xxb] + dim.tolist() + [nsm]
#raise NotImplementedError('verify PBEAML...')
return list_fields
[docs]
def repr_fields(self):
group = set_blank_if_default(self.group, 'MSCBML0')
list_fields = self.raw_fields()
list_fields[3] = group
return list_fields
[docs]
def write_card(self, size: int=8, is_double: bool=False) -> str:
""".. todo:: having bug with PBEAML"""
card = self.repr_fields()
if size == 8:
return self.comment + print_card_8(card)
return self.comment + print_card_16(card) #is this allowed???
[docs]
def pbeaml_op2_data_to_init(data, valid_types: dict[str, int]):
(pid, mid, group, beam_type, fvalues) = data
group = group.strip()
beam_type = beam_type.strip()
ndim = valid_types[beam_type]
nfvalues = len(fvalues)
nsections = nfvalues // (3 + ndim)
sections = fvalues.reshape(nsections, ndim+3)
#print('sections = \n%s' % sections)
xxb = []
so = []
dims = []
nsm = []
# XXB, SO, NSM, and dimensions
isections = []
for i, section in enumerate(sections):
xxbi = section[1]
#print('PBEAML - i=%s x/xb=%s' % (i, xxbi))
if i > 0 and allclose(xxbi, 0.0):
#print(' PBEAML - skipping i=%s x/xb=%s' % (i, xxbi))
continue
if xxbi in xxb:
#print(" PBEAML - skipping i=%s x/xb=%s because it's a duplicate" % (i, xxbi))
continue
isections.append(i)
so_float = section[0]
if so_float == 0.:
so_string = 'YES'
elif so_float == 1.:
so_string = 'NO'
else:
msg = 'so_float=%r; expected 0.0 or 1.0; data=%s' % (so_float, data)
raise NotImplementedError(msg)
dim = list(section[2:-1])
nsmi = section[-1]
#print(dim, section)
xxb.append(xxbi)
so.append(so_string)
dims.append(dim)
nsm.append(nsmi)
#print('sections2 = \n%s' % sections[isections])
return pid, mid, beam_type, xxb, dims, group, so, nsm
[docs]
def _sort_pbeam(pid: int,
xxb, so, area, i1, i2, i12, j, nsm,
c1, c2, d1, d2, e1, e2, f1, f2,
ensure_xxb_1_section: bool=True) -> tuple[
np.ndarray, np.ndarray, np.ndarray, np.ndarray,
np.ndarray, np.ndarray, np.ndarray, np.ndarray,
np.ndarray, np.ndarray, np.ndarray, np.ndarray,
np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
nxxb = len(xxb)
# sort xxb
ixxb = np.argsort(xxb)
#duplicate_xxb = (len(xxb) == 1)
#if duplicate_xxb and ensure_xxb_1_section:
#ixxb = np.array([0, 0])
self_so = np.array(so, dtype='|U8')[ixxb]
self_xxb = np.array(xxb, dtype='float64')[ixxb]
#print('ixxb = %s' % ixxb)
#print('i12 = %s' % i12)
assert len(area) == nxxb, 'pid=%s len(xxb)=%s len(A =)=%s' % (pid, nxxb, len(area))
assert len(i1) == nxxb, 'pid=%s len(xxb)=%s len(i1 )=%s' % (pid, nxxb, len(i1))
assert len(i2) == nxxb, 'pid=%s len(xxb)=%s len(i2 )=%s' % (pid, nxxb, len(i2))
assert len(i12) == nxxb, 'pid=%s len(xxb)=%s len(i12)=%s' % (pid, nxxb, len(i12))
assert len(j) == nxxb, 'pid=%s len(xxb)=%s len(j =)=%s' % (pid, nxxb, len(j))
assert len(nsm) == nxxb, 'pid=%s len(xxb)=%s len(nsm)=%s' % (pid, nxxb, len(nsm))
self_A = np.array(area, dtype='float64')[ixxb]
self_i1 = np.array(i1, dtype='float64')[ixxb]
self_i2 = np.array(i2, dtype='float64')[ixxb]
self_i12 = np.array(i12, dtype='float64')[ixxb]
self_j = np.array(j, dtype='float64')[ixxb]
self_nsm = np.array(nsm, dtype='float64')[ixxb]
#assert len(area) == nxxb, 'pid=%s len(xxb)=%s len(area)=%s' % (nxxb, len(area))
assert len(c1) == nxxb, 'pid=%s len(xxb)=%s len(c1)=%s' % (pid, nxxb, len(c1))
assert len(c2) == nxxb, 'pid=%s len(xxb)=%s len(c2)=%s' % (pid, nxxb, len(c2))
assert len(d1) == nxxb, 'pid=%s len(xxb)=%s len(d1)=%s' % (pid, nxxb, len(d1))
assert len(d2) == nxxb, 'pid=%s len(xxb)=%s len(d2)=%s' % (pid, nxxb, len(d2))
assert len(e1) == nxxb, 'pid=%s len(xxb)=%s len(e1)=%s' % (pid, nxxb, len(e1))
assert len(e2) == nxxb, 'pid=%s len(xxb)=%s len(e2)=%s' % (pid, nxxb, len(e2))
assert len(f1) == nxxb, 'pid=%s len(xxb)=%s len(f1)=%s' % (pid, nxxb, len(f1))
assert len(f2) == nxxb, 'pid=%s len(xxb)=%s len(f2)=%s' % (pid, nxxb, len(f2))
self_c1 = np.array(c1, dtype='float64')[ixxb]
self_c2 = np.array(c2, dtype='float64')[ixxb]
self_d1 = np.array(d1, dtype='float64')[ixxb]
self_d2 = np.array(d2, dtype='float64')[ixxb]
self_e1 = np.array(e1, dtype='float64')[ixxb]
self_e2 = np.array(e2, dtype='float64')[ixxb]
self_f1 = np.array(f1, dtype='float64')[ixxb]
self_f2 = np.array(f2, dtype='float64')[ixxb]
#if duplicate_xxb:
#self_xxb = np.array([0., 1.], dtype='float64')
out = (
self_xxb, self_so, self_A, self_i1, self_i2, self_i12, self_j, self_nsm,
self_c1, self_c2, self_d1, self_d2, self_e1, self_e2, self_f1, self_f2,
)
return out
[docs]
def _linearly_interpolate(i: int, x: np.ndarray, y: np.ndarray):
"""
For the continuations that have intermediate values of X/XB between 0.0
and 1.0 and use the default option (any of the fields 4 through 9 are blank), a
linear interpolation between the values at ends A and B is performed to obtain
the missing section properties.
"""
inonzero = np.where(y != 0.)[0]
if len(inonzero) == 0:
return 0.
idiff = (inonzero - i)
ineg = (idiff < 0)
ipos = (idiff > 0)
ilow = i + idiff[ineg]
ihigh = i + idiff[ipos]
xlow = x[ilow]
ylow = y[ilow]
xhigh = x[ihigh]
yhigh = y[ihigh]
yi = y[ilow] + (yhigh - ylow) / (xhigh - xlow) * x[i]
assert isinstance(yi[0], float), yi
return yi
[docs]
class PBMSECT(LineProperty):
"""
not done
"""
type = 'PBMSECT'
_properties = ['outp_id']
[docs]
@classmethod
def _init_from_empty(cls):
pid = 1
mid = 2
form = 'FORM'
options = [('OUTP', 10)]
return PBMSECT(pid, mid, form, options, comment='')
[docs]
def _finalize_hdf5(self, encoding):
self.brps = {key : value for key, value in zip(*self.brps)}
self.ts = {key : value for key, value in zip(*self.ts)}
self.inps = {key : value for key, value in zip(*self.inps)}
self.core = {key : value for key, value in zip(*self.core)}
def __init__(self, pid, mid, form, options, comment=''):
LineProperty.__init__(self)
if comment:
self.comment = comment
#: Property ID
self.pid = pid
#: Material ID
self.mid = mid
self.form = form
# integer
self.outp = None
# float
self.nsm = 0.
# int : int
self.brps = {}
self.inps = {}
self.core = {}
# int : floats
self.ts = {}
assert isinstance(options, list), options
for key_value in options:
try:
key, value = key_value
except ValueError:
print(key_value)
raise
key = key.upper()
if key == 'NSM':
self.nsm = float(value)
elif 'INP' in key:
if key.startswith('INP('):
assert key.endswith(')'), 'key=%r' % key
key_id = int(key[4:-1])
self.inps[key_id] = int(value)
else:
if 1 not in self.inps:
self.inps[1] = []
self.inps[1].append(int(value))
elif key == 'OUTP':
self.outp = int(value)
elif key.startswith('BRP'):
if key.startswith('BRP('):
assert key.endswith(')'), 'key=%r' % key
key_id = int(key[4:-1])
self.brps[key_id] = int(value)
else:
self.brps[0] = int(value)
elif key.startswith('T('):
index, out = split_arbitrary_thickness_section(key, value)
self.ts[index] = out
elif key == 'T':
self.ts[1] = float(value)
elif key == 'CORE':
key = 'CORE(1)'
index, out = split_arbitrary_thickness_section(key, value)
self.core[index] = out
elif key.startswith(('CORE(', 'C(')):
index, out = split_arbitrary_thickness_section(key, value)
self.core[index] = out
else:
raise NotImplementedError('PBMSECT.pid=%s key=%r value=%r' % (pid, key, value))
assert self.outp is not None, 'options=%s' % str(options)
self.mid_ref = None
self.outp_ref = None
self.brps_ref = {}
[docs]
def validate(self):
assert self.form in ['GS', 'OP', 'CP'], 'pid=%s form=%r' % (self.pid, self.form)
[docs]
@classmethod
def add_card(cls, card, comment=''):
"""
Adds a PBMSECT card from ``BDF.add_card(...)``
Parameters
----------
card : list[str]
this card is special and is not a ``BDFCard`` like other cards
comment : str; default=''
a comment for the card
"""
line0 = card[0]
if '\t' in line0:
line0 = line0.expandtabs()
bdf_card = BDFCard(to_fields([line0], 'PBMSECT'))
#line0_eq = line0[16:]
lines_joined = ','.join(card[1:]).replace(' ', '').replace(',,', ',')
if lines_joined:
fields = get_beam_sections(lines_joined)
options = [field.split('=', 1) for field in fields]
else:
options = []
pid = integer(bdf_card, 1, 'pid')
mid = integer(bdf_card, 2, 'mid')
form = string_or_blank(bdf_card, 3, 'form')
return PBMSECT(pid, mid, form, options, comment=comment)
#@classmethod
#def add_op2_data(cls, data, comment=''): # pragma: no cover
#pid = data[0]
#mid = data[1]
#group = data[2].strip()
#Type = data[3].strip()
#dim = list(data[4:-1])
#nsm = data[-1]
#print("group = %r" % group)
#print("Type = %r" % Type)
#print("dim = ",dim)
#print(str(self))
#print("*PBMSECT = ", data)
#raise NotImplementedError('PBMSECT not finished...data=%s' % str(data))
#return PBMSECT(pid, mid, group, Type, dim, nsm, comment=comment)
[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 PBMSECT mid=%s' % self.mid
self.mid_ref = model.Material(self.mid, msg=msg)
self.outp_ref = model.Set(self.outp)
self.outp_ref.cross_reference_set(model, 'Point', msg=msg)
self.brps_ref = {}
if len(self.brps):
for key, brpi in self.brps.items():
brpi_ref = model.Set(brpi, msg=msg)
brpi_ref.cross_reference_set(model, 'Point', msg=msg)
self.brps_ref[key] = brpi_ref
[docs]
def plot(self, model, figure_id=1, show=False):
"""
Plots the beam section
Parameters
----------
model : BDF()
the BDF object
figure_id : int; default=1
the figure id
show : bool; default=False
show the figure when done
"""
class_name = self.__class__.__name__
form_map = {
'GS' : 'General Section',
'OP' : 'Open Profile',
'CP' : 'Closed Profile',
}
formi = ' form=%s' % form_map[self.form]
plot_arbitrary_section(
model, self,
self.inps, self.ts, self.brps_ref, self.nsm, self.outp_ref,
figure_id=figure_id,
title=class_name + ' pid=%s' % self.pid + formi,
show=show)
@property
def outp_id(self):
if self.outp_ref is not None:
return self.outp_ref.sid
return self.outp
#@property
#def brp1_id(self):
#if self.brp1_ref is not None:
#return self.brp1_ref.sid
#return self.brp1
[docs]
def uncross_reference(self) -> None:
"""Removes cross-reference links"""
self.mid = self.Mid()
self.mid_ref = None
self.outp_ref = None
self.brps_ref = {}
def _verify(self, xref):
pid = self.Pid()
mid = self.Mid()
#A = self.Area()
#J = self.J()
#nsm = self.Nsm()
#mpl = self.MassPerLength()
assert isinstance(pid, int), 'PBMSECT: pid=%r' % pid
assert isinstance(mid, int), 'PBMSECT: mid=%r' % mid
#assert isinstance(A, float), 'PBMSECT: pid=%r' % A
#assert isinstance(J, float), 'PBMSECT: cid=%r' % J
#assert isinstance(nsm, float), 'PBMSECT: nsm=%r' % nsm
#assert isinstance(mpl, float), 'PBMSECT: mass_per_length=%r' % mpl
[docs]
def Area(self):
"""
Gets the area :math:`A` of the CBEAM.
"""
return 0.
#raise NotImplementedError('Area is not implemented for PBMSECT')
[docs]
def Nsm(self):
"""
Gets the non-structural mass :math:`nsm` of the CBEAM.
"""
return 0.
#raise NotImplementedError('Nsm is not implemented for PBMSECT')
[docs]
def MassPerLength(self):
r"""
Gets the mass per length :math:`\frac{m}{L}` of the CBEAM.
.. math:: \frac{m}{L} = A \rho + nsm
"""
rho = self.Rho()
area = self.Area()
nsm = self.Nsm()
return area * rho + nsm
[docs]
def I11(self):
raise NotImplementedError('I11 is not implemented for PBMSECT')
#def I12(self):
#return self.I12()
[docs]
def J(self):
raise NotImplementedError('J is not implemented for PBMSECT')
[docs]
def I22(self):
raise NotImplementedError('I22 is not implemented for PBMSECT')
[docs]
def raw_fields(self):
"""not done..."""
list_fields = ['PBMSECT', self.pid, self.Mid(), self.form]
return list_fields
[docs]
def repr_fields(self):
"""not done..."""
list_fields = ['PBMSECT', self.pid, self.Mid(), self.form]
return list_fields
[docs]
def write_card(self, size: int=8, is_double: bool=False) -> str:
card = ['PBMSECT', self.pid, self.Mid(), self.form]
end = write_arbitrary_beam_section(
self.inps, self.ts, self.brps, self.nsm, self.outp_id, self.core)
out = self.comment + print_card_8(card) + end
return out
def __repr__(self):
return self.write_card()
[docs]
class PBCOMP(LineProperty):
"""
+--------+------+-----+-----+------+----+-----+--------+-----+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+========+======+=====+=====+======+====+=====+========+=====+
| PBCOMP | PID | MID | A | I1 | I2 | I12 | J | NSM |
+--------+------+-----+-----+------+----+-----+--------+-----+
| | K1 | K2 | M1 | M2 | N1 | N2 | SYMOPT | |
+--------+------+-----+-----+------+----+-----+--------+-----+
| | Y1 | Z1 | C1 | MID1 | | | | |
+--------+------+-----+-----+------+----+-----+--------+-----+
| | Y2 | Z2 | C2 | MID2 | | | | |
+--------+------+-----+-----+------+----+-----+--------+-----+
| | ... | ... | ... | | | | | |
+--------+------+-----+-----+------+----+-----+--------+-----+
"""
type = 'PBCOMP'
[docs]
@classmethod
def _init_from_empty(cls):
pid = 1
mid = 1
y = [1.]
z = [1.]
c = [1.]
mids = [1]
return PBCOMP(pid, mid, y, z, c, mids,
area=0.0, i1=0.0, i2=0.0, i12=0.0, j=0.0, nsm=0.0,
k1=1.0, k2=1.0, m1=0.0, m2=0.0, n1=0.0, n2=0.0,
symopt=0, comment='')
def __init__(self, pid, mid, y, z, c, mids,
area=0.0, i1=0.0, i2=0.0, i12=0.0, j=0.0, nsm=0.0,
k1=1.0, k2=1.0, m1=0.0, m2=0.0, n1=0.0, n2=0.0,
symopt=0, comment=''):
"""
Creates a PBCOMP card
Parameters
----------
pid : int
Property ID
mid : int
Material ID
mids : list[int]
Material ID for the i-th integration point
y / z : list[float]
The (y,z) coordinates of the lumped areas in the element
coordinate system
c : list[float]; default=0.0
Fraction of the total area for the i-th lumped area
default not supported...
area : float
Area of beam cross section
i1 / i2 : float; default=0.0
Area moment of inertia about plane 1/2 about the neutral axis
i12 : float; default=0.0
area product of inertia
j : float; default=0.0
Torsional moment of interia
nsm : float; default=0.0
Nonstructural mass per unit length
k1 / k2 : float; default=1.0
Shear stiffness factor K in K*A*G for plane 1/2
m1 / m2 : float; default=0.0
The (y,z) coordinates of center of gravity of nonstructural mass
n1 / n2 : float; default=0.0
The (y,z) coordinates of neutral axis
symopt : int; default=0
Symmetry option to input lumped areas for the beam cross section
0 < Integer < 5
comment : str; default=''
a comment for the card
"""
LineProperty.__init__(self)
if comment:
self.comment = comment
#: Property ID
self.pid = pid
#: Material ID
self.mid = mid
# Area
self.A = area
self.i1 = i1
self.i2 = i2
self.i12 = i12
#: Polar Moment of Inertia :math:`J`
self.j = j
#: Non-structural mass per unit length :math:`\frac{m}{L}`
self.nsm = nsm
self.k1 = k1
self.k2 = k2
self.m1 = m1
self.m2 = m2
self.n1 = n1
self.n2 = n2
self.symopt = symopt
self.y = y
self.z = z
self.c = c
self.mids = mids
assert 0 <= self.symopt <= 5, 'symopt=%i is invalid; ' % self.symopt
self.mid_ref = None
self.mids_ref = None
[docs]
def validate(self):
assert isinstance(self.mids, list), 'mids=%r type=%s' % (self.mids, type(self.mids))
assert isinstance(self.y, list), 'y=%r type=%s' % (self.y, type(self.y))
assert isinstance(self.z, list), 'z=%r type=%s' % (self.z, type(self.z))
assert isinstance(self.c, list), 'c=%r type=%s' % (self.c, type(self.c))
nmids = len(self.mids)
assert nmids == len(self.y), 'len(mids)=%s len(y)=%s' % (nmids, len(self.y))
assert nmids == len(self.z), 'len(mids)=%s len(z)=%s' % (nmids, len(self.z))
assert nmids == len(self.c), 'len(mids)=%s len(c)=%s' % (nmids, len(self.c))
assert self.symopt in [0, 1, 2, 3, 4, 5], 'symopt=%r' % self.symopt
[docs]
@classmethod
def add_card(cls, card, comment=''):
"""
Adds a PBCOMP card from ``BDF.add_card(...)``
Parameters
----------
card : BDFCard()
a BDFCard object
comment : str; default=''
a comment for the card
"""
pid = integer(card, 1, 'pid')
mid = integer(card, 2, 'mid')
area = double_or_blank(card, 3, 'Area', 0.0)
i1 = double_or_blank(card, 4, 'I1', 0.0)
i2 = double_or_blank(card, 5, 'I2', 0.0)
i12 = double_or_blank(card, 6, 'I12', 0.0)
j = double_or_blank(card, 7, 'J', 0.0)
nsm = double_or_blank(card, 8, 'nsm', 0.0)
k1 = double_or_blank(card, 9, 'k1', 1.0)
k2 = double_or_blank(card, 10, 'k2', 1.0)
m1 = double_or_blank(card, 11, 'm1', 0.0)
m2 = double_or_blank(card, 12, 'm2', 0.0)
n1 = double_or_blank(card, 13, 'n1', 0.0)
n2 = double_or_blank(card, 14, 'n2', 0.0)
symopt = integer_or_blank(card, 15, 'symopt', 0)
y = []
z = []
c = []
mids = []
nfields = len(card) - 17
nrows = nfields // 8
if nfields % 8 > 0:
nrows += 1
for row in range(nrows):
i = 8 * row + 17
yi = double(card, i, 'y' + str(row))
zi = double(card, i + 1, 'z' + str(row))
ci = double_or_blank(card, i + 2, 'c' + str(row), 0.0)
mid = integer_or_blank(card, i + 3, 'mid' + str(row), mid)
y.append(yi)
z.append(zi)
c.append(ci)
mids.append(mid)
return PBCOMP(pid, mid, y, z, c, mids,
area, i1, i2, i12, j, nsm,
k1, k2, m1, m2, n1, n2,
symopt, comment=comment)
@classmethod
def add_op2_data(cls, data, comment=''):
data1, data2 = data
(pid, mid, area, i1, i2, i12, j, nsm, k1, k2, m1, m2, n1, n2, unused_nsections) = data1
y = []
z = []
c = []
mids = []
symopt = 5
for yi, zi, ci, midi in data2:
y.append(yi)
z.append(zi)
c.append(ci)
mids.append(midi)
return PBCOMP(pid, mid, y, z, c, mids,
area, i1, i2, i12, j, nsm,
k1, k2, m1, m2, n1, n2,
symopt, comment=comment)
def _verify(self, xref):
pid = self.Pid()
assert isinstance(pid, int)
[docs]
def MassPerLength(self):
return self.nsm + self.mid_ref.Rho() * self.A
[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 PBCOMP mid=%s' % self.mid
self.mid_ref = model.Material(self.mid, msg=msg)
self.mids_ref = model.Materials(self.mids, msg=msg)
[docs]
def Mids(self):
if self.mids_ref is None:
return self.mids
return [mid.mid for mid in self.mids_ref]
[docs]
def uncross_reference(self) -> None:
"""Removes cross-reference links"""
self.mid = self.Mid()
self.mids = self.Mids()
self.mid_ref = None
self.mids_ref = None
[docs]
def raw_fields(self):
list_fields = ['PBCOMP', self.pid, self.Mid(), self.A, self.i1,
self.i2, self.i12, self.j, self.nsm, self.k1, self.k2,
self.m1, self.m2, self.n1, self.n2, self.symopt, None]
for (yi, zi, ci, mid) in zip(self.y, self.z, self.c, self.Mids()):
list_fields += [yi, zi, ci, mid, None, None, None, None]
return list_fields
[docs]
def repr_fields(self):
area = set_blank_if_default(self.A, 0.0)
j = set_blank_if_default(self.j, 0.0)
i1 = set_blank_if_default(self.i1, 0.0)
i2 = set_blank_if_default(self.i2, 0.0)
i12 = set_blank_if_default(self.i12, 0.0)
nsm = set_blank_if_default(self.nsm, 0.0)
k1 = set_blank_if_default(self.k1, 1.0)
k2 = set_blank_if_default(self.k2, 1.0)
m1 = set_blank_if_default(self.m1, 0.0)
m2 = set_blank_if_default(self.m2, 0.0)
n1 = set_blank_if_default(self.n1, 0.0)
n2 = set_blank_if_default(self.n2, 0.0)
symopt = set_blank_if_default(self.symopt, 0)
list_fields = ['PBCOMP', self.pid, self.Mid(), area, i1, i2, i12, j,
nsm, k1, k2, m1, m2, n1, n2, symopt, None]
for (yi, zi, ci, mid) in zip(self.y, self.z, self.c, self.mids):
ci = set_blank_if_default(ci, 0.0)
list_fields += [yi, zi, ci, mid, None, None, None, None]
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)
#if is_double:
#return self.comment + print_card_double(card)
return self.comment + print_card_16(card)