Members

All members in Pynite are beam-column elements, meaning they can handle axial and transverse loads. By default all members are also “physical members”, meaning they automatically segment themselves at any internal nodes. Transverse shear deformations are not currently considered.

Creating a New Member

To create a new member use the FEModel3D.add_member() method. For the example below, we’ll assume 3 nodes have already been defined (‘N1’, ‘N2’, and ‘N3’). The next step is to set up some material and section properties for the members we’ll be creating:

# Define a section property (W12x26) to assign to the member
Iy = 17.3  # (in**4) Weak axis moment of inertia
Iz = 204   # (in**4) Strong axis moment of inertia
J = 0.300  # (in**4) Torsional constant
A = 7.65   # (in**2) Cross-sectional area'
my_model.add_section('W12x26', A, Iy, Iz, J)

# Define a new material (steel)
E = 29000  # (ksi) Modulus of elasticity
G = 11400  # (ksi) Shear modulus
nu = 0.30  # Poisson's ratio
rho = 0.000283  # (kci) Density
my_model.add_material('Steel', E, G, nu, rho)

With a cross-section and material properties defined we can now create members:

# Add a member name 'M1' starting at node 'N1' and ending at node 'N2'
# made from a previously defined material named 'Steel'
my_model.add_member('M1', 'N1', 'N2', 'Steel', 'W12x26')

# Add another member 'M2'
my_model.add_member('M2', 'N2', 'N3', 'Steel', 'W12x26')

Local Coordinate System

Each member starts at its i-node and ends at its j-node. The local x-axis for the member is defined by a vector going from the i-node to the j-node.

By default the local z-axis is always horizontal in Pynite, and is on the right-hand side of the member.

The local y-axis is defined as the cross-product of the local z-axis with the local x-axis. In other words, the local y-axis is always perpendicular to the member and to the local z-axis.

Positive sign conventions are shown below.

_images/sign_convention.png

End Releases

End releases can be applied to each end of a member to simulate pinned connections or other end conditions. Use FEModel3D.def_releases(). By applying rotational end releases to both ends of a member you can simulate truss-like behavior (no end moments).

# The following line turns member M1 into a pin-ended member
my_model.def_releases('M1', Dxi=False, Dyi=False, Dzi=False, Rxi=False, Ryi=True, Rzi=True, Dxj=False, Dyj=False, Dzj=False, Rxj=False, Ryj=True, Rzj=True)

# This next line does the same thing as the previous line - just simplified
my_model.def_releases('M1', False, False, False, False, True, True, False, False, False, False, True, True)

# This next line is yet another simple way to do the same thing
my_model.def_releases('M1', 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1)

Note that in the code above, Dxi stands for displacement in the local x direction at the i-node, Rjz stands for rotation about the local z axis at the j-node, and so forth.

In most cases you will only release the rotations about the local y and/or z-axes. Releasing torsion about the local x-axis should only be done at one end (if at all). The same goes for axial releases. Releasing Rxi and Rxj simultaneously, or Dxi and Dxj simultaneously will cause an instability in the member. You should exercise caution when releasing the shears at the ends of the member too.

Member Rotations

The member can be rotated about its own longitudinal (x) axis by passing a rotation angle (in degrees) to the rotation argument in the FEModel3D.add_member() method. Here’s an example:

my_model.add_member('M1', 'N1', 'N2', 'Steel', 'W12x26', rotation=35)

The member created by the code above will be rotated 35 degrees about its local x-axis.

Tension/Compression Only Members

Members can be changed to tension- or compression-only by passing tension_only=True or comp_only=True to the FEModel3D.add_member() method. Here’s an example:

my_model.add_member('M1', 'N1', 'N2', 'Steel', 'W12x26', tension_only=True)
my_model.add_member('M2', 'N2', 'N3', 'Steel', 'W12x26', comp_only=True)

Tension-only and compression-only analysis is an iterative process. When using these types of members be sure to perform a non-linear analysis. Do not use the FEModel3D.analyze_linear() method.

Member loads

Pynite supports member distributed loads and member point loads. It can also calculate self-weight for members (note that self-weight is not supported for plate elements at this time).

