Getting started

This guide walks through a complete NuSA analysis using a one-dimensional bar model. The same workflow is used by the spring, truss, beam, and linear triangle families.

The basic workflow is:

Node + Element + Model
         |
         v
       solve()
         |
         v
    StaticResult

A model describes the finite-element problem. Solving the model returns a separate result object containing the numerical solution.

Installation

Install the current stable release from PyPI:

pip install nusa

For the current development version:

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

See Installation for Gmsh setup, development installation, and platform specific notes.

A first bar analysis

Consider a bar assembled from three finite elements. The end nodes are fixed and a horizontal force is applied at the second node.

The complete executable example used by NuSA’s test suite is shown below:

 1# -*- coding: utf-8 -*-
 2# ***********************************
 3#  Author: Pedro Jorge De Los Santos
 4#  E-mail: delossantosmfq@gmail.com
 5#  License: MIT License
 6# ***********************************
 7
 8from nusa import Bar, BarModel, Node
 9
10
11def test1():
12    """Logan (2007), Example 3.1."""
13    model = BarModel("Bar Model")
14
15    n1 = Node((0.0, 0.0))
16    n2 = Node((30.0, 0.0))
17    n3 = Node((60.0, 0.0))
18    n4 = Node((90.0, 0.0))
19
20    e1 = Bar((n1, n2), E=30e6, A=1.0)
21    e2 = Bar((n2, n3), E=30e6, A=1.0)
22    e3 = Bar((n3, n4), E=15e6, A=2.0)
23
24    model.add_nodes([n1, n2, n3, n4])
25    model.add_elements([e1, e2, e3])
26    model.add_force(n2, (3000.0,))
27    model.add_constraint(n1, ux=0.0)
28    model.add_constraint(n4, ux=0.0)
29
30    result = model.solve()
31
32    print("Node | Displacement | Nodal force")
33    for node in model.nodes:
34        print(
35            f"{node.label}\t"
36            f"{result.displacement(node)['ux']:.6f}\t"
37            f"{result.nodal_force(node)['fx']}"
38        )
39
40    return result
41
42
43if __name__ == "__main__":
44    test1()

The important steps are discussed below.

1. Create nodes

Nodes define geometry and identity:

from nusa import Node

n1 = Node((0.0, 0.0))
n2 = Node((30.0, 0.0))

A Node does not store solved displacements, forces, stresses, or strains. Those quantities belong to the result of an analysis.

2. Create elements

Elements connect nodes and define the finite-element formulation and physical properties:

from nusa import Bar

e1 = Bar((n1, n2), E=30e6, A=1.0)

For a bar element, E is Young’s modulus and A is the cross-sectional area.

3. Build the model

A model contains the finite-element problem definition:

from nusa import BarModel

model = BarModel("Bar Model")
model.add_nodes([n1, n2])
model.add_element(e1)

The model owns topology, loads, and prescribed displacements. It does not own the solved state.

4. Apply loads and constraints

Loads and constraints are expressed using the active degrees of freedom for the model family:

model.add_force(n2, (3000.0,))
model.add_constraint(n1, ux=0.0)

For a BarModel the only displacement degree of freedom is ux and the corresponding force component is fx.

5. Solve the problem

The simplest public entry point is:

result = model.solve()

This is equivalent to:

from nusa import LinearStaticAnalysis

result = LinearStaticAnalysis().solve(model)

Both forms return a nusa.result.StaticResult.

6. Read results

Use the result object to query solved quantities:

result.displacement(n2)
result.reaction(n1)
result.element_result(e1)

The corresponding global arrays are also available:

result.displacements
result.applied_loads
result.nodal_forces
result.reactions
result.element_results

The distinction between these quantities is important:

applied_loads

Loads explicitly specified in the model.

nodal_forces

Generalized nodal forces computed as K @ u.

reactions

Forces associated with prescribed degrees of freedom.

7. Report and visualize

Solved reporting and visualization consume the result object:

result.simple_report()
result.plot_deformed_shape()

Problem visualization consumes the model instead:

from nusa import plot_model

plot_model(model)

This distinction reflects the central NuSA 0.4 design: models define problems; results describe solved analyses.

Next steps

Continue with How NuSA works for the architecture and ownership model, then explore the element families and result API in the user guide and API reference.