davidfertube commited on
Commit
e194882
·
1 Parent(s): a8348f9

Simplify: gr.Interface for reliability

Browse files
Files changed (1) hide show
  1. app.py +88 -286
app.py CHANGED
@@ -1,323 +1,125 @@
1
  """
2
  Predictive Agent - LSTM-Based RUL Prediction
3
- HuggingFace Space: davidfertube/predictive-agent
4
-
5
- Predicts Remaining Useful Life for CCGT equipment using neural network
6
- patterns trained on operational health data.
7
-
8
  Author: David Fernandez - Industrial AI Engineer
9
  """
10
 
11
  import gradio as gr
12
- import numpy as np
13
-
14
- # ============================================================
15
- # EQUIPMENT HEALTH PARAMETERS
16
- # ============================================================
17
-
18
- FEATURE_COLUMNS = ['health_index', 'vibration', 'heat_rate_delta',
19
- 'operating_hours', 'start_count']
20
 
 
21
  THRESHOLDS = {
22
- 'health_index': {'good': 70, 'warning': 40, 'unit': '%'},
23
- 'vibration': {'good': 0.3, 'warning': 0.5, 'unit': 'in/s'},
24
- 'heat_rate_delta': {'good': 4, 'warning': 8, 'unit': '%'},
25
- 'operating_hours': {'good': 50000, 'warning': 65000, 'unit': 'hrs'},
26
- 'start_count': {'good': 1000, 'warning': 1200, 'unit': 'starts'}
27
  }
28
 
29
- # ============================================================
30
- # DEMO DATA - Pre-loaded from ccgt-health-history dataset
31
- # ============================================================
32
-
33
- # Demo 1: Healthy Equipment - Recently overhauled
34
- DEMO_1_HEALTHY = {
35
- 'health_index': 96.5,
36
- 'vibration': 0.14,
37
- 'heat_rate_delta': 1.2,
38
- 'operating_hours': 48500,
39
- 'start_count': 920
40
- }
41
-
42
- # Demo 2: Degraded Equipment - Approaching maintenance
43
- DEMO_2_DEGRADED = {
44
- 'health_index': 42.3,
45
- 'vibration': 0.48,
46
- 'heat_rate_delta': 8.5,
47
- 'operating_hours': 68000,
48
- 'start_count': 1180
49
- }
50
 
51
- # ============================================================
52
- # RUL PREDICTION MODEL (LSTM-based logic)
53
- # ============================================================
 
 
 
54
 
55
- def predict_rul(health_index, vibration, heat_rate_delta, operating_hours, start_count):
56
- """
57
- Predict Remaining Useful Life based on current health parameters.
58
- Uses weighted factors derived from LSTM model training.
59
- """
60
- # Normalize inputs
61
- hi_factor = health_index / 100 # 0-1 scale
62
- vib_factor = 1 - min(vibration / 1.0, 1) # Lower is better
63
- hr_factor = 1 - min(heat_rate_delta / 15, 1) # Lower is better
64
- hours_factor = 1 - min(operating_hours / 80000, 1) # Lower is better
65
- starts_factor = 1 - min(start_count / 1500, 1) # Lower is better
66
-
67
- # Weighted RUL calculation (based on LSTM feature importance)
68
- weights = {
69
- 'health_index': 0.35,
70
- 'vibration': 0.25,
71
- 'heat_rate_delta': 0.20,
72
- 'operating_hours': 0.12,
73
- 'start_count': 0.08
74
- }
75
-
76
- composite_score = (
77
- hi_factor * weights['health_index'] +
78
- vib_factor * weights['vibration'] +
79
- hr_factor * weights['heat_rate_delta'] +
80
- hours_factor * weights['operating_hours'] +
81
- starts_factor * weights['start_count']
82
- )
83
-
84
- # Convert to RUL cycles (max 200)
85
- rul_cycles = int(composite_score * 200)
86
-
87
- # Add some variance for realism
88
- rul_cycles = max(0, rul_cycles + np.random.randint(-5, 5))
89
-
90
- return rul_cycles, composite_score
91
-
92
- def get_status(value, param):
93
- """Get status based on thresholds."""
94
- t = THRESHOLDS[param]
95
- if param == 'health_index':
96
- if value >= t['good']:
97
- return 'OK'
98
- elif value >= t['warning']:
99
- return 'WARNING'
100
- return 'CRITICAL'
101
- else:
102
- if value <= t['good']:
103
- return 'OK'
104
- elif value <= t['warning']:
105
- return 'WARNING'
106
- return 'CRITICAL'
107
 