# Add a point load of -5 to member 'M1' in the global Y-direction at 3
# units from the start of the member. We'll classify it as a live load.
my_model.add_member_pt_load('M1', 'FY', -5, 3, 'LL')

# Add a moment load of 15 to member 'M2' about its weak axis at 4 units
# from the start of the member. We'll classify it as a snow load.
my_model.add_member_pt_load('M2', 'My', 15, 4, 'SL')

Distributed loads can be full length or partial length, and can vary linearly in magnitude:

# Add a linearly varying member distributed load to member 'M1' in its
# local y-direction. The load will start at a magnitude of -0.100 at 2
# units from the start of the member, and end 5 units from the start of
# the member with a magnitude of -0.200. We'll classify it as a dead load.
my_model.add_member_dist_load('M1', 'Fy', -0.100, -0.200, 2, 5, 'DL')

Here’s an example of how to add self-weight to all members (not plates) currently defined in the model.

# Add self-weight to all members (note that any plates in the model will
# not be affected by this command). We'll add 10% to account for connection
# hardware and other misc items. Self-weight is normally a dead load
# acting in the global Y-direction.
my_model.add_member_self_weight('FY', 1.10, 'DL')

It can be seen that when applying loads, capitalization is used to distinguish between the local and global coordinate systems. Loads can be applied in member local (‘Fx’, ‘Fy’, ‘Fz’, ‘Mx’, ‘My’, ‘Mz’) or model global coordinate directions (‘FX’, ‘FY’, ‘FZ’, ‘MX’, ‘MY’, ‘MZ’).

Member Results

You can access members and their results from the members dictionary in the FEModel3D class. Below are some examples.

Shear Results:

# Get the maximum strong-axis shear from member 'M1' for load combination '1.4D'
my_model.members['M1'].max_shear('Fy', '1.4D')

# Get the minimum weak-axis shear from member 'M3' for load combination '1.2D+1.6L'
my_model.members['M3'].min_shear('Fz', '1.2D+1.6L')

# Get the strong axis shear 5 units from the start of member 'M2' for load combination '1.2D+1.6S'
my_model.members['M2'].shear('Fy', 5, '1.2D+1.6S')

# Plot the strong axis shear diagram for member 'M1' for load combination '1.4D' using 100 points
my_model.members['M1'].plot_shear('Fy', '1.4D', 100)

Moment Results:

# Get the maximum strong-axis moment from member 'M1' for load combination '1.4D'
my_model.members['M1'].max_moment('Mz', '1.4D')

# Get the minimum weak-axis moment from member 'M3' for load combination '1.2D+1.6L'
my_model.members['M3'].min_moment('My', '1.2D+1.6L')

# Get the strong axis moment 5 units from the start of member 'M2' for load combination '1.2D+1.6S'
my_model.members['M2'].moment('Mz', 5, '1.2D+1.6S')

# Plot the strong axis moment diagram for member 'M1' for load combination '1.4D' using 100 points
my_model.members['M1'].plot_moment('Mz', '1.4D', 100)

Deflection Results:

# Get the maximum strong-axis deflection from member 'M1' for load combination 'D'
my_model.members['M1'].max_deflection('dy', 'D')

# Get the minimum weak-axis deflection from member 'M3' for load combination 'D+L'
my_model.members['M3'].min_deflection('dz', 'D+L')

# Get the strong axis defletion 5 units from the start of member 'M2' for load combination 'D+S'
my_model.members['M2'].deflection('dy', 5, 'D+S')

# Plot the strong axis deflection diagram for member 'M1' for load combination 'D' using 100 points
my_model.members['M1'].plot_deflection('dy', 'D', 100)

Axial force and torque are also available, as well as array helpers for sampling along the span:

# Axial force at midspan
N_mid = my_model.members['M1'].axial(x=0.5*my_model.members['M1'].L(), combo_name='D')

# Maximum torque in a combination
Tmax = my_model.members['M1'].max_torque('D+L')

Enveloped Results

Pynite supports enveloped results across multiple load combinations using combo tags. When defining load combinations, you can assign tags to categorize them (e.g. 'Strength', 'Service'). You can then pass a list of tags to any max_* or min_* method, and Pynite will return the envelope (maximum or minimum) across all combinations that match any of the given tags.

First, assign tags when creating load combinations:

