Update app.py
Browse files
app.py
CHANGED
|
@@ -1,126 +1,118 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
import pandas as pd
|
| 3 |
-
import
|
| 4 |
-
from
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
@st.cache_data
|
| 13 |
def load_data():
|
| 14 |
try:
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
| 33 |
except Exception as e:
|
| 34 |
-
st.error(f"
|
| 35 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
def main():
|
| 38 |
-
st.
|
| 39 |
|
| 40 |
# Load dataset
|
| 41 |
-
df = load_data()
|
|
|
|
|
|
|
| 42 |
|
| 43 |
-
#
|
| 44 |
-
|
| 45 |
-
st.
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
|
|
|
| 49 |
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
current_hour = current_time.hour
|
| 53 |
|
| 54 |
-
#
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
"
|
| 68 |
-
"
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
left_indicator_color = "green" if grid_load < 3000 else "red"
|
| 81 |
-
right_indicator_color = "green" if grid_load >= 3000 else "red"
|
| 82 |
-
|
| 83 |
-
left_alarm = create_glowing_indicator(left_indicator_color, "")
|
| 84 |
-
right_alarm = create_glowing_indicator(right_indicator_color, "")
|
| 85 |
-
|
| 86 |
-
st.markdown(f"""
|
| 87 |
-
<div style='display: flex; justify-content: space-between;'>
|
| 88 |
-
{left_alarm}
|
| 89 |
-
{right_alarm}
|
| 90 |
-
</div>
|
| 91 |
-
""", unsafe_allow_html=True)
|
| 92 |
-
|
| 93 |
-
# EV Section
|
| 94 |
-
st.markdown("### EV Section")
|
| 95 |
-
|
| 96 |
-
# EV Connection Toggle
|
| 97 |
-
ev_export_toggle = st.checkbox("Allow EV Energy Export to Grid", value=False)
|
| 98 |
-
|
| 99 |
-
# EVs and Grid Calculation
|
| 100 |
-
ev_power = 50 # Assume each EV contributes 50 kW
|
| 101 |
-
|
| 102 |
-
if grid_load > 3000:
|
| 103 |
-
excess_load = grid_load - 3000
|
| 104 |
-
evs_required = -(-excess_load // ev_power) # Round up EVs required
|
| 105 |
-
|
| 106 |
-
st.warning(f"Grid is overloaded by {excess_load} kW. Approximately {evs_required} EVs are needed to stabilize the grid.")
|
| 107 |
-
|
| 108 |
-
if ev_export_toggle:
|
| 109 |
-
# Add EVs slider
|
| 110 |
-
evs_to_connect = st.slider("Number of EVs to Connect", min_value=0, max_value=int(evs_required), value=0, step=1)
|
| 111 |
-
|
| 112 |
-
# Update grid load dynamically
|
| 113 |
-
grid_load = max(0, grid_load - evs_to_connect * ev_power)
|
| 114 |
-
st.success(f"{evs_to_connect} EVs connected. Updated Grid Load: {grid_load} kW.")
|
| 115 |
-
|
| 116 |
-
elif grid_load <= 3000:
|
| 117 |
-
remaining_capacity = 3000 - grid_load
|
| 118 |
-
ev_slots_available = remaining_capacity // ev_power
|
| 119 |
-
st.success(f"Grid is stable. Approximately {ev_slots_available} EV slots available for charging.")
|
| 120 |
-
|
| 121 |
-
# Updated Grid Load Meter
|
| 122 |
-
fig_grid.update_traces(value=grid_load)
|
| 123 |
-
st.plotly_chart(fig_grid, use_container_width=True)
|
| 124 |
|
| 125 |
if __name__ == "__main__":
|
| 126 |
main()
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import pandas as pd
|
| 3 |
+
import datetime
|
| 4 |
+
from difflib import get_close_matches
|
| 5 |
+
|
| 6 |
+
# Function to standardize and find the closest column name
|
| 7 |
+
def get_closest_column(columns, target, threshold=0.6):
|
| 8 |
+
target = target.lower().replace(" ", "").replace("(", "").replace(")", "").replace("-", "")
|
| 9 |
+
columns_cleaned = [col.lower().replace(" ", "").replace("(", "").replace(")", "").replace("-", "") for col in columns]
|
| 10 |
+
matches = get_close_matches(target, columns_cleaned, n=1, cutoff=threshold)
|
| 11 |
+
if matches:
|
| 12 |
+
return columns[columns_cleaned.index(matches[0])]
|
| 13 |
+
return None
|
| 14 |
+
|
| 15 |
+
# Load the dataset
|
| 16 |
@st.cache_data
|
| 17 |
def load_data():
|
| 18 |
try:
|
| 19 |
+
df = pd.read_excel("grid_load_data.xlsx")
|
| 20 |
+
|
| 21 |
+
# Identify the closest matches for required columns
|
| 22 |
+
grid_load_col = get_closest_column(df.columns, "Grid Load (kW)")
|
| 23 |
+
time_col = get_closest_column(df.columns, "Time")
|
| 24 |
+
|
| 25 |
+
if not grid_load_col or not time_col:
|
| 26 |
+
st.error("The dataset must include columns similar to 'Grid Load (kW)' and 'Time'. Please check the formatting.")
|
| 27 |
+
return None, None
|
| 28 |
+
|
| 29 |
+
# Rename columns for consistent processing
|
| 30 |
+
df = df.rename(columns={grid_load_col: "Grid Load (kW)", time_col: "Time"})
|
| 31 |
+
|
| 32 |
+
# Ensure 'Time' column is datetime formatted
|
| 33 |
+
df["Time"] = pd.to_datetime(df["Time"], errors="coerce")
|
| 34 |
+
return df, "Grid Load (kW)"
|
| 35 |
+
|
| 36 |
+
except FileNotFoundError:
|
| 37 |
+
st.error("The file 'grid_load_data.xlsx' was not found. Please upload the correct file.")
|
| 38 |
+
return None, None
|
| 39 |
except Exception as e:
|
| 40 |
+
st.error(f"An error occurred while loading the dataset: {e}")
|
| 41 |
+
return None, None
|
| 42 |
+
|
| 43 |
+
# Function to calculate the predicted grid load for the current hour of the day
|
| 44 |
+
def calculate_predicted_grid_load(df, current_time, grid_load_col):
|
| 45 |
+
if df is None:
|
| 46 |
+
st.error("Dataset is not loaded correctly.")
|
| 47 |
+
return None
|
| 48 |
+
|
| 49 |
+
# Extract the hour and day from the current time
|
| 50 |
+
current_hour = current_time.hour
|
| 51 |
+
|
| 52 |
+
# Filter dataset for the given hour across all days
|
| 53 |
+
df["Hour"] = df["Time"].dt.hour
|
| 54 |
+
hourly_data = df[df["Hour"] == current_hour]
|
| 55 |
|
| 56 |
+
if not hourly_data.empty:
|
| 57 |
+
# Calculate the average grid load for the given hour across all days
|
| 58 |
+
avg_grid_load = hourly_data[grid_load_col].mean()
|
| 59 |
+
return avg_grid_load
|
| 60 |
+
else:
|
| 61 |
+
st.warning("No data found for the current hour. Using default value.")
|
| 62 |
+
return 0 # Default to 0 if no data is found for the given hour
|
| 63 |
+
|
| 64 |
+
# Display the live clock
|
| 65 |
+
def display_clock():
|
| 66 |
+
current_time = datetime.datetime.now()
|
| 67 |
+
st.sidebar.markdown(f"### 🕒 {current_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
| 68 |
+
return current_time
|
| 69 |
+
|
| 70 |
+
# Main app
|
| 71 |
def main():
|
| 72 |
+
st.title("Optimized EV Charging and Grid Management")
|
| 73 |
|
| 74 |
# Load dataset
|
| 75 |
+
df, grid_load_col = load_data()
|
| 76 |
+
if df is None or grid_load_col is None:
|
| 77 |
+
return # Exit if the dataset is not loaded properly
|
| 78 |
|
| 79 |
+
# Sidebar for user inputs
|
| 80 |
+
with st.sidebar:
|
| 81 |
+
st.header("User Inputs")
|
| 82 |
|
| 83 |
+
# Battery details (manually entered)
|
| 84 |
+
state_of_charge = st.number_input("State of Charge (SOC) in kWh", min_value=0, max_value=100, step=1)
|
| 85 |
+
battery_capacity = st.number_input("Battery Capacity (kWh)", min_value=10, max_value=200, step=1)
|
| 86 |
|
| 87 |
+
# ON/OFF button for exporting battery power to the grid
|
| 88 |
+
export_power = st.checkbox("Export Power to Grid (ON/OFF)")
|
|
|
|
| 89 |
|
| 90 |
+
# Display live clock
|
| 91 |
+
current_time = display_clock()
|
| 92 |
+
|
| 93 |
+
# Predicted grid load
|
| 94 |
+
predicted_grid_load = calculate_predicted_grid_load(df, current_time, grid_load_col)
|
| 95 |
+
if predicted_grid_load is not None:
|
| 96 |
+
st.write(f"### Predicted Grid Load at {current_time.strftime('%H:%M:%S')}: **{predicted_grid_load:.2f} kW**")
|
| 97 |
+
|
| 98 |
+
# Evaluate grid status based on the predicted load
|
| 99 |
+
if predicted_grid_load > 3400:
|
| 100 |
+
st.error("Grid is Overloaded! EV Charging Disallowed")
|
| 101 |
+
st.write("🚫 Red Light: EV Charging is Disconnected.")
|
| 102 |
+
else:
|
| 103 |
+
st.success("Grid is Stable! EV Charging Allowed")
|
| 104 |
+
st.write("✅ Green Light: EV Charging is Active.")
|
| 105 |
+
|
| 106 |
+
# Calculate available EV slots
|
| 107 |
+
available_capacity = 3400 - predicted_grid_load
|
| 108 |
+
ev_slots = available_capacity // 50 # Assuming each EV slot requires 50kW
|
| 109 |
+
st.write(f"### Available EV Slots: **{ev_slots}**")
|
| 110 |
+
|
| 111 |
+
# Export power status
|
| 112 |
+
if export_power:
|
| 113 |
+
st.warning("Battery is exporting power to the grid.")
|
| 114 |
+
else:
|
| 115 |
+
st.info("Battery is not exporting power.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
if __name__ == "__main__":
|
| 118 |
main()
|