Structural
Overview
Structural analysis evaluates how beams, frames, plates, and trusses respond to loads, constraints, and material behavior, making it a core discipline in civil, mechanical, and infrastructure engineering. In data-driven workflows, these methods convert geometric and material inputs into actionable response quantities such as displacements, reactions, and internal forces. The category sits within the broader context of the finite element method, where complex continua are discretized into elements and solved numerically. This matters in engineering analytics because it enables repeatable scenario studies, design checks, and early-stage optimization directly from tabular inputs.
The unifying ideas across these tools are stiffness assembly, boundary conditions, and result recovery. In linear static form, each model solves the global equilibrium system
\mathbf{K}\mathbf{u} = \mathbf{F}
where \mathbf{K} is the assembled structural stiffness matrix, \mathbf{u} is the nodal unknown vector (translations/rotations), and \mathbf{F} is the external load vector. Once nodal states are obtained, post-processing maps those states back to engineering outputs such as shear and moment diagrams, member axial force, plate force components, and support reactions.
Implementation is based on PyNite, a Python finite-element library for practical structural modeling and linear analysis. PyNite provides the model objects, element formulations, load handling, and solvers that these spreadsheet-facing tools expose in a consistent, Excel-friendly interface.
BEAM_2D and FRAME_2D address line-member systems where bending behavior is central. BEAM_2D focuses on continuous planar beams defined by spans, support types, and distributed or point loading, returning reactions, nodal displacements, shear, moment, and deflection summaries. FRAME_2D generalizes this to rigid-jointed planar frames with user-defined nodes, members, materials, and sections, so axial-bending interaction and frame action can be captured in one model. Together they support common use cases such as preliminary building-frame checks, portal frame sizing, and beam line load verification.
TRUSS_2D targets pin-jointed systems where members primarily carry axial force, making it suitable for triangulated roof trusses, bracing layouts, and lightweight lattice concepts. By enforcing truss-like end releases and solving for nodal displacements and reactions, it yields direct tension/compression force results for each member. This specialization makes interpretation straightforward for capacity checks and member selection workflows. In practice, it complements frame analysis by covering the axial-dominant end of structural behavior.
PLATE extends the category to surface elements, enabling plate/shell-style modeling with generated meshes or manually defined nodes and elements. It is used when two-dimensional structural surfaces (slabs, panels, deck regions, and thin wall segments) require distributed pressure loading and spatial response assessment. Typical outputs include nodal displacement fields, support reactions, and plate force quantities derived from the solved deformation state. This bridges the gap between 1D member models and continuum-like behavior needed in slab and panel design studies.
Collectively, these four tools form a coherent structural workflow: choose the element family that matches the physical system, apply supports and loads, solve a linear FE model, and extract decision-ready response metrics. That workflow is especially effective for spreadsheet-centric engineering teams that need transparent assumptions, rapid iterations, and reproducible calculations.
BEAM_2D
This function performs two-dimensional beam analysis by constructing a finite element model from span geometry, support conditions, material/section properties, and applied loads. It supports common beam loading types such as concentrated forces and distributed loads, then solves for nodal reactions, displacements, internal shear/moment response, and deflection summaries.
The underlying beam equilibrium is governed by force and moment balance, with stiffness-based solution of nodal unknowns. For a linear static case, the assembled system is solved as:
\mathbf{K}\mathbf{u} = \mathbf{F}
where \mathbf{K} is the global stiffness matrix, \mathbf{u} is the nodal displacement vector, and \mathbf{F} is the applied load vector. Member result quantities are then recovered from the solved displacement state.
Excel Usage
=BEAM_2D(spans, supports, loads, material_section, pynite_beam_output)
spans(list[list], required): List of span lengths [length].supports(list[list], required): Support locations and types [x_location, type]. Type=‘pinned’, ‘fixed’, ‘roller’, ‘free’.loads(list[list], required): Loads [type, magnitude, x_start, x_end]. Type=‘point’, ‘distributed’, ‘moment’.material_section(list[list], required): Material and Section properties [E, G, nu, rho, A, Iy].pynite_beam_output(str, optional, default: “Reactions”): Output type.
Returns (list[list]): Analysis results based on pynite_output.
Example 1: Reactions Output
Inputs:
| spans | supports | loads | material_section | pynite_beam_output | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | 0 | pinned | dist | -1 | 0 | 10 | 29000 | 11200 | 0.3 | 0 | 10 | 100 | Reactions |
| 10 | roller |
Excel formula:
=BEAM_2D({10}, {0,"pinned";10,"roller"}, {"dist",-1,0,10}, {29000,11200,0.3,0,10,100}, "Reactions")
Expected output:
| Node | Rx (k) | Ry (k) | Mz (k-ft) |
|---|---|---|---|
| N0 | 0 | 5 | 0 |
| N1 | 0 | 5 | 0 |
Example 2: Displacements Output
Inputs:
| spans | supports | loads | material_section | pynite_beam_output | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | 0 | fixed | point | -1 | 10 | 29000 | 11200 | 0.3 | 0 | 10 | 100 | Displacements |
Excel formula:
=BEAM_2D({10}, {0,"fixed"}, {"point",-1,10}, {29000,11200,0.3,0,10,100}, "Displacements")
Expected output:
| Node | Dx (in) | Dy (in) | Rz (rad) |
|---|---|---|---|
| N0 | 0 | 0 | 0 |
| N1 | 0 | -0.0114943 | -0.00172414 |
Example 3: Shear Diagram Output
Inputs:
| spans | supports | loads | material_section | pynite_beam_output | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | 0 | pinned | dist | -1 | 0 | 10 | 29000 | 11200 | 0.3 | 0 | 10 | 100 | Shear |
| 10 | roller |
Excel formula:
=BEAM_2D({10}, {0,"pinned";10,"roller"}, {"dist",-1,0,10}, {29000,11200,0.3,0,10,100}, "Shear")
Expected output:
| Member | Loc (ft) | Shear (k) | |
|---|---|---|---|
| M1 | 0 | 5 | |
| M1 | 5 | 0 | |
| M1 | 10 | -5 |
Example 4: Moment Diagram Output
Inputs:
| spans | supports | loads | material_section | pynite_beam_output | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | 0 | pinned | dist | -1 | 0 | 10 | 29000 | 11200 | 0.3 | 0 | 10 | 100 | Moment |
| 10 | roller |
Excel formula:
=BEAM_2D({10}, {0,"pinned";10,"roller"}, {"dist",-1,0,10}, {29000,11200,0.3,0,10,100}, "Moment")
Expected output:
| Member | Loc (ft) | Moment (k-ft) | |
|---|---|---|---|
| M1 | 0 | 0 | |
| M1 | 5 | -12.5 | |
| M1 | 10 | 0 |
Example 5: Deflection Output
Inputs:
| spans | supports | loads | material_section | pynite_beam_output | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | 0 | pinned | point | -1 | 5 | 29000 | 11200 | 0.3 | 0 | 10 | 100 | Deflection |
| 10 | roller |
Excel formula:
=BEAM_2D({10}, {0,"pinned";10,"roller"}, {"point",-1,5}, {29000,11200,0.3,0,10,100}, "Deflection")
Expected output:
| Node | Dy (in) | ||
|---|---|---|---|
| N0 | 0 | ||
| N1 | 0 |
Example 6: Stacked Results Output
Inputs:
| spans | supports | loads | material_section | pynite_beam_output | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 | 0 | pinned | dist | -1 | 0 | 10 | 29000 | 11200 | 0.3 | 0 | 10 | 100 | Stacked |
| 10 | roller |
Excel formula:
=BEAM_2D({10}, {0,"pinned";10,"roller"}, {"dist",-1,0,10}, {29000,11200,0.3,0,10,100}, "Stacked")
Expected output:
| Node | Rx (k) | Ry (k) | Mz (k-ft) |
|---|---|---|---|
| N0 | 0 | 5 | 0 |
| N1 | 0 | 5 | 0 |
| Node | Dx (in) | Dy (in) | Rz (rad) |
| N0 | 0 | 0 | -0.00143678 |
| N1 | 0 | 0 | 0.00143678 |
| Member | Loc (ft) | Shear (k) | |
| M1 | 0 | 5 | |
| M1 | 5 | 0 | |
| M1 | 10 | -5 | |
| Member | Loc (ft) | Moment (k-ft) | |
| M1 | 0 | 0 | |
| M1 | 5 | -12.5 | |
| M1 | 10 | 0 | |
| Node | Dy (in) | ||
| N0 | 0 | ||
| N1 | 0 |
Python Code
Show Code
import sys
import types
# PATCH: PyNiteFEA 2.0.2 pkg_resources fix
if "pip._vendor.pkg_resources" not in sys.modules:
mock_pkg = types.ModuleType("pip._vendor.pkg_resources")
mock_pkg.get_distribution = lambda _: types.SimpleNamespace(version="2.0.2")
mock_pkg.working_set = []
sys.modules["pip._vendor.pkg_resources"] = mock_pkg
pip = sys.modules.setdefault("pip", types.ModuleType("pip"))
vendor = sys.modules.setdefault("pip._vendor", types.ModuleType("pip._vendor"))
pip._vendor = vendor
vendor.pkg_resources = mock_pkg
from Pynite import FEModel3D
def beam_2d(spans, supports, loads, material_section, pynite_beam_output='Reactions'):
"""
Analyze continuous 2D beams with auto-meshing. Supports point and distributed loads.
See: https://pynite.readthedocs.io/en/latest/index.html
This example function is provided as-is without any representation of accuracy.
Args:
spans (list[list]): List of span lengths [length].
supports (list[list]): Support locations and types [x_location, type]. Type='pinned', 'fixed', 'roller', 'free'.
loads (list[list]): Loads [type, magnitude, x_start, x_end]. Type='point', 'distributed', 'moment'.
material_section (list[list]): Material and Section properties [E, G, nu, rho, A, Iy].
pynite_beam_output (str, optional): Output type. Valid options: Reactions, Displacements, Shear Diagram, Moment Diagram, Deflection Plot, Stacked Results. Default is 'Reactions'.
Returns:
list[list]: Analysis results based on pynite_output.
"""
try:
# Helpers
def to2d(x):
return [[x]] if not isinstance(x, list) else x
# Prepare inputs
spans = [float(row[0]) for row in to2d(spans) if row[0] is not None]
# Create Model
model = FEModel3D()
# Material/Section (Simplified: Uniform for now, or take first row)
props = to2d(material_section)[0]
E, G, nu, rho = float(props[0]), float(props[1]), float(props[2]), float(props[3])
A, Iy = float(props[4]), float(props[5])
model.add_material('Mat1', E, G, nu, rho)
model.add_section('Sec1', A, Iy, 1.0, 1.0) # Dummy J, Iz for 2D beam
# Generate Nodes and Members from Spans
x_accum = 0.0
node_names = ['N0']
model.add_node('N0', 0, 0, 0)
for i, length in enumerate(spans):
x_end = x_accum + length
n_name = f'N{i+1}'
model.add_node(n_name, x_end, 0, 0)
model.add_member(f'M{i+1}', node_names[-1], n_name, 'Mat1', 'Sec1')
node_names.append(n_name)
x_accum = x_end
# 2D Stability: Fix all nodes out-of-plane
for n_name in model.nodes:
model.def_support(n_name, False, False, True, True, True, False)
# Supports
for row in to2d(supports):
if not row or row[0] is None: continue
x_loc = float(row[0])
sup_type = str(row[1]).lower()
for n_name in model.nodes:
n = model.nodes[n_name]
if abs(n.X - x_loc) < 1e-4:
if 'fixed' in sup_type:
model.def_support(n_name, True, True, True, True, True, True)
elif 'pinned' in sup_type:
model.def_support(n_name, True, True, True, True, True, False)
elif 'roller' in sup_type:
model.def_support(n_name, False, True, True, True, True, False)
break
# Loads
for row in to2d(loads):
if not row or row[0] is None: continue
l_type, mag, x1 = str(row[0]).lower(), float(row[1]), float(row[2])
if 'point' in l_type:
for m_name in model.members:
m = model.members[m_name]
if m.i_node.X <= x1 <= m.j_node.X:
model.add_member_pt_load(m_name, 'Fy', mag, x1 - m.i_node.X)
break
elif 'dist' in l_type:
x2 = float(row[3])
for m_name in model.members:
m = model.members[m_name]
s, e = max(x1, m.i_node.X), min(x2, m.j_node.X)
if s < e:
model.add_member_dist_load(m_name, 'Fy', mag, mag, s - m.i_node.X, e - m.i_node.X)
# Safe result helper
def get_val(obj, attr, *args):
try:
val = getattr(obj, attr)
if callable(val): val = val(*args)
if isinstance(val, dict): return float(val.get('Combo 1', 0.0))
return float(val)
except: return 0.0
# Add load combination and analyze
model.add_load_combo('Combo 1', {'Case 1': 1.0})
model.analyze(check_statics=False)
res = []
# Column count for Stacked output: 4
if pynite_beam_output == 'Reactions' or pynite_beam_output == 'Stacked':
res.append(['Node', 'Rx (k)', 'Ry (k)', 'Mz (k-ft)'])
for n_nm in model.nodes:
n = model.nodes[n_nm]
res.append([n_nm, get_val(n, 'RxnFX'), get_val(n, 'RxnFY'), get_val(n, 'RxnMZ')])
if pynite_beam_output == 'Displacements' or pynite_beam_output == 'Stacked':
res.append(['Node', 'Dx (in)', 'Dy (in)', 'Rz (rad)'])
for n_nm in model.nodes:
n = model.nodes[n_nm]
res.append([n_nm, get_val(n, 'DX'), get_val(n, 'DY'), get_val(n, 'RZ')])
if pynite_beam_output == 'Shear' or pynite_beam_output == 'Stacked':
res.append(['Member', 'Loc (ft)', 'Shear (k)', ''])
for m_nm in model.members:
m = model.members[m_nm]
for x in [0.0, m.L()/2, m.L()]:
res.append([m_nm, x, get_val(m, 'shear', 'Fy', x), ''])
if pynite_beam_output == 'Moment' or pynite_beam_output == 'Stacked':
res.append(['Member', 'Loc (ft)', 'Moment (k-ft)', ''])
for m_nm in model.members:
m = model.members[m_nm]
for x in [0.0, m.L()/2, m.L()]:
res.append([m_nm, x, get_val(m, 'moment', 'Mz', x), ''])
if pynite_beam_output == 'Deflection' or pynite_beam_output == 'Stacked':
res.append(['Node', 'Dy (in)', '', ''])
for n_nm in model.nodes:
res.append([n_nm, get_val(model.nodes[n_nm], 'DY'), '', ''])
return res
except Exception as e:
return f"Error: {str(e)}"Online Calculator
FRAME_2D
This function analyzes planar rigid frames by assembling a finite element model from user-defined nodes, members, materials, and sections. It applies support constraints plus nodal/member loads, then computes structural response including reactions, nodal displacements, and representative member force quantities.
Frame analysis is performed using linear elastic stiffness methods, solving the global equilibrium equations:
\mathbf{K}\mathbf{u} = \mathbf{F}
where \mathbf{K} is the assembled frame stiffness matrix, \mathbf{u} are nodal degrees of freedom, and \mathbf{F} are applied loads. Post-processing recovers internal member actions from the solved displacement field.
Excel Usage
=FRAME_2D(nodes, members, materials, sections, supports, nodal_loads, member_loads, pynite_frame_output)
nodes(list[list], required): Nodes [name, x, y].members(list[list], required): Members [name, node_i, node_j, material_name, section_name].materials(list[list], required): Materials [name, E, G, nu, rho].sections(list[list], required): Sections [name, A, Iy].supports(list[list], required): Supports [node, fix_x, fix_y, fix_mz]. Bools.nodal_loads(list[list], optional, default: []): Nodal loads [node, direction, magnitude]. Direction=FX, FY, MZ.member_loads(list[list], optional, default: []): Member loads [member, type, magnitude, x1, x2]. Type=point, dist.pynite_frame_output(str, optional, default: “Reactions”): Output type.
Returns (list[list]): Analysis results.
Example 1: Reactions Output
Inputs:
| nodes | members | materials | sections | supports | member_loads | pynite_frame_output | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 0 | 0 | C1 | N1 | N2 | Mat1 | Sec1 | Mat1 | 29000 | 11200 | 0.3 | 0 | Sec1 | 10 | 100 | N1 | true | true | true | B1 | dist | -1 | 0 | 10 | Reactions |
| N2 | 0 | 10 | B1 | N2 | N3 | Mat1 | Sec1 | N4 | true | true | true | ||||||||||||||
| N3 | 10 | 10 | C2 | N3 | N4 | Mat1 | Sec1 | ||||||||||||||||||
| N4 | 10 | 0 |
Excel formula:
=FRAME_2D({"N1",0,0;"N2",0,10;"N3",10,10;"N4",10,0}, {"C1","N1","N2","Mat1","Sec1";"B1","N2","N3","Mat1","Sec1";"C2","N3","N4","Mat1","Sec1"}, {"Mat1",29000,11200,0.3,0}, {"Sec1",10,100}, {"N1",TRUE,TRUE,TRUE;"N4",TRUE,TRUE,TRUE}, {"B1","dist",-1,0,10}, "Reactions")
Expected output:
| Node | Rx | Ry | Mz |
|---|---|---|---|
| N1 | 0.830841 | 5 | -2.76116 |
| N2 | 0 | 0 | 0 |
| N3 | 0 | 0 | 0 |
| N4 | -0.830841 | 5 | 2.76116 |
Example 2: Displacements Output
Inputs:
| nodes | members | materials | sections | supports | nodal_loads | pynite_frame_output | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 0 | 0 | C1 | N1 | N2 | Mat1 | Sec1 | Mat1 | 29000 | 11200 | 0.3 | 0 | Sec1 | 10 | 100 | N1 | true | true | true | N2 | FX | 10 | Displacements |
| N2 | 0 | 10 |
Excel formula:
=FRAME_2D({"N1",0,0;"N2",0,10}, {"C1","N1","N2","Mat1","Sec1"}, {"Mat1",29000,11200,0.3,0}, {"Sec1",10,100}, {"N1",TRUE,TRUE,TRUE}, {"N2","FX",10}, "Displacements")
Expected output:
| Node | Dx | Dy | Rz |
|---|---|---|---|
| N1 | 0 | 0 | 0 |
| N2 | 0.114943 | 0 | -0.0172414 |
Example 3: Member Forces Output
Inputs:
| nodes | members | materials | sections | supports | member_loads | pynite_frame_output | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 0 | 0 | M1 | N1 | N2 | Mat1 | Sec1 | Mat1 | 29000 | 11200 | 0.3 | 0 | Sec1 | 10 | 100 | N1 | true | true | true | M1 | dist | -1 | 0 | 10 | MemberForces |
| N2 | 10 | 0 | N2 | true | true | true |
Excel formula:
=FRAME_2D({"N1",0,0;"N2",10,0}, {"M1","N1","N2","Mat1","Sec1"}, {"Mat1",29000,11200,0.3,0}, {"Sec1",10,100}, {"N1",TRUE,TRUE,TRUE;"N2",TRUE,TRUE,TRUE}, {"M1","dist",-1,0,10}, "MemberForces")
Expected output:
| Member | Axial (Fx) | Shear (Fy) | Moment (Mz) Start | Moment (Mz) End |
|---|---|---|---|---|
| M1 | 0 | 5 | 8.33333 | 8.33333 |
Example 4: Stacked Results Output
Inputs:
| nodes | members | materials | sections | supports | nodal_loads | pynite_frame_output | |||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 0 | 0 | C1 | N1 | N2 | Mat1 | Sec1 | Mat1 | 29000 | 11200 | 0.3 | 0 | Sec1 | 10 | 100 | N1 | true | true | true | N2 | FX | 10 | Stacked |
| N2 | 0 | 10 |
Excel formula:
=FRAME_2D({"N1",0,0;"N2",0,10}, {"C1","N1","N2","Mat1","Sec1"}, {"Mat1",29000,11200,0.3,0}, {"Sec1",10,100}, {"N1",TRUE,TRUE,TRUE}, {"N2","FX",10}, "Stacked")
Expected output:
| Node | Rx | Ry | Mz | |
|---|---|---|---|---|
| N1 | -10 | 0 | 100 | |
| N2 | 0 | 0 | 0 | |
| Node | Dx | Dy | Rz | |
| N1 | 0 | 0 | 0 | |
| N2 | 0.114943 | 0 | -0.0172414 | |
| Member | Axial (Fx) | Shear (Fy) | Moment (Mz) Start | Moment (Mz) End |
| C1 | 0 | 10 | 100 | 1.42109e-14 |
Python Code
Show Code
import sys
import types
# PATCH: PyNiteFEA 2.0.2 pkg_resources fix
if "pip._vendor.pkg_resources" not in sys.modules:
mock_pkg = types.ModuleType("pip._vendor.pkg_resources")
mock_pkg.get_distribution = lambda _: types.SimpleNamespace(version="2.0.2")
mock_pkg.working_set = []
sys.modules["pip._vendor.pkg_resources"] = mock_pkg
pip = sys.modules.setdefault("pip", types.ModuleType("pip"))
vendor = sys.modules.setdefault("pip._vendor", types.ModuleType("pip._vendor"))
pip._vendor = vendor
vendor.pkg_resources = mock_pkg
from Pynite import FEModel3D
def frame_2d(nodes, members, materials, sections, supports, nodal_loads=[], member_loads=[], pynite_frame_output='Reactions'):
"""
Analyze 2D rigid frames. Supports nodal and member loads.
See: https://pynite.readthedocs.io/en/latest/index.html
This example function is provided as-is without any representation of accuracy.
Args:
nodes (list[list]): Nodes [name, x, y].
members (list[list]): Members [name, node_i, node_j, material_name, section_name].
materials (list[list]): Materials [name, E, G, nu, rho].
sections (list[list]): Sections [name, A, Iy].
supports (list[list]): Supports [node, fix_x, fix_y, fix_mz]. Bools.
nodal_loads (list[list], optional): Nodal loads [node, direction, magnitude]. Direction=FX, FY, MZ. Default is [].
member_loads (list[list], optional): Member loads [member, type, magnitude, x1, x2]. Type=point, dist. Default is [].
pynite_frame_output (str, optional): Output type. Valid options: Reactions, Displacements, Member Forces, Stacked Results. Default is 'Reactions'.
Returns:
list[list]: Analysis results.
"""
try:
def to2d(x): return [[x]] if not isinstance(x, list) else x
model = FEModel3D()
# Materials
for row in to2d(materials):
if not row: continue
model.add_material(str(row[0]), float(row[1]), float(row[2]), float(row[3]), float(row[4]))
# Sections
for row in to2d(sections):
if not row: continue
# 2D frame: Area and Iy are critical. Iz, J dummy.
model.add_section(str(row[0]), float(row[1]), float(row[2]), 1.0, 1.0)
# Nodes
for row in to2d(nodes):
if not row: continue
model.add_node(str(row[0]), float(row[1]), float(row[2]), 0.0)
# Members
for row in to2d(members):
if not row: continue
model.add_member(str(row[0]), str(row[1]), str(row[2]), str(row[3]), str(row[4]))
# 2D Stability: Fix all nodes out-of-plane
for n_name in model.nodes:
model.def_support(n_name, False, False, True, True, True, False)
# Supports
for row in to2d(supports):
if not row: continue
n = str(row[0])
fx, fy, fmz = bool(row[1]), bool(row[2]), bool(row[3])
# Fix out-of-plane for 2D stability
model.def_support(n, fx, fy, True, True, True, fmz)
# Nodal Loads
for row in to2d(nodal_loads):
if not row: continue
model.add_node_load(str(row[0]), str(row[1]), float(row[2]))
# Member Loads
for row in to2d(member_loads):
if not row: continue
m_name = str(row[0])
l_type = str(row[1]).lower()
mag = float(row[2])
x1 = float(row[3]) if len(row) > 3 else 0
if 'point' in l_type:
model.add_member_pt_load(m_name, 'Fy', mag, x1) # Assume vertical load in local y
elif 'dist' in l_type:
x2 = float(row[4]) if len(row) > 4 else x1
w2 = mag # Uniform by default
model.add_member_dist_load(m_name, 'Fy', mag, w2, x1, x2)
# Safe result helper
def get_val(obj, attr, *args):
try:
val = getattr(obj, attr)
if callable(val): val = val(*args)
if isinstance(val, dict): return float(val.get('Combo 1', 0.0))
return float(val)
except: return 0.0
# Add load combination and analyze
model.add_load_combo('Combo 1', {'Case 1': 1.0})
model.analyze(check_statics=False)
res = []
# Column count for Stacked output: 5
if pynite_frame_output == 'Reactions' or pynite_frame_output == 'Stacked':
header = ['Node', 'Rx', 'Ry', 'Mz']
if pynite_frame_output == 'Stacked': header += [''] * 1
res.append(header)
for n_nm in model.nodes:
n = model.nodes[n_nm]
row = [n_nm, get_val(n, 'RxnFX'), get_val(n, 'RxnFY'), get_val(n, 'RxnMZ')]
if pynite_frame_output == 'Stacked': row += [''] * 1
res.append(row)
if pynite_frame_output == 'Displacements' or pynite_frame_output == 'Stacked':
header = ['Node', 'Dx', 'Dy', 'Rz']
if pynite_frame_output == 'Stacked': header += [''] * 1
res.append(header)
for n_nm in model.nodes:
n = model.nodes[n_nm]
row = [n_nm, get_val(n, 'DX'), get_val(n, 'DY'), get_val(n, 'RZ')]
if pynite_frame_output == 'Stacked': row += [''] * 1
res.append(row)
if pynite_frame_output == 'MemberForces' or pynite_frame_output == 'Stacked':
res.append(['Member', 'Axial (Fx)', 'Shear (Fy)', 'Moment (Mz) Start', 'Moment (Mz) End'])
for m_nm in model.members:
m = model.members[m_nm]
res.append([m_nm, get_val(m, 'axial', 0), get_val(m, 'shear', 'Fy', 0),
get_val(m, 'moment', 'Mz', 0), get_val(m, 'moment', 'Mz', m.L())])
return res
except Exception as e:
return f"Error: {str(e)}"Online Calculator
PLATE
This function performs plate and shell analysis by building a finite element surface model from material properties, optional generated meshes, optional manually defined plate elements, supports, and pressure loads. It returns nodal displacement/reaction summaries and plate force result tables, depending on the selected output mode.
The solver applies linear elastic finite element equilibrium over plate degrees of freedom:
\mathbf{K}\mathbf{u} = \mathbf{F}
where \mathbf{K} is the assembled plate stiffness matrix, \mathbf{u} contains nodal displacement/rotation unknowns, and \mathbf{F} represents external nodal and equivalent surface load effects. Derived plate quantities (such as moment components) are then evaluated from the solved field.
Excel Usage
=PLATE(materials, meshes, plates, nodes, supports, pressures, pynite_plate_output)
materials(list[list], required): Materials [name, E, nu, rho].meshes(list[list], optional, default: []): Rectangular meshes [name, width, height, div_x, div_y, material, thickness, origin_x, origin_y, origin_z].plates(list[list], optional, default: []): Manual plates [name, n1, n2, n3, n4, material, thickness].nodes(list[list], optional, default: []): Manual nodes [name, x, y, z].supports(list[list], optional, default: []): Supports [node, fix_dx, fix_dy, fix_dz, fix_rx, fix_ry, fix_rz]. Bools.pressures(list[list], optional, default: []): Uniform pressures [plate_or_mesh, pressure].pynite_plate_output(str, optional, default: “Displacements”): Output type.
Returns (list[list]): Analysis results.
Example 1: Displacements Output
Inputs:
| materials | meshes | supports | pressures | pynite_plate_output | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Concrete | 3000000 | 0.2 | 0.15 | Mesh1 | 10 | 10 | 2 | 2 | Concrete | 0.5 | 0 | 0 | 0 | Mesh1_N_0_0 | true | true | true | true | true | true | Mesh1 | -10 | Displacements |
Excel formula:
=PLATE({"Concrete",3000000,0.2,0.15}, {"Mesh1",10,10,2,2,"Concrete",0.5,0,0,0}, {"Mesh1_N_0_0",TRUE,TRUE,TRUE,TRUE,TRUE,TRUE}, {"Mesh1",-10}, "Displacements")
Expected output:
| Node | Dx | Dy | Dz |
|---|---|---|---|
| Mesh1_N_0_0 | 0 | 0 | 0 |
| Mesh1_N_0_1 | 0 | 0 | -0.552582 |
| Mesh1_N_0_2 | 0 | 0 | -1.45879 |
| Mesh1_N_1_0 | 0 | 0 | -0.552582 |
| Mesh1_N_1_1 | 0 | 0 | -1.47975 |
| Mesh1_N_1_2 | 0 | 0 | -2.43399 |
| Mesh1_N_2_0 | 0 | 0 | -1.45879 |
| Mesh1_N_2_1 | 0 | 0 | -2.43399 |
| Mesh1_N_2_2 | 0 | 0 | -3.39759 |
Example 2: Reactions Output
Inputs:
| materials | meshes | supports | pressures | pynite_plate_output | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Concrete | 3000 | 0.2 | 0.15 | M1 | 5 | 5 | 1 | 1 | Concrete | 0.5 | 0 | 0 | 0 | M1_N_0_0 | true | true | true | true | true | true | M1 | -1 | Reactions |
Excel formula:
=PLATE({"Concrete",3000,0.2,0.15}, {"M1",5,5,1,1,"Concrete",0.5,0,0,0}, {"M1_N_0_0",TRUE,TRUE,TRUE,TRUE,TRUE,TRUE}, {"M1",-1}, "Reactions")
Expected output:
| Node | Rx | Ry | Rz |
|---|---|---|---|
| M1_N_0_0 | 0 | 0 | 25 |
| M1_N_0_1 | 0 | 0 | 0 |
| M1_N_1_0 | 0 | 0 | 0 |
| M1_N_1_1 | 0 | 0 | 0 |
Example 3: Plate Forces Output
Inputs:
| materials | nodes | plates | supports | pressures | pynite_plate_output | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Steel | 29000 | 0.3 | 0.283 | N1 | 0 | 0 | 0 | P1 | N1 | N2 | N3 | N4 | Steel | 0.2 | N1 | true | true | true | true | true | true | P1 | -5 | PlateForces |
| N2 | 10 | 0 | 0 | N2 | true | true | true | true | true | true | ||||||||||||||
| N3 | 10 | 10 | 0 | |||||||||||||||||||||
| N4 | 0 | 10 | 0 |
Excel formula:
=PLATE({"Steel",29000,0.3,0.283}, {"N1",0,0,0;"N2",10,0,0;"N3",10,10,0;"N4",0,10,0}, {"P1","N1","N2","N3","N4","Steel",0.2}, {"N1",TRUE,TRUE,TRUE,TRUE,TRUE,TRUE;"N2",TRUE,TRUE,TRUE,TRUE,TRUE,TRUE}, {"P1",-5}, "PlateForces")
Expected output:
| Plate | Mx | My | Mxy | Qx | Qy |
|---|---|---|---|---|---|
| P1 | 0 | 0 | 0 | 0 | 0 |
Example 4: Stacked Results Output
Inputs:
| materials | meshes | supports | pressures | pynite_plate_output | |||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Concrete | 3000 | 0.2 | 0.15 | M1 | 5 | 5 | 1 | 1 | Concrete | 0.5 | 0 | 0 | 0 | M1_N_0_0 | true | true | true | true | true | true | M1 | -1 | Stacked |
Excel formula:
=PLATE({"Concrete",3000,0.2,0.15}, {"M1",5,5,1,1,"Concrete",0.5,0,0,0}, {"M1_N_0_0",TRUE,TRUE,TRUE,TRUE,TRUE,TRUE}, {"M1",-1}, "Stacked")
Expected output:
| Node | Dx | Dy | Dz | ||
|---|---|---|---|---|---|
| M1_N_0_0 | 0 | 0 | 0 | ||
| M1_N_0_1 | 0 | 0 | -6.16228 | ||
| M1_N_1_0 | 0 | 0 | -6.16228 | ||
| M1_N_1_1 | 0 | 0 | -15.3246 | ||
| Node | Rx | Ry | Rz | ||
| M1_N_0_0 | 0 | 0 | 25 | ||
| M1_N_0_1 | 0 | 0 | 0 | ||
| M1_N_1_0 | 0 | 0 | 0 | ||
| M1_N_1_1 | 0 | 0 | 0 | ||
| Plate | Mx | My | Mxy | Qx | Qy |
| M1_Q_0_0 | 0 | 0 | 0 | 0 | 0 |
Python Code
Show Code
import sys
import types
# PATCH: PyNiteFEA 2.0.2 pkg_resources fix
if "pip._vendor.pkg_resources" not in sys.modules:
mock_pkg = types.ModuleType("pip._vendor.pkg_resources")
mock_pkg.get_distribution = lambda _: types.SimpleNamespace(version="2.0.2")
mock_pkg.working_set = []
sys.modules["pip._vendor.pkg_resources"] = mock_pkg
pip = sys.modules.setdefault("pip", types.ModuleType("pip"))
vendor = sys.modules.setdefault("pip._vendor", types.ModuleType("pip._vendor"))
pip._vendor = vendor
vendor.pkg_resources = mock_pkg
from Pynite import FEModel3D
def plate(materials, meshes=[], plates=[], nodes=[], supports=[], pressures=[], pynite_plate_output='Displacements'):
"""
Analyze 2D plates and shells. Supports rectangular meshing or manual quad definition.
See: https://pynite.readthedocs.io/en/latest/index.html
This example function is provided as-is without any representation of accuracy.
Args:
materials (list[list]): Materials [name, E, nu, rho].
meshes (list[list], optional): Rectangular meshes [name, width, height, div_x, div_y, material, thickness, origin_x, origin_y, origin_z]. Default is [].
plates (list[list], optional): Manual plates [name, n1, n2, n3, n4, material, thickness]. Default is [].
nodes (list[list], optional): Manual nodes [name, x, y, z]. Default is [].
supports (list[list], optional): Supports [node, fix_dx, fix_dy, fix_dz, fix_rx, fix_ry, fix_rz]. Bools. Default is [].
pressures (list[list], optional): Uniform pressures [plate_or_mesh, pressure]. Default is [].
pynite_plate_output (str, optional): Output type. Valid options: Displacements, Reactions, Plate Forces/Moments, Stacked Results. Default is 'Displacements'.
Returns:
list[list]: Analysis results.
"""
try:
def to2d(x): return [[x]] if not isinstance(x, list) else x
model = FEModel3D()
# Materials
for row in to2d(materials):
if not row: continue
name = str(row[0])
E = float(row[1])
nu = float(row[2])
rho = float(row[3])
G = E / (2 * (1 + nu)) # Calculate G
model.add_material(name, E, G, nu, rho)
# Nodes (Manual)
for row in to2d(nodes):
if not row: continue
model.add_node(str(row[0]), float(row[1]), float(row[2]), float(row[3]))
# Meshes
for row in to2d(meshes):
if not row: continue
name, w, h, div_x, div_y, mat, thick = str(row[0]), float(row[1]), float(row[2]), int(row[3]), int(row[4]), str(row[5]), float(row[6])
ox = float(row[7]) if len(row) > 7 else 0
oy = float(row[8]) if len(row) > 8 else 0
oz = float(row[9]) if len(row) > 9 else 0
dx_inc, dy_inc = w / div_x, h / div_y
# Generate mesh manually for robustness
node_map = {}
for i in range(div_x + 1):
for j in range(div_y + 1):
nx, ny, nz = ox + i * dx_inc, oy + j * dy_inc, oz
n_name = f"{name}_N_{i}_{j}"
model.add_node(n_name, nx, ny, nz)
node_map[(i,j)] = n_name
for i in range(div_x):
for j in range(div_y):
p_name = f"{name}_Q_{i}_{j}"
n1, n2, n3, n4 = node_map[(i,j)], node_map[(i+1,j)], node_map[(i+1,j+1)], node_map[(i,j+1)]
model.add_plate(p_name, n1, n2, n3, n4, thick, mat)
# Manual Plates
for row in to2d(plates):
if not row: continue
model.add_plate(str(row[0]), str(row[1]), str(row[2]), str(row[3]), str(row[4]), float(row[6]), str(row[5]))
# Supports
for row in to2d(supports):
if not row: continue
n = str(row[0])
flags = [bool(x) for x in row[1:7]]
model.def_support(n, *flags)
# Pressures
for row in to2d(pressures):
if not row: continue
target = str(row[0])
press = float(row[1])
# Target could be a single plate or a mesh prefix?
# Simple check:
if target in model.plates:
model.add_plate_surface_pressure(target, press)
else:
# Try prefix match
for p_name in model.plates:
if p_name.startswith(target):
model.add_plate_surface_pressure(p_name, press)
# Add load combination and analyze
model.add_load_combo('Combo 1', {'Case 1': 1.0})
model.analyze(check_statics=False)
# Safe result helper
def get_val(obj, attr, *args):
try:
val = getattr(obj, attr)
if callable(val): val = val(*args)
if isinstance(val, dict): return float(val.get('Combo 1', 0.0))
return float(val)
except: return 0.0
res = []
# max_cols = 6 for Stacked compatibility (PlateForces has 6)
if pynite_plate_output == 'Displacements' or pynite_plate_output == 'Stacked':
header = ['Node', 'Dx', 'Dy', 'Dz']
if pynite_plate_output == 'Stacked': header += [''] * 2
res.append(header)
for n_nm in model.nodes:
n = model.nodes[n_nm]
row = [n_nm, get_val(n, 'DX'), get_val(n, 'DY'), get_val(n, 'DZ')]
if pynite_plate_output == 'Stacked': row += [''] * 2
res.append(row)
if pynite_plate_output == 'Reactions' or pynite_plate_output == 'Stacked':
header = ['Node', 'Rx', 'Ry', 'Rz']
if pynite_plate_output == 'Stacked': header += [''] * 2
res.append(header)
for n_nm in model.nodes:
n = model.nodes[n_nm]
row = [n_nm, get_val(n, 'RxnFX'), get_val(n, 'RxnFY'), get_val(n, 'RxnFZ')]
if pynite_plate_output == 'Stacked': row += [''] * 2
res.append(row)
if pynite_plate_output == 'PlateForces' or pynite_plate_output == 'Stacked':
res.append(['Plate', 'Mx', 'My', 'Mxy', 'Qx', 'Qy'])
for p_nm in model.plates:
p = model.plates[p_nm]
# moment returns [Mx, My, Mxy]
m = get_val(p, 'moment', 0.5, 0.5)
# Since get_val might return a float if it fails, we handle it
if isinstance(m, list):
res.append([p_nm, m[0], m[1], m[2], 0, 0])
else:
res.append([p_nm, 0, 0, 0, 0, 0])
return res
except Exception as e:
return f"Error: {str(e)}"Online Calculator
TRUSS_2D
This function analyzes two-dimensional pin-jointed trusses using a finite element representation of nodes, axial members, support conditions, and nodal loads. It automatically applies rotational member end releases to emulate ideal truss behavior, then reports reactions, nodal displacements, and member axial forces.
For linear static loading, the assembled structural equations are solved in stiffness form:
\mathbf{K}\mathbf{u} = \mathbf{F}
with \mathbf{K} as the global truss stiffness matrix, \mathbf{u} as nodal displacement unknowns, and \mathbf{F} as external loads. Member axial force in each bar is then recovered from the relative end displacements and element stiffness.
Excel Usage
=TRUSS_2D(nodes, members, supports, loads, pynite_truss_output)
nodes(list[list], required): Nodes [name, x, y].members(list[list], required): Members [name, node_i, node_j, A, E].supports(list[list], optional, default: []): Supports [node, fix_x, fix_y]. Bools.loads(list[list], optional, default: []): Nodal loads [node, fx, fy].pynite_truss_output(str, optional, default: “AxialForces”): Output type (Reactions, AxialForces, Displacements).
Returns (list[list]): Analysis results.
Example 1: Axial Forces Output
Inputs:
| nodes | members | supports | loads | pynite_truss_output | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 0 | 0 | M1 | N1 | N2 | 10 | 29000 | N1 | true | true | N3 | 0 | -10 | AxialForces |
| N2 | 10 | 0 | M2 | N1 | N3 | 10 | 29000 | N2 | false | true | ||||
| N3 | 5 | 5 | M3 | N2 | N3 | 10 | 29000 |
Excel formula:
=TRUSS_2D({"N1",0,0;"N2",10,0;"N3",5,5}, {"M1","N1","N2",10,29000;"M2","N1","N3",10,29000;"M3","N2","N3",10,29000}, {"N1",TRUE,TRUE;"N2",FALSE,TRUE}, {"N3",0,-10}, "AxialForces")
Expected output:
| Member | Axial Force (k) |
|---|---|
| M1 | -5 |
| M2 | 7.07107 |
| M3 | 7.07107 |
Example 2: Reactions Output
Inputs:
| nodes | members | supports | loads | pynite_truss_output | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 0 | 0 | M1 | N1 | N2 | 10 | 29000 | N1 | true | true | N3 | 0 | -10 | Reactions |
| N2 | 10 | 0 | M2 | N1 | N3 | 10 | 29000 | N2 | false | true | ||||
| N3 | 5 | 5 | M3 | N2 | N3 | 10 | 29000 |
Excel formula:
=TRUSS_2D({"N1",0,0;"N2",10,0;"N3",5,5}, {"M1","N1","N2",10,29000;"M2","N1","N3",10,29000;"M3","N2","N3",10,29000}, {"N1",TRUE,TRUE;"N2",FALSE,TRUE}, {"N3",0,-10}, "Reactions")
Expected output:
| Node | Rx (k) | Ry (k) | Rz (k) | Mx (k-ft) | My (k-ft) | Mz (k-ft) |
|---|---|---|---|---|---|---|
| N1 | 8.88178e-16 | 5 | 0 | 0 | 0 | 0 |
| N2 | 0 | 5 | 0 | 0 | 0 | 0 |
| N3 | 0 | 0 | 0 | 0 | 0 | 0 |
Example 3: Displacements Output
Inputs:
| nodes | members | supports | loads | pynite_truss_output | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 0 | 0 | M1 | N1 | N2 | 10 | 29000 | N1 | true | true | N3 | 0 | -10 | Displacements |
| N2 | 10 | 0 | M2 | N1 | N3 | 10 | 29000 | N2 | false | true | ||||
| N3 | 5 | 5 | M3 | N2 | N3 | 10 | 29000 |
Excel formula:
=TRUSS_2D({"N1",0,0;"N2",10,0;"N3",5,5}, {"M1","N1","N2",10,29000;"M2","N1","N3",10,29000;"M3","N2","N3",10,29000}, {"N1",TRUE,TRUE;"N2",FALSE,TRUE}, {"N3",0,-10}, "Displacements")
Expected output:
| Node | Dx (in) | Dy (in) | Dz (in) | Rx (rad) | Ry (rad) | Rz (rad) |
|---|---|---|---|---|---|---|
| N1 | 0 | 0 | 0 | 0 | 0 | 0 |
| N2 | 0.000172414 | 0 | 0 | 0 | 0 | 0 |
| N3 | 0.0000862069 | -0.000330037 | 0 | 0 | 0 | 0 |
Example 4: Stacked Results Output
Inputs:
| nodes | members | supports | loads | pynite_truss_output | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N1 | 0 | 0 | M1 | N1 | N2 | 10 | 29000 | N1 | true | true | N3 | 0 | -10 | Stacked |
| N2 | 10 | 0 | M2 | N1 | N3 | 10 | 29000 | N2 | false | true | ||||
| N3 | 5 | 5 | M3 | N2 | N3 | 10 | 29000 |
Excel formula:
=TRUSS_2D({"N1",0,0;"N2",10,0;"N3",5,5}, {"M1","N1","N2",10,29000;"M2","N1","N3",10,29000;"M3","N2","N3",10,29000}, {"N1",TRUE,TRUE;"N2",FALSE,TRUE}, {"N3",0,-10}, "Stacked")
Expected output:
| Member | Axial Force (k) | |||||
|---|---|---|---|---|---|---|
| M1 | -5 | |||||
| M2 | 7.07107 | |||||
| M3 | 7.07107 | |||||
| Node | Rx (k) | Ry (k) | Rz (k) | Mx (k-ft) | My (k-ft) | Mz (k-ft) |
| N1 | 8.88178e-16 | 5 | 0 | 0 | 0 | 0 |
| N2 | 0 | 5 | 0 | 0 | 0 | 0 |
| N3 | 0 | 0 | 0 | 0 | 0 | 0 |
| Node | Dx (in) | Dy (in) | Dz (in) | Rx (rad) | Ry (rad) | Rz (rad) |
| N1 | 0 | 0 | 0 | 0 | 0 | 0 |
| N2 | 0.000172414 | 0 | 0 | 0 | 0 | 0 |
| N3 | 0.0000862069 | -0.000330037 | 0 | 0 | 0 | 0 |
Python Code
Show Code
import sys
import types
# PATCH: PyNiteFEA 2.0.2 pkg_resources fix
if "pip._vendor.pkg_resources" not in sys.modules:
mock_pkg = types.ModuleType("pip._vendor.pkg_resources")
mock_pkg.get_distribution = lambda _: types.SimpleNamespace(version="2.0.2")
mock_pkg.working_set = []
sys.modules["pip._vendor.pkg_resources"] = mock_pkg
pip = sys.modules.setdefault("pip", types.ModuleType("pip"))
vendor = sys.modules.setdefault("pip._vendor", types.ModuleType("pip._vendor"))
pip._vendor = vendor
vendor.pkg_resources = mock_pkg
from Pynite import FEModel3D
def truss_2d(nodes, members, supports=[], loads=[], pynite_truss_output='AxialForces'):
"""
Analyze 2D pin-jointed trusses. Members are automatically released for rotation.
See: https://pynite.readthedocs.io/en/latest/index.html
This example function is provided as-is without any representation of accuracy.
Args:
nodes (list[list]): Nodes [name, x, y].
members (list[list]): Members [name, node_i, node_j, A, E].
supports (list[list], optional): Supports [node, fix_x, fix_y]. Bools. Default is [].
loads (list[list], optional): Nodal loads [node, fx, fy]. Default is [].
pynite_truss_output (str, optional): Output type (Reactions, AxialForces, Displacements). Valid options: Axial Forces, Reactions, Displacements, Stacked Results. Default is 'AxialForces'.
Returns:
list[list]: Analysis results.
"""
try:
def to2d(x): return [[x]] if not isinstance(x, list) else x
model = FEModel3D()
# Nodes
for row in to2d(nodes):
if not row: continue
model.add_node(str(row[0]), float(row[1]), float(row[2]), 0.0)
# Members (A, E needed)
# We define a unique material/section for each Member or share?
# Simplest: create generic Mat/Sec for each execution or reused if identical.
# For truss, we just need A and E.
# We'll create a dummy material and section for each member or generic.
# Let's assume generic for this simple function if A/E are passed inline.
count = 0
for row in to2d(members):
if not row: continue
count += 1
m_name = str(row[0])
ni = str(row[1])
nj = str(row[2])
A = float(row[3])
E = float(row[4])
mat_name = f"Mat_{count}"
sec_name = f"Sec_{count}"
model.add_material(mat_name, E, E*0.4, 0.3, 1.0) # G approximated
model.add_section(sec_name, A, Iy=1.0, Iz=1.0, J=1.0) # Dummy inertias
model.add_member(m_name, ni, nj, mat_name, sec_name)
# RELEASE MOMENTS (Pin-Pin)
# PyNite 2.0+: member.releases = [xi, yi, zi, rix, riy, riz, xj, yj, zj, rjx, rjy, rjz] (Springs?)
# Or methods?
# Docs: model.def_releases(member_name, Dxi, Dyi, Dzi, Rxi, Ryi, Rzi, Dxj, Dyj, Dzj, Rxj, Ryj, Rzj)
# Releasing Rotation Z (Rzi, Rzj) for 2D truss behavior.
# Note: PyNite convention True = Fixed (Support). For releases, it's typically "Released" = True?
# Actually, FEModel3D.def_releases: "False" means fixed, "True" means released? Or "False" means released?
# Standard FEA: Support 'True' = Fixed. Release 'True' = Released?
# Let's check PyNite convention via testing or safe assumptions.
# Most PyNite examples: model.def_releases('M1', Rzi=True, Rzj=True) (kwargs).
# Since I can't verify, I'll use the kwarg method if it exists, or assume standard.
# Pynite docs say: def_releases(member_name, Rxi=False, ... Rzj=False) where True = Released.
# Release MZ at both ends
model.def_releases(m_name, Rzi=True, Rzj=True)
# 2D Stability: Fix all nodes out-of-plane
# For a TRUSS nodes are pins, but we still need to prevent RZ rotation if the solver expects it.
# Fix DZ, RX, RY, and RZ at ALL nodes to ensure global stability in 3D solver.
for n_name in model.nodes:
model.def_support(n_name, False, False, True, True, True, True)
# Supports
for row in to2d(supports):
if not row: continue
# [node, fix_x, fix_y]
n = str(row[0])
fx = bool(row[1])
fy = bool(row[2])
# Apply user-specified fixes for FX and FY.
# Out-of-plane DOFs (DZ, RX, RY, RZ) remain fixed from the stability loop above.
model.def_support(n, fx, fy, True, True, True, True)
# Nodal loads
for row in to2d(loads):
if not row or row[0] is None: continue
n, fx, fy = str(row[0]), float(row[1]), float(row[2])
model.add_node_load(n, 'FX', fx)
model.add_node_load(n, 'FY', fy)
# Add load combination and analyze
model.add_load_combo('Combo 1', {'Case 1': 1.0})
model.analyze(check_statics=False)
# Safe result helper
def get_val(obj, attr, *args):
try:
val = getattr(obj, attr)
if callable(val): val = val(*args)
if isinstance(val, dict): return float(val.get('Combo 1', 0.0))
return float(val)
except: return 0.0
res = []
# Column count for Stacked output: 7
if pynite_truss_output == 'AxialForces' or pynite_truss_output == 'Stacked':
header = ['Member', 'Axial Force (k)']
if pynite_truss_output == 'Stacked': header += [''] * 5
res.append(header)
for m_nm in model.members:
row = [m_nm, get_val(model.members[m_nm], 'axial', 0)]
if pynite_truss_output == 'Stacked': row += [''] * 5
res.append(row)
if pynite_truss_output == 'Reactions' or pynite_truss_output == 'Stacked':
res.append(['Node', 'Rx (k)', 'Ry (k)', 'Rz (k)', 'Mx (k-ft)', 'My (k-ft)', 'Mz (k-ft)'])
for n_name in model.nodes:
node = model.nodes[n_name]
# Use getattr for robustness in case combo name or attribute access differs
def get_val(obj, attr):
try:
val = getattr(obj, attr)
if isinstance(val, dict): return val.get('Combo 1', 0.0)
return float(val)
except: return 0.0
res.append([n_name, get_val(node, 'RxnFX'), get_val(node, 'RxnFY'),
get_val(node, 'RxnFZ'), get_val(node, 'RxnMX'),
get_val(node, 'RxnMY'), get_val(node, 'RxnMZ')])
if pynite_truss_output == 'Displacements' or pynite_truss_output == 'Stacked':
res.append(['Node', 'Dx (in)', 'Dy (in)', 'Dz (in)', 'Rx (rad)', 'Ry (rad)', 'Rz (rad)'])
for n_name in model.nodes:
node = model.nodes[n_name]
def get_val(obj, attr):
try:
val = getattr(obj, attr)
if isinstance(val, dict): return val.get('Combo 1', 0.0)
return float(val)
except: return 0.0
res.append([n_name, get_val(node, 'DX'), get_val(node, 'DY'),
get_val(node, 'DZ'), get_val(node, 'RX'),
get_val(node, 'RY'), get_val(node, 'RZ')])
return res
except Exception as e:
return f"Error: {str(e)}"Online Calculator