-
Notifications
You must be signed in to change notification settings - Fork 0
ch3_api_reference.md
This chapter documents every public class, method, and function in the toolkit. Private and internal methods are omitted; consult the source code for implementation details.
All indices (node IDs, element IDs, DOF numbers) are 0-based.
Stores topology, material properties, boundary conditions, and DOF mappings. Properties that depend on thickness or node positions are kept in sync through dedicated setter methods.
from feaopt import StructureGeometry
geom = StructureGeometry(nodes, connectivity, props, constraints)| Argument | Type / Shape | Description |
|---|---|---|
nodes |
(N, 2) array |
Node coordinates [x, y] in metres. |
connectivity |
(M, 2) int array |
Element connectivity [start, end], 0-based. |
props |
dict | Material/section properties (see below). |
constraints |
(C, 3) array or list of Constraint
|
Boundary conditions [node_id, dof, value]. |
The props dict accepts the following keys. Scalar values are automatically expanded to (M,) arrays.
| Key | Units | Required | Description |
|---|---|---|---|
'E' |
Pa | Yes | Elastic modulus. |
'thickness' |
m | Yes | Element thickness. |
'width' |
m | Yes | Out-of-plane width. |
'ultimate' |
Pa | No | Ultimate tensile strength (default 0). |
'rho' |
kg/m³ | No | Density (default 7850). |
'is_rigid' |
bool | No | Rigid element flag (default False). |
geom.update_thickness(new_t) # (M,) thickness array (m)
geom.update_node_positions(new_xy) # (N, 2) node coordinates (m)Both methods automatically recompute all dependent quantities (A, I, L, θ₀).
n1, n2 = geom.get_member_nodes(i) # node IDs for element i
dofs = geom.get_member_dof(i) # (6,) global DOF indices
xy = geom.get_node_coords(i) # (2,) coordinates
dofs = geom.get_node_dof(i) # (3,) global DOF indices
tf = geom.is_dof_constrained(d) # True if DOF is fixed
mass = geom.get_total_mass() # total structure mass (kg)| Property | Shape | Description |
|---|---|---|
nodes |
(N, 2) |
Node coordinates. |
connectivity |
(M, 2) |
Element connectivity. |
num_nodes |
int | Number of nodes. |
num_members |
int | Number of elements. |
num_dof_total |
int | Total DOFs (3 × N). |
num_dof_free |
int | Number of unconstrained DOFs. |
E, thickness, width, A, I
|
(M,) |
Material and section properties. |
L, theta0
|
(M,) |
Element lengths and initial orientations. |
is_rigid |
(M,) bool |
Rigid element flags. |
dof_map |
DOFMap |
Contains free_dof and fixed_dof index arrays. |
assembly_map |
AssemblyMap |
Contains member_dof array of shape (M, 6). |
Manages pre-computed stiffness and transformation matrices for every element. All matrices are stored as (M, 6, 6) NumPy arrays and rebuilt vectorised.
from feaopt.element_matrices import ElementMatrices
em = ElementMatrices(geometry)Computes all local, transformation, and global matrices from the initial geometry.
# Retrieval
k = em.get_local_matrix(i) # (6, 6) local stiffness
K = em.get_global_matrix(i) # (6, 6) global stiffness
T = em.get_transformation(i) # (6, 6) transformation matrix
th = em.get_current_angle(i) # current orientation (rad)
L = em.get_current_length(i) # current length (m)
# Geometry updates (Newton-Raphson)
em.update_member_geometry(i, new_L, new_theta)
em.update_all_member_geometry(lengths, thetas) # vectorised batch update
# Assembly
K_sparse = em.assemble_global_stiffness_matrix(geometry) # returns scipy.sparse.csr_matrix
# Reset
em.reset_to_initial_configuration()
# Rigid stiffness
em.set_rigid_stiffness_factor(1e4)
# Vectorised force computation
f_int = em.compute_internal_forces_vectorized(u, geometry)
f_local, f_global = em.compute_member_forces_vectorized(u, geometry)Mutable container for displacement, force, and residual vectors during analysis.
from feaopt.analysis_state import AnalysisState
state = AnalysisState(geometry) # zero-initialised| Property | Shape | Description |
|---|---|---|
u |
(D,) |
Full displacement vector. |
f_external |
(D,) |
External force vector. |
f_internal |
(D,) |
Internal (restoring) force vector. |
u_free |
(F,) |
Free-DOF displacements. |
residual |
(F,) |
f_ext − f_int on free DOFs. |
residual_norm |
float | ‖residual‖. |
member_forces_local |
(M, 6) |
Element end forces (local). |
member_forces_global |
(M, 6) |
Element end forces (global). |
member_displ_local |
(M, 6) |
Element displacements (local). |
member_displ_global |
(M, 6) |
Element displacements (global). |
state.reset() # zero everything
state.copy_from(other_state) # deep copy
state.update_displacements(u_free) # set free DOFs, map to full
state.compute_internal_forces(elem_matrices) # assemble f_int from K*u
state.compute_residual() # R = f_ext - f_int
state.apply_increment(delta_u) # u += delta_u
state.extract_member_displacements(elem_matrices) # populate member_displ_*
state.compute_member_forces(elem_matrices) # populate member_forces_*
d = state.get_max_displacement() # max translational displacement
state.display_summary() # print summary to consoleMain analysis controller.
from feaopt import FEAAnalysis
analysis = FEAAnalysis(geometry)
analysis = FEAAnalysis(geometry, verbose=True, max_iterations=1000)| Property | Type | Default | Description |
|---|---|---|---|
verbose |
bool | True |
Print iteration info. |
max_iterations |
int | 1000 | Max N-R iterations per step. |
force_tolerance |
float | 10⁻² | Normalised force residual. |
displacement_tolerance |
float | 10⁻⁶ | Normalised displacement increment. |
energy_tolerance |
float | 10⁻⁶ | |Rᵀ Δu|. |
results = analysis.run_linear_analysis(load_case)
results = analysis.solve_large_deflection(load_case, num_steps)
stress = analysis.perform_stress_analysis()The results dict returned by both analysis methods contains:
| Key | Description |
|---|---|
'displacements' |
(D,) full displacement vector. |
'displacements_free' |
(F,) free-DOF displacements. |
'max_displacement' |
Maximum translational displacement magnitude. |
'external_forces' |
(D,) applied force vector. |
'nodes_deformed' |
(N, 2) deformed node coordinates. |
The nonlinear results dict additionally contains:
| Key | Description |
|---|---|
'member_rotations' |
(M,) element rotation increments. |
'load_history' |
List of load factors. |
'displacement_history' |
(num_steps,) max displacement at each step. |
'state_history' |
List of converged AnalysisState snapshots. |
The stress dict from perform_stress_analysis contains:
| Key | Description |
|---|---|
'stresses' → 'axial'
|
(M, 2) axial stress at each node (Pa). |
'stresses' → 'bending'
|
(M, 2) bending stress (Pa). |
'stresses' → 'shear'
|
(M, 2) shear stress (Pa). |
'stresses' → 'von_mises'
|
(M, 2) von Mises stress (Pa). |
'stresses' → 'max_stress'
|
(M,) max von Mises per element (Pa). |
'stresses' → 'critical_member'
|
Element index with highest stress. |
'FOS' → 'member_FOS'
|
(M,) factor of safety per element. |
'FOS' → 'min_FOS'
|
Global minimum FOS. |
'FOS' → 'critical_member'
|
Element index with lowest FOS. |
analysis.reset_analysis()
coords = analysis.get_deformed_coordinates(scale=1.0) # (N, 2) arrayManages nodal loads and load-factor scaling.
from feaopt import LoadCase
lc = LoadCase(geometry)
lc.add_nodal_load(node_id, fx, fy, mz, 'description')
lc.set_load_factor(1.0)
lc.clear_loads()
P = lc.get_scaled_load() # (D,) current load vector
Pf = lc.get_free_dof_loads() # (F,) free-DOF loads
lc.apply_to_state(state) # write loads into an AnalysisState
factors = lc.generate_load_steps(10, 'linear')
lc.display_loads()| Property | Type | Description |
|---|---|---|
P_ref |
(D,) array |
Reference (unscaled) load vector. |
P_current |
(D,) array |
Scaled load vector (P_ref × λ). |
lambda_ |
float | Current load factor. |
load_records |
list of LoadRecord
|
All defined loads. |
Visualisation utilities. Elements are drawn as filled rectangles matching their physical thickness using matplotlib's PatchCollection and PolyCollection for efficient batch rendering.
from feaopt import StructurePlotter
plotter = StructurePlotter(geometry)
plotter = StructurePlotter(geometry, analysis)All drawing methods require a matplotlib Axes object as the first argument.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Structure drawing
plotter.draw_structure(ax) # undeformed, default colour
plotter.draw_structure(ax, nodes=deformed, color=(0.8, 0.2, 0.2))
sm = plotter.draw_stress(ax, stress_values=stress_mpa) # returns ScalarMappable
plt.colorbar(sm, ax=ax)
# Supports and loads
plotter.plot_supports(ax)
plotter.plot_loads(ax, load_case, scale=None)The structure_plotter module also exposes two standalone batch drawing functions:
from feaopt.structure_plotter import draw_beams_batch, draw_beams_stress
draw_beams_batch(ax, nodes, connectivity, thickness, color)
sm = draw_beams_stress(ax, nodes, connectivity, thickness, stress_values)Standalone function implementing stress-ratio thickness optimisation.
from feaopt import optimize_thickness
geometry, results = optimize_thickness(geometry, analysis, load_case, options)In-place mutation: The
geometryobject is modified in place. The returnedgeometryis the same object. If you need to preserve the original, create a deep copy before calling this function.
| Key | Type | Default | Description |
|---|---|---|---|
'alpha' |
float | 0.4 | Stress-ratio exponent. |
'max_iterations' |
int | 100 | Iteration cap. |
'convergence_tol' |
float | 0.01 | Max relative Δt for convergence. |
'move_limit' |
float | 0.2 | Per-iteration change fraction. |
't_min' |
float | 0.001 | Minimum thickness (m). |
't_max' |
float | 0.020 | Maximum thickness (m). |
'safety_factor' |
float | 1.5 | Divisor on σ_ult. |
'use_nonlinear' |
bool | False |
Use large-deflection analysis. |
'num_load_steps' |
int | 2 | Load steps for nonlinear. |
'verbose' |
bool | True |
Print progress table. |
| Key | Description |
|---|---|
'converged' |
True if converged within iteration limit. |
'iterations' |
Number of iterations performed. |
'thickness_initial' |
(M,) starting thicknesses. |
'thickness_final' |
(M,) final thicknesses. |
'thickness_history' |
(M, iters) thickness at each iteration. |
'mass_initial' |
Starting mass (kg). |
'mass_final' |
Final mass (kg). |
'mass_reduction' |
Percentage mass reduction. |
'mass_history' |
(iters,) mass at each iteration. |
'final_stress_results' |
Stress dict from final analysis. |
'stress_uniformity' |
Coefficient of variation of element stresses. |
from feaopt import subdivide_truss
new_nodes, new_conn, member_map = subdivide_truss(nodes, conn, num_sub)Replaces every member with a chain of num_sub smaller elements. Original joint nodes are preserved; intermediate nodes are inserted at uniform spacing along each member.
| Argument | Type / Shape | Description |
|---|---|---|
nodes |
(N, 2) array |
Original node coordinates. |
conn |
(M, 2) int array |
Original connectivity, 0-based. |
num_sub |
int ≥ 1 | Sub-elements per member. |
| Return | Type / Shape | Description |
|---|---|---|
new_nodes |
(N2, 2) array |
Expanded node array. |
new_conn |
(M2, 2) int array |
Expanded connectivity. |
member_map |
(M2,) int array |
Original member index for each sub-element. |
from feaopt import import_csv_curve, import_igs_curve
result = import_csv_curve(filename, num_elements=250, scale=1.0,
flip=False, plot=True, verbose=True)
result = import_igs_curve(filename, num_elements=250, scale=1.0,
samples_per_entity=300, flip=False,
plot=True, verbose=True)Both return a dict with keys: 'nodes', 'connectivity', 'num_nodes', 'num_elements', 'arc_length', 'raw_points', 'start_tangent', 'end_tangent', 'start_angle', 'end_angle'.
The IGES importer additionally returns 'raw_entities'.
| Type | Name | Notes |
|---|---|---|
| 110 | Line | Straight segment. |
| 100 | Circular Arc | CCW arc in the XY plane. |
| 104 | Conic Arc | Simplified as a straight line between endpoints. |
| 106 | Copious Data | Point sequences (flags 1, 2, 3). |
| 112 | Parametric Spline | Cubic polynomial segments. |
| 126 | B-Spline | Rational B-spline (NURBS) — most common for SolidWorks exports. |
Entity 124 (Transformation Matrix) is not applied. Control points are read as 2-D (X, Y) in the entity's local coordinate system, matching the original MATLAB behaviour.
from feaopt import export_profile
profile = export_profile(geometry, options)Rasterises the structure into a binary image, extracts boundary contours, fits smoothing splines, and exports each loop as a SolidWorks-compatible text file. See Chapter 5: Profile Export Pipeline for a detailed description of the algorithm.
| Key | Type | Default | Description |
|---|---|---|---|
'num_spline_points' |
int | 1000 | Points per loop. |
'smoothing' |
float | 0.9999999 | Spline smoothing factor. |
'scale_to_mm' |
float | 1000 | Coordinate multiplier for export. |
'resolution' |
float | 50000 | Pixels per metre. |
'min_thickness_frac' |
float | 0 | Filter thin elements (0 = no filter). |
'export_filename' |
str | None |
Base name for output files. |
'plot' |
bool | True |
Show diagnostic figure. |
'verbose' |
bool | True |
Print summary. |
Performance note: For small geometries, increase
resolutionto get a usable image size. For large geometries or thick elements, the morphological closing disk radius is capped to prevent excessive computation times.
The returned profile dict contains:
| Key | Description |
|---|---|
'loops' |
List of loop dicts, each with 'x', 'y', 'xs', 'ys', 'is_hole', 'arc_length'. |
'num_loops' |
Number of boundary loops found. |
'image' |
(H, W) bool array — the rasterised binary image. |
'export_files' |
List of file paths written. |