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

# pylint: disable=C0103,R0914,R0902,R0913
"""
All bar properties are defined in this file.  This includes:
 *   PBAR
 *   PBARL
 *   PBEAM3
 *   PBEND
 *   PBRSECT

All bars are LineProperty objects.
Multi-segment beams are IntegratedLineProperty objects.

shoelace formula
second moment of area on wikipedia
"""
from __future__ import annotations
from itertools import count
from typing import Union, Any, TYPE_CHECKING
from numpy import pi, array
import numpy as np

from pyNastran.bdf.field_writer_8 import set_blank_if_default
from pyNastran.bdf.cards.base_card import Property
from pyNastran.bdf.bdf_interface.assign_type import (
    integer, double, double_or_blank, string, string_or_blank,
    blank, integer_or_double, #integer_or_blank,
)
from pyNastran.utils.mathematics import integrate_unit_line, integrate_positive_unit_line
from pyNastran.bdf import MAX_INT
from pyNastran.bdf.field_writer_8 import print_card_8
from pyNastran.bdf.field_writer_16 import print_card_16
from pyNastran.bdf.bdf_interface.bdf_card import BDFCard
from pyNastran.bdf.bdf_interface.utils import to_fields
from pyNastran.utils.numpy_utils import float_types
if TYPE_CHECKING:  # pragma: no cover
    from pyNastran.bdf.bdf import BDF


