STEM: Open-Source Railway Vibration Prediction Tool

Summary
A practical guide to STEM (Spoor Trillingen Emissie Model), an open-source Python-based tool for predicting railway-induced ground vibrations using the finite element method.

Introduction

As urbanization accelerates, buildings increasingly encroach upon railway corridors, amplifying resident complaints about ground-borne vibrations. Engineers face a critical challenge: how to accurately predict railway-induced vibrations before construction and evaluate the effectiveness of mitigation measures?

STEM (Spoor Trillingen Emissie Model, Dutch for Railway Vibration Emission Model) addresses this challenge. Developed collaboratively by ProRail, Deltares, TNO, and TU Delft, STEM is an open-source Python tool that simulates vibration transmission from train wheels through track structures into layered soil.

FeatureBenefit
Open SourceNo licensing costs; full transparency for research
Python-BasedEasy scripting; integrates with scientific Python stack
Kratos BackendProduction-ready FEM solver with parallel computing
Purpose-BuiltPre-configured workflows for railway vibration analysis

Key Capabilities

  • Track Irregularities: Model measured or statistical rail unevenness profiles
  • Vehicle Dynamics: Configure train types, axle loads, and speeds
  • Layered Soil: Handle heterogeneous stratigraphy with varying properties
  • Mitigation Assessment: Evaluate elastic pads, floating slabs, and wave barriers

Installation

STEM requires Python 3.10 or 3.11 (Python 3.12+ not yet fully supported).

1
2
3
4
5
6
7
# Create virtual environment (recommended)
python -m venv stem_env
stem_env\Scripts\activate    # Windows
source stem_env/bin/activate  # Linux/macOS

# Install STEM
pip install STEM-Vibrations

Prerequisites: Python 3.10/3.11, ParaView for visualization. See Installation Guide for troubleshooting.

ResourceLink
Documentationstemvibrations.readthedocs.io
GitHubgithub.com/StemVibrations/STEM
LicenseBSD 3-Clause

Tutorial: Moving Load on Embankment

This tutorial follows STEM Tutorial 2 —simulating a moving load on an embankment with layered soil.

Model Overview

The 3D model consists of:

  • Two soil layers (each 1 m thick, 5 m wide)
  • Trapezoidal embankment (1 m high, 3 m base, 0.75 m top)
  • Extrusion: 50 m in $z$-direction

A 10 kN/m line load moves at 30 m/s along the embankment top.

Material Properties

Layer$\rho$ (kg/m³)$E$ (MPa)$\nu$Porosity
Soil 1 (bottom)2650300.20.3
Soil 22550300.20.3
Embankment2650100.20.3

The embankment has lower stiffness ($E = 10$ MPa) than the soil layers ($E = 30$ MPa), representing typical ballast/subgrade material.

Boundary & Solver Settings

Boundary Conditions:

  • Base ($y = 0$): Fixed
  • Lateral sides: Roller (fixed in $x$ and $z$)
  • Top: Free surface

Solver:

  • Dynamic analysis with Newmark time integration
  • Duration: $t = 1.5$ s, time step: $\Delta t = 0.01$ s
  • Rayleigh damping: $\alpha_M = 0.6$, $\alpha_K = 0.0002$

Code Example

tutorial_moving_load.py (click to expand)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# STEM Tutorial 2: Moving Load on Embankment
# Source: https://stemvibrations.readthedocs.io/v1.2/tutorial2.html

from stem.model import Model
from stem.soil_material import (OnePhaseSoil, LinearElasticSoil, 
                                 SoilMaterial, SaturatedBelowPhreaticLevelLaw)
from stem.load import MovingLoad
from stem.boundary import DisplacementConstraint
from stem.solver import (AnalysisType, SolutionType, TimeIntegration,
                         DisplacementConvergenceCriteria, LinearNewtonRaphsonStrategy,
                         NewmarkScheme, Amgcl, StressInitialisationType,
                         SolverSettings, Problem)
from stem.output import NodalOutput, VtkOutputParameters
from stem.stem import Stem

# Initialize 3D model
model = Model(ndim=3)
model.extrusion_length = 50

# Helper function for material creation
def create_material(name, density, E):
    formulation = OnePhaseSoil(3, IS_DRAINED=True, DENSITY_SOLID=density, POROSITY=0.3)
    constitutive = LinearElasticSoil(YOUNG_MODULUS=E, POISSON_RATIO=0.2)
    return SoilMaterial(name, formulation, constitutive, SaturatedBelowPhreaticLevelLaw())

# Materials
soil_1 = create_material("soil_1", 2650, 30e6)
soil_2 = create_material("soil_2", 2550, 30e6)
embankment = create_material("embankment", 2650, 10e6)

