Examples

This section provides detailed examples demonstrating the use of the time-domain modal estimation package.

Example 1: Single Mode Estimation

This example demonstrates basic usage for a single-mode system.

"""
Single mode estimation example
"""
import numpy as np
from time_domain_modal_estimation import complex_exponential_algorithm

# Define parameters
dt = 0.01  # Time step (seconds)
duration = 5.0  # Duration (seconds)
t = np.arange(0, duration, dt)

# True modal parameters
f_true = 5.0  # Natural frequency (Hz)
zeta_true = 0.03  # Damping ratio
A_true = 1.0  # Amplitude

# Generate response
omega_n = 2 * np.pi * f_true
omega_d = omega_n * np.sqrt(1 - zeta_true**2)
y = A_true * np.exp(-zeta_true * omega_n * t) * np.cos(omega_d * t)

# Apply CEA
results = complex_exponential_algorithm(y=y, dt=dt, n_modes=1)

# Display results
print("True vs Estimated Parameters:")
print(f"Frequency: {f_true:.4f} Hz -> {results['frequencies'][0]:.4f} Hz")
print(f"Damping:   {zeta_true:.4f} -> {results['damping_ratios'][0]:.4f}")

# Check reconstruction quality
error = np.linalg.norm(y - results['reconstruction']) / np.linalg.norm(y)
print(f"Reconstruction error: {error:.6f}")

Example 2: Multi-Mode System

Estimating parameters for a system with multiple modes.

"""
Multi-mode estimation example
"""
import numpy as np
from time_domain_modal_estimation import complex_exponential_algorithm

# Time parameters
dt = 0.01
t = np.arange(0, 10, dt)

# Define three modes
modes = [
    {'f': 2.5, 'zeta': 0.02, 'A': 1.0},
    {'f': 5.0, 'zeta': 0.03, 'A': 0.6},
    {'f': 8.3, 'zeta': 0.05, 'A': 0.3},
]

# Generate combined response
y = np.zeros_like(t)
for mode in modes:
    omega_n = 2 * np.pi * mode['f']
    omega_d = omega_n * np.sqrt(1 - mode['zeta']**2)
    y += mode['A'] * np.exp(-mode['zeta'] * omega_n * t) * np.cos(omega_d * t)

# Estimate parameters
results = complex_exponential_algorithm(y=y, dt=dt, n_modes=3)

# Sort by frequency
sort_idx = np.argsort(results['frequencies'])

print("\\nMode Identification Results:")
print("-" * 60)
for i, idx in enumerate(sort_idx):
    true_f = modes[i]['f']
    true_zeta = modes[i]['zeta']
    est_f = results['frequencies'][idx]
    est_zeta = results['damping_ratios'][idx]

    print(f"Mode {i+1}:")
    print(f"  Frequency: {true_f:.2f} Hz -> {est_f:.2f} Hz")
    print(f"  Damping:   {true_zeta:.4f} -> {est_zeta:.4f}")

Example 3: Noisy Data

Handling measurement noise in modal estimation.

"""
Modal estimation with noisy data
"""
import numpy as np
from time_domain_modal_estimation import complex_exponential_algorithm

np.random.seed(42)  # For reproducibility

# Generate clean signal
dt = 0.01
t = np.arange(0, 8, dt)
f_true = 5.0
zeta_true = 0.03
omega_n = 2 * np.pi * f_true

y_clean = np.exp(-zeta_true * omega_n * t) * np.cos(omega_n * np.sqrt(1 - zeta_true**2) * t)

# Add noise (5% of signal amplitude)
noise_level = 0.05
y_noisy = y_clean + np.random.normal(0, noise_level, len(t))

# Estimate from noisy data
results = complex_exponential_algorithm(y=y_noisy, dt=dt, n_modes=1)

print("Results with 5% noise:")
print(f"Frequency error: {abs(results['frequencies'][0] - f_true)/f_true * 100:.2f}%")
print(f"Damping error: {abs(results['damping_ratios'][0] - zeta_true)/zeta_true * 100:.2f}%")

Example 4: Visualization

Complete example with visualization.

"""
Complete example with plotting
"""
import numpy as np
import matplotlib.pyplot as plt
from time_domain_modal_estimation import complex_exponential_algorithm

