This guide will walk you through creating your first structural analysis model with milcapy. You’ll build a simple beam, apply loads, and visualize the results.
Prerequisites
Before starting, make sure you have installed milcapy .
Your First Model
Let’s create a simple cantilever beam model step by step.
Import milcapy
First, import the required classes and functions from milcapy: from milcapy import SystemModel, BeamTheoriesType
SystemModel - The main class for creating structural models
BeamTheoriesType - Enum for selecting beam theory (Timoshenko or Euler-Bernoulli)
Create a model
Initialize a new structural model: This creates an empty model ready for you to add materials, sections, nodes, and elements.
Define material
Add a concrete material with elastic properties: model.add_material(
name = "concrete" ,
modulus_elasticity = 2.1e6 , # kN/m²
poisson_ratio = 0.2
)
Ensure consistent units throughout your model. Here we’re using kN and meters.
Define section
Create a rectangular beam section: model.add_rectangular_section(
name = "beam" ,
material_name = "concrete" ,
base = 0.3 , # m
height = 0.5 # m
)
This defines a 0.3m × 0.5m rectangular cross-section.
Add nodes
Define the geometry by adding nodes: model.add_node( 1 , 0 , 0 ) # Node 1 at origin
model.add_node( 2 , 5 , 0 ) # Node 2 at 5m horizontal
Node IDs must be sequential integers starting from 1: (1, 2, 3, …).
Add member
Connect the nodes with a beam element: model.add_member(
id = 1 ,
node_i_id = 1 ,
node_j_id = 2 ,
section_name = "beam" ,
beam_theory = BeamTheoriesType. TIMOSHENKO
)
This creates a Timoshenko beam between nodes 1 and 2.
Apply boundary conditions
Fix node 1 (cantilever support) and restrain vertical movement at node 2: model.add_restraint( 1 , ux = True , uy = True , rz = True ) # Fixed support
model.add_restraint( 2 , ux = False , uy = True , rz = False ) # Vertical restraint
ux - Horizontal displacement
uy - Vertical displacement
rz - Rotation about Z-axis
Create load pattern
Define a load case: model.add_load_pattern( "Live Load" )
Load patterns let you organize different loading scenarios (dead load, live load, wind, etc.).
Apply loads
Add a point load at node 2: model.add_point_load(
node_id = 2 ,
load_pattern_name = "Live Load" ,
fx = 0 , # kN
fy =- 100 , # kN (downward)
mz = 0 # kN·m
)
This applies a 100 kN downward force at the free end.
Solve the model
Run the structural analysis: This assembles the global stiffness matrix, applies boundary conditions, and solves for displacements and internal forces.
View results
Extract and display results: # Get results for the load pattern
results = model.get_results( "Live Load" )
# Get node displacements
node_2_disp = results.get_node_displacements( 2 )
print ( f "Node 2 displacement: { node_2_disp } " )
# Get member forces
member_forces = results.get_member_internal_forces( 1 )
print ( f "Member 1 forces: { member_forces } " )
# Visualize the model
model.show()
The show() method opens an interactive viewer where you can see:
Model geometry
Deformed shape
Force diagrams (axial, shear, moment)
Reactions
Complete Example
Here’s the complete code for the cantilever beam:
Complete Example
Simple Truss
from milcapy import SystemModel, BeamTheoriesType
# Create model
model = SystemModel()
# Define material
model.add_material( "concrete" , modulus_elasticity = 2.1e6 , poisson_ratio = 0.2 )
# Define section
model.add_rectangular_section( "beam" , "concrete" , base = 0.3 , height = 0.5 )
# Add nodes
model.add_node( 1 , 0 , 0 )
model.add_node( 2 , 5 , 0 )
# Add member
model.add_member( 1 , 1 , 2 , "beam" , BeamTheoriesType. TIMOSHENKO )
# Boundary conditions
model.add_restraint( 1 , ux = True , uy = True , rz = True )
model.add_restraint( 2 , ux = False , uy = True , rz = False )
# Load pattern
model.add_load_pattern( "Live Load" )
model.add_point_load( 2 , "Live Load" , fx = 0 , fy =- 100 , mz = 0 )
# Solve
model.solve()
# Results
results = model.get_results( "Live Load" )
displacements = results.get_node_displacements( 2 )
print ( f "Node 2 displacement: { displacements } " )
# Visualize
model.show()
Understanding Results
After solving, you can extract various results:
results = model.get_results( "Live Load" )
# Node results
displacements = results.get_node_displacements( 2 ) # [ux, uy, rz]
reactions = results.get_node_reactions( 1 ) # [Rx, Ry, Mz]
# Member results
axial = results.get_member_axial_force( 1 ) # Axial force diagram
shear = results.get_member_shear_force( 1 ) # Shear force diagram
moment = results.get_member_bending_moment( 1 ) # Bending moment diagram
deflection = results.get_member_deflection( 1 ) # Deflection curve
The results are returned as NumPy arrays for easy plotting and post-processing.
Next Steps
Now that you’ve created your first model, explore more advanced features:
Truss Analysis Learn about truss elements and axial members
Membrane Elements Explore 2D finite element analysis
Load Types Apply distributed loads and self-weight
Advanced Features Use end offsets, releases, and elastic supports
Tips for Success
Start Simple : Begin with simple models to understand the workflow, then add complexity gradually.
Check Units : Maintain consistent units throughout (kN-m, N-mm, etc.) to avoid errors.
Visualize Early : Use model.show() frequently to verify your geometry and loads before solving.
Sequential IDs : Always use sequential node IDs (1, 2, 3, …). The solver requires this for matrix assembly.
Getting Help
If you encounter issues: