Source code for sectionproperties.pre.nastran_sections

from sectionproperties.pre.sections import (
    Geometry, RectangularSection, CustomSection, MergedSection
)
from sectionproperties.pre.pre import create_mesh
import numpy as np


[docs]class BARSection(Geometry): """Constructs a BAR section with the center at the origin *(0, 0)*, with two parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ [5]_ for definition of parameters. Added by JohnDN90. :param float DIM1: Width (x) of bar :param float DIM2: Depth (y) of bar :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a BAR cross-section with a depth of 1.5 and width of 2.0, and generates a mesh with a maximum triangular area of 0.001:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.BARSection(DIM1=2.0, DIM2=1.5) mesh = geometry.create_mesh(mesh_sizes=[0.001]) .. figure:: ../images/sections/bar_geometry.png :align: center :scale: 75 % BAR section geometry. .. figure:: ../images/sections/bar_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, shift=[0, 0]): """Inits the BARSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 # assign control point control_points = [[0., 0.]] # shift = [-0.5*DIM1+shift[0], -0.5*DIM2+shift[1]] super().__init__(control_points, shift) # construct the points and facets self.points = [ [-0.5*DIM1, -0.5*DIM2], [0.5*DIM1, -0.5*DIM2], [0.5*DIM1, 0.5*DIM2], [-0.5*DIM1, 0.5*DIM2] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM1-shift[0], 0.5*self.DIM2-shift[1]) D = (0.5*self.DIM1-shift[0], -0.5*self.DIM2-shift[1]) E = (-0.5*self.DIM1-shift[0], -0.5*self.DIM2-shift[1]) F = (-0.5*self.DIM1-shift[0], 0.5*self.DIM2-shift[1]) return C, D, E, F
[docs]class BOXSection(Geometry): """ Constructs a BOX section with the center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ [5]_ for definition of parameters. Added by JohnDN90. :param float DIM1: Width (x) of box :param float DIM2: Depth (y) of box :param float DIM3: Thickness of box in y direction :param float DIM4: Thickness of box in x direction :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a BOX cross-section with a depth of 3.0 and width of 4.0, and generates a mesh with a maximum triangular area of 0.001:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.BOXSection(DIM1=4.0, DIM2=3.0, DIM3=0.375, DIM4=0.5) mesh = geometry.create_mesh(mesh_sizes=[0.001]) .. figure:: ../images/sections/box_geometry.png :align: center :scale: 75 % BOX section geometry. .. figure:: ../images/sections/box_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the BOXSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(2.0*DIM4 < DIM1, "Invalid geometry specified.") np.testing.assert_(2.0*DIM3 < DIM2, "Invalid geometry specified.") # assign control point control_points = [[0., 0.5*DIM2 - 0.5*DIM3]] super().__init__(control_points, shift) # specify a hole in the centre of the Box self.holes = [[0., 0.]] # construct the points and facets self.points = [ [-0.5*DIM1, -0.5*DIM2], [0.5*DIM1, -0.5*DIM2], [0.5*DIM1, 0.5*DIM2], [-0.5*DIM1, 0.5*DIM2], [-0.5*DIM1 + DIM4, -0.5*DIM2 + DIM3], [0.5*DIM1 - DIM4, -0.5*DIM2 + DIM3], [0.5*DIM1 - DIM4, 0.5*DIM2 - DIM3], [-0.5*DIM1 + DIM4, 0.5*DIM2 - DIM3] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM1-shift[0], 0.5*self.DIM2-shift[1]) D = (0.5*self.DIM1-shift[0], -0.5*self.DIM2-shift[1]) E = (-0.5*self.DIM1-shift[0], -0.5*self.DIM2-shift[1]) F = (-0.5*self.DIM1-shift[0], 0.5*self.DIM2-shift[1]) return C, D, E, F
[docs]class BOX1Section(Geometry): """Constructs a BOX1 section with the center at the origin *(0, 0)*, with six parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of box :param float DIM2: Depth (y) of box :param float DIM3: Thickness of top wall :param float DIM4: Thickness of bottom wall :param float DIM5: Thickness of left wall :param float DIM6: Thickness of right wall :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a BOX1 cross-section with a depth of 3.0 and width of 4.0, and generates a mesh with a maximum triangular area of 0.007:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.BOX1Section( DIM1=4.0, DIM2=3.0, DIM3=0.375, DIM4=0.5, DIM5=0.25, DIM6=0.75 ) mesh = geometry.create_mesh(mesh_sizes=[0.007]) .. figure:: ../images/sections/box1_geometry.png :align: center :scale: 75 % BOX1 section geometry. .. figure:: ../images/sections/box1_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, shift=[0, 0]): """Inits the Box1Section class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 DIM5 *= 1.0 DIM6 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 self.DIM5 = DIM5 self.DIM6 = DIM6 # Ensure dimensions are physically relevant np.testing.assert_(DIM5+DIM6 < DIM1, "Invalid geometry specified.") np.testing.assert_(DIM3+DIM4 < DIM2, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM1, 0.5*DIM4]] shift = [-0.5*DIM1+shift[0], -0.5*DIM2+shift[1]] super().__init__(control_points, shift) # specify a hole in the centre of the Box self.holes = [[DIM6 + 0.5*(DIM1-DIM5), DIM4+0.5*(DIM2-DIM3)]] # construct the points and facets self.points = [ [0., 0.], [DIM1, 0.], [DIM1, DIM2], [0., DIM2], [DIM6, DIM4], [DIM1-DIM5, DIM4], [DIM1-DIM5, DIM2-DIM3], [DIM6, DIM2-DIM3] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """ Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM1-shift[0], 0.5*self.DIM2-shift[1]) D = (0.5*self.DIM1-shift[0], -0.5*self.DIM2-shift[1]) E = (-0.5*self.DIM1-shift[0], -0.5*self.DIM2-shift[1]) F = (-0.5*self.DIM1-shift[0], 0.5*self.DIM2-shift[1]) return C, D, E, F
[docs]class CHANSection(Geometry): """ Constructs a CHAN (C-Channel) section with the web's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of the CHAN-section :param float DIM2: Depth (y) of the CHAN-section :param float DIM3: Thickness of web (vertical portion) :param float DIM4: Thickness of flanges (top/bottom portion) :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a CHAN cross-section with a depth of 4.0 and width of 2.0, and generates a mesh with a maximum triangular area of 0.008:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.CHANSection(DIM1=2.0, DIM2=4.0, DIM3=0.25, DIM4=0.5) mesh = geometry.create_mesh(mesh_sizes=[0.008]) .. figure:: ../images/sections/chan_geometry.png :align: center :scale: 75 % CHAN section geometry. .. figure:: ../images/sections/chan_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the CHANSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(2.0*DIM4 < DIM2, "Invalid geometry specified.") np.testing.assert_(DIM3 < DIM1, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM1, 0.5*DIM4]] shift = [-0.5*DIM3+shift[0], -0.5*DIM2+shift[1]] super().__init__(control_points, shift) # construct the points and facets self.points = [ [0., 0.], [DIM1, 0.], [DIM1, DIM4], [DIM3, DIM4], [DIM3, DIM2-DIM4], [DIM1, DIM2-DIM4], [DIM1, DIM2], [0., DIM2] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """ Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (self.DIM1-0.5*self.DIM3-shift[0], 0.5*self.DIM2-shift[1]) D = (self.DIM1-0.5*self.DIM3-shift[0], -0.5*self.DIM2-shift[1]) E = (-0.5*self.DIM3-shift[0], -0.5*self.DIM2-shift[1]) F = (-0.5*self.DIM3-shift[0], 0.5*self.DIM2-shift[1]) return C, D, E, F
[docs]class CHAN1Section(Geometry): """ Constructs a CHAN1 (C-Channel) section with the web's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of channels :param float DIM2: Thicknesss (x) of web :param float DIM3: Spacing between channels (length of web) :param float DIM4: Depth (y) of CHAN1-section :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a CHAN1 cross-section with a depth of 4.0 and width of 1.75, and generates a mesh with a maximum triangular area of 0.01:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.CHAN1Section(DIM1=0.75, DIM2=1.0, DIM3=3.5, DIM4=4.0) mesh = geometry.create_mesh(mesh_sizes=[0.01]) .. figure:: ../images/sections/chan1_geometry.png :align: center :scale: 75 % CHAN1 section geometry. .. figure:: ../images/sections/chan1_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the CHAN1Section class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 > DIM3, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM1, 0.5*DIM4]] shift = [-0.5*DIM2+shift[0], -0.5*DIM4+shift[1]] super().__init__(control_points, shift) # construct the points and facets tf = 0.5 * (DIM4 - DIM3) self.points = [ [0, 0], [DIM1+DIM2, 0], [DIM1+DIM2, tf], [DIM2, tf], [DIM2, tf+DIM3], [DIM2+DIM1, tf+DIM3], [DIM2+DIM1, DIM4], [0, DIM4] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """ Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM2+self.DIM1-shift[0], 0.5*self.DIM4-shift[1]) D = (0.5*self.DIM2+self.DIM1-shift[0], -0.5*self.DIM4-shift[1]) E = (-0.5*self.DIM2-shift[0], -0.5*self.DIM4-shift[1]) F = (-0.5*self.DIM2-shift[0], 0.5*self.DIM4-shift[1]) return C, D, E, F
[docs]class CHAN2Section(Geometry): """ Constructs a CHAN2 (C-Channel) section with the bottom web's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Thickness of channels :param float DIM2: Thickness of web :param float DIM3: Depth (y) of CHAN2-section :param float DIM4: Width (x) of CHAN2-section :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a CHAN2 cross-section with a depth of 2.0 and width of 4.0, and generates a mesh with a maximum triangular area of 0.01:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.CHAN2Section(DIM1=0.375, DIM2=0.5, DIM3=2.0, DIM4=4.0) mesh = geometry.create_mesh(mesh_sizes=[0.01]) .. figure:: ../images/sections/chan2_geometry.png :align: center :scale: 75 % CHAN2 section geometry. .. figure:: ../images/sections/chan2_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the CHAN2Section class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 > 2.0*DIM1, "Invalid geometry specified.") np.testing.assert_(DIM3 > DIM2, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM4, 0.5*DIM2]] shift = [-0.5*DIM4+shift[0], -0.5*DIM2+shift[1]] super().__init__(control_points, shift) # construct the points and facets self.points = [ [0., 0.], [DIM4, 0.], [DIM4, DIM3], [DIM4-DIM1, DIM3], [DIM4-DIM1, DIM2], [DIM1, DIM2], [DIM1, DIM3], [0., DIM3] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """ Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM4-shift[0], self.DIM3-0.5*self.DIM2-shift[1]) D = (0.5*self.DIM4-shift[0], -0.5*self.DIM2-shift[1]) E = (-0.5*self.DIM4-shift[0], -0.5*self.DIM2-shift[1]) F = (-0.5*self.DIM4-shift[0], self.DIM3-0.5*self.DIM2-shift[1]) return C, D, E, F
[docs]class CROSSSection(Geometry): """ Constructs Nastran's cruciform/cross section with the intersection's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Twice the width of horizontal member protruding from the vertical center member :param float DIM2: Thickness of the vertical member :param float DIM3: Depth (y) of the CROSS-section :param float DIM4: Thickness of the horizontal members :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a rectangular cross-section with a depth of 3.0 and width of 1.875, and generates a mesh with a maximum triangular area of 0.008:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.CROSSSection(DIM1=1.5, DIM2=0.375, DIM3=3.0, DIM4=0.25) mesh = geometry.create_mesh(mesh_sizes=[0.008]) .. figure:: ../images/sections/cross_geometry.png :align: center :scale: 75 % Cruciform/cross section geometry. .. figure:: ../images/sections/cross_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the CROSSSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 < DIM3, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM1+0.5*DIM2, 0.5*DIM3]] shift = [-(0.5*DIM1+0.5*DIM2)+shift[0], -(0.5*DIM3)+shift[1]] super().__init__(control_points, shift) # construct the points and facets d = 0.5*(DIM3 - DIM4) self.points = [ [0.5*DIM1, 0], [0.5*DIM1+DIM2, 0], [0.5*DIM1+DIM2, d], [DIM1+DIM2, d], [DIM1+DIM2, d+DIM4], [0.5*DIM1+DIM2, d+DIM4], [0.5*DIM1+DIM2, DIM3], [0.5*DIM1, DIM3], [0.5*DIM1, d+DIM4], [0, d+DIM4], [0, d], [0.5*DIM1, d] ] self.facets = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 0] ] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (-shift[0], 0.5*self.DIM3-shift[1]) D = (0.5*(self.DIM1+self.DIM2)-shift[0], -shift[1]) E = (-shift[0], -0.5*self.DIM3-shift[1]) F = (-0.5*(self.DIM1+self.DIM2)-shift[0], -shift[1]) return C, D, E, F
[docs]class FCROSSSection(Geometry): """ Constructs a flanged cruciform/cross section with the intersection's middle center at the origin *(0, 0)*, with eight parameters defining dimensions. Added by JohnDN90. :param float DIM1: Depth (y) of flanged cruciform :param float DIM2: Width (x) of flanged cruciform :param float DIM3: Thickness of vertical web :param float DIM4: Thickness of horizontal web :param float DIM5: Length of flange attached to vertical web :param float DIM6: Thickness of flange attached to vertical web :param float DIM7: Length of flange attached to horizontal web :param float DIM8: Thickness of flange attached to horizontal web :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example demonstrates the creation of a flanged cross section:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.FCROSSSection( DIM1=9.0, DIM2=6.0, DIM3=0.75, DIM4=0.625, DIM5=2.1, DIM6=0.375, DIM7=4.5, DIM8=0.564 ) mesh = geometry.create_mesh(mesh_sizes=[0.03]) .. figure:: ../images/sections/fcross_geometry.png :align: center :scale: 75 % Flanged Cruciform/cross section geometry. .. figure:: ../images/sections/fcross_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, DIM7, DIM8, shift=[0, 0]): """Inits the FCROSSSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 DIM5 *= 1.0 DIM6 *= 1.0 DIM7 *= 1.0 DIM8 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 self.DIM5 = DIM5 self.DIM6 = DIM6 self.DIM7 = DIM7 self.DIM7 = DIM8 # Ensure dimensions are physically relevant # TODO: Finish dimension checks. np.testing.assert_(DIM5 > DIM3, "Invalid geometry specified.") np.testing.assert_(DIM7 > DIM4, "Invalid geometry specified.") np.testing.assert_(DIM7 < DIM1, "Invalid geometry specified.") np.testing.assert_(DIM5 < DIM2, "Invalid geometry specified.") np.testing.assert_(DIM8 < (0.5*DIM2-0.5*DIM3), "Invalid geometry specified.") np.testing.assert_(DIM6 < (0.5*DIM1-0.5*DIM4), "Invalid geometry specified.") # assign control point control_points = [[0.0, 0.0]] shift = [shift[0], shift[1]] super().__init__(control_points, shift) # construct the points and facets self.points = [ [0.5*DIM3, -0.5*DIM4], [0.5*DIM2-DIM8, -0.5*DIM4], [0.5*DIM2-DIM8, -0.5*DIM7], [0.5*DIM2, -0.5*DIM7], [0.5*DIM2, 0.5*DIM7], [0.5*DIM2-DIM8, 0.5*DIM7], [0.5*DIM2-DIM8, 0.5*DIM4], [0.5*DIM3, 0.5*DIM4], [0.5*DIM3, 0.5*DIM1-DIM6], [0.5*DIM5, 0.5*DIM1-DIM6], [0.5*DIM5, 0.5*DIM1], [-0.5*DIM5, 0.5*DIM1], [-0.5*DIM5, 0.5*DIM1-DIM6], [-0.5*DIM3, 0.5*DIM1-DIM6], [-0.5*DIM3, 0.5*DIM4], [-0.5*DIM2+DIM8, 0.5*DIM4], [-0.5*DIM2+DIM8, 0.5*DIM7], [-0.5*DIM2, 0.5*DIM7], [-0.5*DIM2, -0.5*DIM7], [-0.5*DIM2+DIM8, -0.5*DIM7], [-0.5*DIM2+DIM8, -0.5*DIM4], [-0.5*DIM3, -0.5*DIM4], [-0.5*DIM3, -0.5*DIM1+DIM6], [-0.5*DIM5, -0.5*DIM1+DIM6], [-0.5*DIM5, -0.5*DIM1], [0.5*DIM5, -0.5*DIM1], [0.5*DIM5, -0.5*DIM1+DIM6], [0.5*DIM3, -0.5*DIM1+DIM6] ] self.facets = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 12], [12, 13], [13, 14], [14, 15], [15, 16], [16, 17], [17, 18], [18, 19], [19, 20], [20, 21], [21, 22], [22, 23], [23, 24], [24, 25], [25, 26], [26, 27], [27, 0] ] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (-shift[0], 0.5*self.DIM1-shift[1]) D = (0.5*self.DIM2-shift[0], -shift[1]) E = (-shift[0], -0.5*self.DIM1-shift[1]) F = (-0.5*self.DIM2-shift[0], -shift[1]) return C, D, E, F
[docs]class DBOXSection(Geometry): """ Constructs a DBOX section with the center at the origin *(0, 0)*, with ten parameters defining dimensions. See MSC Nastran documentation [1]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of the DBOX-section :param float DIM2: Depth (y) of the DBOX-section :param float DIM3: Width (x) of left-side box :param float DIM4: Thickness of left wall :param float DIM5: Thickness of center wall :param float DIM6: Thickness of right wall :param float DIM7: Thickness of top left wall :param float DIM8: Thickness of bottom left wall :param float DIM9: Thickness of top right wall :param float DIM10: Thickness of bottom right wall :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a DBOX cross-section with a depth of 3.0 and width of 8.0, and generates a mesh with a maximum triangular area of 0.01:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.DBOXSection( DIM1=8.0, DIM2=3.0, DIM3=3.0, DIM4=0.5, DIM5=0.625, DIM6=0.75, DIM7=0.375, DIM8=0.25, DIM9=0.5, DIM10=0.375 ) mesh = geometry.create_mesh(mesh_sizes=[0.01]) .. figure:: ../images/sections/dbox_geometry.png :align: center :scale: 75 % DBOX section geometry. .. figure:: ../images/sections/dbox_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, DIM7, DIM8, DIM9, DIM10, shift=[0, 0]): """Inits the DBOXSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 DIM5 *= 1.0 DIM6 *= 1.0 DIM7 *= 1.0 DIM8 *= 1.0 DIM9 *= 1.0 DIM10 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 self.DIM5 = DIM5 self.DIM6 = DIM6 self.DIM7 = DIM7 self.DIM8 = DIM8 self.DIM9 = DIM9 self.DIM10 = DIM10 # Ensure dimensions are physically relevant np.testing.assert_((DIM4+DIM5+DIM6) < DIM1, "Invalid geometry specified.") np.testing.assert_((DIM4+0.5*DIM5) < DIM3, "Invalid geometry specified.") np.testing.assert_((DIM7+DIM8) < DIM2, "Invalid geometry specified.") np.testing.assert_((DIM9+DIM10) < DIM2, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM3, 0.5*DIM8]] shift = [-0.5*DIM1+shift[0], -0.5*DIM2+shift[1]] super().__init__(control_points, shift) # specify a hole in the centre of the Box d2 = 0.5*(DIM1 - DIM6 - DIM3 - 0.5*DIM5) self.holes = [ [DIM4 + 0.5*(DIM3 - DIM4 - 0.5*DIM5), DIM8 + 0.5*(DIM2 - DIM8 - DIM7)], [DIM3 + 0.5*DIM5 + d2, DIM10 + 0.5*(DIM2 - DIM10 - DIM9)] ] # construct the points and facets self.points = [ [0., 0.], [DIM1, 0.], [DIM1, DIM2], [0., DIM2], [DIM4, DIM8], [DIM3-DIM5/2., DIM8], [DIM3-DIM5/2., DIM2-DIM7], [DIM4, DIM2-DIM7], [DIM3+DIM5/2., DIM10], [DIM1-DIM6, DIM10], [DIM1-DIM6, DIM2-DIM9], [DIM3+DIM5/2., DIM2-DIM9] ] self.facets = [ [0, 1], [1, 2], [2, 3], [3, 0], [4, 5], [5, 6], [6, 7], [7, 4], [8, 9], [9, 10], [10, 11], [11, 8] ] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """ Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5 * self.DIM1 - shift[0], 0.5 * self.DIM2 - shift[1]) D = (0.5 * self.DIM1 - shift[0], -0.5 * self.DIM2 - shift[1]) E = (-0.5 * self.DIM1 - shift[0], -0.5 * self.DIM2 - shift[1]) F = (-0.5 * self.DIM1 - shift[0], 0.5 * self.DIM2 - shift[1]) return C, D, E, F
[docs]class GBOXSection(Geometry): """ Constructs a GBOX section with the center at the origin *(0, 0)*, with six parameters defining dimensions. See ASTROS documentation [5]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of the GBOX-section :param float DIM2: Depth (y) of the GBOX-section :param float DIM3: Thickness of top flange :param float DIM4: Thickness of bottom flange :param float DIM5: Thickness of webs :param float DIM6: Spacing between webs :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a GBOX cross-section with a depth of 2.5 and width of 6.0, and generates a mesh with a maximum triangular area of 0.01:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.GBOXSection( DIM1=6.0, DIM2=2.5, DIM3=0.375, DIM4=0.25, DIM5=0.625, DIM6=1.0 ) mesh = geometry.create_mesh(mesh_sizes=[0.01]) .. figure:: ../images/sections/gbox_geometry.png :align: center :scale: 75 % GBOX section geometry. .. figure:: ../images/sections/gbox_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, shift=[0, 0]): """Inits the GBOXSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 DIM5 *= 1.0 DIM6 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 self.DIM5 = DIM5 self.DIM6 = DIM6 # Ensure dimensions are physically relevant np.testing.assert_((DIM3+DIM4) < DIM2, "Invalid geometry specified.") np.testing.assert_((2.0*DIM5+DIM6) < DIM1, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM1, 0.5*DIM4]] shift = [-(0.5*DIM1)+shift[0], -(DIM4 + 0.5*(DIM2-DIM3-DIM4))+shift[1]] super().__init__(control_points, shift) # specify a hole in the centre of the GBOX self.holes = [[0.5*DIM1, 0.5*DIM2]] # construct the points and facets d = 0.5*(DIM1 - DIM6 - 2.0 * DIM5) self.points = [ [0., 0.], [DIM1, 0.], [DIM1, DIM4], [d + 2. * DIM5 + DIM6, DIM4], [d + 2. * DIM5 + DIM6, DIM2 - DIM3], [DIM1, DIM2 - DIM3], [DIM1, DIM2], [0., DIM2], [0., DIM2 - DIM3], [d, DIM2 - DIM3], [d, DIM4], [0., DIM4], [d + DIM5, DIM4], [d + DIM5 + DIM6, DIM4], [d + DIM5 + DIM6, DIM2 - DIM3], [d + DIM5, DIM2 - DIM3] ] self.facets = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 0], [12, 13], [13, 14], [14, 15], [15, 12] ] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM1-shift[0], 0.5*self.DIM2-shift[1]) D = (0.5*self.DIM1-shift[0], -0.5*self.DIM2-shift[1]) E = (-0.5*self.DIM1-shift[0], -0.5*self.DIM2-shift[1]) F = (-0.5*self.DIM1-shift[0], 0.5*self.DIM2-shift[1]) return C, D, E, F
[docs]class HSection(Geometry): """Constructs a H section with the middle web's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Spacing between vertical flanges (length of web) :param float DIM2: Twice the thickness of the vertical flanges :param float DIM3: Depth (y) of the H-section :param float DIM4: Thickness of the middle web :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a H cross-section with a depth of 3.5 and width of 2.75, and generates a mesh with a maximum triangular area of 0.005:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.HSection(DIM1=2.0, DIM2=0.75, DIM3=3.5, DIM4=0.25) mesh = geometry.create_mesh(mesh_sizes=[0.005]) .. figure:: ../images/sections/h_geometry.png :align: center :scale: 75 % H section geometry. .. figure:: ../images/sections/h_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the HSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 < DIM3, "Invalid geometry specified.") d1 = 0.5 * (DIM3 - DIM4) d2 = 0.5 * DIM2 # assign control point control_points = [[0.5*d2, 0.5*DIM3]] shift = [-0.5*(DIM2+DIM1)+shift[0], -0.5*DIM3+shift[1]] super().__init__(control_points, shift) # construct the points and facets self.points = [ [0, 0], [d2, 0], [d2, d1], [d2+DIM1, d1], [d2+DIM1, 0], [DIM1+DIM2, 0], [DIM1+DIM2, DIM3], [DIM1+DIM2-d2, DIM3], [DIM1+DIM2-d2, d1+DIM4], [d2, d1+DIM4], [d2, DIM3], [0, DIM3] ] self.facets = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 0] ] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*(self.DIM1+self.DIM2)-shift[0], 0.5*self.DIM3-shift[1]) D = (0.5*(self.DIM1+self.DIM2)-shift[0], -0.5*self.DIM3-shift[1]) E = (-0.5*(self.DIM1+self.DIM2)-shift[0], -0.5*self.DIM3-shift[1]) F = (-0.5*(self.DIM1+self.DIM2)-shift[0], 0.5*self.DIM3-shift[1]) return C, D, E, F
[docs]class HATSection(Geometry): """Constructs a Hat section with the top most section's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Note that HAT in ASTROS is actually HAT1 in this code. Added by JohnDN90. :param float DIM1: Depth (y) of HAT-section :param float DIM2: Thickness of HAT-section :param float DIM3: Width (x) of top most section :param float DIM4: Width (x) of bottom sections :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a HAT cross-section with a depth of 1.25 and width of 2.5, and generates a mesh with a maximum triangular area of 0.001:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.HATSection(DIM1=1.25, DIM2=0.25, DIM3=1.5, DIM4=0.5) mesh = geometry.create_mesh(mesh_sizes=[0.001]) .. figure:: ../images/sections/hat_geometry.png :align: center :scale: 75 % HAT section geometry. .. figure:: ../images/sections/hat_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the HATSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(2.0*DIM2 < DIM1, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM4, 0.5*DIM2]] shift = [-DIM4-0.5*DIM3+shift[0], -DIM1+0.5*DIM2+shift[1]] super().__init__(control_points, shift) # construct the points and facets self.points = [ [0., 0.], [DIM4+DIM2, 0.], [DIM4+DIM2, DIM1-DIM2], [DIM4+DIM3-DIM2, DIM1-DIM2], [DIM4+DIM3-DIM2, 0.], [2*DIM4+DIM3, 0.], [2.*DIM4+DIM3, DIM2], [DIM4+DIM3, DIM2], [DIM4+DIM3, DIM1], [DIM4, DIM1], [DIM4, DIM2], [0., DIM2] ] self.facets = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 0] ] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the origin by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM3 - shift[0], 0.5*self.DIM2 - shift[1]) D = (0.5*self.DIM3 + self.DIM4 - shift[0], -self.DIM1 + self.DIM2 - shift[1]) E = (-0.5*self.DIM3 - self.DIM4 - shift[0], -self.DIM1 + self.DIM2 - shift[1]) F = (-0.5*self.DIM3 - shift[0], 0.5*self.DIM2 - shift[1]) return C, D, E, F
[docs]class HAT1Section(Geometry): """ Constructs a HAT1 section with the bottom plate's bottom center at the origin *(0, 0)*, with five parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [5]_ for definition of parameters. Note that in ASTROS, HAT1 is called HAT. Added by JohnDN90. :param float DIM1: Width(x) of the HAT1-section :param float DIM2: Depth (y) of the HAT1-section :param float DIM3: Width (x) of hat's top flange :param float DIM4: Thickness of hat stiffener :param float DIM5: Thicknesss of bottom plate :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a HAT1 cross-section with a depth of 2.0 and width of 4.0, and generates a mesh with a maximum triangular area of 0.005:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.HAT1Section(DIM1=4.0, DIM2=2.0, DIM3=1.5, DIM4=0.1875, DIM5=0.375) mesh = geometry.create_mesh(mesh_sizes=[0.005]) .. figure:: ../images/sections/hat1_geometry.png :align: center :scale: 75 % HAT1 section geometry. .. figure:: ../images/sections/hat1_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, DIM5, shift=[0, 0]): """Inits the HAT1Section class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 DIM5 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 self.DIM5 = DIM5 # Ensure dimensions are physically relevant np.testing.assert_((2.0*DIM4+DIM5) < DIM2, "Invalid geometry specified.") np.testing.assert_(DIM3 < DIM1, "Invalid geometry specified.") shift = [-0.5*DIM1+shift[0], shift[1]] # create bottom rectangular plate bottom_plate = RectangularSection(d=DIM5, b=DIM1, shift=shift) # create the hat stiffener d1 = DIM2 - DIM5 d2 = DIM4 d4 = 0.5*(DIM1 - DIM3) # specify a hole in the combined plate and hat structure holes = [[0.5*DIM1, 0.5*DIM2]] # assign control point control_points = [[0.5*d4, DIM5 + 0.5*DIM4]] super().__init__(control_points, shift) # construct the points and facets points = [ [0., DIM5 + 0.], [d4 + d2, DIM5 + 0.], [d4 + d2, DIM5 + d1 - d2], [d4 + DIM3 - d2, DIM5 + d1 - d2], [d4 + DIM3 - d2, DIM5 + 0.], [2. * d4 + DIM3, DIM5 + 0.], [2. * d4 + DIM3, DIM5 + d2], [d4 + DIM3, DIM5 + d2], [d4 + DIM3, DIM5 + d1], [d4, DIM5 + d1], [d4, DIM5 + d2], [0, DIM5 + d2] ] facets = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 0] ] hat = CustomSection(points, facets, holes, control_points, shift=shift) # Create a list of the sections to merge section_list = [bottom_plate, hat] # Merge the three sections into one geometry geometry = MergedSection(section_list) # Clean the geometry and print information to the terminal geometry.clean_geometry(verbose=False) self.control_points = geometry.control_points self.shift = geometry.shift self.points = geometry.points self.facets = geometry.facets self.holes = geometry.holes
[docs] def create_mesh(self, mesh_sizes): """Creates a quadratic triangular mesh from the Geometry object. This is overloaded here to allow specifying only one mesh_size which is used for both regions in the Hat1 section. :param mesh_sizes: A list of maximum element areas corresponding to each region within the cross-section geometry. :type mesh_size: list[float] :return: Object containing generated mesh data :rtype: :class:`meshpy.triangle.MeshInfo` :raises AssertionError: If the number of mesh sizes does not match the number of regions """ mesh_sizes *= 2 str = "Number of mesh_sizes ({0}), should match the number of regions ({1})".format( len(mesh_sizes), len(self.control_points) ) assert(len(mesh_sizes) == len(self.control_points)), str return create_mesh(self.points, self.facets, self.holes, self.control_points, mesh_sizes)
[docs] def getStressPoints(self, shift=(0., 0.)): """ Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the origin by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (-0.5*self.DIM1 - shift[0], -shift[1]) D = (0.5*self.DIM1 - shift[0], -shift[1]) E = (-0.5*self.DIM3 - shift[0], self.DIM2 - shift[1]) F = (0.5*self.DIM3 - shift[0], self.DIM2 - shift[1]) return C, D, E, F
[docs]class HEXASection(Geometry): """ Constructs a HEXA (hexagon) section with the center at the origin *(0, 0)*, with three parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Spacing between bottom right point and right most point :param float DIM2: Width (x) of hexagon :param float DIM3: Depth (y) of hexagon :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a rectangular cross-section with a depth of 1.5 and width of 2.0, and generates a mesh with a maximum triangular area of 0.005:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.HEXASection(DIM1=0.5, DIM2=2.0, DIM3=1.5) mesh = geometry.create_mesh(mesh_sizes=[0.005]) .. figure:: ../images/sections/hexa_geometry.png :align: center :scale: 75 % HEXA section geometry. .. figure:: ../images/sections/hexa_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, shift=[0, 0]): """Inits the HEXASection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 # Ensure dimensions are physically relevant np.testing.assert_(DIM2 > DIM1, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM2, 0.5*DIM3]] shift = [-0.5*DIM2+shift[0], -0.5*DIM3+shift[1]] super().__init__(control_points, shift) # construct the points and facets self.points = [ [DIM1, 0.], [DIM2-DIM1, 0.], [DIM2, 0.5*DIM3], [DIM2-DIM1, DIM3], [DIM1, DIM3], [0., 0.5*DIM3] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (-shift[0], 0.5*self.DIM3-shift[1]) D = (-shift[0], -0.5*self.DIM3-shift[1]) E = (0.5*self.DIM2-shift[0], -shift[1]) F = (-0.5*self.DIM2-shift[0], -shift[1]) return C, D, E, F
[docs]class NISection(Geometry): """Constructs Nastran's I section with the bottom flange's middle center at the origin *(0, 0)*, with six parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for definition of parameters. Added by JohnDN90. :param float DIM1: Depth(y) of the I-section :param float DIM2: Width (x) of bottom flange :param float DIM3: Width (x) of top flange :param float DIM4: Thickness of web :param float DIM5: Thickness of bottom web :param float DIM6: Thickness of top web :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a Nastran I cross-section with a depth of 5.0, and generates a mesh with a maximum triangular area of 0.008:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.NISection( DIM1=5.0, DIM2=2.0, DIM3=3.0, DIM4=0.25, DIM5=0.375, DIM6=0.5 ) mesh = geometry.create_mesh(mesh_sizes=[0.008]) .. figure:: ../images/sections/ni_geometry.png :align: center :scale: 75 % Nastran's I section geometry. .. figure:: ../images/sections/ni_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, DIM5, DIM6, shift=[0, 0]): """Inits the NISection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 DIM5 *= 1.0 DIM6 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 self.DIM5 = DIM5 self.DIM6 = DIM6 # Ensure dimensions are physically relevant np.testing.assert_((DIM5 + DIM6) < DIM1, "Invalid geometry specified.") np.testing.assert_(DIM4 < DIM3, "Invalid geometry specified.") np.testing.assert_(DIM4 < DIM2, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM2, 0.5*DIM5]] shift = [-0.5*DIM2+shift[0], -0.5*DIM1+shift[1]] super().__init__(control_points, shift) # construct the points and facets db = 0.5*(DIM2 - DIM4) dt = 0.5*(DIM3 - DIM4) self.points = [ [0., 0.], [DIM2, 0.], [DIM2, DIM5], [db+DIM4, DIM5], [db + DIM4, DIM1-DIM6], [db+DIM4+dt, DIM1-DIM6], [db+DIM4+dt, DIM1], [db-dt, DIM1], [db-dt, DIM1-DIM6], [db, DIM1-DIM6], [db, DIM5], [0, DIM5] ] self.facets = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 0] ] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM3-shift[0], 0.5*self.DIM1-shift[1]) D = (0.5*self.DIM3-shift[0], -0.5*self.DIM1-shift[1]) E = (-0.5*self.DIM3-shift[0], -0.5*self.DIM1-shift[1]) F = (-0.5*self.DIM3-shift[0], 0.5*self.DIM1-shift[1]) return C, D, E, F
[docs]class I1Section(Geometry): """Constructs a I1 section with the web's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Twice distance from web end to flange end :param float DIM2: Thickness of web :param float DIM3: Length of web (spacing between flanges) :param float DIM4: Depth (y) of the I1-section :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a I1 cross-section with a depth of 5.0 and width of 1.75, and generates a mesh with a maximum triangular area of 0.02:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.I1Section(DIM1=1.0, DIM2=0.75, DIM3=4.0, DIM4=5.0) mesh = geometry.create_mesh(mesh_sizes=[0.02]) .. figure:: ../images/sections/i1_geometry.png :align: center :scale: 75 % I1 section geometry. .. figure:: ../images/sections/i1_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the I1section class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 > DIM3, "Invalid geometry specified.") shift = [-0.5*(DIM1+DIM2)+shift[0], -0.5*DIM4+shift[1]] # assign control point control_points = [[0.5*(DIM1+DIM2), 0.5*DIM4]] super().__init__(control_points, shift) # construct the points and facets t = 0.5*(DIM4 - DIM3) self.points = [ [0., 0.], [DIM1+DIM2, 0.], [DIM1+DIM2, t], [0.5*DIM1+DIM2, t], [0.5*DIM1+DIM2, t+DIM3], [DIM1+DIM2, t+DIM3], [DIM1+DIM2, DIM4], [0., DIM4], [0., t+DIM3], [0.5*DIM1, t+DIM3], [0.5*DIM1, t], [0., t] ] self.facets = [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9], [9, 10], [10, 11], [11, 0] ] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*(self.DIM1+self.DIM2)-shift[0], 0.5*self.DIM4-shift[1]) D = (0.5*(self.DIM1+self.DIM2)-shift[0], -0.5*self.DIM4-shift[1]) E = (-0.5*(self.DIM1+self.DIM2)-shift[0], -0.5*self.DIM4-shift[1]) F = (-0.5*(self.DIM1+self.DIM2)-shift[0], 0.5*self.DIM4-shift[1]) return C, D, E, F
[docs]class LSection(Geometry): """Constructs a L section with the intersection's center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of the L-section :param float DIM2: Depth (y) of the L-section :param float DIM3: Thickness of flange (horizontal portion) :param float DIM4: Thickness of web (vertical portion) :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a L cross-section with a depth of 6.0 and width of 3.0, and generates a mesh with a maximum triangular area of 0.01:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.LSection(DIM1=3.0, DIM2=6.0, DIM3=0.375, DIM4=0.625) mesh = geometry.create_mesh(mesh_sizes=[0.01]) .. figure:: ../images/sections/l_geometry.png :align: center :scale: 75 % L section geometry. .. figure:: ../images/sections/l_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the LSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 < DIM1, "Invalid geometry specified.") np.testing.assert_(DIM3 < DIM2, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM1, 0.5*DIM3]] shift = [-0.5*DIM4+shift[0], -0.5*DIM3+shift[1]] super().__init__(control_points, shift) # construct the points and facets self.points = [[0, 0], [DIM1, 0], [DIM1, DIM3], [DIM4, DIM3], [DIM4, DIM2], [0, DIM2]] self.facets = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM4-shift[0], self.DIM2-0.5*self.DIM3-shift[1]) D = (self.DIM1-0.5*self.DIM4-shift[0], -0.5*self.DIM3-shift[1]) E = (-0.5*self.DIM4-shift[0], -0.5*self.DIM3-shift[1]) F = (-0.5*self.DIM4-shift[0], self.DIM2-0.5*self.DIM3-shift[1]) return C, D, E, F
[docs]class RODSection(Geometry): """Constructs a circular rod section with the center at the origin *(0, 0)*, with one parameter defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Radius of the circular rod section :param int n: Number of points discretising the circle :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a circular rod with a radius of 3.0 and 50 points discretizing the boundary, and generates a mesh with a maximum triangular area of 0.01:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.RODSection(DIM1=3.0, n=50) mesh = geometry.create_mesh(mesh_sizes=[0.01]) .. figure:: ../images/sections/rod_geometry.png :align: center :scale: 75 % Rod section geometry. .. figure:: ../images/sections/rod_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, n, shift=[0, 0]): """Inits the RODSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 self.DIM1 = DIM1 # assign control point control_points = [[0, 0]] super().__init__(control_points, shift) # loop through each point on the circle d = 2.0*DIM1 for i in range(n): # determine polar angle theta = i * 2 * np.pi * 1.0 / n # calculate location of the point x = 0.5 * d * np.cos(theta) y = 0.5 * d * np.sin(theta) # append the current point to the points list self.points.append([x, y]) # if we are not at the last point if i != n - 1: self.facets.append([i, i + 1]) # if we are at the last point, complete the circle else: self.facets.append([i, 0]) self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param float DIM1: Radius of the circular rod section :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (-shift[0], self.DIM1-shift[1]) D = (self.DIM1-shift[0], -shift[1]) E = (-shift[0], -self.DIM1-shift[1]) F = (-self.DIM1-shift[0], -shift[1]) return C, D, E, F
[docs]class TSection(Geometry): """Constructs a T section with the top flange's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ [5]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of top flange :param float DIM2: Depth (y) of the T-section :param float DIM3: Thickness of top flange :param float DIM4: Thickness of web :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a T cross-section with a depth of 4.0 and width of 3.0, and generates a mesh with a maximum triangular area of 0.001:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.TSection(DIM1=3.0, DIM2=4.0, DIM3=0.375, DIM4=0.25) mesh = geometry.create_mesh(mesh_sizes=[0.001]) .. figure:: ../images/sections/t_geometry.png :align: center :scale: 75 % T section geometry. .. figure:: ../images/sections/t_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the TSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 < DIM1, "Invalid geometry specified.") np.testing.assert_(DIM3 < DIM2, "Invalid geometry specified.") d = DIM2 b = DIM1 t_f = DIM3 t_w = DIM4 r = 0 n_r = 1 shift = [-DIM1/2.0+shift[0], -(DIM2-DIM3/2.0)+shift[1]] # assign control point control_points = [[b * 0.5, d - t_f * 0.5]] super().__init__(control_points, shift) # add first two points self.points.append([b * 0.5 - t_w * 0.5, 0]) self.points.append([b * 0.5 + t_w * 0.5, 0]) # construct the top right radius pt = [b * 0.5 + t_w * 0.5 + r, d - t_f - r] self.draw_radius(pt, r, np.pi, n_r, False) # add next four points self.points.append([b, d - t_f]) self.points.append([b, d]) self.points.append([0, d]) self.points.append([0, d - t_f]) # construct the top left radius pt = [b * 0.5 - t_w * 0.5 - r, d - t_f - r] self.draw_radius(pt, r, 0.5 * np.pi, n_r, False) # build the facet list for i in range(len(self.points)): # if we are not at the last point if i != len(self.points) - 1: self.facets.append([i, i + 1]) # if we are at the last point, complete the loop else: self.facets.append([len(self.points) - 1, 0]) self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """ Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (-shift[0], 0.5*self.DIM3-shift[1]) D = (0.5*self.DIM1-shift[0], 0.5*self.DIM3-shift[1]) E = (-shift[0], 0.5*self.DIM3-self.DIM2-shift[1]) F = (-0.5*self.DIM1-shift[0], 0.5*self.DIM3-shift[1]) return C, D, E, F
[docs]class T1Section(Geometry): """Constructs a T1 section with the right flange's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Depth (y) of T1-section :param float DIM2: Length (x) of web :param float DIM3: Thickness of right flange :param float DIM4: Thickness of web :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a T1 cross-section with a depth of 3.0 and width of 3.875, and generates a mesh with a maximum triangular area of 0.001:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.T1Section(DIM1=3.0, DIM2=3.5, DIM3=0.375, DIM4=0.25) mesh = geometry.create_mesh(mesh_sizes=[0.001]) .. figure:: ../images/sections/t1_geometry.png :align: center :scale: 75 % T1 section geometry. .. figure:: ../images/sections/t1_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the T1section class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 < DIM1, "Invalid geometry specified.") shift = [-0.5*DIM3+shift[0], -0.5*DIM1+shift[1]] # assign control point control_points = [[0.5*DIM3, 0.5*DIM1]] super().__init__(control_points, shift) # construct the points and facets d1 = (DIM1 - DIM4) / 2.0 self.points = [ [0, 0], [DIM3, 0], [DIM3, DIM1], [0, DIM1], [0, d1 + DIM4], [-DIM2, d1 + DIM4], [-DIM2, d1], [0, d1] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM3-shift[0], -shift[1]) D = (0.5*self.DIM3-shift[0], -0.5*self.DIM1-shift[1]) E = (-0.5*self.DIM3-self.DIM2-shift[0], -shift[1]) F = (0.5*self.DIM3-shift[0], 0.5*self.DIM1-shift[1]) return C, D, E, F
[docs]class T2Section(Geometry): """Constructs a T2 section with the bottom flange's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of T2-section :param float DIM2: Depth (y) of T2-section :param float DIM3: Thickness of bottom flange :param float DIM4: Thickness of web :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a T2 cross-section with a depth of 4.0 and width of 3.0, and generates a mesh with a maximum triangular area of 0.005:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.T2Section(DIM1=3.0, DIM2=4.0, DIM3=0.375, DIM4=0.5) mesh = geometry.create_mesh(mesh_sizes=[0.005]) .. figure:: ../images/sections/t2_geometry.png :align: center :scale: 75 % T2 section geometry. .. figure:: ../images/sections/t2_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the T2Section class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 < DIM1, "Invalid geometry specified.") np.testing.assert_(DIM3 < DIM2, "Invalid geometry specified.") # assign control point control_points = [[0.5*DIM1, 0.5*DIM3]] shift = [-0.5*DIM1+shift[0], -0.5*DIM3+shift[1]] super().__init__(control_points, shift) # construct the points and facets d1 = 0.5*(DIM1 - DIM4) self.points = [ [0., 0.], [DIM1, 0.], [DIM1, DIM3], [DIM1-d1, DIM3], [DIM1-d1, DIM2], [d1, DIM2], [d1, DIM3], [0, DIM3] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM4-shift[0], self.DIM2-0.5*self.DIM3-shift[1]) D = (0.5*self.DIM1-shift[0], -0.5*self.DIM3-shift[1]) E = (-0.5*self.DIM1-shift[0], -0.5*self.DIM3-shift[1]) F = (-0.5*self.DIM4-shift[0], self.DIM2-0.5*self.DIM3-shift[1]) return C, D, E, F
[docs]class TUBESection(Geometry): """Constructs a circular tube section with the center at the origin *(0, 0)*, with two parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Outer radius of the circular tube section :param float DIM2: Inner radius of the circular tube section :param int n: Number of points discretising the circle :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a circular tube cross-section with an outer radius of 3.0 and an inner radius of 2.5, and generates a mesh with 37 points discretizing the boundaries and a maximum triangular area of 0.01:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.TUBESection(DIM1=3.0, DIM2=2.5, n=37) mesh = geometry.create_mesh(mesh_sizes=[0.01]) .. figure:: ../images/sections/tube_geometry.png :align: center :scale: 75 % TUBE section geometry. .. figure:: ../images/sections/tube_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, n, shift=[0, 0]): """Inits the TUBESection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 # Ensure dimensions are physically relevant np.testing.assert_(DIM2 < DIM1, "Invalid geometry specified.") d = 2.0*DIM1 t = DIM1-DIM2 # assign control point control_points = [[d * 0.5 - t * 0.5, 0]] super().__init__(control_points, shift) # specify a hole in the centre of the CHS self.holes = [[0., 0.]] # loop through each point of the CHS for i in range(n): # determine polar angle theta = i * 2 * np.pi * 1.0 / n # calculate location of outer and inner points x_outer = 0.5 * d * np.cos(theta) y_outer = 0.5 * d * np.sin(theta) x_inner = (0.5 * d - t) * np.cos(theta) y_inner = (0.5 * d - t) * np.sin(theta) # append the current points to the points list self.points.append([x_outer, y_outer]) self.points.append([x_inner, y_inner]) # if we are not at the last point if i != n - 1: self.facets.append([i * 2, i * 2 + 2]) self.facets.append([i * 2 + 1, i * 2 + 3]) # if we are at the last point, complete the circle else: self.facets.append([i * 2, 0]) self.facets.append([i * 2 + 1, 1]) self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (-shift[0], self.DIM1-shift[1]) D = (self.DIM1-shift[0], -shift[1]) E = (-shift[0], -self.DIM1-shift[1]) F = (-self.DIM1-shift[0], -shift[1]) return C, D, E, F
[docs]class TUBE2Section(Geometry): """Constructs a circular TUBE2 section with the center at the origin *(0, 0)*, with two parameters defining dimensions. See MSC Nastran documentation [1]_ for more details. Added by JohnDN90. :param float DIM1: Outer radius of the circular tube section :param float DIM2: Thickness of wall :param int n: Number of points discretising the circle :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a ciruclar TUBE2 cross-section with an outer radius of 3.0 and a wall thickness of 0.5, and generates a mesh with 37 point discritizing the boundary and a maximum triangular area of 0.01:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.TUBE2Section(DIM1=3.0, DIM2=0.5, n=37) mesh = geometry.create_mesh(mesh_sizes=[0.01]) .. figure:: ../images/sections/tube2_geometry.png :align: center :scale: 75 % TUBE2 section geometry. .. figure:: ../images/sections/tube2_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, n, shift=[0, 0]): """Inits the TUBE2Section class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 # Ensure dimensions are physically relevant np.testing.assert_(DIM2 < DIM1, "Invalid geometry specified.") d = 2.0*DIM1 t = DIM2 # assign control point control_points = [[d * 0.5 - t * 0.5, 0]] super().__init__(control_points, shift) # specify a hole in the centre of the section self.holes = [[0., 0.]] # loop through each point of the section for i in range(n): # determine polar angle theta = i * 2 * np.pi * 1.0 / n # calculate location of outer and inner points x_outer = 0.5 * d * np.cos(theta) y_outer = 0.5 * d * np.sin(theta) x_inner = (0.5 * d - t) * np.cos(theta) y_inner = (0.5 * d - t) * np.sin(theta) # append the current points to the points list self.points.append([x_outer, y_outer]) self.points.append([x_inner, y_inner]) # if we are not at the last point if i != n - 1: self.facets.append([i * 2, i * 2 + 2]) self.facets.append([i * 2 + 1, i * 2 + 3]) # if we are at the last point, complete the circle else: self.facets.append([i * 2, 0]) self.facets.append([i * 2 + 1, 1]) self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (-shift[0], self.DIM1-shift[1]) D = (self.DIM1-shift[0], -shift[1]) E = (-shift[0], -self.DIM1-shift[1]) F = (-self.DIM1-shift[0], -shift[1]) return C, D, E, F
[docs]class ZSection(Geometry): """Constructs a Z section with the web's middle center at the origin *(0, 0)*, with four parameters defining dimensions. See Nastran documentation [1]_ [2]_ [3]_ [4]_ for more details. Added by JohnDN90. :param float DIM1: Width (x) of horizontal members :param float DIM2: Thickness of web :param float DIM3: Spacing between horizontal members (length of web) :param float DIM4: Depth (y) of Z-section :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: list[float, float] The following example creates a rectangular cross-section with a depth of 4.0 and width of 2.75, and generates a mesh with a maximum triangular area of 0.005:: import sectionproperties.pre.nastran_sections as nsections geometry = nsections.ZSection(DIM1=1.125, DIM2=0.5, DIM3=3.5, DIM4=4.0) mesh = geometry.create_mesh(mesh_sizes=[0.005]) .. figure:: ../images/sections/z_geometry.png :align: center :scale: 75 % Z section geometry. .. figure:: ../images/sections/z_mesh.png :align: center :scale: 75 % Mesh generated from the above geometry. """ def __init__(self, DIM1, DIM2, DIM3, DIM4, shift=[0, 0]): """Inits the ZSection class.""" # force dimensions to be floating point values DIM1 *= 1.0 DIM2 *= 1.0 DIM3 *= 1.0 DIM4 *= 1.0 self.DIM1 = DIM1 self.DIM2 = DIM2 self.DIM3 = DIM3 self.DIM4 = DIM4 # Ensure dimensions are physically relevant np.testing.assert_(DIM4 > DIM3, "Invalid geometry specified.") # assign control point control_points = [[DIM1+0.5*DIM2, 0.5*DIM4]] shift = [-0.5*(DIM1+DIM2)+shift[0], -0.5*DIM4+shift[1]] super().__init__(control_points, shift) # construct the points and facets t = 0.5*(DIM4 - DIM3) self.points = [ [DIM1, 0.], [2.*DIM1+DIM2, 0.], [2.*DIM1+DIM2, t], [DIM1+DIM2, t], [DIM1+DIM2, DIM4], [0., DIM4], [0., DIM4-t], [DIM1, DIM4-t] ] self.facets = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 0]] self.shift_section()
[docs] def getStressPoints(self, shift=(0., 0.)): """Returns the coordinates of the stress evaluation points relative to the origin of the cross-section. The shift parameter can be used to make the coordinates relative to the centroid or the shear center. :param shift: Vector that shifts the cross-section by *(x, y)* :type shift: tuple(float, float) :returns: Stress evaluation points relative to shifted origin - C, D, E, F """ C = (0.5*self.DIM2-shift[0], 0.5*self.DIM4-shift[1]) D = (0.5*self.DIM2+self.DIM1-shift[0], -0.5*self.DIM4-shift[1]) E = (-0.5*self.DIM2-shift[0], -0.5*self.DIM4-shift[1]) F = (-0.5*self.DIM2-self.DIM1-shift[0], 0.5*self.DIM4-shift[1]) return C, D, E, F