[docs] def Iyy_beam(b, h): """gets the Iyy for a solid beam""" return 1 / 12. * b * h ** 3
[docs] def get_inertia_rectangular(sections): """ Calculates the moment of inertia for a section about the CG. Parameters ---------- sections : [[b,h,y,z]_1,...] [[b,h,y,z]_1,...] y,z is the centroid (x in the direction of the beam, y right, z up) Returns ------- interia_parameters : list[Area, Iyy, Izz, Iyz] the inertia parameters .. seealso:: http://www.webs1.uidaho.edu/mindworks/Machine_Design/Posters/PDF/Moment%20of%20Inertia.pdf """ As = [] Ax = 0. Ay = 0. for section in sections: (b, h, x, y) = section A = b * h As.append(A) Ax += A * x Ay += A * y xcg = Ax / A ycg = Ay / A Axx = 0. Ayy = 0. Axy = 0. for (i, section) in enumerate(sections): (b, h, x, y) = section #A = b*h #As.append(A) Axx += As[i] * (x - xcg) ** 2 Ayy += As[i] * (y - ycg) ** 2 Axy += As[i] * (x - xcg) * (y - ycg) Ixx = Axx / A Iyy = Ayy / A Ixy = Axy / A return (A, Ixx, Iyy, Ixy)
[docs] def _IAreaL(prop, dim): # type: (Any, list[float]) -> tuple[float, float, float, float] beam_type = prop.beam_type if beam_type == 'ROD': A, I1, I2, I12 = rod_section(prop.type, prop.beam_type, dim, prop) elif beam_type == 'TUBE': A, I1, I2, I12 = tube_section(prop.type, prop.beam_type, dim, prop) elif beam_type == 'TUBE2': A, I1, I2, I12 = tube2_section(prop.type, prop.beam_type, dim, prop) elif beam_type == 'I': # | ------------ # | | A | d6 # | ------------ # | >| |<--d3 # | |B| "I" beam # | d1 | | # | | | # | ---------- # | | C | d5 # | ---------- sections = [] h1 = dim[5] # d2 w1 = dim[2] y1 = dim[0] / 2. - h1 sections.append([w1, h1, 0., y1]) h3 = dim[4] w3 = dim[1] #y3 = -dim[0] / 2. + h3 sections.append([w3, h3, 0., y1]) h2 = dim[0] - h1 - h3 w2 = dim[3] # d1 sections.append([w2, h2, 0., 0.]) (A, Iyy, Izz, Iyz) = get_inertia_rectangular(sections) assert Iyz == 0. elif beam_type == 'BAR': #: *-------* #: | | #: | BAR |h1 #: | | #: *-------* #: w1 #: I_{xx}=\frac{bh^3}{12} #: I_{yy}=\frac{hb^3}{12} h1 = dim[1] w1 = dim[0] A = h1 * w1 Iyy = 1 / 12. * w1 * h1 ** 3 Izz = 1 / 12. * h1 * w1 ** 3 Iyz = 0. #: .. todo:: is the Ixy of a bar 0 ??? else: # pragma: no cover msg = 'beam_type=%r is not supported for %s class...' % ( beam_type, prop.type) raise NotImplementedError(msg) return (A, Iyy, Izz, Iyz)
[docs] class LineProperty(Property): def __init__(self): self.beam_type = None self.A = None self.i1 = None self.i2 = None self.i12 = None self.j = None self.nsm = None Property.__init__(self) self.mid_ref = None #def D_bending(self): #pass #def D_axial(self): #pass #def D_thermal(self): #pass #def D_shear(self): #pass
[docs] def Area(self) -> float: """gets area""" return self.A
[docs] def Nsm(self) -> float: """gets nonstructural mass per unit length""" return self.nsm
[docs] def J(self) -> float: """gets J""" return self.j
[docs] def I11(self) -> float: """gets I11""" return self.i1
[docs] def I22(self) -> float: """gets I22""" return self.i2
[docs] def Rho(self) -> float: """gets the material density""" return self.mid_ref.rho
[docs] def E(self) -> float: """gets the material Young's ratio""" return self.mid_ref.E
[docs] def G(self) -> float: """gets the material Shear ratio""" return self.mid_ref.G
[docs] def Nu(self) -> float: """gets the material Poisson's ratio""" return self.mid_ref.nu
[docs] def A_I1_I2_I12(prop, beam_type: str, dim: list[float]) -> tuple[float, float, float, float]: r""" :: BAR 2 ^ | *---|--* | | | | | | |h *-----------> 1 | | | b | *------* .. math:: I_1 = \frac{1}{12} b h^3 .. math:: I_2 = \frac{1}{12} h b^3 """ if beam_type == 'ROD': A, I1, I2, I12 = rod_section(prop.type, beam_type, dim, prop) elif beam_type == 'TUBE': A, I1, I2, I12 = tube_section(prop.type, beam_type, dim, prop) elif beam_type == 'BAR': A, I1, I2, I12 = bar_section(prop.type, beam_type, dim, prop) elif beam_type == 'BOX': A, I1, I2, I12 = box_section(prop.type, beam_type, dim, prop) elif beam_type == 'L': A, I1, I2, I12 = l_section(prop.type, beam_type, dim, prop) else: # pragma: no cover msg = 'A_I1_I2_I12; beam_type=%s is not supported for %s class...' % ( beam_type, prop.type) raise NotImplementedError(msg) assert A == prop.Area(), prop return A, I1, I2, I12
[docs] def _bar_areaL(class_name: str, beam_type: str, dim: list[float], prop: Any) -> tuple[float, float, float, float]: """ Area(x) method for the PBARL and PBEAML classes (pronounced **Area-L**) Parameters ---------- dim : list[float] a list of the dimensions associated with **beam_type** Returns ------- Area : float Area of the given cross section defined by **self.beam_type** Notes ----- internal method """ if beam_type == 'ROD': A, I1, I2, I12 = rod_section(class_name, beam_type, dim, prop) elif beam_type == 'TUBE': A, I1, I2, I12 = tube_section(class_name, beam_type, dim, prop) elif beam_type == 'TUBE2': A, I1, I2, I12 = tube2_section(class_name, beam_type, dim, prop) elif beam_type == 'I': A, I1, I2, I12 = i_section(class_name, beam_type, dim, prop) elif beam_type == 'I1': A, I1, I2, I12 = i1_section(class_name, beam_type, dim, prop) elif beam_type == 'L': A, I1, I2, I12 = l_section(class_name, beam_type, dim, prop) elif beam_type == 'CHAN': A, I1, I2, I12 = chan_section(class_name, beam_type, dim, prop) elif beam_type == 'CHAN1': A, I1, I2, I12 = chan1_section(class_name, beam_type, dim, prop) elif beam_type == 'CHAN2': A, I1, I2, I12 = chan2_section(class_name, beam_type, dim, prop) elif beam_type == 'T': A, I1, I2, I12 = t_section(class_name, beam_type, dim, prop) elif beam_type == 'T1': A, I1, I2, I12 = t1_section(class_name, beam_type, dim, prop) elif beam_type == 'T2': A, I1, I2, I12 = t2_section(class_name, beam_type, dim, prop) elif beam_type == 'BOX': A, I1, I2, I12 = box_section(class_name, beam_type, dim, prop) elif beam_type == 'BOX1': A, I1, I2, I12 = box1_section(class_name, beam_type, dim, prop) elif beam_type == 'BAR': A, I1, I2, I12 = bar_section(class_name, beam_type, dim, prop) elif beam_type == 'CROSS': A, I1, I2, I12 = cross_section(class_name, beam_type, dim, prop) elif beam_type == 'H': A, I1, I2, I12 = h_section(class_name, beam_type, dim, prop) elif beam_type == 'Z': A, I1, I2, I12 = zee_section(class_name, beam_type, dim, prop) elif beam_type == 'HEXA': A, I1, I2, I12 = hexa_section(class_name, beam_type, dim, prop) elif beam_type == 'HAT': A, I1, I2, I12 = hat_section(class_name, beam_type, dim, prop) elif beam_type == 'HAT1': A, I1, I2, I12 = hat1_section(class_name, beam_type, dim, prop) elif beam_type == 'DBOX': A, I1, I2, I12 = dbox_section(class_name, beam_type, dim, prop) else: # pragma: no cover msg = 'areaL; beam_type=%s is not supported for %s class...' % ( beam_type, class_name) raise NotImplementedError(msg) assert A > 0, 'beam_type=%r dim=%r A=%s\n%s' % (beam_type, dim, A, prop) return A, I1, I2, I12
[docs] def rod_section(class_name, beam_type, dim, prop): # This is a circle if you couldn't tell... # __C__ # / \ # | | # F D # | | # \ / # \_E_/ # R = dim[0] A = pi * R ** 2 I1 = A * R ** 2 / 4. I2 = I1 I12 = 0. return A, I1, I2, I12
[docs] def tube_section(class_type: str, beam_type: str, dim: np.ndarray, prop): r_outer, r_inner = dim assert r_outer > r_inner, 'TUBE; r_outer=%s r_inner=%s' % (r_outer, r_inner) A = pi * (r_outer ** 2 - r_inner ** 2) I1 = pi * (r_outer ** 4 - r_inner ** 4) / 4. I2 = I1 I12 = 0. return A, I1, I2, I12
[docs] def tube2_section(class_type: str, beam_type: str, dim, prop): r_outer, thick = dim r_inner = r_outer - thick assert r_outer > r_inner, 'TUBE2; r_outer=%s r_inner=%s' % (r_outer, r_inner) # same as tube A = pi * (r_outer ** 2 - r_inner ** 2) I1 = pi * (r_outer ** 4 - r_inner ** 4) / 4. I2 = I1 I12 = 0. return A, I1, I2, I12
[docs] def bar_section(class_type: str, beam_type: str, dim, prop): #per https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/element.pdf # <------> D1 # # F------C ^ # | | | # | | | D2 # | | | # E------D v b, h = dim A = b * h I1 = b * h ** 3 / 12. I2 = h * b ** 3 / 12. I12 = 0. #J = b*h**3*(1/3. - 0.21*h/b*(1-h**4/(12*b**4))) return A, I1, I2, I12
[docs] def box_section(class_name: str, beam_type: str, dim, prop): # # +----------+ ^ ^ # | | | d3 | # | +----+ | v | # | | | | | d2 # | +----+ | | # | | | # +----------+ v # <-> d4 # <----d1----> # dim1, dim2, dim3, dim4 = dim assert dim1 > 2.*dim4, 'BOX; required: dim1 > 2*dim4; dim1=%s dim4=%s\n%s' % (dim1, dim4, prop) assert dim2 > 2.*dim3, 'BOX; required: dim2 > 2*dim3; dim2=%s dim3=%s\n%s' % (dim2, dim3, prop) # per https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/element.pdf b = dim1 h = dim2 t1 = dim3 t2 = dim4 bi = b - 2 * t2 hi = h - 2 * t1 A = b * h - bi * hi assert A > 0, A I1 = (b * h ** 3 - bi * hi ** 3) / 12 I2 = (h * b ** 3 - hi * bi ** 3) / 12 I12 = 0. #j = (2*t2*t1*(b-t2)**2*(h-t1)**2)/(b*t2+h*t1-t2**2-t1**2) return A, I1, I2, I12
[docs] def box1_section(class_name: str, beam_type: str, dim, prop): dim1, dim2, dim3, dim4, dim5, dim6 = dim h1 = dim3 # top w1 = dim1 h2 = dim4 # btm A1 = (h1 + h2) * w1 h3 = dim2 - h1 - h2 # left w3 = dim6 w4 = dim5 # right A2 = h3 * (w3 + w4) A = A1 + A2 I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def cross_section(class_type: str, beam_type: str, dim, prop): dim1, dim2, dim3, dim4 = dim h1 = dim3 w1 = dim2 h2 = dim4 w2 = dim1 A = h1 * w1 + h2 * w2 I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def h_section(class_name: str, beam_type: str, dim, prop): dim1, dim2, dim3, dim4 = dim h1 = dim3 w1 = dim2 h2 = dim4 w2 = dim1 A = h1 * w1 + h2 * w2 I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def t_section(class_name: str, beam_type: str, dim, prop): # per https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/element.pdf # ^ y # | # | # <----d1=d---> # +-----------+ ^ ^ # | 1 | | | d3=tf ----> z # +---+ +----+ | v # | | | ^ # | | | d2 | # | | | | hw # | | | | # +--+ v v # # <--> d4=tw # dim1, dim2, dim3, dim4 = dim d = dim1 # bf tf = dim3 tw = dim4 h = dim2 - 0.5 * tf hw = dim2 - tf #print('tf=%s tw=%s bf=%s' % (tf, tw, bf)) #print('h=%s hw=%s' % (h, hw)) A = d*tf + hw*tw yna = hw*tw*(hw+tf)/(2*A) i1 = ( (d*tf**3 + tw*hw**3) / 12. + hw*tw*(yna + 0.5 * (hw +tf))**2 + d*tf*yna**2 ) i2 = (tf*d**3 + hw*tw**3)/12. i12 = 0. #j = 1/3 * (tf**3*d + tw**3 * h) return A, i1, i2, i12
[docs] def t1_section(class_name: str, beam_type: str, dim, prop): dim1, dim2, dim3, dim4 = dim h1 = dim1 w1 = dim3 h2 = dim4 w2 = dim2 A = h1 * w1 + h2 * w2 I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def t2_section(class_name: str, beam_type: str, dim, prop): # ^ y # | # | # +--+ ^ v # | | | ^ # |1 | | d2 | # | | | | hw=bweb # | | | | # +---+ +----+ | v # | 2 | | | | | d3=tf ----> z # +---+--+----+ v v # <----d1=d---> # # <--> d4=tweb # dim1, dim2, dim3, dim4 = dim ball = dim1 hall = dim2 tflange = dim3 tweb = dim4 # bweb = hall - tflange bflange = ball - tweb #print(f'hall={hall} ball={ball} tweb={tweb} bflange={bflange}') assert hall - tflange > 0, f'bweb={hall-tflange} hall(dim2)={hall} tflange(dim3)={tflange}' assert ball - tweb > 0, f'bflange={bflange} ball(dim1)={ball} tweb(dim4)={tweb}' A = tweb * hall + tflange * bflange I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def zee_section(class_name: str, beam_type: str, dim, prop): # bflange bweb # <--d1-><--d2--> # +-------------+ ^ # | | | # +------+ | ^ | # | | | | # | | | d3=hweb | d4 = hall # | | v | # | +---------+ | # | | | # +----------------+ v # dim1, dim2, dim3, dim4 = dim bflange = dim1 bweb = dim2 hweb = dim3 hall = dim4 tflange = hall - hweb # actually 2*tflange A = bflange * tflange + hall * bweb I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def hexa_section(class_name: str, beam_type: str, dim, prop): # _______ # / \ ^ # / \ | # * * | d3 = h # \ / | # \ / | # \______/ v # |d1| = wtri # <-----d2----> = wall # dim1, dim2, dim3 = dim hbox = dim3 wall = dim2 wtri = dim1 wbox = wall - 2 * wtri assert wbox > 0, 'wbox=%s' % (wbox) A = hbox * wbox + hbox * wtri #print('hbox=%s wbox=%s hbox*wbox=%s 2*wtri*hbox=%s A=%s' % ( #hbox, wbox, hbox*wbox, 2*wtri*hbox, A)) I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def hat_section(class_name: str, beam_type: str, dim, prop): # # <--------d3-------> # # +-----------------+ ^ # d4 | A | d4 | # <----> +-d2-+-------+-d2-+ <----> | # | B | | B | | d1 # +------+----+ +----+------+ | # | C | | C | t=d2 | # +-----------+ +-----------+ v dim1, dim2, dim3, dim4 = dim t = dim2 wa = dim3 assert wa > 2.*t, f'HAT; required: dim3 > 2*dim2; dim2={t} dim3={wa}; delta={wa-2*t}\n{prop}' #DIM3, CAN NOT BE LESS THAN THE SUM OF FLANGE #THICKNESSES, 2*DIM2 hb = dim1 - 2. * t wc = dim4 + t assert hb > 0., f'HAT; required hb=dim1-2*dim2={hb} dim1={dim1} dim2={dim2}' A = wa * t + (2. * wc * t) + (2. * hb * t) I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def hat1_section(class_name: str, beam_type: str, dim, prop): # per https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/element.pdf # # <-----d3=wmid-----> # # +-----------------+ ^ # d4 | | | # +----+-------+-d4-+ d4=t | # | | | | | d2=hall # +------+----+ x +----+------+ | # | | | | t=d2 | # +-----------+-------+-----------+ | ^ # | | | | # | | | | d5=hbox # +-------------------------------+ v v # # <------------d1=wbox------------> dim1, dim2, dim3, dim4, dim5 = dim wbox = dim1 hall = dim2 unused_wmid = dim3 t = dim4 hbox = dim5 hweb = hall - hbox - t assert hweb > 0, hweb Abox = hbox * wbox # we do a first pass assuming the hat is flat and then # add the leftover web sections Ahat = wbox * t + 2 * hweb * t A = Abox + Ahat b1 = dim1 h = dim2 b2 = dim3 t2 = dim4 t1 = dim5 A0 = b1 * t1 + t2 * (b1 - b2) + 2 * t2 * (h - t1) + t2 * (b2 - 2 * t2) assert np.allclose(A, A0), 'A=%s A0=%s' % (A, A0) #assert dim2 > dim4+dim5, 'HAT1; required: dim2 > dim4+dim5; dim2=%s dim4=%s; dim5=%s\n%s' % (dim2, dim4, dim5, prop) #*DIM4+DIM5, CAN NOT BE LARGER THAN THE HEIGHT OF #THE HAT, DIM2. #h1 = w # upper, horizontal lower bar (see HAT) #w1 = w0 - w3 #A = 2. * (h0 * w0 + h1 * w1 + h2 * w2 + h3 * w3) I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def i_section(class_name: str, beam_type: str, dim, prop): # per https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/element.pdf # <----d3=b----> # # 1------------2 ^ # | B | d6=tb | # 12---11---4---3 | # | | | # | | | d1=h # | C | <--- d4=tw | # | | | # 9---10---5---6 | # | A | d5=ta | # 8------------7 v # # <----d2=a----> # dim1, dim2, dim3, dim4, dim5, dim6 = dim h = dim1 a = dim2 b = dim3 tw = dim4 ta = dim5 tb = dim6 hw = h - (ta + tb) hf = h - 0.5 * (ta + tb) assert hw > 0, 'hw=%s' % hw assert hf > 0, 'hf=%s' % hf A = ta * a + hw * tw + b * tb yc = (0.5 * hw * (hw + ta)*tw + hf*tb*b) / A #ys = tb * hf * b**3/(tb * b**3 + ta * a**a) #yna = yc - ys i1 = ( 1 / 12 * (h*tb**3 + a*ta**3 + tw*hw**3) + (hf-yc)**2*b*tb+yc**2*a*ta + (yc - 0.5*(hw+ta))**2*hw*tw ) i2 = (b**3 * tb + ta * a**3 + hw*tw**3) / 12. i12 = 0. #j = 1/3 * (tb**3*b + ta**3*a + tw**3*hf) #assert np.allclose(i1+i2, j), 'I1+I2=%s+%s=%s J=%s' % (i1, i2, i1 + i2, j) assert dim1 > dim5+dim6, 'I; required: dim1 > dim5+dim6; dim1=%s dim5=%s dim6=%s\n%s' % (dim1, dim5, dim6, prop) #THE SUM OF THE FLANGE THICKNESSES,DIM5 + DIM6, #CAN NOT BE GREATER THAT THE WEB HEIGHT, DIM1. return A, i1, i2, i12
[docs] def i1_section(class_name: str, beam_type: str, dim, prop): # # d1/2 d2 d1/2 # <---><---><---> # # 1--------------2 ^ # | A | | # 12---11---4-----3 | # | | ^ | # | | | | d4 # | B | d3 | | # | | v | # 9----10---5----6 | # | C | | # 8--------------7 v # # <----d2----> # # # h1 = hA = (d4 - d3) / 2 # h2 = hB = d3 # # w1 = wA = d1 + d2 # w2 = d2 # # A3 = A1 # A = A1 + A2 + A3 dim1, dim2, dim3, dim4 = dim w1 = dim1 + dim2 h1 = (dim4 - dim3) / 2. w2 = dim2 h2 = dim3 A = 2. * (h1 * w1) + h2 * w2 I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def l_section(class_type: str, beam_type: str, dim, prop): # per https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/element.pdf # # D4 # F---C ^ # | | | # | 2 | | # | | | D2 # +---+---+ | # |1 D3 | | # E-------D v # # <------> D1 # dim1, dim2, dim3, dim4 = dim t1 = dim3 t2 = dim4 b = dim1 - 0.5 * t2 h = dim2 - 0.5 * t1 h2 = dim2 - t1 b1 = dim1 - t2 A = (b + 0.5 * t2) * t1 + h2 * t2 yc = t2*h2 * (h2 + t1) / (2 * A) zc = t1*b1 * (b1 + t2) / (2 * A) i1 = ( t1 ** 3 * (b + 0.5 * t2) / 12. + t1 * (b + 0.5 * t2) * yc ** 2 + t2 * h ** 3 / 12 + h2 * t2 * (0.5 * (h2 + t1) - yc) ** 2 ) i2 = ( t2 ** 3 * h2 / 12. + t1*(b + 0.5 * t2) ** 3 / 12. + t1*(b + 0.5 * t2) * (0.5 * b1 - zc) ** 2 # zc is z2 in the docs... ) i12 = ( zc * yc * t1 * t2 - b1 * t1 * yc * (0.5 * (b1 + t2) - zc) - h2 * t2 * zc * (0.5 * (h2 + t1) - yc) ) #j = 1/3 * (t1**3*b + t2**3 * h) #k1 = h2 * t2 / A #k2 = b1 * t1 / A #yna = yc #zna = zc return A, i1, i2, i12
[docs] def chan_section(class_name: str, beam_type: str, dim, prop): # # +-+----+ ^ ^ # | | | | | d4 = tf # | +----+ | v # | | | ^ # | |<-- d3 | | # | | tw | d2 = h | hw # | | | | # | +----+ | v # | | | | # +-+----+ v # <-bf-> # <--d1--> # dim1, dim2, dim3, dim4 = dim tw = tweb = dim3 tf = tflange = dim4 bflange_tweb = dim1 bflange = bflange_tweb - tweb hweb_all = dim2 hweb = dim2 - 2. * tf #print(f'tflange={tflange} bflage={bflange} hweb_all={hweb_all} hweb={hweb} tweb={tweb}') A0 = 2 * (tflange * bflange) + tweb * hweb_all d = dim2 - 2 * tf # per https://docs.plm.automation.siemens.com/data_services/resources/nxnastran/10/help/en_US/tdocExt/pdf/element.pdf #b = dim1 - 0.5 * tw #h = dim2 - tf #bf = dim1 - tw #hw = dim2 - 2 * tf #A = 2 * tf * bf + (h + tf) * tweb # I think tt is tf... #zc = bf * tf * (bf + tw) / A #zs = b**2 * tf / (2 * b * tw + h * tf / 3) #i1 = ( #h ** 2 * tf * bf / 2 #+ bf * tf ** 3 / 6 #+ (h + tf) ** 3 * tw / 12 #) #i2 = ( #(h + tf) * tw**3/12 #+ bf**3 * tf / 6 #+ 0.5 * (bf + tw) ** 2 * bf * tf #- zc ** 2 * A #) #j = (2 * b * tf **3 + h * tw ** 3) / 3 # warping coefficient for the cross section relative to the shear center #iw = tf * b**3 * h**2 / 12 * (2 * tw * h + 3 * tf * b) / (tw * h + 6 * tf * b) #k1 = tw * hw / A #k2 = 2 * tf * bf / A #zna = zc + zs hweb = dim2 - 2 * tf A1 = 2 * tf * dim1 + hweb * tweb A = A1 I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def chan1_section(class_name: str, beam_type: str, dim, prop): # # +--------+ ^ # | | | # +---+----+ | # | | | ^ # | | | | # | | | d4 = h | d3 = hw = h2 # | | | | # +---+----+ | v # | | | # +--------+ v # <---w1---> # <--><----> # dim2 dim1 # tw bf dim1, dim2, dim3, dim4 = dim tweb = dim2 bflange = dim1 h_all = dim4 h_inner = dim3 h1 = h_all - h_inner # 2*tflange assert h_all > h_inner, f'h_all(dim4)={h_all} h_inner(dim3)={h_inner}' tflange = h1 / 2. w_all = bflange + tweb # bf + tw A = h1 * w_all + h_inner * tweb #print(f'h_all={h_all} h_inner={h_inner} tweb={tweb} tflange={tflange} bflange={bflange}') A2 = dim2 * dim3 + (h_all - dim3) * (dim1 + dim2) assert np.allclose(A, A2), 'A=%s A2=%s' % (A, A2) I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def chan2_section(class_name: str, beam_type: str, dim, prop): # d1 d1 # <--> <--> # +--+ +--+ ^ # | | | | | # | | | | | d3 # +--+------+--+ ^ | # | | | d2 | # +------------+ v v # # <-----d4-----> # dim1, dim2, dim3, dim4 = dim hupper = dim3 - dim2 hlower = dim2 wlower = dim4 wupper = dim1 A = 2 * hupper * wupper + hlower * wlower h2 = dim2 w2 = dim4 h1 = dim3 - h2 w1 = dim1 * 2 A2 = h1 * w1 + h2 * w2 A3 = 2 * dim1 * dim3 + (dim4 - 2 * dim1) * dim2 assert np.allclose(A, A2), 'A=%s A2=%s A3=%s' % (A, A2, A3) assert np.allclose(A, A3), 'A=%s A2=%s A3=%s' % (A, A2, A3) I1 = I2 = I12 = None return A, I1, I2, I12
[docs] def dbox_section(class_name: str, beam_type: str, dim, prop): #DBOX = ((DIM2*DIM3)-((DIM2-DIM7-DIM8)*(DIM3-((0.5*DIM5)+DIM4)))) + # (((DIM1-DIM3)*DIM2)-((DIM2-(DIM9+DIM10))*(DIM1-DIM3-(0.5*DIM5)-DIM6))) # # |--2--|---5---| # | | | # 1 3 6 # | | | # |--4--|---7---| # dim1, dim2, dim3, dim4, dim5, dim6, dim7, dim8, dim9, dim10 = dim #0,1,2,6,11 #1,2,3,7,12 htotal = dim2 wtotal = dim1 wleft = dim3 - dim4 - dim5 / 2. wright = dim1 - dim3 - dim6 - dim5 / 2. assert wleft > 0, wleft #assert wright > 0, wright #A1 = dim4 * htotal #A2 = dim7 * wleft #A3 = dim5 * htotal #A4 = dim8 * wleft #A5 = dim9 * wright #A6 = dim6 * htotal #A7 = dim10 * wright #A = A1 + A2 + A3 + A4 + A5 + A6 + A7 A1 = dim4 * htotal A2 = wleft * dim7 A3 = dim5 * htotal A4 = wleft * dim8 A5 = wright * dim9 A6 = dim6 * htotal A7 = wright * dim10 A = A1 + A2 + A3 + A4 + A5 + A6 + A7 z1 = dim4 / 2 z2 = dim4 + wleft / 2 z3 = dim3 z4 = z2 z5 = dim3 + dim5 / 2 + wright / 2 z6 = dim1 - dim6 / 2 z7 = z5 y1 = htotal / 2 y2 = htotal - dim7 / 2 y3 = y1 y4 = dim8 / 2 y5 = htotal - dim9 / 2. y6 = y1 y7 = dim10 / 2 cg_y = (A1 * y1 + A2 * y2 + A3 * y3 + A4 * y4 + A5 * y5 + A6 * y6 + A7 * y7) / A cg_z = (A1 * z1 + A2 * z2 + A3 * z3 + A4 * z4 + A5 * z5 + A6 * z6 + A7 * z7) / A dy1 = y1 - cg_y dy2 = y2 - cg_y dy3 = y3 - cg_y dy4 = y4 - cg_y dy5 = y5 - cg_y dy6 = y6 - cg_y dy7 = y7 - cg_y dz1 = z1 - cg_z dz2 = z2 - cg_z dz3 = z3 - cg_z dz4 = z4 - cg_z dz5 = z5 - cg_z dz6 = z6 - cg_z dz7 = z7 - cg_z assert dz1 < 0., dz1 assert dy2 > 0., dy2 # inertia taken about the centroid # 1/12 * b * h^3 = 1/12 * A * h^2 Iy1 = A1 * htotal ** 2 Iy2 = A2 * dim7 ** 2 Iy3 = A3 * htotal ** 2 Iy4 = A4 * dim8 ** 2 Iy5 = A5 * dim9 ** 2 Iy6 = A6 * htotal ** 2 Iy7 = A7 * dim10 ** 2 dI1 = A1 * dy1 + A2 * dy2 + A3 * dy3 + A4 * dy4 + A5 * dy5 + A6 * dy6 + A7 * dy7 I1 = (Iy1 + Iy2 + Iy3 + Iy4 + Iy5 + Iy6 + Iy7) / 12. + dI1 # 1/12 * b^3 * h = 1/12 * A * b^2 Iz1 = A1 * dim4 ** 2 Iz2 = A2 * wleft ** 2 Iz3 = A3 * dim5 ** 2 Iz4 = A4 * wleft ** 2 Iz5 = A5 * wright ** 2 Iz6 = A6 * dim6 ** 2 Iz7 = A7 * wright ** 2 dI2 = A1 * dz1 + A2 * dz2 + A3 * dz3 + A4 * dz4 + A5 * dz5 + A6 * dz6 + A7 * dz7 I2 = (Iz1 + Iz2 + Iz3 + Iz4 + Iz5 + Iz6 + Iz7) / 12. + dI2 I12 = None return A, I1, I2, I12
[docs] class IntegratedLineProperty(LineProperty): def __init__(self): self.xxb = None self.A = None self.j = None self.i1 = None self.i2 = None self.i12 = None LineProperty.__init__(self)
[docs] def Area(self) -> float: A = integrate_positive_unit_line(self.xxb, self.A) return A
[docs] def J(self) -> float: J = integrate_positive_unit_line(self.xxb, self.j) return J
[docs] def I11(self) -> float: i1 = integrate_positive_unit_line(self.xxb, self.i1) return i1
[docs] def I22(self) -> float: i2 = integrate_positive_unit_line(self.xxb, self.i2) return i2
[docs] def I12(self) -> float: i12 = integrate_unit_line(self.xxb, self.i12) return i12
[docs] def Nsm(self) -> float: nsm = integrate_positive_unit_line(self.xxb, self.nsm) return nsm
[docs] class PBAR(LineProperty): """ Defines the properties of a simple beam element (CBAR entry). +------+-----+-----+-----+----+----+----+-----+-----+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +======+=====+=====+=====+====+====+====+=====+=====+ | PBAR | PID | MID | A | I1 | I2 | J | NSM | | +------+-----+-----+-----+----+----+----+-----+-----+ | | C1 | C2 | D1 | D2 | E1 | E2 | F1 | F2 | +------+-----+-----+-----+----+----+----+-----+-----+ | | K1 | K2 | I12 | | | | | | +------+-----+-----+-----+----+----+----+-----+-----+ .. todo:: support solution 600 default do a check for mid -> MAT1 for structural do a check for mid -> MAT4/MAT5 for thermal """ type = 'PBAR' pname_fid_map = { # 1-based 4 : 'A', 'A' : 'A', 5 : 'i1', 'I1' : 'i1', 6 : 'i2', 'I2' : 'i2', 7 : 'j', 'J' : 'j', 10 : 'c1', 11 : 'c2', 12 : 'd1', 13 : 'd2', 14 : 'e1', 15 : 'e2', 16 : 'f1', 17 : 'f2', 18 : 'k1', 19 : 'k1', 20 : 'i12', 'I12' : 'i12', }
[docs] @classmethod def export_to_hdf5(cls, h5_file, model, pids): """exports the properties in a vectorized way""" #comments = [] mids = [] A = [] J = [] I = [] c = [] d = [] e = [] f = [] k = [] nsm = [] for pid in pids: prop = model.properties[pid] #comments.append(prop.comment) mids.append(prop.mid) A.append(prop.A) I.append([prop.i1, prop.i2, prop.i12]) J.append(prop.j) c.append([prop.c1, prop.c2]) d.append([prop.d1, prop.d2]) e.append([prop.e1, prop.e2]) f.append([prop.f1, prop.f2]) ki = [] if prop.k1 is None: ki.append(np.nan) else: ki.append(prop.k1) if prop.k2 is None: ki.append(np.nan) else: ki.append(prop.k2) k.append(ki) nsm.append(prop.nsm) #h5_file.create_dataset('_comment', data=comments) h5_file.create_dataset('pid', data=pids) h5_file.create_dataset('mid', data=mids) h5_file.create_dataset('A', data=A) h5_file.create_dataset('J', data=J) h5_file.create_dataset('I', data=I) h5_file.create_dataset('c', data=c) h5_file.create_dataset('d', data=d) h5_file.create_dataset('e', data=e) h5_file.create_dataset('f', data=f) h5_file.create_dataset('k', data=k) h5_file.create_dataset('nsm', data=nsm)
#h5_file.create_dataset('_comment', data=comments) def __init__(self, pid, mid, A=0., i1=0., i2=0., i12=0., j=0., nsm=0., c1=0., c2=0., d1=0., d2=0., e1=0., e2=0., f1=0., f2=0., k1=1.e8, k2=1.e8, comment=''): """ Creates a PBAR card Parameters ---------- pid : int property id mid : int material id area : float area i1, i2, i12, j : float moments of inertia nsm : float; default=0. nonstructural mass per unit length c1/c2, d1/d2, e1/e2, f1/f2 : float the y/z locations of the stress recovery points c1 - point C.y c2 - point C.z k1 / k2 : float; default=1.e8 Shear stiffness factor K in K*A*G for plane 1/2. comment : str; default='' a comment for the card """ LineProperty.__init__(self) if comment: self.comment = comment #: property ID -> use Pid() self.pid = pid #: material ID -> use Mid() self.mid = mid #: Area -> use Area() self.A = A #: I1 -> use I1() self.i1 = i1 #: I2 -> use I2() self.i2 = i2 #: I12 -> use I12() self.i12 = i12 #: Polar Moment of Inertia J -> use J() #: default=1/2(I1+I2) for SOL=600, otherwise 0.0 #: .. todo:: support SOL 600 default self.j = j #: nonstructral mass -> use Nsm() self.nsm = nsm self.c1 = c1 self.c2 = c2 self.d1 = d1 self.d2 = d2 self.e1 = e1 self.e2 = e2 self.f1 = f1 self.f2 = f2 # K1/K2 must be blank #: default=infinite; assume 1e8 self.k1 = k1 #: default=infinite; assume 1e8 self.k2 = k2 self.mid_ref = None
[docs] def validate(self): if self.i1 < 0.: raise ValueError('I1=%r must be greater than or equal to 0.0' % self.i1) if self.i2 < 0.: raise ValueError('I2=%r must be greater than or equal to 0.0' % self.i2) if self.j < 0.: raise ValueError('J=%r must be greater than or equal to 0.0' % self.j)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBAR 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') A = double_or_blank(card, 3, 'A', 0.0) i1 = double_or_blank(card, 4, 'I1', 0.0) i2 = double_or_blank(card, 5, 'I2', 0.0) j = double_or_blank(card, 6, 'J', 0.0) nsm = double_or_blank(card, 7, 'nsm', 0.0) c1 = double_or_blank(card, 9, 'C1', 0.0) c2 = double_or_blank(card, 10, 'C2', 0.0) d1 = double_or_blank(card, 11, 'D1', 0.0) d2 = double_or_blank(card, 12, 'D2', 0.0) e1 = double_or_blank(card, 13, 'E1', 0.0) e2 = double_or_blank(card, 14, 'E2', 0.0) f1 = double_or_blank(card, 15, 'F1', 0.0) f2 = double_or_blank(card, 16, 'F2', 0.0) i12 = double_or_blank(card, 19, 'I12', 0.0) if A == 0.0: k1 = blank(card, 17, 'K1') k2 = blank(card, 18, 'K2') elif i12 != 0.0: # K1 / K2 are ignored k1 = None k2 = None else: #: default=infinite; assume 1e8 k1 = double_or_blank(card, 17, 'K1', 1e8) #: default=infinite; assume 1e8 k2 = double_or_blank(card, 18, 'K2', 1e8) assert len(card) <= 20, f'len(PBAR card) = {len(card):d}\ncard={card}' return PBAR(pid, mid, A, i1, i2, i12, j, nsm, c1, c2, d1, d2, e1, e2, f1, f2, k1, k2, comment=comment)
@classmethod def add_op2_data(cls, data, comment=''): pid = data[0] mid = data[1] A = data[2] i1 = data[3] i2 = data[4] j = data[5] nsm = data[6] #self.fe = data[7] #: .. todo:: not documented.... c1 = data[8] c2 = data[9] d1 = data[10] d2 = data[11] e1 = data[12] e2 = data[13] f1 = data[14] f2 = data[15] k1 = data[16] k2 = data[17] i12 = data[18] if k1 == 0.: k1 = None if k2 == 0.: k2 = None return PBAR(pid, mid, A=A, i1=i1, i2=i2, i12=i12, j=j, nsm=nsm, c1=c1, c2=c2, d1=d1, d2=d2, e1=e1, e2=e2, f1=f1, f2=f2, k1=k1, k2=k2, comment=comment) def _verify(self, xref): pid = self.pid mid = self.Mid() A = self.Area() J = self.J() #c = self.c assert isinstance(pid, int), 'PBAR: pid=%r' % pid assert isinstance(mid, int), 'PBAR: mid=%r' % mid assert isinstance(A, float), 'PBAR: pid=%r' % A assert isinstance(J, float), 'PBAR: cid=%r' % J #assert isinstance(c, float), 'c=%r' % c if xref: nsm = self.Nsm() mpa = self.MassPerLength() assert isinstance(nsm, float), 'PBAR: nsm=%r' % nsm assert isinstance(mpa, float), 'PBAR: mass_per_length=%r' % mpa
[docs] def MassPerLength(self) -> float: r""" Gets the mass per length :math:`\frac{m}{L}` of the CBAR. .. math:: \frac{m}{L} = \rho A + nsm """ rho = self.Rho() nsm = self.Nsm() if rho == 0.0 and nsm == 0.: return 0. A = self.Area() return rho * A + nsm
[docs] def get_cdef(self): cdef = np.array([ [self.c1, self.c2], [self.d1, self.d2], [self.e1, self.e2], [self.f1, self.f2], ], dtype='float64') return cdef
[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 PBAR 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 Area(self) -> float: """Gets the area :math:`A` of the CBAR.""" return self.A
#def Nsm(self) -> float: # return self.nsm #def J(self) -> float: #return self.j
[docs] def I11(self) -> float: """gets the section I11 moment of inertia""" return self.i1
[docs] def I22(self) -> float: """gets the section I22 moment of inertia""" return self.i2
[docs] def I12(self) -> float: """gets the section I12 moment of inertia""" return self.i12
[docs] def raw_fields(self): list_fields = ['PBAR', self.pid, self.Mid(), self.A, self.i1, self.i2, self.j, self.nsm, None, self.c1, self.c2, self.d1, self.d2, self.e1, self.e2, self.f1, self.f2, self.k1, self.k2, self.i12] return list_fields
[docs] def repr_fields(self): #A = set_blank_if_default(self.A,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) j = set_blank_if_default(self.j, 0.0) nsm = set_blank_if_default(self.nsm, 0.0) c1 = set_blank_if_default(self.c1, 0.0) c2 = set_blank_if_default(self.c2, 0.0) d1 = set_blank_if_default(self.d1, 0.0) d2 = set_blank_if_default(self.d2, 0.0) e1 = set_blank_if_default(self.e1, 0.0) e2 = set_blank_if_default(self.e2, 0.0) f1 = set_blank_if_default(self.f1, 0.0) f2 = set_blank_if_default(self.f2, 0.0) k1 = set_blank_if_default(self.k1, 1e8) k2 = set_blank_if_default(self.k2, 1e8) list_fields = ['PBAR', self.pid, self.Mid(), self.A, i1, i2, j, nsm, None, c1, c2, d1, d2, e1, e2, f1, f2, k1, k2, i12] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() if max(self.pid, self.mid) > MAX_INT: size = 16 if size == 8: return self.comment + print_card_8(card) return self.comment + print_card_16(card)
PBARL_MSG = '\n' + """ +-------+------+------+-------+------+------+------+------+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+======+======+=======+======+======+======+======+======+ | PBARL | PID | MID | GROUP | TYPE | | | | | +-------+------+------+-------+------+------+------+------+------+ | | DIM1 | DIM2 | DIM3 | DIM4 | DIM5 | DIM6 | DIM7 | DIM8 | +-------+------+------+-------+------+------+------+------+------+ | | DIM9 | etc. | NSM | | | | | | +-------+------+------+-------+------+------+------+------+------+""".strip()
[docs] class PBARL(LineProperty): """ .. todo:: doesnt support user-defined types +-------+------+------+-------+------+------+------+------+------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +=======+======+======+=======+======+======+======+======+======+ | PBARL | PID | MID | GROUP | TYPE | | | | | +-------+------+------+-------+------+------+------+------+------+ | | DIM1 | DIM2 | DIM3 | DIM4 | DIM5 | DIM6 | DIM7 | DIM8 | +-------+------+------+-------+------+------+------+------+------+ | | DIM9 | etc. | NSM | | | | | | +-------+------+------+-------+------+------+------+------+------+ """ type = 'PBARL' _properties = ['Type', 'valid_types'] valid_types = { "ROD": 1, "TUBE": 2, "TUBE2": 2, "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, # was 12 # approximate #'I', 'CHAN', 'T', 'CHAN1', 'T1', 'CHAN2', 'T2', 'L' and 'BOX1'. 'L' : 4, } # for GROUP="MSCBML0" #pname_fid_map = { #12 : 'DIM1', #13 : 'DIM2', #14 : 'DIM3', #15 : 'DIM3', #}
[docs] def update_by_pname_fid(self, pname_fid, value): if isinstance(pname_fid, str) and pname_fid.startswith('DIM'): num = int(pname_fid[3:]) self.dim[num - 1] = value else: raise NotImplementedError('PBARL Type=%r name=%r has not been implemented' % ( self.Type, pname_fid))
[docs] @classmethod def _init_from_empty(cls): pid = 1 mid = 1 Type = 'ROD' dim = [1.] return PBARL(pid, mid, Type, dim, group='MSCBML0', nsm=0., comment='')
def __init__(self, pid: int, mid: int, Type: str, dim: list[float], group: str='MSCBML0', nsm: float=0., comment: str=''): """ Creates a PBARL card, which defines A, I1, I2, I12, and J using dimensions rather than explicit values. Parameters ---------- pid : int property id mid : int material id Type : str type of the bar {ROD, TUBE, TUBE2, I, CHAN, T, BOX, BAR, CROSS, H, T1, I1, CHAN1, Z, CHAN2, T2, BOX1, HEXA, HAT, HAT1, DBOX} dim : list[float] dimensions for cross-section corresponding to Type; the length varies group : str; default='MSCBML0' this parameter can lead to a very broken deck with a very bad error message; don't touch it! nsm : float; default=0. non-structural mass comment : str; default='' a comment for the card The shear center and neutral axis do not coincide when: - Type = I and dim2 != dim3 - Type = CHAN, CHAN1, CHAN2 - Type = T - Type = T1, T2 - Type = BOX1 - Type = HAT, HAT1 - Type = DBOX """ LineProperty.__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.Type = Type self.dim = dim #: non-structural mass self.nsm = nsm #ndim = self.valid_types[Type] #assert len(dim) == ndim, 'PBARL ndim=%s len(dims)=%s' % (ndim, len(dim)) #self.validate() #area = self.Area() #assert area > 0, 'Type=%s dim=%s A=%s\n%s' % (self.Type, self.dim, area, str(self)) self.mid_ref = None
[docs] def validate(self): if self.Type not in self.valid_types: keys = list(self.valid_types.keys()) msg = ('Invalid PBARL Type, Type=%s ' 'valid_types=%s' % (self.Type, ', '.join(sorted(keys)))) raise ValueError(msg) try: ndim = self.valid_types[self.Type] except KeyError: allowed = list(self.valid_types.keys()) msg = f'PBARL pid={self.pid}; Type={self.Type}; allowed={allowed}' raise KeyError(msg) if not isinstance(self.dim, list): msg = 'PBARL pid=%s; dim must be a list; type=%r' % (self.pid, type(self.dim)) raise TypeError(msg) if len(self.dim) != ndim: msg = 'dim=%s len(dim)=%s Type=%s len(dimType)=%s' % ( self.dim, len(self.dim), self.Type, self.valid_types[self.Type]) raise RuntimeError(msg) assert len(self.dim) == ndim, 'PBARL ndim=%s len(dims)=%s' % (ndim, len(self.dim)) if not isinstance(self.group, str): raise TypeError('Invalid group; pid=%s group=%r' % (self.pid, self.group)) #if self.group != 'MSCBML0': #msg = 'Invalid group; pid=%s group=%r expected=[MSCBML0]' % (self.pid, self.group) #raise ValueError(msg) assert None not in self.dim
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBARL 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') Type = string(card, 4, 'Type') try: ndim = cls.valid_types[Type] except KeyError: keys = list(cls.valid_types.keys()) raise KeyError('%r is not a valid PBARL type\nallowed_types={%s}' % ( Type, ', '.join(sorted(keys)))) # 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 # DIM4 if not provided. # b. DIM9 and DIM10 have a default value of DIM6 if not # provided. dim = [] if Type == 'DBOX': for i in range(ndim): if i in [4, 5, 6, 7]: dim4 = dim[3] dimi = double_or_blank(card, 9 + i, f'ndim={ndim}; dim{i+1}', default=dim4, end=PBARL_MSG) elif i in [8, 9]: dim6 = dim[5] dimi = double_or_blank(card, 9 + i, f'ndim={ndim}; dim{i+1}', default=dim6, end=PBARL_MSG) else: dimi = double(card, 9 + i, f'ndim={ndim}; dim{i+1}', end=PBARL_MSG) dim.append(dimi) else: for i in range(ndim): dimi = double(card, 9 + i, f'ndim={ndim}; dim{i+1}', end=PBARL_MSG) dim.append(dimi) #: dimension list assert len(dim) == ndim, 'PBARL ndim=%s len(dims)=%s' % (ndim, len(dim)) #assert len(dims) == len(self.dim), 'PBARL ndim=%s len(dims)=%s' % (ndim, len(self.dim)) nsm = double_or_blank(card, 9 + ndim, 'nsm', default=0.0) return PBARL(pid, mid, Type, dim, group=group, nsm=nsm, comment=comment)
@classmethod def add_op2_data(cls, data, comment=''): pid = data[0] mid = data[1] group = data[2].strip() Type = data[3].strip() dim = list(data[4:-1]) nsm = data[-1] return PBARL(pid, mid, Type, dim, group=group, nsm=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 PBARL 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
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.Type) print(msg) J = 0.0 nsm = self.Nsm() assert isinstance(pid, int), 'PBARL: pid=%r' % pid assert isinstance(mid, int), 'PBARL: mid=%r' % mid assert isinstance(A, float), 'PBARL: pid=%r' % A assert isinstance(J, float), 'PBARL: J=%r' % J assert isinstance(nsm, float), 'PBARL: nsm=%r' % nsm if xref: mpl = self.MassPerLength() assert isinstance(mpl, float), 'mass_per_length=%r' % mpl
[docs] def get_cdef(self): r""" these axes are backwards... ^ y +---|---+ | | | | +------> z | | +-------+ """ if self.Type in ['ROD', 'TUBE']: R = self.dims[0] cdef = np.array([ [R, 0.], [0., R], [-R, 0.], [0., -R], ], dtype='float64') elif self.Type in ['BAR', 'BOX']: height, width = self.dims cdef = np.array([ [width, height], [width, -height], [-width, -height], [-width, height], ], dtype='float64') / 2. else: raise NotImplementedError(self.Type) return cdef
@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 Area(self) -> float: """Gets the area :math:`A` of the CBAR.""" return _bar_areaL('PBARL', self.beam_type, self.dim, self)[0]
[docs] def Nsm(self) -> float: """Gets the non-structural mass :math:`nsm` of the CBAR.""" return self.nsm
[docs] def MassPerLength(self) -> float: r""" Gets the mass per length :math:`\frac{m}{L}` of the CBAR. .. math:: \frac{m}{L} = A \rho + nsm """ rho = self.Rho() nsm = self.Nsm() if rho == 0.0 and nsm == 0.: return 0. area = self.Area() return area * rho + nsm
[docs] def I1(self) -> float: """gets the section I1 moment of inertia""" I1 = A_I1_I2_I12(self, self.beam_type, self.dim)[1] assert isinstance(I1, float), I1 return I1
[docs] def I2(self) -> float: """gets the section I2 moment of inertia""" I2 = A_I1_I2_I12(self, self.beam_type, self.dim)[2] assert isinstance(I2, float), I2 return I2
[docs] def I12(self) -> float: """gets the section I12 moment of inertia""" try: I12 = A_I1_I2_I12(self, self.beam_type, self.dim) except Exception: print(str(self)) raise return I12[3]
#def I1_I2_I12(self): #"""gets the section I1, I2, I12 moment of inertia""" #return I1_I2_I12(prop, prop.dim)
[docs] def I11(self) -> float: return self.I1()
[docs] def I22(self) -> float: return self.I2()
[docs] def _points(self, beam_type, dim): if beam_type == 'BAR': # origin ar center (d1, d2) = dim Area = d1 * d2 y1 = d2 / 2. x1 = d1 / 2. points = [ # start at upper right, go clockwise [x1, y1], # p1 [x1, y1], # p2 [-x1, -y1], # p3 [-x1, -y1], # p4 ] elif beam_type == 'CROSS': (d1, d2, d3, d4) = dim # origin at center x1 = d2 / 2. x2 = d2 / 2. + d1 y1 = -d3 / 2. y2 = -d4 / 2. y3 = d4 / 2. y4 = d3 / 2. points = [ # start at top right, go clockwise, down first [x1, y4], # p1 [x1, y3], # p2 [x2, y3], # p3 [x2, y2], # p4 [x1, y2], # p5 [x1, y1], # p6 [-x1, y1], # p7 [-x1, y2], # p8 [-x2, y2], # p9 [-x2, y3], # p10 [-x1, y3], # p11 [-x1, y4], # p12 ] Area = d2*d3 + 2*d1*d4 elif beam_type == 'HEXA': (d1, d2, d3) = dim x1 = d2 / 2. - d1 x2 = d2 / 2. y1 = 0. y2 = d3 / 2. y3 = d3 points = [ # start at upper center, go clockwise, diagonal down right [x1, y3], # p1 [x2, y2], # p2 [x1, y1], # p3 [x1, y1], # p4 [-x2, y2], # p5 [-x1, y3], # p6 ] Area = d1 * (d2 + 2 * d3) elif beam_type == 'I': (d1, d2, d3) = dim raise NotImplementedError('PBARL beam_type=%r' % beam_type) elif beam_type == 'H': (d1, d2, d3, d4) = dim x1 = d1 / 2. x2 = (d1 + d2) / 2. y3 = d4 / 2. y4 = d3 / 2. y1 = -y4 y2 = -y3 points = [ # start at top of H in dip, go clockwise, up first [x1, y3], # p1 # right side [x1, y4], # p2 [x2, y4], # p3 [x2, y1], # p4 [x1, y1], # p5 [x1, y2], # p6 [-x1, y2], # p7 # left side [-x1, y1], # p8 [-x2, y1], # p9 [-x2, y4], # p10 [-x1, y4], # p11 [-x1, y3], # p12 ] Area = d2 * d3 + d1 * d4 elif beam_type == 'T2': d1, d2, d3, d4 = dim # check origin, y3 at bottom, x1 innner x1 = d4 / 2. x2 = d1 / 2. y1 = -d3 / 2. y2 = d3 / 2. y3 = -d3 / 2. points = [ # start at upper right, go clockwise [x1, y3], # p1 [x1, y2], # p2 [x2, y2], # p3 [x2, y1], # p4 [-x2, y1], # p5 [-x2, y2], # p6 [-x1, y2], # p7 [-x1, y3] # p8 ] Area = d1*d3 + (d2-d3)*d4 else: msg = '_points for beam_type=%r dim=%r on PBARL is not supported' % (beam_type, self.dim) raise NotImplementedError(msg) return array(points), Area
[docs] def J(self) -> float: beam_type = self.beam_type if beam_type == 'ROD': A, I1, I2, I12 = rod_section(self.type, beam_type, self.dim, self) elif beam_type == 'TUBE': A, I1, I2, I12 = tube_section(self.type, beam_type, self.dim, self) elif beam_type == 'TUBE2': A, I1, I2, I12 = tube2_section(self.type, beam_type, self.dim, self) elif beam_type == 'BOX': A, I1, I2, I12 = box_section(self.type, beam_type, self.dim, self) #elif beam_type in ['BAR']: #assert len(self.dim) == 2, 'dim=%r' % self.dim #b, h = self.dim #(A, Ix, Iy, Ixy) = self.A_I1_I2_I12() #J = Ix + Iy elif beam_type in ['BAR', 'CROSS', 'HEXA', 'T2', 'H']: points, unused_Area = self._points(beam_type, self.dim) yi = points[0, :-1] yip1 = points[0, 1:] xi = points[1, :-1] xip1 = points[1, 1:] #: .. seealso:: http://en.wikipedia.org/wiki/Area_moment_of_inertia ai = xi*yip1 - xip1*yi I1 = 1/12 * sum((yi**2 + yi*yip1+yip1**2)*ai) I2 = 1/12 * sum((xi**2 + xi*xip1+xip1**2)*ai) #I12 = 1/24*sum((xi*yip1 + 2*xi*yi + 2*xip1*yip1 + xip1*yi)*ai) elif beam_type == 'I': # http://www.efunda.com/designstandards/beams/SquareIBeam.cfm # d - outside height # h - inside height # b - base # t - l thickness # s - web thickness #(b, d, t, s) = self.dim #h = d - 2 * s #cx = b / 2. #cy = d / 2. (d, b1, b2, t, s1, s2) = self.dim if b1 != b2: msg = 'J for beam_type=%r dim=%r on PBARL b1 != b2 is not supported' % ( beam_type, self.dim) raise NotImplementedError(msg) if s1 != s2: msg = 'J for beam_type=%r dim=%r on PBARL s1 != s2 is not supported' % ( beam_type, self.dim) raise NotImplementedError(msg) h = d - b1 - b2 s = s1 b = b1 I1 = (b*d**3-h**3*(b-t)) / 12. I2 = (2.*s*b**3 + h*t**3) / 12. #elif beam_type == 'T': # test # http://www.amesweb.info/SectionalPropertiesTabs/SectionalPropertiesTbeam.aspx # http://www.amesweb.info/SectionalPropertiesTabs/SectionalPropertiesTbeam.aspx # d - outside height # h - inside height # b - base # t - l thickness # s - web thickness #(b, d, t, s) = self.dim #h = d - 2 * s #(b, d, s, t) = self.dim #if b1 != b2: #msg = 'J for beam_type=%r dim=%r on PBARL b1 != b2 is not supported' % ( #beam_type, self.dim) #raise NotImplementedError(msg) #if s1 != s2: #msg = 'J for beam_type=%r dim=%r on PBARL s1 != s2 is not supported' % ( #beam_type, self.dim) #raise NotImplementedError(msg) #h = d - b1 - b2 #s = s1 #b = b1 # http://www.engineersedge.com/material_science/moment-inertia-gyration-6.htm #y = d**2*t+s**2*(b-t)/(2*(b*s+h*t)) #I1 = (t*y**3 + b*(d-y)**3 - (b-t)*(d-y-s)**3)/3. #I2 = t**3*(h-s)/12. + b**3*s/12. #A = b*s + h*t elif beam_type == 'C': # http://www.efunda.com/math/areas/squarechannel.cfm # d - outside height # h - inside height # b - base # t - l thickness # s - web thickness (b, d, t, s) = self.dim h = d - 2 * s #cx = (2.*b**2*s + h*t**2)/(2*b*d - 2*h*(b-t)) #cy = d / 2. I1 = (b * d**3 - h **3 * (b-t)) / 12. #I12 = (2.*s*b**3 + h*t**3)/3 - A*cx**2 else: # pragma: no cover msg = 'J for beam_type=%r dim=%r on PBARL is not supported' % (beam_type, self.dim) raise NotImplementedError(msg) #: .. seealso:: http://en.wikipedia.org/wiki/Perpendicular_axis_theorem J = I1 + I2 return J
[docs] def raw_fields(self): list_fields = ['PBARL', self.pid, self.Mid(), self.group, self.beam_type, None, None, None, None] + self.dim + [self.nsm] return list_fields
[docs] def repr_fields(self): group = set_blank_if_default(self.group, 'MSCBML0') ndim = self.valid_types[self.beam_type] assert len(self.dim) == ndim, 'PBARL ndim=%s len(dims)=%s' % (ndim, len(self.dim)) list_fields = ['PBARL', self.pid, self.Mid(), group, self.beam_type, None, None, None, None] + self.dim + [self.nsm] 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 PBRSECT(LineProperty): """ not done """ type = 'PBRSECT'
[docs] @classmethod def _init_from_empty(cls): pid = 1 mid = 2 form = 'FORM' options = [('OUTP', 10)] return PBRSECT(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)}
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 self.nsm = 0. self.t = None # int : int #self.brps = {} #self.inps = {} # int : floats #self.ts = {} nsm, brps, inps, outp, ts = parse_pbrsect_options(pid, options) self.outp = outp self.nsm = nsm self.brps = brps self.inps = inps self.ts = ts self.mid_ref = None self.brps_ref = {} self.outp_ref = None
[docs] def validate(self): assert self.form in ['GS', 'OP', 'CP'], 'pid=%s form=%r' % (self.pid, self.form) #assert self.outp is not None, 'form=%s outp=%s' % (self.form, self.outp) if self.form == 'GS': assert len(self.inps) > 0, 'form=%s inps=%s' % (self.form, self.inps) assert len(self.brps) == 0, 'form=%s brps=%s' % (self.form, self.brps) assert len(self.ts) == 0, 'form=%s ts=%s' % (self.form, self.ts) elif self.form in ['OP', 'CP']: assert len(self.inps) == 0, 'form=%s inps=%s' % (self.form, self.inps) assert len(self.brps) >= 0, 'form=%s brps=%s' % (self.form, self.brps) assert len(self.ts) >= 0, 'form=%s ts=%s' % (self.form, self.ts)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBRSECT 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')) unused_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] #C:\MSC.Software\MSC.Nastran\msc20051\nast\tpl\zbr3.dat #options = [ #[u'OUTP', u'201'], #[u'T', u'1.0'], #[u'BRP', u'202'], #[u'T(11)', u'[1.2'], #[u'PT', u'(202'], [u'224)]'], #[u'T(12)', u'[1.2'], #[u'PT', u'(224'], #[u'205)]'], #] else: options = [] pid = integer(bdf_card, 1, 'pid') mid = integer(bdf_card, 2, 'mid') form = string_or_blank(bdf_card, 3, 'form') return PBRSECT(pid, mid, form, options, comment=comment)
@classmethod def add_op2_data(cls, data, comment=''): #pid = data[0] #mid = data[1] #group = data[2].strip() #beam_type = data[3].strip() #dim = list(data[4:-1]) #nsm = data[-1] #print("group = %r" % self.group) #print("beam_type = %r" % self.beam_type) #print("dim = ",self.dim) #print(str(self)) #print("*PBARL = ",data) raise NotImplementedError('not finished...') #return PBRSECT(pid, mid, group, beam_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) if self.outp is not None: self.outp_ref = model.Set(self.outp) self.outp_ref.cross_reference_set(model, 'Point', msg=msg) 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)
[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), 'PBARL: pid=%r' % pid assert isinstance(mid, int), 'PBARL: mid=%r' % mid #assert isinstance(A, float), 'PBARL: pid=%r' % A #assert isinstance(J, float), 'PBARL: cid=%r' % J #assert isinstance(nsm, float), 'PBARL: nsm=%r' % nsm #assert isinstance(mpl, float), 'PBARL: mass_per_length=%r' % mpl
[docs] def Area(self) -> float: """Gets the area :math:`A` of the CBAR.""" return 0.
#raise NotImplementedError('Area is not implemented for PBRSECT')
[docs] def Nsm(self) -> float: """Gets the non-structural mass :math:`nsm` of the CBAR.""" return 0.
#raise NotImplementedError('Nsm is not implemented for PBRSECT')
[docs] def MassPerLength(self) -> float: r""" Gets the mass per length :math:`\frac{m}{L}` of the CBAR. .. 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 PBRSECT')
#def I12(self): #return self.I12()
[docs] def J(self): raise NotImplementedError('J is not implemented for PBRSECT')
[docs] def I22(self): raise NotImplementedError('I22 is not implemented for PBRSECT')
[docs] def raw_fields(self): """not done...""" list_fields = ['PBRSECT', self.pid, self.Mid(), self.form] #None, None, None, None] + self.dim + [self.nsm] return list_fields
[docs] def repr_fields(self): """not done...""" list_fields = ['PBRSECT', self.pid, self.Mid(), self.form] return list_fields
[docs] def write_card(self, size: int=8, is_double: bool=False) -> str: card = self.repr_fields() end = write_arbitrary_beam_section(self.inps, self.ts, self.brps, self.nsm, self.outp) out = self.comment + print_card_8(card) + end return out
def __repr__(self): return self.write_card()
[docs] def parse_pbrsect_options(pid: int, options: list[Any]): # ??? outp = None # int : int brps = {} inps = {} # int : floats ts = {} nsm = 0.0 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': nsm = float(value) elif 'INP' in key: if key.startswith('INP('): assert key.endswith(')'), 'key=%r' % key key_id = int(key[4:-1]) inps[key_id] = int(value) else: inps[0] = int(value) elif key == 'OUTP': outp = int(value) elif key.startswith('BRP'): if key.startswith('BRP('): assert key.endswith(')'), 'key=%r' % key key_id = int(key[4:-1]) brps[key_id] = int(value) else: brps[0] = int(value) elif key.startswith('T('): index, out = split_arbitrary_thickness_section(key, value) ts[index] = out elif key == 'T': ts[1] = float(value) #if key == 'NSM': #self.nsm = float(value) #elif key == 'OUTP': #self.outp = int(value) #elif key == 'BRP(1)': #self.brp1 = int(value) #elif key == 'T': #self.t = float(value) else: raise NotImplementedError('PBRSECT.pid=%d key=%r value=%r' % (pid, key, value)) return nsm, brps, inps, outp, ts
[docs] class PBEAM3(LineProperty): # not done, cleanup; MSC specific card """ +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +========+==========+=========+=========+==========+=========+=========+==========+==========+ | PBEAM3 | PID | MID | A(A) | IZ(A) | IY(A) | IYZ(A) | J(A) | NSM(A) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | CY(A) | CZ(A) | DY(A) | DZ(A) | EY(A) | EZ(A) | FY(A) | FZ(A) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | SO(B) | | A(B) | IZ(B) | IY(B) | IYZ(B) | J(B) | NSM(B) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | CY(B) | CZ(B) | DY(B) | DZ(B) | EY(B) | EZ(B) | FY(B) | FZ(B) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | SO(C) | | A(C) | IZ(C) | IY(C) | IYZ(C) | J(C) | NSM(C) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | CY(C) | CZ(C) | DY(C) | DZ(C) | EY(C) | EZ(C) | FY(C) | FZ(C) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | KY | KZ | NY(A) | NZ(A) | NY(B) | NZ(B) | NY(C) | NZ(C) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | MY(A) | MZ(A) | MY(B) | MZ(B) | MY(C) | MZ(C) | NSIY(A) | NSIZ(A) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | NSIYZ(A) | NSIY(B) | NSIZ(B) | NSIYZ(B) | NSIY(C) | NSIZ(C) | NSIYZ(C) | CW(A) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | CW(B) | CW(C) | STRESS | | | | | | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | WC(A) | WYC(A) | WZC(A) | WD(A) | WYD(A) | WZD(A) | WE(A) | WYE(A) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | WZE(A) | WF(A) | WYF(A) | WZF(A) | WC(B) | WYC(B) | WZC(B) | WD(B) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | WYD(B) | WZD(B) | WE(B) | WYE(B) | WZE(B) | WF(B) | WYF(B) | WZF(B) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | WC(C) | WYC(C) | WZC(C) | WD(C) | WYD(C) | WZD(C) | WE(C) | WYE(C) | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ | | WZE(C) | WF(C) | WYF(C) | WZF(C) | | | | | +--------+----------+---------+---------+----------+---------+---------+----------+----------+ """ type = 'PBEAM3'
[docs] @classmethod def _init_from_empty(cls): pid = 1 mid = 2 A = 3. iz = 4. iy = 5. return PBEAM3(pid, mid, A, iz, iy, iyz=None, j=None, nsm=0., so=None, cy=None, cz=None, dy=None, dz=None, ey=None, ez=None, fy=None, fz=None, ky=1., kz=1., ny=None, nz=None, my=None, mz=None, nsiy=None, nsiz=None, nsiyz=None, cw=None, stress='GRID', w=None, wy=None, wz=None, comment='')
def __init__(self, pid, mid, A, iz, iy, iyz=None, j=None, nsm=0., so=None, cy=None, cz=None, dy=None, dz=None, ey=None, ez=None, fy=None, fz=None, ky=1., kz=1., ny=None, nz=None, my=None, mz=None, nsiy=None, nsiz=None, nsiyz=None, cw=None, stress='GRID', w=None, wy=None, wz=None, comment=''): """ Creates a PBEAM3 card Parameters ---------- pid : int property id mid : int material id A : list[float] areas for ABC iz / iy / iyz : list[float] area moment of inertias for ABC iyz : list[float]; default=None -> [0., 0., 0.] area moment of inertias for ABC j : list[float]; default=None polar moment of inertias for ABC None -> iy + iz from section A for ABC so : list[str]; default=None None -> ['YES', 'YESA', 'YESA'] cy / cz / dy / dz / ey / ez / fy / fz : list[float]; default=[0., 0., 0.] stress recovery loctions for ABC ny / nz : list[float] Local (y, z) coordinates of neutral axis for ABC my / mz : list[float] Local (y, z) coordinates of nonstructural mass center of gravity for ABC nsiy / nsiz / nsiyz : list[float] Nonstructural mass moments of inertia per unit length about local y and z-axes, respectively, with regard to the nonstructural mass center of gravity for ABC cw : list[float] warping coefficients for ABC stress : str; default='GRID' Location selection for stress, strain and force output. w : (4, 3) float numpy array; default=None Values of warping function at stress recovery points None : array of 0.0 wy / wz : (4, 3) float numpy array; default=None Gradients of warping function in the local (y, z) coordinate system at stress recovery points None : array of 0.0 """ LineProperty.__init__(self) if comment: self.comment = comment def _pbeam3_station_a(values): """used by A, Iz, Iy""" if isinstance(values, float_types): values = [values] * 3 station_a_value = values[0] for i, value in zip(count(), values[1:]): if value is None: values[i + 1] = station_a_value return values def _pbeam3_station_a_default(values, default_value): """used by Iyz, nsm""" if values is None: values = [default_value] * 3 elif isinstance(values, float_types): values = [values] * 3 station_a_value = default_value if values[0] is None else values[0] for i, value in zip(count(), values[1:]): if value is None: values[i + 1] = station_a_value return values self.pid = pid self.mid = mid self.A = _pbeam3_station_a(A) self.iz = _pbeam3_station_a(iz) self.iy = _pbeam3_station_a(iy) self.iyz = _pbeam3_station_a_default(iyz, 0.0) self.j = _pbeam3_station_a_default(j, self.iy[0] + self.iz[0]) self.nsm = _pbeam3_station_a_default(nsm, 0.0) if so is None: so = ['YES', 'YESA', 'YESA'] elif isinstance(so, str): so = [so] * 3 self.so = so def _pbeam3_default_list(values, default): """used by Cy/Cz, Dy/Dz, Ey/Ez, Fy/Fz""" if values is None: values = [default] * 3 elif isinstance(values, float_types): values = [values] * 3 for i, value in zip(count(), values): if value is None: values[i] = default return values self.cy = _pbeam3_default_list(cy, 0.) self.cz = _pbeam3_default_list(cz, 0.) self.dy = _pbeam3_default_list(dy, 0.) self.dz = _pbeam3_default_list(dz, 0.) self.ey = _pbeam3_default_list(ey, 0.) self.ez = _pbeam3_default_list(ez, 0.) self.fy = _pbeam3_default_list(fy, 0.) self.fz = _pbeam3_default_list(fz, 0.) self.ky = ky self.kz = kz def _pbeam3_default_list_station_a(values, default): """used by Ny/Nz, My/Mz, NSIy/NSIz/NSIyz, Cw""" if values is None: values = [default] * 3 elif isinstance(values, float_types): values = [values] * 3 if values[0] is None: values[0] = default station_a_value = values[0] for i, value in zip(count(), values[1:]): if value is None: values[i + 1] = station_a_value return values self.ny = _pbeam3_default_list_station_a(ny, 0.) self.nz = _pbeam3_default_list_station_a(nz, 0.) self.my = _pbeam3_default_list_station_a(my, 0.) self.mz = _pbeam3_default_list_station_a(mz, 0.) self.nsiy = _pbeam3_default_list_station_a(nsiy, 0.) self.nsiz = _pbeam3_default_list_station_a(nsiz, 0.) self.nsiyz = _pbeam3_default_list_station_a(nsiyz, 0.) self.cw = _pbeam3_default_list_station_a(cw, 0.) self.stress = stress if w is None: w = np.zeros((3, 4), dtype='float64') if wy is None: wy = np.zeros((3, 4), dtype='float64') if wz is None: wz = np.zeros((3, 4), dtype='float64') self.w = w self.wy = wy self.wz = wz self.mid_ref = None
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBARL card from ``BDF.add_card(...)`` Parameters ---------- card : BDFCard() a BDFCard object comment : str; default='' a comment for the card """ #PID MID A(A) IZ(A) IY(A) IYZ(A) J(A) NSM(A) pid = integer(card, 1, 'pid') mid = integer(card, 2, 'mid') area = [double(card, 3, 'A')] iz = [double(card, 4, 'Iz')] iy = [double(card, 5, 'Iy')] iyz = [double_or_blank(card, 6, 'Iyz', 0.0)] j = [double_or_blank(card, 7, 'J', iy[0] + iz[0])] nsm = [double_or_blank(card, 8, 'nsm', 0.0)] #CY(A) CZ(A) DY(A) DZ(A) EY(A) EZ(A) FY(A) FZ(A) cy = [double_or_blank(card, 9, 'cy', default=0.)] cz = [double_or_blank(card, 10, 'cz', default=0.)] dy = [double_or_blank(card, 11, 'dy', default=0.)] dz = [double_or_blank(card, 12, 'dz', default=0.)] ey = [double_or_blank(card, 13, 'ey', default=0.)] ez = [double_or_blank(card, 14, 'ez', default=0.)] fy = [double_or_blank(card, 15, 'fy', default=0.)] fz = [double_or_blank(card, 16, 'fz', default=0.)] #SO(B) A(B) IZ(B) IY(B) IYZ(B) J(B) NSM(B) #CY(B) CZ(B) DY(B) DZ(B) EY(B) EZ(B) FY(B) FZ(B) #SO(C) A(C) IZ(C) IY(C) IYZ(C) J(C) NSM(C) #CY(C) CZ(C) DY(C) DZ(C) EY(C) EZ(C) FY(C) FZ(C) so = ['YES'] locations = ['B', 'C'] for i, location in enumerate(locations): offset = 17 + i * 16 so.append(string_or_blank(card, offset, 'SO_%s' % location, default='YESA')) area.append(double_or_blank(card, offset + 2, 'area_%s' % location, default=area[0])) iz.append(double_or_blank(card, offset + 3, 'Iz', default=iz[0])) iy.append(double_or_blank(card, offset + 4, 'Iy', default=iy[0])) iyz.append(double_or_blank(card, offset + 5, 'Iyz', default=iyz[0])) j.append(double_or_blank(card, offset + 6, 'J', default=j[0])) nsm.append(double_or_blank(card, offset + 7, 'nsm', default=nsm[0])) cy.append(double_or_blank(card, offset + 8, 'cy', default=0.)) cz.append(double_or_blank(card, offset + 9, 'cz', default=0.)) dy.append(double_or_blank(card, offset + 10, 'dy', default=0.)) dz.append(double_or_blank(card, offset + 11, 'dz', default=0.)) ey.append(double_or_blank(card, offset + 12, 'ey', default=0.)) ez.append(double_or_blank(card, offset + 13, 'ez', default=0.)) fy.append(double_or_blank(card, offset + 14, 'fy', default=0.)) fz.append(double_or_blank(card, offset + 15, 'fz', default=0.)) #KY KZ NY(A) NZ(A) NY(B) NZ(B) NY(C) NZ(C) #MY(A) MZ(A) MY(B) MZ(B) MY(C) MZ(C) NSIY(A) NSIZ(A) #NSIYZ(A) NSIY(B) NSIZ(B) NSIYZ(B) NSIY(C) NSIZ(C) NSIYZ(C) CW(A) #CW(B) CW(C) STRESS ifield = 49 ky = double_or_blank(card, ifield, 'Ky', default=1.0) kz = double_or_blank(card, ifield + 1, 'Kz', default=1.0) ifield += 2 locations = ['A', 'B', 'C'] ny = [] nz = [] for i, location in enumerate(locations): if i == 0: nyi = double_or_blank(card, ifield, 'NY(%s)' % location, default=0.0) nzi = double_or_blank(card, ifield + 1, 'NZ(%s)' % location, default=0.0) else: nyi = double_or_blank(card, ifield, 'NY(%s)' % location, default=ny[0]) nzi = double_or_blank(card, ifield + 1, 'NZ(%s)' % location, default=nz[0]) ny.append(nyi) nz.append(nzi) ifield += 2 my = [] mz = [] for i, location in enumerate(locations): if i == 0: myi = double_or_blank(card, ifield, 'MY(%s)' % location, default=0.0) mzi = double_or_blank(card, ifield + 1, 'MZ(%s)' % location, default=0.0) else: myi = double_or_blank(card, ifield, 'MY(%s)' % location, default=my[0]) mzi = double_or_blank(card, ifield + 1, 'MZ(%s)' % location, default=mz[0]) my.append(myi) mz.append(mzi) ifield += 2 nsiy = [] nsiz = [] nsiyz = [] for i, location in enumerate(locations): if i == 0: nsiyi = double_or_blank(card, ifield, 'NSIY(%s)' % location, default=0.0) nsizi = double_or_blank(card, ifield + 1, 'NSIZ(%s)' % location, default=0.0) nsiyzi = double_or_blank(card, ifield + 2, 'NSIYZ(%s)' % location, default=0.0) else: nsiyi = double_or_blank(card, ifield, 'NSIY(%s)' % location, default=nsiy[0]) nsizi = double_or_blank(card, ifield + 1, 'NSIZ(%s)' % location, default=nsiz[0]) nsiyzi = double_or_blank(card, ifield + 2, 'NSIYZ(%s)' % location, default=nsiyz[0]) nsiy.append(nsiyi) nsiz.append(nsizi) nsiyz.append(nsiyzi) ifield += 3 cw = [] for location in locations: cwi = double_or_blank(card, ifield, 'CW(%s)' % location, default=0.0) cw.append(cwi) ifield += 1 stress = string_or_blank(card, ifield, 'STRESS', default='GRID') ifield += 6 # WC(A) WYC(A) WZC(A) WD(A) WYD(A) WZD(A) WE(A) WYE(A) # WZE(A) WF(A)) WYF(A) WZF(A) WC(B) WYC(B) WZC(B) WD(B) # WYD(B) WZD(B) WE(B) WYE(B) WZE(B) WF(B) WYF(B) WZF(B) # WC(C) WYC(C) WZC(C) WD(C) WYD(C) WZD(C) WE(C) WYE(C) # WZE(C) WF(C) WYF(C) WZF(C) spots = ('C', 'D', 'E', 'F') w = np.zeros((3, 4), dtype='float64') wy = np.zeros((3, 4), dtype='float64') wz = np.zeros((3, 4), dtype='float64') for iloc, location in enumerate(locations): for ispot, spot in enumerate(spots): wi = double_or_blank(card, ifield, 'W%s(%s)' % (spot, location), default=0.0) wyi = double_or_blank(card, ifield + 1, 'WY%s(%s)' % (spot, location), default=0.0) wzi = double_or_blank(card, ifield + 2, 'WZ%s(%s)' % (spot, location), default=0.0) w[iloc, ispot] = wi wy[iloc, ispot] = wyi wz[iloc, ispot] = wzi ifield += 3 return PBEAM3(pid, mid, area, iz, iy, iyz, j, nsm=nsm, so=so, cy=cy, cz=cz, dy=dy, dz=dz, ey=ey, ez=ez, fy=fy, fz=fz, ky=ky, kz=kz, ny=ny, nz=nz, my=my, mz=mz, nsiy=nsiy, nsiz=nsiz, nsiyz=nsiyz, cw=cw, stress=stress, w=w, wy=wy, wz=wz, comment=comment)
def add_op2_data(self, data, comment=''): if comment: self.comment = comment raise NotImplementedError(data)
[docs] def Nsm(self) -> list[float]: """ Gets the non-structural mass :math:`nsm`. .. warning:: nsm field not supported fully on PBEAM3 card """ return self.nsm
[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 PBEAM3 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 raw_fields(self): list_fields = ['PBEAM3', self.pid, self.Mid()] for (i, soi, ai, iz, iy, iyz, j, nsm, cy, cz, dy, dz, ey, ez, fy, fz) in zip( count(), self.so, self.A, self.iz, self.iy, self.iyz, self.j, self.nsm, self.cy, self.cz, self.dy, self.dz, self.ey, self.ez, self.fy, self.fz): if i == 0: list_fields += [ ai, iz, iy, iyz, j, nsm, cy, cz, dy, dz, ey, ez, fy, fz] else: list_fields += [ soi, None, ai, iz, iy, iyz, j, nsm, cy, cz, dy, dz, ey, ez, fy, fz] #KY KZ NY(A) NZ(A) NY(B) NZ(B) NY(C) NZ(C) #MY(A) MZ(A) MY(B) MZ(B) MY(C) MZ(C) NSIY(A) NSIZ(A) #NSIYZ(A) NSIY(B) NSIZ(B) NSIYZ(B) NSIY(C) NSIZ(C) NSIYZ(C) CW(A) #CW(B) CW(C) STRESS list_fields += [self.ky, self.kz] for ny, nz in zip(self.ny, self.nz): list_fields += [ny, nz] for my, mz in zip(self.my, self.mz): list_fields += [my, mz] for nsiy, nsiz, nsiyz in zip(self.nsiy, self.nsiz, self.nsiyz): list_fields += [nsiy, nsiz, nsiyz] list_fields += self.cw list_fields += [self.stress, None, None, None, None, None] #WC(A) WYC(A) WZC(A) WD(A) WYD(A) WZD(A) WE(A) WYE(A) #WZE(A) WF(A)) WYF(A) WZF(A) for w, wy, wz in zip(self.w, self.wy, self.wz): for wi, wyi, wzi in zip(w, wy, wz): list_fields += [wi, wyi, wzi] 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 PBEND(LineProperty): """ MSC/NX Option A +-------+------+-------+-----+----+----+--------+----+--------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 7 | 8 | +=======+======+=======+=====+====+====+========+====+========+ | PBEND | PID | MID | A | I1 | I2 | J | RB | THETAB | +-------+------+-------+-----+----+----+--------+----+--------+ | | C1 | C2 | D1 | D2 | E1 | E2 | F1 | F2 | +-------+------+-------+-----+----+----+--------+----+--------+ | | K1 | K2 | NSM | RC | ZC | DELTAN | | | +-------+------+-------+-----+----+----+--------+----+--------+ MSC Option B +-------+------+-------+-----+----+----+--------+----+--------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 7 | 8 | +=======+======+=======+=====+====+====+========+====+========+ | PBEND | PID | MID | FSI | RM | T | P | RB | THETAB | +-------+------+-------+-----+----+----+--------+----+--------+ | | | | NSM | RC | ZC | | | | +-------+------+-------+-----+----+----+--------+----+--------+ NX Option B +-------+------+-------+-----+----+----+--------+----+--------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 7 | 8 | +=======+======+=======+=====+====+====+========+====+========+ | PBEND | PID | MID | FSI | RM | T | P | RB | THETAB | +-------+------+-------+-----+----+----+--------+----+--------+ | | SACL | ALPHA | NSM | RC | ZC | FLANGE | | | +-------+------+-------+-----+----+----+--------+----+--------+ | | KX | KY | KZ | | SY | SZ | | | +-------+------+-------+-----+----+----+--------+----+--------+ """ type = 'PBEND'
[docs] @classmethod def _init_from_empty(cls): pid = 1 mid = 1 fsi = 1 rm = 0.1 t = 0.01 return cls.add_beam_type_2(pid, mid, fsi, rm, t, p=None, rb=None, theta_b=None, nsm=0., rc=0., zc=0., comment='')
def __init__(self, pid, mid, beam_type, A, i1, i2, j, c1, c2, d1, d2, e1, e2, f1, f2, k1, k2, nsm, rc, zc, delta_n, fsi, rm, t, p, rb, theta_b, comment=''): LineProperty.__init__(self) if comment: self.comment = comment self.pid = pid self.mid = mid self.beam_type = beam_type self.A = A self.i1 = i1 self.i2 = i2 self.j = j self.c1 = c1 self.c2 = c2 self.d1 = d1 self.d2 = d2 self.e1 = e1 self.e2 = e2 self.f1 = f1 self.f2 = f2 self.k1 = k1 self.k2 = k2 self.nsm = nsm self.rc = rc self.zc = zc self.delta_n = delta_n self.fsi = fsi self.rm = rm self.t = t self.p = p self.rb = rb self.theta_b = theta_b self.mid_ref = None
[docs] @classmethod def add_beam_type_1(cls, pid, mid, A, i1, i2, j, rb=None, theta_b=None, c1=0., c2=0., d1=0., d2=0., e1=0., e2=0., f1=0., f2=0., k1=None, k2=None, nsm=0., rc=0., zc=0., delta_n=0., comment=''): """ +-------+------+-------+-----+----+----+--------+----+--------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 7 | 8 | +=======+======+=======+=====+====+====+========+====+========+ | PBEND | PID | MID | A | I1 | I2 | J | RB | THETAB | +-------+------+-------+-----+----+----+--------+----+--------+ | | C1 | C2 | D1 | D2 | E1 | E2 | F1 | F2 | +-------+------+-------+-----+----+----+--------+----+--------+ | | K1 | K2 | NSM | RC | ZC | DELTAN | | | +-------+------+-------+-----+----+----+--------+----+--------+ Parameters ---------- A : float cross-sectional area i1, i2 : float area moments of inertia for plane 1/2 j : float torsional stiffness rb : float; default=None bend radius of the line of centroids theta_b : float; default=None arc angle of element (degrees) c1, c2, d1, d2, e1, e2, f1, f2 : float; default=0.0 the r/z locations from the geometric centroid for stress recovery k1, k2 : float; default=None Shear stiffness factor K in K*A*G for plane 1 and plane 2 nsm : float; default=0. nonstructural mass per unit length??? zc : float; default=None Offset of the geometric centroid in a direction perpendicular to the plane of points GA and GB and vector v. delta_n : float; default=None Radial offset of the neutral axis from the geometric centroid, positive is toward the center of curvature """ beam_type = 1 fsi = None rm = None t = None p = None return PBEND(pid, mid, beam_type, A, i1, i2, j, c1, c2, d1, d2, e1, e2, f1, f2, k1, k2, nsm, rc, zc, delta_n, fsi, rm, t, p, rb, theta_b, comment=comment)
[docs] @classmethod def add_beam_type_2(cls, pid, mid, fsi, rm, t, p=None, rb=None, theta_b=None, nsm=0., rc=0., zc=0., comment=''): """ +-------+------+-------+-----+----+----+--------+----+--------+ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 7 | 8 | +=======+======+=======+=====+====+====+========+====+========+ | PBEND | PID | MID | FSI | RM | T | P | RB | THETAB | +-------+------+-------+-----+----+----+--------+----+--------+ | | | | NSM | RC | ZC | | | | +-------+------+-------+-----+----+----+--------+----+--------+ Parameters ---------- fsi : int Flag selecting the flexibility and stress intensification factors. See Remark 3. (Integer = 1, 2, or 3) rm : float Mean cross-sectional radius of the curved pipe t : float Wall thickness of the curved pipe p : float; default=None Internal pressure rb : float; default=None bend radius of the line of centroids theta_b : float; default=None arc angle of element (degrees) nsm : float; default=0. nonstructural mass per unit length??? rc : float; default=None Radial offset of the geometric centroid from points GA and GB. zc : float; default=None Offset of the geometric centroid in a direction perpendicular to the plane of points GA and GB and vector v """ beam_type = 2 A = None i1 = None i2 = None j = None c1 = None c2 = None d1 = None d2 = None e1 = None e2 = None f1 = None f2 = None k1 = None k2 = None delta_n = None return PBEND(pid, mid, beam_type, A, i1, i2, j, c1, c2, d1, d2, e1, e2, f1, f2, k1, k2, nsm, rc, zc, delta_n, fsi, rm, t, p, rb, theta_b, comment=comment)
#@classmethod #def add_beam_type_3(cls, pid, mid, #fsi, rm, t, p, rb, theta_b, ##sacl, alpha, nsm, rc, zc, flange, kx, ky, kz, sy, sz, #comment=''): #""" #+-------+------+-------+-----+----+----+--------+----+--------+ #| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 7 | 8 | #+=======+======+=======+=====+====+====+========+====+========+ #| PBEND | PID | MID | FSI | RM | T | P | RB | THETAB | #+-------+------+-------+-----+----+----+--------+----+--------+ #| | SACL | ALPHA | NSM | RC | ZC | FLANGE | | | #+-------+------+-------+-----+----+----+--------+----+--------+ #| | KX | KY | KZ | | SY | SZ | | | #+-------+------+-------+-----+----+----+--------+----+--------+ #""" #beam_type = 3 #A = None #i1 = None #i2 = None #j = None #c1 = None #c2 = None #d1 = None #d2 = None #e1 = None #e2 = None #f1 = None #f2 = None #k1 = None #k2 = None #rc = None #zc = None #nsm = None #delta_n = None #return PBEND(pid, mid, beam_type, A, i1, i2, j, #c1, c2, d1, d2, e1, e2, f1, f2, k1, k2, #nsm, rc, zc, delta_n, fsi, rm, t, p, rb, theta_b, comment=comment)
[docs] @classmethod def add_card(cls, card, comment=''): """ Adds a PBEND 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') value3 = integer_or_double(card, 3, 'Area/FSI') #print("PBEND: area/fsi=%s" % value3) # MSC/NX option A A = None i1 = None i2 = None j = None c1 = None c2 = None d1 = None d2 = None e1 = None e2 = None f1 = None f2 = None k1 = None k2 = None delta_n = None # MSC option B rm = None t = None p = None # NX option B #sacl = None #alpha = None #flange = None #kx = None #ky = None #kz = None #sy = None #sz = None if isinstance(value3, float): fsi = 0 beam_type = 1 #: Area of the beam cross section A = double(card, 3, 'A') #: Area moments of inertia in planes 1 and 2. i1 = double(card, 4, 'I1') i2 = double(card, 5, 'I2') #: Torsional stiffness :math:`J` j = double(card, 6, 'J') # line2 #: The r,z locations from the geometric centroid for stress #: data recovery. c1 = double_or_blank(card, 9, 'c1', 0.) c2 = double_or_blank(card, 10, 'c2', 0.) d1 = double_or_blank(card, 11, 'd1', 0.) d2 = double_or_blank(card, 12, 'd2', 0.) e1 = double_or_blank(card, 13, 'e1', 0.) e2 = double_or_blank(card, 14, 'e2', 0.) f1 = double_or_blank(card, 15, 'f1', 0.) f2 = double_or_blank(card, 16, 'f2', 0.) # line 3 #: Shear stiffness factor K in K*A*G for plane 1. k1 = double_or_blank(card, 17, 'k1') #: Shear stiffness factor K in K*A*G for plane 2. k2 = double_or_blank(card, 18, 'k2') #: Nonstructural mass per unit length. nsm = double_or_blank(card, 19, 'nsm', 0.) #: Radial offset of the geometric centroid from points GA and GB. rc = double_or_blank(card, 20, 'rc', 0.) #: Offset of the geometric centroid in a direction perpendicular #: to the plane of points GA and GB and vector v zc = double_or_blank(card, 21, 'zc', 0.) #: Radial offset of the neutral axis from the geometric #: centroid, positive is toward the center of curvature delta_n = double_or_blank(card, 22, 'delta_n', 0.) elif isinstance(value3, int): # alternate form beam_type = 2 #: Flag selecting the flexibility and stress intensification #: factors. See Remark 3. (Integer = 1, 2, or 3) fsi = integer(card, 3, 'fsi') if fsi in [1, 2, 3]: # assuming MSC #: Mean cross-sectional radius of the curved pipe rm = double(card, 4, 'rm') #: Wall thickness of the curved pipe t = double(card, 5, 't') #: Internal pressure p = double_or_blank(card, 6, 'p') # line3 # Non-structural mass :math:`nsm` nsm = double_or_blank(card, 11, 'nsm', 0.) rc = double_or_blank(card, 12, 'rc', 0.) zc = double_or_blank(card, 13, 'zc', 0.) elif fsi in [4, 5, 6]: # Non-structural mass :math:`nsm` nsm = double_or_blank(card, 11, 'nsm', 0.) rc = double_or_blank(card, 12, 'rc', 0.) zc = double_or_blank(card, 13, 'zc', 0.) #sacl = double_or_blank(card, 9, 'sacl') #alpha = double_or_blank(card, 10, 'alpha', 0.) #flange = integer_or_blank(card, 15, 'flange', 0) #kx = double_or_blank(card, 18, 'kx', 1.0) #ky = double_or_blank(card, 19, 'ky', 1.0) #kz = double_or_blank(card, 20, 'kz', 1.0) #sy = double_or_blank(card, 22, 'sy', 1.0) #sz = double_or_blank(card, 23, 'sz', 1.0) else: assert fsi in [1, 2, 3, 4, 5, 6], 'pid=%s fsi=%s\ncard:%s' % (pid, fsi, card) else: raise RuntimeError('Area/FSI on CBEND must be defined...') assert fsi in [0, 1, 2, 3, 4, 5, 6], 'pid=%s fsi=%s\ncard:%s' % (pid, fsi, card) #: Bend radius of the line of centroids rb = double_or_blank(card, 7, 'rb') #: Arc angle :math:`\theta_B` of element (optional) theta_b = double_or_blank(card, 8, 'thetab') assert len(card) <= 23, f'len(PBEND card) = {len(card):d}\ncard={card}' return PBEND(pid, mid, beam_type, A, i1, i2, j, c1, c2, d1, d2, e1, e2, f1, f2, k1, k2, nsm, rc, zc, delta_n, fsi, rm, t, p, rb, theta_b, comment=comment)
[docs] def validate(self): """card checking method""" if self.delta_n is not None and not isinstance(self.delta_n, float_types): raise RuntimeError('delta_n=%r must be None or a float; type=%s; fsi=%s\n%s' % ( self.delta_n, type(self.delta_n), self.fsi, str(self)))
#def Nsm(self): #""".. warning:: nsm field not supported fully on PBEND card""" #raise RuntimeError(self.nsm[0]) #return self.nsm
[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 PBEND 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 MassPerLength(self) -> float: """m/L = rho*A + nsm""" rho = self.mid_ref.Rho() assert isinstance(self.A, float), self.get_stats() return self.A * rho + self.nsm
[docs] def raw_fields(self) -> list[Union[str, float, int, None]]: return self.repr_fields()
[docs] def repr_fields(self): list_fields = ['PBEND', self.pid, self.Mid(), ] # other if self.beam_type == 1: list_fields += [self.A, self.i1, self.i2, self.j, self.rb, self.theta_b, self.c1, self.c2, self.d1, self.d2, self.e1, self.e2, self.f1, self.f2, self.k1, self.k2, self.nsm, self.rc, self.zc, self.delta_n] #print("beam_type=0 I1=%s I2=%s; J=%s RM=%s T=%s P=%s" % ( #self.i1, self.i2, self.j, self.rm, self.t, self.p), list_fields) elif self.beam_type == 2: list_fields += [self.fsi, self.rm, self.t, self.p, self.rb, self.theta_b, None, None, self.nsm, self.rc, self.zc] elif self.beam_type == 0: # dunno list_fields += [self.A, self.i1, self.i2, self.j, self.rb, self.theta_b, self.c1, self.c2, self.d1, self.d2, self.e1, self.e2, self.f1, self.f2, self.k1, self.k2, self.nsm, self.rc, self.zc, self.delta_n] #print("beam_type=0 I1=%s I2=%s; J=%s RM=%s T=%s P=%s" % ( #self.i1, self.i2, self.j, self.rm, self.t, self.p), list_fields) else: raise ValueError('only beam_type=1 and 2 supported; beam_type/fsi=%s' % self.beam_type) 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 split_arbitrary_thickness_section(key: str, value: Union[str, float, list[int]]) -> tuple[int, Union[float, list[int]]]: """ Helper method for PBRSECT/PBMSECT >>> key = 'T(11)' >>> value = '[1.2,PT=(123,204)]' >>> index, out = split_arbitrary_thickness_section(key, value) >>> index 11 >>> out [1.2, [123, 204]] """ assert key.endswith(')'), 'key=%r' % key # T(3), CORE(3) key_id = key[:-1].split('(', 1)[1] key_id = int(key_id) if isinstance(value, (int, float)): return key_id, value value = value.replace(' ', '') if 'PT' in value: bracketed_values = value.strip('[]') sline = bracketed_values.split(',', 1) thicknessi = float(sline[0]) pt_value = sline[1].split('=') assert pt_value[0] == 'PT', pt_value points = pt_value[1].strip('()').split(',') assert len(points) == 2, pt_value int_points = [int(pointi) for pointi in points] out = [thicknessi, int_points] else: out = float(value) return key_id, out
[docs] def get_beam_sections(line: str) -> list[str]: """ Splits a PBRSECT/PBMSECT line >>> line = 'OUTP=10,BRP=20,T=1.0,T(11)=[1.2,PT=(123,204)], NSM=0.01' >>> sections = get_beam_sections(line) >>> sections ['OUTP=10', 'BRP=20', 'T=1.0', 'T(11)=[1.2,PT=(123,204)', 'NSM=0.01'], sections """ line = line.replace(' ', '') words = [] i0 = None nopen_parantheses = 0 nopen_brackets = 0 i = 0 while i < len(line): char = line[i] if char == '(': nopen_parantheses += 1 elif char == ')': nopen_parantheses -= 1 elif char == '[': nopen_brackets += 1 elif char == ']': nopen_brackets -= 1 elif nopen_parantheses == 0 and nopen_brackets == 0 and char == ',': word = line[i0:i].strip(',') words.append(word) i0 = i i += 1 word = line[i0:].strip(',') if word: words.append(word) return words
[docs] def write_arbitrary_beam_section(inps, ts, branch_paths, nsm, outp_id, core=None): """writes the PBRSECT/PBMSECT card""" end = '' for key, dicts in [('INP', inps), ('T', ts), ('BRP', branch_paths), ('CORE', core)]: if dicts is None: continue # dicts = {int index : int/float value} for index, value1 in sorted(dicts.items()): if index == 0: if isinstance(value1, list): for value1i in value1: end += ' %s=%s,\n' % (key, value1i) else: end += ' %s=%s,\n' % (key, value1) else: if isinstance(value1, list): if len(value1) == 2: assert len(value1) == 2, value1 thicknessi = value1[0] points = value1[1] end += ' %s(%s)=[%s, PT=(%s,%s)],\n' % ( key, index, thicknessi, points[0], points[1]) else: assert len(value1) == 1, value1 end += ' %s=%s,\n' % (key, value1[0]) else: end += ' %s(%s)=%s,\n' % (key, index, value1) for key, value in [('NSM', nsm), ('outp', outp_id),]: if value: end += ' %s=%s,\n' % (key, value) if end: end = end[:-2] + '\n' return end
[docs] def plot_arbitrary_section(model, self, inps, ts, branch_paths, nsm, outp_ref, figure_id=1, title='', show=False): """helper for PBRSECT/PBMSECT""" import matplotlib.pyplot as plt if ts: try: ts2 = {1 : ts[1]} except KeyError: print('ts =', ts) print(self) raise for key, value in ts.items(): if key == 1: ts2[key] = value else: thickness_value, section = value p1, p2 = section p1, p2 = min(p1, p2), max(p1, p2) ts2[(p1, p2)] = thickness_value ts = ts2 def _plot_rectangles(ax, sections, xy_dict, ts): """helper method for ``plot_arbitrary_section``""" for section in sections: #p1, p2 = section out = xy_dict[section] (x1, x2, y1, y2) = out dy = y2 - y1 dx = x2 - x1 length = np.sqrt(dy**2 + dx**2) angle = np.arctan2(dy, dx) angled = np.degrees(angle) thickness = ts.get(section, ts[1]) assert isinstance(thickness, float), thickness width = thickness dx_width = +width / 2. * np.sin(angle) dy_width = -width / 2. * np.cos(angle) xy = (x1+dx_width, y1+dy_width) rect_height = length rect_width = width #print('dxy_width = (%.2f,%.2f)' % (dx_width, dy_width)) #print('p1,2=(%s, %s) xy=(%.2f,%.2f) t=%s width=%s height=%s angled=%s\n' % ( #p1, p2, xy[0], xy[1], thickness, rect_width, rect_height, angled)) rect = plt.Rectangle(xy, rect_height, rect_width, angle=angled, fill=True) #, alpha=1.2+0.15*i) ax.add_patch(rect) #break def add_to_sections(sections, xy, points, x, y): """helper method for ``plot_arbitrary_section``""" for i in range(len(points)-1): p1 = points[i] p2 = points[i+1] x1 = x[i] x2 = x[i+1] y1 = y[i] y2 = y[i+1] p1, p2 = min(p1, p2), max(p1, p2) sections.add((p1, p2)) xy[(p1, p2)] = (x1, x2, y1, y2) fig = plt.figure(figure_id) ax = fig.add_subplot(111, aspect='equal') #print('outp:\n%s' % outp_ref) out_points = outp_ref.ids if self.form == 'CP' and out_points[0] != out_points[-1]: out_points = out_points + [out_points[0]] #out_points_ref = outp_ref.ids_ref #print('out_points =', out_points) #out_xyz = np.array([point.get_position() for point in out_points_ref]) out_xyz = np.array([model.points[point_id].get_position() for point_id in out_points]) #print('out_xyz:\n%s' % out_xyz) sections = set() x = out_xyz[:, 0] y = out_xyz[:, 1] xy = {} add_to_sections(sections, xy, out_points, x, y) #print('x=%s y=%s' % (x, y)) ax.plot(x, y, '-o', label='OUTP') all_points = {point_id : (xi, yi) for point_id, xi, yi in zip(out_points, x, y)} #print('out_points =', out_points) #print('all_points =', all_points) #plt.show() for key, unused_thickness in ts.items(): if key == 1: continue #print(thickness) section = key #print(model.points) #thickness_value, section = thickness out_xyz = np.array([model.points[point_id].get_position() for point_id in section]) x = out_xyz[:, 0] y = out_xyz[:, 1] #print('adding t=%s section %s' % (thickness, str(section))) #print(sections) add_to_sections(sections, xy, section, x, y) #print(sections) if branch_paths: for key, brp_set_ref in branch_paths.items(): brp_points = brp_set_ref.ids brp_points_ref = brp_set_ref.ids_ref brp_xyz = np.array([point.get_position() for point in brp_points_ref]) #print('branch = %s' % brp_points) x = brp_xyz[:, 0] y = brp_xyz[:, 1] ax.plot(x, y, '-o', label='BRP(%i)' % key) add_to_sections(sections, xy, brp_points, x, y) for point_id, xi, yi in zip(brp_points, x, y): all_points[point_id] = (xi, yi) #print('xy =', xy) if inps: #print('inps! = %s' % inps) for key, inp in inps.items(): if isinstance(inp, int): inp = [inp] for inpi in inp: inp_ref = model.Set(inpi) #inp_ref.cross_reference_set(model, 'Point', msg='') inp_points = inp_ref.ids #if inp_points[0] != inp_points[-1]: #inp_points = inp_points + [inp_points[0]] #print('inp_points = %s' % inp_points) #inp_points_ref = inp_ref.ids_ref inp_xyz = np.array([model.points[point_id].get_position() for point_id in inp_points]) #inp_xyz = np.array([point.get_position() for point in inp_points_ref]) #print('inp_xyz:\n%s' % (inp_xyz[:, :2])) x = inp_xyz[:, 0] y = inp_xyz[:, 1] ax.plot(x, y, '--x', label='INP') if ts: _plot_rectangles(ax, sections, xy, ts) #print('all_points =', all_points) for point_id, xy in sorted(all_points.items()): ax.annotate(str(point_id), xy=xy) ax.grid(True) ax.set_title(title) ax.set_ylabel('Y') ax.set_xlabel('X') ax.legend() if show: plt.show()