# Geometry (x-y plane, extruded in z)
model.add_soil_layer_by_coordinates(
    [(0,0,0), (5,0,0), (5,1,0), (0,1,0)], soil_1, "layer_1")
model.add_soil_layer_by_coordinates(
    [(0,1,0), (5,1,0), (5,2,0), (0,2,0)], soil_2, "layer_2")
model.add_soil_layer_by_coordinates(
    [(0,2,0), (3,2,0), (1.5,3,0), (0.75,3,0), (0,3,0)], embankment, "embankment")

# Moving load: 10 kN/m at 30 m/s
model.add_load_by_coordinates(
    [(0.75, 3, 0), (0.75, 3, 50)],
    MovingLoad(load=[0,-10000,0], direction=[1,1,1], velocity=30, 
               origin=[0.75,3,0], offset=0),
    "moving_load"
)

# Boundary conditions
model.synchronise_geometry()
fixed = DisplacementConstraint(active=[True]*3, is_fixed=[True]*3, value=[0]*3)
roller = DisplacementConstraint(active=[True]*3, is_fixed=[True,False,True], value=[0]*3)
model.add_boundary_condition_by_geometry_ids(2, [1], fixed, "base")
model.add_boundary_condition_by_geometry_ids(2, [2,4,5,6,7,10,11,12,15,16,17], roller, "sides")

# Mesh and solver
model.set_mesh_size(element_size=1.0)
model.project_parameters = Problem(
    "moving_load_embankment", 1,
    SolverSettings(
        analysis_type=AnalysisType.MECHANICAL,
        solution_type=SolutionType.DYNAMIC,
        stress_initialisation_type=StressInitialisationType.NONE,
        time_integration=TimeIntegration(0, 1.5, 0.01),
        is_stiffness_matrix_constant=True,
        are_mass_and_damping_constant=True,
        convergence_criteria=DisplacementConvergenceCriteria(1e-4, 1e-9),
        strategy_type=LinearNewtonRaphsonStrategy(),
        scheme=NewmarkScheme(),
        linear_solver_settings=Amgcl(),
        rayleigh_k=0.0002, rayleigh_m=0.6
    )
)

# Output
model.add_output_settings(
    part_name="porous_computational_model_part",
    output_name="vtk_output", output_dir="output",
    output_parameters=VtkOutputParameters(
        output_interval=1, output_control_type="step",
        nodal_results=[NodalOutput.DISPLACEMENT, NodalOutput.VELOCITY, NodalOutput.ACCELERATION],
        gauss_point_results=[]
    )
)

# Run simulation
stem = Stem(model, "moving_load")
stem.write_all_input_files()
stem.run_calculation()

Results

Animations of the dynamic soil response (visualized in ParaView):

Displacement

Velocity

Acceleration

Key observations:

  • Maximum displacement occurs directly beneath the moving load
  • Wave propagation is slower in the softer embankment layer
  • Acceleration peaks at material interfaces (relevant for vibration perception)

Extensibility & Applications

Custom Interfaces

InterfacePurpose
UVECUser-defined vehicle models (suspension, multi-body dynamics)
UMATUser-defined material models (hypoplasticity, nonlinear soil)

Practical Use Cases

  • Environmental compliance: Check vibration levels against SBR-B and other standards
  • Mitigation design: Compare under-ballast mats, floating slabs, and trenches
  • Parametric studies: Sensitivity analysis on speed, track quality, soil properties
  • Maintenance planning: Identify track sections requiring priority intervention

Alternative Software

This simulation can also be performed with general-purpose FEM software:

SoftwareTypeMoving LoadNotes
COMSOLCommercialScriptedExcellent multi-physics; custom load functions needed
ABAQUSCommercial*DLOADIndustry standard; steep learning curve
PLAXISCommercialLimitedGeotechnical focus; better for static/seismic
ANSYSCommercialAPDL/ACTComprehensive but complex
OpenSeesOpen-sourceTCL scriptsEarthquake-focused

Choose STEM for rapid Python prototyping of railway-specific problems without licensing costs. Choose commercial tools when you need multi-physics coupling, complex CAD import, or certified software for regulatory compliance.

Summary

STEM brings railway vibration prediction into the open-source ecosystem. By combining finite element rigor with Python accessibility, it offers engineers a transparent, cost-effective tool for:

  • Predicting ground-borne vibrations from train operations
  • Evaluating mitigation measures before construction
  • Conducting parametric studies and sensitivity analyses

For railway design, environmental assessment, or vibration research, STEM provides a practical alternative to commercial software—with full code transparency and zero licensing costs.

References

  1. STEM Documentation
  2. STEM GitHub
  3. Kratos Multiphysics
  4. ParaView