Skip to main content

FEM2D

PyPI version Downloads GitHub repo License: MIT

An open-source Python library for structural finite element analysis of 2D structures.

FEM2D is a Python package for performing 2D finite element analysis (FEA) of structural frames, including truss, beam, and spring elements with support for linear static analysis, geometrically non-linear analysis, and global mass matrix assembly for dynamic analysis.

Documentation: Documentation Status

Features

  • Element Library:
    • Beam Element: Elastic 2D Euler-Bernoulli beam elements including axial and bending stiffness (shear deformation is neglected). Supports uniform and varying member loads, moment releases (hinges), and rotational/translational mass.
    • Truss Element: Pin-jointed bar elements with axial stiffness only. The same TrussElement class is used for linear and corotational geometrically-nonlinear analyses.
    • Spring Element: 2D elastic spring elements with customizable axial stiffness.
  • Analysis Types:
    • Linear Static Analysis: Standard matrix analysis under nodal loads, distributed loads, and concentrated member loads.
    • Geometrically Non-Linear Analysis: Iterative solver using the Newton-Raphson scheme combined with corotational formulations for large displacement/rotation problems. Enabled by passing geometric_nonlinear=True to Structure.solve(...).
    • Mass Matrix Assembly: Assembles global mass matrices (including rotational inertia and extra non-structural mass) to support modal and eigenvalue analysis.
  • Post-Processing & Visualization:
    • Pandas Integration: Convert displacements, reactions, and element forces directly into pandas DataFrames for easy analysis and post-processing.
    • Graphical Plots: Publication-ready visualization of undeformed and deformed shapes, realistic boundary supports (fixed clamp, pin, roller), load notation badges, and shaded distributed load bands using Matplotlib.

Project Structure & Architecture

The repository is modularly architected into distinct layers: core finite element abstractions, element formulations, constitutive material models, cross-sections, numerical solvers, external schema adapters, and visualization utilities:

fem-2d/
├── fem2d/                       # Core finite element analysis package
│   ├── structure.py             # Global model container, DOF indexing, matrix assembly, and solvers
│   ├── nodes.py                 # Node definitions, boundary conditions [ux, uy, rz], mass/inertia
│   ├── loads.py                 # Concentrated forces/moments, UDLs, and triangular/varying UVLs
│   ├── solver.py                # Newton-Raphson nonlinear solver for large displacements
│   ├── results.py               # Post-processing, pandas DataFrame extraction, and text/HTML reports
│   ├── buckling_analysis.py     # Elastic buckling analysis via geometric stiffness (Ke + λ Kg)
│   ├── m_phi_analysis.py        # Cross-section moment-curvature (M-φ) analysis
│   ├── elements/                # Structural element library
│   │   ├── element.py           # Abstract base element class (ElementBase)
│   │   ├── beam.py              # 2D Euler-Bernoulli beam (axial + bending stiffness, mass, deformed shape)
│   │   ├── beam_hinges.py       # Beam element with moment releases (hinge_i, hinge_j)
│   │   ├── truss.py             # Linear and corotational geometrically non-linear truss element
│   │   ├── spring.py            # 2D translational elastic spring element
│   │   ├── beamNL.py            # Geometrically non-linear beam formulations
│   │   ├── beam_materialNL.py   # Material-nonlinear beam element
│   │   └── trussNL.py           # Specialized nonlinear truss formulation
│   ├── materials/               # Constitutive material relationships
│   │   ├── material.py          # Abstract material base class
│   │   ├── elastic.py           # Linear elastic material (ElasticMaterial)
│   │   ├── bilinear.py          # Elastoplastic bilinear material (BilinearMaterial)
│   │   └── csv_material.py      # Custom stress-strain curve loaded from CSV (CSVMaterial)
│   ├── sections/                # Geometric cross-section definitions
│   │   ├── section.py           # Standard cross-section (A, Iz)
│   │   ├── fiber.py             # Discretized fiber section for nonlinear stress integration
│   │   └── moment_curvature.py  # User-defined moment-curvature section
│   ├── adapters/                # Interoperability with the structural engineering ecosystem
│   │   └── struct_core_adapter.py # Two-way converter for the struct_core JSON schema
│   └── utils/                   # High-level utilities and visualization
│       ├── simple_frame.py      # Streamlined high-level builder API (SimpleFrame)
│       └── draw_structure.py    # Publication-quality structure visualizer (DrawStructure)
├── examples/                    # Executable scripts and benchmarks
│   ├── linear/                  # Classical textbook frames, continuous beams, and trusses
│   ├── non_linear/              # Corotational trusses, buckling factors, and nonlinear materials
│   ├── dynamic/                 # Mass matrix assembly, modal analysis, and time-history examples
│   └── adapters/                # Model conversion and round-tripping with struct_core
├── tests/                       # Comprehensive automated test suite (pytest)
└── docs/                        # Sphinx documentation configuration and guides

