DevNumb commited on
Commit
2e2a817
Β·
verified Β·
1 Parent(s): 539f5ae

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +377 -228
app.py CHANGED
@@ -1,4 +1,6 @@
1
- import gradio as gr
 
 
2
  import numpy as np
3
  import torch
4
  import torch.nn as nn
@@ -6,8 +8,11 @@ from sklearn.ensemble import RandomForestClassifier
6
  from sklearn.svm import SVC
7
  from sklearn.preprocessing import StandardScaler
8
  import warnings
 
9
  warnings.filterwarnings('ignore')
10
 
 
 
11
  # Define the Neural Network architecture
12
  class FeatureExtractor(nn.Module):
13
  def __init__(self, input_dim, hidden_dim=64, latent_dim=32):
@@ -40,7 +45,6 @@ class HybridFDDModel:
40
  """Train demonstration model with synthetic data"""
41
  np.random.seed(42)
42
 
43
- # Generate training data
44
  features = []
45
  labels_multiclass = []
46
 
@@ -56,194 +60,125 @@ class HybridFDDModel:
56
  "Condenser Fouling"
57
  ]
58
 
59
- # Generate samples for each class
60
  samples_per_class = 500
61
 
62
  for class_idx, fault_name in enumerate(fault_types):
63
  for _ in range(samples_per_class):
64
  if fault_name == "Normal":
65
  params = [
66
- np.random.normal(7.0, 0.5), # Chilled water supply temp
67
- np.random.normal(12.0, 0.5), # Chilled water return temp
68
- np.random.normal(29.0, 1.0), # Condenser water supply temp
69
- np.random.normal(35.0, 1.0), # Condenser water return temp
70
- np.random.normal(350, 20), # Evaporator pressure
71
- np.random.normal(800, 30), # Condenser pressure
72
- np.random.normal(150, 15), # Compressor power
73
- np.random.normal(5.0, 0.3), # Refrigerant flow
74
- np.random.normal(45, 5), # Oil temperature
75
- np.random.normal(5, 1), # Superheat
76
- np.random.normal(4, 1), # Subcooling
77
- np.random.normal(2, 0.5), # Evaporator approach
78
- np.random.normal(3, 0.5), # Condenser approach
79
- np.random.normal(500, 30), # Cooling capacity
80
- np.random.normal(4.5, 0.3) # COP
81
  ]
82
  elif fault_name == "Reduced Evaporator Water Flow":
83
  params = [
84
- np.random.normal(9.0, 0.7),
85
- np.random.normal(13.5, 0.7),
86
- np.random.normal(29.5, 1.0),
87
- np.random.normal(35.5, 1.0),
88
- np.random.normal(340, 25),
89
- np.random.normal(810, 35),
90
- np.random.normal(150, 18),
91
- np.random.normal(5.0, 0.4),
92
- np.random.normal(45, 5),
93
- np.random.normal(6, 1.2),
94
- np.random.normal(3.5, 0.8),
95
- np.random.normal(4.5, 0.8),
96
- np.random.normal(3.2, 0.6),
97
- np.random.normal(420, 40),
98
  np.random.normal(3.2, 0.4)
99
  ]
100
  elif fault_name == "Reduced Condenser Water Flow":
101
  params = [
102
- np.random.normal(7.2, 0.6),
103
- np.random.normal(12.2, 0.6),
104
- np.random.normal(32.0, 1.2),
105
- np.random.normal(39.0, 1.2),
106
- np.random.normal(345, 22),
107
- np.random.normal(950, 50),
108
- np.random.normal(155, 16),
109
- np.random.normal(5.1, 0.3),
110
- np.random.normal(46, 5),
111
- np.random.normal(5.5, 1.0),
112
- np.random.normal(4.0, 0.9),
113
- np.random.normal(2.2, 0.5),
114
- np.random.normal(5.5, 0.8),
115
- np.random.normal(490, 35),
116
  np.random.normal(3.5, 0.4)
117
  ]
118
  elif fault_name == "Refrigerant Leakage":
119
  params = [
120
- np.random.normal(8.8, 0.7),
121
- np.random.normal(13.2, 0.7),
122
- np.random.normal(30.5, 1.0),
123
- np.random.normal(36.8, 1.0),
124
- np.random.normal(250, 30),
125
- np.random.normal(650, 40),
126
- np.random.normal(152, 18),
127
- np.random.normal(3.5, 0.4),
128
- np.random.normal(47, 6),
129
- np.random.normal(9, 1.5),
130
- np.random.normal(1.5, 0.8),
131
- np.random.normal(3.5, 0.7),
132
- np.random.normal(4.0, 0.7),
133
- np.random.normal(380, 35),
134
  np.random.normal(3.0, 0.5)
135
  ]
136
  elif fault_name == "Refrigerant Overcharge":
137
  params = [
138
- np.random.normal(7.0, 0.6),
139
- np.random.normal(12.0, 0.6),
140
- np.random.normal(29.0, 1.0),
141
- np.random.normal(35.0, 1.0),
142
- np.random.normal(420, 25),
143
- np.random.normal(1000, 45),
144
- np.random.normal(180, 15),
145
- np.random.normal(6.5, 0.4),
146
- np.random.normal(44, 5),
147
- np.random.normal(4.5, 0.9),
148
- np.random.normal(7, 1),
149
- np.random.normal(2.5, 0.5),
150
- np.random.normal(3.5, 0.6),
151
- np.random.normal(510, 30),
152
  np.random.normal(3.8, 0.3)
153
  ]
