EVolve-Intelligence-Backend / app /agents /readiness_agent.py
Demonicade's picture
Upload folder using huggingface_hub
2d7db98 verified
Raw
History Blame Contribute Delete
6.37 kB
import numpy as np
import pandas as pd
from typing import List, Dict
class FleetReadinessAgent:
"""AI agent for fleet electrification readiness & procurement intelligence."""
def __init__(self):
# OEM catalogue: recommended EVs for industrial/commercial segments
self.ev_catalogue = [
{"model": "Tata Ultra EV 7", "oem": "Tata Motors", "category": "Freight", "range_km": 200, "battery_kwh": 168, "price_inr_lakh": 45, "payload_tons": 7},
{"model": "Ashok Leyland AVTR EV", "oem": "Ashok Leyland", "category": "Freight", "range_km": 300, "battery_kwh": 250, "price_inr_lakh": 75, "payload_tons": 16},
{"model": "Mahindra Treo Zor", "oem": "Mahindra Electric", "category": "Last Mile", "range_km": 130, "battery_kwh": 48, "price_inr_lakh": 12, "payload_tons": 0.5},
{"model": "Piaggio Ape E-City", "oem": "Piaggio", "category": "Last Mile", "range_km": 90, "battery_kwh": 26, "price_inr_lakh": 6, "payload_tons": 0.3},
{"model": "JBM ECO-LIFE", "oem": "JBM Auto", "category": "Bus", "range_km": 250, "battery_kwh": 200, "price_inr_lakh": 120, "payload_tons": 12},
{"model": " Volvo FM Electric", "oem": "Volvo Group", "category": "Construction", "range_km": 320, "battery_kwh": 450, "price_inr_lakh": 250, "payload_tons": 25},
]
def score_asset(self, asset: Dict) -> Dict:
"""Compute Electrification Readiness Index (ERI) 0-100."""
route_fit = self._route_fit(asset['route_distance_km'])
payload_fit = self._payload_fit(asset['payload_tons'], asset['category'])
duty_fit = self._duty_fit(asset['duty_cycle_hrs'], asset['dwell_time_hrs'])
terrain_fit = self._terrain_fit(asset['terrain_score'])
age_penalty = max(0, 1 - asset['age_years'] / 15)
# Weighted score
score = (
route_fit * 0.30 +
payload_fit * 0.20 +
duty_fit * 0.25 +
terrain_fit * 0.15 +
age_penalty * 0.10
) * 100
confidence = min(0.95, 0.6 + (len([route_fit, payload_fit, duty_fit, terrain_fit]) * 0.08))
recommended = self._match_ev(asset)
tco_savings = self._estimate_tco_savings(asset, recommended)
co2_reduction = self._estimate_co2_reduction(asset)
reasoning = [
f"Route distance {asset['route_distance_km']} km fits EV range with {route_fit:.0%} confidence.",
f"Payload/category compatibility: {payload_fit:.0%}.",
f"Duty cycle {asset['duty_cycle_hrs']}h vs dwell {asset['dwell_time_hrs']}h gives {duty_fit:.0%} charging feasibility.",
f"Terrain difficulty score {asset['terrain_score']} maps to {terrain_fit:.0%} energy efficiency."
]
return {
"asset_id": asset['asset_id'],
"readiness_score": round(score, 1),
"confidence": round(confidence, 2),
"recommended_ev": recommended['model'],
"oem": recommended['oem'],
"battery_capacity_kwh": recommended['battery_kwh'],
"range_km": recommended['range_km'],
"tco_savings_inr_lakh": round(tco_savings, 2),
"payback_months": round(recommended['price_inr_lakh'] / (tco_savings / 12 + 0.01), 1),
"co2_reduction_tons_yr": round(co2_reduction, 2),
"reasoning": reasoning
}
def _route_fit(self, distance_km: float) -> float:
# Ideal route < 150 km per shift
if distance_km <= 100:
return 1.0
if distance_km <= 200:
return 0.85
if distance_km <= 300:
return 0.60
return 0.35
def _payload_fit(self, payload_tons: float, category: str) -> float:
if category.lower() in ["last mile", "intra-plant"]:
return 1.0 if payload_tons <= 1 else 0.8
if payload_tons <= 7:
return 0.9
if payload_tons <= 16:
return 0.75
if payload_tons <= 25:
return 0.55
return 0.35
def _duty_fit(self, duty_hrs: float, dwell_hrs: float) -> float:
if duty_hrs <= 8 and dwell_hrs >= 8:
return 1.0
if duty_hrs <= 12 and dwell_hrs >= 4:
return 0.8
if duty_hrs <= 16 and dwell_hrs >= 2:
return 0.55
return 0.3
def _terrain_fit(self, terrain_score: float) -> float:
# terrain_score 1=flat, 5=extreme hills/mining
return max(0.2, 1 - (terrain_score - 1) * 0.2)
def _match_ev(self, asset: Dict) -> Dict:
candidates = [ev for ev in self.ev_catalogue if ev['payload_tons'] >= asset['payload_tons']]
if not candidates:
candidates = self.ev_catalogue
# Prefer lower price with sufficient range
best = min(candidates, key=lambda x: x['price_inr_lakh'] / (x['range_km'] + 1))
return best
def _estimate_tco_savings(self, asset: Dict, ev: Dict) -> float:
diesel_cost_per_km = asset['diesel_l_per_100km'] * 90 / 100 # INR/km at Rs 90/l
electricity_cost_per_km = ev['battery_kwh'] * 9 / ev['range_km'] # INR/km at Rs 9/kWh
annual_km = asset['route_distance_km'] * 250 # ~250 operating days
annual_fuel_savings = (diesel_cost_per_km - electricity_cost_per_km) * annual_km / 100000 # INR lakh
maintenance_savings = 0.5 # INR lakh/year rough estimate
return annual_fuel_savings + maintenance_savings
def _estimate_co2_reduction(self, asset: Dict) -> float:
annual_km = asset['route_distance_km'] * 250
diesel_co2_kg_per_km = asset['diesel_l_per_100km'] * 2.68 / 100
ev_co2_kg_per_km = 0.05 # India grid factor approx
return (diesel_co2_kg_per_km - ev_co2_kg_per_km) * annual_km / 1000
def score_fleet(self, assets: List[Dict]) -> Dict:
results = [self.score_asset(a) for a in assets]
ready = [r for r in results if r['readiness_score'] >= 70]
return {
"total_assets": len(results),
"ready_assets": len(ready),
"avg_readiness": round(np.mean([r['readiness_score'] for r in results]), 1),
"total_potential_savings_inr_lakh": round(sum(r['tco_savings_inr_lakh'] for r in ready), 2),
"total_co2_reduction_tons_yr": round(sum(r['co2_reduction_tons_yr'] for r in ready), 2),
"asset_scores": results
}