Module Breakdown

Module / Directory Primary Role Key Classes & Functions
fem2d.structure Central coordinator for the FE model Structure
fem2d.nodes Spatial points and boundary constraints Node
fem2d.loads Concentrated loads, UDLs, and UVLs PointLoad, DistributedLoad, TriangularLoad, ElementPointLoad
fem2d.elements 2D element stiffness and mass formulations BeamElement, BeamWithHingesElement, TrussElement, SpringElement
fem2d.materials Material constitutive laws ElasticMaterial, BilinearMaterial, CSVMaterial
fem2d.sections Cross-section geometry & fiber discretization Section, FiberSection, MomentCurvatureSection
fem2d.solver Nonlinear iterative solvers NewtonRaphsonSolver
fem2d.buckling_analysis Critical elastic buckling factors buckling_analysis
fem2d.results Tabular output and pandas integration Results
fem2d.adapters Ecosystem data interchange (struct_core) model_from_core, model_to_core, result_to_core
fem2d.utils.simple_frame High-level quick-modeling interface SimpleFrame
fem2d.utils.draw_structure Engineering visualization & figure export DrawStructure

Installation

From Source (Developer Install)

  1. Clone the repository:

    git clone https://github.com/learnstructure/fem-2d.git
    cd fem-2d
    
  2. Install in editable mode along with development dependencies:

    pip install -e .[dev]
    

Quick Start Examples

1. Linear Static Frame Analysis (High-Level API)

The SimpleFrame class provides a simplified API for building and solving structures.

from fem2d import SimpleFrame
from fem2d.results import Results

# Initialize simple frame
frame = SimpleFrame()

# Define nodes (id, x, y)
frame.add_node(1, 0.0, 0.0)
frame.add_node(2, 0.0, 120.0)
frame.add_node(3, 120.0, 120.0)
frame.add_node(4, 120.0, 0.0)

# Properties
E = 30000.0  # ksi
A = 10.0     # sq. in.
I = 200.0    # in^4

# Add frame elements (id, node_i, node_j, E, A, I)
frame.add_frame(1, 1, 2, E, A, I)
frame.add_frame(2, 2, 3, E, A, I / 2)
frame.add_frame(3, 3, 4, E, A, I)

# Apply fixed supports at base nodes (node_id, [ux, uy, rz])
frame.add_support(1, [True, True, True])
frame.add_support(4, [True, True, True])

# Apply nodal loads (node_id, [Fx, Fy, Mz])
frame.add_node_load(2, [10.0, 0.0, 0.0])
frame.add_node_load(3, [0.0, 0.0, 5.0])

# Solve the structure
frame.solve()

# Retrieve results
results = Results(frame)
print("Node Displacements:\n", results.node_displacements())
print("Reactions:\n", results.reactions())
print("Element End Forces:\n", results.element_forces())

2. Geometrically Non-Linear Truss Analysis

For advanced analyses, use the core Structure class. The same TrussElement is used for both linear and corotational geometrically non-linear analyses — pass geometric_nonlinear=True to Structure.solve(...) to switch on the corotational formulation.

from fem2d import Structure, Node, ElasticMaterial, TrussElement
from fem2d.results import Results
from fem2d.sections import Section

# Create structure and nodes
structure = Structure()
node1 = Node(1, 0.0, 0.0)
node2 = Node(2, 4.0, 3.0)
node3 = Node(3, 8.0, 0.0)

structure.add_node(node1)
structure.add_node(node2)
structure.add_node(node3)

# Material and section
E = 200e6              # Material modulus (kN/m^2)
EA = 45155.0           # axial stiffness (kN)
A = EA / E             # cross-sectional area (m^2)
material = ElasticMaterial(E)
section = Section(A)

# Add linear truss elements (corotational path is enabled via solve below)
structure.add_element(TrussElement(1, node1, node2, material, section))
structure.add_element(TrussElement(2, node2, node3, material, section))
structure.add_element(TrussElement(3, node3, node1, material, section))

# Support boundaries — compact (ux, uy) form
node1.set_support(1, 1)  # pinned
node3.set_support(0, 1)  # roller

# External vertical point force at Node 2
node2.set_load(fx=0.0, fy=-2000.0, mz=0.0)

# Run Newton-Raphson analysis with corotational formulation
structure.solve(geometric_nonlinear=True, tolerance=1e-8, max_iter=30)

# Print displacements and forces
results = Results(structure)
print(results.node_displacements())
print(results.element_forces())

3. Visualizing Structures

DrawStructure renders publication-ready Matplotlib plots with realistic support symbols (fixed clamps, pins, rollers), clear load notation badges, shaded distributed load bands, internal hinge releases, and deformed shapes:

from fem2d import DrawStructure

# Initialize plotter with analyzed structure (set displacement scale factor)
plotter = DrawStructure(structure, scale=50.0)