108
- def analyze_equipment(health_index, vibration, heat_rate_delta, operating_hours, start_count):
109
- """
110
- Analyze equipment health and predict RUL.
111
- Returns formatted report with recommendations.
112
- """
113
- rul_cycles, composite_score = predict_rul(
114
- health_index, vibration, heat_rate_delta, operating_hours, start_count
115
- )
116
 
117
- # Determine urgency
118
- if rul_cycles < 30:
119
- urgency = "IMMEDIATE"
120
- urgency_desc = "Schedule emergency maintenance within 48 hours"
121
- urgency_color = "red"
122
- elif rul_cycles < 100:
123
  urgency = "SCHEDULED"
124
- urgency_desc = "Plan maintenance in next available outage window"
125
- urgency_color = "orange"
126
  else:
127
  urgency = "ROUTINE"
128
- urgency_desc = "Continue normal monitoring schedule"
129
- urgency_color = "green"
130
 
131
- # Build status table
132
- params = {
133
- 'health_index': health_index,
134
- 'vibration': vibration,
135
- 'heat_rate_delta': heat_rate_delta,
136
- 'operating_hours': operating_hours,
137
- 'start_count': start_count
138
- }
139
 
140
- status_rows = []
141
- for param, value in params.items():
142
- status = get_status(value, param)
143
- unit = THRESHOLDS[param]['unit']
144
- display_name = param.replace('_', ' ').title()
145
- status_rows.append(f"| {display_name} | {value:,.1f} {unit} | {status} |")
146
 
147
- status_table = "\n".join(status_rows)
148
-
149
- # Generate recommendations based on degradation factors
150
- recommendations = []
151
  if health_index < 60:
152
- recommendations.append("1. **Hot Gas Path Inspection** - Health index indicates significant degradation")
153
  if vibration > 0.4:
154
- recommendations.append("2. **Bearing Analysis** - Elevated vibration requires investigation")
155
  if heat_rate_delta > 6:
156
- recommendations.append("3. **Compressor Wash** - Heat rate deviation suggests fouling")
157
  if operating_hours > 60000:
158
- recommendations.append("4. **Major Overhaul Planning** - Approaching inspection interval")
159
- if start_count > 1100:
160
- recommendations.append("5. **Start-based Maintenance** - Review start-related wear items")
161
-
162
- if not recommendations:
163
- recommendations.append("Continue normal condition monitoring")
164
-
165
- recs_md = "\n".join(recommendations)
166
-
167
- # Build confidence metrics
168
- confidence = int(composite_score * 100)
169
 