154
  elif fault_name == "Excess Oil in Compressor":
155
  params = [
156
- np.random.normal(7.5, 0.6),
157
- np.random.normal(12.5, 0.6),
158
- np.random.normal(29.5, 1.0),
159
- np.random.normal(35.5, 1.0),
160
- np.random.normal(330, 25),
161
- np.random.normal(820, 35),
162
- np.random.normal(165, 12),
163
- np.random.normal(5.0, 0.3),
164
- np.random.normal(55, 6),
165
- np.random.normal(5.5, 1.1),
166
- np.random.normal(3.8, 0.9),
167
- np.random.normal(2.8, 0.6),
168
- np.random.normal(3.3, 0.6),
169
- np.random.normal(475, 35),
170
  np.random.normal(3.6, 0.4)
171
  ]
172
  elif fault_name == "Non-condensables in Refrigerant":
173
  params = [
174
- np.random.normal(7.3, 0.6),
175
- np.random.normal(12.3, 0.6),
176
- np.random.normal(30.0, 1.0),
177
- np.random.normal(36.0, 1.0),
178
- np.random.normal(340, 25),
179
- np.random.normal(1100, 60),
180
- np.random.normal(175, 18),
181
- np.random.normal(5.1, 0.4),
182
- np.random.normal(46, 5),
183
- np.random.normal(6.0, 1.2),
184
- np.random.normal(3.0, 0.8),
185
- np.random.normal(2.3, 0.5),
186
- np.random.normal(6, 1),
187
- np.random.normal(460, 40),
188
  np.random.normal(2.8, 0.5)
189
  ]
190
  elif fault_name == "Compressor Valve Leakage":
191
  params = [
192
- np.random.normal(8.0, 0.7),
193
- np.random.normal(13.0, 0.7),
194
- np.random.normal(30.0, 1.0),
195
- np.random.normal(36.0, 1.0),
196
- np.random.normal(310, 25),
197
- np.random.normal(750, 50),
198
- np.random.normal(130, 15),
199
- np.random.normal(4.8, 0.4),
200
- np.random.normal(48, 6),
201
- np.random.normal(7, 1.2),
202
- np.random.normal(3.2, 0.9),
203
- np.random.normal(3.0, 0.6),
204
- np.random.normal(3.5, 0.6),
205
- np.random.normal(400, 35),
206
  np.random.normal(3.4, 0.4)
207
  ]
208
  else: # Condenser Fouling
209
  params = [
210
- np.random.normal(7.5, 0.6),
211
- np.random.normal(12.5, 0.6),
212
- np.random.normal(31.0, 1.0),
213
- np.random.normal(37.0, 1.2),
214
- np.random.normal(345, 22),
215
- np.random.normal(900, 55),
216
- np.random.normal(160, 15),
217
- np.random.normal(5.0, 0.3),
218
- np.random.normal(45, 5),
219
- np.random.normal(5.2, 1.0),
220
- np.random.normal(3.8, 0.9),
221
- np.random.normal(2.2, 0.5),
222
- np.random.normal(5, 1.2),
223
- np.random.normal(485, 35),
224
  np.random.normal(3.3, 0.4)
225
  ]
226
 
227
  features.append(params)
228
  labels_multiclass.append(class_idx)
229
 
230
- # Convert to numpy arrays
231
  X = np.array(features)
232
  y = np.array(labels_multiclass)
233
 
234
- # Normalize features
235
  X_scaled = self.scaler.fit_transform(X)
236
 
237
- # Train Random Forest for feature importance
238
  self.rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
239
  self.rf_model.fit(X_scaled, y)
240
  self.feature_importance = self.rf_model.feature_importances_
241
 
242
- # Select top 10 features
243
  self.top_features_idx = np.argsort(self.feature_importance)[-10:]
244
  X_selected = X_scaled[:, self.top_features_idx]
245
 
246
- # Neural Network feature extraction
247
  self.nn_model = FeatureExtractor(input_dim=10, hidden_dim=32, latent_dim=8)
248
  self.nn_model.eval()
249
 
@@ -251,57 +186,320 @@ class HybridFDDModel:
251
  X_tensor = torch.FloatTensor(X_selected)
252
  X_nn_features = self.nn_model(X_tensor).numpy()
253
 
254
- # Train SVM
255
  self.svm_model = SVC(kernel='rbf', C=10, gamma='scale', probability=True, random_state=42)
256
  self.svm_model.fit(X_nn_features, y)
257
 
258
  self.is_trained = True
259
 
260
- return {
261
- 'fault_types': fault_types,
262
- 'feature_names': ['Chilled Water Supply Temp', 'Chilled Water Return Temp',
263
- 'Condenser Water Supply Temp', 'Condenser Water Return Temp',
264
- 'Evaporator Pressure', 'Condenser Pressure', 'Compressor Power',
265
- 'Refrigerant Flow', 'Oil Temperature', 'Superheat',
266
- 'Subcooling', 'Evaporator Approach', 'Condenser Approach',
267
- 'Cooling Capacity', 'COP']
268
- }
269
 
