NuSA
A Python library for structural analysis using the finite element method, designed for academic purposes.
Releases
- 0.1.0 (16/11/2016)
- 0.2.0 (14/07/2019)
- 0.3.0a1 First alpha release (20/09/2026)
- 0.3.0b1 Beta release (21/09/2026)
- 0.3.0rc1 Release candidate (22/09/2026)
- 0.3.0rc2 Release candidate (22/09/2026)
- 0.3.0 Stable release (23/09/2026)
- 0.4.0 Stable release (06/10/2026)
- 0.5.0 Stable release (12/10/2026)
Requirements
NuSA requires Python 3.10 or newer and installs NumPy, Matplotlib, tabulate, and meshio as Python dependencies.
The nusa.mesh helpers can read triangular meshes immediately after installing NuSA.
Generating new meshes from geometry additionally requires the external
Gmsh executable to be installed and available on PATH.
Installation
Install the current PyPI release:
pip install nusa
Mesh-file support through meshio is included in the default installation.
The historical nusa[mesh] extra remains accepted as a compatibility alias,
but is no longer required.
To generate meshes, install Gmsh separately and verify that NuSA can discover it:
gmsh --version
Install the current development branch directly from GitHub:
pip install "nusa @ git+https://github.com/JorgeDeLosSantos/nusa.git@develop"
For local development:
git clone https://github.com/JorgeDeLosSantos/nusa.git
cd nusa
git checkout develop
python -m pip install -e ".[test]"
python -m pytest
Analysis workflow
NuSA 0.4 separates finite-element problem definition from solved state:
from nusa import LinearStaticAnalysis, solve
result = solve(model)
Equivalent explicit form:
result = LinearStaticAnalysis().solve(model)
Model contains topology, loads, and prescribed displacements. A solve returns
an independent StaticResult snapshot containing displacements, reactions,
nodal forces, and canonical element results.
Physical properties in NuSA 0.5
NuSA 0.5 uses reusable Material and Section objects for
Bar, Truss, and Beam, while LinearTriangle takes a Material and
explicit thickness. The examples below require NuSA 0.5.0 or newer
(or the current develop branch). If upgrading from 0.4.0, consult the
0.4 → 0.5 migration guide.
Supported element types
- Spring
- Bar
- Truss
- Beam
- Linear triangle (plane stress formulation)
Mini-Demos
Linear Triangle Element
import numpy as np
from nusa import LinearTriangle, LinearTriangleModel, Material, Node, plot_model
import nusa.mesh as nmsh
md = nmsh.Modeler()
a = md.add_rectangle((0,0),(1,1), esize=0.1)
b = md.add_circle((0.5,0.5), 0.1, esize=0.05)
md.subtract_surfaces(a,b)
nc, ec = md.generate_mesh()
x,y = nc[:,0], nc[:,1]
nodes = []
elements = []
for k,nd in enumerate(nc):
cn = Node((x[k],y[k]))
nodes.append(cn)
material = Material(E=200e9, nu=0.3)
for elm in ec:
i, j, k = int(elm[0]), int(elm[1]), int(elm[2])
ni, nj, nk = nodes[i], nodes[j], nodes[k]
ce = LinearTriangle((ni, nj, nk), material=material, thickness=0.1)
elements.append(ce)
model = LinearTriangleModel()
for node in nodes: model.add_node(node)
for elm in elements: model.add_element(elm)
Boundary conditions and loads
minx, maxx = min(x), max(x)
miny, maxy = min(y), max(y)
for node in nodes:
if np.isclose(node.x, minx):
model.add_constraint(node, ux=0, uy=0)
if np.isclose(node.x, maxx):
model.add_force(node, (10e3,0))
plot_model(model)
result = model.solve()
result.plot_nodal_field("von_mises_stress")
Spring element
Example 01. For the spring assemblage with arbitrarily numbered nodes shown in the figure,
obtain (a) the displacements of nodes 3 and 4, (b) the reaction forces at nodes 1 and 2,
and (c) the forces in each spring. A force of 5000 lb
is applied at node 4 in the x direction. The spring constants are given in the figure.
Nodes 1 and 2 are fixed.
# NuSA Demo
from nusa import Node, Spring, SpringModel
def test1():
"""
Logan, D. (2007). A first course in the finite element analysis.
Example 2.1, pp. 42.
"""
P = 5000.0
# Model
m1 = SpringModel("2D Model")
# Nodes
n1 = Node((0,0))
n2 = Node((0,0))
n3 = Node((0,0))
n4 = Node((0,0))
# Elements
e1 = Spring((n1,n3),1000.0)
e2 = Spring((n3,n4),2000.0)
e3 = Spring((n4,n2),3000.0)
# Adding elements and nodes to the model
for nd in (n1,n2,n3,n4):
m1.add_node(nd)
for el in (e1,e2,e3):
m1.add_element(el)
m1.add_force(n4, (P,))
m1.add_constraint(n1, ux=0)
m1.add_constraint(n2, ux=0)
result = m1.solve()
return result
if __name__ == '__main__':
test1()
Beam element
Example 02. For the beam and loading shown, determine the deflection at point C. Use E = 29 x 106 psi.
"""
Beer & Johnston. (2012) Mechanics of materials.
Problem 9.13 , pp. 568.
"""
from nusa import Beam, BeamModel, Material, Node, Section
Input data
E = 29e6
I = 291 # W14x30
P = 35e3
material = Material(E=E)
section = Section(I=I)
L1 = 5*12 # in
L2 = 10*12 #in
Model
m1 = BeamModel("Beam Model")
Nodes
n1 = Node((0,0))
n2 = Node((L1,0))
n3 = Node((L1+L2,0))
Elements
e1 = Beam((n1,n2), material=material, section=section)
e2 = Beam((n2,n3), material=material, section=section)
Add elements and nodes
for nd in (n1,n2,n3): m1.add_node(nd)
for el in (e1,e2): m1.add_element(el)
m1.add_force(n2, (-P,))
m1.add_constraint(n1, uy=0) # pin
m1.add_constraint(n3, uy=0) # roller
result = m1.solve()
Displacement at C point
print(result.displacement(n2)["uy"])
GUIs based on NuSA
Documentation and Examples
You can view the online documentation at the following link: https://jorgedelossantos.github.io/nusa/.
The repository also contains historical Jupyter notebooks under docs/nusa-info/.
Some of those notebooks predate the 0.3.0 API and are retained as archival material;
the executable scripts under examples/ and the current Sphinx documentation are
the recommended references for the current API.
About...
Developer: Pedro Jorge De Los Santos E-mail: [email protected]