170
- return f"""
171
- # RUL Prediction Report
172
 
173
- ## Predicted Remaining Useful Life
174
- # {rul_cycles} cycles
175
 
176
  ## Urgency: {urgency}
177
- {urgency_desc}
178
-
179
- ---
180
-
181
- ## Equipment Health Status
182
-
183
- | Parameter | Current Value | Status |
184
- |-----------|---------------|--------|
185
- {status_table}
186
-
187
- ---
188
-
189
- ## Model Confidence
190
-
191
- **Composite Health Score**: {confidence}%
192
-
193
- Based on LSTM pattern analysis trained on 35+ equipment health histories.
194
-
195
- ---
196
-
197
- ## Maintenance Recommendations
198
-
199
- {recs_md}
200
 
201
  ---
202
 
203
- ## Next Steps
 
 
 
 
 
 
 
204
 
205
- {"**ACTION REQUIRED**: Contact maintenance planner to schedule emergency outage." if urgency == "IMMEDIATE" else "**PLAN AHEAD**: Coordinate with operations for next maintenance window." if urgency == "SCHEDULED" else "**MONITOR**: Review trends at next weekly reliability meeting."}
 
206
 
207
  ---
208
- **Model**: [rul-predictor-ccgt](https://huggingface.co/davidfertube/rul-predictor-ccgt) (LSTM Regression)
209
- **Dataset**: [ccgt-health-history](https://huggingface.co/datasets/davidfertube/ccgt-health-history)
210
  """
211
 
212
- def analyze_csv(file):
213
- """Analyze uploaded CSV file with health data."""
214
- if file is None:
215
- return "Please upload a CSV file with equipment health data."
216
-
217
- try:
218
- import pandas as pd
219
- df = pd.read_csv(file.name)
220
-
221
- # Check for required columns
222
- missing = set(FEATURE_COLUMNS) - set(df.columns)
223
- if missing:
224
- return f"Missing columns: {', '.join(missing)}\n\nExpected: {', '.join(FEATURE_COLUMNS)}"
225
-
226
- # Analyze last row (most recent reading)
227
- latest = df[FEATURE_COLUMNS].iloc[-1]
228
- return analyze_equipment(*[latest[col] for col in FEATURE_COLUMNS])
229
- except ImportError:
230
- return "CSV analysis requires pandas. Please enter values manually."
231
- except Exception as e:
232
- return f"Error processing file: {str(e)}"
233
-
234
- def load_demo_1():
235
- return [DEMO_1_HEALTHY[col] for col in FEATURE_COLUMNS]
236
-
237
- def load_demo_2():
238
- return [DEMO_2_DEGRADED[col] for col in FEATURE_COLUMNS]
239
-
240
- # ============================================================
241
- # GRADIO INTERFACE
242
- # ============================================================
243
-
244
- with gr.Blocks(theme=gr.themes.Soft(), title="Predictive Agent") as demo:
245
-
246
- gr.Markdown("""
247
- # Predictive Agent
248
- **LSTM-Based RUL Prediction for CCGT Equipment**
249
-
250
- Predict Remaining Useful Life for Combined Cycle Gas Turbine equipment.
251
- Click a demo below to see immediate results, or enter your own equipment data.
252
-
253
- ---
254
- """)
255
-
256
- with gr.Row():
257
- demo1_btn = gr.Button("Demo: Healthy Equipment", variant="secondary", size="lg")
258
- demo2_btn = gr.Button("Demo: Degraded Equipment", variant="primary", size="lg")
259
-
260
- gr.Markdown("---")
261
-
262
- with gr.Row():
263
- with gr.Column(scale=1):
264
- gr.Markdown("### Equipment Health Inputs")
265
-
266
- health_index = gr.Number(label="Health Index (%)", value=85.0, minimum=0, maximum=100)
267
- vibration = gr.Number(label="Vibration (in/s)", value=0.18, minimum=0, maximum=1)
268
- heat_rate_delta = gr.Number(label="Heat Rate Delta (%)", value=2.5, minimum=0, maximum=15)
269
- operating_hours = gr.Number(label="Operating Hours", value=52000)
270
- start_count = gr.Number(label="Start Count", value=950)
271
-
272
- predict_btn = gr.Button("Predict RUL", variant="primary", size="lg")
273
-
274
- with gr.Column(scale=1):
275
- gr.Markdown("### Prediction Results")
276
- output = gr.Markdown()
277
-
278
- with gr.Accordion("Upload Health History CSV", open=False):
279
- file_upload = gr.File(label="Upload Equipment Health CSV", file_types=[".csv"])
280
- upload_btn = gr.Button("Analyze Upload")
281
- gr.Markdown("""
282
- **Expected columns**: health_index, vibration, heat_rate_delta, operating_hours, start_count
283
-
284
- For best results, upload historical data. Latest row will be analyzed.
285
- """)
286
-
287
- gr.Markdown("""
288
- ---
289
- ### How It Works
290
- ```
291
- Health Metrics -> Sequence Generation -> LSTM Inference -> RUL Estimate -> Maintenance Plan
292
- ```
293
-
294
- **Key Metrics**: Health Index (overall condition), Vibration (mechanical state),
295
- Heat Rate Delta (thermal efficiency), Operating Hours, Start Cycles
296
-
297
- **Resources**: [Model](https://huggingface.co/davidfertube/rul-predictor-ccgt) |
298
- [Dataset](https://huggingface.co/datasets/davidfertube/ccgt-health-history) |
299
- [GitHub](https://github.com/davidfertube/predictive-agent) |
300
- [Portfolio](https://davidfernandez.dev)
301
-
302
- *Built by David Fernandez - Industrial AI Engineer*
303
- """)
304
-
305
- # Event handlers
306
- predict_btn.click(
307
- fn=analyze_equipment,
308
- inputs=[health_index, vibration, heat_rate_delta, operating_hours, start_count],
309
- outputs=output,
310
- api_name="predict"
311
- )
312
-
313
- demo1_btn.click(fn=load_demo_1, outputs=[health_index, vibration, heat_rate_delta,
314
- operating_hours, start_count],
315
- api_name="demo_healthy")
316
- demo2_btn.click(fn=load_demo_2, outputs=[health_index, vibration, heat_rate_delta,
317
- operating_hours, start_count],
318
- api_name="demo_degraded")
319
-
320
- upload_btn.click(fn=analyze_csv, inputs=file_upload, outputs=output, api_name="analyze_csv")
321
 