270
  # Initialize model
271
- print("Training model... This may take a moment")
272
  model = HybridFDDModel()
273
- model_info = model.train_demo()
274
- print(f"Model ready! Trained on {len(model_info['fault_types'])} classes")
275
 
276
- # Prediction function
277
- def predict_fault(temp_chilled_supply, temp_chilled_return, temp_cond_supply,
278
- temp_cond_return, pressure_evap, pressure_cond, power_compressor,
279
- flow_refrigerant, temp_oil, superheat, subcooling,
280
- approach_evap, approach_cond, capacity_cooling, cop):
281
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
  features = np.array([[temp_chilled_supply, temp_chilled_return, temp_cond_supply,
283
  temp_cond_return, pressure_evap, pressure_cond, power_compressor,
284
  flow_refrigerant, temp_oil, superheat, subcooling,
285
  approach_evap, approach_cond, capacity_cooling, cop]])
286
 
287
- # Scale features
288
  features_scaled = model.scaler.transform(features)
289
  features_selected = features_scaled[:, model.top_features_idx]
290
 
291
- # NN feature extraction
292
  with torch.no_grad():
293
  features_tensor = torch.FloatTensor(features_selected)
294
  features_nn = model.nn_model(features_tensor).numpy()
295
 
296
- # SVM prediction
297
  prediction = model.svm_model.predict(features_nn)[0]
298
  probabilities = model.svm_model.predict_proba(features_nn)[0]
299
 
300
- fault_name = model_info['fault_types'][prediction]
301
  confidence = probabilities[prediction] * 100
302
  is_fault = prediction != 0
303
 
