fatigue-analysis
Included with Lifetime
$97 forever
Fatigue analysis for offshore structures including S-N curves, rainflow counting, Miner's rule, and DNV standards
subject-matter-expertfatigues-n-curverainflow-countingminers-rulednvmooring-fatiguestructural-fatigue
What this skill does
# Fatigue Analysis SME Skill
Comprehensive fatigue analysis expertise for offshore structures including mooring lines, risers, and structural components using industry-standard methods and DNV regulations.
## When to Use This Skill
Use fatigue analysis when:
- **Mooring line fatigue** - Calculate fatigue life of mooring components
- **Riser fatigue** - Analyze fatigue damage in flexible and rigid risers
- **Structural fatigue** - Assess fatigue in hull, joints, connections
- **S-N curve analysis** - Apply appropriate fatigue curves
- **Rainflow counting** - Process stress/load time series
- **Miner's rule** - Cumulative damage calculation
- **Fatigue design** - Size components for target life
## Core Knowledge Areas
### 1. S-N Curve Fundamentals
**S-N Curve Equation:**
```
N = a / (Δσ)^m
Where:
- N = Number of cycles to failure
- Δσ = Stress range
- a = S-N curve constant
- m = Slope of S-N curve (typically 3 for steel, 3-5 for welds)
```
**DNV S-N Curves:**
```python
import numpy as np
def get_dnv_sn_curve(
curve_class: str,
thickness: float = 25
) -> dict:
"""
Get DNV S-N curve parameters.
DNV-RP-C203 S-N curves:
- B1: High strength welds, machined
- C: Good quality welds
- D: Normal welds
- E: Rough welds
- F, F1, F3: Poor quality, notches
- G: Severe notches
- W1, W2, W3: Seawater with cathodic protection
Args:
curve_class: DNV curve classification
thickness: Plate thickness (mm) for thickness effect
Returns:
S-N curve parameters
"""
# DNV-RP-C203 Table 2-1
sn_curves = {
'B1': {'log_a1': 15.117, 'm1': 4.0, 'log_a2': 17.146, 'm2': 5.0},
'B2': {'log_a1': 14.885, 'm1': 4.0, 'log_a2': 16.856, 'm2': 5.0},
'C': {'log_a1': 12.592, 'm1': 3.0, 'log_a2': 16.320, 'm2': 5.0},
'C1': {'log_a1': 12.449, 'm1': 3.0, 'log_a2': 16.081, 'm2': 5.0},
'C2': {'log_a1': 12.301, 'm1': 3.0, 'log_a2': 15.835, 'm2': 5.0},
'D': {'log_a1': 12.164, 'm1': 3.0, 'log_a2': 15.606, 'm2': 5.0},
'E': {'log_a1': 11.972, 'm1': 3.0, 'log_a2': 15.350, 'm2': 5.0},
'F': {'log_a1': 11.699, 'm1': 3.0, 'log_a2': 14.832, 'm2': 5.0},
'F1': {'log_a1': 11.546, 'm1': 3.0, 'log_a2': 14.576, 'm2': 5.0},
'F3': {'log_a1': 11.398, 'm1': 3.0, 'log_a2': 14.330, 'm2': 5.0},
'G': {'log_a1': 11.245, 'm1': 3.0, 'log_a2': 14.080, 'm2': 5.0},
'W1': {'log_a1': 11.764, 'm1': 3.0, 'log_a2': 15.091, 'm2': 5.0},
'W2': {'log_a1': 11.533, 'm1': 3.0, 'log_a2': 14.706, 'm2': 5.0},
'W3': {'log_a1': 11.262, 'm1': 3.0, 'log_a2': 14.183, 'm2': 5.0}
}
if curve_class not in sn_curves:
raise ValueError(f"Unknown S-N curve class: {curve_class}")
params = sn_curves[curve_class]
# Convert log_a to a
a1 = 10 ** params['log_a1']
a2 = 10 ** params['log_a2']
# Thickness correction (ref thickness = 25mm)
if thickness > 25:
t_factor = (25 / thickness) ** 0.25
a1 *= t_factor ** params['m1']
a2 *= t_factor ** params['m2']
return {
'class': curve_class,
'a1': a1,
'm1': params['m1'],
'a2': a2,
'm2': params['m2'],
'thickness_mm': thickness
}
# Example: Get F3 curve for mooring chain
sn_f3 = get_dnv_sn_curve('F3', thickness=127) # 127mm chain
print(f"S-N Curve F3 (Chain):")
print(f" a1 = {sn_f3['a1']:.2e}, m1 = {sn_f3['m1']}")
print(f" a2 = {sn_f3['a2']:.2e}, m2 = {sn_f3['m2']}")
```
**Calculate Cycles to Failure:**
```python
def calculate_cycles_to_failure(
stress_range: float,
sn_curve: dict
) -> float:
"""
Calculate cycles to failure for given stress range.
N = a / (Δσ)^m
Args:
stress_range: Stress range (MPa)
sn_curve: S-N curve parameters from get_dnv_sn_curve()
Returns:
Cycles to failure
"""
# Use first segment if stress range is high
# Switch to second segment if N > 1e7 (DNV bi-linear curve)
N1 = sn_curve['a1'] / (stress_range ** sn_curve['m1'])
if N1 <= 1e7:
return N1
else:
# Use second segment
N2 = sn_curve['a2'] / (stress_range ** sn_curve['m2'])
return N2
# Example
stress_range = 50 # MPa
N = calculate_cycles_to_failure(stress_range, sn_f3)
print(f"Stress range: {stress_range} MPa")
print(f"Cycles to failure: {N:.2e}")
print(f"Years at 1 Hz: {N / (365.25 * 24 * 3600):.2f}")
```
### 2. Rainflow Counting
**Rainflow Algorithm:**
```python
def rainflow_counting(
time_series: np.ndarray,
bin_width: float = None
) -> tuple[np.ndarray, np.ndarray]:
"""
Rainflow cycle counting algorithm.
ASTM E1049-85 standard implementation.
Args:
time_series: Stress or load time series
bin_width: Bin width for histogram (None = auto)
Returns:
(ranges, counts) - Stress ranges and cycle counts
"""
# Simple peak-valley extraction
peaks_valleys = []
for i in range(1, len(time_series) - 1):
if (time_series[i] > time_series[i-1] and time_series[i] > time_series[i+1]) or \
(time_series[i] < time_series[i-1] and time_series[i] < time_series[i+1]):
peaks_valleys.append(time_series[i])
# Rainflow counting
stack = []
ranges = []
for value in peaks_valleys:
stack.append(value)
while len(stack) >= 3:
# Check for cycle
X = abs(stack[-2] - stack[-3])
Y = abs(stack[-1] - stack[-2])
if len(stack) == 3:
if Y >= X:
# Extract cycle
ranges.append(X)
stack.pop(-2)
stack.pop(-2)
else:
break
else:
Z = abs(stack[-3] - stack[-4])
if Y >= X and X >= Z:
# Extract cycle
ranges.append(X)
stack.pop(-2)
stack.pop(-2)
else:
break
# Create histogram
ranges = np.array(ranges)
if bin_width is None:
bin_width = (np.max(ranges) - np.min(ranges)) / 20
bins = np.arange(0, np.max(ranges) + bin_width, bin_width)
counts, bin_edges = np.histogram(ranges, bins=bins)
# Use bin centers
bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2
return bin_centers, counts
# Example: Mooring tension time series
t = np.linspace(0, 3600, 36000) # 1 hour
tension = 2000 + 300 * np.sin(2*np.pi*t/10) + 100 * np.sin(2*np.pi*t/3) + 50*np.random.randn(len(t))
ranges, counts = rainflow_counting(tension, bin_width=10)
print(f"Rainflow cycles:")
print(f" Total cycles: {np.sum(counts)}")
print(f" Max range: {np.max(ranges):.1f} kN")
```
### 3. Miner's Rule (Cumulative Damage)
**Palmgren-Miner Damage:**
```python
def calculate_fatigue_damage_miners_rule(
stress_ranges: np.ndarray,
cycle_counts: np.ndarray,
sn_curve: dict,
design_factor: float = 10.0
) -> dict:
"""
Calculate fatigue damage using Miner's rule.
D = Σ(n_i / N_i)
Where:
- n_i = number of cycles at stress range i
- N_i = cycles to failure at stress range i
Args:
stress_ranges: Array of stress ranges (MPa)
cycle_counts: Array of cycle counts for each range
sn_curve: S-N curve parameters
design_factor: Safety factor (DNV: 10 for mooring)
Returns:
Fatigue damage and life prediction
"""
total_damage = 0.0
damage_breakdown = []
for stress_range, n_cycles in zip(stress_ranges, cycle_counts):
if stress_range > 0:
# Cycles to failure
N = calculate_cycles_to_failure(stress_range, sn_curve)
# Damage contribution
damage = n_cycles / N
total_damage += damage
damage_breakdown.append({
'stress_range': stress_range,
'cycles': n_cycles,
'N_failure': N,
Related in subject-matter-expert
hydrodynamic-analysis
IncludedHydrodynamic analysis using BEM, RAOs, added mass, damping, and wave loads for offshore structures
subject-matter-expert
wave-theory
IncludedOcean wave theory including wave spectra, statistics, irregular seas, and wave transformation for offshore engineering
subject-matter-expert
ship-dynamics-6dof
Included6DOF ship dynamics, equations of motion, seakeeping analysis, and natural frequency calculations
subject-matter-expert
marine-offshore-engineering
IncludedMarine and offshore engineering fundamentals for platform design, subsea systems, and regulatory compliance
subject-matter-expert
mooring-analysis
IncludedMooring system design, analysis, and assessment for floating offshore platforms
subject-matter-expert