# Generate data
dt = 0.01
t = np.arange(0, 5, dt)
f = 5.0
zeta = 0.03
omega_n = 2 * np.pi * f
y = np.exp(-zeta * omega_n * t) * np.cos(omega_n * np.sqrt(1 - zeta**2) * t)

# Estimate parameters
results = complex_exponential_algorithm(y=y, dt=dt, n_modes=1)

# Create visualization
fig, axes = plt.subplots(2, 2, figsize=(12, 8))

# Time series
ax = axes[0, 0]
ax.plot(t, y, 'b-', label='Measured', linewidth=1.5)
ax.plot(t, results['reconstruction'], 'r--', label='Reconstructed', linewidth=1.5)
ax.set_xlabel('Time (s)')
ax.set_ylabel('Response')
ax.set_title('Time Domain Response')
ax.legend()
ax.grid(True, alpha=0.3)

# Error
ax = axes[0, 1]
error = y - results['reconstruction']
ax.plot(t, error, 'g-', linewidth=1)
ax.set_xlabel('Time (s)')
ax.set_ylabel('Error')
ax.set_title('Reconstruction Error')
ax.grid(True, alpha=0.3)

# Poles
ax = axes[1, 0]
poles = results['poles']
ax.plot(np.real(poles), np.imag(poles), 'ro', markersize=10, label='Poles')
theta = np.linspace(0, 2*np.pi, 100)
ax.plot(np.cos(theta), np.sin(theta), 'k--', alpha=0.3, label='Unit Circle')
ax.set_xlabel('Real Part')
ax.set_ylabel('Imaginary Part')
ax.set_title('System Poles')
ax.legend()
ax.grid(True, alpha=0.3)
ax.axis('equal')

# Info text
ax = axes[1, 1]
ax.axis('off')
info_text = f"""
Estimated Parameters:

Frequency: {results['frequencies'][0]:.4f} Hz
Damping:   {results['damping_ratios'][0]:.4f}

True Parameters:

Frequency: {f:.4f} Hz
Damping:   {zeta:.4f}

Errors:

Freq: {abs(results['frequencies'][0] - f):.6f} Hz
Damp: {abs(results['damping_ratios'][0] - zeta):.6f}
"""
ax.text(0.1, 0.5, info_text, fontsize=12, family='monospace',
        verticalalignment='center')

plt.tight_layout()
plt.show()

Example 5: Low Frequency Mode

Working with very low frequency oscillations.

"""
Low frequency mode example
"""
import numpy as np
from time_domain_modal_estimation import complex_exponential_algorithm

# Very low frequency: 0.02 Hz (50 second period)
dt = 0.5  # Coarser sampling for long duration
t = np.arange(0, 1000, dt)  # 1000 seconds (20 periods)

# Modal parameters
f_true = 0.02  # 0.02 Hz
damping_coeff = 0.0005  # exp(-0.0005*t)
omega_n = 2 * np.pi * f_true
zeta_true = damping_coeff / omega_n

# Generate response: f(x) = exp(-0.0005*t) * cos(2*pi*t/50)
y = np.exp(-damping_coeff * t) * np.cos(2 * np.pi * t / 50)

# Use subset for analysis (to avoid numerical issues with huge matrices)
n_samples = min(2000, len(y))
y_analysis = y[:n_samples]

# Estimate
results = complex_exponential_algorithm(y=y_analysis, dt=dt, n_modes=1)

print(f"Low frequency mode:")
print(f"True frequency:      {f_true:.6f} Hz")
print(f"Estimated frequency: {results['frequencies'][0]:.6f} Hz")
print(f"True damping:        {zeta_true:.6f}")
print(f"Estimated damping:   {results['damping_ratios'][0]:.6f}")

Eigensystem Realization Algorithm Examples

Example 6: Basic ERA Usage

Estimating modal parameters from impulse response data.

"""
Basic ERA example
"""
import numpy as np
from time_domain_modal_estimation import (
    eigensystem_realization_algorithm,
    generate_impulse_response
)

# Time parameters
dt = 0.01  # Time step (seconds)
t = np.arange(0, 5, dt)

# True modal parameters
frequencies = [5.0]  # Hz
damping = [0.03]     # Fraction of critical
mode_shapes = [[1.0]]  # Single output