322
- demo.queue()
323
- demo.launch()
 
1
  """
2
  Predictive Agent - LSTM-Based RUL Prediction
3
+ Predicts Remaining Useful Life for CCGT equipment.
 
 
 
 
4
  Author: David Fernandez - Industrial AI Engineer
5
  """
6
 
7
  import gradio as gr
 
 
 
 
 
 
 
 
8
 
9
+ # Thresholds
10
  THRESHOLDS = {
11
+ 'health_index': (70, 40), # good, warning (higher is better)
12
+ 'vibration': (0.3, 0.5), # good, warning (lower is better)
13
+ 'heat_rate_delta': (4, 8), # good, warning (lower is better)
 
 
14
  }
15
 
16
+ def predict(health_index, vibration, heat_rate_delta, operating_hours, start_count):
17
+ """Predict Remaining Useful Life."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ # Normalize factors (0-1 scale)
20
+ hi_factor = health_index / 100
21
+ vib_factor = 1 - min(vibration / 1.0, 1)
22
+ hr_factor = 1 - min(heat_rate_delta / 15, 1)
23
+ hours_factor = 1 - min(operating_hours / 80000, 1)
24
+ starts_factor = 1 - min(start_count / 1500, 1)
25
 
26
+ # Weighted RUL calculation
27
+ composite = (hi_factor * 0.35 + vib_factor * 0.25 + hr_factor * 0.20 +
28
+ hours_factor * 0.12 + starts_factor * 0.08)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
+ rul = int(composite * 200)
 
 
 
 
 
 
 
31
 
32
+ # Urgency
33
+ if rul < 30:
34
+ urgency = "CRITICAL"
35
+ action = "Schedule emergency maintenance within 48 hours"
36
+ elif rul < 100:
 
37
  urgency = "SCHEDULED"
38
+ action = "Plan maintenance in next available window"
 
39
  else:
40
  urgency = "ROUTINE"
41
+ action = "Continue normal monitoring"
 
42
 
43
+ # Status checks
44
+ def status(val, good, warn, lower_is_better=True):
45
+ if lower_is_better:
46
+ return "OK" if val <= good else "WARNING" if val <= warn else "CRITICAL"
47
+ return "OK" if val >= good else "WARNING" if val >= warn else "CRITICAL"
 
 
 
48
 
49
+ hi_status = status(health_index, 70, 40, lower_is_better=False)
50
+ vib_status = status(vibration, 0.3, 0.5)
51
+ hr_status = status(heat_rate_delta, 4, 8)
 
 
 
52
 
53
+ # Recommendations
54
+ recs = []
 
 
55
  if health_index < 60:
56
+ recs.append("1. **Hot Gas Path Inspection** - Health index degraded")
57
  if vibration > 0.4:
58
+ recs.append("2. **Bearing Analysis** - Elevated vibration")
59
  if heat_rate_delta > 6:
60
+ recs.append("3. **Compressor Wash** - Heat rate deviation")
61
  if operating_hours > 60000:
62
+ recs.append("4. **Major Overhaul Planning** - High operating hours")
63
+ if not recs:
64
+ recs.append("Continue normal condition monitoring")
 
 
 
 
 
 
 
 
65
 
66
+ return f"""# RUL Prediction
 