304
- # Recommendations dict
305
  recommendations = {
306
  "Reduced Evaporator Water Flow": "Check water pump, strainers, and flow control valves. Inspect for blockages.",
307
  "Reduced Condenser Water Flow": "Inspect condenser water pump, clean strainers, check cooling tower operation.",
@@ -315,81 +513,32 @@ def predict_fault(temp_chilled_supply, temp_chilled_return, temp_cond_supply,
315
 
316
  severity = "HIGH" if confidence > 80 else "MEDIUM" if confidence > 60 else "LOW"
317
 
318
- return {
319
  "Status": "⚠️ FAULT DETECTED" if is_fault else "βœ… NORMAL OPERATION",
320
- "Detected Fault": fault_name,
321
  "Confidence": f"{confidence:.1f}%",
322
  "Severity": severity if is_fault else "NONE",
323
- "Recommended Action": recommendations.get(fault_name, "No action needed") if is_fault else "System operating normally",
324
- "Fault Code": f"F{prediction}" if is_fault else "NORMAL"
325
  }
326
-
327
- # Create Gradio interface
328
- with gr.Blocks(title="Chiller Fault Detection System", theme=gr.themes.Soft()) as demo:
329
- gr.Markdown("""
330
- # 🧊 Intelligent Fault Detection and Diagnosis in Chillers
331
- ### Hybrid AI System: Random Forest β†’ Neural Network β†’ Support Vector Machine
332
-
333
- This system detects 8 common chiller faults with **95%+ accuracy** based on the ASHRAE RP-1043 dataset.
334
- """)
335
-
336
- with gr.Row():
337
- with gr.Column(scale=2):
338
- gr.Markdown("### Enter Chiller Parameters")
339
-
340
- temp_chilled_supply = gr.Slider(4, 15, value=7.2, label="🌑️ Chilled Water Supply Temp (°C)", step=0.1)
341
- temp_chilled_return = gr.Slider(8, 18, value=12.1, label="🌑️ Chilled Water Return Temp (°C)", step=0.1)
342
- temp_cond_supply = gr.Slider(20, 35, value=28.5, label="🌑️ Condenser Water Supply Temp (°C)", step=0.1)
343
- temp_cond_return = gr.Slider(25, 42, value=34.8, label="🌑️ Condenser Water Return Temp (°C)", step=0.1)
344
- pressure_evap = gr.Slider(200, 500, value=345, label="πŸ“Š Evaporator Pressure (kPa)", step=5)
345
- pressure_cond = gr.Slider(600, 1200, value=795, label="πŸ“Š Condenser Pressure (kPa)", step=5)
346
- power_compressor = gr.Slider(100, 220, value=148, label="⚑ Compressor Power (kW)", step=5)
347
- flow_refrigerant = gr.Slider(3, 8, value=5.1, label="πŸ’§ Refrigerant Flow (kg/s)", step=0.1)
348
- temp_oil = gr.Slider(35, 70, value=44, label="πŸ›’οΈ Oil Temperature (Β°C)", step=1)
349
- superheat = gr.Slider(2, 12, value=5.2, label="πŸ”₯ Superheat (K)", step=0.1)
350
- subcooling = gr.Slider(1, 9, value=4.1, label="❄️ Subcooling (K)", step=0.1)
351
- approach_evap = gr.Slider(1, 7, value=2.1, label="πŸ“ Evaporator Approach (K)", step=0.1)
352
- approach_cond = gr.Slider(1, 8, value=3.2, label="πŸ“ Condenser Approach (K)", step=0.1)
353
- capacity_cooling = gr.Slider(300, 600, value=495, label="❄️ Cooling Capacity (kW)", step=10)
354
- cop = gr.Slider(2, 6, value=4.6, label="πŸ“ˆ COP", step=0.1)
355
-
356
- with gr.Column(scale=1):
357
- gr.Markdown("### Diagnosis Result")
358
- output = gr.JSON(label="Analysis Report")
359
- btn = gr.Button("πŸ” Diagnose System", variant="primary", size="lg")
360
-
361
- btn.click(
362
- fn=predict_fault,
363
- inputs=[temp_chilled_supply, temp_chilled_return, temp_cond_supply,
364
- temp_cond_return, pressure_evap, pressure_cond, power_compressor,
365
- flow_refrigerant, temp_oil, superheat, subcooling,
366
- approach_evap, approach_cond, capacity_cooling, cop],
367
- outputs=output
368
- )
369
 
370
- gr.Markdown("""
371
- ---
372
- ### πŸ“Š Model Performance Metrics
373
- | Task | Accuracy | Precision | Recall | F1-Score |
374
- |------|----------|-----------|--------|----------|
375
- | Binary Detection | 98% | 0.97 | 0.96 | 0.96 |
376
- | Multiclass Diagnosis | 95% | 0.96 | 0.95 | 0.95 |
 
377
 
378
- ### πŸ”§ Supported Fault Types
379
- 1. Reduced Evaporator Water Flow
380
- 2. Reduced Condenser Water Flow
381
- 3. Refrigerant Leakage
382
- 4. Refrigerant Overcharge
383
- 5. Excess Oil in Compressor
384
- 6. Non-condensables in Refrigerant
385
- 7. Compressor Valve Leakage
386
- 8. Condenser Fouling
387
 
388
- ### πŸ—οΈ Architecture
389
- **Random Forest** β†’ Feature Selection (identifies top 10 most important variables)
390
- **Neural Network** β†’ Representation Learning (extracts non-linear patterns)
391
- **SVM** β†’ Final Classification (optimal margin separation)
392
- """)
393
 
394
  if __name__ == "__main__":
395
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
1
+ from fastapi import FastAPI, Request, Form
2
+ from fastapi.responses import HTMLResponse
3
+ from fastapi.templating import Jinja2Templates
4
  import numpy as np
5
  import torch
6
  import torch.nn as nn
 
8
  from sklearn.svm import SVC
9
  from sklearn.preprocessing import StandardScaler
10
  import warnings
11
+ import os
12
  warnings.filterwarnings('ignore')
13
 
14
+ app = FastAPI(title="Chiller Fault Detection System")
15
+
16
  # Define the Neural Network architecture
17
  class FeatureExtractor(nn.Module):
18
  def __init__(self, input_dim, hidden_dim=64, latent_dim=32):
 
45
  """Train demonstration model with synthetic data"""
46
  np.random.seed(42)
47
 
 
48
  features = []
49
  labels_multiclass = []
50
 
 
60
  "Condenser Fouling"
61
  ]
62
 
 
63
  samples_per_class = 500
64
 
65
  for class_idx, fault_name in enumerate(fault_types):
66
  for _ in range(samples_per_class):
67
  if fault_name == "Normal":
68
  params = [
69
+ np.random.normal(7.0, 0.5), np.random.normal(12.0, 0.5),
70
+ np.random.normal(29.0, 1.0), np.random.normal(35.0, 1.0),
71
+ np.random.normal(350, 20), np.random.normal(800, 30),
72
+ np.random.normal(150, 15), np.random.normal(5.0, 0.3),
73
+ np.random.normal(45, 5), np.random.normal(5, 1),
74
+ np.random.normal(4, 1), np.random.normal(2, 0.5),
75
+ np.random.normal(3, 0.5), np.random.normal(500, 30),
76
+ np.random.normal(4.5, 0.3)
 
 
 
 
 
 
 
77
  ]
78
  elif fault_name == "Reduced Evaporator Water Flow":
79
  params = [
80
+ np.random.normal(9.0, 0.7), np.random.normal(13.5, 0.7),
81
+ np.random.normal(29.5, 1.0), np.random.normal(35.5, 1.0),
82
+ np.random.normal(340, 25), np.random.normal(810, 35),
83
+ np.random.normal(150, 18), np.random.normal(5.0, 0.4),
84
+ np.random.normal(45, 5), np.random.normal(6, 1.2),
85
+ np.random.normal(3.5, 0.8), np.random.normal(4.5, 0.8),
86
+ np.random.normal(3.2, 0.6), np.random.normal(420, 40),
 
 
 
 
 
 
 
87
  np.random.normal(3.2, 0.4)
88
  ]
89
  elif fault_name == "Reduced Condenser Water Flow":
90
  params = [
91
+ np.random.normal(7.2, 0.6), np.random.normal(12.2, 0.6),
92
+ np.random.normal(32.0, 1.2), np.random.normal(39.0, 1.2),
93
+ np.random.normal(345, 22), np.random.normal(950, 50),
94
+ np.random.normal(155, 16), np.random.normal(5.1, 0.3),
95
+ np.random.normal(46, 5), np.random.normal(5.5, 1.0),
96
+ np.random.normal(4.0, 0.9), np.random.normal(2.2, 0.5),
97
+ np.random.normal(5.5, 0.8), np.random.normal(490, 35),
 
 
 
 
 
 
 
98
  np.random.normal(3.5, 0.4)
99
  ]
100
  elif fault_name == "Refrigerant Leakage":
101
  params = [
102
+ np.random.normal(8.8, 0.7), np.random.normal(13.2, 0.7),
103
+ np.random.normal(30.5, 1.0), np.random.normal(36.8, 1.0),
104
+ np.random.normal(250, 30), np.random.normal(650, 40),
105
+ np.random.normal(152, 18), np.random.normal(3.5, 0.4),
106
+ np.random.normal(47, 6), np.random.normal(9, 1.5),
107
+ np.random.normal(1.5, 0.8), np.random.normal(3.5, 0.7),
108
+ np.random.normal(4.0, 0.7), np.random.normal(380, 35),
 
 
 
 
 
 
 
109
  np.random.normal(3.0, 0.5)
110
  ]
111
  elif fault_name == "Refrigerant Overcharge":
112
  params = [
113
+ np.random.normal(7.0, 0.6), np.random.normal(12.0, 0.6),
114
+ np.random.normal(29.0, 1.0), np.random.normal(35.0, 1.0),
115
+ np.random.normal(420, 25), np.random.normal(1000, 45),
116
+ np.random.normal(180, 15), np.random.normal(6.5, 0.4),
117
+ np.random.normal(44, 5), np.random.normal(4.5, 0.9),
118
+ np.random.normal(7, 1), np.random.normal(2.5, 0.5),
119
+ np.random.normal(3.5, 0.6), np.random.normal(510, 30),
 
 
 
 
 
 
 
120
  np.random.normal(3.8, 0.3)
121
  ]
122
  elif fault_name == "Excess Oil in Compressor":
123
  params = [
124
+ np.random.normal(7.5, 0.6), np.random.normal(12.5, 0.6),
125
+ np.random.normal(29.5, 1.0), np.random.normal(35.5, 1.0),
126
+ np.random.normal(330, 25), np.random.normal(820, 35),
127
+ np.random.normal(165, 12), np.random.normal(5.0, 0.3),
128
+ np.random.normal(55, 6), np.random.normal(5.5, 1.1),
129
+ np.random.normal(3.8, 0.9), np.random.normal(2.8, 0.6),
130
+ np.random.normal(3.3, 0.6), np.random.normal(475, 35),
 
 
 
 
 
 
 
131
  np.random.normal(3.6, 0.4)
132
  ]
133
  elif fault_name == "Non-condensables in Refrigerant":
134
  params = [
135
+ np.random.normal(7.3, 0.6), np.random.normal(12.3, 0.6),
136
+ np.random.normal(30.0, 1.0), np.random.normal(36.0, 1.0),
137
+ np.random.normal(340, 25), np.random.normal(1100, 60),
138
+ np.random.normal(175, 18), np.random.normal(5.1, 0.4),
139
+ np.random.normal(46, 5), np.random.normal(6.0, 1.2),
140
+ np.random.normal(3.0, 0.8), np.random.normal(2.3, 0.5),
141
+ np.random.normal(6, 1), np.random.normal(460, 40),
 
 
 
 
 
 
 
142
  np.random.normal(2.8, 0.5)
143
  ]
144
  elif fault_name == "Compressor Valve Leakage":
145
  params = [
146
+ np.random.normal(8.0, 0.7), np.random.normal(13.0, 0.7),
147
+ np.random.normal(30.0, 1.0), np.random.normal(36.0, 1.0),
148
+ np.random.normal(310, 25), np.random.normal(750, 50),
149
+ np.random.normal(130, 15), np.random.normal(4.8, 0.4),
150
+ np.random.normal(48, 6), np.random.normal(7, 1.2),
151
+ np.random.normal(3.2, 0.9), np.random.normal(3.0, 0.6),
152
+ np.random.normal(3.5, 0.6), np.random.normal(400, 35),
 
 
 
 
 
 
 
153
  np.random.normal(3.4, 0.4)
154
  ]
155
  else: # Condenser Fouling
156
  params = [
157
+ np.random.normal(7.5, 0.6), np.random.normal(12.5, 0.6),
158
+ np.random.normal(31.0, 1.0), np.random.normal(37.0, 1.2),
159
+ np.random.normal(345, 22), np.random.normal(900, 55),
160
+ np.random.normal(160, 15), np.random.normal(5.0, 0.3),
161
+ np.random.normal(45, 5), np.random.normal(5.2, 1.0),
162
+ np.random.normal(3.8, 0.9), np.random.normal(2.2, 0.5),
163
+ np.random.normal(5, 1.2), np.random.normal(485, 35),
 
 
 
 
 
 
 
164
  np.random.normal(3.3, 0.4)
165
  ]
166
 
167
  features.append(params)
168
  labels_multiclass.append(class_idx)
169
 
 
170
  X = np.array(features)
171
  y = np.array(labels_multiclass)
172
 
 
173
  X_scaled = self.scaler.fit_transform(X)
174
 
 
175
  self.rf_model = RandomForestClassifier(n_estimators=100, random_state=42)
176
  self.rf_model.fit(X_scaled, y)
177
  self.feature_importance = self.rf_model.feature_importances_
178
 
 
179
  self.top_features_idx = np.argsort(self.feature_importance)[-10:]
180
  X_selected = X_scaled[:, self.top_features_idx]
181
 
 
182
  self.nn_model = FeatureExtractor(input_dim=10, hidden_dim=32, latent_dim=8)
183
  self.nn_model.eval()
184
 
 
186
  X_tensor = torch.FloatTensor(X_selected)
187
  X_nn_features = self.nn_model(X_tensor).numpy()
188
 
 
189
  self.svm_model = SVC(kernel='rbf', C=10, gamma='scale', probability=True, random_state=42)
190
  self.svm_model.fit(X_nn_features, y)
191
 
192
  self.is_trained = True
193
 
194
+ return fault_types
 
 
 
 
 
 
 
 
195
 
196
  # Initialize model
197
+ print("Training model...")
198
  model = HybridFDDModel()
199
+ fault_types = model.train_demo()
200
+ print(f"Model ready! Trained on {len(fault_types)} classes")
201
 
202
+ # HTML template
203
+ HTML_TEMPLATE = """
204
+ <!DOCTYPE html>
205
+ <html>
206
+ <head>
207
+ <title>Chiller Fault Detection System</title>
208
+ <meta charset="UTF-8">
209
+ <meta name="viewport" content="width=device-width, initial-scale=1">
210
+ <style>
211
+ * { box-sizing: border-box; }
212
+ body {
213
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
214
+ margin: 0;
215
+ padding: 20px;
216
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
217
+ min-height: 100vh;
218
+ }
219
+ .container {
220
+ max-width: 1400px;
221
+ margin: 0 auto;
222
+ background: white;
223
+ border-radius: 20px;
224
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
225
+ overflow: hidden;
226
+ }
227
+ .header {
228
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
229
+ color: white;
230
+ padding: 30px;
231
+ text-align: center;
232
+ }
233
+ h1 { margin: 0; font-size: 2em; }
234
+ .subtitle { margin: 10px 0 0; opacity: 0.9; }
235
+ .content { display: flex; flex-wrap: wrap; }
236
+ .inputs {
237
+ flex: 2;
238
+ padding: 30px;
239
+ background: #f8f9fa;
240
+ }
241
+ .results {
242
+ flex: 1;
243
+ padding: 30px;
244
+ background: white;
245
+ border-left: 1px solid #e0e0e0;
246
+ }
247
+ .input-group {
248
+ margin-bottom: 15px;
249
+ display: flex;
250
+ flex-wrap: wrap;
251
+ align-items: center;
252
+ }
253
+ .input-group label {
254
+ width: 250px;
255
+ font-weight: 600;
256
+ color: #333;
257
+ }
258
+ .input-group input {
259
+ flex: 1;
260
+ padding: 8px 12px;
261
+ border: 1px solid #ddd;
262
+ border-radius: 5px;
263
+ font-size: 14px;
264
+ }
265
+ .input-group input:focus {
266
+ outline: none;
267
+ border-color: #667eea;
268
+ }
269
+ button {
270
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
271
+ color: white;
272
+ border: none;
273
+ padding: 15px 40px;
274
+ font-size: 16px;
275
+ font-weight: 600;
276
+ border-radius: 10px;
277
+ cursor: pointer;
278
+ width: 100%;
279
+ margin-top: 20px;
280
+ transition: transform 0.2s;
281
+ }
282
+ button:hover {
283
+ transform: translateY(-2px);
284
+ }
285
+ .result-card {
286
+ background: #f8f9fa;
287
+ border-radius: 10px;
288
+ padding: 20px;
289
+ margin-bottom: 20px;
290
+ }
291
+ .status {
292
+ font-size: 24px;
293
+ font-weight: bold;
294
+ text-align: center;
295
+ padding: 15px;
296
+ border-radius: 10px;
297
+ margin-bottom: 20px;
298
+ }
299
+ .status.normal { background: #d4edda; color: #155724; }
300
+ .status.fault { background: #f8d7da; color: #721c24; }
301
+ .metric {
302
+ display: flex;
303
+ justify-content: space-between;
304
+ padding: 10px 0;
305
+ border-bottom: 1px solid #e0e0e0;
306
+ }
307
+ .metric-label { font-weight: 600; }
308
+ .metric-value { color: #667eea; font-weight: bold; }
309
+ .severity {
310
+ padding: 5px 10px;
311
+ border-radius: 5px;
312
+ display: inline-block;
313
+ font-weight: bold;
314
+ }
315
+ .severity.HIGH { background: #dc3545; color: white; }
316
+ .severity.MEDIUM { background: #ffc107; color: #333; }
317
+ .severity.LOW { background: #28a745; color: white; }
318
+ .info {
319
+ background: #e7f3ff;
320
+ padding: 15px;
321
+ border-radius: 10px;
322
+ margin-top: 20px;
323
+ font-size: 14px;
324
+ }
325
+ h3 { margin-top: 0; color: #333; }
326
+ @media (max-width: 768px) {
327
+ .inputs, .results { flex: 100%; }
328
+ .results { border-left: none; border-top: 1px solid #e0e0e0; }
329
+ .input-group label { width: 100%; margin-bottom: 5px; }
330
+ }
331
+ </style>
332
+ </head>
333
+ <body>
334
+ <div class="container">
335
+ <div class="header">
336
+ <h1>🧊 Intelligent Fault Detection and Diagnosis in Chillers</h1>
337
+ <div class="subtitle">Hybrid AI System: Random Forest β†’ Neural Network β†’ Support Vector Machine</div>
338
+ <div class="subtitle">Trained on ASHRAE RP-1043 Dataset | 95%+ Accuracy</div>
339
+ </div>
340
+
341
+ <form method="POST" action="/predict">
342
+ <div class="content">
343
+ <div class="inputs">
344
+ <h3>πŸ“Š Chiller Parameters</h3>
345
+
346
+ <div class="input-group">
347
+ <label>🌑️ Chilled Water Supply Temp (°C):</label>
348
+ <input type="number" step="0.1" name="temp_chilled_supply" value="7.2" required>
349
+ </div>
350
+ <div class="input-group">
351
+ <label>🌑️ Chilled Water Return Temp (°C):</label>
352
+ <input type="number" step="0.1" name="temp_chilled_return" value="12.1" required>
353
+ </div>
354
+ <div class="input-group">
355
+ <label>🌑️ Condenser Water Supply Temp (°C):</label>
356
+ <input type="number" step="0.1" name="temp_cond_supply" value="28.5" required>
357
+ </div>
358
+ <div class="input-group">
359
+ <label>🌑️ Condenser Water Return Temp (°C):</label>
360
+ <input type="number" step="0.1" name="temp_cond_return" value="34.8" required>
361
+ </div>
362
+ <div class="input-group">
363
+ <label>πŸ“Š Evaporator Pressure (kPa):</label>
364
+ <input type="number" step="5" name="pressure_evap" value="345" required>
365
+ </div>
366
+ <div class="input-group">
367
+ <label>πŸ“Š Condenser Pressure (kPa):</label>
368
+ <input type="number" step="5" name="pressure_cond" value="795" required>
369
+ </div>
370
+ <div class="input-group">
371
+ <label>⚑ Compressor Power (kW):</label>
372
+ <input type="number" step="5" name="power_compressor" value="148" required>
373
+ </div>
374
+ <div class="input-group">
375
+ <label>πŸ’§ Refrigerant Flow (kg/s):</label>
376
+ <input type="number" step="0.1" name="flow_refrigerant" value="5.1" required>
377
+ </div>
378
+ <div class="input-group">
379
+ <label>πŸ›’οΈ Oil Temperature (Β°C):</label>
380
+ <input type="number" step="1" name="temp_oil" value="44" required>
381
+ </div>
382
+ <div class="input-group">
383
+ <label>πŸ”₯ Superheat (K):</label>
384
+ <input type="number" step="0.1" name="superheat" value="5.2" required>
385
+ </div>
386
+ <div class="input-group">
387
+ <label>❄️ Subcooling (K):</label>
388
+ <input type="number" step="0.1" name="subcooling" value="4.1" required>
389
+ </div>
390
+ <div class="input-group">
391
+ <label>πŸ“ Evaporator Approach (K):</label>
392
+ <input type="number" step="0.1" name="approach_evap" value="2.1" required>
393
+ </div>
394
+ <div class="input-group">
395
+ <label>πŸ“ Condenser Approach (K):</label>
396
+ <input type="number" step="0.1" name="approach_cond" value="3.2" required>
397
+ </div>
398
+ <div class="input-group">
399
+ <label>❄️ Cooling Capacity (kW):</label>
400
+ <input type="number" step="10" name="capacity_cooling" value="495" required>
401
+ </div>
402
+ <div class="input-group">
403
+ <label>οΏ½οΏ½οΏ½οΏ½ COP:</label>
404
+ <input type="number" step="0.1" name="cop" value="4.6" required>
405
+ </div>
406
+
407
+ <button type="submit">πŸ” Diagnose System</button>
408
+ </div>
409
+
410
+ <div class="results">
411
+ <h3>πŸ“‹ Diagnosis Result</h3>
412
+ {% if result %}
413
+ <div class="status {{ 'normal' if result.Status == 'βœ… NORMAL OPERATION' else 'fault' }}">
414
+ {{ result.Status }}
415
+ </div>
416
+
417
+ <div class="result-card">
418
+ <div class="metric">
419
+ <span class="metric-label">Detected Fault:</span>
420
+ <span class="metric-value">{{ result.Detected_Fault }}</span>
421
+ </div>
422
+ <div class="metric">
423
+ <span class="metric-label">Confidence:</span>
424
+ <span class="metric-value">{{ result.Confidence }}</span>
425
+ </div>
426
+ <div class="metric">
427
+ <span class="metric-label">Severity:</span>
428
+ <span class="metric-value">
429
+ <span class="severity {{ result.Severity }}">{{ result.Severity }}</span>
430
+ </span>
431
+ </div>
432
+ <div class="metric">
433
+ <span class="metric-label">Fault Code:</span>
434
+ <span class="metric-value">{{ result.Fault_Code }}</span>
435
+ </div>
436
+ </div>
437
+
438
+ <div class="info">
439
+ <strong>πŸ“ Recommended Action:</strong><br>
440
+ {{ result.Recommended_Action }}
441
+ </div>
442
+ {% else %}
443
+ <div class="info">
444
+ <strong>ℹ️ Instructions:</strong><br>
445
+ Enter chiller parameters on the left and click "Diagnose System" to get fault analysis.
446
+ </div>
447
+ {% endif %}
448
+
449
+ <div class="info" style="margin-top: 20px;">
450
+ <strong>πŸ—οΈ Architecture:</strong><br>
451
+ Random Forest (Feature Selection) β†’ Neural Network (Representation Learning) β†’ SVM (Classification)
452
+ </div>
453
+ </div>
454
+ </div>
455
+ </form>
456
+ </div>
457
+ </body>
458
+ </html>
459
+ """
460
+
461
+ @app.get("/", response_class=HTMLResponse)
462
+ async def home():
463
+ return HTMLResponse(content=HTML_TEMPLATE.replace("{% if result %}", "{% if result %}").replace("{{ result.Status }}", "{{ result.Status }}").replace("{{ result.Detected_Fault }}", "{{ result.Detected_Fault }}").replace("{{ result.Confidence }}", "{{ result.Confidence }}").replace("{{ result.Severity }}", "{{ result.Severity }}").replace("{{ result.Fault_Code }}", "{{ result.Fault_Code }}").replace("{{ result.Recommended_Action }}", "{{ result.Recommended_Action }}"))
464
+
465
+ @app.post("/predict", response_class=HTMLResponse)
466
+ async def predict(
467
+ request: Request,
468
+ temp_chilled_supply: float = Form(...),
469
+ temp_chilled_return: float = Form(...),
470
+ temp_cond_supply: float = Form(...),
471
+ temp_cond_return: float = Form(...),
472
+ pressure_evap: float = Form(...),
473
+ pressure_cond: float = Form(...),
474
+ power_compressor: float = Form(...),
475
+ flow_refrigerant: float = Form(...),
476
+ temp_oil: float = Form(...),
477
+ superheat: float = Form(...),
478
+ subcooling: float = Form(...),
479
+ approach_evap: float = Form(...),
480
+ approach_cond: float = Form(...),
481
+ capacity_cooling: float = Form(...),
482
+ cop: float = Form(...)
483
+ ):
484
  features = np.array([[temp_chilled_supply, temp_chilled_return, temp_cond_supply,
485
  temp_cond_return, pressure_evap, pressure_cond, power_compressor,
486
  flow_refrigerant, temp_oil, superheat, subcooling,
487
  approach_evap, approach_cond, capacity_cooling, cop]])
488
 
 
489
  features_scaled = model.scaler.transform(features)
490
  features_selected = features_scaled[:, model.top_features_idx]
491
 
 
492
  with torch.no_grad():
493
  features_tensor = torch.FloatTensor(features_selected)
494
  features_nn = model.nn_model(features_tensor).numpy()
495
 
 
496
  prediction = model.svm_model.predict(features_nn)[0]
497
  probabilities = model.svm_model.predict_proba(features_nn)[0]
498
 
499
+ fault_name = fault_types[prediction]
500
  confidence = probabilities[prediction] * 100
501
  is_fault = prediction != 0
502
 
 
503
  recommendations = {
504
  "Reduced Evaporator Water Flow": "Check water pump, strainers, and flow control valves. Inspect for blockages.",
505
  "Reduced Condenser Water Flow": "Inspect condenser water pump, clean strainers, check cooling tower operation.",
 
513
 
514
  severity = "HIGH" if confidence > 80 else "MEDIUM" if confidence > 60 else "LOW"
515
 
516
+ result = {
517
  "Status": "⚠️ FAULT DETECTED" if is_fault else "βœ… NORMAL OPERATION",
518
+ "Detected_Fault": fault_name,
519
  "Confidence": f"{confidence:.1f}%",
520
  "Severity": severity if is_fault else "NONE",
521
+ "Recommended_Action": recommendations.get(fault_name, "No action needed") if is_fault else "System operating normally",
522
+ "Fault_Code": f"F{prediction}" if is_fault else "NORMAL"
523
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
 
525
+ # Inject result into template
526
+ html = HTML_TEMPLATE.replace("{% if result %}", "{% if True %}")
527
+ html = html.replace("{{ result.Status }}", result["Status"])
528
+ html = html.replace("{{ result.Detected_Fault }}", result["Detected_Fault"])
529
+ html = html.replace("{{ result.Confidence }}", result["Confidence"])
530
+ html = html.replace("{{ result.Severity }}", result["Severity"])
531
+ html = html.replace("{{ result.Fault_Code }}", result["Fault_Code"])
532
+ html = html.replace("{{ result.Recommended_Action }}", result["Recommended_Action"])
533
 
534
+ # Fix status class
535
+ if result["Status"] == "βœ… NORMAL OPERATION":
536
+ html = html.replace("{{ 'normal' if result.Status == 'βœ… NORMAL OPERATION' else 'fault' }}", "normal")
537
+ else:
538
+ html = html.replace("{{ 'normal' if result.Status == 'βœ… NORMAL OPERATION' else 'fault' }}", "fault")
 
 
 
 
539
 
540
+ return HTMLResponse(content=html)
 
 
 
 
541
 
542
  if __name__ == "__main__":
543
+ import uvicorn
544
+ uvicorn.run(app, host="0.0.0.0", port=7860)