Control_System / app.py
ahmad123445's picture
Create app.py
ff86322 verified
import streamlit as st
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import time
# =====================================================
# PAGE CONFIGURATION
# =====================================================
# =====================================================
# FIX FOR STREAMLIT CLOUD / HUGGING FACE
# =====================================================
st.set_page_config(
page_title="Industrial PLC-DCS-SCADA Simulator",
layout="wide"
)
st.title("🏭 Industrial Chemical Plant Simulator")
st.markdown("## PLC + DCS + SCADA Integrated Control System")
# =====================================================
# SIDEBAR
# =====================================================
st.sidebar.header("Plant Parameters")
setpoint = st.sidebar.slider(
"Reactor Temperature Setpoint",
40,
120,
80
)
simulation_time = st.sidebar.slider(
"Simulation Time",
100,
500,
250
)
noise = st.sidebar.slider(
"Sensor Noise",
0.0,
2.0,
0.3
)
# =====================================================
# PROCESS MODEL
# =====================================================
ambient_temp = 25
process_gain = 0.12
cooling_gain = 0.20
thermal_mass = 18
# =====================================================
# PLC TAGS
# =====================================================
class PLC:
def __init__(self):
self.tags = {
"TT101": 0,
"CV101": 0,
"SP101": setpoint,
"ALARM_HIGH": False,
"PUMP_RUN": True
}
def update(self, temperature, valve):
self.tags["TT101"] = temperature
self.tags["CV101"] = valve
if temperature > setpoint + 10:
self.tags["ALARM_HIGH"] = True
else:
self.tags["ALARM_HIGH"] = False
# =====================================================
# CONTROL SIMULATION
# =====================================================
def simulate(controller):
plc = PLC()
temp = 30
integral = 0
previous_error = 0
temperatures = []
valves = []
alarms = []
times = []
for t in range(simulation_time):
error = setpoint - temp
# =============================================
# PLC + ON/OFF CONTROL
# =============================================
if controller == "ONOFF":
if temp < setpoint:
valve = 0
else:
valve = 100
# =============================================
# PLC + PID CONTROL
# =============================================
elif controller == "PID":
kp = 2.5
ki = 0.04
kd = 0.7
integral += error
derivative = error - previous_error
valve = kp * error + ki * integral + kd * derivative
valve = np.clip(valve, 0, 100)
previous_error = error
# =============================================
# PLC + MPC CONTROL
# =============================================
elif controller == "MPC":
future_error = error * 0.85
valve = 1.8 * future_error + 25
valve = np.clip(valve, 0, 100)
# =============================================
# PROCESS DYNAMICS
# =============================================
heating = process_gain * (100 - temp)
cooling = cooling_gain * (valve / 100) * (temp - ambient_temp)
dT = (heating - cooling) / thermal_mass
temp += dT + np.random.normal(0, noise)
# PLC UPDATE
plc.update(temp, valve)
temperatures.append(temp)
valves.append(valve)
alarms.append(plc.tags["ALARM_HIGH"])
times.append(t)
return pd.DataFrame({
"Time": times,
"Temperature": temperatures,
"Valve": valves,
"Alarm": alarms
})
# =====================================================
# RUN ALL CONTROLLERS
# =====================================================
onoff_df = simulate("ONOFF")
pid_df = simulate("PID")
mpc_df = simulate("MPC")
# =====================================================
# SCADA HEADER
# =====================================================
st.markdown("---")
sc1, sc2, sc3, sc4 = st.columns(4)
with sc1:
st.metric("Plant Status", "RUNNING")
with sc2:
st.metric("PLC Status", "ONLINE")
with sc3:
st.metric("DCS Network", "CONNECTED")
with sc4:
st.metric("SCADA", "ACTIVE")
# =====================================================
# SECTION 1 : PLC + ON-OFF CONTROL
# =====================================================
st.markdown("---")
st.header("🟒 SECTION 1 : PLC + ON-OFF CONTROL")
import streamlit as st
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import time
# -------------------------------------------------
# PAGE CONFIG
# -------------------------------------------------
st.set_page_config(
page_title="Chemical Plant Control Simulator",
layout="wide"
)
st.title("🏭 Chemical Plant DCS / SCADA Simulator")
st.markdown("### Temperature Control of a Chemical Reactor")
# -------------------------------------------------
# SIDEBAR
# -------------------------------------------------
st.sidebar.header("Simulation Settings")
setpoint = st.sidebar.slider(
"Temperature Setpoint (Β°C)",
40,
120,
80
)
simulation_time = st.sidebar.slider(
"Simulation Time",
50,
500,
200
)
noise_level = st.sidebar.slider(
"Process Noise",
0.0,
2.0,
0.3
)
# -------------------------------------------------
# PROCESS MODEL
# -------------------------------------------------
ambient_temp = 25
process_gain = 0.12
cooling_gain = 0.18
thermal_mass = 18
# -------------------------------------------------
# SIMULATION FUNCTION
# -------------------------------------------------
def simulate(controller_type):
temp = 30
integral = 0
previous_error = 0
temperatures = []
valve_positions = []
times = []
for t in range(simulation_time):
error = setpoint - temp
# -----------------------------------------
# ON-OFF CONTROL
# -----------------------------------------
if controller_type == "ON-OFF":
if temp < setpoint:
valve = 0
else:
valve = 100
# -----------------------------------------
# PID CONTROL
# -----------------------------------------
elif controller_type == "PID":
kp = 2.5
ki = 0.05
kd = 0.8
integral += error
derivative = error - previous_error
valve = (
kp * error
+ ki * integral
+ kd * derivative
)
valve = np.clip(valve, 0, 100)
previous_error = error
# -----------------------------------------
# MPC-LIKE CONTROL
# -----------------------------------------
elif controller_type == "MPC":
future_error = error * 0.85
valve = 1.8 * future_error + 25
valve = np.clip(valve, 0, 100)
# -----------------------------------------
# PROCESS DYNAMICS
# -----------------------------------------
heating = process_gain * (100 - temp)
cooling = cooling_gain * (valve / 100) * (temp - ambient_temp)
dT = (heating - cooling) / thermal_mass
temp += dT + np.random.normal(0, noise_level)
temperatures.append(temp)
valve_positions.append(valve)
times.append(t)
return pd.DataFrame({
"Time": times,
"Temperature": temperatures,
"Valve": valve_positions
})
# -------------------------------------------------
# RUN SIMULATIONS
# -------------------------------------------------
onoff_df = simulate("ON-OFF")
pid_df = simulate("PID")
mpc_df = simulate("MPC")
# -------------------------------------------------
# LIVE SCADA CARDS
# -------------------------------------------------
col1, col2, col3 = st.columns(3)
latest_onoff = round(onoff_df["Temperature"].iloc[-1], 2)
latest_pid = round(pid_df["Temperature"].iloc[-1], 2)
latest_mpc = round(mpc_df["Temperature"].iloc[-1], 2)
with col1:
st.metric("ON-OFF Temp", f"{latest_onoff} Β°C")
with col2:
st.metric("PID Temp", f"{latest_pid} Β°C")
with col3:
st.metric("MPC Temp", f"{latest_mpc} Β°C")
# -------------------------------------------------
# DCS STYLE REACTOR DIAGRAM
# -------------------------------------------------
st.markdown("---")
st.subheader("βš™οΈ Reactor DCS Overview")
reactor_col1, reactor_col2 = st.columns([2, 1])
with reactor_col1:
fig_reactor = go.Figure()
fig_reactor.add_shape(
type="rect",
x0=1,
y0=1,
x1=4,
y1=8,
line=dict(color="cyan", width=4),
fillcolor="rgba(0,255,255,0.1)"
)
fig_reactor.add_annotation(
x=2.5,
y=4.5,
text=f"Reactor\nSP = {setpoint}Β°C",
showarrow=False,
font=dict(size=18)
)
fig_reactor.add_shape(
type="line",
x0=4,
y0=4,
x1=7,
y1=4,
line=dict(color="orange", width=8)
)
fig_reactor.add_annotation(
x=6,
y=4.5,
text="Cooling Water",
showarrow=False
)
fig_reactor.update_layout(
height=400,
paper_bgcolor="black",
plot_bgcolor="black",
font_color="white",
xaxis=dict(visible=False),
yaxis=dict(visible=False)
)
st.plotly_chart(fig_reactor, use_container_width=True)
with reactor_col2:
gauge = go.Figure(go.Indicator(
mode="gauge+number",
value=latest_pid,
title={'text': "PID Reactor Temp"},
gauge={
'axis': {'range': [None, 120]},
'bar': {'color': "red"},
'steps': [
{'range': [0, 60], 'color': "lightgreen"},
{'range': [60, 90], 'color': "yellow"},
{'range': [90, 120], 'color': "orange"}
]
}
))
gauge.update_layout(height=400)
st.plotly_chart(gauge, use_container_width=True)
# -------------------------------------------------
# TEMPERATURE COMPARISON
# -------------------------------------------------
st.markdown("---")
st.subheader("πŸ“ˆ Temperature Response Comparison")
fig = go.Figure()
fig.add_trace(go.Scatter(
x=onoff_df["Time"],
y=onoff_df["Temperature"],
mode='lines',
name='ON-OFF'
))
fig.add_trace(go.Scatter(
x=pid_df["Time"],
y=pid_df["Temperature"],
mode='lines',
name='PID'
))
fig.add_trace(go.Scatter(
x=mpc_df["Time"],
y=mpc_df["Temperature"],
mode='lines',
name='MPC'
))
fig.add_hline(
y=setpoint,
line_dash="dash",
annotation_text="Setpoint"
)
fig.update_layout(
template="plotly_dark",
height=500,
xaxis_title="Time",
yaxis_title="Temperature (Β°C)"
)
st.plotly_chart(fig, use_container_width=True)
# -------------------------------------------------
# VALVE POSITION COMPARISON
# -------------------------------------------------
st.markdown("---")
st.subheader("πŸŒ€ Cooling Valve Position")
fig2 = make_subplots(rows=1, cols=3,
subplot_titles=("ON-OFF", "PID", "MPC"))
fig2.add_trace(
go.Scatter(
x=onoff_df["Time"],
y=onoff_df["Valve"],
mode='lines'
),
row=1,
col=1
)
fig2.add_trace(
go.Scatter(
x=pid_df["Time"],
y=pid_df["Valve"],
mode='lines'
),
row=1,
col=2
)
fig2.add_trace(
go.Scatter(
x=mpc_df["Time"],
y=mpc_df["Valve"],
mode='lines'
),
row=1,
col=3
)
fig2.update_layout(
template="plotly_dark",
height=450,
showlegend=False
)
st.plotly_chart(fig2, use_container_width=True)
# -------------------------------------------------
# CONTROL SYSTEM EXPLANATION
# -------------------------------------------------
st.markdown("---")
st.subheader("πŸ“š Control System Explanation")
st.markdown("""
### 1. ON-OFF Control
- Simplest controller
- Valve fully opens or fully closes
- Causes oscillation
- Used in household thermostats
### 2. PID Control
- Most common industrial controller
- Smooth response
- Reduces overshoot
- Widely used in DCS systems
### 3. MPC Control
- Predictive strategy
- Estimates future behavior
- Used in advanced chemical plants
- Common in refineries and petrochemical plants
""")
# -------------------------------------------------
# PERFORMANCE TABLE
# -------------------------------------------------
st.markdown("---")
st.subheader("πŸ“Š Performance Comparison")
performance = pd.DataFrame({
"Controller": ["ON-OFF", "PID", "MPC"],
"Final Temp": [
round(onoff_df["Temperature"].iloc[-1], 2),
round(pid_df["Temperature"].iloc[-1], 2),
round(mpc_df["Temperature"].iloc[-1], 2)
],
"Valve Stability": [
"Low",
"High",
"Very High"
],
"Industrial Use": [
"Basic",
"Very Common",
"Advanced"
]
})
st.dataframe(performance, use_container_width=True)
# -------------------------------------------------
# FOOTER
# -------------------------------------------------
st.markdown("---")
st.markdown("## βœ… Simulation Complete")
st.markdown("Designed for Chemical Engineering DCS / SCADA Learning")