#finite-element #node #truss #solver #2d #force #trusses

qtruss

A simple finite-element solver for trusses

29 releases (12 breaking)

0.13.0 Oct 29, 2023
0.11.0 Oct 23, 2023

#448 in Algorithms

MIT license

200KB
942 lines

QTruss

A simple finite-element solver for 2D trusses.

Getting Started

Importing maria-linalg

You must import the latest version of the Rust crate maria-linalg in order to use this package.

Creating a List of Nodes

First, you must construct a list of nodes. Each node must have a location and a constraint type. There are four types of constraints.

  • Constraint::Pin. 0 degrees of freedom. An immobile pin joint.
  • Constraint::Free (force). 2 degrees of freedom. A free joint with an applied force of type maria_linalg::Vector<2>. Note that this applied force may be zero.
  • Constraint::HorizontalSlide (force). 1 degree of freedom. A horizontal slider joint with an applied force of type maria_linalg::Vector<2>. Note that this applied force may be zero. This joint is free to move in the X direction but cannot move in the Y direction.
  • Constraint::VerticalSlide (force). 1 degree of freedom. A vertical slider joint with an applied force of type maria_linalg::Vector<2>. Note that this applied force may be zero. This joint is free to move in the X direction but cannot move in the Y direction.

Construct nodes according to the following method.

use maria_linalg::Vector;
use qtruss::{Constraint, Node};

// Note that From<[f64; N]> is implemented for Vector<N>
let n0 = Node::new(
    [0.0, 1.0].into(),
    Constraint::Pin,
);

// Note that From<[f64; N]> is implemented for Vector<N>
let n1 = Node::new(
    [0.0, 1.0].into(),
    Constraint::HorizontalSlide ([-1.0, 0.0].into()),
);

Constructing a Truss

Once nodes are constructed, place these in an array like so.

let nodes = [
    n0,
    n1,
    // All the nodes
];

You are now ready to construct your truss. Note that the truss should be mutable, as you will add elements later. Additionally, the Truss::solve function stores its results inside of the Truss structure.

Note that there are three generic constants that must be defined.

  • N: usize. The number of nodes in this truss.
  • K: usize. The number of elements in this truss.
  • F: usize. The number of degrees of freedom in this truss. As of the latest version of qopt, you must compute this manually. Compute this by summing the number of degrees of freedom for each node (see above about enum Constraint).
let mut truss = Truss::<N, K, F>::new(nodes);

Creating Elements

Create elements according to the following pattern.

truss.add(n0, n1);

This function has two arguments.

  • n0: usize. The index in nodes of the first node to which this element connects.
  • n1: usize. The index in nodes of the second node to which this element connects.

Note that the area and material of this element will be passed at evaluation time. This allows improved run-time on specific optimization problems.

Call Truss::add for every element in your truss.

This function returns Option<usize>. If the provided node numbers are valid, it returns variant Some with the index of this element. The first call to Truss::add will have index 0, the second call index 1, and so on. If the provided node numbers are invalid (beyond the range of nodes), this function returns variant None.

Solving the Truss

let (forces, displacements) = truss.solve(areas, materials).unwrap();

This function has two arguments.

  • areas: [f64; K]. The areas of each element in the truss.
  • materials: [Material; K]. The materials of each element in the truss.

Determining Member Forces

let force: Option<f64> = truss.internal_force(areas, materials, k);

This function has three arguments.

  • areas: [f64; K]. The areas of each element in the truss.
  • materials: [Material; K]. The materials of each element in the truss.
  • k: usize. The index of the desired element. This is determined by the number of calls to Truss::add. The first call corresponds to k = 0, the second call k = 1, and so on.

This function is positive when the element is in tension and negative when the element is in compression.

If force is of variant None, there is something preventing qtruss from solving your truss. Check your N, K, and F values, and ensure that your truss is fully constrained.

Determining Node Displacements

let displacement: Option<Vector<2>> = truss.displacement(areas, materials, n);

This function has three arguments.

  • areas: [f64; K]. The areas of each element in the truss.
  • materials: [Material; K]. The materials of each element in the truss.
  • n: usize. The index of the desired node in nodes.

If displacement is of variant None, there is something preventing qtruss from solving your truss. Check your N, K, and F values, and ensure that your truss is fully constrained.

Determining Total Truss Compliance

let compliance: Option<f64> = truss.compliance(areas, materials);

This function has two arguments.

  • areas: [f64; K]. The areas of each element in the truss.
  • materials: [Material; K]. The materials of each element in the truss.

If compliance is of variant None, there is something preventing qtruss from solving your truss. Check your N, K, and F values, and ensure that your truss is fully constrained.

Determining Truss Volume

let volume: f64 = truss.volume(areas);

This function has one argument.

  • areas: [f64; K]. The areas of each element in the truss.

Determining Truss Fabrication Complexity

let complexity: f64 = truss.volume(areas, maxarea, beta);

This function has three arguments.

  • areas: [f64; K]. The areas of each element in the truss.
  • maxarea: f64. An area standardization term of each element. This should be close to the maximum allowable area.
  • beta: f64. A term to adjust the regularized Heaviside distribution.

Dependencies

~5–15MB
~192K SLoC