A 2D linear-elastic FEA program for isotropic materials, built in Rust.
Magnetite is a simple linear-elastic mechanical solver for isotropic 2D models. Here's how it works:
-
First, we give Magnetite two things: an input json and a geometry file
- The input json is explained in greater detail below; it lays out boundary conditions and other parameters for the simulation, including part-thickness and material elasticity.
- The geometry is provided via an
svg
file. There's some limitations to what Magnetite supports, and this is covered below too. Optionally, you can provide CSV files of vertices, but this method tends to be convoluted.
-
Next, Magnetite rebuilds your geometry into a
.geo
file. This allows us to use Gmesh—an open source meshing program—to create a mesh of the geometry using Delaunay triangulation.Gmesh is a vastly equipped piece of software. The algorithm Magnetite uses is Delaunay-based, but many mesh conflicts are resolved by other Gmesh algorithms.
-
After parsing the geometry into a
.geo
file, Magnetite runs Gmesh to create a.msh
mesh. -
After the meshing has finished, Magnetite parses the mesh output into a list of Nodes and Vertices. At this time, it will apply boundary conditions listed in the input json.
-
Once the mesh has been parsed, it's onto solving. A lot happens under the hood, and the process is documented in detail here.
-
After solving the system, we post-process the system to solve for stresses, then we output the results as a
nodes.csv
andelements.csv
. -
Finally, Magnetite triggers a Python script to plot these elements in matplotlib.
Here's a fun example I did with the LinedIn logo. This mesh is pretty fine, but it only took Magnetite 0.286 seconds to solve on my not-so-special MacBook Air.
It's important to build this crate in release mode. The underlying linear algebra accelerators don't seem to trigger in dev mode, meaning that everything will work in dev—just very slowly.
To install Magnetite, run:
git clone https://github.com/kyle-tennison/Magnetite.git; cd Magnetite
cargo build --release
Then, optionally, you can alias the binary:
alias magnetite=target/release/magnetite
You will also need Gmsh installed on your machine.
Try running the example above! Go to examples/cover-example
and run the following:
magnetite input.json geom.svg --cmap gist_heat
The cmap
flag is optional; it tells us to use the gist_heat
colormap when plotting
our results in matplotlib. All matplotlib colormaps are supported.
Magnetite has some other options. Run the following to display the help page:
magnetite -h
Geometry can be provided in two ways:
- As an
.svg
file - As a series of
.csv
files
In both cases, geometry is limited to simple regions, meaning that regions can have holes, but they must contribute to the same "part." To facilitate this requirement, Magnetite needs one external region and any number of internal regions (including none).
The easiest way to generate a .svg
file for Magnetite is through Adobe Illustrator; however, this product is far-from free. If you don't have access to Illustrator, another vector art program should suffice.
Currently, Magnetite supports the following SVG elements:
rect
polyline
polygons
Unfortunately, this means that other common elements are not currently supported. This includes, but is not limited to:
path
(Includes Bézier)circle
elipse
Eventually, I hope to support these elements, but my priorities with this project are elsewhere.
In illustrator, if you exclusively use the rectangle and pen tool (without splines), your geometry should be parsed correctly.
The example above was created in illustrator:
Again, Magnetite needs one external region and any number of internal regions. The green in the image above shows the external region, which wraps the yellow internal regions. These interal regions will be "cut" out of the final geometry.
To tell Magnetite what's internal versus external, we simply name the layers INNER
or OUTER
—respectively:
It's not pretty, but it works. Eventually, Magnetite might support .dxf
drawings.
Notice how there's a few leftover layers? That's perfectly fine. Any layers that aren't named
INNER
orOUTER
will be ignored.
We can export this document as a .svg
, then we're ready to run a simulation on it!
If two vertices are closer than the minimum element length specified in the input json, one will be removed. If something ends up looking jagged, try refining the mesh.
CSV files are simpler to use, but they are cumbersome to create. These CSV files only have two fields, x & y, which detail the vertices in the model. The ordering of these vertices defines the connections between them.
x,y
-11,4.5
-10,4.5
-9,4.5
-8,4.5
-7,4.5
...
CSV Files follow the same OUTER-INNER requirements defined above. Magnetite will always require one external geometry, and any number of additional internal geometries.
For instance, say we have an external.csv
for our outer region, then internal_1.csv
and internal_2.csv
. To tell Magnetite which geometry is which, we need to pass the external geometry first. In this scenario, that would look like:
magnetite input.json external.csv interal_1.csv internal_2.csv
The actual simulation is defined in an input json. Let's break down the following example:
{
"metadata": {
"part_thickness": 0.5,
"material_elasticity": 69e9,
"poisson_ratio": 0.33,
"characteristic_length_min": 0,
"characteristic_length_max": 0.9
},
"boundary_conditions": {
"restraint": {
"region": {
"x_target_min": -12,
"x_target_max": -10
},
"targets": {
"ux": 0,
"uy": 0,
"fx": null,
"fy": null
}
},
"load": {
"region": {
"x_target_min": 10,
"x_target_max": 12
},
"targets": {
"ux": null,
"uy": null,
"fx": -6e8,
"fy": 0
}
}
}
}
The metadata field defines the properties of the simulation. All the fields shown (i.e., part_thickness, material_elasticity, etc.) are required for each simulation.
These fields describe:
part_thickness
– The thickness of the partmaterial_elasticity
– The Young's Modulus of the partcharacteristic_length_min
– (Effectively) The minimum mesh element sizecharacteristic_length_max
– (Effectively) The maximum mesh element size
Here, we specify boundary conditions for the simulation. In the example above, we define two boundary conditions, restraint
and load
. We can name these whatever we like.
Each boundary condition must have two fields: region
and targets
Boundary conditions are applied by rectangular-region; if a node falls within this region, the targets
will be applied to it.
By default, the region is
x_target_min
x_target_max
y_target_min
y_target_max
We are effectively saying:
Values left undefined will default to
$\infty$ .
If a node falls within the region, the parameters defined here will be applied to that node. As with the metadata
section, all fields in the target
section must be defined.
To create a properly constrained model, there must be one unknown in each axis. For instance, in this example, the external forces fx
and fy
are known in the ux
and uy
as null
. If we over- or under-define our model, Magnetite will error.
The equations used to obtain stiffness matrices were derived in this University of New Mexico Paper.
Other references are made to this Indian Institute of Science Paper.
More information is found on this Wikipedia page
Magnetite is a Rust-adapdation of my Python-based solver Pyrite. Eventually, I hope to build a 3D solver, and Magnetite will be the starting-place for that.
These isotropic linear-elastic solvers are stepping stones, and eventually, I hope to use them for some pretty ambitious projects.
A lot more of how this solver works is documented in "under the hood." If you're interested, check it out!