Complex Exponential Algorithm Module

Complex Exponential Algorithm (CEA) for Time Domain Modal Estimation

Implements the algorithm from equations 7-13 for extracting modal parameters from single reference-response pairs.

References

Fahey, S. O’F., & Pratt, J. (1998). Time domain modal estimation techniques. Experimental Techniques, 22(6), 45-49.

@article{fahey1998time,

title={Time domain modal estimation techniques}, author={Fahey, S O’F and Pratt, J}, journal={Experimental techniques}, volume={22}, number={6}, pages={45–49}, year={1998}, publisher={Springer}

}

time_domain_modal_estimation.complex_exp.build_toeplitz_matrix(y, n)[source]

Build Toeplitz matrix from response data (Equation 7).

Parameters:
  • y (np.ndarray) – Response time series data of length N

  • n (int) – Number of modes to estimate (order of the system)

Return type:

Tuple[ndarray, ndarray]

Returns:

  • Y (np.ndarray) – Toeplitz matrix of shape (N-2n, 2n)

  • y_target (np.ndarray) – Target vector of length (N-2n)

Notes

From eq (7), the Toeplitz matrix structure is: Y = [y1 y2 … y2n+1 ]

[y2 y3 … y2n+2 ] [… … … … ] [yN-2n yN-2n+1 … yN-1 ]

And we solve: Y * α = -yN

time_domain_modal_estimation.complex_exp.solve_polynomial_coefficients(Y, y_target)[source]

Solve for polynomial coefficients using least squares (Equation 11).

Parameters:
  • Y (np.ndarray) – Toeplitz matrix

  • y_target (np.ndarray) – Target vector

Returns:

alpha – Polynomial coefficients [α0, α1, …, α_{2n-1}]

Return type:

np.ndarray

time_domain_modal_estimation.complex_exp.find_system_poles(alpha)[source]

Find system poles from characteristic polynomial (Equation 8).

Parameters:

alpha (np.ndarray) – Polynomial coefficients [α0, α1, …, α_{2n-1}]

Returns:

z_k – System poles (roots of characteristic polynomial)

Return type:

np.ndarray

Notes

From eq (8): Π(z - z_k) = 0 We solve: z^{2n} + α_{2n-1}*z^{2n-1} + … + α_1*z + α_0 = 0

time_domain_modal_estimation.complex_exp.poles_to_modal_frequencies(z_k, dt)[source]

Convert poles to modal frequencies (Equations 9 and 10).

Parameters:
  • z_k (np.ndarray) – System poles

  • dt (float) – Time step (sampling interval)

Returns:

lambda_k – Modal frequencies (complex, with real = damping, imag = frequency)

Return type:

np.ndarray

Notes

From eq (9): λ_k = (1/(2π*Δt)) * ln(z_k) [in Hz] From eq (10): λ_k = (1/Δt) * ln(z_k) [in rad/s]

We use eq (10) for rad/s convention.

time_domain_modal_estimation.complex_exp.build_vandermonde_matrix(lambda_k, t)[source]

Build Vandermonde-like matrix Λ (Equation 12).

Parameters:
  • lambda_k (np.ndarray) – Modal frequencies [λ_1, λ_2, …, λ_n]

  • t (np.ndarray) – Time vector

Returns:

Lambda – Matrix with exp[λ_k * t_j] entries

Return type:

np.ndarray

Notes

From eq (12): Λ_{1,N} = [exp[λ_1*t_1] exp[λ_1*t_2] … exp[λ_1*t_N]]

[exp[λ_2*t_1] exp[λ_2*t_2] … exp[λ_2*t_N]] [ … … … … ] [exp[λ_n*t_1] exp[λ_n*t_2] … exp[λ_n*t_N]]

time_domain_modal_estimation.complex_exp.solve_mode_shapes(Lambda, y)[source]

Solve for mode shapes/participation factors (Equation 13).

Parameters:
  • Lambda (np.ndarray) – Vandermonde matrix of shape (n, N)

  • y (np.ndarray) – Response data of length N