67
 
68
+ ## Remaining Useful Life: **{rul} cycles**
 
69
 
70
  ## Urgency: {urgency}
71
+ {action}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
  ---
74
 
75
+ ## Equipment Status
76
+ | Parameter | Value | Status |
77
+ |-----------|-------|--------|
78
+ | Health Index | {health_index}% | {hi_status} |
79
+ | Vibration | {vibration} in/s | {vib_status} |
80
+ | Heat Rate Delta | {heat_rate_delta}% | {hr_status} |
81
+ | Operating Hours | {int(operating_hours):,} | - |
82
+ | Start Count | {int(start_count):,} | - |
83
 
84
+ ## Recommendations
85
+ {chr(10).join(recs)}
86
 
87
  ---
88
+ *Model: [rul-predictor-ccgt](https://huggingface.co/davidfertube/rul-predictor-ccgt)*
 
89
  """
90
 
91
+ demo = gr.Interface(
92
+ fn=predict,
93
+ inputs=[
94
+ gr.Number(label="Health Index (%)", value=85, minimum=0, maximum=100),
95
+ gr.Number(label="Vibration (in/s)", value=0.18, minimum=0, maximum=1),
96
+ gr.Number(label="Heat Rate Delta (%)", value=2.5, minimum=0, maximum=15),
97
+ gr.Number(label="Operating Hours", value=52000),
98
+ gr.Number(label="Start Count", value=950),
99
+ ],
100
+ outputs=gr.Markdown(),
101
+ title="Predictive Agent",
102
+ description="""**LSTM-Based RUL Prediction for CCGT Equipment**
103
+
104
+ Predict Remaining Useful Life to optimize maintenance scheduling.
105
+
106
+ Click an example below to test.""",
107
+ examples=[
108
+ [96.5, 0.14, 1.2, 48500, 920], # Healthy
109
+ [42.3, 0.48, 8.5, 68000, 1180], # Degraded
110
+ ],
111
+ cache_examples=False,
112
+ article="""
113
+ ## How It Works
114
+ ```
115
+ Health Metrics → LSTM Inference → RUL Estimate → Maintenance Plan
116
+ ```
117
+
118
+ **Resources**: [Model](https://huggingface.co/davidfertube/rul-predictor-ccgt) | [Dataset](https://huggingface.co/datasets/davidfertube/ccgt-health-history) | [Portfolio](https://davidfernandez.dev)
119
+
120
+ *Built by David Fernandez - Industrial AI Engineer*
121
+ """
122
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
+ if __name__ == "__main__":
125
+ demo.launch()