DevNumb commited on
Commit
ea59c0a
·
verified ·
1 Parent(s): cbcb440

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -54
app.py CHANGED
@@ -1,6 +1,6 @@
1
  # ============================================
2
  # YORK CHILLER OPTIMIZER API
3
- # Using NumPy 2.0.2 to match your model
4
  # ============================================
5
 
6
  import os
@@ -8,11 +8,13 @@ import sys
8
  import warnings
9
  warnings.filterwarnings('ignore')
10
 
11
- # Import with NumPy 2.0.2
12
  import numpy as np
13
  print(f"NumPy version: {np.__version__}")
14
 
15
  import pandas as pd
 
 
16
  from datetime import datetime
17
  from typing import List, Optional, Dict, Any
18
  from fastapi import FastAPI, HTTPException
@@ -64,7 +66,7 @@ def load_model_files():
64
  print(f" ❌ Missing: production_model.pkl")
65
  return False
66
 
67
- # Load model with joblib (should work with NumPy 2.0.2)
68
  try:
69
  model = joblib.load("production_model.pkl")
70
  print(f"\n✅ Model loaded successfully with joblib")
@@ -78,17 +80,8 @@ def load_model_files():
78
  print(f" Max depth: {model.max_depth}")
79
 
80
  except Exception as e:
81
- print(f" Error loading model with joblib: {e}")
82
-
83
- # Try with pickle as fallback
84
- try:
85
- print(" Trying with pickle...")
86
- with open("production_model.pkl", 'rb') as f:
87
- model = pickle.load(f)
88
- print(f"✅ Model loaded successfully with pickle")
89
- except Exception as e2:
90
- print(f"❌ Pickle also failed: {e2}")
91
- return False
92
 
93
  # Load scaler if exists
94
  if os.path.exists("scaler.pkl"):
@@ -97,34 +90,39 @@ def load_model_files():
97
  print(f"✅ Scaler loaded")
98
  except Exception as e:
99
  print(f"⚠️ Could not load scaler: {e}")
100
- try:
101
- with open("scaler.pkl", 'rb') as f:
102
- scaler = pickle.load(f)
103
- print(f"✅ Scaler loaded with pickle")
104
- except:
105
- pass
106
 
107
  # Load feature names from features.pkl
108
  if os.path.exists("features.pkl"):
109
  try:
110
- feature_names = joblib.load("features.pkl")
111
- if isinstance(feature_names, list):
112
- print(f"✅ Features loaded from features.pkl: {len(feature_names)} features")
 
 
 
 
 
 
 
 
 
113
  print(f" First 3 features: {feature_names[:3]}")
114
  except Exception as e:
115
  print(f"⚠️ Could not load features.pkl: {e}")
116
- try:
117
- with open("features.pkl", 'rb') as f:
118
- feature_names = pickle.load(f)
119
- print(f"✅ Features loaded with pickle: {len(feature_names)} features")
120
- except:
121
- pass
122
 
123
  # Use default feature names if none loaded
124
  if feature_names is None:
125
  feature_names = EXPECTED_FEATURES
126
  print(f"✅ Using default feature names: {len(feature_names)} features")
127
 
 
 
 
 
 
 
 
128
  return True
129
 
130
  # Load model on startup
@@ -133,11 +131,11 @@ load_success = load_model_files()
133
  if model:
134
  print(f"\n📊 Model Status: ✅ ONLINE")
135
  print(f" NumPy version: {np.__version__}")
136
- print(f" Features: {len(feature_names) if feature_names else 'Unknown'}")
 
137
  print(f" Scaler: {'✅' if scaler else '❌'}")
138
  else:
139
  print(f"\n📊 Model Status: ❌ OFFLINE")
140
- print(f" NumPy version: {np.__version__}")
141
 
142
  # ============================================
143
  # REQUEST MODELS
@@ -189,8 +187,10 @@ class OptimizeResponse(BaseModel):
189
  # PREDICTION FUNCTIONS