Returns:

A – Mode shape coefficients/participation factors

Return type:

np.ndarray

Notes

From eq (13): [A_1, A_2, …, A_n]^T = Λ_{1,N}^{-1} * y

The response is modeled as: y = Λ^T * A Where Lambda is (n x N), A is (n,), and y is (N,)

Solving: A = (Λ * Λ^T)^{-1} * Λ * y Or using pseudo-inverse: A = pinv(Λ^T) * y

time_domain_modal_estimation.complex_exp.complex_exponential_algorithm(y, dt, n_modes, t_start=None)[source]

Complete Complex Exponential Algorithm (CEA) implementation.

Parameters:
  • y (np.ndarray) – Response time series data

  • dt (float) – Time step (sampling interval)

  • n_modes (int) – Number of modes to estimate

  • t_start (float, optional) – Starting time (default: 0)

Returns:

results – Dictionary containing: - ‘frequencies’: Natural frequencies (Hz) - ‘damping_ratios’: Damping ratios (fraction of critical) - ‘mode_shapes’: Mode shape coefficients - ‘poles’: System poles z_k - ‘lambda’: Modal frequencies λ_k (complex)

Return type:

dict

Notes

Implements the CEA algorithm from equations 7-13: 1. Build Toeplitz matrix (eq 7) 2. Solve for polynomial coefficients (eq 11) 3. Find poles (eq 8) 4. Convert to modal frequencies (eq 9-10) 5. Build Λ matrix (eq 12) 6. Solve for mode shapes (eq 13)

References

Fahey, S. O’F., & Pratt, J. (1998). Time domain modal estimation techniques. Experimental Techniques, 22(6), 45-49.

time_domain_modal_estimation.complex_exp.reconstruct_response(lambda_k, A, t)[source]

Reconstruct the response from modal parameters.

Parameters:
  • lambda_k (np.ndarray) – Modal frequencies

  • A (np.ndarray) – Mode shape coefficients

  • t (np.ndarray) – Time vector

Returns:

y_reconstructed – Reconstructed response

Return type:

np.ndarray

Notes

Response is: y = Λ^T @ A = Σ A_k * exp(λ_k * t)

Main Function

time_domain_modal_estimation.complex_exp.complex_exponential_algorithm(y, dt, n_modes, t_start=None)[source]

Complete Complex Exponential Algorithm (CEA) implementation.

Parameters:
  • y (np.ndarray) – Response time series data

  • dt (float) – Time step (sampling interval)

  • n_modes (int) – Number of modes to estimate

  • t_start (float, optional) – Starting time (default: 0)

Returns:

results – Dictionary containing: - ‘frequencies’: Natural frequencies (Hz) - ‘damping_ratios’: Damping ratios (fraction of critical) - ‘mode_shapes’: Mode shape coefficients - ‘poles’: System poles z_k - ‘lambda’: Modal frequencies λ_k (complex)

Return type:

dict

Notes

Implements the CEA algorithm from equations 7-13: 1. Build Toeplitz matrix (eq 7) 2. Solve for polynomial coefficients (eq 11) 3. Find poles (eq 8) 4. Convert to modal frequencies (eq 9-10) 5. Build Λ matrix (eq 12) 6. Solve for mode shapes (eq 13)

References

Fahey, S. O’F., & Pratt, J. (1998). Time domain modal estimation techniques. Experimental Techniques, 22(6), 45-49.

Helper Functions

Building Matrices

time_domain_modal_estimation.complex_exp.build_toeplitz_matrix(y, n)[source]

Build Toeplitz matrix from response data (Equation 7).

Parameters:
  • y (np.ndarray) – Response time series data of length N

  • n (int) – Number of modes to estimate (order of the system)

Return type:

Tuple[ndarray, ndarray]

Returns:

  • Y (np.ndarray) – Toeplitz matrix of shape (N-2n, 2n)

  • y_target (np.ndarray) – Target vector of length (N-2n)

