Configuration Guide#
This page explains how to adapt pyOMA-Monitoring to a new monitored structure. There are two independent pieces to configure:
``config.yaml`` — static paths and channel lists for the site.
A new ``site_*.py`` — the Python callbacks that the engine calls for every file and every window.
Both pieces must be completed before running daily.py.
Part 1 — Editing config.yaml#
config.yaml holds everything that does not require Python logic: file
system roots, quantity-to-origin mappings, channel definitions, plausibility
ranges, and the earliest available data date per origin. config.py
loads and validates this file at import time and re-exports every value as a
module-level attribute.
A minimal annotated config.yaml:
# Absolute paths — change these for your deployment
paths:
file_root: '/data/raw/' # root for raw measurement files
slice_root: '/data/slices/' # root for cached preprocessed slices
db_root: '/data/results/' # root for NetCDF result databases
modal_conf_dir: '/data/modal_conf/' # root for OMA configuration files
# Quantity → origin mapping.
# The "origin tag" is used as a key in subpaths, dtstarts, and channels.
origins:
accel: accel
wind: wind
temp: temp
# Origin tag → relative subdirectory under paths.file_root
subpaths:
accel: 'accel/'
wind: 'wind/'
temp: 'temp/'
# Earliest available data per origin (ISO date string)
dtstarts:
accel: '2020-01-01'
wind: '2020-01-01'
temp: '2020-01-01'
# Channel definitions
channels:
# Required channels per quantity — slices missing any of these are discarded
all:
accel: ['Accel_01', 'Accel_02', 'Accel_03', 'Accel_04']
wind: ['Wg', 'Wr']
temp: ['Pt100_01']
# Optional channels — may be present but are not required
optional:
accel: []
wind: []
temp: []
# Lists used by strain callbacks (leave empty if not applicable)
strain_list: []
temp_list: []
# Physical plausibility ranges [min, max].
# Slices with any channel outside its range are flagged as erroneous.
ranges:
Accel_01: [-20.0, 20.0]
Accel_02: [-20.0, 20.0]
Wg: [0.0, 60.0]
# FBG-specific fields — leave as empty dicts if not using FBG sensors
strain_temperature_map: {}
initial_wavelengths: {}
config.py validates the structure on load. Missing required keys or
wrong types raise a descriptive ValueError before any processing starts.
Part 2 — Writing a site_*.py#
site_example.py is a fully documented template. Copy it, rename it,
and fill in the stubs.
cp site_example.py site_mysite.py
The minimal set of things to implement before running the pipeline:
Configuration constants — database paths, origins, channel lists (can mirror what is in
config.yamlor be hardcoded in the site module).``file_list_fn`` — discovers raw files on disk for a given origin.
``sync_policy`` — corrects raw timestamps (if the embedded clock is unreliable, return
file_time - duration; otherwise returnstart_timeunchanged).``register_<mysite>_site()`` — builds the
Sitedataclass and callsregister_site/set_active_site.
Transforms (transforms), OMA channel mapping (setup_prep),
kurtosis thresholds (error_rules), and custom channel means
(channel_mean_fn) are all optional and can be added later as needed.
Minimal worked template#
The following is a self-contained, runnable site module for a hypothetical
building with two accelerometers and one temperature sensor. It is
distilled from site_example.py:
"""site_building.py — minimal site module for a two-accelerometer building."""
import glob
import os
import logging
import numpy as np
logger = logging.getLogger(__name__)
# -- Configuration ---------------------------------------------------------
DB_ROOT = '/data/building/results/'
SLICE_ROOT = '/data/building/slices/'
CONF_DIR = '/data/building/modal_conf/'
FILE_ROOT = '/data/building/raw/'
ORIGINS = {'accel': 'accel', 'temp': 'temp'}
SUBPATHS = {'accel': 'accel/', 'temp': 'temp/'}
ALL_CHANNELS = {'accel': ['Accel_01', 'Accel_02'], 'temp': ['Pt100_01']}
OPTIONAL_CHANNELS = {'accel': [], 'temp': []}
DTSTARTS = {'accel': '2024-01-01', 'temp': '2024-01-01'}
RANGES = {'Accel_01': (-20.0, 20.0), 'Accel_02': (-20.0, 20.0)}
ERROR_RULES = {'accel': {'kurtosis_max': 5, 'kurtosis_min': -2}}
MODAL_BANDS = [(1.0, 1.5), (2.5, 3.0)]
# -- File discovery --------------------------------------------------------
def _building_get_file_list(origin, reduced=False, file_info=None):
path = os.path.join(FILE_ROOT, SUBPATHS[origin])
file_list = glob.glob(os.path.join(path, '*.dat'))
if reduced and file_info is not None and 'file_name' in file_info:
existing = set(file_info['file_name'].values.astype(str))
file_list = [f for f in file_list
if os.path.basename(f) not in existing]
return file_list
# -- Sync policy -----------------------------------------------------------
def _building_sync_policy(start_time, file_time, duration):
return start_time # embedded clock is reliable
# -- OMA channel mapping ---------------------------------------------------
def _building_setup_accel(headers):
ref_channels = [headers.index('Accel_01')]
accel_channels = list(range(len(headers)))
disp_channels = []
chan_dofs_dict = {
'Accel_01': [1, 0, 0],
'Accel_02': [2, 90, 0],
}
return ref_channels, accel_channels, disp_channels, chan_dofs_dict
# -- Registration ----------------------------------------------------------
def register_building_site():
import monitoring as _m
site = _m.Site(
name='building',
transforms={},
setup_prep={'accel': _building_setup_accel},
error_rules=ERROR_RULES,
sync_policy=_building_sync_policy,
modal_bands=MODAL_BANDS,
file_list_fn=_building_get_file_list,
channel_mean_fn=None,
preproc_channels={'accel': ['Accel_01', 'Accel_02']},
db_root_path=DB_ROOT,
slice_root_path=SLICE_ROOT,
modal_conf_dir=CONF_DIR,
file_root_path=FILE_ROOT,
origins=ORIGINS,
subpaths=SUBPATHS,
all_channels=ALL_CHANNELS,
optional_channels=OPTIONAL_CHANNELS,
dtstarts=DTSTARTS,
ranges=RANGES,
)
_m.register_site(site)
_m.set_active_site(site)
register_building_site()
Then wire it into daily.py by replacing the existing site import:
import site_building # registers and activates on import
And run:
python daily.py -d 120 -q accel --file_info --stats --modal --plot
OMA configuration files#
modal_conf_dir/<quantity>/ must contain the pyOMA geometry and SSI
configuration files. See the
pyOMA input-file format documentation for the
exact format of each file.
File |
Contents |
|---|---|
|
Node coordinates used by |
|
Line connectivity between nodes. |
|
Master/slave channel relationships (may be empty). |
|
SSI parameters: model-order range, number of block rows, etc. |
|
Channel-to-DOF mapping (auto-generated from |
Graceful failure when no site is registered#
If you import monitoring without first importing a site module,
_active_site is None. Engine functions that can proceed without a
site (statistics, file reading) do so with safe defaults. Functions that
require a site-specific hook raise a RuntimeError with a descriptive
message:
RuntimeError: No file_list_fn registered.
Import the appropriate site module first.
This makes the failure mode explicit: the error message names the missing step, rather than producing silent incorrect results.