Skip to main content
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.
1

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)
2

Create a model

Initialize a new structural model:
model = SystemModel()
This creates an empty model ready for you to add materials, sections, nodes, and elements.
3

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.
4

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.
5

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, …).
6

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.
7

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
8

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.).
9

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.
10

Solve the model

Run the structural analysis:
model.solve()
This assembles the global stiffness matrix, applies boundary conditions, and solves for displacements and internal forces.
11

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:
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:

Build docs developers (and LLMs) love