190
  # ============================================
191
 
192
- def prepare_features(input_data: ChillerInput) -> np.ndarray:
193
- """Prepare features in the exact order expected by the model"""
 
 
194
 
195
  # Create feature array with exact order
196
  features = np.array([[
@@ -206,24 +206,16 @@ def prepare_features(input_data: ChillerInput) -> np.ndarray:
206
  input_data.day_of_week,
207
  input_data.month,
208
  input_data.day_of_year
209
- ]])
210
 
211
  # Apply scaler if available
212
  if scaler is not None:
213
  try:
214
  features = scaler.transform(features)
215
- print(f" Applied scaler transformation")
216
  except Exception as e:
217
  print(f" ⚠️ Scaler transform failed: {e}")
218
 
219
- return features
220
-
221
- def predict_kw_per_tr(input_data: ChillerInput) -> float:
222
- """Predict Combined_Kw_per_TR using the loaded model"""
223
- if model is None:
224
- raise ValueError("Model not loaded")
225
-
226
- features = prepare_features(input_data)
227
  prediction = model.predict(features)[0]
228
 
229
  # Clip to realistic range
@@ -253,9 +245,10 @@ def optimize_chw_setpoint(input_data: ChillerInput) -> tuple:
253
  best_sp = current_sp
254
 
255
  for sp in test_setpoints:
256
- # Create copy with new setpoint
257
- test_input = ChillerInput(**input_data.dict())
258
- test_input.current_chw_setpoint_c = sp
 
259
 
260
  try:
261
  kw = predict_kw_per_tr(test_input)
@@ -263,7 +256,6 @@ def optimize_chw_setpoint(input_data: ChillerInput) -> tuple:
263
  best_kw = kw
264
  best_sp = sp
265
  except Exception as e:
266
- print(f" Error testing setpoint {sp}: {e}")
267
  continue
268
 
269
  return best_sp, best_kw
@@ -275,6 +267,8 @@ def optimize_chw_setpoint(input_data: ChillerInput) -> tuple:
275
  @app.get("/")
276
  async def root():
277
  """Root endpoint with API information"""
 
 
278
  return {
279
  "service": "York Chiller Energy Optimizer",
280
  "model_type": "Random Forest Regressor",
@@ -282,10 +276,10 @@ async def root():
282
  "status": "online" if model is not None else "model_not_loaded",
283
  "model_info": {
284
  "loaded": model is not None,
285
- "features": feature_names if feature_names else EXPECTED_FEATURES,
286
- "feature_count": len(feature_names) if feature_names else len(EXPECTED_FEATURES),
287
  "scaler_loaded": scaler is not None,
288
- "numpy_version": np.__version__
 
289
  },
290
  "endpoints": {
291
  "/": "This information",
@@ -305,13 +299,16 @@ async def root():
305
  @app.get("/health")
306
  async def health():
307
  """Health check endpoint"""
 
 
308
  return {
309
  "status": "healthy" if model is not None else "degraded",
310
  "model_loaded": model is not None,
311
  "model_type": type(model).__name__ if model else None,
312
- "feature_count": len(feature_names) if feature_names else len(EXPECTED_FEATURES),
313
  "scaler_loaded": scaler is not None,
314
- "numpy_version": np.__version__
 
315
  }
316
 
317
  @app.post("/predict", response_model=PredictionResponse)
@@ -324,11 +321,13 @@ async def predict_endpoint(input_data: ChillerInput):
324
  kw_per_tr = predict_kw_per_tr(input_data)
325
  rating = get_efficiency_rating(kw_per_tr)
326
 
 
 
327
  return PredictionResponse(
328
  status="success",
329
  kw_per_tr=round(kw_per_tr, 4),
330
  efficiency_rating=rating,
331
- features_used=len(feature_names) if feature_names else len(EXPECTED_FEATURES),
332
  model_type="RandomForestRegressor",
333
  timestamp=datetime.now().isoformat()
334
  )
@@ -364,7 +363,7 @@ async def optimize_endpoint(input_data: ChillerInput):
364
  recommended_value=f"{optimal_sp:.1f}°C",
365
  expected_savings=f"{savings_pct:.1f}%",
366
  priority="HIGH" if savings_pct > 5 else "MEDIUM",
367
- operator_action=f"Adjust CHW setpoint from {current_sp:.1f}°C to {optimal_sp:.1f}°C"
368
  ))