# Generate impulse response
Y = generate_impulse_response(
    frequencies,
    damping,
    mode_shapes,
    t,
    n_outputs=1
)

# Apply ERA
results = eigensystem_realization_algorithm(
    Y=Y,
    dt=dt,
    n_modes=1,
    r=100,  # Hankel matrix rows
    s=100   # Hankel matrix columns
)

print(f"Estimated frequency: {results['frequencies'][0]:.4f} Hz")
print(f"Estimated damping: {results['damping_ratios'][0]:.4f}")

Example 7: Multi-Mode ERA

Identifying multiple modes from impulse response.

"""
Multi-mode ERA example
"""
import numpy as np
from time_domain_modal_estimation import (
    eigensystem_realization_algorithm,
    generate_impulse_response
)

# Time parameters
dt = 0.01
t = np.arange(0, 10, dt)

# Multiple modes
frequencies = [2.5, 5.0, 8.0]
damping = [0.02, 0.03, 0.05]
mode_shapes = [[1.0], [0.8], [0.5]]

# Generate impulse response
Y = generate_impulse_response(
    frequencies,
    damping,
    mode_shapes,
    t,
    n_outputs=1
)

# Add noise
Y += np.random.normal(0, 0.01, Y.shape)

# Apply ERA
results = eigensystem_realization_algorithm(
    Y=Y,
    dt=dt,
    n_modes=3,
    r=min(len(t)//3, 150),
    s=min(len(t)//3, 150)
)

# Sort by frequency
sort_idx = np.argsort(results['frequencies'])

print("\\nMulti-mode ERA Results:")
for i, idx in enumerate(sort_idx):
    print(f"Mode {i+1}: f = {results['frequencies'][idx]:.2f} Hz, "
          f"ζ = {results['damping_ratios'][idx]:.4f}")

Example 8: ERA with Stabilization Diagram

Using stabilization diagrams for model order selection.

"""
ERA with stabilization diagram
"""
import numpy as np
import matplotlib.pyplot as plt
from time_domain_modal_estimation import (
    generate_impulse_response,
    stabilization_diagram
)

# Parameters
dt = 0.01
t = np.arange(0, 5, dt)

frequencies = [3.0, 7.0]
damping = [0.025, 0.035]
mode_shapes = [[1.0], [0.7]]

# Generate data
Y = generate_impulse_response(frequencies, damping, mode_shapes, t, n_outputs=1)
Y += np.random.normal(0, 0.005, Y.shape)

# Create stabilization diagram
diagram = stabilization_diagram(
    Y=Y,
    dt=dt,
    max_order=20,
    r=100,
    s=100,
    freq_tol=0.01,
    damp_tol=0.05
)

# Plot
fig, ax = plt.subplots(figsize=(10, 6))

# Unstable poles
unstable = ~diagram['stability']
ax.plot(diagram['frequencies'][unstable],
        diagram['orders'][unstable],
        'o', color='lightgray', markersize=4, label='Unstable')

# Stable poles
stable = diagram['stability']
ax.plot(diagram['frequencies'][stable],
        diagram['orders'][stable],
        'ro', markersize=6, label='Stable')

ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Model Order')
ax.set_title('Stabilization Diagram')
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()

print("Stable poles typically align vertically at true frequencies")

Running the Examples

The package includes ready-to-run example scripts in the examples/ directory:

Basic Usage

python examples/basic_usage.py

CEA Demonstration

python examples/demo_cea.py

This generates a comprehensive demonstration with visualization saved as cea_results.png.

CEA Results Visualization

CEA demonstration results: The figure shows (top) the original response and CEA reconstruction, (middle) the identified poles in the complex plane, and (bottom) the reconstruction error over time. The demonstration uses the function f(x) = exp{-1E-3*x/2}*cos(2*pi/50*x) over 5000 seconds.

ERA Demonstration

python examples/demo_era.py

This generates a comprehensive ERA demonstration with visualization saved as era_results.png.

ERA Results Visualization

ERA demonstration results: The figure shows (top) the original impulse response and ERA reconstruction, (middle) the identified poles in the complex plane, and (bottom) the reconstruction error over time. The demonstration uses the function f(x) = exp{-1E-3*x/2}*cos(2*pi/50*x) over 5000 seconds.