Metadata-Version: 2.4
Name: nusa
Version: 0.4.0
Summary: Numerical Structural Analysis in Python using the finite element method
Author-email: Pedro Jorge De Los Santos <delossantosmfq@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/JorgeDeLosSantos/nusa
Project-URL: Documentation, https://jorgedelossantos.github.io/nusa/
Project-URL: Repository, https://github.com/JorgeDeLosSantos/nusa
Project-URL: Issues, https://github.com/JorgeDeLosSantos/nusa/issues
Keywords: structural analysis,finite element analysis,mechanical engineering,engineering education
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: matplotlib
Requires-Dist: tabulate
Requires-Dist: meshio
Provides-Extra: mesh
Requires-Dist: meshio; extra == "mesh"
Provides-Extra: test
Requires-Dist: pytest>=8; extra == "test"
Requires-Dist: pytest-cov>=5; extra == "test"
Provides-Extra: docs
Requires-Dist: sphinx>=7; extra == "docs"
Requires-Dist: sphinx-rtd-theme>=2; extra == "docs"
Requires-Dist: numpydoc>=1.6; extra == "docs"
Dynamic: license-file

# NuSA

![](nusa/img/nusa-logo.png)

A Python library for structural analysis using the finite element method, designed for academic purposes.

![PyPI](https://img.shields.io/pypi/v/nusa)
![Python](https://img.shields.io/pypi/pyversions/nusa)
![License](https://img.shields.io/github/license/JorgeDeLosSantos/nusa)

## 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)

## 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](https://gmsh.info/) executable to be installed and available on `PATH`.

## Installation

Install the current PyPI release:

```bash
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:

```bash
gmsh --version
```

Install the current development branch directly from GitHub:

```bash
pip install "nusa @ git+https://github.com/JorgeDeLosSantos/nusa.git@develop"
```

For local development:

```bash
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:

```python
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.

## Supported element types

* Spring
* Bar
* Truss
* Beam
* Linear triangle (plane stress formulation)

## Mini-Demos

### Linear Triangle Element

```python
import numpy as np

from nusa import LinearTriangle, LinearTriangleModel, 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)
    
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), 200e9, 0.3, 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")
```

![](docs/nusa-info/es/src/linear-triangle-element/model_plot.png)
![](docs/nusa-info/es/src/linear-triangle-element/nsol.png)


### 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.

![](docs/nusa-info/es/src/spring-element/example_01.PNG)

```python
# 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 10<sup>6</sup> psi.

![](docs/nusa-info/es/src/beam-element/P913_beer.PNG)

```python
"""
Beer & Johnston. (2012) Mechanics of materials. 
Problem 9.13 , pp. 568.
"""
from nusa import Beam, BeamModel, Node

# Input data 
E = 29e6
I = 291 # W14x30 
P = 35e3
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),E,I)
e2 = Beam((n2,n3),E,I)

# 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

* [wxTruss](https://github.com/JorgeDeLosSantos/wxtruss)


## Documentation and Examples

You can view the online documentation at the following link: [https://jorgedelossantos.github.io/nusa/](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:** delossantosmfq@gmail.com
