# 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`](https://crates.io/crates/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 `mut`able, 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.