Notes

From eq (7), the Toeplitz matrix structure is: Y = [y1 y2 … y2n+1 ]

[y2 y3 … y2n+2 ] [… … … … ] [yN-2n yN-2n+1 … yN-1 ]

And we solve: Y * α = -yN

time_domain_modal_estimation.complex_exp.build_vandermonde_matrix(lambda_k, t)[source]

Build Vandermonde-like matrix Λ (Equation 12).

Parameters:
  • lambda_k (np.ndarray) – Modal frequencies [λ_1, λ_2, …, λ_n]

  • t (np.ndarray) – Time vector

Returns:

Lambda – Matrix with exp[λ_k * t_j] entries

Return type:

np.ndarray

Notes

From eq (12): Λ_{1,N} = [exp[λ_1*t_1] exp[λ_1*t_2] … exp[λ_1*t_N]]

[exp[λ_2*t_1] exp[λ_2*t_2] … exp[λ_2*t_N]] [ … … … … ] [exp[λ_n*t_1] exp[λ_n*t_2] … exp[λ_n*t_N]]

Solving for Parameters

time_domain_modal_estimation.complex_exp.solve_polynomial_coefficients(Y, y_target)[source]

Solve for polynomial coefficients using least squares (Equation 11).

Parameters:
  • Y (np.ndarray) – Toeplitz matrix

  • y_target (np.ndarray) – Target vector

Returns:

alpha – Polynomial coefficients [α0, α1, …, α_{2n-1}]

Return type:

np.ndarray

time_domain_modal_estimation.complex_exp.find_system_poles(alpha)[source]

Find system poles from characteristic polynomial (Equation 8).

Parameters:

alpha (np.ndarray) – Polynomial coefficients [α0, α1, …, α_{2n-1}]

Returns:

z_k – System poles (roots of characteristic polynomial)

Return type:

np.ndarray

Notes

From eq (8): Π(z - z_k) = 0 We solve: z^{2n} + α_{2n-1}*z^{2n-1} + … + α_1*z + α_0 = 0

time_domain_modal_estimation.complex_exp.poles_to_modal_frequencies(z_k, dt)[source]

Convert poles to modal frequencies (Equations 9 and 10).

Parameters:
  • z_k (np.ndarray) – System poles

  • dt (float) – Time step (sampling interval)

Returns:

lambda_k – Modal frequencies (complex, with real = damping, imag = frequency)

Return type:

np.ndarray

Notes

From eq (9): λ_k = (1/(2π*Δt)) * ln(z_k) [in Hz] From eq (10): λ_k = (1/Δt) * ln(z_k) [in rad/s]

We use eq (10) for rad/s convention.

time_domain_modal_estimation.complex_exp.solve_mode_shapes(Lambda, y)[source]

Solve for mode shapes/participation factors (Equation 13).

Parameters:
  • Lambda (np.ndarray) – Vandermonde matrix of shape (n, N)

  • y (np.ndarray) – Response data of length N

Returns:

A – Mode shape coefficients/participation factors

Return type:

np.ndarray

Notes

From eq (13): [A_1, A_2, …, A_n]^T = Λ_{1,N}^{-1} * y

The response is modeled as: y = Λ^T * A Where Lambda is (n x N), A is (n,), and y is (N,)

Solving: A = (Λ * Λ^T)^{-1} * Λ * y Or using pseudo-inverse: A = pinv(Λ^T) * y

Reconstruction

time_domain_modal_estimation.complex_exp.reconstruct_response(lambda_k, A, t)[source]

Reconstruct the response from modal parameters.

Parameters:
  • lambda_k (np.ndarray) – Modal frequencies

  • A (np.ndarray) – Mode shape coefficients

  • t (np.ndarray) – Time vector

Returns:

y_reconstructed – Reconstructed response

Return type:

np.ndarray

Notes

Response is: y = Λ^T @ A = Σ A_k * exp(λ_k * t)