# Render structure in Matplotlib window (or save directly to file)
plotter.draw(
    show_deformed=True,
    show_loads=True,
    show_supports=True,
    show_node_labels=True,
    show_grid=False,             # Clean background without distracting gridlines
    support_style="detailed",    # "detailed" (fixed clamp, pin, roller) or "box"
    save_path="deformed_shape.png"  # Optional: export 300 DPI high-res figure
)

4. Ecosystem Integration with struct_core

fem2d ships with a three-function adapter to the struct_core schema so that models and analysis results can be shared with other packages in the ecosystem (visualizers, design tools, code-checkers, …).

Install the optional dependency:

pip install fem2d[ecosystem]

The adapter exposes three functions — all are pure conversions, no side effects, no global state. Naming follows <artefact>_<direction>_<target>:

Function Direction Purpose
model_from_core struct_core → fem2d Build a fem2d.Structure from a struct_core.StructuralModel (or Project).
model_to_core fem2d → struct_core Build a struct_core.StructuralModel from a fem2d.Structure / SimpleFrame / Results.
result_to_core fem2d → struct_core Build a struct_core.AnalysisResult from an analyzed fem2d model.

There is intentionally no result_from_core — round-tripping an AnalysisResult back into a fem2d.Results is out of scope.

4.1 Convert analysis results for downstream consumers

from fem2d import SimpleFrame, result_to_core
from struct_core import save_json

frame = SimpleFrame()
frame.add_node(1, 0.0, 0.0)
frame.add_node(2, 120.0, 0.0)
frame.add_frame(1, 1, 2, 30000.0, 10.0, 200.0)
frame.add_support(1, [True, True, True])
frame.add_node_load(2, [10.0, 0.0, 0.0])
frame.solve()

# fem2d → struct_core.AnalysisResult
result = result_to_core(frame, analysis_case_id="Static")

# Serialize to JSON for downstream consumers
save_json(result, "static_result.json")

4.2 Build a struct_core model from a fem2d Structure

from fem2d import SimpleFrame, model_to_core, result_to_core
from struct_core import Project, save_json

frame = SimpleFrame()
frame.add_node(1, 0.0, 0.0)
frame.add_node(2, 100.0, 0.0)
frame.add_frame(1, 1, 2, 30000.0, 10.0, 200.0)
frame.add_support(1, [True, True, True])
frame.add_node_load(2, [0.0, -1000.0, 0.0])
frame.solve()

# fem2d → struct_core.StructuralModel
sc_model = model_to_core(frame)

# Wrap in a Project and serialize
project = Project()
project.metadata.title = "Cantilever beam"
project.model = sc_model
save_json(project, "model.json")

4.3 Build a fem2d.Structure from a struct_core model

from fem2d import model_from_core

# `my_project` is a struct_core.Project (or StructuralModel)
structure = model_from_core(my_project)
structure.solve()

4.4 Round-trip a model through struct_core

The three functions are designed to be inverse:

from fem2d import SimpleFrame, model_from_core, model_to_core, result_to_core

frame = SimpleFrame()
# ... build & solve ...

# fem2d → core → fem2d
sc_model = model_to_core(frame)
round_tripped = model_from_core(sc_model)
round_tripped.solve()
assert round_tripped.disp == pytest.approx(frame.structure.disp)

Running Tests

Verify that your installation is working correctly by running the tests:

pytest

Citing FEM2D

If you use fem2d in your academic research or professional work, please cite it as follows:

Mandal, A. (2026). FEM2D: An open-source Python library for structural analysis of 2D structures (v0.5.0). Zenodo. https://doi.org/10.5281/zenodo.20990850

Refer to CITATION.cff for the BibTeX format details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Release files for fem2d 0.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fem2d 0.5.1
File Size Uploaded
fem2d-0.5.1.tar.gz 68.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fem2d 0.5.1
File Interpreter ABI Platform
fem2d-0.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 133.6 kB

Release files / fem2d-0.5.1.tar.gz

Download URL fem2d-0.5.1.tar.gz
Size 68.0 kB
Tags Source
SHA-256 checksum
How to use checksums
da40012c5dac2e175e8435af4fe1762d459e75924dd4007ccdd2bbf3cab098f6
BLAKE2b-256 checksum
How to use checksums
784226367a945fb3f42f45109917a96debdd085d63dd23884898d4e39134ee27
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release files / fem2d-0.5.1-py3-none-any.whl

Download URL fem2d-0.5.1-py3-none-any.whl
Size 65.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9edebc46d4cc1fb6d322649efabbd5c84d262ac016c82811a3c644e30ded3ab0
BLAKE2b-256 checksum
How to use checksums
0bcfe95058f8f9db81cc1537c5393088a40c2e5c452a2c152316ef6d3921cf38
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release history Release notifications | RSS feed

This release

0.5.1 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page