Getting Started#

This page explains the core pyOMA workflow step by step. After reading it you should be able to set up your own analysis without looking at the source code.

Installation#

Download and install anaconda or [miniconda](https://www.anaconda.com/docs/getting-started/miniconda/install/overview)

If you haven’t setup your environment already: .. code-block:: bash

conda create –name pyoma conda activate pyoma conda install git pip

Activate the environment and clone the repository:

For Jupyter notebook support (interactive stabilisation and mode-shape widgets):

pip install -e ".[jupyter]"

For the desktop PyQt5 GUI:

pip install -e ".[gui]"

Or with both GUI options:

pip install -e ".[jupyter, gui]"

The five-step workflow#

Every OMA analysis follows the same five stages:

Step

Class / method

What happens

1

GeometryProcessor

Load node coordinates and structural connectivity

2

PreProcessSignals

Load the time-series data; assign sampling rate, channel types, and reference channels

3

correlation()

Decimate, filter, and compute cross-correlation functions

4

BRSSICovRef or other method

Identify modal parameters at multiple model orders

5

StabilCalc

Compute the stabilisation diagram and select physical modes

Step 1 – Structural geometry#

from pyOMA.core import GeometryProcessor

geometry_data = GeometryProcessor.load_geometry(
    nodes_file='grid.txt',
    lines_file='lines.txt',               # optional
    parent_childs_file='parent_childs.txt',  # optional
)

GeometryProcessor stores node coordinates and connectivity. It is only required for mode-shape visualisation; you can skip it for a numbers-only analysis.

File formats are described on the Input File Formats page.

Step 2 – Loading measurement signals#

Providing a file loader#

pyOMA is format-agnostic. Before calling init_from_config (or constructing PreProcessSignals directly), you must assign a callable to the class attribute load_measurement_file. It receives the file path and must return an (n_samples, n_channels) NumPy array:

import numpy as np
from pyOMA.core import PreProcessSignals

# NumPy .npy
PreProcessSignals.load_measurement_file = np.load

# Whitespace-separated ASCII
PreProcessSignals.load_measurement_file = lambda f, **kw: np.loadtxt(f)

# Custom binary
PreProcessSignals.load_measurement_file = my_loader

Step 3 – Pre-processing#

Decimation#

decimate_signals() reduces the sampling rate by an integer factor. An anti-aliasing filter is applied automatically before down-sampling. Call it multiple times to achieve large total reduction factors while keeping each step moderate:

prep_signals.decimate_signals(3)   # 256 Hz → 85.3 Hz
prep_signals.decimate_signals(3)   # 85.3 Hz → 28.4 Hz

Filtering#

An optional explicit bandpass filter can be applied before computing correlations if you want to restrict the analysis to a specific frequency band:

prep_signals.filter_signals(lowpass=10.0, highpass=0.1)  # Hz

Correlation functions#

Covariance-driven SSI and PLSCF need the cross-correlation matrix. The Blackman–Tukey method generally gives better frequency resolution:

prep_signals.corr_blackman_tukey(m_lags=200)  # or corr_welch(m_lags=200)

m_lags must satisfy m_lags > num_block_columns + num_block_rows (see Step 4 below).

Step 4 – System identification#

All identification methods share the same interface. Pick the class, set the parameters, call the two core methods:

Class

What it needs

Best for

BRSSICovRef

Correlations (Step 3)

General ambient vibration, most widely used

SSIData

Raw signals

Shorter records; avoids explicit correlation step

VarSSIRef

Correlations

When uncertainty (variance) of modal parameters is required

PLSCF

Correlations

Frequency-domain alternative to SSI

ERA

Impulse response data

Impact-hammer and FRF tests

Example using SSI-cov (recommended — all parameters visible in the notebook):

from pyOMA.core import BRSSICovRef

modal_data = BRSSICovRef(prep_signals)
modal_data.build_toeplitz_cov(num_block_columns=100)  # must be < m_lags
modal_data.compute_modal_params(max_model_order=40)

Example using SSI-data directly:

from pyOMA.core import SSIData

ssi = SSIData(prep_signals)
ssi.build_block_hankel(num_block_rows=100)
ssi.compute_modal_params(max_model_order=40)

Example using PLSCF directly:

from pyOMA.core import PLSCF

plscf = PLSCF(prep_signals)
plscf.build_half_spectra(
    nperseg=200,            # correlation lag length (same as m_lags)
    begin_frequency=0.0,   # Hz
    end_frequency=12.0,    # Hz
)
plscf.compute_modal_params(max_model_order=40)

Key parameters#

num_block_columns / num_block_rows

Number of block rows/columns in the block-Toeplitz or Hankel matrix. Higher values capture longer time correlations but increase computation time. Typical range: 50–200. Must satisfy num_block_columns + num_block_rows < m_lags.

max_model_order

The algorithm estimates modal parameters at every order from 1 up to this value. Stable physical modes appear across many orders. Typical range: 20–100. Setting it to 2 × expected_number_of_modes is a safe starting point.

Step 5 – Stabilisation diagram#

from pyOMA.core import StabilCluster, StabilPlot

stabil_calc = StabilCluster(modal_data)

# Compute hard-criteria masks
stabil_calc.calculate_stabilization_masks(
    d_range=(0, 0.10),   # damping 0–10 %
    df_max=0.01,          # max relative frequency change between orders
    dd_max=0.05,          # max relative damping change between orders
    dmac_max=0.05,        # max MAC change between orders
)

# Static plot
stabil_plot = StabilPlot(stabil_calc)
stabil_plot.plot()

# Automated mode selection
stabil_calc.automatic_clearing()
stabil_calc.automatic_classification()

# Export results to a tab-separated text file
stabil_calc.export_results('modes.txt')

Pole stability criteria#

d_range

Absolute damping ratio limits (min, max).

df_max, dd_max

Maximum relative change in frequency / damping between consecutive model orders. A pole that shifts by more than df_max × 100 % between order n and n+1 is considered new/unstable.

dmac_max

Maximum MAC difference between mode shapes at consecutive orders. Ensures that the shape is consistent and not just a numerical artefact.

Choosing thresholds#

There is no universal set of thresholds. As a starting point:

  • For lightly damped civil structures: df_max=0.01, dd_max=0.05, dmac_max=0.05, d_range=(0, 0.05).

  • For more heavily damped mechanical systems: loosen d_range and dd_max accordingly.

  • Always inspect the raw stabilisation diagram visually before accepting automatically selected modes.

Example scripts and notebooks#

Three ready-to-run examples are included in the scripts/ directory. Each is available as a plain Python script (requires pip install "pyOMA[gui]") and as an interactive Jupyter notebook (requires pip install "pyOMA[jupyter]"):

Scenario

Script

Notebook

Single setup

scripts/single_setup_analysis.py

pyOMA – Single-Setup Operational Modal Analysis

Multi-setup — PoSER (post-identification merging)

scripts/multi_setup_analysis.py

pyOMA – Multi-Setup OMA with PoSER merging

Multi-setup — PoGER (pre-identification merging)

scripts/multi_setup_analysis_poger.py

pyOMA – Multi-Setup OMA with PoGER merging

The PoSER workflow runs SSI independently on each measurement setup and then merges the estimated modal parameters using MergePoSER. The PoGER workflow stacks correlation functions from all setups into a joint Hankel matrix before a single SSI run, yielding global frequencies, damping ratios, and re-scaled mode shapes directly via PogerSSICovRef.