|
|
|
|
|
""" |
|
|
Custom Controller Example |
|
|
|
|
|
This example demonstrates how to implement and use custom |
|
|
control strategies with the TEP simulator. |
|
|
""" |
|
|
|
|
|
import numpy as np |
|
|
from tep import TEPSimulator |
|
|
from tep.simulator import ControlMode |
|
|
from tep.controllers import PIController, ManualController |
|
|
|
|
|
|
|
|
def main(): |
|
|
print("TEP Custom Controller Example") |
|
|
print("=" * 50) |
|
|
|
|
|
|
|
|
print("\nExample 1: Simple P-only reactor temperature control") |
|
|
print("-" * 50) |
|
|
|
|
|
class SimpleReactorTempController: |
|
|
"""Simple P-only controller for reactor temperature.""" |
|
|
|
|
|
def __init__(self, setpoint=120.0, gain=0.5): |
|
|
self.setpoint = setpoint |
|
|
self.gain = gain |
|
|
|
|
|
def calculate(self, xmeas, xmv, step): |
|
|
"""Calculate new MV values.""" |
|
|
new_xmv = xmv.copy() |
|
|
|
|
|
|
|
|
reactor_temp = xmeas[8] |
|
|
|
|
|
|
|
|
error = self.setpoint - reactor_temp |
|
|
|
|
|
|
|
|
|
|
|
adjustment = -self.gain * error |
|
|
new_xmv[9] = np.clip(new_xmv[9] + adjustment, 0, 100) |
|
|
|
|
|
return new_xmv |
|
|
|
|
|
sim = TEPSimulator(random_seed=12345) |
|
|
sim.initialize() |
|
|
|
|
|
controller = SimpleReactorTempController(setpoint=120.0, gain=0.3) |
|
|
|
|
|
result = sim.simulate_with_controller( |
|
|
duration_hours=2.0, |
|
|
controller=controller, |
|
|
disturbances={4: (0.5, 1)}, |
|
|
record_interval=60 |
|
|
) |
|
|
|
|
|
print(f"Setpoint: 120.0 °C") |
|
|
print(f"Final reactor temp: {result.measurements[-1, 8]:.2f} °C") |
|
|
print(f"Temperature std: {np.std(result.measurements[:, 8]):.2f} °C") |
|
|
|
|
|
|
|
|
print("\n" + "=" * 50) |
|
|
print("Example 2: Function-based controller") |
|
|
print("-" * 50) |
|
|
|
|
|
def my_control_function(xmeas, xmv, step): |
|
|
""" |
|
|
Simple function-based controller. |
|
|
Maintains reactor level by adjusting A+C feed. |
|
|
""" |
|
|
new_xmv = xmv.copy() |
|
|
|
|
|
|
|
|
level_error = 75.0 - xmeas[7] |
|
|
new_xmv[3] += 0.1 * level_error |
|
|
new_xmv[3] = np.clip(new_xmv[3], 0, 100) |
|
|
|
|
|
return new_xmv |
|
|
|
|
|
sim.initialize() |
|
|
result = sim.simulate_with_controller( |
|
|
duration_hours=1.0, |
|
|
controller=my_control_function, |
|
|
record_interval=60 |
|
|
) |
|
|
|
|
|
print(f"Level setpoint: 75%") |
|
|
print(f"Final reactor level: {result.measurements[-1, 7]:.1f}%") |
|
|
|
|
|
|
|
|
print("\n" + "=" * 50) |
|
|
print("Example 3: Cascade control structure") |
|
|
print("-" * 50) |
|
|
|
|
|
class CascadeController: |
|
|
""" |
|
|
Cascade controller: Reactor temp -> Cooling water flow. |
|
|
Outer loop adjusts CW outlet temp setpoint, |
|
|
Inner loop controls CW valve. |
|
|
""" |
|
|
|
|
|
def __init__(self): |
|
|
|
|
|
self.outer = PIController( |
|
|
setpoint=120.0, |
|
|
gain=2.0, |
|
|
taui=0.5, |
|
|
output_min=80, |
|
|
output_max=110, |
|
|
scale=1.0 |
|
|
) |
|
|
|
|
|
|
|
|
self.inner = PIController( |
|
|
setpoint=95.0, |
|
|
gain=-0.5, |
|
|
taui=0.05, |
|
|
scale=1.0 |
|
|
) |
|
|
|
|
|
self.dt = 1.0 / 3600.0 |
|
|
|
|
|
def calculate(self, xmeas, xmv, step): |
|
|
new_xmv = xmv.copy() |
|
|
|
|
|
|
|
|
if step % 10 == 0: |
|
|
reactor_temp = xmeas[8] |
|
|
cw_setpoint = self.outer.calculate(reactor_temp, self.inner.setpoint, 10*self.dt) |
|
|
self.inner.setpoint = cw_setpoint |
|
|
|
|
|
|
|
|
cw_outlet_temp = xmeas[20] |
|
|
new_xmv[9] = self.inner.calculate(cw_outlet_temp, new_xmv[9], self.dt) |
|
|
|
|
|
return new_xmv |
|
|
|
|
|
sim.initialize() |
|
|
cascade = CascadeController() |
|
|
|
|
|
result = sim.simulate_with_controller( |
|
|
duration_hours=2.0, |
|
|
controller=cascade, |
|
|
disturbances={4: (0.5, 1)}, |
|
|
record_interval=60 |
|
|
) |
|
|
|
|
|
print(f"Reactor temp setpoint: 120.0 °C") |
|
|
print(f"Mean reactor temp: {np.mean(result.measurements[:, 8]):.2f} °C") |
|
|
print(f"Temp std: {np.std(result.measurements[:, 8]):.2f} °C") |
|
|
|
|
|
|
|
|
print("\n" + "=" * 50) |
|
|
print("Example 4: Manual control mode") |
|
|
print("-" * 50) |
|
|
|
|
|
sim = TEPSimulator(control_mode=ControlMode.MANUAL) |
|
|
sim.initialize() |
|
|
|
|
|
print("Running manual control simulation...") |
|
|
print("Step changes:") |
|
|
|
|
|
|
|
|
for step in range(3600): |
|
|
|
|
|
if step == 600: |
|
|
sim.set_mv(10, 55.0) |
|
|
print(f" t=10min: MV10 -> 55%") |
|
|
|
|
|
|
|
|
if step == 1800: |
|
|
sim.set_mv(10, 45.0) |
|
|
print(f" t=30min: MV10 -> 45%") |
|
|
|
|
|
if not sim.step(): |
|
|
print("Process shutdown!") |
|
|
break |
|
|
|
|
|
print(f"\nFinal reactor temp: {sim.get_measurements()[8]:.1f} °C") |
|
|
print(f"(Manual control without feedback leads to drift)") |
|
|
|
|
|
print("\nAll examples complete!") |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |
|
|
|