| name | numpy-numerical-analysis |
| version | 1.0.0 |
| description | NumPy for matrix operations, FFT, linear algebra, and numerical computations in marine engineering |
| author | workspace-hub |
| category | programming |
| tags | ["numpy","numerical-analysis","matrix-operations","fft","linear-algebra","engineering"] |
| platforms | ["python"] |
NumPy Numerical Analysis Skill
Master NumPy for efficient numerical computations, matrix operations, FFT analysis, and linear algebra in marine and offshore engineering applications.
When to Use This Skill
Use NumPy numerical analysis when you need:
- Matrix operations - 6DOF equations of motion, mass matrices, stiffness matrices
- FFT analysis - Frequency domain analysis, spectral density, response spectra
- Linear algebra - Solve linear systems, eigenvalue analysis, matrix decomposition
- Array operations - Efficient computations on large datasets
- Numerical integration - Time-stepping, ODE solvers
- Signal processing - Filtering, windowing, convolution
Avoid when:
- Symbolic mathematics needed (use SymPy)
- Sparse matrices dominate (use SciPy sparse)
- GPU acceleration required (use CuPy or JAX)
- Distributed computing needed (use Dask)
Core Capabilities
1. Array Creation and Operations
Array Creation:
import numpy as np
zeros = np.zeros((3, 3))
ones = np.ones((3, 3))
identity = np.eye(3)
arange = np.arange(0, 10, 0.1)
linspace = np.linspace(0, 10, 100)
arr = np.array([1, 2, 3, 4, 5])
matrix = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
random_uniform = np.random.rand(3, 3)
random_normal = np.random.randn(3, 3)
random_int = np.random.randint(0, 100, size=(3, 3))
Array Operations:
a = np.array([1, 2, 3, 4, 5])
b = np.array([10, 20, 30, 40, 50])
c = a + b
d = a * b
e = a ** 2
sin_a = np.sin(a)
cos_a = np.cos(a)
exp_a = np.exp(a)
log_a = np.log(a)
sqrt_a = np.sqrt(a)
mean = np.mean(a)
std = np.std(a)
var = np.var(a)
min_val = np.min(a)
max_val = np.max(a)
2. Matrix Operations
Matrix Multiplication:
def compute_force_response(
mass_matrix: np.ndarray,
stiffness_matrix: np.ndarray,
force_vector: np.ndarray
) -> np.ndarray:
"""
Compute structural response: F = K * x
Solve for displacement: x = K^-1 * F
Args:
mass_matrix: Mass matrix [M]
stiffness_matrix: Stiffness matrix [K]
force_vector: Applied force vector {F}
Returns:
Displacement vector {x}
"""
displacement = np.linalg.solve(stiffness_matrix, force_vector)
return displacement
K = np.array([
[200, -100, 0],
[-100, 200, -100],
[0, -100, 100]
])
F = np.array([1000, 0, 0])
x = compute_force_response(None, K, F)
print(f"Displacements: {x} m")
Matrix Properties:
def analyze_matrix_properties(matrix: np.ndarray) -> dict:
"""
Analyze matrix properties for structural analysis.
Args:
matrix: Input matrix (mass or stiffness)
Returns:
Dictionary with matrix properties
"""
properties = {}
properties['determinant'] = np.linalg.det(matrix)
properties['condition_number'] = np.linalg.cond(matrix)
properties['rank'] = np.linalg.matrix_rank(matrix)
eigenvalues, eigenvectors = np.linalg.eig(matrix)
properties['eigenvalues'] = eigenvalues
properties['eigenvectors'] = eigenvectors
properties['is_symmetric'] = np.allclose(matrix, matrix.T)
properties['is_positive_definite'] = np.all(eigenvalues > 0)
return properties
K = np.array([
[200, -100, 0],
[-100, 200, -100],
[0, -100, 100]
])
props = analyze_matrix_properties(K)
print(f"Determinant: {props['determinant']:.2f}")
print(f"Condition number: {props['condition_number']:.2f}")
print()
()
3. 6DOF Equations of Motion
6DOF Dynamics:
def solve_6dof_equation_of_motion(
mass_matrix: np.ndarray,
damping_matrix: np.ndarray,
stiffness_matrix: np.ndarray,
force_vector: np.ndarray,
displacement: np.ndarray,
velocity: np.ndarray,
dt: float
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""
Solve 6DOF equation of motion using Newmark-Beta method.
[M]{ẍ} + [C]{ẋ} + [K]{x} = {F}
Args:
mass_matrix: 6x6 mass matrix [M]
damping_matrix: 6x6 damping matrix [C]
stiffness_matrix: 6x6 stiffness matrix [K]
force_vector: 6x1 force vector {F}
displacement: Current displacement {x_n}
velocity: Current velocity {ẋ_n}
dt: Time step
Returns:
(acceleration, velocity, displacement) at next time step
"""
beta = 0.25
gamma = 0.5
K_eff = (
mass_matrix / (beta * dt**2) +
damping_matrix * gamma / (beta * dt) +
stiffness_matrix
)
F_eff = (
force_vector +
mass_matrix @ (
displacement / (beta * dt**2) +
velocity / (beta * dt)
) +
damping_matrix @ (
displacement * gamma / (beta * dt) -
velocity * (1 - gamma / beta)
)
)
displacement_next = np.linalg.solve(K_eff, F_eff)
velocity_next = (
gamma / (beta * dt) * (displacement_next - displacement) +
(1 - gamma / beta) * velocity
)
acceleration_next = (
(displacement_next - displacement) / (beta * dt**2) -
velocity / (beta * dt)
)
return acceleration_next, velocity_next, displacement_next
M = np.diag([100000, 100000, 100000, , , ])
C = np.diag([, , , , , ])
K = np.diag([, , , , , ])
F = np.array([, , , , , ])
x = np.zeros()
v = np.zeros()
a, v_new, x_new = solve_6dof_equation_of_motion(M, C, K, F, x, v, dt=)
()
()
()
4. FFT and Frequency Analysis
FFT for Spectral Analysis:
def compute_fft_spectrum(
time_series: np.ndarray,
dt: float,
window: str = 'hann'
) -> tuple[np.ndarray, np.ndarray]:
"""
Compute FFT spectrum of time series.
Args:
time_series: Time series data
dt: Time step
window: Window function ('hann', 'hamming', 'blackman')
Returns:
(frequencies, amplitude_spectrum)
"""
n = len(time_series)
if window == 'hann':
windowed = time_series * np.hanning(n)
elif window == 'hamming':
windowed = time_series * np.hamming(n)
elif window == 'blackman':
windowed = time_series * np.blackman(n)
else:
windowed = time_series
fft_result = np.fft.fft(windowed)
frequencies = np.fft.fftfreq(n, d=dt)
amplitude = np.abs(fft_result)[:n//2] * 2 / n
frequencies_positive = frequencies[:n//2]
return frequencies_positive, amplitude
import numpy as np
t = np.linspace(0, 100, 10000)
dt = t[1] - t[0]
wave = (
2.0 * np.sin(2*np.pi*t / 6) +
1.5 * np.sin(*np.pi*t / ) +
* np.sin(*np.pi*t / )
)
wave += * np.random.randn((t))
freq, amplitude = compute_fft_spectrum(wave, dt, window=)
peak_indices = np.argsort(amplitude)[-:]
peak_frequencies = freq[peak_indices]
peak_periods = / peak_frequencies
()
period (peak_periods, reverse=):
()
Power Spectral Density:
def compute_power_spectral_density(
time_series: np.ndarray,
dt: float,
nfft: int = None
) -> tuple[np.ndarray, np.ndarray]:
"""
Compute power spectral density using Welch's method.
Args:
time_series: Time series data
dt: Time step
nfft: FFT length (None = length of time series)
Returns:
(frequencies, PSD)
"""
from scipy import signal
frequencies, psd = signal.welch(
time_series,
fs=1/dt,
nperseg=nfft or len(time_series)//8,
window='hann'
)
return frequencies, psd
t = np.linspace(0, 3600, 36000)
dt = t[1] - t[0]
wave_elevation = np.random.randn(len(t)) * 2.0
freq, psd = compute_power_spectral_density(wave_elevation, dt)
m0 = np.trapz(psd, freq)
Hs = 4 * np.sqrt(m0)
print(f"Significant wave height: {Hs:.2f} m")
5. Linear Algebra Operations
Eigenvalue Analysis:
def natural_frequency_analysis(
mass_matrix: np.ndarray,
stiffness_matrix: np.ndarray
) -> dict:
"""
Perform eigenvalue analysis to find natural frequencies and mode shapes.
[K]{ϕ} = ω²[M]{ϕ}
Args:
mass_matrix: Mass matrix [M]
stiffness_matrix: Stiffness matrix [K]
Returns:
Dictionary with natural frequencies and mode shapes
"""
eigenvalues, eigenvectors = np.linalg.eig(
np.linalg.solve(mass_matrix, stiffness_matrix)
)
natural_frequencies_rad = np.sqrt(eigenvalues)
natural_frequencies_hz = natural_frequencies_rad / (2 * np.pi)
sort_indices = np.argsort(natural_frequencies_hz)
natural_frequencies_hz = natural_frequencies_hz[sort_indices]
eigenvectors = eigenvectors[:, sort_indices]
periods = 1 / natural_frequencies_hz
return {
'frequencies_hz': natural_frequencies_hz,
'frequencies_rad_s': natural_frequencies_rad[sort_indices],
'periods_s': periods,
'mode_shapes': eigenvectors
}
M = np.diag([150000, 150000, 150000, 1e7, 1e7, 5e6])
K = np.diag([500, 500, 3000, 5e5, 5e5, 1e5])
results = natural_frequency_analysis(M, K)
print("Natural Frequencies:")
for i, (freq, period) ((results[], results[])):
dof_names = [, , , , , ]
()
Matrix Decomposition:
def lu_decomposition_solve(A: np.ndarray, b: np.ndarray) -> np.ndarray:
"""
Solve linear system using LU decomposition.
Args:
A: Coefficient matrix
b: Right-hand side vector
Returns:
Solution vector x
"""
from scipy.linalg import lu
P, L, U = lu(A)
y = np.linalg.solve(L, P @ b)
x = np.linalg.solve(U, y)
return x
A = np.array([[4, -1, 0],
[-1, 4, -1],
[0, -1, 3]])
b = np.array([15, 10, 10])
x = lu_decomposition_solve(A, b)
print(f"Solution: {x}")
6. Numerical Integration
Time-Stepping Integration:
def runge_kutta_4th_order(
derivative_func,
y0: np.ndarray,
t: np.ndarray
) -> np.ndarray:
"""
4th-order Runge-Kutta integration.
Args:
derivative_func: Function dy/dt = f(t, y)
y0: Initial conditions
t: Time array
Returns:
Solution array
"""
n = len(t)
y = np.zeros((n, len(y0)))
y[0] = y0
for i in range(n - 1):
dt = t[i+1] - t[i]
k1 = derivative_func(t[i], y[i])
k2 = derivative_func(t[i] + dt/2, y[i] + k1*dt/2)
k3 = derivative_func(t[i] + dt/2, y[i] + k2*dt/2)
k4 = derivative_func(t[i] + dt, y[i] + k3*dt)
y[i+1] = y[i] + (dt/6) * (k1 + 2*k2 + 2*k3 + k4)
return y
def oscillator_derivatives(t, y):
"""Simple harmonic oscillator."""
m = 1.0
k = 4.0
x, v = y
dxdt = v
dvdt = -k/m * x
return np.array([dxdt, dvdt])
y0 = np.array([1.0, 0.0])
t = np.linspace(0, 10, 1000)
solution = runge_kutta_4th_order(oscillator_derivatives, y0, t)
()
()
Trapezoidal Integration:
def integrate_spectrum(
frequencies: np.ndarray,
spectral_density: np.ndarray
) -> float:
"""
Integrate spectral density to get variance.
m_0 = ∫ S(f) df
Args:
frequencies: Frequency array
spectral_density: Spectral density array
Returns:
Integral (variance)
"""
variance = np.trapz(spectral_density, frequencies)
return variance
freq = np.linspace(0.05, 0.5, 100)
S = 10 * freq**(-5)
m0 = integrate_spectrum(freq, S)
Hs = 4 * np.sqrt(m0)
print(f"Significant wave height: {Hs:.2f} m")
Complete Examples
Example 1: 6DOF Time-Domain Simulation
import numpy as np
import plotly.graph_objects as go
def simulate_6dof_vessel_motion(
mass_matrix: np.ndarray,
damping_matrix: np.ndarray,
stiffness_matrix: np.ndarray,
force_time_series: np.ndarray,
time: np.ndarray
) -> dict:
"""
Complete 6DOF time-domain simulation of vessel motion.
Args:
mass_matrix: 6x6 mass matrix
damping_matrix: 6x6 damping matrix
stiffness_matrix: 6x6 stiffness matrix
force_time_series: Force time series (n_steps x 6)
time: Time array
Returns:
Dictionary with motion time series
"""
n_steps = len(time)
dt = time[1] - time[0]
displacement = np.zeros((n_steps, 6))
velocity = np.zeros((n_steps, 6))
acceleration = np.zeros((n_steps, 6))
displacement[0] = np.zeros(6)
velocity[0] = np.zeros(6)
for i in range(n_steps - 1):
a, v, d = solve_6dof_equation_of_motion(
mass_matrix,
damping_matrix,
stiffness_matrix,
force_time_series[i],
displacement[i],
velocity[i],
dt
)
acceleration[i+1] = a
velocity[i+1] = v
displacement[i+1] = d
return {
'time': time,
'displacement': displacement,
'velocity': velocity,
'acceleration': acceleration
}
M = np.diag([, , , , , ])
C = np.diag([, , , , , ])
K = np.diag([, , , , , ])
time = np.linspace(, , )
dt = time[] - time[]
F = np.zeros(((time), ))
F[:, ] = * np.sin(*np.pi*time / )
results = simulate_6dof_vessel_motion(M, C, K, F, time)
fig = go.Figure()
dof_names = [, , , , , ]
i ():
fig.add_trace(go.Scatter(
x=results[],
y=results[][:, i],
name=dof_names[i],
mode=
))
fig.update_layout(
title=,
xaxis_title=,
yaxis_title=,
hovermode=
)
fig.write_html()
()
i, name (dof_names):
()
()
()
Example 2: RAO Calculation from FFT
def calculate_rao_from_time_series(
wave_elevation: np.ndarray,
vessel_response: np.ndarray,
dt: float
) -> tuple[np.ndarray, np.ndarray]:
"""
Calculate Response Amplitude Operator (RAO) from time series.
RAO(ω) = |Response(ω)| / |Wave(ω)|
Args:
wave_elevation: Wave elevation time series
vessel_response: Vessel response time series
dt: Time step
Returns:
(frequencies, RAO)
"""
freq_wave, amplitude_wave = compute_fft_spectrum(wave_elevation, dt)
freq_response, amplitude_response = compute_fft_spectrum(vessel_response, dt)
rao = np.where(
amplitude_wave > 1e-6,
amplitude_response / amplitude_wave,
0
)
return freq_wave, rao
time = np.linspace(0, 100, 10000)
dt = time[1] - time[0]
wave = 1.0 * np.sin(2*np.pi*time / 8)
heave = 1.5 * np.sin(2*np.pi*time / 8 - np.pi/6)
freq, rao = calculate_rao_from_time_series(wave, heave, dt)
fig = go.Figure()
fig.add_trace(go.Scatter(
x=freq,
y=rao,
name='Heave RAO',
mode='lines'
))
fig.update_layout(
title='Heave Response Amplitude Operator',
xaxis_title='Frequency (Hz)',
yaxis_title='RAO (m/m)',
xaxis_range=[0, ]
)
fig.write_html()
peak_idx = np.argmax(rao[freq < ])
peak_freq = freq[peak_idx]
peak_period = / peak_freq
()
Example 3: Mooring Stiffness Matrix
def calculate_mooring_stiffness_matrix(
num_lines: int,
pretension: float,
fairlead_radius: float,
fairlead_depth: float,
line_azimuth: np.ndarray,
weight_per_length: float
) -> np.ndarray:
"""
Calculate mooring system stiffness matrix.
Args:
num_lines: Number of mooring lines
pretension: Pretension per line (kN)
fairlead_radius: Horizontal distance from center to fairlead (m)
fairlead_depth: Depth of fairlead below waterline (m)
line_azimuth: Azimuth angle of each line (degrees)
weight_per_length: Line weight per unit length (kN/m)
Returns:
6x6 mooring stiffness matrix
"""
K_mooring = np.zeros((6, 6))
k_line = weight_per_length * pretension / fairlead_depth
for i in range(num_lines):
theta = np.radians(line_azimuth[i])
cx = np.cos(theta)
cy = np.sin(theta)
K_mooring[0, 0] += k_line * cx**2
K_mooring[1, 1] += k_line * cy**2
K_mooring[0, 1] += k_line * cx * cy
K_mooring[1, 0] += k_line * cx * cy
K_mooring[5, 5] += k_line * fairlead_radius**2
K_mooring[0, 5] += k_line * fairlead_radius * cy
K_mooring[, ] += k_line * fairlead_radius * cy
K_mooring[, ] -= k_line * fairlead_radius * cx
K_mooring[, ] -= k_line * fairlead_radius * cx
K_mooring
num_lines =
azimuths = np.array([, , , , , , , , , , , ])
K = calculate_mooring_stiffness_matrix(
num_lines=,
pretension=,
fairlead_radius=,
fairlead_depth=,
line_azimuth=azimuths,
weight_per_length=
)
()
(K)
()
Example 4: Statistical Analysis of Extremes
def extreme_value_statistics(
data: np.ndarray,
method: str = '3hr_max'
) -> dict:
"""
Perform extreme value statistical analysis.
Args:
data: Time series data
method: '3hr_max' or 'annual_max'
Returns:
Statistical parameters
"""
if method == '3hr_max':
chunk_size = int(3 * 3600 / 0.1)
n_chunks = len(data) // chunk_size
maxima = np.array([
np.max(data[i*chunk_size:(i+1)*chunk_size])
for i in range(n_chunks)
])
elif method == 'annual_max':
maxima = data
mu = np.mean(maxima)
sigma = np.std(maxima)
beta = sigma * np.sqrt(6) / np.pi
mu_gumbel = mu - 0.5772 * beta
return_periods = np.array([1, 10, 100, 10000])
extreme_values = mu_gumbel - beta * np.log(-np.log(1 - 1/return_periods))
return {
'mean': mu,
'std': sigma,
'gumbel_location': mu_gumbel,
'gumbel_scale': beta,
: return_periods,
: extreme_values
}
time = np.linspace(, , )
tension = + * np.random.rayleigh(scale=, size=(time))
stats = extreme_value_statistics(tension, method=)
()
()
()
()
T, val (stats[], stats[]):
()
Example 5: Convolution for Impulse Response
def convolve_impulse_response(
impulse_response: np.ndarray,
force_time_series: np.ndarray,
dt: float
) -> np.ndarray:
"""
Convolve impulse response with force time series.
Response(t) = ∫ h(τ) * F(t-τ) dτ
Args:
impulse_response: Impulse response function
force_time_series: Force time series
dt: Time step
Returns:
Response time series
"""
response = np.convolve(impulse_response, force_time_series, mode='same') * dt
return response
t = np.linspace(0, 10, 1000)
dt = t[1] - t[0]
omega = 2 * np.pi
zeta = 0.1
h = np.exp(-zeta * omega * t) * np.sin(omega * np.sqrt(1 - zeta**2) * t)
F = np.random.randn(len(t))
response = convolve_impulse_response(h, F, dt)
print(f"Max response: {np.max(np.abs(response)):.3f}")
print(f"Std response: {np.std(response):.3f}")
Best Practices
1. Use Vectorization
result = np.zeros(len(x))
for i in range(len(x)):
result[i] = x[i]**2 + y[i]**2
result = x**2 + y**2
2. Avoid Unnecessary Copies
a = np.array([1, 2, 3])
b = a
b[0] = 10
a = np.array([1, 2, 3])
b = a.copy()
b[0] = 10
3. Use In-Place Operations
a = a + 1
a += 1
4. Choose Appropriate Data Types
large_array = np.zeros((10000, 10000), dtype=np.float32)
Resources
Use this skill for all numerical computations in DigitalModel!