# Create tagged load combinations
my_model.add_load_combo('1.4D', {'D': 1.4}, combo_tags=['Strength'])
my_model.add_load_combo('1.2D+1.6L', {'D': 1.2, 'L': 1.6}, combo_tags=['Strength'])
my_model.add_load_combo('1.2D+1.6S', {'D': 1.2, 'S': 1.6}, combo_tags=['Strength'])
my_model.add_load_combo('D+L', {'D': 1.0, 'L': 1.0}, combo_tags=['Service'])
my_model.add_load_combo('D+S', {'D': 1.0, 'S': 1.0}, combo_tags=['Service'])

Then pass a list of tags to retrieve the envelope. When a list of tags is passed, the return value is a tuple (value, governing_combo_name) so you can see which load combination governed:

# Get the maximum strong-axis shear across all 'Strength' combinations
Vmax, combo = my_model.members['M1'].max_shear('Fy', ['Strength'])

# Get the minimum strong-axis moment across all 'Strength' combinations
Mmin, combo = my_model.members['M1'].min_moment('Mz', ['Strength'])

# Get the maximum deflection across all 'Service' combinations
dmax, combo = my_model.members['M1'].max_deflection('dy', ['Service'])

# Get the minimum axial force across all 'Strength' combinations
Pmin, combo = my_model.members['M1'].min_axial(['Strength'])

# Get the maximum torque across all 'Strength' combinations
Tmax, combo = my_model.members['M1'].max_torque(['Strength'])

You can also pass multiple tags at once. Any combination that has any of the provided tags will be included in the envelope:

# Envelope across all combinations tagged 'Strength' or 'Service'
Vmax, combo = my_model.members['M1'].max_shear('Fy', ['Strength', 'Service'])

The following methods support enveloped results via combo_tags. When a single string is passed they return a float; when a list of tags is passed they return a tuple[float, str] containing the governing value and the name of the governing load combination:

  • max_shear(Direction, combo_tags) / min_shear(Direction, combo_tags)

  • max_moment(Direction, combo_tags) / min_moment(Direction, combo_tags)

  • max_axial(combo_tags) / min_axial(combo_tags)

  • max_torque(combo_tags) / min_torque(combo_tags)

  • max_deflection(Direction, combo_tags) / min_deflection(Direction, combo_tags)

Envelope Diagrams

The plotting methods (plot_shear, plot_moment, plot_deflection, plot_axial, plot_torque) also support envelope plotting. Pass a list of combo tags instead of a single combo name and the plot will show each individual combination curve along with thick max/min envelope lines:

# Plot the strong-axis moment envelope across all 'Strength' combos
my_model.members['M1'].plot_moment('Mz', ['Strength'], n_points=100)

# Plot the shear envelope across all 'Strength' combos
my_model.members['M1'].plot_shear('Fy', ['Strength'], n_points=100)

# Plot the deflection envelope across all 'Service' combos
my_model.members['M1'].plot_deflection('dy', ['Service'], n_points=100)

# Plot the axial force envelope across all 'Strength' combos
my_model.members['M1'].plot_axial(['Strength'], n_points=100)

# Plot the torque envelope across all 'Strength' combos
my_model.members['M1'].plot_torque(['Strength'], n_points=100)

Note

When a single string is passed (e.g. '1.4D'), it is treated as a specific load combination name. When a list is passed (e.g. ['Strength']), each item is treated as a tag, and all combinations matching any of the tags are enveloped.

Tips and Patterns

  • Local vs global directions: lower-case (Fx, Fy, Fz) and (Mx, My, Mz) are in the member’s local axes; upper-case (FX, FY, FZ) and (MX, MY, MZ) are in global axes.

  • Prefer releasing only Ry and/or Rz for pin-like ends; avoid releasing both axial (Dx) and torsion (Rx) at the same end unless you understand stability implications.

  • For tension/compression-only behavior, run FEModel3D.analyze(...) with num_steps > 1 for better convergence, not analyze_linear().

Similar methods can be used to obtain results for axial forces and torques.