369
 
370
  # Load-based recommendations
 
1
  # ============================================
2
  # YORK CHILLER OPTIMIZER API
3
+ # Compatible with NumPy 2.0.2 and pandas 2.2.3
4
  # ============================================
5
 
6
  import os
 
8
  import warnings
9
  warnings.filterwarnings('ignore')
10
 
11
+ # Import libraries
12
  import numpy as np
13
  print(f"NumPy version: {np.__version__}")
14
 
15
  import pandas as pd
16
+ print(f"Pandas version: {pd.__version__}")
17
+
18
  from datetime import datetime
19
  from typing import List, Optional, Dict, Any
20
  from fastapi import FastAPI, HTTPException
 
66
  print(f" ❌ Missing: production_model.pkl")
67
  return False
68
 
69
+ # Load model with joblib
70
  try:
71
  model = joblib.load("production_model.pkl")
72
  print(f"\n✅ Model loaded successfully with joblib")
 
80
  print(f" Max depth: {model.max_depth}")
81
 
82
  except Exception as e:
83
+ print(f"⚠️ Error loading model with joblib: {e}")
84
+ return False
 
 
 
 
 
 
 
 
 
85
 
86
  # Load scaler if exists
87
  if os.path.exists("scaler.pkl"):
 
90
  print(f"✅ Scaler loaded")
91
  except Exception as e:
92
  print(f"⚠️ Could not load scaler: {e}")
 
 
 
 
 
 
93
 
94
  # Load feature names from features.pkl
95
  if os.path.exists("features.pkl"):
96
  try:
97
+ feature_data = joblib.load("features.pkl")
98
+ # Convert to list if it's a pandas Series or other type
99
+ if hasattr(feature_data, 'tolist'):
100
+ feature_names = feature_data.tolist()
101
+ elif isinstance(feature_data, (list, tuple)):
102
+ feature_names = list(feature_data)
103
+ elif hasattr(feature_data, 'values'):
104
+ feature_names = list(feature_data.values)
105
+ else:
106
+ feature_names = list(feature_data)
107
+ print(f"✅ Features loaded from features.pkl: {len(feature_names)} features")
108
+ if len(feature_names) > 0:
109
  print(f" First 3 features: {feature_names[:3]}")
110
  except Exception as e:
111
  print(f"⚠️ Could not load features.pkl: {e}")
112
+ feature_names = None
 
 
 
 
 
113
 
114
  # Use default feature names if none loaded
115
  if feature_names is None:
116
  feature_names = EXPECTED_FEATURES
117
  print(f"✅ Using default feature names: {len(feature_names)} features")
118
 
119
+ # Ensure feature_names is a list
120
+ if not isinstance(feature_names, list):
121
+ if hasattr(feature_names, 'tolist'):
122
+ feature_names = feature_names.tolist()
123
+ else:
124
+ feature_names = list(feature_names)
125
+
126
  return True
127
 
128
  # Load model on startup
 
131
  if model:
132
  print(f"\n📊 Model Status: ✅ ONLINE")
133
  print(f" NumPy version: {np.__version__}")
134
+ print(f" Pandas version: {pd.__version__}")
135
+ print(f" Features: {len(feature_names) if feature_names else 0}")
136
  print(f" Scaler: {'✅' if scaler else '❌'}")
137
  else:
138
  print(f"\n📊 Model Status: ❌ OFFLINE")
 
