graph TD
A[Start: What is the dominant behavior?] --> B{Main load path in line members?}
B -- Yes --> C{Are joints intended as rigid?}
C -- Yes --> D[Use FRAME_2D]
C -- No, pin-jointed axial system --> E[Use TRUSS_2D]
B -- Yes, single/continuous beam focus --> F[Use BEAM_2D]
B -- No, area/surface load redistribution --> G[Use PLATE]
Finite Elements
Overview
Introduction
Finite element analysis (FEA) is a numerical method for solving boundary-value problems in structures, continua, and multi-physics systems by dividing a complex domain into smaller, connected elements. Instead of attempting a closed-form solution for an entire structure, FEA approximates displacement, rotation, stress, strain, and internal force fields over each element, then enforces compatibility and equilibrium at shared nodes. The method is now foundational in structural engineering, mechanical design, geotechnics, and many reliability workflows because real systems rarely match textbook geometries or loading conditions. For a broad conceptual reference, see Finite element method (Wikipedia).
In practical terms, finite elements convert “hard geometry + hard loading + hard supports” into a matrix problem that computers solve efficiently. Boardflare’s finite-elements category packages this workflow into spreadsheet-friendly tools backed by the PyNite structural analysis library. The category includes BEAM_2D, FRAME_2D, PLATE, and TRUSS_2D, each aimed at a different idealization of structural behavior. Together, these functions cover most first-pass linear static analysis tasks: one-dimensional flexural members, planar frame action, in-plane axial truss behavior, and two-dimensional plate/shell response.
For business and technical users, this matters because engineering decisions are usually constrained by time, not by theory. Teams often need to evaluate many alternatives—different spans, section properties, support layouts, or load patterns—before selecting one design direction for deeper code-compliant checks. Traditional spreadsheet methods can become brittle as models scale. Finite-element workflows provide a more systematic representation of stiffness interactions, boundary conditions, and load paths while still being automatable inside familiar analyst tooling.
The core economic value is faster and more defensible iteration. A project team can quickly answer questions such as: “How sensitive is midspan deflection to stiffness upgrades?”, “Which support reaction governs foundation design?”, or “How does axial force redistribute if one truss diagonal is resized?” Boardflare’s function set makes these scenarios repeatable by exposing solver inputs and outputs directly in tabular form. That structure supports scenario analysis, dashboarding, and design option comparisons without forcing every stakeholder into a standalone FEA desktop application.
It is important to frame scope clearly: these tools are designed for linear elastic, small-deformation structural response in typical conceptual-to-preliminary design workflows. They are excellent for member sizing studies, load path understanding, and rapid sensitivity checks. They are not substitutes for full nonlinear analyses, detailed code checks, connection design, or jurisdiction-specific stamped calculations where those are required.
When to Use It
Finite-element calculators are most valuable when the structure is too interconnected for hand methods but not so specialized that a full custom simulation stack is needed. The right use case is usually a “decision under uncertainty” problem: many design choices are possible, and the team needs robust, comparable response metrics quickly.
A first common scenario is building framing concept selection. Suppose a team is comparing a long-span floor beam with several support and section alternatives. They need reaction envelopes for support sizing, shear/moment shapes for reinforcement or connection planning, and displacement trends for serviceability. BEAM_2D fits this job: it captures support types, span definitions, and mixed load formats, then returns reactions, displacements, shear, moment, and deflection-oriented views. The business outcome is faster option screening and clearer communication to architects or owners about what drives structural depth and cost.
A second scenario is portal frame and low-rise lateral/gravity interaction studies. Early in a project, engineers often test several frame topologies and stiffness distributions before final member schedules are set. FRAME_2D is appropriate here because frame behavior couples axial, shear, and bending response through rigid joints. By modeling nodes, members, materials, sections, supports, and both nodal/member loads, a team can compare drift trends, base reactions, and member force patterns under different assumptions. This is useful for deciding where stiffness should be added (columns, beams, braces) and for identifying likely governing members before detailed code checks.
A third scenario is slab and panel behavior estimation under distributed pressure. Floor plates, equipment pads, and panelized systems often require more than line-member approximations, especially when support conditions are uneven or load spread is two-dimensional. PLATE addresses this by supporting mesh-based or manually defined plate models with pressure loading and six-DOF support constraints. It is especially helpful when teams need nodal displacement patterns and plate force components to determine whether a region warrants thickening, additional support points, or a more refined model downstream.
A fourth scenario is axial-force-dominated systems such as light trusses, roof bracing, and simplified bridge triangulations. When joints are intended to be pin-connected and bending is secondary, TRUSS_2D provides an efficient model that emphasizes member axial forces, support reactions, and nodal movements. This is ideal for rapid member force ranking, sign checking (tension vs compression), and preliminary sizing loops where dozens of geometry variants must be tested.
These functions are also useful in cross-functional contexts:
- In procurement planning, reactions and force envelopes support early sizing assumptions that influence cost forecasts.
- In risk reviews, displacement outputs highlight serviceability or compatibility concerns before late-stage redesign.
- In value engineering, side-by-side model outputs make it easier to quantify trade-offs between material usage and structural performance.
A practical heuristic is to use these tools when three conditions hold: (1) load paths are coupled enough that statics alone is cumbersome, (2) the team needs repeatable scenario runs, and (3) linear elastic assumptions are acceptable for the decision stage.
How It Works
All four calculators follow the same finite-element pipeline: discretize, assign properties, apply boundary conditions, load the model, solve for nodal degrees of freedom, and post-process element-level response quantities. The mathematical backbone is the global stiffness formulation implemented by PyNite.
At a high level, each element contributes a local stiffness matrix \mathbf{k}^{(e)} in its local coordinate system. Through coordinate transformation and assembly, these contributions populate a global matrix \mathbf{K} such that:
\mathbf{K}\mathbf{u} = \mathbf{F}
where:
- \mathbf{K} is the assembled global stiffness matrix,
- \mathbf{u} is the vector of unknown nodal displacements and rotations,
- \mathbf{F} is the global load vector including nodal loads and equivalent nodal loads from distributed actions.
After partitioning known and unknown DOFs (from supports/constraints), the reduced linear system is solved, then reactions are recovered from constrained DOFs:
\mathbf{R} = \mathbf{K}\mathbf{u} - \mathbf{F}
Element internal forces are obtained by mapping solved nodal DOFs back into element formulations. For beam/frame members, this yields shear and bending moments along the member coordinate; for truss elements, axial force is dominant; for plate elements, bending/twisting moments and transverse shears are derived from plate kinematics and constitutive relations.
For thin isotropic plate behavior, a common constitutive bending stiffness term is:
D = \frac{Et^3}{12(1-\nu^2)}
with elastic modulus E, plate thickness t, and Poisson ratio \nu. While users do not manually derive these matrices in Boardflare, understanding this dependency is essential: small increases in thickness can dramatically increase plate bending stiffness due to the t^3 relationship.
The calculators differ primarily in element type and DOF interpretation:
- BEAM_2D models 2D beam-line behavior and returns classic line-member outputs (reactions, nodal displacements, shear/moment, deflection-oriented summaries).
- FRAME_2D extends line-member modeling to rigid-joint frame behavior in a plane, capturing coupled axial-flexural effects across connected members.
- TRUSS_2D idealizes members as pin-jointed axial bars; rotational releases are applied to mimic truss assumptions.
- PLATE models 2D surface behavior through meshes or manually defined plate elements, appropriate when load redistribution is inherently area-based.
Underlying assumptions should be explicit in any engineering workflow:
- Linearity: Material behavior is elastic and load-displacement response is linear.
- Small deformations: Geometry changes under load do not significantly alter stiffness.
- Idealized supports/joints: Real boundary behavior is approximated with discrete constraints.
- Model fidelity dependence: Output quality depends on geometry, property, and load definitions.
These assumptions are often acceptable in preliminary design, but analysts should escalate to more advanced models when large displacement, nonlinear material behavior, instability, contact, cracking, or staged construction effects are expected.
For implementation details and solver behavior, refer to the PyNite documentation: PyNite Docs. Boardflare wraps these capabilities into spreadsheet-compatible interfaces that expose model inputs and selected output views.
Practical Example
Consider a design team evaluating a small single-story industrial structure with three analysis questions due in one review cycle:
- Can the roof beam layout meet serviceability under gravity load?
- How do frame support reactions and member actions change with alternate column stiffness?
- Does a local equipment slab region need thickening under pressure load?
The team can run this as a staged workflow using the category functions.
Step 1: Screen roof beam behavior with BEAM_2D.
The analyst defines span lengths, support conditions (for example pinned–roller or fixed), load cases (point and distributed), and material/section properties. The first pass requests reactions and moment output to identify peak support and internal demand locations. A second pass requests displacement/deflection output to evaluate serviceability trends. At this stage, the team is not trying to finalize member design; they are narrowing feasible options and identifying which combinations merit deeper review.
Step 2: Test frame system interactions with FRAME_2D.
Next, nodes and members are assembled for a portal frame abstraction. Material and section sets represent candidate stiffness options, while supports and loads capture boundary assumptions. The analyst runs at least two alternatives (baseline and stiffer columns) and compares reactions, nodal displacements, and member force outputs. If drift-sensitive behavior improves significantly with modest stiffness changes, the team gains a data-backed rationale for the preferred scheme before detailed design resources are committed.
Step 3: Evaluate local slab response with PLATE.
For the equipment zone, the analyst creates either a generated rectangular mesh or manually defined plate elements. Material, thickness, support constraints, and pressure loads are specified. Displacement outputs reveal deflection patterns and high-response regions; plate force outputs provide additional insight into bending demand directionality. If sensitivity runs show that a slight thickness increase greatly reduces displacement, this can become a targeted design intervention instead of globally over-thickening the slab.
Step 4: Validate bracing concept with TRUSS_2D.
A triangulated bracing alternative is assessed as a pin-jointed truss model. The analyst checks axial force distribution and identifies likely compression-critical members for preliminary sizing. Reactions and displacements are reviewed to ensure compatibility with adjacent framing assumptions. This isolates whether the bracing concept behaves as intended before adding connection and out-of-plane complexity.
Step 5: Consolidate decisions in a single comparison table.
Because outputs are tabular, the team can align key metrics across options: maximum displacement, controlling reaction, peak bending/axial indicators, and qualitative constructability notes. This creates a transparent audit trail from assumptions to recommendations, which is useful in design reviews and stakeholder communication.
Why this workflow is effective in Boardflare rather than ad hoc spreadsheets:
- It keeps the structural logic explicit through model inputs instead of hidden equation chains.
- It supports repeatable scenario runs with consistent output schemas.
- It enables phased fidelity: quick screening first, refinement second.
- It reduces manual transcription risk when comparing alternatives.
The result is not “automatic final design.” The result is faster convergence on the right engineering questions and the most promising design directions.
How to Choose
Choosing the right calculator starts with structural idealization. The most common mistake is selecting a solver based on convenience rather than dominant behavior. A short decision tree helps:
The table below provides a more detailed selection guide.
| Function | Best for | Key inputs | Primary outputs | Strengths | Limitations |
|---|---|---|---|---|---|
| BEAM_2D | Single or continuous planar beam lines with standard supports and load patterns | Spans, supports, loads, material/section properties | Reactions, nodal displacements, shear, moment, deflection-style summaries | Fast setup, clear line-member diagrams, excellent for serviceability and support demand screening | Not intended for full frame joint interaction or 2D area behavior |
| FRAME_2D | Planar rigid-joint systems (portal frames, multi-member lateral/gravity interaction) | Nodes, members, materials, sections, supports, nodal/member loads | Reactions, displacements, member force quantities | Captures coupled axial-shear-bending behavior in connected frames | Still a planar linear model; 3D effects and advanced nonlinear behavior are outside scope |
| TRUSS_2D | Pin-jointed axial systems (trusses, bracing layouts) | Nodes, members (A, E), supports, nodal loads | Axial forces, reactions, displacements | Efficient for tension/compression force paths and rapid sizing loops | Assumes truss idealization; bending and joint rigidity effects are not primary |
| PLATE | Surface elements under pressure/distributed action (slabs, panels, local plate regions) | Materials, meshes/plates/nodes, supports, pressures | Nodal displacements, reactions, plate force tables | Represents area load redistribution and surface response beyond line models | Mesh quality/definition matters; interpretation requires plate behavior awareness |
A practical selection workflow:
- Start with geometry and connection intent: line members vs surfaces; rigid vs pin joints.
- Match dominant response metric to solver output: moment/shear trends, axial forces, or plate effects.
- Run a coarse baseline model first, then refine only where sensitivity indicates high leverage.
- Validate that assumptions (linearity, support idealization) are acceptable for the decision stage.
If two tools seem plausible, use both at low fidelity as a cross-check. For example, a beam abstraction with BEAM_2D can quickly bracket expected trends before a fuller FRAME_2D model is built. Likewise, a frame can identify boundary force patterns that inform where PLATE detail is worth the additional setup.
The category is strongest when used as a hierarchy of insight:
- TRUSS_2D for axial-system concept screening,
- BEAM_2D for line-member flexural behavior,
- FRAME_2D for connected planar system response,
- PLATE for local or global surface behavior.
Selecting the smallest model that still captures the governing behavior generally yields the best balance of speed, interpretability, and decision quality.
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: Simply supported beam reactions under uniform load
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: Cantilever beam tip displacement under end point load
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 samples for uniformly loaded simple span
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 samples for uniformly loaded simple span
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: Support-node deflection summary for a centered point load
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: Combined beam output for a uniformly loaded simple span
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
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/member.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)
elif 'moment' 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, 'Mz', mag, x1 - m.i_node.X)
break
# 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: Portal frame support reactions under beam distributed load
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: Cantilever column displacement from horizontal nodal load
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: Fixed-fixed beam end forces from uniform member load
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: Combined frame output for a cantilever column load case
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
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/member.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: Mesh nodal displacements under uniform surface pressure
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: Supported corner reaction summary for a single mesh panel
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 center force and shear result sample
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 | 4.34028 | -83.3333 | 1.17832e-14 | -9.32486e-15 | -29.9603 |
Example 4: Combined plate displacement reaction and force 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 | -4.16667 | -4.16667 | 0.171327 | -4.77865 | -4.77865 |
Python Code
Show Code
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/plate.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
def flatten_result(value):
if hasattr(value, 'flatten'):
value = value.flatten().tolist()
elif isinstance(value, tuple):
value = list(value)
return value
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]
try:
center_x = ((p.j_node.X - p.i_node.X)**2 + (p.j_node.Y - p.i_node.Y)**2 + (p.j_node.Z - p.i_node.Z)**2)**0.5 / 2
center_y = ((p.n_node.X - p.i_node.X)**2 + (p.n_node.Y - p.i_node.Y)**2 + (p.n_node.Z - p.i_node.Z)**2)**0.5 / 2
moment = flatten_result(p.moment(center_x, center_y, combo_name='Combo 1'))
shear = flatten_result(p.shear(center_x, center_y, combo_name='Combo 1'))
res.append([
p_nm,
float(moment[0]),
float(moment[1]),
float(moment[2]),
float(shear[0]),
float(shear[1]),
])
except:
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: Triangle truss axial member forces under apex load
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: Triangle truss support reactions under apex load
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: Triangle truss nodal displacements under apex load
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: Combined triangle truss output for an apex point load
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
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/member.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