Member API Quick Reference

  • Creation - FEModel3D.add_member(name, i_node, j_node, material_name, section_name, rotation=0.0, tension_only=False, comp_only=False) -> str

  • End Releases - FEModel3D.def_releases(member_name, Dxi=False, Dyi=False, Dzi=False, Rxi=False, Ryi=False, Rzi=False, Dxj=False, Dyj=False, Dzj=False, Rxj=False, Ryj=False, Rzj=False)

  • Loads - FEModel3D.add_member_pt_load(member_name, direction, P, x, case='Case 1') - FEModel3D.add_member_dist_load(member_name, direction, w1, w2, x1=None, x2=None, case='Case 1') - FEModel3D.add_member_self_weight(global_direction, factor, case='Case 1')

  • Geometry/Accessors - model.members['M'].L(): returns member length - model.members['M'].i_node, .j_node: node objects

  • Shear - shear(Direction, x, combo_name='Combo 1') where Direction ∈ {'Fy', 'Fz'} - max_shear(Direction, combo_tags='Combo 1'), min_shear(...) — pass a list of tags to envelope across combos; returns (value, governing_combo) when a list is passed - shear_array(Direction, n_points, combo_name='Combo 1', x_array=None)

  • Moment - moment(Direction, x, combo_name='Combo 1') where Direction ∈ {'My', 'Mz'} - max_moment(Direction, combo_tags='Combo 1'), min_moment(...) — pass a list of tags to envelope across combos; returns (value, governing_combo) when a list is passed - moment_array(Direction, n_points, combo_name='Combo 1', x_array=None)

  • Axial and Torque - axial(x, combo_name='Combo 1'), max_axial(combo_tags), min_axial(combo_tags) — pass a list of tags to envelope; returns (value, governing_combo) when a list is passed - axial_array(n_points, combo_name='Combo 1', x_array=None) - torque(x, combo_name='Combo 1'), max_torque(combo_tags), min_torque(combo_tags) — pass a list of tags to envelope; returns (value, governing_combo) when a list is passed - torque_array(n_points, combo_name='Combo 1', x_array=None)

  • Deflection - deflection(Direction, x, combo_name='Combo 1') where Direction ∈ {'dx', 'dy', 'dz'} - max_deflection(Direction, combo_tags='Combo 1'), min_deflection(...) — pass a list of tags to envelope across combos; returns (value, governing_combo) when a list is passed - deflection_array(Direction, n_points, combo_name='Combo 1', x_array=None)

  • Plotting - plot_shear(Direction, combo_name='Combo 1', n_points=20) — pass a list of tags to get an envelope diagram - plot_moment(Direction, combo_name='Combo 1', n_points=20) — pass a list of tags to get an envelope diagram - plot_deflection(Direction, combo_name='Combo 1', n_points=20) — pass a list of tags to get an envelope diagram - plot_axial(combo_name='Combo 1', n_points=20) — pass a list of tags to get an envelope diagram - plot_torque(combo_name='Combo 1', n_points=20) — pass a list of tags to get an envelope diagram

Worked Example

from Pynite import FEModel3D

# Simple cantilever beam example
model = FEModel3D()
model.add_material('Steel', E=29_000_000.0, G=11_200_000.0, nu=0.3, rho=0.283)
model.add_section('W12x26', A=7.65, Iy=17.3, Iz=204.0, J=0.300)

n1 = model.add_node('N1', 0, 0, 0)
n2 = model.add_node('N2', 10, 0, 0)  # 10 units long
m1 = model.add_member('M1', n1, n2, 'Steel', 'W12x26')

# Fix the base
model.def_support('N1', support_DX=True, support_DY=True, support_DZ=True,
                         support_RX=True, support_RY=True, support_RZ=True)

# Apply a downward tip load at the free end
model.add_node_load('N2', 'FZ', -5.0, case='D')
model.add_load_combo('D', {'D': 1.0})

model.analyze_linear()

# Query common results
Vmax = model.members['M1'].max_shear('Fz', 'D')
Mmax = model.members['M1'].max_moment('Mz', 'D')
dtip = model.members['M1'].deflection('dz', x=model.members['M1'].L(), combo_name='D')

# Plot diagrams (requires matplotlib)
# model.members['M1'].plot_shear('Fz', 'D', n_points=50)
# model.members['M1'].plot_moment('Mz', 'D', n_points=50)
# model.members['M1'].plot_deflection('dz', 'D', n_points=50)

API Reference