139
 
140
  # ============================================
141
  # REQUEST MODELS
 
187
  # PREDICTION FUNCTIONS
188
  # ============================================
189
 
190
+ def predict_kw_per_tr(input_data: ChillerInput) -> float:
191
+ """Predict Combined_Kw_per_TR using the loaded model"""
192
+ if model is None:
193
+ raise ValueError("Model not loaded")
194
 
195
  # Create feature array with exact order
196
  features = np.array([[
 
206
  input_data.day_of_week,
207
  input_data.month,
208
  input_data.day_of_year
209
+ ]], dtype=np.float64)
210
 
211
  # Apply scaler if available
212
  if scaler is not None:
213
  try:
214
  features = scaler.transform(features)
 
215
  except Exception as e:
216
  print(f" ⚠️ Scaler transform failed: {e}")
217
 
218
+ # Predict
 
 
 
 
 
 
 
219
  prediction = model.predict(features)[0]
220
 
221
  # Clip to realistic range
 
245
  best_sp = current_sp
246
 
247
  for sp in test_setpoints:
248
+ # Create copy with new setpoint using dict
249
+ input_dict = input_data.dict()
250
+ input_dict['current_chw_setpoint_c'] = sp
251
+ test_input = ChillerInput(**input_dict)
252
 
253
  try:
254
  kw = predict_kw_per_tr(test_input)
 
256
  best_kw = kw
257
  best_sp = sp
258
  except Exception as e:
 
259
  continue
260
 
261
  return best_sp, best_kw
 
267
  @app.get("/")
268
  async def root():
269
  """Root endpoint with API information"""
270
+ feature_count = len(feature_names) if feature_names and isinstance(feature_names, list) else len(EXPECTED_FEATURES)
271
+
272
  return {
273
  "service": "York Chiller Energy Optimizer",
274
  "model_type": "Random Forest Regressor",
 
276
  "status": "online" if model is not None else "model_not_loaded",
277
  "model_info": {
278
  "loaded": model is not None,
279
+ "features": feature_count,
 
280
  "scaler_loaded": scaler is not None,
281
+ "numpy_version": np.__version__,
282
+ "pandas_version": pd.__version__
283
  },
284
  "endpoints": {
285
  "/": "This information",
 
299
  @app.get("/health")
300
  async def health():
301
  """Health check endpoint"""
302
+ feature_count = len(feature_names) if feature_names and isinstance(feature_names, list) else len(EXPECTED_FEATURES)
303
+
304
  return {
305
  "status": "healthy" if model is not None else "degraded",
306
  "model_loaded": model is not None,
307
  "model_type": type(model).__name__ if model else None,
308
+ "feature_count": feature_count,
309
  "scaler_loaded": scaler is not None,
310
+ "numpy_version": np.__version__,
311
+ "pandas_version": pd.__version__
312
  }
313
 
314
  @app.post("/predict", response_model=PredictionResponse)
 
321
  kw_per_tr = predict_kw_per_tr(input_data)
322
  rating = get_efficiency_rating(kw_per_tr)
323
 
324
+ feature_count = len(feature_names) if feature_names and isinstance(feature_names, list) else len(EXPECTED_FEATURES)
325
+
326
  return PredictionResponse(
327
  status="success",
328
  kw_per_tr=round(kw_per_tr, 4),
329
  efficiency_rating=rating,
330
+ features_used=feature_count,
331
  model_type="RandomForestRegressor",
332
  timestamp=datetime.now().isoformat()
333
  )
 
363
  recommended_value=f"{optimal_sp:.1f}°C",
364
  expected_savings=f"{savings_pct:.1f}%",
365
  priority="HIGH" if savings_pct > 5 else "MEDIUM",
366
+ operator_action=f"Adjust CHW setpoint to {optimal_sp:.1f}°C"
367
  ))
368
 
369
  # Load-based recommendations