A Python implementation of the exponential degradation model for remaining useful life (RUL) prediction
Project description
Exponential Degradation Model
A Python implementation of the exponential degradation model for predicting remaining useful life (RUL) of degrading systems. This package provides a probabilistic approach to prognostics using Bayesian parameter updating.
Overview
This implementation is based on the exponential degradation model described in:
Gebraeel, N. (2006). "Sensory-Updated Residual Life Distributions for Components With Exponential Degradation Patterns." IEEE Transactions on Automation Science and Engineering, 3(4), 382-393.
The model is designed for systems that exhibit exponential degradation patterns and provides:
- Bayesian parameter updating as new degradation measurements become available
- Probabilistic RUL predictions with confidence intervals
- Uncertainty quantification through parameter variance tracking
- Correlation modeling between degradation parameters
This package provides functionality similar to MATLAB's exponentialDegradationModel from the Predictive Maintenance Toolbox.
Features
- ✅ Bayesian updating: Refines model parameters with each new observation
- ✅ Multiple observation fitting: Efficient batch parameter updates
- ✅ Comprehensive predictions: Point estimates, confidence intervals, and full probability distributions
- ✅ Truncated distributions: Ensures physically meaningful (positive) RUL predictions
- ✅ Correlation handling: Accounts for parameter correlations in uncertainty propagation
- ✅ Numerical stability: Robust handling of edge cases and numerical issues
Installation
From PyPI (when published)
pip install exponential-degradation-model
From Source
git clone https://github.com/houtj/exponentialDegradationModel.git
cd exponentialDegradationModel
pip install -e .
Development Installation
pip install -e ".[dev]"
Quick Start
import numpy as np
from exponential_degradation import ExponentialDegradationModel
# Create model with failure threshold
model = ExponentialDegradationModel(threshold=10.0)
# Prepare degradation measurements
times = np.array([1, 2, 3, 4, 5])
measurements = np.array([1.5, 2.1, 2.9, 4.2, 5.8])
# Fit the model to observations
model.fit(measurements, times)
# Predict remaining useful life
rul_result = model.predict_rul(confidence_level=0.95)
print(f"Predicted RUL: {rul_result['RUL']:.2f}")
print(f"Mean RUL: {rul_result['mean']:.2f}")
print(f"95% CI: [{rul_result['CI'][0]:.2f}, {rul_result['CI'][1]:.2f}]")
Mathematical Model
Degradation Path
The model assumes an exponential degradation path:
y(t) = exp(θ + β·t) + φ
where:
- θ (theta): Initial value parameter (log-scale)
- β (beta): Growth rate parameter (degradation rate)
- φ (phi): Offset parameter
- t: Time
Failure Condition
The system fails when the degradation measurement reaches a threshold D:
y(t) ≥ D
Remaining Useful Life
The RUL at time t is:
RUL = L - t
where L is the time to failure:
L = [ln(D - φ) - θ] / β
Bayesian Updating
As new measurements arrive, the model uses Bayesian updating to refine the parameter estimates (θ, β) and their uncertainties (variances and correlation). This provides increasingly accurate RUL predictions as more degradation data becomes available.
Uncertainty Quantification
The RUL prediction accounts for parameter uncertainty through variance propagation:
Var(L) = (1/β²) · [Var(θ) + (μ_L·β)²·Var(β) - 2·μ_L·β·Cov(θ,β)]
The RUL distribution is modeled as a truncated normal distribution (truncated at zero) to ensure physically meaningful positive RUL values.
API Reference
ExponentialDegradationModel
Initialization
ExponentialDegradationModel(
threshold,
theta=1.0,
theta_variance=1e6,
beta=1.0,
beta_variance=1e6,
rho=0.0,
phi=-1.0
)
Parameters:
threshold(float): Failure threshold valuetheta(float, optional): Initial value parameter. Default: 1.0theta_variance(float, optional): Initial variance of theta. Default: 1e6 (high uncertainty)beta(float, optional): Growth rate parameter. Default: 1.0beta_variance(float, optional): Initial variance of beta. Default: 1e6rho(float, optional): Correlation coefficient between theta and beta [-1, 1]. Default: 0.0phi(float, optional): Offset parameter. Default: -1.0
Methods
update(measurement, time)
Update model parameters with a single new observation.
model.update(measurement=2.5, time=1.0)
Parameters:
measurement(float): Degradation measurement at the given timetime(float): Time point of the measurement
fit(measurements, times)
Update model parameters with multiple observations (batch update).
measurements = np.array([1.5, 2.1, 2.9, 4.2, 5.8])
times = np.array([1, 2, 3, 4, 5])
model.fit(measurements, times)
Parameters:
measurements(array-like): Array of degradation measurementstimes(array-like): Array of corresponding time points
predict()
Get a simple point estimate of the mean RUL.
rul = model.predict()
Returns:
float: Mean remaining useful life
predict_rul(confidence_level=0.95, num_samples=1000)
Get comprehensive RUL prediction with confidence intervals and probability distributions.
result = model.predict_rul(confidence_level=0.95, num_samples=1000)
Parameters:
confidence_level(float, optional): Confidence level for CI (0-1). Default: 0.95num_samples(int, optional): Number of points for PDF/CDF. Default: 1000
Returns:
Dictionary containing:
'RUL': Median RUL (recommended point estimate)'mean': Mean RUL'std': Standard deviation of RUL'CI': Tuple of (lower_bound, upper_bound)'pdf_time': Array of time points for PDF'pdf_values': PDF values at each time point'cdf_values': CDF values at each time point'mu_untruncated': Mean of untruncated distribution'sigma_untruncated': Std of untruncated distribution
Usage Examples
Example 1: Sequential Updating
import numpy as np
from exponential_degradation import ExponentialDegradationModel
# Initialize model
model = ExponentialDegradationModel(threshold=15.0)
# Sequential updates as new measurements arrive
times = [1, 2, 3, 4, 5]
measurements = [2.1, 3.5, 5.2, 7.8, 10.5]
for t, m in zip(times, measurements):
model.update(m, t)
rul = model.predict()
print(f"Time {t}: RUL = {rul:.2f}")
Example 2: Batch Fitting with Visualization
import numpy as np
import matplotlib.pyplot as plt
from exponential_degradation import ExponentialDegradationModel
# Generate synthetic degradation data
np.random.seed(42)
true_theta = 0.5
true_beta = 0.3
times = np.array([0, 1, 2, 3, 4, 5])
measurements = np.exp(true_theta + true_beta * times) + np.random.normal(0, 0.1, len(times))
# Create and fit model
model = ExponentialDegradationModel(threshold=20.0)
model.fit(measurements, times)
# Predict RUL with confidence intervals
result = model.predict_rul(confidence_level=0.95)
# Visualize results
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
# Plot degradation path
ax1.scatter(times, measurements, label='Measurements', color='blue')
ax1.axhline(y=20.0, color='r', linestyle='--', label='Failure Threshold')
ax1.set_xlabel('Time')
ax1.set_ylabel('Degradation')
ax1.set_title('Degradation Path')
ax1.legend()
ax1.grid(True)
# Plot RUL distribution
ax2.plot(result['pdf_time'], result['pdf_values'], label='PDF', color='green')
ax2.axvline(x=result['RUL'], color='b', linestyle='--', label=f"Median RUL: {result['RUL']:.2f}")
ax2.axvline(x=result['CI'][0], color='gray', linestyle=':', label='95% CI')
ax2.axvline(x=result['CI'][1], color='gray', linestyle=':')
ax2.set_xlabel('Remaining Useful Life')
ax2.set_ylabel('Probability Density')
ax2.set_title('RUL Distribution')
ax2.legend()
ax2.grid(True)
plt.tight_layout()
plt.show()
Example 3: Batch Fitting and Sequential Updating
import numpy as np
from exponential_degradation import ExponentialDegradationModel
# Generate synthetic degradation data (30 points total)
np.random.seed(42)
true_theta = 0.2
true_beta = 0.15
all_times = np.linspace(1, 30, 30)
all_measurements = np.exp(true_theta + true_beta * all_times) + np.random.normal(0, 0.3, 30)
# Split data: first 5 points for batch fitting, remaining 25 for sequential updates
initial_times = all_times[:5]
initial_measurements = all_measurements[:5]
update_times = all_times[5:]
update_measurements = all_measurements[5:]
# Initialize model with appropriate threshold
model = ExponentialDegradationModel(threshold=200.0)
# Step 1: Batch fitting with initial 5 points
print("Step 1: Initial batch fitting with 5 points")
model.fit(initial_measurements, initial_times)
result = model.predict_rul()
print(f" Current time: {model.ti:.2f}")
print(f" Predicted RUL: {result['RUL']:.2f}")
print(f" 95% CI: [{result['CI'][0]:.2f}, {result['CI'][1]:.2f}]")
print(f" Parameters: theta={model.theta:.3f}, beta={model.beta:.3f}")
# Step 2: Sequential updates with remaining 25 points
print("\nStep 2: Sequential updates with remaining 25 points")
print(f"{'Time':<8} {'Measurement':<14} {'RUL':<10} {'CI Width':<12} {'Theta':<10} {'Beta':<10}")
print("-" * 72)
rul_history = []
ci_width_history = []
for t, m in zip(update_times, update_measurements):
model.update(m, t)
result = model.predict_rul()
ci_width = result['CI'][1] - result['CI'][0]
# Store for analysis
rul_history.append(result['RUL'])
ci_width_history.append(ci_width)
# Print every 5th update for brevity
if int(t) % 5 == 0 or t == update_times[-1]:
print(f"{t:<8.1f} {m:<14.2f} {result['RUL']:<10.2f} {ci_width:<12.2f} "
f"{model.theta:<10.3f} {model.beta:<10.3f}")
# Final prediction summary
print("\nFinal Prediction (after 30 observations):")
final_result = model.predict_rul()
print(f" Median RUL: {final_result['RUL']:.2f}")
print(f" Mean RUL: {final_result['mean']:.2f}")
print(f" Std Dev: {final_result['std']:.2f}")
print(f" 95% CI: [{final_result['CI'][0]:.2f}, {final_result['CI'][1]:.2f}]")
print(f" Final parameters: theta={model.theta:.4f}, beta={model.beta:.4f}, rho={model.rho:.4f}")
# Analysis
print("\nObservation: CI width decreased from", f"{ci_width_history[0]:.2f} to {ci_width_history[-1]:.2f}")
print(" Parameter estimates converged with more data")
print(f"\nThis example demonstrates the hybrid approach:")
print(f" 1. Batch fitting (fit) with initial data for fast initialization")
print(f" 2. Sequential updating (update) for real-time monitoring")
print(f" 3. Uncertainty reduction as more observations are incorporated")
Example 4: Comparing Different Initial Parameters
import numpy as np
from exponential_degradation import ExponentialDegradationModel
# Same data, different initial parameter uncertainties
times = np.array([1, 2, 3, 4, 5])
measurements = np.array([1.5, 2.5, 4.0, 6.5, 10.0])
# High initial uncertainty (default)
model_uncertain = ExponentialDegradationModel(
threshold=15.0,
theta_variance=1e6,
beta_variance=1e6
)
model_uncertain.fit(measurements, times)
result_uncertain = model_uncertain.predict_rul()
# Low initial uncertainty (strong prior)
model_informed = ExponentialDegradationModel(
threshold=15.0,
theta=0.5,
theta_variance=0.1,
beta=0.4,
beta_variance=0.01
)
model_informed.fit(measurements, times)
result_informed = model_informed.predict_rul()
print("High Uncertainty Prior:")
print(f" RUL: {result_uncertain['RUL']:.2f}")
print(f" 95% CI: [{result_uncertain['CI'][0]:.2f}, {result_uncertain['CI'][1]:.2f}]")
print(f" CI Width: {result_uncertain['CI'][1] - result_uncertain['CI'][0]:.2f}")
print("\nLow Uncertainty Prior (Informed):")
print(f" RUL: {result_informed['RUL']:.2f}")
print(f" 95% CI: [{result_informed['CI'][0]:.2f}, {result_informed['CI'][1]:.2f}]")
print(f" CI Width: {result_informed['CI'][1] - result_informed['CI'][0]:.2f}")
When to Use This Model
Suitable Applications
This model is appropriate when:
- ✅ Degradation follows an exponential growth pattern
- ✅ Measurements are available over time
- ✅ A clear failure threshold can be defined
- ✅ Degradation is monotonically increasing
- ✅ Uncertainty quantification is important
Example Applications
- Bearing degradation: Vibration amplitude growth
- Battery capacity fade: Capacity loss over charge cycles
- Crack propagation: Crack size growth in structural components
- Wear processes: Tool wear, brake pad wear
- Corrosion: Material thickness loss
- Sensor drift: Calibration drift in sensors
Not Suitable For
- ❌ Non-exponential degradation patterns (linear, power-law, etc.)
- ❌ Non-monotonic degradation (with recovery or fluctuations)
- ❌ Systems without clear failure thresholds
- ❌ Sudden failures without gradual degradation
Technical Details
Numerical Stability
The implementation includes several features to ensure numerical stability:
- Variance clamping to prevent negative variances
- Truncation at zero for physically meaningful RUL values
- Safe handling of extreme parameter values
- Fallback strategies for edge cases
- Warning flags for numerical issues
Parameter Initialization
For systems with weak prior knowledge, use large initial variances (default 1e6) to allow the data to drive parameter estimates. For systems with strong prior knowledge, set smaller variances and appropriate mean values for theta and beta.
Measurement Requirements
For reliable parameter estimation:
- Minimum 3-5 measurements recommended
- Measurements should span a significant portion of the degradation range
- More frequent measurements early in life improve predictions
- Measurements closer to failure threshold provide more information
Dependencies
- Python ≥ 3.8
- NumPy ≥ 1.20.0
- SciPy ≥ 1.7.0
Optional (for examples and visualization):
- Matplotlib ≥ 3.5.0
- Jupyter ≥ 1.0.0
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
License
This project is licensed under the MIT License - see the LICENSE file for details.
References
-
Gebraeel, N. (2006). "Sensory-Updated Residual Life Distributions for Components With Exponential Degradation Patterns." IEEE Transactions on Automation Science and Engineering, 3(4), 382-393.
-
MATLAB Documentation: exponentialDegradationModel
-
Si, X. S., Wang, W., Hu, C. H., & Zhou, D. H. (2011). "Remaining useful life estimation–a review on the statistical data driven approaches." European Journal of Operational Research, 213(1), 1-14.
Author
Tianjun HOU
- GitHub: @houtj
Acknowledgments
This implementation is based on the theoretical framework developed by Prof. Nagi Gebraeel and draws inspiration from MATLAB's Predictive Maintenance Toolbox.
Keywords: prognostics, remaining useful life, RUL, degradation modeling, predictive maintenance, reliability engineering, exponential degradation, Bayesian updating
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file exponential_degradation_model-0.1.0.tar.gz.
File metadata
- Download URL: exponential_degradation_model-0.1.0.tar.gz
- Upload date:
- Size: 260.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16746009885b6d5154aed2538f7a118350f31c345f1a2aa8f5f4a835d99ce5a1
|
|
| MD5 |
d80e9282ee90446ed648566418e4dae3
|
|
| BLAKE2b-256 |
f2365b1e41cbde604d320b26f1ed3a00a66c2083891525f1b86f3b6e487369ae
|
File details
Details for the file exponential_degradation_model-0.1.0-py3-none-any.whl.
File metadata
- Download URL: exponential_degradation_model-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c84e2cdda8d8d1f72dca6465fb9cb18f765ce2568e66753b1f75d5b3239d52cd
|
|
| MD5 |
2f4e09b349fef52437de6e970789e66f
|
|
| BLAKE2b-256 |
7c824cc1b6ffa5204294980468872516428b5110cd4d8f654039f0517783a1cb
|